Skip to content

Commit 11890c8

Browse files
author
Jegors Cemisovs
committed
Add Temporal Adjusters concept with documentation and config
Introduces the `Temporal Adjusters` concept, including an overview, examples, and links for Java's `TemporalAdjuster` and `TemporalAdjusters` utilities. Updates `config.json` to include the concept and a new exercise, "Meetup," with relevant prerequisites.
1 parent a606586 commit 11890c8

File tree

5 files changed

+320
-0
lines changed

5 files changed

+320
-0
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"blurb": "Perform common and complex date adjustments using Java's `TemporalAdjuster` interface and `TemporalAdjusters` utility class.",
3+
"authors": ["rabestro"],
4+
"contributors": []
5+
}
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
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
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
# Introduction
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
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[
2+
{
3+
"url": "https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/time/temporal/TemporalAdjuster.html",
4+
"description": "TemporalAdjuster interface documentation"
5+
},
6+
{
7+
"url": "https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/time/temporal/TemporalAdjusters.html",
8+
"description": "TemporalAdjusters utility class documentation"
9+
},
10+
{
11+
"url": "https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/time/temporal/Temporal.html",
12+
"description": "Temporal interface documentation"
13+
},
14+
{
15+
"url": "https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/time/DayOfWeek.html",
16+
"description": "DayOfWeek enum documentation"
17+
}
18+
]

config.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,18 @@
321321
"foreach-loops",
322322
"generic-types"
323323
]
324+
},
325+
{
326+
"slug": "meetup",
327+
"name": "Meetup",
328+
"uuid": "602511d5-7e89-4def-b072-4dd311816810",
329+
"concepts": [
330+
"temporal-adjusters"
331+
],
332+
"prerequisites": [
333+
"datetime",
334+
"enums"
335+
]
324336
}
325337
],
326338
"practice": [
@@ -1971,6 +1983,11 @@
19711983
"uuid": "78f3c7b2-cb9c-4d21-8cb4-7106a188f713",
19721984
"slug": "ternary-operators",
19731985
"name": "Ternary Operators"
1986+
},
1987+
{
1988+
"uuid": "cc7a06d1-ddb3-4722-88a5-f762254d664e",
1989+
"slug": "temporal-adjusters",
1990+
"name": "Temporal Adjusters"
19741991
}
19751992
],
19761993
"key_features": [

0 commit comments

Comments
 (0)