70. Finding the first/last day of the month

The proper solution to this problem will rely on JDK 8's, Temporal and TemporalAdjuster interfaces.

The Temporal interface sits behind representations of date-time. In other words, classes that represent a date and/or a time implement this interface. For example, the following classes are just a few that implement this interface:

  • LocalDate (date without a time zone in the ISO-8601 calendar system)
  • LocalTime (time without a time zone in the ISO-8601 calendar system)
  • LocalDateTime (date-time without a time zone in the ISO-8601 calendar system)
  • ZonedDateTime (date-time with a time zone in the ISO-8601 calendar system), and so on
  • OffsetDateTime (date-time with an offset from UTC/Greenwich in the ISO-8601 calendar system)
  • HijrahDate (date in the Hijrah calendar system)

The TemporalAdjuster class is a functional interface that defines strategies that can be used to adjust a Temporal object. Beside the possibility of defining custom strategies, the TemporalAdjuster class provides several predefined strategies, as follows (the documentation contains the entire list, which is pretty impressive):

  • firstDayOfMonth() (return the first day of the current month)
  • lastDayOfMonth() (return the last day of the current month)
  • firstDayOfNextMonth() (return the first day of the next month)
  • firstDayOfNextYear() (return the first day of the next year)

Notice that the first two adjusters in the preceding list are exactly the ones needed by this problem.

Consider a fix—LocalDate:

LocalDate date = LocalDate.of(2019, Month.FEBRUARY, 27);

And, let's see when the first/last days of February are:

// 2019-02-01
LocalDate firstDayOfFeb
= date.with(TemporalAdjusters.firstDayOfMonth());

// 2019-02-28
LocalDate lastDayOfFeb
= date.with(TemporalAdjusters.lastDayOfMonth());

Looks like relying on the predefined strategies is pretty simple. But, let's assume that the problem requests you to find the date that's 21 days after February, 27 2019, which is March 20, 2019. For this problem there is no predefined strategy, therefore a custom strategy is needed. A solution to this problem can rely on a lambda expression, as in the following helper method:

public static LocalDate getDayAfterDays(
LocalDate startDate, int days) {

Period period = Period.ofDays(days);
TemporalAdjuster ta = p -> p.plus(period);
LocalDate endDate = startDate.with(ta);

return endDate;
}

If this method lives in a class named DateTimes, then the following call will return the expected result:

// 2019-03-20
LocalDate datePlus21Days = DateTimes.getDayAfterDays(date, 21);

Following the same technique, but relying on the static factory method ofDateAdjuster(), the following snippet of code defines a static adjuster that returns the next date that falls on a Saturday:

static TemporalAdjuster NEXT_SATURDAY 
= TemporalAdjusters.ofDateAdjuster(today -> {

DayOfWeek dayOfWeek = today.getDayOfWeek();

if (dayOfWeek == DayOfWeek.SATURDAY) {
return today;
}

if (dayOfWeek == DayOfWeek.SUNDAY) {
return today.plusDays(6);
}

return today.plusDays(6 - dayOfWeek.getValue());
});

Let's call this method for February 27, 2019 (the next Saturday is on March 2, 2019):

// 2019-03-02
LocalDate nextSaturday = date.with(NEXT_SATURDAY);

Finally, this functional interface defines an abstract method named adjustInto(). This method can be overridden in custom implementations by passing a Temporal object to it, as follows:

public class NextSaturdayAdjuster implements TemporalAdjuster {

@Override
public Temporal adjustInto(Temporal temporal) {

DayOfWeek dayOfWeek = DayOfWeek
.of(temporal.get(ChronoField.DAY_OF_WEEK));

if (dayOfWeek == DayOfWeek.SATURDAY) {
return temporal;
}

if (dayOfWeek == DayOfWeek.SUNDAY) {
return temporal.plus(6, ChronoUnit.DAYS);
}

return temporal.plus(6 - dayOfWeek.getValue(), ChronoUnit.DAYS);
}
}

Here is the usage example:

NextSaturdayAdjuster nsa = new NextSaturdayAdjuster();

// 2019-03-02
LocalDate nextSaturday = date.with(nsa);
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset