5
\$\begingroup\$

Problem statement:

Calculate the date of meetups.

Typically meetups happen on the same day of the week.

Examples are

  • the first Monday
  • the third Tuesday
  • the Wednesteenth
  • the last Thursday

Note that "Monteenth", "Tuesteenth", etc are all made up words. There was a meetup whose members realised that there are exactly 7 days that end in '-teenth'. Therefore, one is guaranteed that each day of the week (Monday, Tuesday, ...) will have exactly one date that is named with '-teenth' in every month.

Code:

import org.joda.time.DateTime;
import org.joda.time.Weeks;
import org.joda.time.Period;
import static org.joda.time.DateTimeConstants.*;

enum MeetupSchedule {
    FIRST,
    SECOND,
    THIRD,
    FOURTH,
    LAST,
    TEENTH
}

public class Meetup {
  private final int month;
  private final int year;
  private final DateTime firstOfMonth;

  public Meetup(int month, int year) {
    this.month = month;
    this.year = year;
    firstOfMonth = new DateTime(year, month, 1, 0, 0);
  }

  public DateTime day(int dayOfWeek, MeetupSchedule schedule) {
    switch (schedule) {
      case FIRST:
        return getNthWeekDayOfMonth(dayOfWeek, 1);
      case SECOND:
        return getNthWeekDayOfMonth(dayOfWeek, 2);
      case THIRD:
        return getNthWeekDayOfMonth(dayOfWeek, 3);
      case FOURTH:
        return getNthWeekDayOfMonth(dayOfWeek, 4);
      case LAST:
        return getNthWeekDayOfMonth(dayOfWeek, 6);
      case TEENTH:
        return getTeenthWeekDayOfMonth(dayOfWeek);
      default:
        return new DateTime(year, month, 31, 0, 0);
    }
  }

  private DateTime getNthWeekDayOfMonth(int dayOfWeek, int nthWeekDay) {
    DateTime current = getFirstDayOfWeekForMonth(firstOfMonth, dayOfWeek);
    int weekCount = 1;
    while (current.getMonthOfYear() == month && weekCount <= nthWeekDay) {
      current = current.plus(Period.days(7));
      weekCount += 1;
    }
    return current.minus(Period.days(7));
  }

  private DateTime getTeenthWeekDayOfMonth(int dayOfWeek) {
    DateTime current = getFirstDayOfWeekForMonth(firstOfMonth, dayOfWeek);
    while (current.getMonthOfYear() == month) {
      if (isTeenth(current.dayOfMonth().get())) {
        break;
      }
      current = current.plus(Period.days(7));
    }
    return current;
  }

  private DateTime getFirstDayOfWeekForMonth(DateTime current, int dayOfWeek) {
    while (current.dayOfWeek().get() != dayOfWeek) {
      current = current.plus(Period.days(1));
    }
    return current;
  }

  private boolean isTeenth(int day) {
    return day > 12 && day < 20;
  }
}

Test Suite:

import org.joda.time.DateTime;
import org.joda.time.DateTimeConstants;
import org.junit.Test;

import static org.assertj.core.api.Assertions.assertThat;
import static org.joda.time.DateTimeConstants.*;

/*
 * We use Joda Time here to encourage the use of a saner date manipulation library.
 */
public class MeetupTest {
    @Test
    public void test_monteenth_of_may_2013() {
        DateTime expected = new DateTime(2013, 5, 13, 0, 0);
        Meetup meetup = new Meetup(5, 2013);
        assertThat(meetup.day(MONDAY, MeetupSchedule.TEENTH)).isEqualTo(expected);
    }

    @Test
    public void test_monteenth_of_august_2013() {
        DateTime expected = new DateTime(2013, 8, 19, 0, 0);
        Meetup meetup = new Meetup(8, 2013);
        assertThat(meetup.day(MONDAY, MeetupSchedule.TEENTH)).isEqualTo(expected);
    }

    @Test
    public void test_monteenth_of_september_2013() {
        DateTime expected = new DateTime(2013, 9, 16, 0, 0);
        Meetup meetup = new Meetup(9, 2013);
        assertThat(meetup.day(MONDAY, MeetupSchedule.TEENTH)).isEqualTo(expected);
    }

    @Test
    public void test_tuesteenth_of_march_2013() {
        DateTime expected = new DateTime(2013, 3, 19, 0, 0);
        Meetup meetup = new Meetup(3, 2013);
        assertThat(meetup.day(TUESDAY, MeetupSchedule.TEENTH)).isEqualTo(expected);
    }

    @Test
    public void test_tuesteenth_of_april_2013() {
        DateTime expected = new DateTime(2013, 4, 16, 0, 0);
        Meetup meetup = new Meetup(4, 2013);
        assertThat(meetup.day(TUESDAY, MeetupSchedule.TEENTH)).isEqualTo(expected);
    }

