Created
August 30, 2013 16:59
-
-
Save jodastephen/6392028 to your computer and use it in GitHub Desktop.
Patch to implement ChronoPeriod
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# HG changeset patch | |
# User scolebourne | |
# Date 1377881894 -3600 | |
# Node ID 774445b243ad6e5f9cf2b6a65af0e5ae90ac4f6b | |
# Parent 66f2d8a694e598397e077a683d773692e7a4fb49 | |
Add ChronoPeriod | |
Make Period ISO-only, adding a Chronology-specific period concept | |
Fixes #331, JBS 8023762 | |
diff --git a/src/share/classes/java/time/LocalDate.java b/src/share/classes/java/time/LocalDate.java | |
--- a/src/share/classes/java/time/LocalDate.java | |
+++ b/src/share/classes/java/time/LocalDate.java | |
@@ -1642,12 +1642,12 @@ | |
* </pre> | |
* The choice should be made based on which makes the code more readable. | |
* | |
- * @param endDate the end date, exclusive, which may be in any chronology, not null | |
+ * @param endDateExclusive the end date, exclusive, which may be in any chronology, not null | |
* @return the period between this date and the end date, not null | |
*/ | |
@Override | |
- public Period until(ChronoLocalDate endDate) { | |
- LocalDate end = LocalDate.from(endDate); | |
+ public Period until(ChronoLocalDate endDateExclusive) { | |
+ LocalDate end = LocalDate.from(endDateExclusive); | |
long totalMonths = end.getProlepticMonth() - this.getProlepticMonth(); // safe | |
int days = end.day - this.day; | |
if (totalMonths > 0 && days < 0) { | |
diff --git a/src/share/classes/java/time/Period.java b/src/share/classes/java/time/Period.java | |
--- a/src/share/classes/java/time/Period.java | |
+++ b/src/share/classes/java/time/Period.java | |
@@ -61,7 +61,6 @@ | |
*/ | |
package java.time; | |
-import static java.time.temporal.ChronoField.MONTH_OF_YEAR; | |
import static java.time.temporal.ChronoUnit.DAYS; | |
import static java.time.temporal.ChronoUnit.MONTHS; | |
import static java.time.temporal.ChronoUnit.YEARS; | |
@@ -73,14 +72,17 @@ | |
import java.io.ObjectStreamException; | |
import java.io.Serializable; | |
import java.time.chrono.ChronoLocalDate; | |
+import java.time.chrono.ChronoPeriod; | |
import java.time.chrono.Chronology; | |
+import java.time.chrono.IsoChronology; | |
import java.time.format.DateTimeParseException; | |
import java.time.temporal.ChronoUnit; | |
import java.time.temporal.Temporal; | |
+import java.time.temporal.TemporalAccessor; | |
import java.time.temporal.TemporalAmount; | |
+import java.time.temporal.TemporalQuery; | |
import java.time.temporal.TemporalUnit; | |
import java.time.temporal.UnsupportedTemporalTypeException; | |
-import java.time.temporal.ValueRange; | |
import java.util.Arrays; | |
import java.util.Collections; | |
import java.util.List; | |
@@ -89,12 +91,13 @@ | |
import java.util.regex.Pattern; | |
/** | |
- * A date-based amount of time, such as '2 years, 3 months and 4 days'. | |
+ * A date-based amount of time in the ISO-8601 calendar system, | |
+ * such as '2 years, 3 months and 4 days'. | |
* <p> | |
* This class models a quantity or amount of time in terms of years, months and days. | |
* See {@link Duration} for the time-based equivalent to this class. | |
* <p> | |
- * Durations and period differ in their treatment of daylight savings time | |
+ * Durations and periods differ in their treatment of daylight savings time | |
* when added to {@link ZonedDateTime}. A {@code Duration} will add an exact | |
* number of seconds, thus a duration of one day is always exactly 24 hours. | |
* By contrast, a {@code Period} will add a conceptual day, trying to maintain | |
@@ -110,14 +113,12 @@ | |
* {@link ChronoUnit#MONTHS MONTHS} and {@link ChronoUnit#DAYS DAYS}. | |
* All three fields are always present, but may be set to zero. | |
* <p> | |
- * The period may be used with any calendar system. | |
- * The meaning of a "year" or "month" is only applied when the object is added to a date. | |
+ * The ISO-8601 calendar system is the modern civil calendar system used today | |
+ * in most of the world. It is equivalent to the proleptic Gregorian calendar | |
+ * system, in which today's rules for leap years are applied for all time. | |
* <p> | |
* The period is modeled as a directed amount of time, meaning that individual parts of the | |
* period may be negative. | |
- * <p> | |
- * The months and years fields may be {@linkplain #normalized() normalized}. | |
- * The normalization assumes a 12 month year, so is not appropriate for all calendar systems. | |
* | |
* @implSpec | |
* This class is immutable and thread-safe. | |
@@ -125,7 +126,7 @@ | |
* @since 1.8 | |
*/ | |
public final class Period | |
- implements TemporalAmount, Serializable { | |
+ implements ChronoPeriod, Serializable { | |
/** | |
* A constant for a period of zero. | |
@@ -140,6 +141,7 @@ | |
*/ | |
private final static Pattern PATTERN = | |
Pattern.compile("([-+]?)P(?:([-+]?[0-9]+)Y)?(?:([-+]?[0-9]+)M)?(?:([-+]?[0-9]+)W)?(?:([-+]?[0-9]+)D)?", Pattern.CASE_INSENSITIVE); | |
+ | |
/** | |
* The set of supported units. | |
*/ | |
@@ -234,12 +236,14 @@ | |
* <p> | |
* This obtains a period based on the specified amount. | |
* A {@code TemporalAmount} represents an amount of time, which may be | |
- * date-based or time-based, which this factory extracts to a period. | |
+ * date-based or time-based, which this factory extracts to a {@code Period}. | |
* <p> | |
* The conversion loops around the set of units from the amount and uses | |
* the {@link ChronoUnit#YEARS YEARS}, {@link ChronoUnit#MONTHS MONTHS} | |
* and {@link ChronoUnit#DAYS DAYS} units to create a period. | |
* If any other units are found then an exception is thrown. | |
+ * <p> | |
+ * If the amount is a {@code ChronoPeriod} then it must use the ISO chronology. | |
* | |
* @param amount the temporal amount to convert, not null | |
* @return the equivalent period, not null | |
@@ -247,6 +251,14 @@ | |
* @throws ArithmeticException if the amount of years, months or days exceeds an int | |
*/ | |
public static Period from(TemporalAmount amount) { | |
+ if (amount instanceof Period) { | |
+ return (Period) amount; | |
+ } | |
+ if (amount instanceof ChronoPeriod) { | |
+ if (IsoChronology.INSTANCE.equals(((ChronoPeriod) amount).getChronology()) == false) { | |
+ throw new DateTimeException("Period requires ISO chronology: " + amount); | |
+ } | |
+ } | |
Objects.requireNonNull(amount, "amount"); | |
int years = 0; | |
int months = 0; | |
@@ -358,13 +370,13 @@ | |
* The result of this method can be a negative period if the end is before the start. | |
* The negative sign will be the same in each of year, month and day. | |
* | |
- * @param startDate the start date, inclusive, not null | |
- * @param endDate the end date, exclusive, not null | |
+ * @param startDateInclusive the start date, inclusive, not null | |
+ * @param endDateExclusive the end date, exclusive, not null | |
* @return the period between this date and the end date, not null | |
* @see ChronoLocalDate#until(ChronoLocalDate) | |
*/ | |
- public static Period between(LocalDate startDate, LocalDate endDate) { | |
- return startDate.until(endDate); | |
+ public static Period between(LocalDate startDateInclusive, LocalDate endDateExclusive) { | |
+ return startDateInclusive.until(endDateExclusive); | |
} | |
//----------------------------------------------------------------------- | |
@@ -439,6 +451,21 @@ | |
return SUPPORTED_UNITS; | |
} | |
+ /** | |
+ * Gets the chronology of this period, which is the ISO calendar system. | |
+ * <p> | |
+ * The {@code Chronology} represents the calendar system in use. | |
+ * The ISO-8601 calendar system is the modern civil calendar system used today | |
+ * in most of the world. It is equivalent to the proleptic Gregorian calendar | |
+ * system, in which today's rules for leap years are applied for all time. | |
+ * | |
+ * @return the ISO chronology, not null | |
+ */ | |
+ @Override | |
+ public IsoChronology getChronology() { | |
+ return IsoChronology.INSTANCE; | |
+ } | |
+ | |
//----------------------------------------------------------------------- | |
/** | |
* Checks if all three units of this period are zero. | |
@@ -468,7 +495,7 @@ | |
* <p> | |
* This returns the years unit. | |
* <p> | |
- * The months unit is not normalized with the years unit. | |
+ * The months unit is not automatically normalized with the years unit. | |
* This means that a period of "15 months" is different to a period | |
* of "1 year and 3 months". | |
* | |
@@ -483,7 +510,7 @@ | |
* <p> | |
* This returns the months unit. | |
* <p> | |
- * The months unit is not normalized with the years unit. | |
+ * The months unit is not automatically normalized with the years unit. | |
* This means that a period of "15 months" is different to a period | |
* of "1 year and 3 months". | |
* | |
@@ -511,7 +538,7 @@ | |
* This sets the amount of the years unit in a copy of this period. | |
* The months and days units are unaffected. | |
* <p> | |
- * The months unit is not normalized with the years unit. | |
+ * The months unit is not automatically normalized with the years unit. | |
* This means that a period of "15 months" is different to a period | |
* of "1 year and 3 months". | |
* <p> | |
@@ -533,7 +560,7 @@ | |
* This sets the amount of the months unit in a copy of this period. | |
* The years and days units are unaffected. | |
* <p> | |
- * The months unit is not normalized with the years unit. | |
+ * The months unit is not automatically normalized with the years unit. | |
* This means that a period of "15 months" is different to a period | |
* of "1 year and 3 months". | |
* <p> | |
@@ -572,21 +599,28 @@ | |
* Returns a copy of this period with the specified period added. | |
* <p> | |
* This operates separately on the years, months and days. | |
+ * No normalization is performed. | |
* <p> | |
* For example, "1 year, 6 months and 3 days" plus "2 years, 2 months and 2 days" | |
* returns "3 years, 8 months and 5 days". | |
* <p> | |
+ * The specified amount is typically an instance of {@Period}. | |
+ * Other types are interpreted using {@link Period#from(TemporalAmount)}. | |
+ * <p> | |
* This instance is immutable and unaffected by this method call. | |
* | |
* @param amountToAdd the period to add, not null | |
* @return a {@code Period} based on this period with the requested period added, not null | |
+ * @throws DateTimeException if the specified amount has a non-ISO chronology or | |
+ * contains an invalid unit | |
* @throws ArithmeticException if numeric overflow occurs | |
*/ | |
- public Period plus(Period amountToAdd) { | |
+ public Period plus(TemporalAmount amountToAdd) { | |
+ Period isoAmount = Period.from(amountToAdd); | |
return create( | |
- Math.addExact(years, amountToAdd.years), | |
- Math.addExact(months, amountToAdd.months), | |
- Math.addExact(days, amountToAdd.days)); | |
+ Math.addExact(years, isoAmount.years), | |
+ Math.addExact(months, isoAmount.months), | |
+ Math.addExact(days, isoAmount.days)); | |
} | |
/** | |
@@ -654,21 +688,28 @@ | |
* Returns a copy of this period with the specified period subtracted. | |
* <p> | |
* This operates separately on the years, months and days. | |
+ * No normalization is performed. | |
* <p> | |
* For example, "1 year, 6 months and 3 days" minus "2 years, 2 months and 2 days" | |
* returns "-1 years, 4 months and 1 day". | |
* <p> | |
+ * The specified amount is typically an instance of {@Period}. | |
+ * Other types are interpreted using {@link Period#from(TemporalAmount)}. | |
+ * <p> | |
* This instance is immutable and unaffected by this method call. | |
* | |
* @param amountToSubtract the period to subtract, not null | |
* @return a {@code Period} based on this period with the requested period subtracted, not null | |
+ * @throws DateTimeException if the specified amount has a non-ISO chronology or | |
+ * contains an invalid unit | |
* @throws ArithmeticException if numeric overflow occurs | |
*/ | |
- public Period minus(Period amountToSubtract) { | |
+ public Period minus(TemporalAmount amountToSubtract) { | |
+ Period isoAmount = Period.from(amountToSubtract); | |
return create( | |
- Math.subtractExact(years, amountToSubtract.years), | |
- Math.subtractExact(months, amountToSubtract.months), | |
- Math.subtractExact(days, amountToSubtract.days)); | |
+ Math.subtractExact(years, isoAmount.years), | |
+ Math.subtractExact(months, isoAmount.months), | |
+ Math.subtractExact(days, isoAmount.days)); | |
} | |
/** | |
@@ -766,8 +807,7 @@ | |
//----------------------------------------------------------------------- | |
/** | |
- * Returns a copy of this period with the years and months normalized | |
- * using a 12 month year. | |
+ * Returns a copy of this period with the years and months normalized. | |
* <p> | |
* This normalizes the years and months units, leaving the days unit unchanged. | |
* The months unit is adjusted to have an absolute value less than 11, | |
@@ -778,8 +818,6 @@ | |
* For example, a period of "1 year and -25 months" will be normalized to | |
* "-1 year and -1 month". | |
* <p> | |
- * This normalization uses a 12 month year which is not valid for all calendar systems. | |
- * <p> | |
* This instance is immutable and unaffected by this method call. | |
* | |
* @return a {@code Period} based on this period with excess months normalized to years, not null | |
@@ -796,13 +834,11 @@ | |
} | |
/** | |
- * Gets the total number of months in this period using a 12 month year. | |
+ * Gets the total number of months in this period. | |
* <p> | |
* This returns the total number of months in the period by multiplying the | |
* number of years by 12 and adding the number of months. | |
* <p> | |
- * This uses a 12 month year which is not valid for all calendar systems. | |
- * <p> | |
* This instance is immutable and unaffected by this method call. | |
* | |
* @return the total number of months in the period, may be negative | |
@@ -817,6 +853,7 @@ | |
* <p> | |
* This returns a temporal object of the same observable type as the input | |
* with this period added. | |
+ * If the temporal has a chronology, it must be the ISO chronology. | |
* <p> | |
* In most cases, it is clearer to reverse the calling pattern by using | |
* {@link Temporal#plus(TemporalAmount)}. | |
@@ -826,10 +863,17 @@ | |
* dateTime = dateTime.plus(thisPeriod); | |
* </pre> | |
* <p> | |
- * The calculation will add the years, then months, then days. | |
- * Only non-zero amounts will be added. | |
- * If the date-time has a calendar system with a fixed number of months in a | |
- * year, then the years and months will be combined before being added. | |
+ * The calculation operates as follows. | |
+ * First, the chronology of the temporal is checked to ensure it is ISO chronology or null. | |
+ * Second, if the months are zero, the years are added if non-zero, otherwise | |
+ * the combination of years and months is added if non-zero. | |
+ * Finally, any days are added. | |
+ * <p> | |
+ * This approach ensures that a partial period can be added to a partial date. | |
+ * For example, a period of years and/or months can be added to a {@code YearMonth}, | |
+ * but a period including days cannot. | |
+ * The approach also adds years and months together when necessary, which ensures | |
+ * correct behaviour at the end of the month. | |
* <p> | |
* This instance is immutable and unaffected by this method call. | |
* | |
@@ -840,18 +884,15 @@ | |
*/ | |
@Override | |
public Temporal addTo(Temporal temporal) { | |
- Objects.requireNonNull(temporal, "temporal"); | |
- if ((years | months) != 0) { | |
- long monthRange = monthRange(temporal); | |
- if (monthRange >= 0) { | |
- temporal = temporal.plus(years * monthRange + months, MONTHS); | |
- } else { | |
- if (years != 0) { | |
- temporal = temporal.plus(years, YEARS); | |
- } | |
- if (months != 0) { | |
- temporal = temporal.plus(months, MONTHS); | |
- } | |
+ validateChrono(temporal); | |
+ if (months == 0) { | |
+ if (years != 0) { | |
+ temporal = temporal.plus(years, YEARS); | |
+ } | |
+ } else { | |
+ long totalMonths = toTotalMonths(); | |
+ if (totalMonths != 0) { | |
+ temporal = temporal.plus(totalMonths, MONTHS); | |
} | |
} | |
if (days != 0) { | |
@@ -865,6 +906,7 @@ | |
* <p> | |
* This returns a temporal object of the same observable type as the input | |
* with this period subtracted. | |
+ * If the temporal has a chronology, it must be the ISO chronology. | |
* <p> | |
* In most cases, it is clearer to reverse the calling pattern by using | |
* {@link Temporal#minus(TemporalAmount)}. | |
@@ -874,10 +916,17 @@ | |
* dateTime = dateTime.minus(thisPeriod); | |
* </pre> | |
* <p> | |
- * The calculation will subtract the years, then months, then days. | |
- * Only non-zero amounts will be subtracted. | |
- * If the date-time has a calendar system with a fixed number of months in a | |
- * year, then the years and months will be combined before being subtracted. | |
+ * The calculation operates as follows. | |
+ * First, the chronology of the temporal is checked to ensure it is ISO chronology or null. | |
+ * Second, if the months are zero, the years are subtracted if non-zero, otherwise | |
+ * the combination of years and months is subtracted if non-zero. | |
+ * Finally, any days are subtracted. | |
+ * <p> | |
+ * This approach ensures that a partial period can be subtracted from a partial date. | |
+ * For example, a period of years and/or months can be subtracted from a {@code YearMonth}, | |
+ * but a period including days cannot. | |
+ * The approach also subtracts years and months together when necessary, which ensures | |
+ * correct behaviour at the end of the month. | |
* <p> | |
* This instance is immutable and unaffected by this method call. | |
* | |
@@ -888,18 +937,15 @@ | |
*/ | |
@Override | |
public Temporal subtractFrom(Temporal temporal) { | |
- Objects.requireNonNull(temporal, "temporal"); | |
- if ((years | months) != 0) { | |
- long monthRange = monthRange(temporal); | |
- if (monthRange >= 0) { | |
- temporal = temporal.minus(years * monthRange + months, MONTHS); | |
- } else { | |
- if (years != 0) { | |
- temporal = temporal.minus(years, YEARS); | |
- } | |
- if (months != 0) { | |
- temporal = temporal.minus(months, MONTHS); | |
- } | |
+ validateChrono(temporal); | |
+ if (months == 0) { | |
+ if (years != 0) { | |
+ temporal = temporal.minus(years, YEARS); | |
+ } | |
+ } else { | |
+ long totalMonths = toTotalMonths(); | |
+ if (totalMonths != 0) { | |
+ temporal = temporal.minus(totalMonths, MONTHS); | |
} | |
} | |
if (days != 0) { | |
@@ -909,26 +955,21 @@ | |
} | |
/** | |
- * Calculates the range of months based on the temporal. | |
- * | |
- * @param temporal the temporal, not null | |
- * @return the month range, negative if not fixed range | |
+ * Validates that the temporal has the correct chronology. | |
*/ | |
- private long monthRange(Temporal temporal) { | |
- if (temporal.isSupported(MONTH_OF_YEAR)) { | |
- ValueRange startRange = Chronology.from(temporal).range(MONTH_OF_YEAR); | |
- if (startRange.isFixed() && startRange.isIntValue()) { | |
- return startRange.getMaximum() - startRange.getMinimum() + 1; | |
- } | |
+ private void validateChrono(TemporalAccessor temporal) { | |
+ Objects.requireNonNull(temporal, "temporal"); | |
+ Chronology temporalChrono = temporal.query(TemporalQuery.chronology()); | |
+ if (temporalChrono != null && IsoChronology.INSTANCE.equals(temporalChrono) == false) { | |
+ throw new DateTimeException("Chronology mismatch, expected: ISO, actual: " + temporalChrono.getId()); | |
} | |
- return -1; | |
} | |
//----------------------------------------------------------------------- | |
/** | |
* Checks if this period is equal to another period. | |
* <p> | |
- * The comparison is based on the amounts held in the period. | |
+ * The comparison is based on the type {@Period} and each of the three amounts. | |
* To be equal, the years, months and days units must be individually equal. | |
* Note that this means that a period of "15 Months" is not equal to a period | |
* of "1 Year and 3 Months". | |
diff --git a/src/share/classes/java/time/chrono/ChronoLocalDate.java b/src/share/classes/java/time/chrono/ChronoLocalDate.java | |
--- a/src/share/classes/java/time/chrono/ChronoLocalDate.java | |
+++ b/src/share/classes/java/time/chrono/ChronoLocalDate.java | |
@@ -602,9 +602,12 @@ | |
long until(Temporal endDate, TemporalUnit unit); | |
/** | |
- * Calculates the period between this date and another date as a {@code Period}. | |
+ * Calculates the period between this date and another date as a {@code ChronoPeriod}. | |
* <p> | |
- * This calculates the period between two dates in terms of years, months and days. | |
+ * This calculates the period between two dates. All supplied chronologies | |
+ * calculate the period using years, months and days, however the | |
+ * {@ChronoPeriod} API allows the period to be represented using other units. | |
+ * <p> | |
* The start and end points are {@code this} and the specified date. | |
* The result will be negative if the end is before the start. | |
* The negative sign will be the same in each of year, month and day. | |
@@ -614,12 +617,12 @@ | |
* <p> | |
* This instance is immutable and unaffected by this method call. | |
* | |
- * @param endDate the end date, exclusive, which may be in any chronology, not null | |
+ * @param endDateExclusive the end date, exclusive, which may be in any chronology, not null | |
* @return the period between this date and the end date, not null | |
* @throws DateTimeException if the period cannot be calculated | |
* @throws ArithmeticException if numeric overflow occurs | |
*/ | |
- Period until(ChronoLocalDate endDate); | |
+ ChronoPeriod until(ChronoLocalDate endDateExclusive); | |
/** | |
* Formats this date using the specified formatter. | |
diff --git a/src/share/classes/java/time/chrono/ChronoPeriod.java b/src/share/classes/java/time/chrono/ChronoPeriod.java | |
new file mode 100644 | |
--- /dev/null | |
+++ b/src/share/classes/java/time/chrono/ChronoPeriod.java | |
@@ -0,0 +1,367 @@ | |
+/* | |
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. | |
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
+ * | |
+ * This code is free software; you can redistribute it and/or modify it | |
+ * under the terms of the GNU General Public License version 2 only, as | |
+ * published by the Free Software Foundation. Oracle designates this | |
+ * particular file as subject to the "Classpath" exception as provided | |
+ * by Oracle in the LICENSE file that accompanied this code. | |
+ * | |
+ * This code is distributed in the hope that it will be useful, but WITHOUT | |
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
+ * version 2 for more details (a copy is included in the LICENSE file that | |
+ * accompanied this code). | |
+ * | |
+ * You should have received a copy of the GNU General Public License version | |
+ * 2 along with this work; if not, write to the Free Software Foundation, | |
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
+ * | |
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
+ * or visit www.oracle.com if you need additional information or have any | |
+ * questions. | |
+ */ | |
+ | |
+/* | |
+ * This file is available under and governed by the GNU General Public | |
+ * License version 2 only, as published by the Free Software Foundation. | |
+ * However, the following notice accompanied the original version of this | |
+ * file: | |
+ * | |
+ * Copyright (c) 2013, Stephen Colebourne & Michael Nascimento Santos | |
+ * | |
+ * All rights reserved. | |
+ * | |
+ * Redistribution and use in source and binary forms, with or without | |
+ * modification, are permitted provided that the following conditions are met: | |
+ * | |
+ * * Redistributions of source code must retain the above copyright notice, | |
+ * this list of conditions and the following disclaimer. | |
+ * | |
+ * * Redistributions in binary form must reproduce the above copyright notice, | |
+ * this list of conditions and the following disclaimer in the documentation | |
+ * and/or other materials provided with the distribution. | |
+ * | |
+ * * Neither the name of JSR-310 nor the names of its contributors | |
+ * may be used to endorse or promote products derived from this software | |
+ * without specific prior written permission. | |
+ * | |
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR | |
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
+ */ | |
+package java.time.chrono; | |
+ | |
+import java.time.DateTimeException; | |
+import java.time.temporal.ChronoUnit; | |
+import java.time.temporal.Temporal; | |
+import java.time.temporal.TemporalAccessor; | |
+import java.time.temporal.TemporalAmount; | |
+import java.time.temporal.TemporalQuery; | |
+import java.time.temporal.TemporalUnit; | |
+import java.time.temporal.UnsupportedTemporalTypeException; | |
+import java.util.List; | |
+import java.util.Objects; | |
+ | |
+/** | |
+ * A date-based amount of time, such as '3 years, 4 months and 5 days' in an | |
+ * arbitrary chronology, intended for advanced globalization use cases. | |
+ * <p> | |
+ * This interface models a date-based amount of time in a calendar system. | |
+ * While most calendar systems use years, months and days, some do not. | |
+ * Therefore, this interface operates solely in terms of a set of supported | |
+ * units that are defined by the {@code Chronology}. | |
+ * The set of supported units is fixed for a given chronology. | |
+ * The amount of a supported unit may be set to zero. | |
+ * <p> | |
+ * The period is modeled as a directed amount of time, meaning that individual | |
+ * parts of the period may be negative. | |
+ * | |
+ * @implSpec | |
+ * This interface must be implemented with care to ensure other classes operate correctly. | |
+ * All implementations that can be instantiated must be final, immutable and thread-safe. | |
+ * Subclasses should be Serializable wherever possible. | |
+ * | |
+ * @since 1.8 | |
+ */ | |
+public interface ChronoPeriod | |
+ extends TemporalAmount { | |
+ | |
+ /** | |
+ * Obtains a {@code ChronoPeriod} consisting of amount of time between two dates. | |
+ * <p> | |
+ * The start date is included, but the end date is not. | |
+ * The period is calculated using {@link ChronoLocalDate#until(ChronoLocalDate)}. | |
+ * As such, the calculation is chronology specific. | |
+ * <p> | |
+ * The chronology of the first date is used. | |
+ * The chronology of the second date is ignored, with the date being converted | |
+ * to the target chronology system before the calculation starts. | |
+ * <p> | |
+ * The result of this method can be a negative period if the end is before the start. | |
+ * In most cases, the positive/negative sign will be the same in each of the supported fields. | |
+ * | |
+ * @param startDateInclusive the start date, inclusive, specifying the chronology of the calculation, not null | |
+ * @param endDateExclusive the end date, exclusive, in any chronology, not null | |
+ * @return the period between this date and the end date, not null | |
+ * @see ChronoLocalDate#until(ChronoLocalDate) | |
+ */ | |
+ public static ChronoPeriod between(ChronoLocalDate startDateInclusive, ChronoLocalDate endDateExclusive) { | |
+ Objects.requireNonNull(startDateInclusive, "startDateInclusive"); | |
+ Objects.requireNonNull(endDateExclusive, "endDateExclusive"); | |
+ return startDateInclusive.until(endDateExclusive); | |
+ } | |
+ | |
+ //----------------------------------------------------------------------- | |
+ /** | |
+ * Gets the value of the requested unit. | |
+ * <p> | |
+ * The supported units are chronology specific. | |
+ * They will typically be {@link ChronoUnit#YEARS YEARS}, | |
+ * {@link ChronoUnit#MONTHS MONTHS} and {@link ChronoUnit#DAYS DAYS}. | |
+ * Requesting an unsupported unit will throw an exception. | |
+ * | |
+ * @param unit the {@code TemporalUnit} for which to return the value | |
+ * @return the long value of the unit | |
+ * @throws DateTimeException if the unit is not supported | |
+ * @throws UnsupportedTemporalTypeException if the unit is not supported | |
+ */ | |
+ @Override | |
+ long get(TemporalUnit unit); | |
+ | |
+ /** | |
+ * Gets the set of units supported by this period. | |
+ * <p> | |
+ * The supported units are chronology specific. | |
+ * They will typically be {@link ChronoUnit#YEARS YEARS}, | |
+ * {@link ChronoUnit#MONTHS MONTHS} and {@link ChronoUnit#DAYS DAYS}. | |
+ * They are returned in order from largest to smallest. | |
+ * <p> | |
+ * This set can be used in conjunction with {@link #get(TemporalUnit)} | |
+ * to access the entire state of the period. | |
+ * | |
+ * @return a list containing the supported units, not null | |
+ */ | |
+ @Override | |
+ List<TemporalUnit> getUnits(); | |
+ | |
+ /** | |
+ * Gets the chronology that defines the meaning of the supported units. | |
+ * <p> | |
+ * The period is defined by the chronology. | |
+ * It controls the supported units and restricts addition/subtraction | |
+ * to {@code ChronoLocalDate} instances of the same chronology. | |
+ * | |
+ * @return the chronology defining the period, not null | |
+ */ | |
+ Chronology getChronology(); | |
+ | |
+ //----------------------------------------------------------------------- | |
+ /** | |
+ * Checks if all the supported units of this period are zero. | |
+ * | |
+ * @return true if this period is zero-length | |
+ */ | |
+ default boolean isZero() { | |
+ for (TemporalUnit unit : getUnits()) { | |
+ if (get(unit) != 0) { | |
+ return false; | |
+ } | |
+ } | |
+ return true; | |
+ } | |
+ | |
+ /** | |
+ * Checks if any of the supported units of this period are negative. | |
+ * | |
+ * @return true if any unit of this period is negative | |
+ */ | |
+ default boolean isNegative() { | |
+ for (TemporalUnit unit : getUnits()) { | |
+ if (get(unit) < 0) { | |
+ return true; | |
+ } | |
+ } | |
+ return false; | |
+ } | |
+ | |
+ //----------------------------------------------------------------------- | |
+ /** | |
+ * Returns a copy of this period with the specified period added. | |
+ * <p> | |
+ * If the specified amount is a {@code ChronoPeriod} then it must have | |
+ * the same chronology as this period. Implementations may choose to | |
+ * accept or reject other {@code TemporalAmount} implementations. | |
+ * <p> | |
+ * This instance is immutable and unaffected by this method call. | |
+ * | |
+ * @param amountToAdd the period to add, not null | |
+ * @return a {@code Period} based on this period with the requested period added, not null | |
+ * @throws ArithmeticException if numeric overflow occurs | |
+ */ | |
+ ChronoPeriod plus(TemporalAmount amountToAdd); | |
+ | |
+ /** | |
+ * Returns a copy of this period with the specified period subtracted. | |
+ * <p> | |
+ * If the specified amount is a {@code ChronoPeriod} then it must have | |
+ * the same chronology as this period. Implementations may choose to | |
+ * accept or reject other {@code TemporalAmount} implementations. | |
+ * <p> | |
+ * This instance is immutable and unaffected by this method call. | |
+ * | |
+ * @param amountToSubtract the period to subtract, not null | |
+ * @return a {@code Period} based on this period with the requested period subtracted, not null | |
+ * @throws ArithmeticException if numeric overflow occurs | |
+ */ | |
+ ChronoPeriod minus(TemporalAmount amountToSubtract); | |
+ | |
+ //----------------------------------------------------------------------- | |
+ /** | |
+ * Returns a new instance with each amount in this period in this period | |
+ * multiplied by the specified scalar. | |
+ * <p> | |
+ * This returns a period with each supported unit individually multiplied. | |
+ * For example, a period of "2 years, -3 months and 4 days" multiplied by | |
+ * 3 will return "6 years, -9 months and 12 days". | |
+ * No normalization is performed. | |
+ * | |
+ * @param scalar the scalar to multiply by, not null | |
+ * @return a {@code Period} based on this period with the amounts multiplied | |
+ * by the scalar, not null | |
+ * @throws ArithmeticException if numeric overflow occurs | |
+ */ | |
+ ChronoPeriod multipliedBy(int scalar); | |
+ | |
+ /** | |
+ * Returns a new instance with each amount in this period negated. | |
+ * <p> | |
+ * This returns a period with each supported unit individually negated. | |
+ * For example, a period of "2 years, -3 months and 4 days" will be | |
+ * negated to "-2 years, 3 months and -4 days". | |
+ * No normalization is performed. | |
+ * | |
+ * @return a {@code Period} based on this period with the amounts negated, not null | |
+ * @throws ArithmeticException if numeric overflow occurs, which only happens if | |
+ * one of the units has the value {@code Long.MIN_VALUE} | |
+ */ | |
+ default ChronoPeriod negated() { | |
+ return multipliedBy(-1); | |
+ } | |
+ | |
+ //----------------------------------------------------------------------- | |
+ /** | |
+ * Returns a copy of this period with the amounts of each unit normalized. | |
+ * <p> | |
+ * The process of normalization is specific to each calendar system. | |
+ * For example, in the ISO calendar system, the years and months are | |
+ * normalized but the days are not, such that "15 months" would be | |
+ * normalized to "1 year and 3 months". | |
+ * <p> | |
+ * This instance is immutable and unaffected by this method call. | |
+ * | |
+ * @return a {@code Period} based on this period with the amounts of each | |
+ * unit normalized, not null | |
+ * @throws ArithmeticException if numeric overflow occurs | |
+ */ | |
+ ChronoPeriod normalized(); | |
+ | |
+ //------------------------------------------------------------------------- | |
+ /** | |
+ * Adds this period to the specified temporal object. | |
+ * <p> | |
+ * This returns a temporal object of the same observable type as the input | |
+ * with this period added. | |
+ * <p> | |
+ * In most cases, it is clearer to reverse the calling pattern by using | |
+ * {@link Temporal#plus(TemporalAmount)}. | |
+ * <pre> | |
+ * // these two lines are equivalent, but the second approach is recommended | |
+ * dateTime = thisPeriod.addTo(dateTime); | |
+ * dateTime = dateTime.plus(thisPeriod); | |
+ * </pre> | |
+ * <p> | |
+ * The specified temporal must have the same chronology as this period. | |
+ * This returns a temporal with the non-zero supported units added. | |
+ * <p> | |
+ * This instance is immutable and unaffected by this method call. | |
+ * | |
+ * @param temporal the temporal object to adjust, not null | |
+ * @return an object of the same type with the adjustment made, not null | |
+ * @throws DateTimeException if unable to add | |
+ * @throws ArithmeticException if numeric overflow occurs | |
+ */ | |
+ @Override | |
+ Temporal addTo(Temporal temporal); | |
+ | |
+ /** | |
+ * Subtracts this period from the specified temporal object. | |
+ * <p> | |
+ * This returns a temporal object of the same observable type as the input | |
+ * with this period subtracted. | |
+ * <p> | |
+ * In most cases, it is clearer to reverse the calling pattern by using | |
+ * {@link Temporal#minus(TemporalAmount)}. | |
+ * <pre> | |
+ * // these two lines are equivalent, but the second approach is recommended | |
+ * dateTime = thisPeriod.subtractFrom(dateTime); | |
+ * dateTime = dateTime.minus(thisPeriod); | |
+ * </pre> | |
+ * <p> | |
+ * The specified temporal must have the same chronology as this period. | |
+ * This returns a temporal with the non-zero supported units subtracted. | |
+ * <p> | |
+ * This instance is immutable and unaffected by this method call. | |
+ * | |
+ * @param temporal the temporal object to adjust, not null | |
+ * @return an object of the same type with the adjustment made, not null | |
+ * @throws DateTimeException if unable to subtract | |
+ * @throws ArithmeticException if numeric overflow occurs | |
+ */ | |
+ @Override | |
+ Temporal subtractFrom(Temporal temporal); | |
+ | |
+ //----------------------------------------------------------------------- | |
+ /** | |
+ * Checks if this period is equal to another period, including the chronology. | |
+ * <p> | |
+ * Compares this period with another ensuring that the type, each amount and | |
+ * the chronology are the same. | |
+ * Note that this means that a period of "15 Months" is not equal to a period | |
+ * of "1 Year and 3 Months". | |
+ * | |
+ * @param obj the object to check, null returns false | |
+ * @return true if this is equal to the other period | |
+ */ | |
+ @Override | |
+ boolean equals(Object obj); | |
+ | |
+ /** | |
+ * A hash code for this period. | |
+ * | |
+ * @return a suitable hash code | |
+ */ | |
+ @Override | |
+ int hashCode(); | |
+ | |
+ //----------------------------------------------------------------------- | |
+ /** | |
+ * Outputs this period as a {@code String}. | |
+ * <p> | |
+ * The output will include the period amounts and chronology. | |
+ * | |
+ * @return a string representation of this period, not null | |
+ */ | |
+ @Override | |
+ String toString(); | |
+ | |
+} | |
diff --git a/src/share/classes/java/time/chrono/ChronoPeriodImpl.java b/src/share/classes/java/time/chrono/ChronoPeriodImpl.java | |
new file mode 100644 | |
--- /dev/null | |
+++ b/src/share/classes/java/time/chrono/ChronoPeriodImpl.java | |
@@ -0,0 +1,399 @@ | |
+/* | |
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. | |
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
+ * | |
+ * This code is free software; you can redistribute it and/or modify it | |
+ * under the terms of the GNU General Public License version 2 only, as | |
+ * published by the Free Software Foundation. Oracle designates this | |
+ * particular file as subject to the "Classpath" exception as provided | |
+ * by Oracle in the LICENSE file that accompanied this code. | |
+ * | |
+ * This code is distributed in the hope that it will be useful, but WITHOUT | |
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
+ * version 2 for more details (a copy is included in the LICENSE file that | |
+ * accompanied this code). | |
+ * | |
+ * You should have received a copy of the GNU General Public License version | |
+ * 2 along with this work; if not, write to the Free Software Foundation, | |
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
+ * | |
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
+ * or visit www.oracle.com if you need additional information or have any | |
+ * questions. | |
+ */ | |
+ | |
+/* | |
+ * Copyright (c) 2013, Stephen Colebourne & Michael Nascimento Santos | |
+ * | |
+ * All rights reserved. | |
+ * | |
+ * Redistribution and use in source and binary forms, with or without | |
+ * modification, are permitted provided that the following conditions are met: | |
+ * | |
+ * * Redistributions of source code must retain the above copyright notice, | |
+ * this list of conditions and the following disclaimer. | |
+ * | |
+ * * Redistributions in binary form must reproduce the above copyright notice, | |
+ * this list of conditions and the following disclaimer in the documentation | |
+ * and/or other materials provided with the distribution. | |
+ * | |
+ * * Neither the name of JSR-310 nor the names of its contributors | |
+ * may be used to endorse or promote products derived from this software | |
+ * without specific prior written permission. | |
+ * | |
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR | |
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
+ */ | |
+package java.time.chrono; | |
+ | |
+import static java.time.temporal.ChronoField.MONTH_OF_YEAR; | |
+import static java.time.temporal.ChronoUnit.DAYS; | |
+import static java.time.temporal.ChronoUnit.MONTHS; | |
+import static java.time.temporal.ChronoUnit.YEARS; | |
+ | |
+import java.io.DataInput; | |
+import java.io.DataOutput; | |
+import java.io.IOException; | |
+import java.io.InvalidObjectException; | |
+import java.io.ObjectStreamException; | |
+import java.io.Serializable; | |
+import java.time.DateTimeException; | |
+import java.time.temporal.ChronoUnit; | |
+import java.time.temporal.Temporal; | |
+import java.time.temporal.TemporalAccessor; | |
+import java.time.temporal.TemporalAmount; | |
+import java.time.temporal.TemporalQuery; | |
+import java.time.temporal.TemporalUnit; | |
+import java.time.temporal.UnsupportedTemporalTypeException; | |
+import java.time.temporal.ValueRange; | |
+import java.util.Arrays; | |
+import java.util.Collections; | |
+import java.util.List; | |
+import java.util.Objects; | |
+ | |
+/** | |
+ * A period expressed in terms of a standard year-month-day calendar system. | |
+ * <p> | |
+ * This class is used by applications seeking to handle dates in non-ISO calendar systems. | |
+ * For example, the Japanese, Minguo, Thai Buddhist and others. | |
+ * | |
+ * @implSpec | |
+ * This class is immutable nad thread-safe. | |
+ * | |
+ * @since 1.8 | |
+ */ | |
+final class ChronoPeriodImpl | |
+ implements ChronoPeriod, Serializable { | |
+ // this class is only used by JDK chronology implementations and makes assumptions based on that fact | |
+ | |
+ /** | |
+ * Serialization version. | |
+ */ | |
+ private static final long serialVersionUID = 57387258289L; | |
+ | |
+ /** | |
+ * The set of supported units. | |
+ */ | |
+ private final static List<TemporalUnit> SUPPORTED_UNITS = | |
+ Collections.unmodifiableList(Arrays.<TemporalUnit>asList(YEARS, MONTHS, DAYS)); | |
+ | |
+ /** | |
+ * The chronology. | |
+ */ | |
+ private final Chronology chrono; | |
+ /** | |
+ * The number of years. | |
+ */ | |
+ final int years; | |
+ /** | |
+ * The number of months. | |
+ */ | |
+ final int months; | |
+ /** | |
+ * The number of days. | |
+ */ | |
+ final int days; | |
+ | |
+ /** | |
+ * Creates an instance. | |
+ */ | |
+ ChronoPeriodImpl(Chronology chrono, int years, int months, int days) { | |
+ Objects.requireNonNull(chrono, "chrono"); | |
+ this.chrono = chrono; | |
+ this.years = years; | |
+ this.months = months; | |
+ this.days = days; | |
+ } | |
+ | |
+ //----------------------------------------------------------------------- | |
+ @Override | |
+ public long get(TemporalUnit unit) { | |
+ if (unit == ChronoUnit.YEARS) { | |
+ return years; | |
+ } else if (unit == ChronoUnit.MONTHS) { | |
+ return months; | |
+ } else if (unit == ChronoUnit.DAYS) { | |
+ return days; | |
+ } else { | |
+ throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit); | |
+ } | |
+ } | |
+ | |
+ @Override | |
+ public List<TemporalUnit> getUnits() { | |
+ return ChronoPeriodImpl.SUPPORTED_UNITS; | |
+ } | |
+ | |
+ @Override | |
+ public Chronology getChronology() { | |
+ return chrono; | |
+ } | |
+ | |
+ //----------------------------------------------------------------------- | |
+ @Override | |
+ public boolean isZero() { | |
+ return years == 0 && months == 0 && days == 0; | |
+ } | |
+ | |
+ @Override | |
+ public boolean isNegative() { | |
+ return years < 0 || months < 0 || days < 0; | |
+ } | |
+ | |
+ //----------------------------------------------------------------------- | |
+ @Override | |
+ public ChronoPeriod plus(TemporalAmount amountToAdd) { | |
+ ChronoPeriodImpl amount = validateAmount(amountToAdd); | |
+ return new ChronoPeriodImpl( | |
+ chrono, | |
+ Math.addExact(years, amount.years), | |
+ Math.addExact(months, amount.months), | |
+ Math.addExact(days, amount.days)); | |
+ } | |
+ | |
+ @Override | |
+ public ChronoPeriod minus(TemporalAmount amountToSubtract) { | |
+ ChronoPeriodImpl amount = validateAmount(amountToSubtract); | |
+ return new ChronoPeriodImpl( | |
+ chrono, | |
+ Math.subtractExact(years, amount.years), | |
+ Math.subtractExact(months, amount.months), | |
+ Math.subtractExact(days, amount.days)); | |
+ } | |
+ | |
+ /** | |
+ * Obtains an instance of {@code ChronoPeriodImpl} from a temporal amount. | |
+ * | |
+ * @param amount the temporal amount to convert, not null | |
+ * @return the period, not null | |
+ */ | |
+ private ChronoPeriodImpl validateAmount(TemporalAmount amount) { | |
+ Objects.requireNonNull(amount, "amount"); | |
+ if (amount instanceof ChronoPeriodImpl == false) { | |
+ throw new DateTimeException("Unable to obtain ChronoPeriod from TemporalAmount: " + amount.getClass()); | |
+ } | |
+ ChronoPeriodImpl period = (ChronoPeriodImpl) amount; | |
+ if (chrono.equals(period.getChronology()) == false) { | |
+ throw new ClassCastException("Chronology mismatch, expected: " + chrono.getId() + ", actual: " + period.getChronology().getId()); | |
+ } | |
+ return period; | |
+ } | |
+ | |
+ //----------------------------------------------------------------------- | |
+ @Override | |
+ public ChronoPeriod multipliedBy(int scalar) { | |
+ if (this.isZero() || scalar == 1) { | |
+ return this; | |
+ } | |
+ return new ChronoPeriodImpl( | |
+ chrono, | |
+ Math.multiplyExact(years, scalar), | |
+ Math.multiplyExact(months, scalar), | |
+ Math.multiplyExact(days, scalar)); | |
+ } | |
+ | |
+ //----------------------------------------------------------------------- | |
+ @Override | |
+ public ChronoPeriod normalized() { | |
+ long monthRange = monthRange(); | |
+ if (monthRange > 0) { | |
+ long totalMonths = years * monthRange + months; | |
+ long splitYears = totalMonths / monthRange; | |
+ int splitMonths = (int) (totalMonths % monthRange); // no overflow | |
+ if (splitYears == years && splitMonths == months) { | |
+ return this; | |
+ } | |
+ return new ChronoPeriodImpl(chrono, Math.toIntExact(splitYears), splitMonths, days); | |
+ | |
+ } | |
+ return this; | |
+ } | |
+ | |
+ /** | |
+ * Calculates the range of months. | |
+ * | |
+ * @return the month range, -1 if not fixed range | |
+ */ | |
+ private long monthRange() { | |
+ ValueRange startRange = chrono.range(MONTH_OF_YEAR); | |
+ if (startRange.isFixed() && startRange.isIntValue()) { | |
+ return startRange.getMaximum() - startRange.getMinimum() + 1; | |
+ } | |
+ return -1; | |
+ } | |
+ | |
+ //------------------------------------------------------------------------- | |
+ @Override | |
+ public Temporal addTo(Temporal temporal) { | |
+ validateChrono(temporal); | |
+ if (months == 0) { | |
+ if (years != 0) { | |
+ temporal = temporal.plus(years, YEARS); | |
+ } | |
+ } else { | |
+ long monthRange = monthRange(); | |
+ if (monthRange > 0) { | |
+ temporal = temporal.plus(years * monthRange + months, MONTHS); | |
+ } else { | |
+ if (years != 0) { | |
+ temporal = temporal.plus(years, YEARS); | |
+ } | |
+ temporal = temporal.plus(months, MONTHS); | |
+ } | |
+ } | |
+ if (days != 0) { | |
+ temporal = temporal.plus(days, DAYS); | |
+ } | |
+ return temporal; | |
+ } | |
+ | |
+ | |
+ | |
+ @Override | |
+ public Temporal subtractFrom(Temporal temporal) { | |
+ validateChrono(temporal); | |
+ if (months == 0) { | |
+ if (years != 0) { | |
+ temporal = temporal.minus(years, YEARS); | |
+ } | |
+ } else { | |
+ long monthRange = monthRange(); | |
+ if (monthRange > 0) { | |
+ temporal = temporal.minus(years * monthRange + months, MONTHS); | |
+ } else { | |
+ if (years != 0) { | |
+ temporal = temporal.minus(years, YEARS); | |
+ } | |
+ temporal = temporal.minus(months, MONTHS); | |
+ } | |
+ } | |
+ if (days != 0) { | |
+ temporal = temporal.minus(days, DAYS); | |
+ } | |
+ return temporal; | |
+ } | |
+ | |
+ /** | |
+ * Validates that the temporal has the correct chronology. | |
+ */ | |
+ private void validateChrono(TemporalAccessor temporal) { | |
+ Objects.requireNonNull(temporal, "temporal"); | |
+ Chronology temporalChrono = temporal.query(TemporalQuery.chronology()); | |
+ if (temporalChrono != null && chrono.equals(temporalChrono) == false) { | |
+ throw new DateTimeException("Chronology mismatch, expected: " + chrono.getId() + ", actual: " + temporalChrono.getId()); | |
+ } | |
+ } | |
+ | |
+ //----------------------------------------------------------------------- | |
+ @Override | |
+ public boolean equals(Object obj) { | |
+ if (this == obj) { | |
+ return true; | |
+ } | |
+ if (obj instanceof ChronoPeriodImpl) { | |
+ ChronoPeriodImpl other = (ChronoPeriodImpl) obj; | |
+ return years == other.years && months == other.months && | |
+ days == other.days && chrono.equals(other.chrono); | |
+ } | |
+ return false; | |
+ } | |
+ | |
+ @Override | |
+ public int hashCode() { | |
+ return (years + Integer.rotateLeft(months, 8) + Integer.rotateLeft(days, 16)) ^ chrono.hashCode(); | |
+ } | |
+ | |
+ //----------------------------------------------------------------------- | |
+ @Override | |
+ public String toString() { | |
+ if (isZero()) { | |
+ return getChronology().toString() + " P0D"; | |
+ } else { | |
+ StringBuilder buf = new StringBuilder(); | |
+ buf.append(getChronology().toString()).append(' ').append('P'); | |
+ if (years != 0) { | |
+ buf.append(years).append('Y'); | |
+ } | |
+ if (months != 0) { | |
+ buf.append(months).append('M'); | |
+ } | |
+ if (days != 0) { | |
+ buf.append(days).append('D'); | |
+ } | |
+ return buf.toString(); | |
+ } | |
+ } | |
+ | |
+ //----------------------------------------------------------------------- | |
+ /** | |
+ * Writes the Chronology using a | |
+ * <a href="../../../serialized-form.html#java.time.chrono.Ser">dedicated serialized form</a>. | |
+ * <pre> | |
+ * out.writeByte(12); // identifies this as a ChronoPeriodImpl | |
+ * out.writeUTF(getId()); // the chronology | |
+ * out.writeInt(years); | |
+ * out.writeInt(months); | |
+ * out.writeInt(days); | |
+ * </pre> | |
+ * | |
+ * @return the instance of {@code Ser}, not null | |
+ */ | |
+ protected Object writeReplace() { | |
+ return new Ser(Ser.CHRONO_PERIOD_TYPE, this); | |
+ } | |
+ | |
+ /** | |
+ * Defend against malicious streams. | |
+ * @return never | |
+ * @throws InvalidObjectException always | |
+ */ | |
+ private Object readResolve() throws ObjectStreamException { | |
+ throw new InvalidObjectException("Deserialization via serialization delegate"); | |
+ } | |
+ | |
+ void writeExternal(DataOutput out) throws IOException { | |
+ out.writeUTF(chrono.getId()); | |
+ out.writeInt(years); | |
+ out.writeInt(months); | |
+ out.writeInt(days); | |
+ } | |
+ | |
+ static ChronoPeriodImpl readExternal(DataInput in) throws IOException { | |
+ Chronology chrono = Chronology.of(in.readUTF()); | |
+ int years = in.readInt(); | |
+ int months = in.readInt(); | |
+ int days = in.readInt(); | |
+ return new ChronoPeriodImpl(chrono, years, months, days); | |
+ } | |
+ | |
+} | |
diff --git a/src/share/classes/java/time/chrono/Chronology.java b/src/share/classes/java/time/chrono/Chronology.java | |
--- a/src/share/classes/java/time/chrono/Chronology.java | |
+++ b/src/share/classes/java/time/chrono/Chronology.java | |
@@ -91,14 +91,11 @@ | |
import java.time.Instant; | |
import java.time.LocalDate; | |
import java.time.LocalTime; | |
-import java.time.Month; | |
-import java.time.Year; | |
import java.time.ZoneId; | |
import java.time.format.DateTimeFormatterBuilder; | |
import java.time.format.ResolverStyle; | |
import java.time.format.TextStyle; | |
import java.time.temporal.ChronoField; | |
-import java.time.temporal.Temporal; | |
import java.time.temporal.TemporalAccessor; | |
import java.time.temporal.TemporalAdjuster; | |
import java.time.temporal.TemporalField; | |
@@ -1192,6 +1189,38 @@ | |
//----------------------------------------------------------------------- | |
/** | |
+ * Obtains a period for this chronology based on years, months and days. | |
+ * <p> | |
+ * This returns a period tied to this chronology using the specified | |
+ * years, months and days. All supplied chronologies use periods | |
+ * based on years, months and days, however the {@ChronoPeriod} API | |
+ * allows the period to be represented using other units. | |
+ * | |
+ * @impSpec | |
+ * The default implementation returns an implementation class suitable | |
+ * for most calendar systems. It is based solely on the three units. | |
+ * Normalization, addition and subtraction derive the number of months | |
+ * in a year from the {@link #range(ChronoField)}. If the number of | |
+ * months within a year is fixed, then the calculation approach for | |
+ * addition, subtraction and normalization is slightly different. | |
+ * <p> | |
+ * If implementing an unusual calendar system that is not based on | |
+ * years, months and days, or where you want direct control, then | |
+ * the {@code ChronoPeriod} interface must be directly implemented. | |
+ * <p> | |
+ * The returned period is immutable and thread-safe. | |
+ * | |
+ * @param years the number of years, may be negative | |
+ * @param months the number of years, may be negative | |
+ * @param days the number of years, may be negative | |
+ * @return the period in terms of this chronology, not null | |
+ */ | |
+ public ChronoPeriod period(int years, int months, int days) { | |
+ return new ChronoPeriodImpl(this, years, months, days); | |
+ } | |
+ | |
+ //----------------------------------------------------------------------- | |
+ /** | |
* Compares this chronology to another chronology. | |
* <p> | |
* The comparison order first by the chronology ID string, then by any | |
diff --git a/src/share/classes/java/time/chrono/HijrahDate.java b/src/share/classes/java/time/chrono/HijrahDate.java | |
--- a/src/share/classes/java/time/chrono/HijrahDate.java | |
+++ b/src/share/classes/java/time/chrono/HijrahDate.java | |
@@ -581,7 +581,7 @@ | |
} | |
@Override | |
- public Period until(ChronoLocalDate endDate) { | |
+ public ChronoPeriod until(ChronoLocalDate endDate) { | |
// TODO: untested | |
HijrahDate end = getChronology().date(endDate); | |
long totalMonths = (end.prolepticYear - this.prolepticYear) * 12 + (end.monthOfYear - this.monthOfYear); // safe | |
@@ -596,7 +596,7 @@ | |
} | |
long years = totalMonths / 12; // safe | |
int months = (int) (totalMonths % 12); // safe | |
- return Period.of(Math.toIntExact(years), months, days); | |
+ return getChronology().period(Math.toIntExact(years), months, days); | |
} | |
//----------------------------------------------------------------------- | |
diff --git a/src/share/classes/java/time/chrono/IsoChronology.java b/src/share/classes/java/time/chrono/IsoChronology.java | |
--- a/src/share/classes/java/time/chrono/IsoChronology.java | |
+++ b/src/share/classes/java/time/chrono/IsoChronology.java | |
@@ -75,6 +75,7 @@ | |
import java.time.LocalDate; | |
import java.time.LocalDateTime; | |
import java.time.Month; | |
+import java.time.Period; | |
import java.time.Year; | |
import java.time.ZoneId; | |
import java.time.ZonedDateTime; | |
@@ -563,4 +564,22 @@ | |
return field.range(); | |
} | |
+ //----------------------------------------------------------------------- | |
+ /** | |
+ * Obtains a period for this chronology based on years, months and days. | |
+ * <p> | |
+ * This returns a period tied to the ISO chronology using the specified | |
+ * years, months and days. See {@link Period} for further details. | |
+ * | |
+ * @param years the number of years, may be negative | |
+ * @param months the number of years, may be negative | |
+ * @param days the number of years, may be negative | |
+ * @return the period in terms of this chronology, not null | |
+ * @return the ISO period, not null | |
+ */ | |
+ @Override // override with covariant return type | |
+ public Period period(int years, int months, int days) { | |
+ return Period.of(years, months, days); | |
+ } | |
+ | |
} | |
diff --git a/src/share/classes/java/time/chrono/JapaneseDate.java b/src/share/classes/java/time/chrono/JapaneseDate.java | |
--- a/src/share/classes/java/time/chrono/JapaneseDate.java | |
+++ b/src/share/classes/java/time/chrono/JapaneseDate.java | |
@@ -658,8 +658,9 @@ | |
} | |
@Override | |
- public Period until(ChronoLocalDate endDate) { | |
- return isoDate.until(endDate); | |
+ public ChronoPeriod until(ChronoLocalDate endDate) { | |
+ Period period = isoDate.until(endDate); | |
+ return getChronology().period(period.getYears(), period.getMonths(), period.getDays()); | |
} | |
@Override // override for performance | |
diff --git a/src/share/classes/java/time/chrono/MinguoDate.java b/src/share/classes/java/time/chrono/MinguoDate.java | |
--- a/src/share/classes/java/time/chrono/MinguoDate.java | |
+++ b/src/share/classes/java/time/chrono/MinguoDate.java | |
@@ -420,8 +420,9 @@ | |
} | |
@Override | |
- public Period until(ChronoLocalDate endDate) { | |
- return isoDate.until(endDate); | |
+ public ChronoPeriod until(ChronoLocalDate endDate) { | |
+ Period period = isoDate.until(endDate); | |
+ return getChronology().period(period.getYears(), period.getMonths(), period.getDays()); | |
} | |
@Override // override for performance | |
diff --git a/src/share/classes/java/time/chrono/Ser.java b/src/share/classes/java/time/chrono/Ser.java | |
--- a/src/share/classes/java/time/chrono/Ser.java | |
+++ b/src/share/classes/java/time/chrono/Ser.java | |
@@ -107,6 +107,7 @@ | |
static final byte MINGUO_ERA_TYPE = 9; | |
static final byte THAIBUDDHIST_DATE_TYPE = 10; | |
static final byte THAIBUDDHIST_ERA_TYPE = 11; | |
+ static final byte CHRONO_PERIOD_TYPE = 12; | |
/** The type being serialized. */ | |
private byte type; | |
@@ -177,6 +178,9 @@ | |
case THAIBUDDHIST_ERA_TYPE: | |
((ThaiBuddhistEra) object).writeExternal(out); | |
break; | |
+ case CHRONO_PERIOD_TYPE: | |
+ ((ChronoPeriodImpl) object).writeExternal(out); | |
+ break; | |
default: | |
throw new InvalidClassException("Unknown serialized type"); | |
} | |
@@ -212,6 +216,7 @@ | |
case MINGUO_ERA_TYPE: return MinguoEra.readExternal(in); | |
case THAIBUDDHIST_DATE_TYPE: return ThaiBuddhistDate.readExternal(in); | |
case THAIBUDDHIST_ERA_TYPE: return ThaiBuddhistEra.readExternal(in); | |
+ case CHRONO_PERIOD_TYPE: return ChronoPeriodImpl.readExternal(in); | |
default: throw new StreamCorruptedException("Unknown serialized type"); | |
} | |
} | |
diff --git a/src/share/classes/java/time/chrono/ThaiBuddhistDate.java b/src/share/classes/java/time/chrono/ThaiBuddhistDate.java | |
--- a/src/share/classes/java/time/chrono/ThaiBuddhistDate.java | |
+++ b/src/share/classes/java/time/chrono/ThaiBuddhistDate.java | |
@@ -420,8 +420,9 @@ | |
} | |
@Override | |
- public Period until(ChronoLocalDate endDate) { | |
- return isoDate.until(endDate); | |
+ public ChronoPeriod until(ChronoLocalDate endDate) { | |
+ Period period = isoDate.until(endDate); | |
+ return getChronology().period(period.getYears(), period.getMonths(), period.getDays()); | |
} | |
@Override // override for performance | |
diff --git a/src/share/classes/java/time/temporal/Temporal.java b/src/share/classes/java/time/temporal/Temporal.java | |
--- a/src/share/classes/java/time/temporal/Temporal.java | |
+++ b/src/share/classes/java/time/temporal/Temporal.java | |
@@ -170,7 +170,8 @@ | |
* </pre> | |
* | |
* @implSpec | |
- * Implementations must not alter either this object. | |
+ * <p> | |
+ * Implementations must not alter either this object or the specified temporal object. | |
* Instead, an adjusted copy of the original must be returned. | |
* This provides equivalent, safe behavior for immutable and mutable implementations. | |
* <p> | |
@@ -209,7 +210,7 @@ | |
* is obtained by invoking {@code TemporalField.adjustInto(Temporal, long)} | |
* passing {@code this} as the first argument. | |
* <p> | |
- * Implementations must not alter either this object or the specified temporal object. | |
+ * Implementations must not alter this object. | |
* Instead, an adjusted copy of the original must be returned. | |
* This provides equivalent, safe behavior for immutable and mutable implementations. | |
* | |
@@ -232,16 +233,17 @@ | |
* <p> | |
* Some example code indicating how and why this method is used: | |
* <pre> | |
- * date = date.plus(period); // add a Period instance | |
- * date = date.plus(duration); // add a Duration instance | |
- * date = date.plus(workingDays(6)); // example user-written workingDays method | |
+ * date = date.plus(period); // add a Period instance | |
+ * date = date.plus(duration); // add a Duration instance | |
+ * date = date.plus(workingDays(6)); // example user-written workingDays method | |
* </pre> | |
* <p> | |
* Note that calling {@code plus} followed by {@code minus} is not guaranteed to | |
* return the same date-time. | |
* | |
* @implSpec | |
- * Implementations must not alter either this object. | |
+ * <p> | |
+ * Implementations must not alter either this object or the specified temporal object. | |
* Instead, an adjusted copy of the original must be returned. | |
* This provides equivalent, safe behavior for immutable and mutable implementations. | |
* <p> | |
@@ -280,7 +282,7 @@ | |
* is obtained by invoking {@code TemporalUnit.addTo(Temporal, long)} | |
* passing {@code this} as the first argument. | |
* <p> | |
- * Implementations must not alter either this object or the specified temporal object. | |
+ * Implementations must not alter this object. | |
* Instead, an adjusted copy of the original must be returned. | |
* This provides equivalent, safe behavior for immutable and mutable implementations. | |
* | |
@@ -303,16 +305,17 @@ | |
* <p> | |
* Some example code indicating how and why this method is used: | |
* <pre> | |
- * date = date.minus(period); // subtract a Period instance | |
- * date = date.minus(duration); // subtract a Duration instance | |
- * date = date.minus(workingDays(6)); // example user-written workingDays method | |
+ * date = date.minus(period); // subtract a Period instance | |
+ * date = date.minus(duration); // subtract a Duration instance | |
+ * date = date.minus(workingDays(6)); // example user-written workingDays method | |
* </pre> | |
* <p> | |
* Note that calling {@code plus} followed by {@code minus} is not guaranteed to | |
* return the same date-time. | |
* | |
* @implSpec | |
- * Implementations must not alter either this object. | |
+ * <p> | |
+ * Implementations must not alter either this object or the specified temporal object. | |
* Instead, an adjusted copy of the original must be returned. | |
* This provides equivalent, safe behavior for immutable and mutable implementations. | |
* <p> | |
@@ -345,7 +348,7 @@ | |
* @implSpec | |
* Implementations must behave in a manor equivalent to the default method behavior. | |
* <p> | |
- * Implementations must not alter either this object or the specified temporal object. | |
+ * Implementations must not alter this object. | |
* Instead, an adjusted copy of the original must be returned. | |
* This provides equivalent, safe behavior for immutable and mutable implementations. | |
* <p> | |
diff --git a/test/java/time/tck/java/time/TCKPeriod.java b/test/java/time/tck/java/time/TCKPeriod.java | |
--- a/test/java/time/tck/java/time/TCKPeriod.java | |
+++ b/test/java/time/tck/java/time/TCKPeriod.java | |
@@ -60,6 +60,7 @@ | |
package tck.java.time; | |
import static java.time.temporal.ChronoUnit.DAYS; | |
+import static java.time.temporal.ChronoUnit.HOURS; | |
import static java.time.temporal.ChronoUnit.YEARS; | |
import static org.testng.Assert.assertEquals; | |
@@ -67,6 +68,7 @@ | |
import java.time.Duration; | |
import java.time.LocalDate; | |
import java.time.Period; | |
+import java.time.chrono.ThaiBuddhistChronology; | |
import java.time.format.DateTimeParseException; | |
import java.time.temporal.ChronoUnit; | |
import java.time.temporal.Temporal; | |
@@ -221,6 +223,41 @@ | |
} | |
@Test(expectedExceptions = DateTimeException.class) | |
+ public void factory_from_TemporalAmount_DaysHours() { | |
+ TemporalAmount amount = new TemporalAmount() { | |
+ @Override | |
+ public long get(TemporalUnit unit) { | |
+ if (unit == DAYS) { | |
+ return 1; | |
+ } else { | |
+ return 2; | |
+ } | |
+ } | |
+ @Override | |
+ public List<TemporalUnit> getUnits() { | |
+ List<TemporalUnit> list = new ArrayList<>(); | |
+ list.add(DAYS); | |
+ list.add(HOURS); | |
+ return list; | |
+ } | |
+ @Override | |
+ public Temporal addTo(Temporal temporal) { | |
+ throw new UnsupportedOperationException(); | |
+ } | |
+ @Override | |
+ public Temporal subtractFrom(Temporal temporal) { | |
+ throw new UnsupportedOperationException(); | |
+ } | |
+ }; | |
+ Period.from(amount); | |
+ } | |
+ | |
+ @Test(expectedExceptions = DateTimeException.class) | |
+ public void factory_from_TemporalAmount_NonISO() { | |
+ Period.from(ThaiBuddhistChronology.INSTANCE.period(1, 1, 1)); | |
+ } | |
+ | |
+ @Test(expectedExceptions = DateTimeException.class) | |
public void factory_from_TemporalAmount_Duration() { | |
Period.from(Duration.ZERO); | |
} | |
@@ -602,10 +639,45 @@ | |
} | |
@Test(dataProvider="plus") | |
- public void test_plus(Period base, Period add, Period expected) { | |
+ public void test_plus_TemporalAmount(Period base, Period add, Period expected) { | |
assertEquals(base.plus(add), expected); | |
} | |
+ @Test(expectedExceptions = DateTimeException.class) | |
+ public void test_plus_TemporalAmount_nonISO() { | |
+ pymd(4, 5, 6).plus(ThaiBuddhistChronology.INSTANCE.period(1, 0, 0)); | |
+ } | |
+ | |
+ @Test(expectedExceptions = DateTimeException.class) | |
+ public void test_plus_TemporalAmount_DaysHours() { | |
+ TemporalAmount amount = new TemporalAmount() { | |
+ @Override | |
+ public long get(TemporalUnit unit) { | |
+ if (unit == DAYS) { | |
+ return 1; | |
+ } else { | |
+ return 2; | |
+ } | |
+ } | |
+ @Override | |
+ public List<TemporalUnit> getUnits() { | |
+ List<TemporalUnit> list = new ArrayList<>(); | |
+ list.add(DAYS); | |
+ list.add(HOURS); | |
+ return list; | |
+ } | |
+ @Override | |
+ public Temporal addTo(Temporal temporal) { | |
+ throw new UnsupportedOperationException(); | |
+ } | |
+ @Override | |
+ public Temporal subtractFrom(Temporal temporal) { | |
+ throw new UnsupportedOperationException(); | |
+ } | |
+ }; | |
+ pymd(4, 5, 6).plus(amount); | |
+ } | |
+ | |
//----------------------------------------------------------------------- | |
// plusYears() | |
//----------------------------------------------------------------------- | |
@@ -712,10 +784,45 @@ | |
} | |
@Test(dataProvider="minus") | |
- public void test_minus(Period base, Period subtract, Period expected) { | |
+ public void test_minus_TemporalAmount(Period base, Period subtract, Period expected) { | |
assertEquals(base.minus(subtract), expected); | |
} | |
+ @Test(expectedExceptions = DateTimeException.class) | |
+ public void test_minus_TemporalAmount_nonISO() { | |
+ pymd(4, 5, 6).minus(ThaiBuddhistChronology.INSTANCE.period(1, 0, 0)); | |
+ } | |
+ | |
+ @Test(expectedExceptions = DateTimeException.class) | |
+ public void test_minus_TemporalAmount_DaysHours() { | |
+ TemporalAmount amount = new TemporalAmount() { | |
+ @Override | |
+ public long get(TemporalUnit unit) { | |
+ if (unit == DAYS) { | |
+ return 1; | |
+ } else { | |
+ return 2; | |
+ } | |
+ } | |
+ @Override | |
+ public List<TemporalUnit> getUnits() { | |
+ List<TemporalUnit> list = new ArrayList<>(); | |
+ list.add(DAYS); | |
+ list.add(HOURS); | |
+ return list; | |
+ } | |
+ @Override | |
+ public Temporal addTo(Temporal temporal) { | |
+ throw new UnsupportedOperationException(); | |
+ } | |
+ @Override | |
+ public Temporal subtractFrom(Temporal temporal) { | |
+ throw new UnsupportedOperationException(); | |
+ } | |
+ }; | |
+ pymd(4, 5, 6).minus(amount); | |
+ } | |
+ | |
//----------------------------------------------------------------------- | |
// minusYears() | |
//----------------------------------------------------------------------- | |
diff --git a/test/java/time/tck/java/time/chrono/TCKChronoPeriod.java b/test/java/time/tck/java/time/chrono/TCKChronoPeriod.java | |
new file mode 100644 | |
--- /dev/null | |
+++ b/test/java/time/tck/java/time/chrono/TCKChronoPeriod.java | |
@@ -0,0 +1,279 @@ | |
+/* | |
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. | |
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
+ * | |
+ * This code is free software; you can redistribute it and/or modify it | |
+ * under the terms of the GNU General Public License version 2 only, as | |
+ * published by the Free Software Foundation. Oracle designates this | |
+ * particular file as subject to the "Classpath" exception as provided | |
+ * by Oracle in the LICENSE file that accompanied this code. | |
+ * | |
+ * This code is distributed in the hope that it will be useful, but WITHOUT | |
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
+ * version 2 for more details (a copy is included in the LICENSE file that | |
+ * accompanied this code). | |
+ * | |
+ * You should have received a copy of the GNU General Public License version | |
+ * 2 along with this work; if not, write to the Free Software Foundation, | |
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
+ * | |
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
+ * or visit www.oracle.com if you need additional information or have any | |
+ * questions. | |
+ */ | |
+ | |
+/* | |
+ * Copyright (c) 2013, Stephen Colebourne & Michael Nascimento Santos | |
+ * | |
+ * All rights reserved. | |
+ * | |
+ * Redistribution and use in source and binary forms, with or without | |
+ * modification, are permitted provided that the following conditions are met: | |
+ * | |
+ * * Redistributions of source code must retain the above copyright notice, | |
+ * this list of conditions and the following disclaimer. | |
+ * | |
+ * * Redistributions in binary form must reproduce the above copyright notice, | |
+ * this list of conditions and the following disclaimer in the documentation | |
+ * and/or other materials provided with the distribution. | |
+ * | |
+ * * Neither the name of JSR-310 nor the names of its contributors | |
+ * may be used to endorse or promote products derived from this software | |
+ * without specific prior written permission. | |
+ * | |
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR | |
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
+ */ | |
+package tck.java.time.chrono; | |
+ | |
+import static java.time.temporal.ChronoUnit.DAYS; | |
+import static java.time.temporal.ChronoUnit.HOURS; | |
+import static java.time.temporal.ChronoUnit.MONTHS; | |
+import static java.time.temporal.ChronoUnit.YEARS; | |
+import static org.testng.Assert.assertEquals; | |
+ | |
+import java.io.ByteArrayInputStream; | |
+import java.io.ByteArrayOutputStream; | |
+import java.io.ObjectInputStream; | |
+import java.io.ObjectOutputStream; | |
+import java.time.DateTimeException; | |
+import java.time.LocalDate; | |
+import java.time.Period; | |
+import java.time.chrono.ChronoLocalDate; | |
+import java.time.chrono.ChronoPeriod; | |
+import java.time.chrono.Chronology; | |
+import java.time.chrono.HijrahChronology; | |
+import java.time.chrono.IsoChronology; | |
+import java.time.chrono.JapaneseChronology; | |
+import java.time.chrono.MinguoChronology; | |
+import java.time.chrono.ThaiBuddhistChronology; | |
+import java.time.temporal.Temporal; | |
+import java.time.temporal.UnsupportedTemporalTypeException; | |
+ | |
+import org.testng.annotations.DataProvider; | |
+import org.testng.annotations.Test; | |
+ | |
+public class TCKChronoPeriod { | |
+ | |
+ //----------------------------------------------------------------------- | |
+ // regular data factory for names and descriptions of available calendars | |
+ //----------------------------------------------------------------------- | |
+ @DataProvider(name = "calendars") | |
+ Chronology[][] data_of_calendars() { | |
+ return new Chronology[][]{ | |
+ {HijrahChronology.INSTANCE}, | |
+ {IsoChronology.INSTANCE}, | |
+ {JapaneseChronology.INSTANCE}, | |
+ {MinguoChronology.INSTANCE}, | |
+ {ThaiBuddhistChronology.INSTANCE}}; | |
+ } | |
+ | |
+ //----------------------------------------------------------------------- | |
+ // Test Serialization of Calendars | |
+ //----------------------------------------------------------------------- | |
+ @Test(dataProvider="calendars") | |
+ public void test_serialization(Chronology chrono) throws Exception { | |
+ ChronoPeriod period = chrono.period(1, 2, 3); | |
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |
+ ObjectOutputStream out = new ObjectOutputStream(baos); | |
+ out.writeObject(period); | |
+ out.close(); | |
+ ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); | |
+ | |
+ ObjectInputStream in = new ObjectInputStream(bais); | |
+ ChronoPeriod ser = (ChronoPeriod) in.readObject(); | |
+ assertEquals(ser, period, "deserialized ChronoPeriod is wrong"); | |
+ } | |
+ | |
+ @Test(dataProvider="calendars") | |
+ public void test_get(Chronology chrono) { | |
+ ChronoPeriod period = chrono.period(1, 2, 3); | |
+ assertEquals(period.get(YEARS), 1); | |
+ assertEquals(period.get(MONTHS), 2); | |
+ assertEquals(period.get(DAYS), 3); | |
+ } | |
+ | |
+ @Test(dataProvider="calendars", expectedExceptions=UnsupportedTemporalTypeException.class) | |
+ public void test_get_unsupported(Chronology chrono) { | |
+ ChronoPeriod period = chrono.period(1, 2, 3); | |
+ period.get(HOURS); | |
+ } | |
+ | |
+ @Test(dataProvider="calendars") | |
+ public void test_getUnits(Chronology chrono) { | |
+ ChronoPeriod period = chrono.period(1, 2, 3); | |
+ assertEquals(period.getUnits().size(), 3); | |
+ assertEquals(period.getUnits().get(0), YEARS); | |
+ assertEquals(period.getUnits().get(1), MONTHS); | |
+ assertEquals(period.getUnits().get(2), DAYS); | |
+ } | |
+ | |
+ @Test(dataProvider="calendars") | |
+ public void test_getChronology(Chronology chrono) { | |
+ ChronoPeriod period = chrono.period(1, 2, 3); | |
+ assertEquals(period.getChronology(), chrono); | |
+ } | |
+ | |
+ @Test(dataProvider="calendars") | |
+ public void test_isZero_isNegative(Chronology chrono) { | |
+ ChronoPeriod periodPositive = chrono.period(1, 2, 3); | |
+ assertEquals(periodPositive.isZero(), false); | |
+ assertEquals(periodPositive.isNegative(), false); | |
+ | |
+ ChronoPeriod periodZero = chrono.period(0, 0, 0); | |
+ assertEquals(periodZero.isZero(), true); | |
+ assertEquals(periodZero.isNegative(), false); | |
+ | |
+ ChronoPeriod periodNegative = chrono.period(-1, 0, 0); | |
+ assertEquals(periodNegative.isZero(), false); | |
+ assertEquals(periodNegative.isNegative(), true); | |
+ } | |
+ | |
+ //----------------------------------------------------------------------- | |
+ @Test(dataProvider="calendars") | |
+ public void test_plus(Chronology chrono) { | |
+ ChronoPeriod period = chrono.period(1, 2, 3); | |
+ ChronoPeriod period2 = chrono.period(2, 3, 4); | |
+ ChronoPeriod result = period.plus(period2); | |
+ assertEquals(result, chrono.period(3, 5, 7)); | |
+ } | |
+ | |
+ @Test(dataProvider="calendars", expectedExceptions=DateTimeException.class) | |
+ public void test_plus_wrongChrono(Chronology chrono) { | |
+ ChronoPeriod period = chrono.period(1, 2, 3); | |
+ ChronoPeriod isoPeriod = Period.of(2, 3, 4); | |
+ ChronoPeriod thaiPeriod = ThaiBuddhistChronology.INSTANCE.period(2, 3, 4); | |
+ // one of these two will fail | |
+ period.plus(isoPeriod); | |
+ period.plus(thaiPeriod); | |
+ } | |
+ | |
+ @Test(dataProvider="calendars") | |
+ public void test_minus(Chronology chrono) { | |
+ ChronoPeriod period = chrono.period(1, 2, 3); | |
+ ChronoPeriod period2 = chrono.period(2, 3, 4); | |
+ ChronoPeriod result = period.minus(period2); | |
+ assertEquals(result, chrono.period(-1, -1, -1)); | |
+ } | |
+ | |
+ @Test(dataProvider="calendars", expectedExceptions=DateTimeException.class) | |
+ public void test_minus_wrongChrono(Chronology chrono) { | |
+ ChronoPeriod period = chrono.period(1, 2, 3); | |
+ ChronoPeriod isoPeriod = Period.of(2, 3, 4); | |
+ ChronoPeriod thaiPeriod = ThaiBuddhistChronology.INSTANCE.period(2, 3, 4); | |
+ // one of these two will fail | |
+ period.minus(isoPeriod); | |
+ period.minus(thaiPeriod); | |
+ } | |
+ | |
+ //----------------------------------------------------------------------- | |
+ @Test(dataProvider="calendars") | |
+ public void test_addTo(Chronology chrono) { | |
+ ChronoPeriod period = chrono.period(1, 2, 3); | |
+ ChronoLocalDate date = chrono.dateNow(); | |
+ Temporal result = period.addTo(date); | |
+ assertEquals(result, date.plus(14, MONTHS).plus(3, DAYS)); | |
+ } | |
+ | |
+ @Test(dataProvider="calendars", expectedExceptions=DateTimeException.class) | |
+ public void test_addTo_wrongChrono(Chronology chrono) { | |
+ ChronoPeriod period = chrono.period(1, 2, 3); | |
+ ChronoLocalDate isoDate = LocalDate.of(2000, 1, 1); | |
+ ChronoLocalDate thaiDate = ThaiBuddhistChronology.INSTANCE.date(2000, 1, 1); | |
+ // one of these two will fail | |
+ period.addTo(isoDate); | |
+ period.addTo(thaiDate); | |
+ } | |
+ | |
+ @Test(dataProvider="calendars") | |
+ public void test_subtractFrom(Chronology chrono) { | |
+ ChronoPeriod period = chrono.period(1, 2, 3); | |
+ ChronoLocalDate date = chrono.dateNow(); | |
+ Temporal result = period.subtractFrom(date); | |
+ assertEquals(result, date.minus(14, MONTHS).minus(3, DAYS)); | |
+ } | |
+ | |
+ @Test(dataProvider="calendars", expectedExceptions=DateTimeException.class) | |
+ public void test_subtractFrom_wrongChrono(Chronology chrono) { | |
+ ChronoPeriod period = chrono.period(1, 2, 3); | |
+ ChronoLocalDate isoDate = LocalDate.of(2000, 1, 1); | |
+ ChronoLocalDate thaiDate = ThaiBuddhistChronology.INSTANCE.date(2000, 1, 1); | |
+ // one of these two will fail | |
+ period.subtractFrom(isoDate); | |
+ period.subtractFrom(thaiDate); | |
+ } | |
+ | |
+ //----------------------------------------------------------------------- | |
+ @Test(dataProvider="calendars") | |
+ public void test_negated(Chronology chrono) { | |
+ ChronoPeriod period = chrono.period(1, 2, 3); | |
+ assertEquals(period.negated(), chrono.period(-1, -2, -3)); | |
+ } | |
+ | |
+ @Test(dataProvider="calendars") | |
+ public void test_multipliedBy(Chronology chrono) { | |
+ ChronoPeriod period = chrono.period(1, 2, 3); | |
+ assertEquals(period.multipliedBy(3), chrono.period(3, 6, 9)); | |
+ } | |
+ | |
+ //----------------------------------------------------------------------- | |
+ @Test(dataProvider="calendars") | |
+ public void test_equals_equal(Chronology chrono) { | |
+ ChronoPeriod a1 = chrono.period(1, 2, 3); | |
+ ChronoPeriod a2 = chrono.period(1, 2, 3); | |
+ assertEquals(a1, a1); | |
+ assertEquals(a1, a2); | |
+ assertEquals(a2, a1); | |
+ assertEquals(a2, a2); | |
+ assertEquals(a1.hashCode(), a2.hashCode()); | |
+ } | |
+ | |
+ @Test(dataProvider="calendars") | |
+ public void test_equals_notEqual(Chronology chrono) { | |
+ ChronoPeriod a = chrono.period(1, 2, 3); | |
+ ChronoPeriod b = chrono.period(2, 2, 3); | |
+ assertEquals(a.equals(b), false); | |
+ assertEquals(b.equals(a), false); | |
+ assertEquals(a.equals(""), false); | |
+ assertEquals(a.equals(null), false); | |
+ } | |
+ | |
+ @Test(dataProvider="calendars") | |
+ public void test_toString(Chronology chrono) { | |
+ ChronoPeriod period = chrono.period(1, 2, 3); | |
+ if (period instanceof Period == false) { | |
+ assertEquals(period.toString(), chrono.getId() + " P1Y2M3D"); | |
+ } | |
+ } | |
+ | |
+} | |
diff --git a/test/java/time/tck/java/time/chrono/TCKJapaneseChronology.java b/test/java/time/tck/java/time/chrono/TCKJapaneseChronology.java | |
--- a/test/java/time/tck/java/time/chrono/TCKJapaneseChronology.java | |
+++ b/test/java/time/tck/java/time/chrono/TCKJapaneseChronology.java | |
@@ -79,6 +79,7 @@ | |
import java.time.ZoneId; | |
import java.time.ZoneOffset; | |
import java.time.chrono.ChronoLocalDate; | |
+import java.time.chrono.ChronoPeriod; | |
import java.time.chrono.Chronology; | |
import java.time.chrono.Era; | |
import java.time.chrono.IsoChronology; | |
@@ -87,6 +88,7 @@ | |
import java.time.chrono.JapaneseEra; | |
import java.time.chrono.MinguoChronology; | |
import java.time.chrono.MinguoDate; | |
+import java.time.chrono.ThaiBuddhistChronology; | |
import java.time.format.ResolverStyle; | |
import java.time.temporal.ChronoField; | |
import java.time.temporal.ChronoUnit; | |
@@ -615,8 +617,8 @@ | |
public void test_periodUntilDate() { | |
JapaneseDate mdate1 = JapaneseDate.of(1970, 1, 1); | |
JapaneseDate mdate2 = JapaneseDate.of(1971, 2, 2); | |
- Period period = mdate1.until(mdate2); | |
- assertEquals(period, Period.of(1, 1, 1)); | |
+ ChronoPeriod period = mdate1.until(mdate2); | |
+ assertEquals(period, JapaneseChronology.INSTANCE.period(1, 1, 1)); | |
} | |
@Test | |
@@ -632,8 +634,8 @@ | |
JapaneseDate mdate1 = JapaneseDate.of(1970, 1, 1); | |
JapaneseDate mdate2 = JapaneseDate.of(1971, 2, 2); | |
MinguoDate ldate2 = MinguoChronology.INSTANCE.date(mdate2); | |
- Period period = mdate1.until(ldate2); | |
- assertEquals(period, Period.of(1, 1, 1)); | |
+ ChronoPeriod period = mdate1.until(ldate2); | |
+ assertEquals(period, JapaneseChronology.INSTANCE.period(1, 1, 1)); | |
} | |
//----------------------------------------------------------------------- | |
diff --git a/test/java/time/tck/java/time/chrono/TCKMinguoChronology.java b/test/java/time/tck/java/time/chrono/TCKMinguoChronology.java | |
--- a/test/java/time/tck/java/time/chrono/TCKMinguoChronology.java | |
+++ b/test/java/time/tck/java/time/chrono/TCKMinguoChronology.java | |
@@ -69,13 +69,13 @@ | |
import java.time.LocalTime; | |
import java.time.Month; | |
import java.time.OffsetDateTime; | |
-import java.time.Period; | |
import java.time.Year; | |
import java.time.ZoneId; | |
import java.time.ZoneOffset; | |
import java.time.ZonedDateTime; | |
import java.time.chrono.ChronoLocalDate; | |
import java.time.chrono.ChronoLocalDateTime; | |
+import java.time.chrono.ChronoPeriod; | |
import java.time.chrono.ChronoZonedDateTime; | |
import java.time.chrono.Chronology; | |
import java.time.chrono.Era; | |
@@ -84,8 +84,6 @@ | |
import java.time.chrono.MinguoChronology; | |
import java.time.chrono.MinguoDate; | |
import java.time.chrono.MinguoEra; | |
-import java.time.chrono.MinguoChronology; | |
-import java.time.chrono.MinguoDate; | |
import java.time.chrono.ThaiBuddhistChronology; | |
import java.time.chrono.ThaiBuddhistDate; | |
import java.time.format.ResolverStyle; | |
@@ -499,8 +497,8 @@ | |
public void test_periodUntilDate() { | |
MinguoDate mdate1 = MinguoDate.of(1970, 1, 1); | |
MinguoDate mdate2 = MinguoDate.of(1971, 2, 2); | |
- Period period = mdate1.until(mdate2); | |
- assertEquals(period, Period.of(1, 1, 1)); | |
+ ChronoPeriod period = mdate1.until(mdate2); | |
+ assertEquals(period, MinguoChronology.INSTANCE.period(1, 1, 1)); | |
} | |
@Test | |
@@ -516,8 +514,8 @@ | |
MinguoDate mdate1 = MinguoDate.of(1970, 1, 1); | |
MinguoDate mdate2 = MinguoDate.of(1971, 2, 2); | |
ThaiBuddhistDate ldate2 = ThaiBuddhistChronology.INSTANCE.date(mdate2); | |
- Period period = mdate1.until(ldate2); | |
- assertEquals(period, Period.of(1, 1, 1)); | |
+ ChronoPeriod period = mdate1.until(ldate2); | |
+ assertEquals(period, MinguoChronology.INSTANCE.period(1, 1, 1)); | |
} | |
//----------------------------------------------------------------------- | |
diff --git a/test/java/time/tck/java/time/chrono/TCKThaiBuddhistChronology.java b/test/java/time/tck/java/time/chrono/TCKThaiBuddhistChronology.java | |
--- a/test/java/time/tck/java/time/chrono/TCKThaiBuddhistChronology.java | |
+++ b/test/java/time/tck/java/time/chrono/TCKThaiBuddhistChronology.java | |
@@ -72,11 +72,11 @@ | |
import java.time.LocalDate; | |
import java.time.LocalDateTime; | |
import java.time.Month; | |
-import java.time.Period; | |
import java.time.Year; | |
import java.time.ZoneId; | |
import java.time.ZoneOffset; | |
import java.time.chrono.ChronoLocalDate; | |
+import java.time.chrono.ChronoPeriod; | |
import java.time.chrono.Chronology; | |
import java.time.chrono.Era; | |
import java.time.chrono.IsoChronology; | |
@@ -458,8 +458,8 @@ | |
public void test_periodUntilDate() { | |
ThaiBuddhistDate mdate1 = ThaiBuddhistDate.of(1, 1, 1); | |
ThaiBuddhistDate mdate2 = ThaiBuddhistDate.of(2, 2, 2); | |
- Period period = mdate1.until(mdate2); | |
- assertEquals(period, Period.of(1, 1, 1)); | |
+ ChronoPeriod period = mdate1.until(mdate2); | |
+ assertEquals(period, ThaiBuddhistChronology.INSTANCE.period(1, 1, 1)); | |
} | |
@Test | |
@@ -475,8 +475,8 @@ | |
ThaiBuddhistDate mdate1 = ThaiBuddhistDate.of(1, 1, 1); | |
ThaiBuddhistDate mdate2 = ThaiBuddhistDate.of(2, 2, 2); | |
MinguoDate ldate2 = MinguoChronology.INSTANCE.date(mdate2); | |
- Period period = mdate1.until(ldate2); | |
- assertEquals(period, Period.of(1, 1, 1)); | |
+ ChronoPeriod period = mdate1.until(ldate2); | |
+ assertEquals(period, ThaiBuddhistChronology.INSTANCE.period(1, 1, 1)); | |
} | |
//----------------------------------------------------------------------- | |
diff --git a/test/java/time/test/java/time/chrono/TestUmmAlQuraChronology.java b/test/java/time/test/java/time/chrono/TestUmmAlQuraChronology.java | |
--- a/test/java/time/test/java/time/chrono/TestUmmAlQuraChronology.java | |
+++ b/test/java/time/test/java/time/chrono/TestUmmAlQuraChronology.java | |
@@ -26,9 +26,9 @@ | |
package test.java.time.chrono; | |
import static java.time.temporal.ChronoField.DAY_OF_MONTH; | |
+import static java.time.temporal.ChronoField.DAY_OF_YEAR; | |
import static java.time.temporal.ChronoField.MONTH_OF_YEAR; | |
import static java.time.temporal.ChronoField.YEAR; | |
-import static java.time.temporal.ChronoField.DAY_OF_YEAR; | |
import static org.testng.Assert.assertEquals; | |
import static org.testng.Assert.assertTrue; | |
import static org.testng.Assert.fail; | |
@@ -39,12 +39,12 @@ | |
import java.time.LocalDateTime; | |
import java.time.LocalTime; | |
import java.time.OffsetDateTime; | |
-import java.time.Period; | |
import java.time.ZoneId; | |
import java.time.ZoneOffset; | |
import java.time.ZonedDateTime; | |
import java.time.chrono.ChronoLocalDate; | |
import java.time.chrono.ChronoLocalDateTime; | |
+import java.time.chrono.ChronoPeriod; | |
import java.time.chrono.ChronoZonedDateTime; | |
import java.time.chrono.Chronology; | |
import java.time.chrono.HijrahChronology; | |
@@ -330,26 +330,26 @@ | |
@DataProvider(name="datesForPeriod") | |
Object[][] data_Period() { | |
return new Object[][] { | |
- {HijrahDate.of(1350, 5, 15), HijrahDate.of(1434, 7, 20), Period.of(84, 2, 5)}, | |
- {HijrahDate.of(1403, 5, 28), HijrahDate.of(1434, 7, 20), Period.of(31, 1, 22)}, | |
- {HijrahDate.of(1434, 7, 20), HijrahDate.of(1484, 2, 15), Period.of(49, 6, 24)}, | |
- {HijrahDate.of(1500, 6, 12), HijrahDate.of(1450, 4, 21), Period.of(-50, -1, -20)}, | |
- {HijrahDate.of(1549, 3, 11), HijrahDate.of(1550, 3, 10), Period.of(0, 11, 28)}, | |
+ {HijrahDate.of(1350, 5, 15), HijrahDate.of(1434, 7, 20), HijrahChronology.INSTANCE.period(84, 2, 5)}, | |
+ {HijrahDate.of(1403, 5, 28), HijrahDate.of(1434, 7, 20), HijrahChronology.INSTANCE.period(31, 1, 22)}, | |
+ {HijrahDate.of(1434, 7, 20), HijrahDate.of(1484, 2, 15), HijrahChronology.INSTANCE.period(49, 6, 24)}, | |
+ {HijrahDate.of(1500, 6, 12), HijrahDate.of(1450, 4, 21), HijrahChronology.INSTANCE.period(-50, -1, -20)}, | |
+ {HijrahDate.of(1549, 3, 11), HijrahDate.of(1550, 3, 10), HijrahChronology.INSTANCE.period(0, 11, 28)}, | |
}; | |
} | |
// Test to get the Period between two given dates | |
@Test(dataProvider="datesForPeriod") | |
- public void test_until(HijrahDate h1, HijrahDate h2, Period p) { | |
- Period period = h1.until(h2); | |
+ public void test_until(HijrahDate h1, HijrahDate h2, ChronoPeriod p) { | |
+ ChronoPeriod period = h1.until(h2); | |
assertEquals(period, p); | |
} | |
// Test to get the Period between dates in different chronologies | |
@Test(dataProvider="datesForPeriod") | |
- public void test_periodUntilDiffChrono(HijrahDate h1, HijrahDate h2, Period p) { | |
+ public void test_periodUntilDiffChrono(HijrahDate h1, HijrahDate h2, ChronoPeriod p) { | |
MinguoDate m = MinguoChronology.INSTANCE.date(h2); | |
- Period period = h1.until(m); | |
+ ChronoPeriod period = h1.until(m); | |
assertEquals(period, p); | |
} | |
LocalTime
does not have a chronology for example.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
450, 1186: validateChrono allows the Chronology to be null; under what use case would the Chronology of a TemporalAccessor be null?
Line 713: "Period" -> "ChronoPeriod in * @return a {@code ChronoPeriod} "
Looks good, thanks for all the code and tests.