# Working with Date/Time types

version 2.5.21

The `groovy-datetime` module supports numerous extensions for working with the Date/Time API introduced in Java 8. This documentation refers to the data types defined by this API as "JSR 310 types."

## 1. Formatting and parsing

A common use case when working with date/time types is to convert them to Strings (formatting) and from Strings (parsing). Groovy provides these additional formatting methods:

Method | Description | Example
--- | --- | ---
`getDateString()` | For `LocalDate` and `LocalDateTime`, formats with `DateTimeFormatter.ISO_LOCAL_DATE` | `2018-03-10`
| For `OffsetDateTime`, formats with `DateTimeFormatter.ISO_OFFSET_DATE` | `2018-03-10+04:00`
| For `ZonedDateTime`, formats with `DateTimeFormatter.ISO_LOCAL_DATE` and appends the `ZoneId` short name | `2018-03-10EST`
`getDateTimeString()` | For `LocalDateTime`, formats with `DateTimeFormatter.ISO_LOCAL_DATE_TIME` | `2018-03-10T20:30:45`
| For `OffsetDateTime`, formats with `DateTimeFormatter.ISO_OFFSET_DATE_TIME` | `2018-03-10T20:30:45+04:00`
| For `ZonedDateTime`, formats with `DateTimeFormatter.ISO_LOCAL_DATE_TIME` and appends the `ZoneId` short name | `2018-03-10T20:30:45EST`
`getTimeString()` | For `LocalTime` and `LocalDateTime`, formats with `DateTimeFormatter.ISO_LOCAL_TIME` | `20:30:45`
| For `OffsetTime` and `OffsetDateTime`, formats with `DateTimeFormatter.ISO_OFFSET_TIME` formatter | `20:30:45+04:00`
| For `ZonedDateTime`, formats with `DateTimeFormatter.ISO_LOCAL_TIME` and appends the `ZoneId` short name | `20:30:45EST`
`format(FormatStyle style)` | For `LocalTime` and `OffsetTime`, formats with `DateTimeFormatter.ofLocalizedTime(style)` | `4:30 AM` (with style `FormatStyle.SHORT`, e.g.)
| For `LocalDate`, formats with `DateTimeFormatter.ofLocalizedDate(style)` | `Saturday, March 10, 2018` (with style `FormatStyle.FULL`, e.g.)
| For `LocalDateTime`, `OffsetDateTime`, and `ZonedDateTime` formats with `DateTimeFormatter.ofLocalizedDateTime(style)` | `Mar 10, 2019 4:30:45 AM` (with style `FormatStyle.MEDIUM`, e.g.)
`format(String pattern)` | Formats with `DateTimeFormatter.ofPattern(pattern)` | `03/10/2018` (with pattern ’MM/dd/yyyy', e.g.)

For parsing, Groovy adds a static `parse` method to many of the JSR 310 types. The method takes two arguments: the value to be formatted and the pattern to use. The pattern is defined by the `java.time.format.DateTimeFormatter` API. As an example:

    def date = LocalDate.parse('Jun 3, 04', 'MMM d, yy')
    assert date == LocalDate.of(2004, Month.JUNE, 3)

    def time = LocalTime.parse('4:45', 'H:mm')
    assert time == LocalTime.of(4, 45, 0)

    def offsetTime = OffsetTime.parse('09:47:51-1234', 'HH:mm:ssZ')
    assert offsetTime == OffsetTime.of(9, 47, 51, 0, ZoneOffset.ofHoursMinutes(-12, -34))

    def dateTime = ZonedDateTime.parse('2017/07/11 9:47PM Pacific Standard Time', 'yyyy/MM/dd h:mma zzzz')
    assert dateTime == ZonedDateTime.of(
            LocalDate.of(2017, 7, 11),
            LocalTime.of(21, 47, 0),
            ZoneId.of('America/Los_Angeles')
    )

Note that these `parse` methods have a different argument ordering than the static `parse` method Groovy added to `java.util.Date`. This was done to be consistent with the existing `parse` methods of the Date/Time API.

## 2. Manipulating date/time

### 2.1. Addition and subtraction

`Temporal` types have `plus` and `minus` methods for adding or subtracting a provided `java.time.temporal.TemporalAmount` argument. Because Groovy maps the `+` and `-` operators to single-argument methods of these names, a more natural expression syntax can be used to add and subtract.

    def aprilFools = LocalDate.of(2018, Month.APRIL, 1)

    def nextAprilFools = aprilFools + Period.ofDays(365) // add 365 days
    assert nextAprilFools.year == 2019

    def idesOfMarch = aprilFools - Period.ofDays(17) // subtract 17 days
    assert idesOfMarch.dayOfMonth == 15
    assert idesOfMarch.month == Month.MARCH

Groovy provides additional `plus` and `minus` methods that accept an integer argument, enabling the above to be rewritten more succinctly:

    def nextAprilFools = aprilFools + 365 // add 365 days
    def idesOfMarch = aprilFools - 17 // subtract 17 days

The unit of these integers depends on the JSR 310 type operand. As evident above, integers used with `ChronoLocalDate` types like `LocalDate` have a unit of days. Integers used with `Year` and `YearMonth` have a unit of years and months, respectively. All other types have a unit of seconds, such as `LocalTime`, for instance:

    def mars = LocalTime.of(12, 34, 56) // 12:34:56 pm

    def thirtySecondsToMars = mars - 30 // go back 30 seconds
    assert thirtySecondsToMars.second == 26

### 2.2. Multiplication and division

The `*` operator can be used to multiply `Period` and `Duration` instances by an integer value; the `/` operator can be used to divide `Duration` instances by an integer value.

    def period = Period.ofMonths(1) * 2 // a 1-month period times 2
    assert period.months == 2

    def duration = Duration.ofSeconds(10) / 5// a 10-second duration divided by 5
    assert duration.seconds == 2

### 2.3. Incrementing and decrementing

The `++` and `--` operators can be used increment and decrement date/time values by one unit. Since the JSR 310 types are immutable, the operation will create a new instance with the incremented/decremented value and reassign it to the reference.

    def year = Year.of(2000)
    --year // decrement by one year
    assert year.value == 1999

    def offsetTime = OffsetTime.of(0, 0, 0, 0, ZoneOffset.UTC) // 00:00:00.000 UTC
    offsetTime++ // increment by one second
    assert offsetTime.second == 1

### 2.4. Negation

The `Duration` and `Period` types represent a negative or positive length of time. These can be negated with the unary `-` operator.

    def duration = Duration.ofSeconds(-15)
    def negated = -duration
    assert negated.seconds == 15

## 3. Interacting with date/time values

### 3.1. Property notation

The `getLong(TemporalField)` method of `TemporalAccessor` types (e.g. `LocalDate`, `LocalTime`, `ZonedDateTime`, etc.) and the `get(TemporalUnit)` method of `TemporalAmount` types (namely `Period` and `Duration`), can be invoked with Groovy’s property notation. For example:

    def date = LocalDate.of(2018, Month.MARCH, 12)
    assert date[ChronoField.YEAR] == 2018
    assert date[ChronoField.MONTH_OF_YEAR] == Month.MARCH.value
    assert date[ChronoField.DAY_OF_MONTH] == 12
    assert date[ChronoField.DAY_OF_WEEK] == DayOfWeek.MONDAY.value

    def period = Period.ofYears(2).withMonths(4).withDays(6)
    assert period[ChronoUnit.YEARS] == 2
    assert period[ChronoUnit.MONTHS] == 4
    assert period[ChronoUnit.DAYS] == 6

### 3.2. Ranges, `upto`, and `downto`

The JSR 310 types can be used with the range operator. The following example iterates between today and the `LocalDate` six days from now, printing out the day of the week for each iteration. As both range bounds are inclusive, this prints all seven days of the week.

    def start = LocalDate.now()
    def end = start + 6 // 6 days later
    (start..end).each { date ->
        println date.dayOfWeek
    }

The `upto` method will accomplish the same as the range in the above example. The `upto` method iterates from the earlier `start` value (inclusive) to the later `end` value (also inclusive), calling the closure with the incremented `next` value once per iteration.

    def start = LocalDate.now()
    def end = start + 6 // 6 days later
    start.upto(end) { next ->
        println next.dayOfWeek
    }

The `downto` method iterates in the opposite direction, from a later `start` value to an earlier `end` value.

The unit of iteration for `upto`, `downto`, and ranges is the same as the unit for addition and subtraction: `LocalDate` iterates by one day at a time, `YearMonth` iterates by one month, `Year` by one year, and everything else by one second. Both methods also support an optional a `TemporalUnit` argument to change the unit of iteration.

Consider the following example, where March 1st, 2018 is iterated up to March 2nd, 2018 using an iteration unit of months.

    def start = LocalDate.of(2018, Month.MARCH, 1)
    def end = start + 1 // 1 day later

    int iterationCount = 0
    start.upto(end, ChronoUnit.MONTHS) { next ->
        println next
        ++iterationCount
    }

    assert iterationCount == 1

Since the `start` date is inclusive, the closure is called with a `next` date value of March 1st. The `upto` method then increments the date by one month, yielding the date, April 1st. Because this date is after the specified `end` date of March 2nd, the iteration stops immediately, having only called the closure once. This behavior is the same for the `downto` method except that the iteration will stop as soon as the value of `next` becomes earlier than the targeted `end` date.

In short, when iterating with the `upto` or `downto` methods with a custom unit of iteration, the current value of iteration will never exceed the end value.

### 3.3. Combining date/time values

The left-shift operator (`<<`) can be used to combine two JSR 310 types into an aggregate type. For example, a `LocalDate` can be left-shifted into a `LocalTime` to produce a composite `LocalDateTime` instance.

    MonthDay monthDay = Month.JUNE << 3 // June 3rd
    LocalDate date = monthDay << Year.of(2015) // 3-Jun-2015
    LocalDateTime dateTime = date << LocalTime.NOON // 3-Jun-2015 @ 12pm
    OffsetDateTime offsetDateTime = dateTime << ZoneOffset.ofHours(-5) // 3-Jun-2015 @ 12pm UTC-5

The left-shift operator is reflexive; the order of the operands does not matter.

    def year = Year.of(2000)
    def month = Month.DECEMBER

    YearMonth a = year << month
    YearMonth b = month << year
    assert a == b

### 3.4. Creating periods and durations

The right-shift operator (`>>`) produces a value representing the period or duration between the operands. For `ChronoLocalDate`, `YearMonth`, and `Year`, the operator yields a `Period` instance:

    def newYears = LocalDate.of(2018, Month.JANUARY, 1)
    def aprilFools = LocalDate.of(2018, Month.APRIL, 1)

    def period = newYears >> aprilFools
    assert period instanceof Period
    assert period.months == 3

The operator produces a `Duration` for the time-aware JSR types:

    def duration = LocalTime.NOON >> (LocalTime.NOON + 30)
    assert duration instanceof Duration
    assert duration.seconds == 30

If the value on the left-hand side of the operator is earlier than the value on the right-hand side, the result is positive. If the left-hand side is later than the right-hand side, the result is negative:

    def decade = Year.of(2010) >> Year.of(2000)
    assert decade.years == -10

## 4. Converting between legacy and JSR 310 types

Despite the shortcomings of `Date`, `Calendar`, and `TimeZone` types in the `java.util` package they are farily common in Java APIs (at least in those prior to Java 8). To accommodate use of such APIs, Groovy provides methods for converting between the JSR 310 types and legacy types.

Most JSR types have been fitted with `toDate()` and `toCalendar()` methods for converting to relatively equivalent `java.util.Date` and `java.util.Calendar` values. Both `ZoneId` and `ZoneOffset` have been given a `toTimeZone()` method for converting to `java.util.TimeZone`.

    // LocalDate to java.util.Date
    def valentines = LocalDate.of(2018, Month.FEBRUARY, 14)
    assert valentines.toDate().format('MMMM dd, yyyy') == 'February 14, 2018'

    // LocalTime to java.util.Date
    def noon = LocalTime.of(12, 0, 0)
    assert noon.toDate().format('HH:mm:ss') == '12:00:00'

    // ZoneId to java.util.TimeZone
    def newYork = ZoneId.of('America/New_York')
    assert newYork.toTimeZone() == TimeZone.getTimeZone('America/New_York')

    // ZonedDateTime to java.util.Calendar
    def valAtNoonInNY = ZonedDateTime.of(valentines, noon, newYork)
    assert valAtNoonInNY.toCalendar().getTimeZone().toZoneId() == newYork

Note that when converting to a legacy type:

- Nanosecond values are truncated to milliseconds. A `LocalTime`, for example, with a `ChronoUnit.NANOS` value of 999,999,999 nanoseconds translates to 999 milliseconds.

- When converting the "local" types (`LocalDate`, `LocalTime`, and `LocalDateTime`), the time zone of the returned `Date` or `Calendar` will be the system default.

- When converting a time-only type (`LocalTime` or `OffsetTime`), the year/month/day of the `Date` or `Calendar` is set to the current date.

- When converting a date-only type (`LocalDate`), the time value of the `Date` or `Calendar` will be cleared, i.e. `00:00:00.000`.

- When converting an `OffsetDateTime` to a `Calendar`, only the hours and minutes of the `ZoneOffset` convey into the corresponding `TimeZone`. Fortunately, Zone Offsets with non-zero seconds are rare.

Groovy has added a number of methods to `Date` and `Calendar` for converting into the various JSR 310 types:

    Date legacy = Date.parse('yyyy-MM-dd HH:mm:ss.SSS', '2010-04-03 10:30:58.999')

    assert legacy.toLocalDate() == LocalDate.of(2010, 4, 3)
    assert legacy.toLocalTime() == LocalTime.of(10, 30, 58, 999_000_000) // 999M ns = 999ms
    assert legacy.toOffsetTime().hour == 10
    assert legacy.toYear() == Year.of(2010)
    assert legacy.toMonth() == Month.APRIL
    assert legacy.toDayOfWeek() == DayOfWeek.SATURDAY
    assert legacy.toMonthDay() == MonthDay.of(Month.APRIL, 3)
    assert legacy.toYearMonth() == YearMonth.of(2010, Month.APRIL)
    assert legacy.toLocalDateTime().year == 2010
    assert legacy.toOffsetDateTime().dayOfMonth == 3
    assert legacy.toZonedDateTime().zone == ZoneId.systemDefault()