    @Test
    public void test_tuesteenth_of_august_2013() {
        DateTime expected = new DateTime(2013, 8, 13, 0, 0);
        Meetup meetup = new Meetup(8, 2013);
        assertThat(meetup.day(TUESDAY, MeetupSchedule.TEENTH)).isEqualTo(expected);
    }

    @Test
    public void test_wednesteenth_of_january_2013() {
        DateTime expected = new DateTime(2013, 1, 16, 0, 0);
        Meetup meetup = new Meetup(1, 2013);
        assertThat(meetup.day(WEDNESDAY, MeetupSchedule.TEENTH)).isEqualTo(expected);
    }

    @Test
    public void test_wednesteenth_of_february_2013() {
        DateTime expected = new DateTime(2013, 2, 13, 0, 0);
        Meetup meetup = new Meetup(2, 2013);
        assertThat(meetup.day(WEDNESDAY, MeetupSchedule.TEENTH)).isEqualTo(expected);
    }

    @Test
    public void test_wednesteenth_of_june_2013() {
        DateTime expected = new DateTime(2013, 6, 19, 0, 0);
        Meetup meetup = new Meetup(6, 2013);
        assertThat(meetup.day(WEDNESDAY, MeetupSchedule.TEENTH)).isEqualTo(expected);
    }

    @Test
    public void test_thursteenth_of_may_2013() {
        DateTime expected = new DateTime(2013, 5, 16, 0, 0);
        Meetup meetup = new Meetup(5, 2013);
        assertThat(meetup.day(THURSDAY, MeetupSchedule.TEENTH)).isEqualTo(expected);
    }

    @Test
    public void test_thursteenth_of_june_2013() {
        DateTime expected = new DateTime(2013, 6, 13, 0, 0);
        Meetup meetup = new Meetup(6, 2013);
        assertThat(meetup.day(THURSDAY, MeetupSchedule.TEENTH)).isEqualTo(expected);
    }

    @Test
    public void test_thursteenth_of_september_2013() {
        DateTime expected = new DateTime(2013, 9, 19, 0, 0);
        Meetup meetup = new Meetup(9, 2013);
        assertThat(meetup.day(THURSDAY, MeetupSchedule.TEENTH)).isEqualTo(expected);
    }

    @Test
    public void test_friteenth_of_april_2013() {
        DateTime expected = new DateTime(2013, 4, 19, 0, 0);
        Meetup meetup = new Meetup(4, 2013);
        assertThat(meetup.day(FRIDAY, MeetupSchedule.TEENTH)).isEqualTo(expected);
    }

    @Test
    public void test_friteenth_of_august_2013() {
        DateTime expected = new DateTime(2013, 8, 16, 0, 0);
        Meetup meetup = new Meetup(8, 2013);
        assertThat(meetup.day(FRIDAY, MeetupSchedule.TEENTH)).isEqualTo(expected);
    }

    @Test
    public void test_friteenth_of_september_2013() {
        DateTime expected = new DateTime(2013, 9, 13, 0, 0);
        Meetup meetup = new Meetup(9, 2013);
        assertThat(meetup.day(FRIDAY, MeetupSchedule.TEENTH)).isEqualTo(expected);
    }

    @Test
    public void test_saturteenth_of_february_2013() {
        DateTime expected = new DateTime(2013, 2, 16, 0, 0);
        Meetup meetup = new Meetup(2, 2013);
        assertThat(meetup.day(SATURDAY, MeetupSchedule.TEENTH)).isEqualTo(expected);
    }

    @Test
    public void test_saturteenth_of_april_2013() {
        DateTime expected = new DateTime(2013, 4, 13, 0, 0);
        Meetup meetup = new Meetup(4, 2013);
        assertThat(meetup.day(SATURDAY, MeetupSchedule.TEENTH)).isEqualTo(expected);
    }

    @Test
    public void test_saturteenth_of_october_2013() {
        DateTime expected = new DateTime(2013, 10, 19, 0, 0);
        Meetup meetup = new Meetup(10, 2013);
        assertThat(meetup.day(SATURDAY, MeetupSchedule.TEENTH)).isEqualTo(expected);
    }

    @Test
    public void test_sunteenth_of_map_2013() {
        DateTime expected = new DateTime(2013, 5, 19, 0, 0);
        Meetup meetup = new Meetup(5, 2013);
        assertThat(meetup.day(SUNDAY, MeetupSchedule.TEENTH)).isEqualTo(expected);
    }

