0
# Enum Extensions
1
2
Enhanced functionality for `DayOfWeek` and `Month` enums including arithmetic operations, utility methods, and integration with other temporal types through operator overloading and factory methods.
3
4
## Capabilities
5
6
### DayOfWeek Operations
7
8
Enhanced functionality for the `DayOfWeek` enum with arithmetic and utility methods.
9
10
```groovy { .api }
11
/**
12
* Returns the DayOfWeek that is the specified number of days after this day.
13
*/
14
DayOfWeek plus(DayOfWeek self, int days)
15
16
/**
17
* Returns the DayOfWeek that is the specified number of days before this day.
18
*/
19
DayOfWeek minus(DayOfWeek self, int days)
20
21
/**
22
* Returns true if this day of the week is a weekend day (Saturday or Sunday).
23
*/
24
boolean isWeekend(DayOfWeek self)
25
26
/**
27
* Returns true if this day of the week is a weekday (Monday through Friday).
28
*/
29
boolean isWeekday(DayOfWeek self)
30
```
31
32
**Usage Examples:**
33
34
```groovy
35
import java.time.*
36
37
def monday = DayOfWeek.MONDAY
38
39
// Arithmetic operations
40
def wednesday = monday + 2 // Add 2 days -> WEDNESDAY
41
def saturday = monday + 5 // Add 5 days -> SATURDAY
42
def previousFriday = monday - 3 // Subtract 3 days -> FRIDAY
43
44
println "Monday + 2 days = ${wednesday}"
45
println "Monday + 5 days = ${saturday}"
46
println "Monday - 3 days = ${previousFriday}"
47
48
// Week wrapping
49
def sunday = DayOfWeek.SATURDAY + 1 // Wraps to SUNDAY
50
def friday = DayOfWeek.SUNDAY - 2 // Wraps to FRIDAY
51
52
println "Saturday + 1 = ${sunday}"
53
println "Sunday - 2 = ${friday}"
54
55
// Large numbers wrap around the week
56
def nextMonday = monday + 7 // 7 days later -> MONDAY
57
def sameDay = monday + 14 // 14 days later -> MONDAY
58
59
println "Monday + 7 days = ${nextMonday}"
60
println "Monday + 14 days = ${sameDay}"
61
62
// Weekend/weekday checking
63
def days = DayOfWeek.values()
64
days.each { day ->
65
def type = day.isWeekend() ? 'weekend' : 'weekday'
66
println "${day} is a ${type}"
67
}
68
69
// Practical usage
70
def today = LocalDate.now().dayOfWeek
71
if (today.isWeekday()) {
72
println "Today (${today}) is a work day"
73
} else {
74
println "Today (${today}) is a weekend"
75
}
76
77
// Find next business day
78
def nextBusinessDay = today
79
while (nextBusinessDay.isWeekend()) {
80
nextBusinessDay = nextBusinessDay + 1
81
}
82
println "Next business day after ${today}: ${nextBusinessDay}"
83
```
84
85
### Month Operations
86
87
Enhanced functionality for the `Month` enum with arithmetic operations and temporal object creation.
88
89
```groovy { .api }
90
/**
91
* Returns the Month that is the specified number of months after this month.
92
*/
93
Month plus(Month self, int months)
94
95
/**
96
* Returns the Month that is the specified number of months before this month.
97
*/
98
Month minus(Month self, int months)
99
100
/**
101
* Creates a MonthDay at the provided day of the month.
102
*/
103
MonthDay leftShift(Month self, int dayOfMonth)
104
105
/**
106
* Creates a YearMonth with the provided Year.
107
*/
108
YearMonth leftShift(Month self, Year year)
109
```
110
111
**Usage Examples:**
112
113
```groovy
114
import java.time.*
115
116
def january = Month.JANUARY
117
118
// Arithmetic operations
119
def march = january + 2 // Add 2 months -> MARCH
120
def november = january - 2 // Subtract 2 months -> NOVEMBER (wraps to previous year)
121
def april = january + 3 // Add 3 months -> APRIL
122
123
println "January + 2 months = ${march}"
124
println "January - 2 months = ${november}"
125
println "January + 3 months = ${april}"
126
127
// Year wrapping
128
def december = Month.JANUARY - 1 // Wraps to DECEMBER
129
def february = Month.DECEMBER + 2 // Wraps to FEBRUARY
130
131
println "January - 1 month = ${december}"
132
println "December + 2 months = ${february}"
133
134
// Large numbers wrap around
135
def sameMonth = january + 12 // 12 months later -> JANUARY
136
def nextJanuary = january + 13 // 13 months later -> FEBRUARY
137
138
println "January + 12 months = ${sameMonth}"
139
println "January + 13 months = ${nextJanuary}"
140
141
// Creating temporal objects using left shift operator
142
def newYearsEve = Month.DECEMBER << 31 // MonthDay: December 31st
143
def christmas = Month.DECEMBER << 25 // MonthDay: December 25th
144
def valentines = Month.FEBRUARY << 14 // MonthDay: February 14th
145
146
println "New Year's Eve: ${newYearsEve}"
147
println "Christmas: ${christmas}"
148
println "Valentine's Day: ${valentines}"
149
150
// Creating YearMonth objects
151
def currentYear = Year.now()
152
def januaryThisYear = Month.JANUARY << currentYear // YearMonth: January of current year
153
def december2024 = Month.DECEMBER << Year.of(2024) // YearMonth: December 2024
154
155
println "January this year: ${januaryThisYear}"
156
println "December 2024: ${december2024}"
157
158
// Practical usage - seasonal operations
159
def seasons = [
160
'Spring': [Month.MARCH, Month.APRIL, Month.MAY],
161
'Summer': [Month.JUNE, Month.JULY, Month.AUGUST],
162
'Fall': [Month.SEPTEMBER, Month.OCTOBER, Month.NOVEMBER],
163
'Winter': [Month.DECEMBER, Month.JANUARY, Month.FEBRUARY]
164
]
165
166
def currentMonth = LocalDate.now().month
167
def currentSeason = seasons.find { season, months ->
168
months.contains(currentMonth)
169
}?.key
170
171
println "Current month ${currentMonth} is in ${currentSeason}"
172
173
// Working with month lengths
174
def year2024 = Year.of(2024)
175
Month.values().each { month ->
176
def monthLength = (month << year2024).lengthOfMonth()
177
println "${month}: ${monthLength} days in 2024"
178
}
179
```
180
181
### Integration with Temporal Objects
182
183
How enum extensions integrate with other temporal types for comprehensive date/time operations.
184
185
**Usage Examples:**
186
187
```groovy
188
import java.time.*
189
190
// Using DayOfWeek with LocalDate operations
191
def findNextOccurrence = { LocalDate startDate, DayOfWeek targetDay ->
192
def current = startDate
193
while (current.dayOfWeek != targetDay) {
194
current = current + 1
195
}
196
return current
197
}
198
199
def today = LocalDate.now()
200
def nextFriday = findNextOccurrence(today, DayOfWeek.FRIDAY)
201
def nextMonday = findNextOccurrence(today, DayOfWeek.MONDAY)
202
203
println "Next Friday: ${nextFriday}"
204
println "Next Monday: ${nextMonday}"
205
206
// Using Month with date creation
207
def createHolidays = { Year year ->
208
return [
209
'New Year': (Month.JANUARY << 1) << year, // January 1st
210
'Independence Day': (Month.JULY << 4) << year, // July 4th
211
'Christmas': (Month.DECEMBER << 25) << year // December 25th
212
]
213
}
214
215
def holidays2024 = createHolidays(Year.of(2024))
216
holidays2024.each { name, date ->
217
println "${name}: ${date} (${date.dayOfWeek})"
218
}
219
220
// Business day calculations
221
def isBusinessDay = { LocalDate date ->
222
return date.dayOfWeek.isWeekday()
223
}
224
225
def addBusinessDays = { LocalDate startDate, int businessDays ->
226
def current = startDate
227
def added = 0
228
229
while (added < businessDays) {
230
current = current + 1
231
if (isBusinessDay(current)) {
232
added++
233
}
234
}
235
return current
236
}
237
238
def startDate = LocalDate.of(2024, 12, 20) // Friday
239
def fiveDaysLater = addBusinessDays(startDate, 5)
240
241
println "5 business days after ${startDate}: ${fiveDaysLater}"
242
243
// Monthly reporting periods
244
def getQuarterMonths = { int quarter ->
245
def baseMonth = Month.JANUARY + ((quarter - 1) * 3)
246
return [
247
baseMonth,
248
baseMonth + 1,
249
baseMonth + 2
250
]
251
}
252
253
(1..4).each { quarter ->
254
def months = getQuarterMonths(quarter)
255
println "Q${quarter}: ${months.join(', ')}"
256
}
257
258
// Seasonal date ranges
259
def getSeason = { Month month ->
260
switch (month) {
261
case [Month.DECEMBER, Month.JANUARY, Month.FEBRUARY]:
262
return 'Winter'
263
case [Month.MARCH, Month.APRIL, Month.MAY]:
264
return 'Spring'
265
case [Month.JUNE, Month.JULY, Month.AUGUST]:
266
return 'Summer'
267
case [Month.SEPTEMBER, Month.OCTOBER, Month.NOVEMBER]:
268
return 'Fall'
269
}
270
}
271
272
def currentMonth = LocalDate.now().month
273
def season = getSeason(currentMonth)
274
println "Current season for ${currentMonth}: ${season}"
275
276
// Weekend vs weekday analysis
277
def analyzeWeek = { LocalDate startOfWeek ->
278
def days = (0..6).collect { startOfWeek + it }
279
def weekdays = days.findAll { it.dayOfWeek.isWeekday() }
280
def weekends = days.findAll { it.dayOfWeek.isWeekend() }
281
282
return [
283
weekdays: weekdays,
284
weekends: weekends,
285
totalDays: days.size(),
286
workingDays: weekdays.size()
287
]
288
}
289
290
def monday = LocalDate.now().with(DayOfWeek.MONDAY) // Start of current week
291
def weekAnalysis = analyzeWeek(monday)
292
293
println "Week starting ${monday}:"
294
println "Working days: ${weekAnalysis.workingDays} (${weekAnalysis.weekdays.join(', ')})"
295
println "Weekend days: ${weekAnalysis.weekends.size()} (${weekAnalysis.weekends.join(', ')})"
296
```
297
298
### Enum Arithmetic Edge Cases
299
300
Understanding the behavior of enum arithmetic with edge cases and boundary conditions.
301
302
**Usage Examples:**
303
304
```groovy
305
import java.time.*
306
307
// DayOfWeek edge cases
308
def sunday = DayOfWeek.SUNDAY
309
310
// Negative arithmetic
311
def saturday = sunday - 1 // SATURDAY
312
def friday = sunday - 2 // FRIDAY
313
def monday = sunday - (-1) // Same as sunday + 1 -> MONDAY
314
315
// Large numbers
316
def stillSunday = sunday + 7 // Wraps around -> SUNDAY
317
def alsoSunday = sunday + 14 // Multiple weeks -> SUNDAY
318
def backToSunday = sunday - 7 // Negative wrap -> SUNDAY
319
320
println "Sunday - 1 = ${saturday}"
321
println "Sunday + 7 = ${stillSunday}"
322
println "Sunday - 7 = ${backToSunday}"
323
324
// Month edge cases
325
def december = Month.DECEMBER
326
327
// Crossing year boundaries
328
def january = december + 1 // Wraps to JANUARY
329
def november = december - 1 // NOVEMBER
330
def february = december + 2 // FEBRUARY (next year conceptually)
331
332
// Large arithmetic
333
def sameDecember = december + 12 // Full year -> DECEMBER
334
def march = december + 3 // MARCH (next year)
335
def september = december - 3 // SEPTEMBER (same year)
336
337
println "December + 1 = ${january}"
338
println "December + 12 = ${sameDecember}"
339
println "December - 3 = ${september}"
340
341
// Zero arithmetic (identity)
342
def unchangedDay = DayOfWeek.WEDNESDAY + 0 // WEDNESDAY
343
def unchangedMonth = Month.JUNE + 0 // JUNE
344
345
println "Wednesday + 0 = ${unchangedDay}"
346
println "June + 0 = ${unchangedMonth}"
347
348
// Modular arithmetic demonstration
349
def demonstrateModular = { DayOfWeek day, List<Integer> offsets ->
350
offsets.each { offset ->
351
def result = day + offset
352
println "${day} + ${offset} = ${result} (offset mod 7 = ${offset % 7})"
353
}
354
}
355
356
println "DayOfWeek modular arithmetic:"
357
demonstrateModular(DayOfWeek.MONDAY, [0, 1, 7, 8, 14, 15, -1, -7, -8])
358
359
// Month modular arithmetic
360
def demonstrateMonthModular = { Month month, List<Integer> offsets ->
361
offsets.each { offset ->
362
def result = month + offset
363
println "${month} + ${offset} = ${result} (offset mod 12 = ${offset % 12})"
364
}
365
}
366
367
println "\nMonth modular arithmetic:"
368
demonstrateMonthModular(Month.JANUARY, [0, 1, 12, 13, 24, 25, -1, -12, -13])
369
```
370
371
### Practical Applications
372
373
Real-world scenarios demonstrating the utility of enum extensions.
374
375
**Usage Examples:**
376
377
```groovy
378
import java.time.*
379
380
// Payroll system - find pay dates (every other Friday)
381
def calculatePayDates = { LocalDate startDate, int numberOfPays ->
382
def payDates = []
383
def current = startDate
384
385
// Find first Friday on or after start date
386
while (current.dayOfWeek != DayOfWeek.FRIDAY) {
387
current = current + 1
388
}
389
390
numberOfPays.times {
391
payDates << current
392
current = current + 14 // Every two weeks
393
}
394
395
return payDates
396
}
397
398
def payDates = calculatePayDates(LocalDate.of(2024, 1, 1), 6)
399
println "Pay dates for first half of 2024:"
400
payDates.each { date ->
401
println "${date} (${date.dayOfWeek})"
402
}
403
404
// Restaurant schedule - closed on Mondays, special menu on weekends
405
def getRestaurantStatus = { LocalDate date ->
406
def dayOfWeek = date.dayOfWeek
407
408
if (dayOfWeek == DayOfWeek.MONDAY) {
409
return 'Closed'
410
} else if (dayOfWeek.isWeekend()) {
411
return 'Special Weekend Menu'
412
} else {
413
return 'Regular Menu'
414
}
415
}
416
417
def thisWeek = (0..6).collect { LocalDate.now().with(DayOfWeek.MONDAY) + it }
418
println "Restaurant schedule this week:"
419
thisWeek.each { date ->
420
println "${date} (${date.dayOfWeek}): ${getRestaurantStatus(date)}"
421
}
422
423
// Seasonal product availability
424
def getProductAvailability = { Month month ->
425
def season = null
426
switch (month) {
427
case [Month.DECEMBER, Month.JANUARY, Month.FEBRUARY]:
428
season = 'Winter'
429
break
430
case [Month.MARCH, Month.APRIL, Month.MAY]:
431
season = 'Spring'
432
break
433
case [Month.JUNE, Month.JULY, Month.AUGUST]:
434
season = 'Summer'
435
break
436
case [Month.SEPTEMBER, Month.OCTOBER, Month.NOVEMBER]:
437
season = 'Fall'
438
break
439
}
440
441
def products = [
442
'Winter': ['Hot chocolate', 'Soup', 'Winter coats'],
443
'Spring': ['Rain gear', 'Gardening supplies', 'Light jackets'],
444
'Summer': ['Sunscreen', 'Swimming gear', 'Ice cream'],
445
'Fall': ['Pumpkins', 'Warm sweaters', 'Back-to-school supplies']
446
]
447
448
return [season: season, products: products[season]]
449
}
450
451
Month.values().each { month ->
452
def availability = getProductAvailability(month)
453
println "${month} (${availability.season}): ${availability.products.join(', ')}"
454
}
455
456
// Meeting scheduler - avoid Mondays and Fridays
457
def findBestMeetingDay = { LocalDate weekStart ->
458
def preferredDays = [DayOfWeek.TUESDAY, DayOfWeek.WEDNESDAY, DayOfWeek.THURSDAY]
459
def monday = weekStart.with(DayOfWeek.MONDAY)
460
461
def availableDays = preferredDays.collect { dayOfWeek ->
462
monday.with(dayOfWeek)
463
}
464
465
return availableDays.find { date ->
466
// Additional logic could check calendar conflicts, etc.
467
true // For demo, all preferred days are available
468
}
469
}
470
471
def nextWeek = LocalDate.now().plusWeeks(1)
472
def bestDay = findBestMeetingDay(nextWeek)
473
println "Best day for meeting next week: ${bestDay} (${bestDay.dayOfWeek})"
474
```