Groovy extensions for working with Java 8+ date/time types, providing convenient methods for temporal operations and transformations
—
Functionality for working with Year and YearMonth temporal objects including arithmetic operations, period calculations, and temporal object creation through operator overloading.
Year represents a year in the ISO-8601 calendar system.
/**
* Returns a Year that is the specified number of years after this year.
*/
Year plus(Year self, long years)
/**
* Returns a Year that is the specified number of years before this year.
*/
Year minus(Year self, long years)
/**
* Returns the next Year after this year.
*/
Year next(Year self)
/**
* Returns the previous Year before this year.
*/
Year previous(Year self)
/**
* Returns a Period between the first day of this year and the first day of the provided Year.
*/
Period rightShift(Year self, Year year)
/**
* Returns a YearMonth of this year and the provided Month.
*/
YearMonth leftShift(Year self, Month month)
/**
* Returns a LocalDate of this year on the given MonthDay.
*/
LocalDate leftShift(Year self, MonthDay monthDay)
/**
* Returns the era of the year (0 for BC, 1 for AD).
*/
int getEra(Year self)
/**
* Returns the year value of the era.
*/
int getYearOfEra(Year self)Usage Examples:
import java.time.*
def year2024 = Year.of(2024)
// Arithmetic operations
def nextYear = year2024 + 1 // Year 2025
def fiveYearsAgo = year2024 - 5 // Year 2019
def decade = year2024 + 10 // Year 2034
// Navigation
def following = year2024.next() // Year 2025
def preceding = year2024.previous() // Year 2023
// Period calculation
def period = year2024 >> Year.of(2030) // Period of 6 years
// Creating other temporal objects
def january2024 = year2024 << Month.JANUARY // YearMonth: 2024-01
def christmas = year2024 << MonthDay.of(12, 25) // LocalDate: 2024-12-25
def newYear = year2024 << MonthDay.of(1, 1) // LocalDate: 2024-01-01
// Era information
def era = year2024.era // 1 (AD/CE)
def yearOfEra = year2024.yearOfEra // 2024
// Practical examples
def isLeapYear = year2024.isLeap() // true (2024 is a leap year)
def daysInYear = year2024.length() // 366 (leap year)
// Working with leap years
def leapYears = (2020..2030).findAll { Year.of(it).isLeap() }
println "Leap years between 2020-2030: ${leapYears}" // [2020, 2024, 2028]YearMonth represents a year-month combination in the ISO-8601 calendar system.
/**
* Returns a YearMonth that is the specified number of months after this year-month.
*/
YearMonth plus(YearMonth self, long months)
/**
* Returns a YearMonth that is the specified number of months before this year-month.
*/
YearMonth minus(YearMonth self, long months)
/**
* Returns the next YearMonth after this year-month.
*/
YearMonth next(YearMonth self)
/**
* Returns the previous YearMonth before this year-month.
*/
YearMonth previous(YearMonth self)
/**
* Returns a LocalDate of this year-month and the given day of the month.
*/
LocalDate leftShift(YearMonth self, int dayOfMonth)
/**
* Returns a Period between the first day of this year-month and the given YearMonth.
*/
Period rightShift(YearMonth self, YearMonth other)Usage Examples:
import java.time.*
def dec2024 = YearMonth.of(2024, 12)
// Arithmetic operations
def nextMonth = dec2024 + 1 // YearMonth: 2025-01
def sixMonthsAgo = dec2024 - 6 // YearMonth: 2024-06
def twoYearsLater = dec2024 + 24 // YearMonth: 2026-12
// Navigation
def following = dec2024.next() // YearMonth: 2025-01
def preceding = dec2024.previous() // YearMonth: 2024-11
// Creating LocalDate with specific day
def christmas = dec2024 << 25 // LocalDate: 2024-12-25
def newYearsEve = dec2024 << 31 // LocalDate: 2024-12-31
def firstOfMonth = dec2024 << 1 // LocalDate: 2024-12-01
// Period calculation
def period = dec2024 >> YearMonth.of(2025, 6) // Period of 6 months
// Practical examples
def monthLength = dec2024.lengthOfMonth() // 31 days
def yearLength = dec2024.lengthOfYear() // 366 days (2024 is leap year)
// Generate date ranges for the month
def allDaysInMonth = (1..monthLength).collect { dec2024 << it }
println "All days in December 2024: ${allDaysInMonth.size()} days"
// Financial quarters
def getQuarter = { YearMonth yearMonth ->
def month = yearMonth.monthValue
return Math.ceil(month / 3.0) as int
}
def quarter = getQuarter(dec2024) // 4 (Q4)
println "December 2024 is in Q${quarter}"
// Month-end processing
def isMonthEnd = { LocalDate date ->
def yearMonth = YearMonth.from(date)
return date.dayOfMonth == yearMonth.lengthOfMonth()
}
def endOfDec = dec2024 << 31
println "Is ${endOfDec} month-end? ${isMonthEnd(endOfDec)}" // trueEnhanced functionality for MonthDay temporal objects.
/**
* Returns a LocalDate of this month-day and the provided year.
*/
LocalDate leftShift(MonthDay self, int year)
/**
* Returns a LocalDate of this month-day and the provided Year.
*/
LocalDate leftShift(MonthDay self, Year year)Usage Examples:
import java.time.*
def christmas = MonthDay.of(12, 25)
def leapDay = MonthDay.of(2, 29)
// Creating LocalDate for specific years
def christmas2024 = christmas << 2024 // LocalDate: 2024-12-25
def christmas2025 = christmas << Year.of(2025) // LocalDate: 2025-12-25
// Working with leap day
def leapDay2024 = leapDay << Year.of(2024) // LocalDate: 2024-02-29
// Note: leapDay << 2023 would throw an exception (2023 is not a leap year)
// Holiday calculations
def holidays = [
'New Year': MonthDay.of(1, 1),
'Valentine\'s Day': MonthDay.of(2, 14),
'Christmas': MonthDay.of(12, 25)
]
def year = Year.of(2024)
holidays.each { name, monthDay ->
def date = monthDay << year
println "${name} in ${year.value}: ${date} (${date.dayOfWeek})"
}
// Anniversary tracking
def anniversary = MonthDay.of(6, 15)
def thisYear = anniversary << Year.now()
def nextYear = anniversary << Year.now().plusYears(1)
println "This year's anniversary: ${thisYear}"
println "Next year's anniversary: ${nextYear}"
// Seasonal events
def seasons = [
'Spring Equinox': MonthDay.of(3, 20),
'Summer Solstice': MonthDay.of(6, 21),
'Fall Equinox': MonthDay.of(9, 22),
'Winter Solstice': MonthDay.of(12, 21)
]
def currentYear = Year.now()
seasons.each { event, monthDay ->
def date = monthDay << currentYear
def daysUntil = LocalDate.now().until(date, ChronoUnit.DAYS)
println "${event}: ${date} (${daysUntil} days away)"
}How Year and YearMonth operations integrate with the broader datetime system.
Usage Examples:
import java.time.*
import java.time.temporal.ChronoUnit
// Financial year calculations
def calculateFiscalYear = { LocalDate date ->
// Assuming fiscal year starts in April
def year = date.year
def fiscalYear = (date.monthValue >= 4) ? year : year - 1
return Year.of(fiscalYear)
}
def today = LocalDate.now()
def fiscalYear = calculateFiscalYear(today)
def fiscalStart = fiscalYear << MonthDay.of(4, 1)
def fiscalEnd = (fiscalYear + 1) << MonthDay.of(3, 31)
println "Current fiscal year: ${fiscalYear.value}"
println "Fiscal year period: ${fiscalStart} to ${fiscalEnd}"
// Age calculations
def calculateAge = { LocalDate birthDate, LocalDate asOfDate ->
def birthYear = Year.from(birthDate)
def currentYear = Year.from(asOfDate)
def yearsDiff = currentYear.value - birthYear.value
// Adjust if birthday hasn't occurred this year
def birthMonthDay = MonthDay.from(birthDate)
def currentMonthDay = MonthDay.from(asOfDate)
if (currentMonthDay.isBefore(birthMonthDay)) {
yearsDiff--
}
return yearsDiff
}
def birthDate = LocalDate.of(1990, 6, 15)
def age = calculateAge(birthDate, LocalDate.now())
println "Age: ${age} years"
// Project timeline management
def projectStart = YearMonth.of(2024, 3)
def projectDuration = 18 // months
def milestones = [:]
(0..projectDuration).step(3) { monthOffset ->
def milestone = projectStart + monthOffset
def quarter = "Q${Math.ceil(milestone.monthValue / 3.0) as int}"
milestones["${milestone.year} ${quarter}"] = milestone
}
println "Project milestones:"
milestones.each { quarter, yearMonth ->
println "${quarter}: ${yearMonth}"
}
// Seasonal business analysis
def analyzeSeason = { YearMonth yearMonth ->
def month = yearMonth.month
switch (month) {
case [Month.DECEMBER, Month.JANUARY, Month.FEBRUARY]:
return [season: 'Winter', category: 'Holiday/Indoor']
case [Month.MARCH, Month.APRIL, Month.MAY]:
return [season: 'Spring', category: 'Renewal/Growth']
case [Month.JUNE, Month.JULY, Month.AUGUST]:
return [season: 'Summer', category: 'Vacation/Outdoor']
case [Month.SEPTEMBER, Month.OCTOBER, Month.NOVEMBER]:
return [season: 'Fall', category: 'Back-to-School/Harvest']
}
}
def currentMonth = YearMonth.now()
def analysis = analyzeSeason(currentMonth)
println "Current month ${currentMonth}: ${analysis.season} (${analysis.category})"
// Lease and contract management
def calculateLeaseEnd = { LocalDate startDate, int durationMonths ->
def startYearMonth = YearMonth.from(startDate)
def endYearMonth = startYearMonth + durationMonths
// Use last day of the end month
return endYearMonth << endYearMonth.lengthOfMonth()
}
def leaseStart = LocalDate.of(2024, 1, 15)
def leaseEnd = calculateLeaseEnd(leaseStart, 12) // 12-month lease
println "Lease period: ${leaseStart} to ${leaseEnd}"
// Birthday and anniversary tracking
def findNextOccurrence = { MonthDay event, LocalDate fromDate ->
def thisYear = event << Year.from(fromDate)
def nextYear = event << Year.from(fromDate).plusYears(1)
return thisYear.isAfter(fromDate) ? thisYear : nextYear
}
def birthday = MonthDay.of(8, 22)
def nextBirthday = findNextOccurrence(birthday, LocalDate.now())
def daysUntil = ChronoUnit.DAYS.between(LocalDate.now(), nextBirthday)
println "Next birthday: ${nextBirthday} (${daysUntil} days away)"Install with Tessl CLI
npx tessl i tessl/maven-org-codehaus-groovy--groovy-datetime