    @Test
    public void test_sunteenth_of_june_2013() {
        DateTime expected = new DateTime(2013, 6, 16, 0, 0);
        Meetup meetup = new Meetup(6, 2013);
        assertThat(meetup.day(SUNDAY, MeetupSchedule.TEENTH)).isEqualTo(expected);
    }

    @Test
    public void test_sunteenth_of_october_2013() {
        DateTime expected = new DateTime(2013, 10, 13, 0, 0);
        Meetup meetup = new Meetup(10, 2013);
        assertThat(meetup.day(SUNDAY, MeetupSchedule.TEENTH)).isEqualTo(expected);
    }

    @Test
    public void test_first_monday_of_march_2013() {
        DateTime expected = new DateTime(2013, 3, 4, 0, 0);
        Meetup meetup = new Meetup(3, 2013);
        assertThat(meetup.day(MONDAY, MeetupSchedule.FIRST)).isEqualTo(expected);
    }

    @Test
    public void test_first_monday_of_april_2013() {
        DateTime expected = new DateTime(2013, 4, 1, 0, 0);
        Meetup meetup = new Meetup(4, 2013);
        assertThat(meetup.day(MONDAY, MeetupSchedule.FIRST)).isEqualTo(expected);
    }

    @Test
    public void test_first_tuesday_of_may_2013() {
        DateTime expected = new DateTime(2013, 5, 7, 0, 0);
        Meetup meetup = new Meetup(5, 2013);
        assertThat(meetup.day(TUESDAY, MeetupSchedule.FIRST)).isEqualTo(expected);
    }

    @Test
    public void test_first_tuesday_of_june_2013() {
        DateTime expected = new DateTime(2013, 6, 4, 0, 0);
        Meetup meetup = new Meetup(6, 2013);
        assertThat(meetup.day(TUESDAY, MeetupSchedule.FIRST)).isEqualTo(expected);
    }

    @Test
    public void test_first_wednesday_of_july_2013() {
        DateTime expected = new DateTime(2013, 7, 3, 0, 0);
        Meetup meetup = new Meetup(7, 2013);
        assertThat(meetup.day(WEDNESDAY, MeetupSchedule.FIRST)).isEqualTo(expected);
    }

    @Test
    public void test_first_wednesday_of_august_2013() {
        DateTime expected = new DateTime(2013, 8, 7, 0, 0);
        Meetup meetup = new Meetup(8, 2013);
        assertThat(meetup.day(WEDNESDAY, MeetupSchedule.FIRST)).isEqualTo(expected);
    }

    @Test
    public void test_first_thursday_of_september_2013() {
        DateTime expected = new DateTime(2013, 9, 5, 0, 0);
        Meetup meetup = new Meetup(9, 2013);
        assertThat(meetup.day(THURSDAY, MeetupSchedule.FIRST)).isEqualTo(expected);
    }

    @Test
    public void test_first_thursday_of_october_2013() {
        DateTime expected = new DateTime(2013, 10, 3, 0, 0);
        Meetup meetup = new Meetup(10, 2013);
        assertThat(meetup.day(THURSDAY, MeetupSchedule.FIRST)).isEqualTo(expected);
    }

    @Test
    public void test_first_friday_of_november_2013() {
        DateTime expected = new DateTime(2013, 11, 1, 0, 0);
        Meetup meetup = new Meetup(11, 2013);
        assertThat(meetup.day(FRIDAY, MeetupSchedule.FIRST)).isEqualTo(expected);
    }

    @Test
    public void test_first_friday_of_december_2013() {
        DateTime expected = new DateTime(2013, 12, 6, 0, 0);
        Meetup meetup = new Meetup(12, 2013);
        assertThat(meetup.day(FRIDAY, MeetupSchedule.FIRST)).isEqualTo(expected);
    }

    @Test
    public void test_first_saturday_of_january_2013() {
        DateTime expected = new DateTime(2013, 1, 5, 0, 0);
        Meetup meetup = new Meetup(1, 2013);
        assertThat(meetup.day(SATURDAY, MeetupSchedule.FIRST)).isEqualTo(expected);
    }

    @Test
    public void test_first_saturday_of_february_2013() {
        DateTime expected = new DateTime(2013, 2, 2, 0, 0);
        Meetup meetup = new Meetup(2, 2013);
        assertThat(meetup.day(SATURDAY, MeetupSchedule.FIRST)).isEqualTo(expected);
    }

    @Test
    public void test_first_sunday_of_march_2013() {
        DateTime expected = new DateTime(2013, 3, 3, 0, 0);
        Meetup meetup = new Meetup(3, 2013);
        assertThat(meetup.day(SUNDAY, MeetupSchedule.FIRST)).isEqualTo(expected);
    }

    @Test
    public void test_first_sunday_of_april_2013() {
        DateTime expected = new DateTime(2013, 4, 7, 0, 0);
        Meetup meetup = new Meetup(4, 2013);
        assertThat(meetup.day(SUNDAY, MeetupSchedule.FIRST)).isEqualTo(expected);
    }

