Repository: groovy Updated Branches: refs/heads/GROOVY_2_6_X 235f9ccba -> 55865ea96
add GDK Date/Time documentation(closes #674) Project: http://git-wip-us.apache.org/repos/asf/groovy/repo Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/963c90b4 Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/963c90b4 Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/963c90b4 Branch: refs/heads/GROOVY_2_6_X Commit: 963c90b4fdb1f115791c12ee99f25fa281d57f54 Parents: b3bd886 Author: Joe Wolf <[email protected]> Authored: Sat Mar 17 20:50:20 2018 -0400 Committer: paulk <[email protected]> Committed: Thu Mar 22 00:41:46 2018 +1000 ---------------------------------------------------------------------- src/spec/doc/core-gdk.adoc | 2 + src/spec/doc/working-with-datetime-types.adoc | 341 +++++++++++++++++++ .../gdk/WorkingWithDateTimeTypesTest.groovy | 222 ++++++++++++ 3 files changed, 565 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/groovy/blob/963c90b4/src/spec/doc/core-gdk.adoc ---------------------------------------------------------------------- diff --git a/src/spec/doc/core-gdk.adoc b/src/spec/doc/core-gdk.adoc index 07caae9..2d239fe 100644 --- a/src/spec/doc/core-gdk.adoc +++ b/src/spec/doc/core-gdk.adoc @@ -25,6 +25,8 @@ include::{projectdir}/src/spec/doc/working-with-io.adoc[leveloffset=+1] include::{projectdir}/src/spec/doc/working-with-collections.adoc[leveloffset=+1] +include::{projectdir}/src/spec/doc/working-with-datetime-types.adoc[leveloffset=+1] + == Handy utilities === ConfigSlurper http://git-wip-us.apache.org/repos/asf/groovy/blob/963c90b4/src/spec/doc/working-with-datetime-types.adoc ---------------------------------------------------------------------- diff --git a/src/spec/doc/working-with-datetime-types.adoc b/src/spec/doc/working-with-datetime-types.adoc new file mode 100644 index 0000000..6567154 --- /dev/null +++ b/src/spec/doc/working-with-datetime-types.adoc @@ -0,0 +1,341 @@ +////////////////////////////////////////// + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + +////////////////////////////////////////// + += Working with Date/Time types +:gdk: http://www.groovy-lang.org/gdk.html[Groovy development kit] +:java-util-list: http://docs.oracle.com/javase/8/docs/api/java/util/List.html[java.util.List] +:java-time-types: `java.time` types + +Groovy's syntax and extension methods within the {gdk} provide conveniences for using +the http://www.oracle.com/technetwork/articles/java/jf14-date-time-2125367.html[Date/Time API +introduced in Java 8]. This documentation refers to the data types defined by this API as +"JSR 310 types." + +== 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: + +[cols="1,1,1" options="header"] +|==== +| Method +| Description +| Example + +| `getDateString()` +| For `LocalDate` and `LocalDateTime`, formats with +https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html#ISO_LOCAL_DATE[`DateTimeFormatter.ISO_LOCAL_DATE`] +| `2018-03-10` + +| +| For `OffsetDateTime`, formats with +https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html#ISO_OFFSET_DATE[`DateTimeFormatter.ISO_OFFSET_DATE`] +| `2018-03-10+04:00` + +| +| For `ZonedDateTime`, formats with +https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html#ISO_LOCAL_DATE[`DateTimeFormatter.ISO_LOCAL_DATE`] +and appends the `ZoneId` short name +| `2018-03-10EST` + +| `getDateTimeString()` +| For `LocalDateTime`, formats with +https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html#ISO_LOCAL_DATE_TIME[`DateTimeFormatter.ISO_LOCAL_DATE_TIME`] +| `2018-03-10T20:30:45` + +| +| For `OffsetDateTime`, formats with +https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html#ISO_OFFSET_DATE_TIME[`DateTimeFormatter.ISO_OFFSET_DATE_TIME`] +| `2018-03-10T20:30:45+04:00` + +| +| For `ZonedDateTime`, formats with +https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html#ISO_LOCAL_DATE_TIME[`DateTimeFormatter.ISO_LOCAL_DATE_TIME`] +and appends the `ZoneId` short name +| `2018-03-10T20:30:45EST` + +| `getTimeString()` +| For `LocalTime` and `LocalDateTime`, formats with +https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html#ISO_LOCAL_TIME[`DateTimeFormatter.ISO_LOCAL_TIME`] +| `20:30:45` + +| +| For `OffsetTime` and `OffsetDateTime`, formats with +https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html#ISO_OFFSET_TIME[`DateTimeFormatter.ISO_OFFSET_TIME`] +formatter +| `20:30:45+04:00` + +| +| For `ZonedDateTime`, formats with +https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html#ISO_LOCAL_TIME[`DateTimeFormatter.ISO_LOCAL_TIME`] +and appends the `ZoneId` short name +| `20:30:45EST` + +| `format(FormatStyle style)` +| For `LocalTime` and `OffsetTime`, formats with +https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html#ofLocalizedTime-java.time.format.FormatStyle-[`DateTimeFormatter.ofLocalizedTime(style)`] +| `4:30 AM` (with style `FormatStyle.SHORT`, e.g.) + +| +| For `LocalDate`, formats with +https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html#ofLocalizedDate-java.time.format.FormatStyle-[`DateTimeFormatter.ofLocalizedDate(style)`] +| `Saturday, March 10, 2018` (with style `FormatStyle.FULL`, e.g.) + +| +| For `LocalDateTime`, `OffsetDateTime`, and `ZonedDateTime` formats with +https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html#ofLocalizedDateTime-java.time.format.FormatStyle-[`DateTimeFormatter.ofLocalizedDateTime(style)`] +| `Mar 10, 2019 4:30:45 AM` (with style `FormatStyle.MEDIUM`, e.g.) + +| `format(String pattern)` +| Formats with +https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html#ofPattern-java.lang.String-[`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 +https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html[`java.time.format.DateTimeFormatter` API]. +As an example: + +[source,groovy] +------------------------------------- +include::{projectdir}/src/spec/test/gdk/WorkingWithDateTimeTypesTest.groovy[tags=static_parsing,indent=0] +------------------------------------- + +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. + +== Manipulating date/time + +=== 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. + +[source,groovy] +------------------------------------- +include::{projectdir}/src/spec/test/gdk/WorkingWithDateTimeTypesTest.groovy[tags=plus_minus_period,indent=0] +------------------------------------- + +Groovy provides additional `plus` and `minus` methods that accept an integer argument, +enabling the above to be rewritten more succinctly: + +[source,groovy] +------------------------------------- +include::{projectdir}/src/spec/test/gdk/WorkingWithDateTimeTypesTest.groovy[tags=localdate_plus_minus_integer,indent=0] +------------------------------------- + +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 +https://docs.oracle.com/javase/8/docs/api/java/time/temporal/ChronoUnit.html#DAYShttp://days[days]. +Integers used with `Year` and `YearMonth` have a unit of +https://docs.oracle.com/javase/8/docs/api/java/time/temporal/ChronoUnit.html#YEARS[years] and +https://docs.oracle.com/javase/8/docs/api/java/time/temporal/ChronoUnit.html#MONTHS[months], respectively. +All other types have a unit of +https://docs.oracle.com/javase/8/docs/api/java/time/temporal/ChronoUnit.html#SECONDS[seconds], +such as `LocalTime`, for instance: + +[source,groovy] +------------------------------------- +include::{projectdir}/src/spec/test/gdk/WorkingWithDateTimeTypesTest.groovy[tags=localtime_plus_minus_integer,indent=0] +------------------------------------- + +=== 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. + +[source,groovy] +------------------------------------- +include::{projectdir}/src/spec/test/gdk/WorkingWithDateTimeTypesTest.groovy[tags=multiply_divide,indent=0] +------------------------------------- + +=== 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. + +[source,groovy] +------------------------------------- +include::{projectdir}/src/spec/test/gdk/WorkingWithDateTimeTypesTest.groovy[tags=next_previous,indent=0] +------------------------------------- + +=== Negation + +The `Duration` and `Period` types represent a negative or positive length of time. +These can be negated with the unary `-` operator. + +[source,groovy] +------------------------------------- +include::{projectdir}/src/spec/test/gdk/WorkingWithDateTimeTypesTest.groovy[tags=duration_negation,indent=0] +------------------------------------- + +== Interacting with date/time values + +=== Property notation + +The +https://docs.oracle.com/javase/8/docs/api/java/time/temporal/TemporalAccessor.html#getLong-java.time.temporal.TemporalField-[`getLong(TemporalField)`] +method of `TemporalAccessor` types (e.g. `LocalDate`, +`LocalTime`, `ZonedDateTime`, etc.) and the +https://docs.oracle.com/javase/8/docs/api/java/time/temporal/TemporalAmount.html#get-java.time.temporal.TemporalUnit-[`get(TemporalUnit)`] +method of `TemporalAmount` types (namely `Period` and `Duration`), can be invoked with +Groovy's property notation. For example: + +[source,groovy] +------------------------------------- +include::{projectdir}/src/spec/test/gdk/WorkingWithDateTimeTypesTest.groovy[tags=property_notation,indent=0] +------------------------------------- + +=== Ranges, `upto`, and `downto` + +The JSR 310 types can be used with the <<core-operators.adoc#_range_operator,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. + +[source,groovy] +------------------------------------- +include::{projectdir}/src/spec/test/gdk/WorkingWithDateTimeTypesTest.groovy[tags=date_ranges,indent=0] +------------------------------------- + +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 value once per iteration. + +[source,groovy] +------------------------------------- +include::{projectdir}/src/spec/test/gdk/WorkingWithDateTimeTypesTest.groovy[tags=date_upto_date,indent=0] +------------------------------------- + +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 +https://docs.oracle.com/javase/8/docs/api/java/time/temporal/ChronoUnit.html#MONTHS[months]. + +[source,groovy] +------------------------------------- +include::{projectdir}/src/spec/test/gdk/WorkingWithDateTimeTypesTest.groovy[tags=date_upto_date_by_months,indent=0] +------------------------------------- + +Since the start date is inclusive, the closure is called with date 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 the value of `end` 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. + +=== 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. + +[source,groovy] +------------------------------------- +include::{projectdir}/src/spec/test/gdk/WorkingWithDateTimeTypesTest.groovy[tags=leftshift_operator,indent=0] +------------------------------------- + +The left-shift operator is reflexive; the order of the operands does not matter. + +[source,groovy] +------------------------------------- +include::{projectdir}/src/spec/test/gdk/WorkingWithDateTimeTypesTest.groovy[tags=leftshift_operator_reflexive,indent=0] +------------------------------------- + +=== 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: + +[source,groovy] +------------------------------------- +include::{projectdir}/src/spec/test/gdk/WorkingWithDateTimeTypesTest.groovy[tags=rightshift_operator_period,indent=0] +------------------------------------- + +The operator produces a `Duration` for the time-aware JSR types: + +[source,groovy] +------------------------------------- +include::{projectdir}/src/spec/test/gdk/WorkingWithDateTimeTypesTest.groovy[tags=rightshift_operator_duration,indent=0] +------------------------------------- + +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: + +[source,groovy] +------------------------------------- +include::{projectdir}/src/spec/test/gdk/WorkingWithDateTimeTypesTest.groovy[tags=rightshift_operator_negative,indent=0] +------------------------------------- + +== 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`. + +[source,groovy] +------------------------------------- +include::{projectdir}/src/spec/test/gdk/WorkingWithDateTimeTypesTest.groovy[tags=todate_tocalendar,indent=0] +------------------------------------- + +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: + +[source,groovy] +------------------------------------- +include::{projectdir}/src/spec/test/gdk/WorkingWithDateTimeTypesTest.groovy[tags=to_jsr310_types,indent=0] +------------------------------------- http://git-wip-us.apache.org/repos/asf/groovy/blob/963c90b4/src/spec/test/gdk/WorkingWithDateTimeTypesTest.groovy ---------------------------------------------------------------------- diff --git a/src/spec/test/gdk/WorkingWithDateTimeTypesTest.groovy b/src/spec/test/gdk/WorkingWithDateTimeTypesTest.groovy new file mode 100644 index 0000000..99e7f8d --- /dev/null +++ b/src/spec/test/gdk/WorkingWithDateTimeTypesTest.groovy @@ -0,0 +1,222 @@ +package gdk + +import java.time.* +import java.time.chrono.JapaneseDate +import java.time.temporal.ChronoField +import java.time.temporal.ChronoUnit + +class WorkingWithDateTimeTypesTest extends GroovyTestCase { + + void testParsing() { + // tag::static_parsing[] + 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') + ) + // end::static_parsing[] + } + + void testRange() { + // tag::date_ranges[] + def start = LocalDate.now() + def end = start + 6 // 6 days later + (start..end).each { date -> + println date.dayOfWeek + } + // end::date_ranges[] + } + + void testUptoDownto() { + // tag::date_upto_date[] + def start = LocalDate.now() + def end = start + 6 // 6 days later + start.upto(end) { date -> + println date.dayOfWeek + } + // end::date_upto_date[] + } + + void testUptoCustomUnit() { + // tag::date_upto_date_by_months[] + def start = LocalDate.of(2018, Month.MARCH, 2) + def end = start + 1 // 1 day later + + int iterationCount = 0 + start.upto(end, ChronoUnit.MONTHS) { date -> + println date + ++iterationCount + } + + assert iterationCount == 1 + // end::date_upto_date_by_months[] + } + + void testPlusMinusWithTemporalAmounts() { + // tag::plus_minus_period[] + 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 + // end::plus_minus_period[] + } + + void testLocalDatePlusMinusInteger() { + def aprilFools = LocalDate.of(2018, Month.APRIL, 1) + + // tag::localdate_plus_minus_integer[] + def nextAprilFools = aprilFools + 365 // add 365 days + def idesOfMarch = aprilFools - 17 // subtract 17 days + // end::localdate_plus_minus_integer[] + + assert nextAprilFools.year == 2019 + assert idesOfMarch.dayOfMonth == 15 + assert idesOfMarch.month == Month.MARCH + } + + void testLocalTimePlusMinusInteger() { + // tag::localtime_plus_minus_integer[] + def mars = LocalTime.of(12, 34, 56) // 12:34:56 pm + + def thirtySecondsToMars = mars - 30 // go back 30 seconds + assert thirtySecondsToMars.second == 26 + // end::localtime_plus_minus_integer[] + } + + void testNextPrevious() { + // tag::next_previous[] + 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 + // end::next_previous[] + } + + void testMultiplyDivide() { + // tag::multiply_divide[] + 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 + // end::multiply_divide[] + } + + void testNegation() { + // tag::duration_negation[] + def duration = Duration.ofSeconds(-15) + def negated = -duration + assert negated.seconds == 15 + // end::duration_negation[] + } + + void testPropertyNotation() { + // tag::property_notation[] + 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 + // end::property_notation[] + } + + void testLeftShift() { + // tag::leftshift_operator[] + 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 + // end::leftshift_operator[] + // tag::leftshift_operator_reflexive[] + def year = Year.of(2000) + def month = Month.DECEMBER + + YearMonth a = year << month + YearMonth b = month << year + assert a == b + // end::leftshift_operator_reflexive[] + } + + void testRightShift() { + // tag::rightshift_operator_period[] + 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 + // end::rightshift_operator_period[] + + // tag::rightshift_operator_duration[] + def duration = LocalTime.NOON >> (LocalTime.NOON + 30) + assert duration instanceof Duration + assert duration.seconds == 30 + // end::rightshift_operator_duration[] + + // tag::rightshift_operator_negative[] + def decade = Year.of(2010) >> Year.of(2000) + assert decade.years == -10 + // end::rightshift_operator_negative[] + } + + void testToDateAndToCalendar() { + // tag::todate_tocalendar[] + // 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 + // end::todate_tocalendar[] + } + + void testConvertToJSR310Types() { + // tag::to_jsr310_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() + // end::to_jsr310_types[] + } + +}
