0
# Temporal Iteration
1
2
Functionality for iterating over temporal ranges with support for custom units, step sizes, and calculating periods or durations between temporal objects.
3
4
## Capabilities
5
6
### Temporal Iteration Methods
7
8
Core iteration functionality that works with all Temporal subtypes.
9
10
```groovy { .api }
11
/**
12
* Iterates from this to the target Temporal, inclusive, incrementing by one default unit each iteration.
13
* Default units: DAYS for dates, MONTHS for YearMonth, YEARS for Year, SECONDS for others.
14
*/
15
void upto(Temporal from, Temporal to, Closure closure)
16
17
/**
18
* Iterates from this to the target Temporal, inclusive, incrementing by one specified unit each iteration.
19
*/
20
void upto(Temporal from, Temporal to, TemporalUnit unit, Closure closure)
21
22
/**
23
* Iterates from this to the target Temporal, inclusive, decrementing by one default unit each iteration.
24
*/
25
void downto(Temporal from, Temporal to, Closure closure)
26
27
/**
28
* Iterates from this to the target Temporal, inclusive, decrementing by one specified unit each iteration.
29
*/
30
void downto(Temporal from, Temporal to, TemporalUnit unit, Closure closure)
31
32
/**
33
* Returns a TemporalAmount (Duration or Period) between this and another Temporal.
34
* Returns Period for dates, Duration for times.
35
*/
36
TemporalAmount rightShift(Temporal self, Temporal other)
37
```
38
39
### TemporalAccessor Operations
40
41
Generic field access operations for all temporal objects that implement TemporalAccessor.
42
43
```groovy { .api }
44
/**
45
* Supports the subscript operator for accessing temporal fields.
46
* Equivalent to calling getLong(TemporalField).
47
*/
48
long getAt(TemporalAccessor self, TemporalField field)
49
```
50
51
### TemporalAmount Operations
52
53
Generic unit access operations for temporal amounts (Duration and Period).
54
55
```groovy { .api }
56
/**
57
* Supports the subscript operator for accessing temporal units.
58
* Equivalent to calling get(TemporalUnit).
59
*/
60
long getAt(TemporalAmount self, TemporalUnit unit)
61
```
62
63
**Usage Examples:**
64
65
```groovy
66
import java.time.*
67
import java.time.temporal.ChronoUnit
68
import java.time.temporal.ChronoField
69
70
// Date iteration with default unit (days)
71
def start = LocalDate.of(2024, 1, 1)
72
def end = start + 5
73
start.upto(end) { date ->
74
println "${date} is ${date.dayOfWeek}"
75
}
76
77
// Date iteration with custom unit
78
start.upto(end, ChronoUnit.DAYS) { date ->
79
println "Processing: ${date}"
80
}
81
82
// Time iteration (default unit is seconds)
83
def startTime = LocalTime.of(10, 0)
84
def endTime = startTime + 5 // 5 seconds later
85
startTime.upto(endTime) { time ->
86
println time
87
}
88
89
// Year iteration (default unit is years)
90
def startYear = Year.of(2020)
91
def endYear = Year.of(2024)
92
startYear.upto(endYear) { year ->
93
println "Year: ${year.value}"
94
}
95
96
// Downto iteration
97
end.downto(start) { date ->
98
println "Counting down: ${date}"
99
}
100
101
// Period calculation using right shift operator
102
def period = start >> end // Returns Period
103
println "Period: ${period.days} days"
104
105
// Duration calculation
106
def duration = startTime >> endTime // Returns Duration
107
println "Duration: ${duration.seconds} seconds"
108
109
// TemporalAccessor field access using subscript operator
110
def now = LocalDateTime.now()
111
def hourOfDay = now[ChronoField.HOUR_OF_DAY] // Get hour field
112
def dayOfYear = now[ChronoField.DAY_OF_YEAR] // Get day of year
113
def month = now[ChronoField.MONTH_OF_YEAR] // Get month
114
115
println "Current time: ${now}"
116
println "Hour of day: ${hourOfDay}"
117
println "Day of year: ${dayOfYear}"
118
println "Month: ${month}"
119
120
// TemporalAmount unit access using subscript operator
121
def period = Period.of(2, 3, 15) // 2 years, 3 months, 15 days
122
def years = period[ChronoUnit.YEARS] // Get years component
123
def months = period[ChronoUnit.MONTHS] // Get months component
124
def days = period[ChronoUnit.DAYS] // Get days component
125
126
println "Period: ${period}"
127
println "Years: ${years}"
128
println "Months: ${months}"
129
println "Days: ${days}"
130
131
def duration2 = Duration.ofHours(5).plusMinutes(30)
132
def hours = duration2[ChronoUnit.HOURS] // Get hours component
133
def minutes = duration2[ChronoUnit.MINUTES] // Get total minutes
134
135
println "Duration: ${duration2}"
136
println "Total hours: ${hours}"
137
println "Total minutes: ${minutes}"
138
```
139
140
### Date Range Iteration
141
142
Specialized iteration methods for date-based temporal types.
143
144
```groovy { .api }
145
/**
146
* Returns a LocalDate one day after this date.
147
*/
148
LocalDate next(LocalDate self)
149
150
/**
151
* Returns a LocalDate one day before this date.
152
*/
153
LocalDate previous(LocalDate self)
154
155
/**
156
* Returns a Period between this date and another date.
157
*/
158
Period rightShift(LocalDate self, LocalDate other)
159
```
160
161
**Usage Examples:**
162
163
```groovy
164
import java.time.*
165
166
def today = LocalDate.now()
167
def dates = []
168
169
// Collect dates using next() method
170
def current = today
171
5.times {
172
dates << current
173
current = current.next()
174
}
175
176
// Create date ranges for iteration
177
def startDate = LocalDate.of(2024, 1, 1)
178
def endDate = LocalDate.of(2024, 1, 7)
179
180
// Standard Groovy range operator works with dates
181
(startDate..endDate).each { date ->
182
println "${date}: ${date.dayOfWeek}"
183
}
184
185
// Calculate period between dates
186
def period = startDate >> endDate
187
println "Difference: ${period.days} days"
188
```
189
190
### Year and Month Iteration
191
192
Iteration support for year and month-based temporal types.
193
194
```groovy { .api }
195
/**
196
* Returns a Year one year after this year.
197
*/
198
Year next(Year self)
199
200
/**
201
* Returns a Year one year before this year.
202
*/
203
Year previous(Year self)
204
205
/**
206
* Returns a Period between the first day of this year and the first day of the target year.
207
*/
208
Period rightShift(Year self, Year other)
209
210
/**
211
* Returns a YearMonth one month after this year-month.
212
*/
213
YearMonth next(YearMonth self)
214
215
/**
216
* Returns a YearMonth one month before this year-month.
217
*/
218
YearMonth previous(YearMonth self)
219
220
/**
221
* Returns a Period between the first day of this year-month and the first day of the target year-month.
222
*/
223
Period rightShift(YearMonth self, YearMonth other)
224
```
225
226
**Usage Examples:**
227
228
```groovy
229
import java.time.*
230
231
// Year iteration
232
def startYear = Year.of(2020)
233
def endYear = Year.of(2024)
234
235
startYear.upto(endYear) { year ->
236
println "Processing year: ${year.value}"
237
println "Leap year: ${year.isLeap()}"
238
}
239
240
// YearMonth iteration
241
def startMonth = YearMonth.of(2024, 1)
242
def endMonth = YearMonth.of(2024, 6)
243
244
startMonth.upto(endMonth) { yearMonth ->
245
println "${yearMonth.month} ${yearMonth.year}: ${yearMonth.lengthOfMonth()} days"
246
}
247
248
// Calculate periods
249
def yearPeriod = startYear >> endYear
250
def monthPeriod = startMonth >> endMonth
251
println "Years between: ${yearPeriod.years}"
252
println "Months between: ${monthPeriod.months}"
253
```
254
255
### Custom Unit Iteration
256
257
Advanced iteration with custom temporal units and step sizes.
258
259
**Usage Examples:**
260
261
```groovy
262
import java.time.*
263
import java.time.temporal.ChronoUnit
264
265
def start = LocalDateTime.of(2024, 1, 1, 0, 0)
266
def end = start.plusDays(2)
267
268
// Iterate by hours
269
start.upto(end, ChronoUnit.HOURS) { dateTime ->
270
println dateTime.format('yyyy-MM-dd HH:mm')
271
}
272
273
// Iterate by 6-hour increments (not directly supported, but can be achieved)
274
def current = start
275
while (current <= end) {
276
println current.format('yyyy-MM-dd HH:mm')
277
current = current.plus(6, ChronoUnit.HOURS)
278
}
279
280
// Week-based iteration
281
def monday = LocalDate.of(2024, 1, 1) // Assume this is a Monday
282
def endOfMonth = LocalDate.of(2024, 1, 31)
283
284
monday.upto(endOfMonth, ChronoUnit.WEEKS) { date ->
285
println "Week starting: ${date}"
286
}
287
```
288
289
### Range Operators and Integration
290
291
Integration with Groovy's range operators for natural iteration syntax.
292
293
**Usage Examples:**
294
295
```groovy
296
import java.time.*
297
298
def start = LocalDate.of(2024, 1, 1)
299
def end = start + 10
300
301
// Using Groovy range operator (works because of next/previous methods)
302
(start..end).each { date ->
303
if (date.dayOfWeek == DayOfWeek.SUNDAY) {
304
println "Sunday: ${date}"
305
}
306
}
307
308
// Using step with ranges (for every other day)
309
(start..end).step(2) { date ->
310
println "Every other day: ${date}"
311
}
312
313
// Reverse ranges
314
(end..start).each { date ->
315
println "Counting down: ${date}"
316
}
317
```
318
319
### Error Handling and Constraints
320
321
Important behavior and limitations for temporal iteration.
322
323
**Exceptions Thrown:**
324
325
- `GroovyRuntimeException` - If the start value is later than the end value for `upto()`
326
- `GroovyRuntimeException` - If the start value is earlier than the end value for `downto()`
327
- `GroovyRuntimeException` - If the temporal types don't match
328
- `GroovyRuntimeException` - If temporal type doesn't support the specified unit
329
330
**Usage Examples:**
331
332
```groovy
333
import java.time.*
334
335
def start = LocalDate.of(2024, 1, 5)
336
def end = LocalDate.of(2024, 1, 1) // Earlier than start
337
338
try {
339
// This will throw GroovyRuntimeException
340
start.upto(end) { date ->
341
println date
342
}
343
} catch (GroovyRuntimeException e) {
344
println "Cannot iterate backwards with upto(): ${e.message}"
345
}
346
347
// Use downto() instead
348
start.downto(end) { date ->
349
println "Going backwards: ${date}"
350
}
351
352
// Type mismatch will also cause errors
353
def date = LocalDate.now()
354
def time = LocalTime.now()
355
356
try {
357
// This will throw GroovyRuntimeException
358
date.upto(time) { /* won't work */ }
359
} catch (GroovyRuntimeException e) {
360
println "Type mismatch: ${e.message}"
361
}
362
```