    @Test
    public void test_second_monday_of_march_2013() {
        DateTime expected = new DateTime(2013, 3, 11, 0, 0);
        Meetup meetup = new Meetup(3, 2013);
        assertThat(meetup.day(MONDAY, MeetupSchedule.SECOND)).isEqualTo(expected);
    }

    @Test
    public void test_second_monday_of_april_2013() {
        DateTime expected = new DateTime(2013, 4, 8, 0, 0);
        Meetup meetup = new Meetup(4, 2013);
        assertThat(meetup.day(MONDAY, MeetupSchedule.SECOND)).isEqualTo(expected);
    }

    @Test
    public void test_second_tuesday_of_may_2013() {
        DateTime expected = new DateTime(2013, 5, 14, 0, 0);
        Meetup meetup = new Meetup(5, 2013);
        assertThat(meetup.day(TUESDAY, MeetupSchedule.SECOND)).isEqualTo(expected);
    }

    @Test
    public void test_second_tuesday_of_june_2013() {
        DateTime expected = new DateTime(2013, 6, 11, 0, 0);
        Meetup meetup = new Meetup(6, 2013);
        assertThat(meetup.day(TUESDAY, MeetupSchedule.SECOND)).isEqualTo(expected);
    }

    @Test
    public void test_second_wednesday_of_july_2013() {
        DateTime expected = new DateTime(2013, 7, 10, 0, 0);
        Meetup meetup = new Meetup(7, 2013);
        assertThat(meetup.day(WEDNESDAY, MeetupSchedule.SECOND)).isEqualTo(expected);
    }

    @Test
    public void test_second_wednesday_of_august_2013() {
        DateTime expected = new DateTime(2013, 8, 14, 0, 0);
        Meetup meetup = new Meetup(8, 2013);
        assertThat(meetup.day(WEDNESDAY, MeetupSchedule.SECOND)).isEqualTo(expected);
    }

    @Test
    public void test_second_thursday_of_september_2013() {
        DateTime expected = new DateTime(2013, 9, 12, 0, 0);
        Meetup meetup = new Meetup(9, 2013);
        assertThat(meetup.day(THURSDAY, MeetupSchedule.SECOND)).isEqualTo(expected);
    }

    @Test
    public void test_second_thursday_of_october_2013() {
        DateTime expected = new DateTime(2013, 10, 10, 0, 0);
        Meetup meetup = new Meetup(10, 2013);
        assertThat(meetup.day(THURSDAY, MeetupSchedule.SECOND)).isEqualTo(expected);
    }

    @Test
    public void test_second_friday_of_november_2013() {
        DateTime expected = new DateTime(2013, 11, 8, 0, 0);
        Meetup meetup = new Meetup(11, 2013);
        assertThat(meetup.day(FRIDAY, MeetupSchedule.SECOND)).isEqualTo(expected);
    }

    @Test
    public void test_second_friday_of_december_2013() {
        DateTime expected = new DateTime(2013, 12, 13, 0, 0);
        Meetup meetup = new Meetup(12, 2013);
        assertThat(meetup.day(FRIDAY, MeetupSchedule.SECOND)).isEqualTo(expected);
    }

    @Test
    public void test_second_saturday_of_january_2013() {
        DateTime expected = new DateTime(2013, 1, 12, 0, 0);
        Meetup meetup = new Meetup(1, 2013);
        assertThat(meetup.day(SATURDAY, MeetupSchedule.SECOND)).isEqualTo(expected);
    }

    @Test
    public void test_second_saturday_of_february_2013() {
        DateTime expected = new DateTime(2013, 2, 9, 0, 0);
        Meetup meetup = new Meetup(2, 2013);
        assertThat(meetup.day(SATURDAY, MeetupSchedule.SECOND)).isEqualTo(expected);
    }

    @Test
    public void test_second_sunday_of_march_2013() {
        DateTime expected = new DateTime(2013, 3, 10, 0, 0);
        Meetup meetup = new Meetup(3, 2013);
        assertThat(meetup.day(SUNDAY, MeetupSchedule.SECOND)).isEqualTo(expected);
    }

    @Test
    public void test_second_sunday_of_april_2013() {
        DateTime expected = new DateTime(2013, 4, 14, 0, 0);
        Meetup meetup = new Meetup(4, 2013);
        assertThat(meetup.day(SUNDAY, MeetupSchedule.SECOND)).isEqualTo(expected);
    }

    @Test
    public void test_third_monday_of_march_2013() {
        DateTime expected = new DateTime(2013, 3, 18, 0, 0);
        Meetup meetup = new Meetup(3, 2013);
        assertThat(meetup.day(MONDAY, MeetupSchedule.THIRD)).isEqualTo(expected);
    }

