Learn to use Java TemporalAdjusters that help with complex date-time calculations such as calculating recurring meeting dates, processing weekly reports, sending automated monthly report outs, etc.
1. Overview
In the new Java Date API, the Temporal interface represents a date, time, or a combination of both. For example, LocalDate, LocalDateTime etc.
The TemporalAdjuster allows us to do complex date-time calculations such as:
- finding the next Sunday
- second and last Saturday of the month
- handling recurring events etc.
The names of most of these tell you directly what they do. If the provided adjusters do not solve specific business requirements, we can build our custom TemporalAdjuster.
We can then apply it to any temporal object using the with()
method of that date/time object. For example, there is always a weekly meeting every Monday in a team then calculate the list of dates for the next five meetings.
LocalDate localDate = LocalDate.of(2020, 5, 9);
List<LocalDate> meetingDates = getWeeklyMeetingDates(localDate, 5);
private static List<LocalDate> getWeeklyMeetingDates(LocalDate localDate, int count) {
return Stream.iterate(localDate.with(TemporalAdjusters.next(DayOfWeek.MONDAY)),
date -> date.with(TemporalAdjusters.next(DayOfWeek.MONDAY)))
.limit(count)
.collect(Collectors.toList());
}
Program output.
[2020-05-11,
2020-05-18,
2020-05-25,
2020-06-01,
2020-06-08]
2. Predefined Adjusters
This is the list of default provided adjusters for easy use. For the detailed information, visit the official Java Doc.
Adjuster | Description |
---|---|
firstDayOfMonth() | returns a new date set it to the first day of the current month. |
lastDayOfMonth() | returns a new date set to the last day of the current month. |
firstDayOfNextMonth() | returns a new date set to the first day of the next month. |
firstDayOfYear() | returns a new date set to the first day of the current year. |
lastDayOfYear() | returns a new date set to the last day of the current year. |
firstDayOfNextYear() | returns a new date set to the first day of the next year. |
firstInMonth() | returns the date to the first occurrence of the specified day-of-week before the adjusted date. |
lastInMonth() | returns a new date in the same month with the last matching day-of-week. |
dayOfWeekInMonth() | returns a new date in the same month with the ordinal day-of-week. |
next() | returns the date to the first occurrence of the specified day-of-week after the date being adjusted. |
previous() | returns the date to the first occurrence of the specified day-of-week before the date being adjusted. |
For example, the following Java program demonstrates the use of these methods for simple usecases.
LocalDate localdate = localdate;
// Calculate the first day of the current month
LocalDate firstDayOfMonth = localdate.with(TemporalAdjusters.firstDayOfMonth());
// Calculate the last day of the current month
LocalDate lastDayOfMonth = localdate.with(TemporalAdjusters.lastDayOfMonth());
// Calculate the first day of the next month
LocalDate firstDayNextMonth = localdate.with(TemporalAdjusters.firstDayOfNextMonth());
// Calculate the next Friday
LocalDate nextFriday = localdate.with(TemporalAdjusters.next(DayOfWeek.FRIDAY));
// Calculate the previous Wednesday
LocalDate previousWednesday = localdate.with(TemporalAdjusters.previous(DayOfWeek.WEDNESDAY));
// Calculate the last Sunday of the current month
LocalDate lastSundayOfMonth = localdate.with(TemporalAdjusters.lastInMonth(DayOfWeek.SUNDAY));
// Calculate the first Monday of the current year
LocalDate firstMondayOfYear = localdate
.with(TemporalAdjusters.firstDayOfYear())
.with(TemporalAdjusters.nextOrSame(DayOfWeek.MONDAY));
// Calculate the next working day (assuming Saturday and Sunday are weekends)
LocalDate nextWorkingDay = localdate.with(date -> {
DayOfWeek day = date.getDayOfWeek();
return day == DayOfWeek.FRIDAY ? date.plusDays(3) : day == DayOfWeek.SATURDAY ? date.plusDays(2) : date.plusDays(1);
});
3. Custom Adjusters
Create a custom adjuster that can be used to adjust recurring dates according to business logic. It can be done in two ways:
- Implement
TemporalAdjuster
interface - Inline Lambda expression
//1. With TemporalAdjuster interface
class NextBirthDay implements TemporalAdjuster
{
@Override
public Temporal adjustInto(Temporal temporal)
{
return temporal.with(ChronoField.MONTH_OF_YEAR, 11)
.with(ChronoField.DAY_OF_MONTH, 22);
}
}
//2. With lambda expressions
TemporalAdjuster temporalAdjuster = t -> t.plus(Period.ofDays(7));
4. Conclusion
In this tutorial, we learned about the TemporalAdjuster interface and how to use its factory methods to manipulate the dates for complex usecases.
Comments