You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2018/03/21 16:44:36 UTC

[06/13] groovy git commit: add GDK Date/Time documentation(closes #674)

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/57ce3a0b
Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/57ce3a0b
Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/57ce3a0b

Branch: refs/heads/GROOVY_2_5_X
Commit: 57ce3a0b792bb129204c44ff1c15c2ff6f1b871c
Parents: 210811e
Author: Joe Wolf <jo...@gmail.com>
Authored: Sat Mar 17 20:50:20 2018 -0400
Committer: paulk <pa...@asert.com.au>
Committed: Thu Mar 22 02:05:58 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/57ce3a0b/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/57ce3a0b/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/57ce3a0b/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[]
+    }
+
+}