    @Test
    public void test_third_monday_of_april_2013() {
        DateTime expected = new DateTime(2013, 4, 15, 0, 0);
        Meetup meetup = new Meetup(4, 2013);
        assertThat(meetup.day(MONDAY, MeetupSchedule.THIRD)).isEqualTo(expected);
    }

    @Test
    public void test_third_tuesday_of_may_2013() {
        DateTime expected = new DateTime(2013, 5, 21, 0, 0);
        Meetup meetup = new Meetup(5, 2013);
        assertThat(meetup.day(TUESDAY, MeetupSchedule.THIRD)).isEqualTo(expected);
    }

    @Test
    public void test_third_tuesday_of_june_2013() {
        DateTime expected = new DateTime(2013, 6, 18, 0, 0);
        Meetup meetup = new Meetup(6, 2013);
        assertThat(meetup.day(TUESDAY, MeetupSchedule.THIRD)).isEqualTo(expected);
    }

    @Test
    public void test_third_wednesday_of_july_2013() {
        DateTime expected = new DateTime(2013, 7, 17, 0, 0);
        Meetup meetup = new Meetup(7, 2013);
        assertThat(meetup.day(WEDNESDAY, MeetupSchedule.THIRD)).isEqualTo(expected);
    }

    @Test
    public void test_third_wednesday_of_august_2013() {
        DateTime expected = new DateTime(2013, 8, 21, 0, 0);
        Meetup meetup = new Meetup(8, 2013);
        assertThat(meetup.day(WEDNESDAY, MeetupSchedule.THIRD)).isEqualTo(expected);
    }

    @Test
    public void test_third_thursday_of_september_2013() {
        DateTime expected = new DateTime(2013, 9, 19, 0, 0);
        Meetup meetup = new Meetup(9, 2013);
        assertThat(meetup.day(THURSDAY, MeetupSchedule.THIRD)).isEqualTo(expected);
    }

    @Test
    public void test_third_thursday_of_october_2013() {
        DateTime expected = new DateTime(2013, 10, 17, 0, 0);
        Meetup meetup = new Meetup(10, 2013);
        assertThat(meetup.day(THURSDAY, MeetupSchedule.THIRD)).isEqualTo(expected);
    }

    @Test
    public void test_third_friday_of_november_2013() {
        DateTime expected = new DateTime(2013, 11, 15, 0, 0);
        Meetup meetup = new Meetup(11, 2013);
        assertThat(meetup.day(FRIDAY, MeetupSchedule.THIRD)).isEqualTo(expected);
    }

    @Test
    public void test_third_friday_of_december_2013() {
        DateTime expected = new DateTime(2013, 12, 20, 0, 0);
        Meetup meetup = new Meetup(12, 2013);
        assertThat(meetup.day(FRIDAY, MeetupSchedule.THIRD)).isEqualTo(expected);
    }

    @Test
    public void test_third_saturday_of_january_2013() {
        DateTime expected = new DateTime(2013, 1, 19, 0, 0);
        Meetup meetup = new Meetup(1, 2013);
        assertThat(meetup.day(SATURDAY, MeetupSchedule.THIRD)).isEqualTo(expected);
    }

    @Test
    public void test_third_saturday_of_february_2013() {
        DateTime expected = new DateTime(2013, 2, 16, 0, 0);
        Meetup meetup = new Meetup(2, 2013);
        assertThat(meetup.day(SATURDAY, MeetupSchedule.THIRD)).isEqualTo(expected);
    }

    @Test
    public void test_third_sunday_of_march_2013() {
        DateTime expected = new DateTime(2013, 3, 17, 0, 0);
        Meetup meetup = new Meetup(3, 2013);
        assertThat(meetup.day(SUNDAY, MeetupSchedule.THIRD)).isEqualTo(expected);
    }

    @Test
    public void test_third_sunday_of_april_2013() {
        DateTime expected = new DateTime(2013, 4, 21, 0, 0);
        Meetup meetup = new Meetup(4, 2013);
        assertThat(meetup.day(SUNDAY, MeetupSchedule.THIRD)).isEqualTo(expected);
    }

    @Test
    public void test_fourth_monday_of_march_2013() {
        DateTime expected = new DateTime(2013, 3, 25, 0, 0);
        Meetup meetup = new Meetup(3, 2013);
        assertThat(meetup.day(MONDAY, MeetupSchedule.FOURTH)).isEqualTo(expected);
    }

    @Test
    public void test_fourth_monday_of_april_2013() {
        DateTime expected = new DateTime(2013, 4, 22, 0, 0);
        Meetup meetup = new Meetup(4, 2013);
        assertThat(meetup.day(MONDAY, MeetupSchedule.FOURTH)).isEqualTo(expected);
    }

