|
| 1 | +# About |
| 2 | + |
| 3 | +While the core `java.time` classes like `LocalDate` and `LocalDateTime` provide basic methods for adding or subtracting time units, more complex date adjustments often require more sophisticated logic (e.g., finding the "second Tuesday of the month" or the "last day of the year"). |
| 4 | + |
| 5 | +The `java.time.temporal` package provides the [`TemporalAdjuster`][temporaladjuster-docs] functional interface for this purpose. It represents a strategy for adjusting a temporal object (like a `LocalDate`). The [`TemporalAdjusters`][temporaladjusters-docs] (note the plural 's') utility class contains static factory methods providing many common, predefined adjuster implementations. |
| 6 | + |
| 7 | +## Using TemporalAdjusters |
| 8 | + |
| 9 | +Temporal objects like `LocalDate` and `LocalDateTime` have a `with()` method that accepts a `TemporalAdjuster`. This method returns a *new* temporal object with the adjustment applied. |
| 10 | + |
| 11 | +```exercism/note |
| 12 | +Using `with(TemporalAdjuster)` returns a _new_ instance and does not update the existing instance, as all `java.time` temporal classes are immutable. |
| 13 | +``` |
| 14 | + |
| 15 | +```java |
| 16 | +import java.time.LocalDate; |
| 17 | +import java.time.Month; |
| 18 | +import java.time.DayOfWeek; |
| 19 | +import java.time.temporal.TemporalAdjusters; |
| 20 | + |
| 21 | +LocalDate date = LocalDate.of(2024, Month.AUGUST, 15); // 2024-08-15 (a Thursday) |
| 22 | + |
| 23 | +// Use a predefined adjuster from TemporalAdjusters |
| 24 | +LocalDate firstDayOfNextMonth = date.with(TemporalAdjusters.firstDayOfNextMonth()); |
| 25 | +// => 2024-09-01 |
| 26 | + |
| 27 | +LocalDate nextSunday = date.with(TemporalAdjusters.next(DayOfWeek.SUNDAY)); |
| 28 | +// => 2024-08-18 |
| 29 | +``` |
| 30 | + |
| 31 | +## Common Predefined Adjusters |
| 32 | + |
| 33 | +The `TemporalAdjusters` class provides many useful static methods. Here are some common examples: |
| 34 | + |
| 35 | +### Finding Ordinal Day of Week |
| 36 | + |
| 37 | +The `dayOfWeekInMonth(int ordinal, DayOfWeek dayOfWeek)` adjuster finds the nth occurrence of a specific day of the week within the month. |
| 38 | + |
| 39 | +```java |
| 40 | +LocalDate date = LocalDate.of(2024, Month.AUGUST, 15); // 2024-08-15 |
| 41 | + |
| 42 | +// Find the second Tuesday of August 2024 |
| 43 | +LocalDate secondTuesday = date.with(TemporalAdjusters.dayOfWeekInMonth(2, DayOfWeek.TUESDAY)); |
| 44 | +// => 2024-08-13 |
| 45 | + |
| 46 | +// Find the fourth Friday of August 2024 |
| 47 | +LocalDate fourthFriday = date.with(TemporalAdjusters.dayOfWeekInMonth(4, DayOfWeek.FRIDAY)); |
| 48 | +// => 2024-08-23 |
| 49 | +``` |
| 50 | + |
| 51 | +### Finding First/Last Day of Week in Month |
| 52 | + |
| 53 | +The `firstInMonth(DayOfWeek dayOfWeek)` and `lastInMonth(DayOfWeek dayOfWeek)` adjusters find the first or last occurrence of a day of the week within the month. |
| 54 | + |
| 55 | +```java |
| 56 | +LocalDate date = LocalDate.of(2024, Month.AUGUST, 15); // 2024-08-15 |
| 57 | + |
| 58 | +// Find the first Sunday of August 2024 |
| 59 | +LocalDate firstSunday = date.with(TemporalAdjusters.firstInMonth(DayOfWeek.SUNDAY)); |
| 60 | +// => 2024-08-04 |
| 61 | + |
| 62 | +// Find the last Monday of August 2024 |
| 63 | +LocalDate lastMonday = date.with(TemporalAdjusters.lastInMonth(DayOfWeek.MONDAY)); |
| 64 | +// => 2024-08-26 |
| 65 | +``` |
| 66 | + |
| 67 | +### Finding First/Last Day of Month/Year |
| 68 | + |
| 69 | +Simpler adjusters find the first or last day of the current month or year. |
| 70 | + |
| 71 | +```java |
| 72 | +LocalDate date = LocalDate.of(2024, Month.AUGUST, 15); // 2024-08-15 |
| 73 | + |
| 74 | +LocalDate firstDay = date.with(TemporalAdjusters.firstDayOfMonth()); |
| 75 | +// => 2024-08-01 |
| 76 | + |
| 77 | +LocalDate lastDay = date.with(TemporalAdjusters.lastDayOfMonth()); |
| 78 | +// => 2024-08-31 |
| 79 | + |
| 80 | +LocalDate lastDayOfYear = date.with(TemporalAdjusters.lastDayOfYear()); |
| 81 | +// => 2024-12-31 |
| 82 | +``` |
| 83 | + |
| 84 | +### Relative Adjustments (Next/Previous) |
| 85 | + |
| 86 | +Adjusters like `next()`, `previous()`, `nextOrSame()`, and `previousOrSame()` find the next or previous occurrence of a given day of the week, relative to the current date. |
| 87 | + |
| 88 | +* `next()`/`previous()`: Excludes the current date if it matches. |
| 89 | +* `nextOrSame()`/`previousOrSame()`: Includes the current date if it matches. |
| 90 | + |
| 91 | +```java |
| 92 | +LocalDate thursday = LocalDate.of(2024, Month.AUGUST, 15); // A Thursday |
| 93 | + |
| 94 | +// Find the next Sunday (exclusive) |
| 95 | +LocalDate nextSunday = thursday.with(TemporalAdjusters.next(DayOfWeek.SUNDAY)); |
| 96 | +// => 2024-08-18 |
| 97 | + |
| 98 | +// Find the next Thursday (exclusive) |
| 99 | +LocalDate nextThursday = thursday.with(TemporalAdjusters.next(DayOfWeek.THURSDAY)); |
| 100 | +// => 2024-08-22 |
| 101 | + |
| 102 | +// Find the next Thursday (inclusive) - returns the same date |
| 103 | +LocalDate nextOrSameThursday = thursday.with(TemporalAdjusters.nextOrSame(DayOfWeek.THURSDAY)); |
| 104 | +// => 2024-08-15 |
| 105 | + |
| 106 | +// Find the previous Monday (exclusive) |
| 107 | +LocalDate previousMonday = thursday.with(TemporalAdjusters.previous(DayOfWeek.MONDAY)); |
| 108 | +// => 2024-08-12 |
| 109 | +``` |
| 110 | +The `nextOrSame()` adjuster is particularly useful for finding dates in ranges like the "teenth" days (13th-19th) of a month, as seen in the Meetup exercise. Applying `nextOrSame(targetDayOfWeek)` to the 13th day of the month will find the correct "teenth" date. |
| 111 | + |
| 112 | +## Custom Adjusters |
| 113 | + |
| 114 | +Since `TemporalAdjuster` is a [`@FunctionalInterface`][functionalinterface-docs], you can create your own custom adjusters using lambda expressions for logic not covered by the predefined methods. The interface requires implementing a single method: `Temporal adjustInto(Temporal temporal)`. |
| 115 | + |
| 116 | +```java |
| 117 | +// Example: An adjuster that finds the next workday (Mon-Fri), skipping weekends. |
| 118 | +TemporalAdjuster nextWorkday = temporal -> { |
| 119 | + LocalDate result = (LocalDate) temporal; // Assuming input is LocalDate |
| 120 | + do { |
| 121 | + result = result.plusDays(1); |
| 122 | + } while (result.getDayOfWeek() == DayOfWeek.SATURDAY || result.getDayOfWeek() == DayOfWeek.SUNDAY); |
| 123 | + return result; |
| 124 | +}; |
| 125 | + |
| 126 | +LocalDate friday = LocalDate.of(2024, Month.AUGUST, 16); |
| 127 | +LocalDate nextWorkdayDate = friday.with(nextWorkday); |
| 128 | +// => 2024-08-19 (Monday) |
| 129 | + |
| 130 | +LocalDate sunday = LocalDate.of(2024, Month.AUGUST, 18); |
| 131 | +LocalDate nextWorkdayFromSunday = sunday.with(nextWorkday); |
| 132 | +// => 2024-08-19 (Monday) |
| 133 | +``` |
| 134 | +While custom adjusters are powerful, the predefined adjusters in the `TemporalAdjusters` class cover a wide range of common use cases. |
| 135 | + |
| 136 | +[temporaladjuster-docs]: https://docs.oracle.com/javase/8/docs/api/java/time/temporal/TemporalAdjuster.html |
| 137 | +[temporaladjusters-docs]: https://docs.oracle.com/javase/8/docs/api/java/time/temporal/TemporalAdjusters.html |
| 138 | +[localdate-docs]: https://docs.oracle.com/javase/8/docs/api/java/time/LocalDate.html |
| 139 | +[dayofweek-docs]: https://docs.oracle.com/javase/8/docs/api/java/time/DayOfWeek.html |
| 140 | +[functionalinterface-docs]: https://docs.oracle.com/javase/8/docs/api/java/lang/FunctionalInterface.html |
0 commit comments