You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2018/03/19 16:01:32 UTC
[2/5] groovy git commit: Fix upto/downto with custom TemporalUnit arg
edge cases. Add Period and Duration methods.
Fix upto/downto with custom TemporalUnit arg edge cases. Add Period and Duration methods.
Project: http://git-wip-us.apache.org/repos/asf/groovy/repo
Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/da357c86
Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/da357c86
Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/da357c86
Branch: refs/heads/master
Commit: da357c864cacf04f1510a48dbab69eae9841ceb3
Parents: 51005fc
Author: Joe Wolf <jo...@gmail.com>
Authored: Sun Mar 4 16:55:56 2018 -0500
Committer: danielsun1106 <re...@hotmail.com>
Committed: Tue Mar 20 00:00:31 2018 +0800
----------------------------------------------------------------------
.../groovy/runtime/DateTimeGroovyMethods.java | 136 ++++++++++--
.../runtime/DefaultGroovyStaticMethods.java | 57 +++++-
src/test/groovy/DateTimeTest.groovy | 205 ++++++++++++++-----
3 files changed, 327 insertions(+), 71 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/groovy/blob/da357c86/src/main/java/org/codehaus/groovy/runtime/DateTimeGroovyMethods.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/runtime/DateTimeGroovyMethods.java b/src/main/java/org/codehaus/groovy/runtime/DateTimeGroovyMethods.java
index 947e368..97d7df7 100644
--- a/src/main/java/org/codehaus/groovy/runtime/DateTimeGroovyMethods.java
+++ b/src/main/java/org/codehaus/groovy/runtime/DateTimeGroovyMethods.java
@@ -23,12 +23,17 @@ import groovy.lang.GroovyRuntimeException;
import java.time.*;
import java.time.chrono.ChronoLocalDate;
+import java.time.chrono.ChronoPeriod;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.time.format.TextStyle;
import java.time.temporal.*;
import java.util.*;
+import static java.time.temporal.ChronoUnit.DAYS;
+import static java.time.temporal.ChronoUnit.MONTHS;
+import static java.time.temporal.ChronoUnit.YEARS;
+
/**
* This class defines new Groovy methods which appear on normal JDK
* Date/Time API (java.time) classes inside the Groovy environment.
@@ -47,9 +52,9 @@ public class DateTimeGroovyMethods {
*/
private static Map<Class<? extends Temporal>, TemporalUnit> DEFAULT_UNITS = new HashMap<>();
static {
- DEFAULT_UNITS.put(ChronoLocalDate.class, ChronoUnit.DAYS);
- DEFAULT_UNITS.put(YearMonth.class, ChronoUnit.MONTHS);
- DEFAULT_UNITS.put(Year.class, ChronoUnit.YEARS);
+ DEFAULT_UNITS.put(ChronoLocalDate.class, DAYS);
+ DEFAULT_UNITS.put(YearMonth.class, MONTHS);
+ DEFAULT_UNITS.put(Year.class, YEARS);
}
/**
@@ -69,14 +74,15 @@ public class DateTimeGroovyMethods {
* Truncates a nanosecond value to milliseconds. No rounding.
*/
private static int millisFromNanos(int nanos) {
- return nanos / 1_000_000;
+ return nanos / 1_000_000;
}
/* ******** java.time.temporal.Temporal extension methods ******** */
/**
- * Iterates from the this to {@code to}, inclusive, incrementing by one unit each iteration, calling the
- * closure once per iteration. The closure may accept a single {@link java.time.temporal.Temporal} argument.
+ * Iterates from this to the {@code to} {@link java.time.temporal.Temporal}, inclusive, incrementing by one
+ * unit each iteration, calling the closure once per iteration. The closure may accept a single
+ * {@link java.time.temporal.Temporal} argument.
* <p>
* The particular unit incremented by depends on the specific sub-type of {@link java.time.temporal.Temporal}.
* Most sub-types use a unit of {@link java.time.temporal.ChronoUnit#SECONDS} except for
@@ -97,8 +103,8 @@ public class DateTimeGroovyMethods {
}
/**
- * Iterates from this to {@code to}, inclusive, incrementing by one {@code unit} each iteration,
- * calling the closure once per iteration. The closure may accept a single
+ * Iterates from this to the {@code to} {@link java.time.temporal.Temporal}, inclusive, incrementing by one
+ * {@code unit} each iteration, calling the closure once per iteration. The closure may accept a single
* {@link java.time.temporal.Temporal} argument.
*
* If the unit is too large to iterate to the second Temporal exactly, such as iterating from two LocalDateTimes
@@ -113,8 +119,8 @@ public class DateTimeGroovyMethods {
* @since 3.0
*/
public static void upto(Temporal from, Temporal to, TemporalUnit unit, Closure closure) {
- if (from.until(to, unit) >= 0) {
- for (Temporal i = from; i.until(to, unit) >= 0; i = i.plus(1, unit)) {
+ if (isUptoEligible(from, to)) {
+ for (Temporal i = from; isUptoEligible(i, to); i = i.plus(1, unit)) {
closure.call(i);
}
} else {
@@ -124,10 +130,26 @@ public class DateTimeGroovyMethods {
}
/**
+ * Returns true if the {@code from} can be iterated up to {@code to}.
+ */
+ private static boolean isUptoEligible(Temporal from, Temporal to) {
+ switch ((ChronoUnit) defaultUnitFor(from)) {
+ case YEARS:
+ return isNonnegative(DefaultGroovyStaticMethods.between(null, (Year) from, (Year) to));
+ case MONTHS:
+ return isNonnegative(DefaultGroovyStaticMethods.between(null, (YearMonth) from, (YearMonth) to));
+ case DAYS:
+ return isNonnegative(ChronoPeriod.between((ChronoLocalDate) from, (ChronoLocalDate) to));
+ default:
+ return isNonnegative(Duration.between(from, to));
+ }
+ }
+
+ /**
* Iterates from this to the {@code to} {@link java.time.temporal.Temporal}, inclusive, decrementing by one
* unit each iteration, calling the closure once per iteration. The closure may accept a single
* {@link java.time.temporal.Temporal} argument.
- *
+ * <p>
* The particular unit decremented by depends on the specific sub-type of {@link java.time.temporal.Temporal}.
* Most sub-types use a unit of {@link java.time.temporal.ChronoUnit#SECONDS} except for
* <ul>
@@ -163,8 +185,8 @@ public class DateTimeGroovyMethods {
* @since 3.0
*/
public static void downto(Temporal from, Temporal to, TemporalUnit unit, Closure closure) {
- if (from.until(to, unit) <= 0) {
- for (Temporal i = from; i.until(to, unit) <= 0; i = i = i.minus(1, unit)) {
+ if (isDowntoEligible(from, to)) {
+ for (Temporal i = from; isDowntoEligible(i, to); i = i.minus(1, unit)) {
closure.call(i);
}
} else {
@@ -174,6 +196,22 @@ public class DateTimeGroovyMethods {
}
/**
+ * Returns true if the {@code from} can be iterated down to {@code to}.
+ */
+ private static boolean isDowntoEligible(Temporal from, Temporal to) {
+ switch ((ChronoUnit) defaultUnitFor(from)) {
+ case YEARS:
+ return isNonpositive(DefaultGroovyStaticMethods.between(null, (Year) from, (Year) to));
+ case MONTHS:
+ return isNonpositive(DefaultGroovyStaticMethods.between(null, (YearMonth) from, (YearMonth) to));
+ case DAYS:
+ return isNonpositive(ChronoPeriod.between((ChronoLocalDate) from, (ChronoLocalDate) to));
+ default:
+ return isNonpositive(Duration.between(from, to));
+ }
+ }
+
+ /**
* Returns a {@link java.time.Duration} of time between this (inclusive) and {@code other} (exclusive).
*
* @param self a Temporal
@@ -315,6 +353,39 @@ public class DateTimeGroovyMethods {
return self.dividedBy(scalar);
}
+ /**
+ * Returns true if this duration is positive, excluding zero.
+ *
+ * @param self a Duration
+ * @return true if positive
+ * @since 3.0
+ */
+ public static boolean isPositive(final Duration self) {
+ return !self.isZero() && !self.isNegative();
+ }
+
+ /**
+ * Returns true if this duration is zero or positive.
+ *
+ * @param self a Duration
+ * @return true if nonnegative
+ * @since 3.0
+ */
+ public static boolean isNonnegative(final Duration self) {
+ return self.isZero() || !self.isNegative();
+ }
+
+ /**
+ * Returns true if this duration is zero or negative.
+ *
+ * @param self a Duration
+ * @return true if nonpositive
+ * @since 3.0
+ */
+ public static boolean isNonpositive(final Duration self) {
+ return self.isZero() || self.isNegative();
+ }
+
/* ******** java.time.Instant extension methods ******** */
/**
@@ -613,7 +684,7 @@ public class DateTimeGroovyMethods {
* @since 3.0
*/
public static LocalDateTime clearTime(final LocalDateTime self) {
- return self.truncatedTo(ChronoUnit.DAYS);
+ return self.truncatedTo(DAYS);
}
/**
@@ -959,7 +1030,7 @@ public class DateTimeGroovyMethods {
* @since 3.0
*/
public static OffsetDateTime clearTime(final OffsetDateTime self) {
- return self.truncatedTo(ChronoUnit.DAYS);
+ return self.truncatedTo(DAYS);
}
/**
@@ -1254,6 +1325,39 @@ public class DateTimeGroovyMethods {
return self.multipliedBy(scalar);
}
+ /**
+ * Returns true if this period is positive, excluding zero.
+ *
+ * @param self a ChronoPeriod
+ * @return true if positive
+ * @since 3.0
+ */
+ public static boolean isPositive(final ChronoPeriod self) {
+ return !self.isZero() && !self.isNegative();
+ }
+
+ /**
+ * Returns true if this period is zero or positive.
+ *
+ * @param self a ChronoPeriod
+ * @return true if nonnegative
+ * @since 3.0
+ */
+ public static boolean isNonnegative(final ChronoPeriod self) {
+ return self.isZero() || !self.isNegative();
+ }
+
+ /**
+ * Returns true if this period is zero or negative.
+ *
+ * @param self a ChronoPeriod
+ * @return true if nonpositive
+ * @since 3.0
+ */
+ public static boolean isNonpositive(final ChronoPeriod self) {
+ return self.isZero() || self.isNegative();
+ }
+
/* ******** java.time.Year extension methods ******** */
/**
@@ -1514,7 +1618,7 @@ public class DateTimeGroovyMethods {
* @since 3.0
*/
public static ZonedDateTime clearTime(final ZonedDateTime self) {
- return self.truncatedTo(ChronoUnit.DAYS);
+ return self.truncatedTo(DAYS);
}
/**
http://git-wip-us.apache.org/repos/asf/groovy/blob/da357c86/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyStaticMethods.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyStaticMethods.java b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyStaticMethods.java
index 55d3d07..0ad2f33 100644
--- a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyStaticMethods.java
+++ b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyStaticMethods.java
@@ -299,15 +299,15 @@ public class DefaultGroovyStaticMethods {
return tempFile;
}
- /**
- * Get the current time in seconds
- *
- * @param self placeholder variable used by Groovy categories; ignored for default static methods
- * @return the difference, measured in seconds, between
- * the current time and midnight, January 1, 1970 UTC.
- * @see System#currentTimeMillis()
- */
- public static long currentTimeSeconds(System self){
+ /**
+ * Get the current time in seconds
+ *
+ * @param self placeholder variable used by Groovy categories; ignored for default static methods
+ * @return the difference, measured in seconds, between
+ * the current time and midnight, January 1, 1970 UTC.
+ * @see System#currentTimeMillis()
+ */
+ public static long currentTimeSeconds(System self){
return System.currentTimeMillis() / 1000;
}
@@ -476,4 +476,43 @@ public class DefaultGroovyStaticMethods {
return DateTimeGroovyMethods.getOffset(ZoneId.systemDefault());
}
+ /**
+ * Obtains a Period consisting of the number of years between two {@link java.time.Year} instances.
+ * The months and days of the Period will be zero.
+ * The result of this method can be a negative period if the end is before the start.
+ *
+ * @param type placeholder variable used by Groovy categories; ignored for default static methods
+ * @param startInclusive the start {@link java.time.Year}, inclusive, not null
+ * @param endExclusive the end {@link java.time.Year}, exclusive, not null
+ * @return a Period between the years
+ * @see java.time.Period#between(LocalDate, LocalDate)
+ */
+ public static Period between(final Period type, Year startInclusive, Year endExclusive) {
+ MonthDay now = MonthDay.of(Month.JANUARY, 1);
+ return Period.between(
+ DateTimeGroovyMethods.leftShift(startInclusive, now),
+ DateTimeGroovyMethods.leftShift(endExclusive, now))
+ .withDays(0)
+ .withMonths(0);
+ }
+
+ /**
+ * Obtains a Period consisting of the number of years and months between two {@link java.time.YearMonth} instances.
+ * The days of the Period will be zero.
+ * The result of this method can be a negative period if the end is before the start.
+ *
+ * @param type placeholder variable used by Groovy categories; ignored for default static methods
+ * @param startInclusive the start {@link java.time.YearMonth}, inclusive, not null
+ * @param endExclusive the end {@link java.time.YearMonth}, exclusive, not null
+ * @return a Period between the year/months
+ * @see java.time.Period#between(LocalDate, LocalDate)
+ */
+ public static Period between(final Period type, YearMonth startInclusive, YearMonth endExclusive) {
+ int dayOfMonth = 1;
+ return Period.between(
+ DateTimeGroovyMethods.leftShift(startInclusive, dayOfMonth),
+ DateTimeGroovyMethods.leftShift(endExclusive, dayOfMonth))
+ .withDays(0);
+ }
+
}
http://git-wip-us.apache.org/repos/asf/groovy/blob/da357c86/src/test/groovy/DateTimeTest.groovy
----------------------------------------------------------------------
diff --git a/src/test/groovy/DateTimeTest.groovy b/src/test/groovy/DateTimeTest.groovy
index 088f460..46eae17 100644
--- a/src/test/groovy/DateTimeTest.groovy
+++ b/src/test/groovy/DateTimeTest.groovy
@@ -170,6 +170,22 @@ class DateTimeTest extends GroovyTestCase {
assert (duration * 2).seconds == 120
}
+ void testDurationIsPositiveIsNonnegativeIsNonpositive() {
+ def pos = Duration.ofSeconds(10)
+ assert pos.isPositive() == true
+ assert pos.isNonpositive() == false
+ assert pos.isNonnegative() == true
+
+ def neg = Duration.ofSeconds(-10)
+ assert neg.isPositive() == false
+ assert neg.isNonpositive() == true
+ assert neg.isNonnegative() == false
+
+ assert Duration.ZERO.isPositive() == false
+ assert Duration.ZERO.isNonpositive() == true
+ assert Duration.ZERO.isNonnegative() == true
+ }
+
void testPeriodPositiveNegative() {
def positivePeriod = Period.of(1,2,3)
Period madeNegative = -positivePeriod
@@ -192,6 +208,22 @@ class DateTimeTest extends GroovyTestCase {
assert doublePeriod.days == 2
}
+ void testPeriodIsPositiveIsNonnegativeIsNonpositive() {
+ def pos = Period.ofDays(10)
+ assert pos.isPositive() == true
+ assert pos.isNonpositive() == false
+ assert pos.isNonnegative() == true
+
+ def neg = Period.ofDays(-10)
+ assert neg.isPositive() == false
+ assert neg.isNonpositive() == true
+ assert neg.isNonnegative() == false
+
+ assert Period.ZERO.isPositive() == false
+ assert Period.ZERO.isNonpositive() == true
+ assert Period.ZERO.isNonnegative() == true
+ }
+
void testTemporalGetAt() {
def epoch = Instant.ofEpochMilli(0)
assert epoch[ChronoField.INSTANT_SECONDS] == 0
@@ -235,50 +267,66 @@ class DateTimeTest extends GroovyTestCase {
assert yearMonthPeriod.months == 2
}
- void testUptoDowntoWithSecondsDefaultUnit() {
+ void testUptoSelfWithDefaultUnit() {
def epoch = Instant.ofEpochMilli(0)
- int uptoSelfIterations = 0
+ int iterations = 0
epoch.upto(epoch) {
- ++uptoSelfIterations
- assert it == epoch : 'upto closure should be provided with arg'
+ ++iterations
+ assert it == epoch: 'upto closure should be provided with arg'
}
- assert uptoSelfIterations == 1 : 'Iterating upto same value should call closure once'
+ assert iterations == 1: 'Iterating upto same value should call closure once'
+ }
- int downtoSelfIterations = 0
+ void testDowntoSelfWithDefaultUnit() {
+ def epoch = Instant.ofEpochMilli(0)
+ int iterations = 0
epoch.downto(epoch) {
- ++downtoSelfIterations
- assert it == epoch : 'downto closure should be provided with arg'
+ ++iterations
+ assert it == epoch: 'downto closure should be provided with arg'
}
- assert downtoSelfIterations == 1 : 'Iterating downto same value should call closure once'
+ assert iterations == 1: 'Iterating downto same value should call closure once'
+ }
+
+ void testUptoWithSecondsDefaultUnit() {
+ def epoch = Instant.ofEpochMilli(0)
- int uptoPlusOneIterations = 0
- Instant endUp = null
+ int iterations = 0
+ Instant end = null
epoch.upto(epoch + 1) {
- ++uptoPlusOneIterations
- endUp = it
+ ++iterations
+ end = it
}
- assert uptoPlusOneIterations == 2 : 'Iterating upto Temporal+1 value should call closure twice'
- assert endUp.epochSecond == 1 : 'Unexpected upto final value'
+ assert iterations == 2: 'Iterating upto Temporal+1 value should call closure twice'
+ assert end.epochSecond == 1: 'Unexpected upto final value'
+ }
+
+ void testDowntoWithSecondsDefaultUnit() {
+ def epoch = Instant.ofEpochMilli(0)
- int downtoPlusOneIterations = 0
- Instant endDown = null
+ int iterations = 0
+ Instant end = null
epoch.downto(epoch - 1) {
- ++downtoPlusOneIterations
- endDown = it
+ ++iterations
+ end = it
}
- assert downtoPlusOneIterations == 2 : 'Iterating downto Temporal+1 value should call closure twice'
- assert endDown.epochSecond == -1 : 'Unexpected downto final value'
+ assert iterations == 2 : 'Iterating downto Temporal+1 value should call closure twice'
+ assert end.epochSecond == -1 : 'Unexpected downto final value'
}
- void testUptoDowntoWithYearsDefaultUnit() {
- // non-ChronoUnit.SECOND iterations
+ void testUptoWithYearsDefaultUnit() {
def endYear = null
Year.of(1970).upto(Year.of(1971)) { year -> endYear = year }
assert endYear.value == 1971
}
- void testUptoDownWithMonthsDefaultUnit() {
+ void testDowntoWithYearsDefaultUnit() {
+ def endYear = null
+ Year.of(1971).downto(Year.of(1970)) { year -> endYear = year }
+ assert endYear.value == 1970
+ }
+
+ void testUptoWithMonthsDefaultUnit() {
def endYearMonth = null
YearMonth.of(1970, Month.JANUARY).upto(YearMonth.of(1970, Month.FEBRUARY)) { yearMonth ->
endYearMonth = yearMonth
@@ -286,21 +334,42 @@ class DateTimeTest extends GroovyTestCase {
assert endYearMonth.month == Month.FEBRUARY
}
- void testUptoDowntoWithDaysDefaultUnit() {
+ void testDowntoWithMonthsDefaultUnit() {
+ def endYearMonth = null
+ YearMonth.of(1970, Month.FEBRUARY).downto(YearMonth.of(1970, Month.JANUARY)) { yearMonth ->
+ endYearMonth = yearMonth
+ }
+ assert endYearMonth.month == Month.JANUARY
+ }
+
+ void testUptoWithDaysDefaultUnit() {
def endLocalDate = null
- LocalDate.of(1970, Month.JANUARY, 1).upto(LocalDate.of(1970, Month.JANUARY, 2)) { localDate ->
+ LocalDate.of(1970, Month.JANUARY, 1).upto(LocalDate.of(1970, Month.JANUARY, 2)) { localDate ->
endLocalDate = localDate
}
assert endLocalDate.dayOfMonth == 2
}
- void testUptoDowntoWithIllegalReversedArguments() {
+ void testDowntoWithDaysDefaultUnit() {
+ def endLocalDate = null
+ LocalDate.of(1970, Month.JANUARY, 2).downto(LocalDate.of(1970, Month.JANUARY, 1)) { localDate ->
+ endLocalDate = localDate
+ }
+ assert endLocalDate.dayOfMonth == 1
+ }
+
+ void testUptoWithIllegalReversedArguments() {
def epoch = Instant.ofEpochMilli(0)
try {
epoch.upto(epoch - 1) {
fail('upto() should fail when passed earlier arg')
}
- } catch (GroovyRuntimeException e) {}
+ } catch (GroovyRuntimeException e) {
+ }
+ }
+
+ void testDowntoWithIllegalReversedArguments() {
+ def epoch = Instant.ofEpochMilli(0)
try {
epoch.downto(epoch + 1) {
fail('downto() should fail when passed earlier arg')
@@ -308,27 +377,56 @@ class DateTimeTest extends GroovyTestCase {
} catch (GroovyRuntimeException e) {}
}
- void testUptoDowntoWithCustomUnit() {
- LocalDateTime ldt1 = LocalDateTime.of(2018, Month.FEBRUARY, 11, 22, 9, 34)
- LocalDateTime ldt2 = ldt1.plusMinutes(1)
+ void testUptoSelfWithCustomUnit() {
+ def today = LocalDate.now()
- int upIterations = 0
- LocalDateTime endUp = null
- ldt1.upto(ldt2, ChronoUnit.DAYS) {
- ++upIterations
- endUp = it
+ int iterations = 0
+ today.upto(today, ChronoUnit.MONTHS) {
+ ++iterations
+ assert it == today: 'upto closure should be provided with arg'
}
- assert upIterations == 2
- assert endUp.dayOfMonth == 12 : "Upto should have iterated by DAYS"
-
- int downIterations = 0
- LocalDateTime endDown = null
- ldt2.downto(ldt1, ChronoUnit.YEARS) {
- ++downIterations
- endDown = it
+ assert iterations == 1: 'Iterating upto same value should call closure once'
+ }
+
+ void testDowntoSelfWithCustomUnit() {
+ def today = LocalDate.now()
+
+ int iterations = 0
+ today.downto(today, ChronoUnit.MONTHS) {
+ ++iterations
+ assert it == today: 'downto closure should be provided with arg'
}
- assert downIterations == 2
- assert endDown.year == 2017 : "Downto should have iterated by YEARS"
+ assert iterations == 1: 'Iterating downto same value should call closure once'
+ }
+
+ void testUptoWithCustomUnit() {
+ LocalDateTime from = LocalDateTime.of(2018, Month.FEBRUARY, 11, 22, 9, 34)
+ // one second beyond one iteration
+ LocalDateTime to = from.plusDays(1).plusSeconds(1)
+
+ int iterations = 0
+ LocalDateTime end = null
+ from.upto(to, ChronoUnit.DAYS) {
+ ++iterations
+ end = it
+ }
+ assert iterations == 2
+ assert end.dayOfMonth == 12: "Upto should have iterated by DAYS twice"
+ }
+
+ void testDowntoWithCustomUnit() {
+ LocalDateTime from = LocalDateTime.of(2018, Month.FEBRUARY, 11, 22, 9, 34)
+ // one day beyond one iteration
+ LocalDateTime to = from.minusYears(1).minusDays(1)
+
+ int iterations = 0
+ LocalDateTime end = null
+ from.downto(to, ChronoUnit.YEARS) {
+ ++iterations
+ end = it
+ }
+ assert iterations == 2
+ assert end.year == 2017 : "Downto should have iterated by YEARS twice"
}
void testInstantToDateToCalendar() {
@@ -677,4 +775,19 @@ class DateTimeTest extends GroovyTestCase {
assert [zdt.hour, zdt.minute, zdt.second] == [21, 43, 03]
assert zdt.nano == 2 * 1e6
}
+
+ void testPeriodBetweenYears() {
+ def period = Period.between(Year.of(2000), Year.of(2010))
+ assert period.years == 10
+ assert period.months == 0
+ assert period.days == 0
+ }
+
+ void testPeriodBetweenYearMonths() {
+ def period = Period.between(YearMonth.of(2018, Month.MARCH), YearMonth.of(2016, Month.APRIL))
+
+ assert period.years == -1
+ assert period.months == -11
+ assert period.days == 0
+ }
}