    @Test
    public void test_fourth_tuesday_of_may_2013() {
        DateTime expected = new DateTime(2013, 5, 28, 0, 0);
        Meetup meetup = new Meetup(5, 2013);
        assertThat(meetup.day(TUESDAY, MeetupSchedule.FOURTH)).isEqualTo(expected);
    }

    @Test
    public void test_fourth_tuesday_of_june_2013() {
        DateTime expected = new DateTime(2013, 6, 25, 0, 0);
        Meetup meetup = new Meetup(6, 2013);
        assertThat(meetup.day(TUESDAY, MeetupSchedule.FOURTH)).isEqualTo(expected);
    }

    @Test
    public void test_fourth_wednesday_of_july_2013() {
        DateTime expected = new DateTime(2013, 7, 24, 0, 0);
        Meetup meetup = new Meetup(7, 2013);
        assertThat(meetup.day(WEDNESDAY, MeetupSchedule.FOURTH)).isEqualTo(expected);
    }

    @Test
    public void test_fourth_wednesday_of_august_2013() {
        DateTime expected = new DateTime(2013, 8, 28, 0, 0);
        Meetup meetup = new Meetup(8, 2013);
        assertThat(meetup.day(WEDNESDAY, MeetupSchedule.FOURTH)).isEqualTo(expected);
    }

    @Test
    public void test_fourth_thursday_of_september_2013() {
        DateTime expected = new DateTime(2013, 9, 26, 0, 0);
        Meetup meetup = new Meetup(9, 2013);
        assertThat(meetup.day(THURSDAY, MeetupSchedule.FOURTH)).isEqualTo(expected);
    }

    @Test
    public void test_fourth_thursday_of_october_2013() {
        DateTime expected = new DateTime(2013, 10, 24, 0, 0);
        Meetup meetup = new Meetup(10, 2013);
        assertThat(meetup.day(THURSDAY, MeetupSchedule.FOURTH)).isEqualTo(expected);
    }

    @Test
    public void test_fourth_friday_of_november_2013() {
        DateTime expected = new DateTime(2013, 11, 22, 0, 0);
        Meetup meetup = new Meetup(11, 2013);
        assertThat(meetup.day(FRIDAY, MeetupSchedule.FOURTH)).isEqualTo(expected);
    }

    @Test
    public void test_fourth_friday_of_december_2013() {
        DateTime expected = new DateTime(2013, 12, 27, 0, 0);
        Meetup meetup = new Meetup(12, 2013);
        assertThat(meetup.day(FRIDAY, MeetupSchedule.FOURTH)).isEqualTo(expected);
    }

    @Test
    public void test_fourth_saturday_of_january_2013() {
        DateTime expected = new DateTime(2013, 1, 26, 0, 0);
        Meetup meetup = new Meetup(1, 2013);
        assertThat(meetup.day(SATURDAY, MeetupSchedule.FOURTH)).isEqualTo(expected);
    }

    @Test
    public void test_fourth_saturday_of_february_2013() {
        DateTime expected = new DateTime(2013, 2, 23, 0, 0);
        Meetup meetup = new Meetup(2, 2013);
        assertThat(meetup.day(SATURDAY, MeetupSchedule.FOURTH)).isEqualTo(expected);
    }

    @Test
    public void test_fourth_sunday_of_march_2013() {
        DateTime expected = new DateTime(2013, 3, 24, 0, 0);
        Meetup meetup = new Meetup(3, 2013);
        assertThat(meetup.day(SUNDAY, MeetupSchedule.FOURTH)).isEqualTo(expected);
    }

    @Test
    public void test_fourth_sunday_of_april_2013() {
        DateTime expected = new DateTime(2013, 4, 28, 0, 0);
        Meetup meetup = new Meetup(4, 2013);
        assertThat(meetup.day(SUNDAY, MeetupSchedule.FOURTH)).isEqualTo(expected);
    }

    @Test
    public void test_last_monday_of_march_2013() {
        DateTime expected = new DateTime(2013, 3, 25, 0, 0);
        Meetup meetup = new Meetup(3, 2013);
        assertThat(meetup.day(MONDAY, MeetupSchedule.LAST)).isEqualTo(expected);
    }

    @Test
    public void test_last_monday_of_april_2013() {
        DateTime expected = new DateTime(2013, 4, 29, 0, 0);
        Meetup meetup = new Meetup(4, 2013);
        assertThat(meetup.day(MONDAY, MeetupSchedule.LAST)).isEqualTo(expected);
    }

    @Test
    public void test_last_tuesday_of_may_2013() {
        DateTime expected = new DateTime(2013, 5, 28, 0, 0);
        Meetup meetup = new Meetup(5, 2013);
        assertThat(meetup.day(TUESDAY, MeetupSchedule.LAST)).isEqualTo(expected);
    }

    @Test
    public void test_last_tuesday_of_june_2013() {
        DateTime expected = new DateTime(2013, 6, 25, 0, 0);
        Meetup meetup = new Meetup(6, 2013);
        assertThat(meetup.day(TUESDAY, MeetupSchedule.LAST)).isEqualTo(expected);
    }

    @Test
    public void test_last_wednesday_of_july_2013() {
        DateTime expected = new DateTime(2013, 7, 31, 0, 0);
        Meetup meetup = new Meetup(7, 2013);
        assertThat(meetup.day(WEDNESDAY, MeetupSchedule.LAST)).isEqualTo(expected);
    }

    @Test
    public void test_last_wednesday_of_august_2013() {
        DateTime expected = new DateTime(2013, 8, 28, 0, 0);
        Meetup meetup = new Meetup(8, 2013);
        assertThat(meetup.day(WEDNESDAY, MeetupSchedule.LAST)).isEqualTo(expected);
    }

    @Test
    public void test_last_thursday_of_september_2013() {
        DateTime expected = new DateTime(2013, 9, 26, 0, 0);
        Meetup meetup = new Meetup(9, 2013);
        assertThat(meetup.day(THURSDAY, MeetupSchedule.LAST)).isEqualTo(expected);
    }

    @Test
    public void test_last_thursday_of_october_2013() {
        DateTime expected = new DateTime(2013, 10, 31, 0, 0);
        Meetup meetup = new Meetup(10, 2013);
        assertThat(meetup.day(THURSDAY, MeetupSchedule.LAST)).isEqualTo(expected);
    }

    @Test
    public void test_last_friday_of_november_2013() {
        DateTime expected = new DateTime(2013, 11, 29, 0, 0);
        Meetup meetup = new Meetup(11, 2013);
        assertThat(meetup.day(FRIDAY, MeetupSchedule.LAST)).isEqualTo(expected);
    }

    @Test
    public void test_last_friday_of_december_2013() {
        DateTime expected = new DateTime(2013, 12, 27, 0, 0);
        Meetup meetup = new Meetup(12, 2013);
        assertThat(meetup.day(FRIDAY, MeetupSchedule.LAST)).isEqualTo(expected);
    }

    @Test
    public void test_last_saturday_of_january_2013() {
        DateTime expected = new DateTime(2013, 1, 26, 0, 0);
        Meetup meetup = new Meetup(1, 2013);
        assertThat(meetup.day(SATURDAY, MeetupSchedule.LAST)).isEqualTo(expected);
    }

    @Test
    public void test_last_saturday_of_february_2013() {
        DateTime expected = new DateTime(2013, 2, 23, 0, 0);
        Meetup meetup = new Meetup(2, 2013);
        assertThat(meetup.day(SATURDAY, MeetupSchedule.LAST)).isEqualTo(expected);
    }

    @Test
    public void test_last_sunday_of_march_2013() {
        DateTime expected = new DateTime(2013, 3, 31, 0, 0);
        Meetup meetup = new Meetup(3, 2013);
        assertThat(meetup.day(SUNDAY, MeetupSchedule.LAST)).isEqualTo(expected);
    }

    @Test
    public void test_last_sunday_of_april_2013() {
        DateTime expected = new DateTime(2013, 4, 28, 0, 0);
        Meetup meetup = new Meetup(4, 2013);
        assertThat(meetup.day(SUNDAY, MeetupSchedule.LAST)).isEqualTo(expected);
    }
}

Finally I was able to solve the above problem but frankly I think its hardly a challenging problem, it needed to grok the external API which I didn't do after all. The code too I think is the worst one. The credit goes to this boring exercise. I didn't want to break the steak but I don't think it helped me in any way to increase my programming ability.

Anyways, the code improvement suggestions are most welcome.

Ref: Exercism

\$\endgroup\$
1
  • 1
    \$\begingroup\$ I have nothing constructive to say at all, but it took me a while to understand that the "teenth" note was referring to there being 7 days of the week and 7 numbers 13-19. Ha. \$\endgroup\$ May 5, 2016 at 17:51

1 Answer 1

3
\$\begingroup\$

Your current code is good and I only have minor comments:

  • Instead of plus(Period.days(7)), consider using plusWeeks(1), which is clearer.
  • To get the last day of week in a month, you're saying that you want to get the 6th day of week. This will work since there will always be less than 6 weeks in a month but it is a bit weird.

But... this is a perfect challenge for it:

Use the Java Time API!

Starting with Java 8, you don't need to use JodaTime. The Java Time API will greatly simplify your code.

For that, we need to consider what your code wants to do:

  • When the schedule is FIRST, you want to adjust the date into the 1st given day of week in the same month.
  • When the schedule is SECOND, you want to adjust the date into the 2nd given day of week in the same month.
  • When the schedule is THIRD, you want to adjust the date into the 3rd given day of week in the same month.
  • When the schedule is FOURTH, you want to adjust the date into the 4th given day of week in the same month.
  • When the schedule is LAST, you want to adjust the date into the last given day of week in the same month.
  • When the schedule is TEENTH, you want to adjust the date into the given day of week in the same month with a day between 13 and 19.

What this really means is that each of your enum should have a single property, which is function taking the day of week and returning the TemporalAdjuster to apply to it. This interface

Fortunately, the Java Time API already has some of those adjusters built-in:

What's missing is the one for the teenth case, but it is easy to build it:

private static TemporalAdjuster teenthDayOfWeekInMonth(DayOfWeek dayOfWeek) {
    return temporal -> {
        Temporal temp = temporal.with(TemporalAdjusters.firstInMonth(dayOfWeek));
        int weeksDiff = (19 - temp.get(ChronoField.DAY_OF_MONTH)) / 7;
        return temp.plus(weeksDiff, ChronoUnit.WEEKS);
    };
}

This adjuster works by setting a given date to the first given day of week in the same month (firstInMonth(dayOfWeek)), then it calculates how many weeks is there to add. We're looking to adjust into a date having a day of month between 13 and 19, so the number of weeks to add is simply the number of weeks between the 19 of that month and the current day of month (you may want to extract that 19 into a constant also).

Putting this into code, you get:

enum MeetupSchedule {
    
    FIRST(dayOfWeek -> TemporalAdjusters.firstInMonth(dayOfWeek)),
    SECOND(dayOfWeek -> TemporalAdjusters.dayOfWeekInMonth(2, dayOfWeek)),
    THIRD(dayOfWeek -> TemporalAdjusters.dayOfWeekInMonth(3, dayOfWeek)),
    FOURTH(dayOfWeek -> TemporalAdjusters.dayOfWeekInMonth(4, dayOfWeek)),
    LAST(dayOfWeek -> TemporalAdjusters.lastInMonth(dayOfWeek)),
    TEENTH(dayOfWeek -> teenthDayOfWeekInMonth(dayOfWeek));
    
    private static TemporalAdjuster teenthDayOfWeekInMonth(DayOfWeek dayOfWeek) {
        return temporal -> {
            Temporal temp = temporal.with(TemporalAdjusters.firstInMonth(dayOfWeek));
            int weeksDiff = (19 - temp.get(ChronoField.DAY_OF_MONTH)) / 7;
            return temp.plus(weeksDiff, ChronoUnit.WEEKS);
        };
    }
    
    private final Function<DayOfWeek, TemporalAdjuster> adjuster;
    
    private MeetupSchedule(Function<DayOfWeek, TemporalAdjuster> adjuster) {
        this.adjuster = adjuster;
    }

    public TemporalAdjuster schedule(DayOfWeek dayOfWeek) {
        return adjuster.apply(dayOfWeek);
    }
    
}

public class Meetup {

    private final LocalDate firstOfMonth;

    public Meetup(int month, int year) {
        firstOfMonth = LocalDate.of(year, month, 1);
    }

    public DateTime day(int dayOfWeek, MeetupSchedule schedule) {
        LocalDate result = firstOfMonth.with(schedule.schedule(DayOfWeek.of(dayOfWeek)));
        return new DateTime(result.getYear(), result.getMonthValue(), result.getDayOfMonth(), 0, 0);
    }
    
}

With such an implementation, all of the tests still pass. Of course, it is now a bit awkward since the test are written with JodaTime. It would be best to rewrite them to also use the Java Time API; in the above code, I adapted the implementation to return Joda classes so that the current tests still pass.

\$\endgroup\$
3
  • \$\begingroup\$ I have missed a important question. Memory! I have used ints everywhere shouldn't be a good idea to use byte for dayOfWeek, short for months and so on? \$\endgroup\$
    – CodeYogi
    May 6, 2016 at 15:13
  • \$\begingroup\$ @CodeYogi Actually, you want to use DayOfWeek :). With binary numeric promotion, short and byte are converted to int anyway, there's really no strong reason to use them (except for arrays): refer to this Stack Overflow answer. \$\endgroup\$
    – Tunaki
    May 6, 2016 at 15:20
  • 1
    \$\begingroup\$ :( now I feel jealous of these people? how the hell they retain all these stuffs in their brain? \$\endgroup\$
    – CodeYogi
    May 6, 2016 at 15:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.