Automatic sources dropoff on 2020-06-10 18:32:38.095721
The change is generated with prebuilt drop tool.
Change-Id: I24cbf6ba6db262a1ae1445db1427a08fee35b3b4
diff --git a/java/time/Clock.java b/java/time/Clock.java
new file mode 100644
index 0000000..b112784
--- /dev/null
+++ b/java/time/Clock.java
@@ -0,0 +1,649 @@
+/*
+ * Copyright (c) 2012, 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) 2007-2012, 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;
+
+import static java.time.LocalTime.NANOS_PER_MINUTE;
+import static java.time.LocalTime.NANOS_PER_SECOND;
+
+import java.io.Serializable;
+import java.util.Objects;
+import java.util.TimeZone;
+
+/**
+ * A clock providing access to the current instant, date and time using a time-zone.
+ * <p>
+ * Instances of this class are used to find the current instant, which can be
+ * interpreted using the stored time-zone to find the current date and time.
+ * As such, a clock can be used instead of {@link System#currentTimeMillis()}
+ * and {@link TimeZone#getDefault()}.
+ * <p>
+ * Use of a {@code Clock} is optional. All key date-time classes also have a
+ * {@code now()} factory method that uses the system clock in the default time zone.
+ * The primary purpose of this abstraction is to allow alternate clocks to be
+ * plugged in as and when required. Applications use an object to obtain the
+ * current time rather than a static method. This can simplify testing.
+ * <p>
+ * Best practice for applications is to pass a {@code Clock} into any method
+ * that requires the current instant. A dependency injection framework is one
+ * way to achieve this:
+ * <pre>
+ * public class MyBean {
+ * private Clock clock; // dependency inject
+ * ...
+ * public void process(LocalDate eventDate) {
+ * if (eventDate.isBefore(LocalDate.now(clock)) {
+ * ...
+ * }
+ * }
+ * }
+ * </pre>
+ * This approach allows an alternate clock, such as {@link #fixed(Instant, ZoneId) fixed}
+ * or {@link #offset(Clock, Duration) offset} to be used during testing.
+ * <p>
+ * The {@code system} factory methods provide clocks based on the best available
+ * system clock This may use {@link System#currentTimeMillis()}, or a higher
+ * resolution clock if one is available.
+ *
+ * @implSpec
+ * This abstract class must be implemented with care to ensure other classes operate correctly.
+ * All implementations that can be instantiated must be final, immutable and thread-safe.
+ * <p>
+ * The principal methods are defined to allow the throwing of an exception.
+ * In normal use, no exceptions will be thrown, however one possible implementation would be to
+ * obtain the time from a central time server across the network. Obviously, in this case the
+ * lookup could fail, and so the method is permitted to throw an exception.
+ * <p>
+ * The returned instants from {@code Clock} work on a time-scale that ignores leap seconds,
+ * as described in {@link Instant}. If the implementation wraps a source that provides leap
+ * second information, then a mechanism should be used to "smooth" the leap second.
+ * The Java Time-Scale mandates the use of UTC-SLS, however clock implementations may choose
+ * how accurate they are with the time-scale so long as they document how they work.
+ * Implementations are therefore not required to actually perform the UTC-SLS slew or to
+ * otherwise be aware of leap seconds.
+ * <p>
+ * Implementations should implement {@code Serializable} wherever possible and must
+ * document whether or not they do support serialization.
+ *
+ * @implNote
+ * The clock implementation provided here is based on {@link System#currentTimeMillis()}.
+ * That method provides little to no guarantee about the accuracy of the clock.
+ * Applications requiring a more accurate clock must implement this abstract class
+ * themselves using a different external clock, such as an NTP server.
+ *
+ * @since 1.8
+ */
+public abstract class Clock {
+
+ /**
+ * Obtains a clock that returns the current instant using the best available
+ * system clock, converting to date and time using the UTC time-zone.
+ * <p>
+ * This clock, rather than {@link #systemDefaultZone()}, should be used when
+ * you need the current instant without the date or time.
+ * <p>
+ * This clock is based on the best available system clock.
+ * This may use {@link System#currentTimeMillis()}, or a higher resolution
+ * clock if one is available.
+ * <p>
+ * Conversion from instant to date or time uses the {@linkplain ZoneOffset#UTC UTC time-zone}.
+ * <p>
+ * The returned implementation is immutable, thread-safe and {@code Serializable}.
+ * It is equivalent to {@code system(ZoneOffset.UTC)}.
+ *
+ * @return a clock that uses the best available system clock in the UTC zone, not null
+ */
+ public static Clock systemUTC() {
+ return new SystemClock(ZoneOffset.UTC);
+ }
+
+ /**
+ * Obtains a clock that returns the current instant using the best available
+ * system clock, converting to date and time using the default time-zone.
+ * <p>
+ * This clock is based on the best available system clock.
+ * This may use {@link System#currentTimeMillis()}, or a higher resolution
+ * clock if one is available.
+ * <p>
+ * Using this method hard codes a dependency to the default time-zone into your application.
+ * It is recommended to avoid this and use a specific time-zone whenever possible.
+ * The {@link #systemUTC() UTC clock} should be used when you need the current instant
+ * without the date or time.
+ * <p>
+ * The returned implementation is immutable, thread-safe and {@code Serializable}.
+ * It is equivalent to {@code system(ZoneId.systemDefault())}.
+ *
+ * @return a clock that uses the best available system clock in the default zone, not null
+ * @see ZoneId#systemDefault()
+ */
+ public static Clock systemDefaultZone() {
+ return new SystemClock(ZoneId.systemDefault());
+ }
+
+ /**
+ * Obtains a clock that returns the current instant using best available
+ * system clock.
+ * <p>
+ * This clock is based on the best available system clock.
+ * This may use {@link System#currentTimeMillis()}, or a higher resolution
+ * clock if one is available.
+ * <p>
+ * Conversion from instant to date or time uses the specified time-zone.
+ * <p>
+ * The returned implementation is immutable, thread-safe and {@code Serializable}.
+ *
+ * @param zone the time-zone to use to convert the instant to date-time, not null
+ * @return a clock that uses the best available system clock in the specified zone, not null
+ */
+ public static Clock system(ZoneId zone) {
+ Objects.requireNonNull(zone, "zone");
+ return new SystemClock(zone);
+ }
+
+ //-------------------------------------------------------------------------
+ /**
+ * Obtains a clock that returns the current instant ticking in whole seconds
+ * using best available system clock.
+ * <p>
+ * This clock will always have the nano-of-second field set to zero.
+ * This ensures that the visible time ticks in whole seconds.
+ * The underlying clock is the best available system clock, equivalent to
+ * using {@link #system(ZoneId)}.
+ * <p>
+ * Implementations may use a caching strategy for performance reasons.
+ * As such, it is possible that the start of the second observed via this
+ * clock will be later than that observed directly via the underlying clock.
+ * <p>
+ * The returned implementation is immutable, thread-safe and {@code Serializable}.
+ * It is equivalent to {@code tick(system(zone), Duration.ofSeconds(1))}.
+ *
+ * @param zone the time-zone to use to convert the instant to date-time, not null
+ * @return a clock that ticks in whole seconds using the specified zone, not null
+ */
+ public static Clock tickSeconds(ZoneId zone) {
+ return new TickClock(system(zone), NANOS_PER_SECOND);
+ }
+
+ /**
+ * Obtains a clock that returns the current instant ticking in whole minutes
+ * using best available system clock.
+ * <p>
+ * This clock will always have the nano-of-second and second-of-minute fields set to zero.
+ * This ensures that the visible time ticks in whole minutes.
+ * The underlying clock is the best available system clock, equivalent to
+ * using {@link #system(ZoneId)}.
+ * <p>
+ * Implementations may use a caching strategy for performance reasons.
+ * As such, it is possible that the start of the minute observed via this
+ * clock will be later than that observed directly via the underlying clock.
+ * <p>
+ * The returned implementation is immutable, thread-safe and {@code Serializable}.
+ * It is equivalent to {@code tick(system(zone), Duration.ofMinutes(1))}.
+ *
+ * @param zone the time-zone to use to convert the instant to date-time, not null
+ * @return a clock that ticks in whole minutes using the specified zone, not null
+ */
+ public static Clock tickMinutes(ZoneId zone) {
+ return new TickClock(system(zone), NANOS_PER_MINUTE);
+ }
+
+ /**
+ * Obtains a clock that returns instants from the specified clock truncated
+ * to the nearest occurrence of the specified duration.
+ * <p>
+ * This clock will only tick as per the specified duration. Thus, if the duration
+ * is half a second, the clock will return instants truncated to the half second.
+ * <p>
+ * The tick duration must be positive. If it has a part smaller than a whole
+ * millisecond, then the whole duration must divide into one second without
+ * leaving a remainder. All normal tick durations will match these criteria,
+ * including any multiple of hours, minutes, seconds and milliseconds, and
+ * sensible nanosecond durations, such as 20ns, 250,000ns and 500,000ns.
+ * <p>
+ * A duration of zero or one nanosecond would have no truncation effect.
+ * Passing one of these will return the underlying clock.
+ * <p>
+ * Implementations may use a caching strategy for performance reasons.
+ * As such, it is possible that the start of the requested duration observed
+ * via this clock will be later than that observed directly via the underlying clock.
+ * <p>
+ * The returned implementation is immutable, thread-safe and {@code Serializable}
+ * providing that the base clock is.
+ *
+ * @param baseClock the base clock to base the ticking clock on, not null
+ * @param tickDuration the duration of each visible tick, not negative, not null
+ * @return a clock that ticks in whole units of the duration, not null
+ * @throws IllegalArgumentException if the duration is negative, or has a
+ * part smaller than a whole millisecond such that the whole duration is not
+ * divisible into one second
+ * @throws ArithmeticException if the duration is too large to be represented as nanos
+ */
+ public static Clock tick(Clock baseClock, Duration tickDuration) {
+ Objects.requireNonNull(baseClock, "baseClock");
+ Objects.requireNonNull(tickDuration, "tickDuration");
+ if (tickDuration.isNegative()) {
+ throw new IllegalArgumentException("Tick duration must not be negative");
+ }
+ long tickNanos = tickDuration.toNanos();
+ if (tickNanos % 1000_000 == 0) {
+ // ok, no fraction of millisecond
+ } else if (1000_000_000 % tickNanos == 0) {
+ // ok, divides into one second without remainder
+ } else {
+ throw new IllegalArgumentException("Invalid tick duration");
+ }
+ if (tickNanos <= 1) {
+ return baseClock;
+ }
+ return new TickClock(baseClock, tickNanos);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains a clock that always returns the same instant.
+ * <p>
+ * This clock simply returns the specified instant.
+ * As such, it is not a clock in the conventional sense.
+ * The main use case for this is in testing, where the fixed clock ensures
+ * tests are not dependent on the current clock.
+ * <p>
+ * The returned implementation is immutable, thread-safe and {@code Serializable}.
+ *
+ * @param fixedInstant the instant to use as the clock, not null
+ * @param zone the time-zone to use to convert the instant to date-time, not null
+ * @return a clock that always returns the same instant, not null
+ */
+ public static Clock fixed(Instant fixedInstant, ZoneId zone) {
+ Objects.requireNonNull(fixedInstant, "fixedInstant");
+ Objects.requireNonNull(zone, "zone");
+ return new FixedClock(fixedInstant, zone);
+ }
+
+ //-------------------------------------------------------------------------
+ /**
+ * Obtains a clock that returns instants from the specified clock with the
+ * specified duration added
+ * <p>
+ * This clock wraps another clock, returning instants that are later by the
+ * specified duration. If the duration is negative, the instants will be
+ * earlier than the current date and time.
+ * The main use case for this is to simulate running in the future or in the past.
+ * <p>
+ * A duration of zero would have no offsetting effect.
+ * Passing zero will return the underlying clock.
+ * <p>
+ * The returned implementation is immutable, thread-safe and {@code Serializable}
+ * providing that the base clock is.
+ *
+ * @param baseClock the base clock to add the duration to, not null
+ * @param offsetDuration the duration to add, not null
+ * @return a clock based on the base clock with the duration added, not null
+ */
+ public static Clock offset(Clock baseClock, Duration offsetDuration) {
+ Objects.requireNonNull(baseClock, "baseClock");
+ Objects.requireNonNull(offsetDuration, "offsetDuration");
+ if (offsetDuration.equals(Duration.ZERO)) {
+ return baseClock;
+ }
+ return new OffsetClock(baseClock, offsetDuration);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Constructor accessible by subclasses.
+ */
+ protected Clock() {
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the time-zone being used to create dates and times.
+ * <p>
+ * A clock will typically obtain the current instant and then convert that
+ * to a date or time using a time-zone. This method returns the time-zone used.
+ *
+ * @return the time-zone being used to interpret instants, not null
+ */
+ public abstract ZoneId getZone();
+
+ /**
+ * Returns a copy of this clock with a different time-zone.
+ * <p>
+ * A clock will typically obtain the current instant and then convert that
+ * to a date or time using a time-zone. This method returns a clock with
+ * similar properties but using a different time-zone.
+ *
+ * @param zone the time-zone to change to, not null
+ * @return a clock based on this clock with the specified time-zone, not null
+ */
+ public abstract Clock withZone(ZoneId zone);
+
+ //-------------------------------------------------------------------------
+ /**
+ * Gets the current millisecond instant of the clock.
+ * <p>
+ * This returns the millisecond-based instant, measured from 1970-01-01T00:00Z (UTC).
+ * This is equivalent to the definition of {@link System#currentTimeMillis()}.
+ * <p>
+ * Most applications should avoid this method and use {@link Instant} to represent
+ * an instant on the time-line rather than a raw millisecond value.
+ * This method is provided to allow the use of the clock in high performance use cases
+ * where the creation of an object would be unacceptable.
+ * <p>
+ * The default implementation currently calls {@link #instant}.
+ *
+ * @return the current millisecond instant from this clock, measured from
+ * the Java epoch of 1970-01-01T00:00Z (UTC), not null
+ * @throws DateTimeException if the instant cannot be obtained, not thrown by most implementations
+ */
+ public long millis() {
+ return instant().toEpochMilli();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the current instant of the clock.
+ * <p>
+ * This returns an instant representing the current instant as defined by the clock.
+ *
+ * @return the current instant from this clock, not null
+ * @throws DateTimeException if the instant cannot be obtained, not thrown by most implementations
+ */
+ public abstract Instant instant();
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if this clock is equal to another clock.
+ * <p>
+ * Clocks should override this method to compare equals based on
+ * their state and to meet the contract of {@link Object#equals}.
+ * If not overridden, the behavior is defined by {@link Object#equals}
+ *
+ * @param obj the object to check, null returns false
+ * @return true if this is equal to the other clock
+ */
+ @Override
+ public boolean equals(Object obj) {
+ return super.equals(obj);
+ }
+
+ /**
+ * A hash code for this clock.
+ * <p>
+ * Clocks should override this method based on
+ * their state and to meet the contract of {@link Object#hashCode}.
+ * If not overridden, the behavior is defined by {@link Object#hashCode}
+ *
+ * @return a suitable hash code
+ */
+ @Override
+ public int hashCode() {
+ return super.hashCode();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Implementation of a clock that always returns the latest time from
+ * {@link System#currentTimeMillis()}.
+ */
+ static final class SystemClock extends Clock implements Serializable {
+ private static final long serialVersionUID = 6740630888130243051L;
+ private final ZoneId zone;
+
+ SystemClock(ZoneId zone) {
+ this.zone = zone;
+ }
+ @Override
+ public ZoneId getZone() {
+ return zone;
+ }
+ @Override
+ public Clock withZone(ZoneId zone) {
+ if (zone.equals(this.zone)) { // intentional NPE
+ return this;
+ }
+ return new SystemClock(zone);
+ }
+ @Override
+ public long millis() {
+ return System.currentTimeMillis();
+ }
+ @Override
+ public Instant instant() {
+ return Instant.ofEpochMilli(millis());
+ }
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof SystemClock) {
+ return zone.equals(((SystemClock) obj).zone);
+ }
+ return false;
+ }
+ @Override
+ public int hashCode() {
+ return zone.hashCode() + 1;
+ }
+ @Override
+ public String toString() {
+ return "SystemClock[" + zone + "]";
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Implementation of a clock that always returns the same instant.
+ * This is typically used for testing.
+ */
+ static final class FixedClock extends Clock implements Serializable {
+ private static final long serialVersionUID = 7430389292664866958L;
+ private final Instant instant;
+ private final ZoneId zone;
+
+ FixedClock(Instant fixedInstant, ZoneId zone) {
+ this.instant = fixedInstant;
+ this.zone = zone;
+ }
+ @Override
+ public ZoneId getZone() {
+ return zone;
+ }
+ @Override
+ public Clock withZone(ZoneId zone) {
+ if (zone.equals(this.zone)) { // intentional NPE
+ return this;
+ }
+ return new FixedClock(instant, zone);
+ }
+ @Override
+ public long millis() {
+ return instant.toEpochMilli();
+ }
+ @Override
+ public Instant instant() {
+ return instant;
+ }
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof FixedClock) {
+ FixedClock other = (FixedClock) obj;
+ return instant.equals(other.instant) && zone.equals(other.zone);
+ }
+ return false;
+ }
+ @Override
+ public int hashCode() {
+ return instant.hashCode() ^ zone.hashCode();
+ }
+ @Override
+ public String toString() {
+ return "FixedClock[" + instant + "," + zone + "]";
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Implementation of a clock that adds an offset to an underlying clock.
+ */
+ static final class OffsetClock extends Clock implements Serializable {
+ private static final long serialVersionUID = 2007484719125426256L;
+ private final Clock baseClock;
+ private final Duration offset;
+
+ OffsetClock(Clock baseClock, Duration offset) {
+ this.baseClock = baseClock;
+ this.offset = offset;
+ }
+ @Override
+ public ZoneId getZone() {
+ return baseClock.getZone();
+ }
+ @Override
+ public Clock withZone(ZoneId zone) {
+ if (zone.equals(baseClock.getZone())) { // intentional NPE
+ return this;
+ }
+ return new OffsetClock(baseClock.withZone(zone), offset);
+ }
+ @Override
+ public long millis() {
+ return Math.addExact(baseClock.millis(), offset.toMillis());
+ }
+ @Override
+ public Instant instant() {
+ return baseClock.instant().plus(offset);
+ }
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof OffsetClock) {
+ OffsetClock other = (OffsetClock) obj;
+ return baseClock.equals(other.baseClock) && offset.equals(other.offset);
+ }
+ return false;
+ }
+ @Override
+ public int hashCode() {
+ return baseClock.hashCode() ^ offset.hashCode();
+ }
+ @Override
+ public String toString() {
+ return "OffsetClock[" + baseClock + "," + offset + "]";
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Implementation of a clock that adds an offset to an underlying clock.
+ */
+ static final class TickClock extends Clock implements Serializable {
+ private static final long serialVersionUID = 6504659149906368850L;
+ private final Clock baseClock;
+ private final long tickNanos;
+
+ TickClock(Clock baseClock, long tickNanos) {
+ this.baseClock = baseClock;
+ this.tickNanos = tickNanos;
+ }
+ @Override
+ public ZoneId getZone() {
+ return baseClock.getZone();
+ }
+ @Override
+ public Clock withZone(ZoneId zone) {
+ if (zone.equals(baseClock.getZone())) { // intentional NPE
+ return this;
+ }
+ return new TickClock(baseClock.withZone(zone), tickNanos);
+ }
+ @Override
+ public long millis() {
+ long millis = baseClock.millis();
+ return millis - Math.floorMod(millis, tickNanos / 1000_000L);
+ }
+ @Override
+ public Instant instant() {
+ if ((tickNanos % 1000_000) == 0) {
+ long millis = baseClock.millis();
+ return Instant.ofEpochMilli(millis - Math.floorMod(millis, tickNanos / 1000_000L));
+ }
+ Instant instant = baseClock.instant();
+ long nanos = instant.getNano();
+ long adjust = Math.floorMod(nanos, tickNanos);
+ return instant.minusNanos(adjust);
+ }
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof TickClock) {
+ TickClock other = (TickClock) obj;
+ return baseClock.equals(other.baseClock) && tickNanos == other.tickNanos;
+ }
+ return false;
+ }
+ @Override
+ public int hashCode() {
+ return baseClock.hashCode() ^ ((int) (tickNanos ^ (tickNanos >>> 32)));
+ }
+ @Override
+ public String toString() {
+ return "TickClock[" + baseClock + "," + Duration.ofNanos(tickNanos) + "]";
+ }
+ }
+
+}
diff --git a/java/time/DateTimeException.java b/java/time/DateTimeException.java
new file mode 100644
index 0000000..c2549fe
--- /dev/null
+++ b/java/time/DateTimeException.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2012, 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) 2008-2012, 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;
+
+/**
+ * Exception used to indicate a problem while calculating a date-time.
+ * <p>
+ * This exception is used to indicate problems with creating, querying
+ * and manipulating date-time objects.
+ *
+ * @implSpec
+ * This class is intended for use in a single thread.
+ *
+ * @since 1.8
+ */
+public class DateTimeException extends RuntimeException {
+
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = -1632418723876261839L;
+
+ /**
+ * Constructs a new date-time exception with the specified message.
+ *
+ * @param message the message to use for this exception, may be null
+ */
+ public DateTimeException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs a new date-time exception with the specified message and cause.
+ *
+ * @param message the message to use for this exception, may be null
+ * @param cause the cause of the exception, may be null
+ */
+ public DateTimeException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
diff --git a/java/time/DayOfWeek.java b/java/time/DayOfWeek.java
new file mode 100644
index 0000000..ee49f8a
--- /dev/null
+++ b/java/time/DayOfWeek.java
@@ -0,0 +1,457 @@
+/*
+ * Copyright (c) 2012, 2015, 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) 2007-2012, 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;
+
+import static java.time.temporal.ChronoField.DAY_OF_WEEK;
+import static java.time.temporal.ChronoUnit.DAYS;
+
+import java.time.format.DateTimeFormatterBuilder;
+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;
+import java.time.temporal.TemporalQueries;
+import java.time.temporal.TemporalQuery;
+import java.time.temporal.UnsupportedTemporalTypeException;
+import java.time.temporal.ValueRange;
+import java.time.temporal.WeekFields;
+import java.util.Locale;
+
+/**
+ * A day-of-week, such as 'Tuesday'.
+ * <p>
+ * {@code DayOfWeek} is an enum representing the 7 days of the week -
+ * Monday, Tuesday, Wednesday, Thursday, Friday, Saturday and Sunday.
+ * <p>
+ * In addition to the textual enum name, each day-of-week has an {@code int} value.
+ * The {@code int} value follows the ISO-8601 standard, from 1 (Monday) to 7 (Sunday).
+ * It is recommended that applications use the enum rather than the {@code int} value
+ * to ensure code clarity.
+ * <p>
+ * This enum provides access to the localized textual form of the day-of-week.
+ * Some locales also assign different numeric values to the days, declaring
+ * Sunday to have the value 1, however this class provides no support for this.
+ * See {@link WeekFields} for localized week-numbering.
+ * <p>
+ * <b>Do not use {@code ordinal()} to obtain the numeric representation of {@code DayOfWeek}.
+ * Use {@code getValue()} instead.</b>
+ * <p>
+ * This enum represents a common concept that is found in many calendar systems.
+ * As such, this enum may be used by any calendar system that has the day-of-week
+ * concept defined exactly equivalent to the ISO calendar system.
+ *
+ * @implSpec
+ * This is an immutable and thread-safe enum.
+ *
+ * @since 1.8
+ */
+public enum DayOfWeek implements TemporalAccessor, TemporalAdjuster {
+
+ /**
+ * The singleton instance for the day-of-week of Monday.
+ * This has the numeric value of {@code 1}.
+ */
+ MONDAY,
+ /**
+ * The singleton instance for the day-of-week of Tuesday.
+ * This has the numeric value of {@code 2}.
+ */
+ TUESDAY,
+ /**
+ * The singleton instance for the day-of-week of Wednesday.
+ * This has the numeric value of {@code 3}.
+ */
+ WEDNESDAY,
+ /**
+ * The singleton instance for the day-of-week of Thursday.
+ * This has the numeric value of {@code 4}.
+ */
+ THURSDAY,
+ /**
+ * The singleton instance for the day-of-week of Friday.
+ * This has the numeric value of {@code 5}.
+ */
+ FRIDAY,
+ /**
+ * The singleton instance for the day-of-week of Saturday.
+ * This has the numeric value of {@code 6}.
+ */
+ SATURDAY,
+ /**
+ * The singleton instance for the day-of-week of Sunday.
+ * This has the numeric value of {@code 7}.
+ */
+ SUNDAY;
+ /**
+ * Private cache of all the constants.
+ */
+ private static final DayOfWeek[] ENUMS = DayOfWeek.values();
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code DayOfWeek} from an {@code int} value.
+ * <p>
+ * {@code DayOfWeek} is an enum representing the 7 days of the week.
+ * This factory allows the enum to be obtained from the {@code int} value.
+ * The {@code int} value follows the ISO-8601 standard, from 1 (Monday) to 7 (Sunday).
+ *
+ * @param dayOfWeek the day-of-week to represent, from 1 (Monday) to 7 (Sunday)
+ * @return the day-of-week singleton, not null
+ * @throws DateTimeException if the day-of-week is invalid
+ */
+ public static DayOfWeek of(int dayOfWeek) {
+ if (dayOfWeek < 1 || dayOfWeek > 7) {
+ throw new DateTimeException("Invalid value for DayOfWeek: " + dayOfWeek);
+ }
+ return ENUMS[dayOfWeek - 1];
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code DayOfWeek} from a temporal object.
+ * <p>
+ * This obtains a day-of-week based on the specified temporal.
+ * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
+ * which this factory converts to an instance of {@code DayOfWeek}.
+ * <p>
+ * The conversion extracts the {@link ChronoField#DAY_OF_WEEK DAY_OF_WEEK} field.
+ * <p>
+ * This method matches the signature of the functional interface {@link TemporalQuery}
+ * allowing it to be used as a query via method reference, {@code DayOfWeek::from}.
+ *
+ * @param temporal the temporal object to convert, not null
+ * @return the day-of-week, not null
+ * @throws DateTimeException if unable to convert to a {@code DayOfWeek}
+ */
+ public static DayOfWeek from(TemporalAccessor temporal) {
+ if (temporal instanceof DayOfWeek) {
+ return (DayOfWeek) temporal;
+ }
+ try {
+ return of(temporal.get(DAY_OF_WEEK));
+ } catch (DateTimeException ex) {
+ throw new DateTimeException("Unable to obtain DayOfWeek from TemporalAccessor: " +
+ temporal + " of type " + temporal.getClass().getName(), ex);
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the day-of-week {@code int} value.
+ * <p>
+ * The values are numbered following the ISO-8601 standard, from 1 (Monday) to 7 (Sunday).
+ * See {@link java.time.temporal.WeekFields#dayOfWeek()} for localized week-numbering.
+ *
+ * @return the day-of-week, from 1 (Monday) to 7 (Sunday)
+ */
+ public int getValue() {
+ return ordinal() + 1;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the textual representation, such as 'Mon' or 'Friday'.
+ * <p>
+ * This returns the textual name used to identify the day-of-week,
+ * suitable for presentation to the user.
+ * The parameters control the style of the returned text and the locale.
+ * <p>
+ * If no textual mapping is found then the {@link #getValue() numeric value} is returned.
+ *
+ * @param style the length of the text required, not null
+ * @param locale the locale to use, not null
+ * @return the text value of the day-of-week, not null
+ */
+ public String getDisplayName(TextStyle style, Locale locale) {
+ return new DateTimeFormatterBuilder().appendText(DAY_OF_WEEK, style).toFormatter(locale).format(this);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if the specified field is supported.
+ * <p>
+ * This checks if this day-of-week can be queried for the specified field.
+ * If false, then calling the {@link #range(TemporalField) range} and
+ * {@link #get(TemporalField) get} methods will throw an exception.
+ * <p>
+ * If the field is {@link ChronoField#DAY_OF_WEEK DAY_OF_WEEK} then
+ * this method returns true.
+ * All other {@code ChronoField} instances will return false.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}
+ * passing {@code this} as the argument.
+ * Whether the field is supported is determined by the field.
+ *
+ * @param field the field to check, null returns false
+ * @return true if the field is supported on this day-of-week, false if not
+ */
+ @Override
+ public boolean isSupported(TemporalField field) {
+ if (field instanceof ChronoField) {
+ return field == DAY_OF_WEEK;
+ }
+ return field != null && field.isSupportedBy(this);
+ }
+
+ /**
+ * Gets the range of valid values for the specified field.
+ * <p>
+ * The range object expresses the minimum and maximum valid values for a field.
+ * This day-of-week is used to enhance the accuracy of the returned range.
+ * If it is not possible to return the range, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is {@link ChronoField#DAY_OF_WEEK DAY_OF_WEEK} then the
+ * range of the day-of-week, from 1 to 7, will be returned.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.rangeRefinedBy(TemporalAccessor)}
+ * passing {@code this} as the argument.
+ * Whether the range can be obtained is determined by the field.
+ *
+ * @param field the field to query the range for, not null
+ * @return the range of valid values for the field, not null
+ * @throws DateTimeException if the range for the field cannot be obtained
+ * @throws UnsupportedTemporalTypeException if the field is not supported
+ */
+ @Override
+ public ValueRange range(TemporalField field) {
+ if (field == DAY_OF_WEEK) {
+ return field.range();
+ }
+ return TemporalAccessor.super.range(field);
+ }
+
+ /**
+ * Gets the value of the specified field from this day-of-week as an {@code int}.
+ * <p>
+ * This queries this day-of-week for the value of the specified field.
+ * The returned value will always be within the valid range of values for the field.
+ * If it is not possible to return the value, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is {@link ChronoField#DAY_OF_WEEK DAY_OF_WEEK} then the
+ * value of the day-of-week, from 1 to 7, will be returned.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
+ * passing {@code this} as the argument. Whether the value can be obtained,
+ * and what the value represents, is determined by the field.
+ *
+ * @param field the field to get, not null
+ * @return the value for the field, within the valid range of values
+ * @throws DateTimeException if a value for the field cannot be obtained or
+ * the value is outside the range of valid values for the field
+ * @throws UnsupportedTemporalTypeException if the field is not supported or
+ * the range of values exceeds an {@code int}
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public int get(TemporalField field) {
+ if (field == DAY_OF_WEEK) {
+ return getValue();
+ }
+ return TemporalAccessor.super.get(field);
+ }
+
+ /**
+ * Gets the value of the specified field from this day-of-week as a {@code long}.
+ * <p>
+ * This queries this day-of-week for the value of the specified field.
+ * If it is not possible to return the value, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is {@link ChronoField#DAY_OF_WEEK DAY_OF_WEEK} then the
+ * value of the day-of-week, from 1 to 7, will be returned.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
+ * passing {@code this} as the argument. Whether the value can be obtained,
+ * and what the value represents, is determined by the field.
+ *
+ * @param field the field to get, not null
+ * @return the value for the field
+ * @throws DateTimeException if a value for the field cannot be obtained
+ * @throws UnsupportedTemporalTypeException if the field is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public long getLong(TemporalField field) {
+ if (field == DAY_OF_WEEK) {
+ return getValue();
+ } else if (field instanceof ChronoField) {
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
+ }
+ return field.getFrom(this);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns the day-of-week that is the specified number of days after this one.
+ * <p>
+ * The calculation rolls around the end of the week from Sunday to Monday.
+ * The specified period may be negative.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param days the days to add, positive or negative
+ * @return the resulting day-of-week, not null
+ */
+ public DayOfWeek plus(long days) {
+ int amount = (int) (days % 7);
+ return ENUMS[(ordinal() + (amount + 7)) % 7];
+ }
+
+ /**
+ * Returns the day-of-week that is the specified number of days before this one.
+ * <p>
+ * The calculation rolls around the start of the year from Monday to Sunday.
+ * The specified period may be negative.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param days the days to subtract, positive or negative
+ * @return the resulting day-of-week, not null
+ */
+ public DayOfWeek minus(long days) {
+ return plus(-(days % 7));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Queries this day-of-week using the specified query.
+ * <p>
+ * This queries this day-of-week using the specified query strategy object.
+ * The {@code TemporalQuery} object defines the logic to be used to
+ * obtain the result. Read the documentation of the query to understand
+ * what the result of this method will be.
+ * <p>
+ * The result of this method is obtained by invoking the
+ * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the
+ * specified query passing {@code this} as the argument.
+ *
+ * @param <R> the type of the result
+ * @param query the query to invoke, not null
+ * @return the query result, null may be returned (defined by the query)
+ * @throws DateTimeException if unable to query (defined by the query)
+ * @throws ArithmeticException if numeric overflow occurs (defined by the query)
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public <R> R query(TemporalQuery<R> query) {
+ if (query == TemporalQueries.precision()) {
+ return (R) DAYS;
+ }
+ return TemporalAccessor.super.query(query);
+ }
+
+ /**
+ * Adjusts the specified temporal object to have this day-of-week.
+ * <p>
+ * This returns a temporal object of the same observable type as the input
+ * with the day-of-week changed to be the same as this.
+ * <p>
+ * The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)}
+ * passing {@link ChronoField#DAY_OF_WEEK} as the field.
+ * Note that this adjusts forwards or backwards within a Monday to Sunday week.
+ * See {@link java.time.temporal.WeekFields#dayOfWeek()} for localized week start days.
+ * See {@code TemporalAdjuster} for other adjusters with more control,
+ * such as {@code next(MONDAY)}.
+ * <p>
+ * In most cases, it is clearer to reverse the calling pattern by using
+ * {@link Temporal#with(TemporalAdjuster)}:
+ * <pre>
+ * // these two lines are equivalent, but the second approach is recommended
+ * temporal = thisDayOfWeek.adjustInto(temporal);
+ * temporal = temporal.with(thisDayOfWeek);
+ * </pre>
+ * <p>
+ * For example, given a date that is a Wednesday, the following are output:
+ * <pre>
+ * dateOnWed.with(MONDAY); // two days earlier
+ * dateOnWed.with(TUESDAY); // one day earlier
+ * dateOnWed.with(WEDNESDAY); // same date
+ * dateOnWed.with(THURSDAY); // one day later
+ * dateOnWed.with(FRIDAY); // two days later
+ * dateOnWed.with(SATURDAY); // three days later
+ * dateOnWed.with(SUNDAY); // four days later
+ * </pre>
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param temporal the target object to be adjusted, not null
+ * @return the adjusted object, not null
+ * @throws DateTimeException if unable to make the adjustment
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public Temporal adjustInto(Temporal temporal) {
+ return temporal.with(DAY_OF_WEEK, getValue());
+ }
+
+}
diff --git a/java/time/Duration.java b/java/time/Duration.java
new file mode 100644
index 0000000..8637c40
--- /dev/null
+++ b/java/time/Duration.java
@@ -0,0 +1,1341 @@
+/*
+ * Copyright (c) 2012, 2015, 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) 2007-2012, 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;
+
+import static java.time.LocalTime.NANOS_PER_SECOND;
+import static java.time.LocalTime.SECONDS_PER_DAY;
+import static java.time.LocalTime.SECONDS_PER_HOUR;
+import static java.time.LocalTime.SECONDS_PER_MINUTE;
+import static java.time.temporal.ChronoField.NANO_OF_SECOND;
+import static java.time.temporal.ChronoUnit.DAYS;
+import static java.time.temporal.ChronoUnit.NANOS;
+import static java.time.temporal.ChronoUnit.SECONDS;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.math.RoundingMode;
+import java.time.format.DateTimeParseException;
+import java.time.temporal.ChronoField;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.Temporal;
+import java.time.temporal.TemporalAmount;
+import java.time.temporal.TemporalUnit;
+import java.time.temporal.UnsupportedTemporalTypeException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+// Android-changed: removed ValueBased paragraph.
+/**
+ * A time-based amount of time, such as '34.5 seconds'.
+ * <p>
+ * This class models a quantity or amount of time in terms of seconds and nanoseconds.
+ * It can be accessed using other duration-based units, such as minutes and hours.
+ * In addition, the {@link ChronoUnit#DAYS DAYS} unit can be used and is treated as
+ * exactly equal to 24 hours, thus ignoring daylight savings effects.
+ * See {@link Period} for the date-based equivalent to this class.
+ * <p>
+ * A physical duration could be of infinite length.
+ * For practicality, the duration is stored with constraints similar to {@link Instant}.
+ * The duration uses nanosecond resolution with a maximum value of the seconds that can
+ * be held in a {@code long}. This is greater than the current estimated age of the universe.
+ * <p>
+ * The range of a duration requires the storage of a number larger than a {@code long}.
+ * To achieve this, the class stores a {@code long} representing seconds and an {@code int}
+ * representing nanosecond-of-second, which will always be between 0 and 999,999,999.
+ * The model is of a directed duration, meaning that the duration may be negative.
+ * <p>
+ * The duration is measured in "seconds", but these are not necessarily identical to
+ * the scientific "SI second" definition based on atomic clocks.
+ * This difference only impacts durations measured near a leap-second and should not affect
+ * most applications.
+ * See {@link Instant} for a discussion as to the meaning of the second and time-scales.
+ *
+ * @implSpec
+ * This class is immutable and thread-safe.
+ *
+ * @since 1.8
+ */
+public final class Duration
+ implements TemporalAmount, Comparable<Duration>, Serializable {
+
+ /**
+ * Constant for a duration of zero.
+ */
+ public static final Duration ZERO = new Duration(0, 0);
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = 3078945930695997490L;
+ /**
+ * Constant for nanos per second.
+ */
+ private static final BigInteger BI_NANOS_PER_SECOND = BigInteger.valueOf(NANOS_PER_SECOND);
+ /**
+ * The pattern for parsing.
+ */
+ private static final Pattern PATTERN =
+ Pattern.compile("([-+]?)P(?:([-+]?[0-9]+)D)?" +
+ "(T(?:([-+]?[0-9]+)H)?(?:([-+]?[0-9]+)M)?(?:([-+]?[0-9]+)(?:[.,]([0-9]{0,9}))?S)?)?",
+ Pattern.CASE_INSENSITIVE);
+
+ /**
+ * The number of seconds in the duration.
+ */
+ private final long seconds;
+ /**
+ * The number of nanoseconds in the duration, expressed as a fraction of the
+ * number of seconds. This is always positive, and never exceeds 999,999,999.
+ */
+ private final int nanos;
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains a {@code Duration} representing a number of standard 24 hour days.
+ * <p>
+ * The seconds are calculated based on the standard definition of a day,
+ * where each day is 86400 seconds which implies a 24 hour day.
+ * The nanosecond in second field is set to zero.
+ *
+ * @param days the number of days, positive or negative
+ * @return a {@code Duration}, not null
+ * @throws ArithmeticException if the input days exceeds the capacity of {@code Duration}
+ */
+ public static Duration ofDays(long days) {
+ return create(Math.multiplyExact(days, SECONDS_PER_DAY), 0);
+ }
+
+ /**
+ * Obtains a {@code Duration} representing a number of standard hours.
+ * <p>
+ * The seconds are calculated based on the standard definition of an hour,
+ * where each hour is 3600 seconds.
+ * The nanosecond in second field is set to zero.
+ *
+ * @param hours the number of hours, positive or negative
+ * @return a {@code Duration}, not null
+ * @throws ArithmeticException if the input hours exceeds the capacity of {@code Duration}
+ */
+ public static Duration ofHours(long hours) {
+ return create(Math.multiplyExact(hours, SECONDS_PER_HOUR), 0);
+ }
+
+ /**
+ * Obtains a {@code Duration} representing a number of standard minutes.
+ * <p>
+ * The seconds are calculated based on the standard definition of a minute,
+ * where each minute is 60 seconds.
+ * The nanosecond in second field is set to zero.
+ *
+ * @param minutes the number of minutes, positive or negative
+ * @return a {@code Duration}, not null
+ * @throws ArithmeticException if the input minutes exceeds the capacity of {@code Duration}
+ */
+ public static Duration ofMinutes(long minutes) {
+ return create(Math.multiplyExact(minutes, SECONDS_PER_MINUTE), 0);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains a {@code Duration} representing a number of seconds.
+ * <p>
+ * The nanosecond in second field is set to zero.
+ *
+ * @param seconds the number of seconds, positive or negative
+ * @return a {@code Duration}, not null
+ */
+ public static Duration ofSeconds(long seconds) {
+ return create(seconds, 0);
+ }
+
+ /**
+ * Obtains a {@code Duration} representing a number of seconds and an
+ * adjustment in nanoseconds.
+ * <p>
+ * This method allows an arbitrary number of nanoseconds to be passed in.
+ * The factory will alter the values of the second and nanosecond in order
+ * to ensure that the stored nanosecond is in the range 0 to 999,999,999.
+ * For example, the following will result in the exactly the same duration:
+ * <pre>
+ * Duration.ofSeconds(3, 1);
+ * Duration.ofSeconds(4, -999_999_999);
+ * Duration.ofSeconds(2, 1000_000_001);
+ * </pre>
+ *
+ * @param seconds the number of seconds, positive or negative
+ * @param nanoAdjustment the nanosecond adjustment to the number of seconds, positive or negative
+ * @return a {@code Duration}, not null
+ * @throws ArithmeticException if the adjustment causes the seconds to exceed the capacity of {@code Duration}
+ */
+ public static Duration ofSeconds(long seconds, long nanoAdjustment) {
+ long secs = Math.addExact(seconds, Math.floorDiv(nanoAdjustment, NANOS_PER_SECOND));
+ int nos = (int) Math.floorMod(nanoAdjustment, NANOS_PER_SECOND);
+ return create(secs, nos);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains a {@code Duration} representing a number of milliseconds.
+ * <p>
+ * The seconds and nanoseconds are extracted from the specified milliseconds.
+ *
+ * @param millis the number of milliseconds, positive or negative
+ * @return a {@code Duration}, not null
+ */
+ public static Duration ofMillis(long millis) {
+ long secs = millis / 1000;
+ int mos = (int) (millis % 1000);
+ if (mos < 0) {
+ mos += 1000;
+ secs--;
+ }
+ return create(secs, mos * 1000_000);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains a {@code Duration} representing a number of nanoseconds.
+ * <p>
+ * The seconds and nanoseconds are extracted from the specified nanoseconds.
+ *
+ * @param nanos the number of nanoseconds, positive or negative
+ * @return a {@code Duration}, not null
+ */
+ public static Duration ofNanos(long nanos) {
+ long secs = nanos / NANOS_PER_SECOND;
+ int nos = (int) (nanos % NANOS_PER_SECOND);
+ if (nos < 0) {
+ nos += NANOS_PER_SECOND;
+ secs--;
+ }
+ return create(secs, nos);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains a {@code Duration} representing an amount in the specified unit.
+ * <p>
+ * The parameters represent the two parts of a phrase like '6 Hours'. For example:
+ * <pre>
+ * Duration.of(3, SECONDS);
+ * Duration.of(465, HOURS);
+ * </pre>
+ * Only a subset of units are accepted by this method.
+ * The unit must either have an {@linkplain TemporalUnit#isDurationEstimated() exact duration} or
+ * be {@link ChronoUnit#DAYS} which is treated as 24 hours. Other units throw an exception.
+ *
+ * @param amount the amount of the duration, measured in terms of the unit, positive or negative
+ * @param unit the unit that the duration is measured in, must have an exact duration, not null
+ * @return a {@code Duration}, not null
+ * @throws DateTimeException if the period unit has an estimated duration
+ * @throws ArithmeticException if a numeric overflow occurs
+ */
+ public static Duration of(long amount, TemporalUnit unit) {
+ return ZERO.plus(amount, unit);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code Duration} from a temporal amount.
+ * <p>
+ * This obtains a duration 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 duration.
+ * <p>
+ * The conversion loops around the set of units from the amount and uses
+ * the {@linkplain TemporalUnit#getDuration() duration} of the unit to
+ * calculate the total {@code Duration}.
+ * Only a subset of units are accepted by this method. The unit must either
+ * have an {@linkplain TemporalUnit#isDurationEstimated() exact duration}
+ * or be {@link ChronoUnit#DAYS} which is treated as 24 hours.
+ * If any other units are found then an exception is thrown.
+ *
+ * @param amount the temporal amount to convert, not null
+ * @return the equivalent duration, not null
+ * @throws DateTimeException if unable to convert to a {@code Duration}
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public static Duration from(TemporalAmount amount) {
+ Objects.requireNonNull(amount, "amount");
+ Duration duration = ZERO;
+ for (TemporalUnit unit : amount.getUnits()) {
+ duration = duration.plus(amount.get(unit), unit);
+ }
+ return duration;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains a {@code Duration} from a text string such as {@code PnDTnHnMn.nS}.
+ * <p>
+ * This will parse a textual representation of a duration, including the
+ * string produced by {@code toString()}. The formats accepted are based
+ * on the ISO-8601 duration format {@code PnDTnHnMn.nS} with days
+ * considered to be exactly 24 hours.
+ * <p>
+ * The string starts with an optional sign, denoted by the ASCII negative
+ * or positive symbol. If negative, the whole period is negated.
+ * The ASCII letter "P" is next in upper or lower case.
+ * There are then four sections, each consisting of a number and a suffix.
+ * The sections have suffixes in ASCII of "D", "H", "M" and "S" for
+ * days, hours, minutes and seconds, accepted in upper or lower case.
+ * The suffixes must occur in order. The ASCII letter "T" must occur before
+ * the first occurrence, if any, of an hour, minute or second section.
+ * At least one of the four sections must be present, and if "T" is present
+ * there must be at least one section after the "T".
+ * The number part of each section must consist of one or more ASCII digits.
+ * The number may be prefixed by the ASCII negative or positive symbol.
+ * The number of days, hours and minutes must parse to an {@code long}.
+ * The number of seconds must parse to an {@code long} with optional fraction.
+ * The decimal point may be either a dot or a comma.
+ * The fractional part may have from zero to 9 digits.
+ * <p>
+ * The leading plus/minus sign, and negative values for other units are
+ * not part of the ISO-8601 standard.
+ * <p>
+ * Examples:
+ * <pre>
+ * "PT20.345S" -- parses as "20.345 seconds"
+ * "PT15M" -- parses as "15 minutes" (where a minute is 60 seconds)
+ * "PT10H" -- parses as "10 hours" (where an hour is 3600 seconds)
+ * "P2D" -- parses as "2 days" (where a day is 24 hours or 86400 seconds)
+ * "P2DT3H4M" -- parses as "2 days, 3 hours and 4 minutes"
+ * "P-6H3M" -- parses as "-6 hours and +3 minutes"
+ * "-P6H3M" -- parses as "-6 hours and -3 minutes"
+ * "-P-6H+3M" -- parses as "+6 hours and -3 minutes"
+ * </pre>
+ *
+ * @param text the text to parse, not null
+ * @return the parsed duration, not null
+ * @throws DateTimeParseException if the text cannot be parsed to a duration
+ */
+ public static Duration parse(CharSequence text) {
+ Objects.requireNonNull(text, "text");
+ Matcher matcher = PATTERN.matcher(text);
+ if (matcher.matches()) {
+ // check for letter T but no time sections
+ if ("T".equals(matcher.group(3)) == false) {
+ boolean negate = "-".equals(matcher.group(1));
+ String dayMatch = matcher.group(2);
+ String hourMatch = matcher.group(4);
+ String minuteMatch = matcher.group(5);
+ String secondMatch = matcher.group(6);
+ String fractionMatch = matcher.group(7);
+ if (dayMatch != null || hourMatch != null || minuteMatch != null || secondMatch != null) {
+ long daysAsSecs = parseNumber(text, dayMatch, SECONDS_PER_DAY, "days");
+ long hoursAsSecs = parseNumber(text, hourMatch, SECONDS_PER_HOUR, "hours");
+ long minsAsSecs = parseNumber(text, minuteMatch, SECONDS_PER_MINUTE, "minutes");
+ long seconds = parseNumber(text, secondMatch, 1, "seconds");
+ int nanos = parseFraction(text, fractionMatch, seconds < 0 ? -1 : 1);
+ try {
+ return create(negate, daysAsSecs, hoursAsSecs, minsAsSecs, seconds, nanos);
+ } catch (ArithmeticException ex) {
+ throw (DateTimeParseException) new DateTimeParseException("Text cannot be parsed to a Duration: overflow", text, 0).initCause(ex);
+ }
+ }
+ }
+ }
+ throw new DateTimeParseException("Text cannot be parsed to a Duration", text, 0);
+ }
+
+ private static long parseNumber(CharSequence text, String parsed, int multiplier, String errorText) {
+ // regex limits to [-+]?[0-9]+
+ if (parsed == null) {
+ return 0;
+ }
+ try {
+ long val = Long.parseLong(parsed);
+ return Math.multiplyExact(val, multiplier);
+ } catch (NumberFormatException | ArithmeticException ex) {
+ throw (DateTimeParseException) new DateTimeParseException("Text cannot be parsed to a Duration: " + errorText, text, 0).initCause(ex);
+ }
+ }
+
+ private static int parseFraction(CharSequence text, String parsed, int negate) {
+ // regex limits to [0-9]{0,9}
+ if (parsed == null || parsed.length() == 0) {
+ return 0;
+ }
+ try {
+ parsed = (parsed + "000000000").substring(0, 9);
+ return Integer.parseInt(parsed) * negate;
+ } catch (NumberFormatException | ArithmeticException ex) {
+ throw (DateTimeParseException) new DateTimeParseException("Text cannot be parsed to a Duration: fraction", text, 0).initCause(ex);
+ }
+ }
+
+ private static Duration create(boolean negate, long daysAsSecs, long hoursAsSecs, long minsAsSecs, long secs, int nanos) {
+ long seconds = Math.addExact(daysAsSecs, Math.addExact(hoursAsSecs, Math.addExact(minsAsSecs, secs)));
+ if (negate) {
+ return ofSeconds(seconds, nanos).negated();
+ }
+ return ofSeconds(seconds, nanos);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains a {@code Duration} representing the duration between two temporal objects.
+ * <p>
+ * This calculates the duration between two temporal objects. If the objects
+ * are of different types, then the duration is calculated based on the type
+ * of the first object. For example, if the first argument is a {@code LocalTime}
+ * then the second argument is converted to a {@code LocalTime}.
+ * <p>
+ * The specified temporal objects must support the {@link ChronoUnit#SECONDS SECONDS} unit.
+ * For full accuracy, either the {@link ChronoUnit#NANOS NANOS} unit or the
+ * {@link ChronoField#NANO_OF_SECOND NANO_OF_SECOND} field should be supported.
+ * <p>
+ * The result of this method can be a negative period if the end is before the start.
+ * To guarantee to obtain a positive duration call {@link #abs()} on the result.
+ *
+ * @param startInclusive the start instant, inclusive, not null
+ * @param endExclusive the end instant, exclusive, not null
+ * @return a {@code Duration}, not null
+ * @throws DateTimeException if the seconds between the temporals cannot be obtained
+ * @throws ArithmeticException if the calculation exceeds the capacity of {@code Duration}
+ */
+ public static Duration between(Temporal startInclusive, Temporal endExclusive) {
+ try {
+ return ofNanos(startInclusive.until(endExclusive, NANOS));
+ } catch (DateTimeException | ArithmeticException ex) {
+ long secs = startInclusive.until(endExclusive, SECONDS);
+ long nanos;
+ try {
+ nanos = endExclusive.getLong(NANO_OF_SECOND) - startInclusive.getLong(NANO_OF_SECOND);
+ if (secs > 0 && nanos < 0) {
+ secs++;
+ } else if (secs < 0 && nanos > 0) {
+ secs--;
+ }
+ } catch (DateTimeException ex2) {
+ nanos = 0;
+ }
+ return ofSeconds(secs, nanos);
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code Duration} using seconds and nanoseconds.
+ *
+ * @param seconds the length of the duration in seconds, positive or negative
+ * @param nanoAdjustment the nanosecond adjustment within the second, from 0 to 999,999,999
+ */
+ private static Duration create(long seconds, int nanoAdjustment) {
+ if ((seconds | nanoAdjustment) == 0) {
+ return ZERO;
+ }
+ return new Duration(seconds, nanoAdjustment);
+ }
+
+ /**
+ * Constructs an instance of {@code Duration} using seconds and nanoseconds.
+ *
+ * @param seconds the length of the duration in seconds, positive or negative
+ * @param nanos the nanoseconds within the second, from 0 to 999,999,999
+ */
+ private Duration(long seconds, int nanos) {
+ super();
+ this.seconds = seconds;
+ this.nanos = nanos;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the value of the requested unit.
+ * <p>
+ * This returns a value for each of the two supported units,
+ * {@link ChronoUnit#SECONDS SECONDS} and {@link ChronoUnit#NANOS NANOS}.
+ * All other units 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
+ public long get(TemporalUnit unit) {
+ if (unit == SECONDS) {
+ return seconds;
+ } else if (unit == NANOS) {
+ return nanos;
+ } else {
+ throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
+ }
+ }
+
+ /**
+ * Gets the set of units supported by this duration.
+ * <p>
+ * The supported units are {@link ChronoUnit#SECONDS SECONDS},
+ * and {@link ChronoUnit#NANOS NANOS}.
+ * They are returned in the order seconds, nanos.
+ * <p>
+ * This set can be used in conjunction with {@link #get(TemporalUnit)}
+ * to access the entire state of the duration.
+ *
+ * @return a list containing the seconds and nanos units, not null
+ */
+ @Override
+ public List<TemporalUnit> getUnits() {
+ return DurationUnits.UNITS;
+ }
+
+ /**
+ * Private class to delay initialization of this list until needed.
+ * The circular dependency between Duration and ChronoUnit prevents
+ * the simple initialization in Duration.
+ */
+ private static class DurationUnits {
+ static final List<TemporalUnit> UNITS =
+ Collections.unmodifiableList(Arrays.<TemporalUnit>asList(SECONDS, NANOS));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if this duration is zero length.
+ * <p>
+ * A {@code Duration} represents a directed distance between two points on
+ * the time-line and can therefore be positive, zero or negative.
+ * This method checks whether the length is zero.
+ *
+ * @return true if this duration has a total length equal to zero
+ */
+ public boolean isZero() {
+ return (seconds | nanos) == 0;
+ }
+
+ /**
+ * Checks if this duration is negative, excluding zero.
+ * <p>
+ * A {@code Duration} represents a directed distance between two points on
+ * the time-line and can therefore be positive, zero or negative.
+ * This method checks whether the length is less than zero.
+ *
+ * @return true if this duration has a total length less than zero
+ */
+ public boolean isNegative() {
+ return seconds < 0;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the number of seconds in this duration.
+ * <p>
+ * The length of the duration is stored using two fields - seconds and nanoseconds.
+ * The nanoseconds part is a value from 0 to 999,999,999 that is an adjustment to
+ * the length in seconds.
+ * The total duration is defined by calling this method and {@link #getNano()}.
+ * <p>
+ * A {@code Duration} represents a directed distance between two points on the time-line.
+ * A negative duration is expressed by the negative sign of the seconds part.
+ * A duration of -1 nanosecond is stored as -1 seconds plus 999,999,999 nanoseconds.
+ *
+ * @return the whole seconds part of the length of the duration, positive or negative
+ */
+ public long getSeconds() {
+ return seconds;
+ }
+
+ /**
+ * Gets the number of nanoseconds within the second in this duration.
+ * <p>
+ * The length of the duration is stored using two fields - seconds and nanoseconds.
+ * The nanoseconds part is a value from 0 to 999,999,999 that is an adjustment to
+ * the length in seconds.
+ * The total duration is defined by calling this method and {@link #getSeconds()}.
+ * <p>
+ * A {@code Duration} represents a directed distance between two points on the time-line.
+ * A negative duration is expressed by the negative sign of the seconds part.
+ * A duration of -1 nanosecond is stored as -1 seconds plus 999,999,999 nanoseconds.
+ *
+ * @return the nanoseconds within the second part of the length of the duration, from 0 to 999,999,999
+ */
+ public int getNano() {
+ return nanos;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this duration with the specified amount of seconds.
+ * <p>
+ * This returns a duration with the specified seconds, retaining the
+ * nano-of-second part of this duration.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param seconds the seconds to represent, may be negative
+ * @return a {@code Duration} based on this period with the requested seconds, not null
+ */
+ public Duration withSeconds(long seconds) {
+ return create(seconds, nanos);
+ }
+
+ /**
+ * Returns a copy of this duration with the specified nano-of-second.
+ * <p>
+ * This returns a duration with the specified nano-of-second, retaining the
+ * seconds part of this duration.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param nanoOfSecond the nano-of-second to represent, from 0 to 999,999,999
+ * @return a {@code Duration} based on this period with the requested nano-of-second, not null
+ * @throws DateTimeException if the nano-of-second is invalid
+ */
+ public Duration withNanos(int nanoOfSecond) {
+ NANO_OF_SECOND.checkValidIntValue(nanoOfSecond);
+ return create(seconds, nanoOfSecond);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this duration with the specified duration added.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param duration the duration to add, positive or negative, not null
+ * @return a {@code Duration} based on this duration with the specified duration added, not null
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public Duration plus(Duration duration) {
+ return plus(duration.getSeconds(), duration.getNano());
+ }
+
+ /**
+ * Returns a copy of this duration with the specified duration added.
+ * <p>
+ * The duration amount is measured in terms of the specified unit.
+ * Only a subset of units are accepted by this method.
+ * The unit must either have an {@linkplain TemporalUnit#isDurationEstimated() exact duration} or
+ * be {@link ChronoUnit#DAYS} which is treated as 24 hours. Other units throw an exception.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param amountToAdd the amount to add, measured in terms of the unit, positive or negative
+ * @param unit the unit that the amount is measured in, must have an exact duration, not null
+ * @return a {@code Duration} based on this duration with the specified duration added, not null
+ * @throws UnsupportedTemporalTypeException if the unit is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public Duration plus(long amountToAdd, TemporalUnit unit) {
+ Objects.requireNonNull(unit, "unit");
+ if (unit == DAYS) {
+ return plus(Math.multiplyExact(amountToAdd, SECONDS_PER_DAY), 0);
+ }
+ if (unit.isDurationEstimated()) {
+ throw new UnsupportedTemporalTypeException("Unit must not have an estimated duration");
+ }
+ if (amountToAdd == 0) {
+ return this;
+ }
+ if (unit instanceof ChronoUnit) {
+ switch ((ChronoUnit) unit) {
+ case NANOS: return plusNanos(amountToAdd);
+ case MICROS: return plusSeconds((amountToAdd / (1000_000L * 1000)) * 1000).plusNanos((amountToAdd % (1000_000L * 1000)) * 1000);
+ case MILLIS: return plusMillis(amountToAdd);
+ case SECONDS: return plusSeconds(amountToAdd);
+ }
+ return plusSeconds(Math.multiplyExact(unit.getDuration().seconds, amountToAdd));
+ }
+ Duration duration = unit.getDuration().multipliedBy(amountToAdd);
+ return plusSeconds(duration.getSeconds()).plusNanos(duration.getNano());
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this duration with the specified duration in standard 24 hour days added.
+ * <p>
+ * The number of days is multiplied by 86400 to obtain the number of seconds to add.
+ * This is based on the standard definition of a day as 24 hours.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param daysToAdd the days to add, positive or negative
+ * @return a {@code Duration} based on this duration with the specified days added, not null
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public Duration plusDays(long daysToAdd) {
+ return plus(Math.multiplyExact(daysToAdd, SECONDS_PER_DAY), 0);
+ }
+
+ /**
+ * Returns a copy of this duration with the specified duration in hours added.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param hoursToAdd the hours to add, positive or negative
+ * @return a {@code Duration} based on this duration with the specified hours added, not null
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public Duration plusHours(long hoursToAdd) {
+ return plus(Math.multiplyExact(hoursToAdd, SECONDS_PER_HOUR), 0);
+ }
+
+ /**
+ * Returns a copy of this duration with the specified duration in minutes added.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param minutesToAdd the minutes to add, positive or negative
+ * @return a {@code Duration} based on this duration with the specified minutes added, not null
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public Duration plusMinutes(long minutesToAdd) {
+ return plus(Math.multiplyExact(minutesToAdd, SECONDS_PER_MINUTE), 0);
+ }
+
+ /**
+ * Returns a copy of this duration with the specified duration in seconds added.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param secondsToAdd the seconds to add, positive or negative
+ * @return a {@code Duration} based on this duration with the specified seconds added, not null
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public Duration plusSeconds(long secondsToAdd) {
+ return plus(secondsToAdd, 0);
+ }
+
+ /**
+ * Returns a copy of this duration with the specified duration in milliseconds added.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param millisToAdd the milliseconds to add, positive or negative
+ * @return a {@code Duration} based on this duration with the specified milliseconds added, not null
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public Duration plusMillis(long millisToAdd) {
+ return plus(millisToAdd / 1000, (millisToAdd % 1000) * 1000_000);
+ }
+
+ /**
+ * Returns a copy of this duration with the specified duration in nanoseconds added.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param nanosToAdd the nanoseconds to add, positive or negative
+ * @return a {@code Duration} based on this duration with the specified nanoseconds added, not null
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public Duration plusNanos(long nanosToAdd) {
+ return plus(0, nanosToAdd);
+ }
+
+ /**
+ * Returns a copy of this duration with the specified duration added.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param secondsToAdd the seconds to add, positive or negative
+ * @param nanosToAdd the nanos to add, positive or negative
+ * @return a {@code Duration} based on this duration with the specified seconds added, not null
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ private Duration plus(long secondsToAdd, long nanosToAdd) {
+ if ((secondsToAdd | nanosToAdd) == 0) {
+ return this;
+ }
+ long epochSec = Math.addExact(seconds, secondsToAdd);
+ epochSec = Math.addExact(epochSec, nanosToAdd / NANOS_PER_SECOND);
+ nanosToAdd = nanosToAdd % NANOS_PER_SECOND;
+ long nanoAdjustment = nanos + nanosToAdd; // safe int+NANOS_PER_SECOND
+ return ofSeconds(epochSec, nanoAdjustment);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this duration with the specified duration subtracted.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param duration the duration to subtract, positive or negative, not null
+ * @return a {@code Duration} based on this duration with the specified duration subtracted, not null
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public Duration minus(Duration duration) {
+ long secsToSubtract = duration.getSeconds();
+ int nanosToSubtract = duration.getNano();
+ if (secsToSubtract == Long.MIN_VALUE) {
+ return plus(Long.MAX_VALUE, -nanosToSubtract).plus(1, 0);
+ }
+ return plus(-secsToSubtract, -nanosToSubtract);
+ }
+
+ /**
+ * Returns a copy of this duration with the specified duration subtracted.
+ * <p>
+ * The duration amount is measured in terms of the specified unit.
+ * Only a subset of units are accepted by this method.
+ * The unit must either have an {@linkplain TemporalUnit#isDurationEstimated() exact duration} or
+ * be {@link ChronoUnit#DAYS} which is treated as 24 hours. Other units throw an exception.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param amountToSubtract the amount to subtract, measured in terms of the unit, positive or negative
+ * @param unit the unit that the amount is measured in, must have an exact duration, not null
+ * @return a {@code Duration} based on this duration with the specified duration subtracted, not null
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public Duration minus(long amountToSubtract, TemporalUnit unit) {
+ return (amountToSubtract == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this duration with the specified duration in standard 24 hour days subtracted.
+ * <p>
+ * The number of days is multiplied by 86400 to obtain the number of seconds to subtract.
+ * This is based on the standard definition of a day as 24 hours.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param daysToSubtract the days to subtract, positive or negative
+ * @return a {@code Duration} based on this duration with the specified days subtracted, not null
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public Duration minusDays(long daysToSubtract) {
+ return (daysToSubtract == Long.MIN_VALUE ? plusDays(Long.MAX_VALUE).plusDays(1) : plusDays(-daysToSubtract));
+ }
+
+ /**
+ * Returns a copy of this duration with the specified duration in hours subtracted.
+ * <p>
+ * The number of hours is multiplied by 3600 to obtain the number of seconds to subtract.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param hoursToSubtract the hours to subtract, positive or negative
+ * @return a {@code Duration} based on this duration with the specified hours subtracted, not null
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public Duration minusHours(long hoursToSubtract) {
+ return (hoursToSubtract == Long.MIN_VALUE ? plusHours(Long.MAX_VALUE).plusHours(1) : plusHours(-hoursToSubtract));
+ }
+
+ /**
+ * Returns a copy of this duration with the specified duration in minutes subtracted.
+ * <p>
+ * The number of hours is multiplied by 60 to obtain the number of seconds to subtract.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param minutesToSubtract the minutes to subtract, positive or negative
+ * @return a {@code Duration} based on this duration with the specified minutes subtracted, not null
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public Duration minusMinutes(long minutesToSubtract) {
+ return (minutesToSubtract == Long.MIN_VALUE ? plusMinutes(Long.MAX_VALUE).plusMinutes(1) : plusMinutes(-minutesToSubtract));
+ }
+
+ /**
+ * Returns a copy of this duration with the specified duration in seconds subtracted.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param secondsToSubtract the seconds to subtract, positive or negative
+ * @return a {@code Duration} based on this duration with the specified seconds subtracted, not null
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public Duration minusSeconds(long secondsToSubtract) {
+ return (secondsToSubtract == Long.MIN_VALUE ? plusSeconds(Long.MAX_VALUE).plusSeconds(1) : plusSeconds(-secondsToSubtract));
+ }
+
+ /**
+ * Returns a copy of this duration with the specified duration in milliseconds subtracted.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param millisToSubtract the milliseconds to subtract, positive or negative
+ * @return a {@code Duration} based on this duration with the specified milliseconds subtracted, not null
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public Duration minusMillis(long millisToSubtract) {
+ return (millisToSubtract == Long.MIN_VALUE ? plusMillis(Long.MAX_VALUE).plusMillis(1) : plusMillis(-millisToSubtract));
+ }
+
+ /**
+ * Returns a copy of this duration with the specified duration in nanoseconds subtracted.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param nanosToSubtract the nanoseconds to subtract, positive or negative
+ * @return a {@code Duration} based on this duration with the specified nanoseconds subtracted, not null
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public Duration minusNanos(long nanosToSubtract) {
+ return (nanosToSubtract == Long.MIN_VALUE ? plusNanos(Long.MAX_VALUE).plusNanos(1) : plusNanos(-nanosToSubtract));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this duration multiplied by the scalar.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param multiplicand the value to multiply the duration by, positive or negative
+ * @return a {@code Duration} based on this duration multiplied by the specified scalar, not null
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public Duration multipliedBy(long multiplicand) {
+ if (multiplicand == 0) {
+ return ZERO;
+ }
+ if (multiplicand == 1) {
+ return this;
+ }
+ return create(toSeconds().multiply(BigDecimal.valueOf(multiplicand)));
+ }
+
+ /**
+ * Returns a copy of this duration divided by the specified value.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param divisor the value to divide the duration by, positive or negative, not zero
+ * @return a {@code Duration} based on this duration divided by the specified divisor, not null
+ * @throws ArithmeticException if the divisor is zero or if numeric overflow occurs
+ */
+ public Duration dividedBy(long divisor) {
+ if (divisor == 0) {
+ throw new ArithmeticException("Cannot divide by zero");
+ }
+ if (divisor == 1) {
+ return this;
+ }
+ return create(toSeconds().divide(BigDecimal.valueOf(divisor), RoundingMode.DOWN));
+ }
+
+ /**
+ * Converts this duration to the total length in seconds and
+ * fractional nanoseconds expressed as a {@code BigDecimal}.
+ *
+ * @return the total length of the duration in seconds, with a scale of 9, not null
+ */
+ private BigDecimal toSeconds() {
+ return BigDecimal.valueOf(seconds).add(BigDecimal.valueOf(nanos, 9));
+ }
+
+ /**
+ * Creates an instance of {@code Duration} from a number of seconds.
+ *
+ * @param seconds the number of seconds, up to scale 9, positive or negative
+ * @return a {@code Duration}, not null
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ private static Duration create(BigDecimal seconds) {
+ BigInteger nanos = seconds.movePointRight(9).toBigIntegerExact();
+ BigInteger[] divRem = nanos.divideAndRemainder(BI_NANOS_PER_SECOND);
+ if (divRem[0].bitLength() > 63) {
+ throw new ArithmeticException("Exceeds capacity of Duration: " + nanos);
+ }
+ return ofSeconds(divRem[0].longValue(), divRem[1].intValue());
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this duration with the length negated.
+ * <p>
+ * This method swaps the sign of the total length of this duration.
+ * For example, {@code PT1.3S} will be returned as {@code PT-1.3S}.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @return a {@code Duration} based on this duration with the amount negated, not null
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public Duration negated() {
+ return multipliedBy(-1);
+ }
+
+ /**
+ * Returns a copy of this duration with a positive length.
+ * <p>
+ * This method returns a positive duration by effectively removing the sign from any negative total length.
+ * For example, {@code PT-1.3S} will be returned as {@code PT1.3S}.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @return a {@code Duration} based on this duration with an absolute length, not null
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public Duration abs() {
+ return isNegative() ? negated() : this;
+ }
+
+ //-------------------------------------------------------------------------
+ /**
+ * Adds this duration to the specified temporal object.
+ * <p>
+ * This returns a temporal object of the same observable type as the input
+ * with this duration 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 = thisDuration.addTo(dateTime);
+ * dateTime = dateTime.plus(thisDuration);
+ * </pre>
+ * <p>
+ * The calculation will add the seconds, then nanos.
+ * Only non-zero amounts will be 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
+ public Temporal addTo(Temporal temporal) {
+ if (seconds != 0) {
+ temporal = temporal.plus(seconds, SECONDS);
+ }
+ if (nanos != 0) {
+ temporal = temporal.plus(nanos, NANOS);
+ }
+ return temporal;
+ }
+
+ /**
+ * Subtracts this duration from the specified temporal object.
+ * <p>
+ * This returns a temporal object of the same observable type as the input
+ * with this duration 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 = thisDuration.subtractFrom(dateTime);
+ * dateTime = dateTime.minus(thisDuration);
+ * </pre>
+ * <p>
+ * The calculation will subtract the seconds, then nanos.
+ * Only non-zero amounts will be 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 subtract
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public Temporal subtractFrom(Temporal temporal) {
+ if (seconds != 0) {
+ temporal = temporal.minus(seconds, SECONDS);
+ }
+ if (nanos != 0) {
+ temporal = temporal.minus(nanos, NANOS);
+ }
+ return temporal;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the number of days in this duration.
+ * <p>
+ * This returns the total number of days in the duration by dividing the
+ * number of seconds by 86400.
+ * This is based on the standard definition of a day as 24 hours.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @return the number of days in the duration, may be negative
+ */
+ public long toDays() {
+ return seconds / SECONDS_PER_DAY;
+ }
+
+ /**
+ * Gets the number of hours in this duration.
+ * <p>
+ * This returns the total number of hours in the duration by dividing the
+ * number of seconds by 3600.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @return the number of hours in the duration, may be negative
+ */
+ public long toHours() {
+ return seconds / SECONDS_PER_HOUR;
+ }
+
+ /**
+ * Gets the number of minutes in this duration.
+ * <p>
+ * This returns the total number of minutes in the duration by dividing the
+ * number of seconds by 60.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @return the number of minutes in the duration, may be negative
+ */
+ public long toMinutes() {
+ return seconds / SECONDS_PER_MINUTE;
+ }
+
+ /**
+ * Converts this duration to the total length in milliseconds.
+ * <p>
+ * If this duration is too large to fit in a {@code long} milliseconds, then an
+ * exception is thrown.
+ * <p>
+ * If this duration has greater than millisecond precision, then the conversion
+ * will drop any excess precision information as though the amount in nanoseconds
+ * was subject to integer division by one million.
+ *
+ * @return the total length of the duration in milliseconds
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public long toMillis() {
+ long millis = Math.multiplyExact(seconds, 1000);
+ millis = Math.addExact(millis, nanos / 1000_000);
+ return millis;
+ }
+
+ /**
+ * Converts this duration to the total length in nanoseconds expressed as a {@code long}.
+ * <p>
+ * If this duration is too large to fit in a {@code long} nanoseconds, then an
+ * exception is thrown.
+ *
+ * @return the total length of the duration in nanoseconds
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public long toNanos() {
+ long totalNanos = Math.multiplyExact(seconds, NANOS_PER_SECOND);
+ totalNanos = Math.addExact(totalNanos, nanos);
+ return totalNanos;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Compares this duration to the specified {@code Duration}.
+ * <p>
+ * The comparison is based on the total length of the durations.
+ * It is "consistent with equals", as defined by {@link Comparable}.
+ *
+ * @param otherDuration the other duration to compare to, not null
+ * @return the comparator value, negative if less, positive if greater
+ */
+ @Override
+ public int compareTo(Duration otherDuration) {
+ int cmp = Long.compare(seconds, otherDuration.seconds);
+ if (cmp != 0) {
+ return cmp;
+ }
+ return nanos - otherDuration.nanos;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if this duration is equal to the specified {@code Duration}.
+ * <p>
+ * The comparison is based on the total length of the durations.
+ *
+ * @param otherDuration the other duration, null returns false
+ * @return true if the other duration is equal to this one
+ */
+ @Override
+ public boolean equals(Object otherDuration) {
+ if (this == otherDuration) {
+ return true;
+ }
+ if (otherDuration instanceof Duration) {
+ Duration other = (Duration) otherDuration;
+ return this.seconds == other.seconds &&
+ this.nanos == other.nanos;
+ }
+ return false;
+ }
+
+ /**
+ * A hash code for this duration.
+ *
+ * @return a suitable hash code
+ */
+ @Override
+ public int hashCode() {
+ return ((int) (seconds ^ (seconds >>> 32))) + (51 * nanos);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * A string representation of this duration using ISO-8601 seconds
+ * based representation, such as {@code PT8H6M12.345S}.
+ * <p>
+ * The format of the returned string will be {@code PTnHnMnS}, where n is
+ * the relevant hours, minutes or seconds part of the duration.
+ * Any fractional seconds are placed after a decimal point i the seconds section.
+ * If a section has a zero value, it is omitted.
+ * The hours, minutes and seconds will all have the same sign.
+ * <p>
+ * Examples:
+ * <pre>
+ * "20.345 seconds" -- "PT20.345S
+ * "15 minutes" (15 * 60 seconds) -- "PT15M"
+ * "10 hours" (10 * 3600 seconds) -- "PT10H"
+ * "2 days" (2 * 86400 seconds) -- "PT48H"
+ * </pre>
+ * Note that multiples of 24 hours are not output as days to avoid confusion
+ * with {@code Period}.
+ *
+ * @return an ISO-8601 representation of this duration, not null
+ */
+ @Override
+ public String toString() {
+ if (this == ZERO) {
+ return "PT0S";
+ }
+ long hours = seconds / SECONDS_PER_HOUR;
+ int minutes = (int) ((seconds % SECONDS_PER_HOUR) / SECONDS_PER_MINUTE);
+ int secs = (int) (seconds % SECONDS_PER_MINUTE);
+ StringBuilder buf = new StringBuilder(24);
+ buf.append("PT");
+ if (hours != 0) {
+ buf.append(hours).append('H');
+ }
+ if (minutes != 0) {
+ buf.append(minutes).append('M');
+ }
+ if (secs == 0 && nanos == 0 && buf.length() > 2) {
+ return buf.toString();
+ }
+ if (secs < 0 && nanos > 0) {
+ if (secs == -1) {
+ buf.append("-0");
+ } else {
+ buf.append(secs + 1);
+ }
+ } else {
+ buf.append(secs);
+ }
+ if (nanos > 0) {
+ int pos = buf.length();
+ if (secs < 0) {
+ buf.append(2 * NANOS_PER_SECOND - nanos);
+ } else {
+ buf.append(nanos + NANOS_PER_SECOND);
+ }
+ while (buf.charAt(buf.length() - 1) == '0') {
+ buf.setLength(buf.length() - 1);
+ }
+ buf.setCharAt(pos, '.');
+ }
+ buf.append('S');
+ return buf.toString();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Writes the object using a
+ * <a href="../../serialized-form.html#java.time.Ser">dedicated serialized form</a>.
+ * @serialData
+ * <pre>
+ * out.writeByte(1); // identifies a Duration
+ * out.writeLong(seconds);
+ * out.writeInt(nanos);
+ * </pre>
+ *
+ * @return the instance of {@code Ser}, not null
+ */
+ private Object writeReplace() {
+ return new Ser(Ser.DURATION_TYPE, this);
+ }
+
+ /**
+ * Defend against malicious streams.
+ *
+ * @param s the stream to read
+ * @throws InvalidObjectException always
+ */
+ private void readObject(ObjectInputStream s) throws InvalidObjectException {
+ throw new InvalidObjectException("Deserialization via serialization delegate");
+ }
+
+ void writeExternal(DataOutput out) throws IOException {
+ out.writeLong(seconds);
+ out.writeInt(nanos);
+ }
+
+ static Duration readExternal(DataInput in) throws IOException {
+ long seconds = in.readLong();
+ int nanos = in.readInt();
+ return Duration.ofSeconds(seconds, nanos);
+ }
+
+}
diff --git a/java/time/Instant.java b/java/time/Instant.java
new file mode 100644
index 0000000..c87ac18
--- /dev/null
+++ b/java/time/Instant.java
@@ -0,0 +1,1365 @@
+/*
+ * Copyright (c) 2012, 2015, 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) 2007-2012, 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;
+
+import static java.time.LocalTime.NANOS_PER_SECOND;
+import static java.time.LocalTime.SECONDS_PER_DAY;
+import static java.time.LocalTime.SECONDS_PER_HOUR;
+import static java.time.LocalTime.SECONDS_PER_MINUTE;
+import static java.time.temporal.ChronoField.INSTANT_SECONDS;
+import static java.time.temporal.ChronoField.MICRO_OF_SECOND;
+import static java.time.temporal.ChronoField.MILLI_OF_SECOND;
+import static java.time.temporal.ChronoField.NANO_OF_SECOND;
+import static java.time.temporal.ChronoUnit.DAYS;
+import static java.time.temporal.ChronoUnit.NANOS;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.time.temporal.ChronoField;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.Temporal;
+import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalAdjuster;
+import java.time.temporal.TemporalAmount;
+import java.time.temporal.TemporalField;
+import java.time.temporal.TemporalQueries;
+import java.time.temporal.TemporalQuery;
+import java.time.temporal.TemporalUnit;
+import java.time.temporal.UnsupportedTemporalTypeException;
+import java.time.temporal.ValueRange;
+import java.util.Objects;
+
+// Android-changed: removed ValueBased paragraph.
+/**
+ * An instantaneous point on the time-line.
+ * <p>
+ * This class models a single instantaneous point on the time-line.
+ * This might be used to record event time-stamps in the application.
+ * <p>
+ * The range of an instant requires the storage of a number larger than a {@code long}.
+ * To achieve this, the class stores a {@code long} representing epoch-seconds and an
+ * {@code int} representing nanosecond-of-second, which will always be between 0 and 999,999,999.
+ * The epoch-seconds are measured from the standard Java epoch of {@code 1970-01-01T00:00:00Z}
+ * where instants after the epoch have positive values, and earlier instants have negative values.
+ * For both the epoch-second and nanosecond parts, a larger value is always later on the time-line
+ * than a smaller value.
+ *
+ * <h3>Time-scale</h3>
+ * <p>
+ * The length of the solar day is the standard way that humans measure time.
+ * This has traditionally been subdivided into 24 hours of 60 minutes of 60 seconds,
+ * forming a 86400 second day.
+ * <p>
+ * Modern timekeeping is based on atomic clocks which precisely define an SI second
+ * relative to the transitions of a Caesium atom. The length of an SI second was defined
+ * to be very close to the 86400th fraction of a day.
+ * <p>
+ * Unfortunately, as the Earth rotates the length of the day varies.
+ * In addition, over time the average length of the day is getting longer as the Earth slows.
+ * As a result, the length of a solar day in 2012 is slightly longer than 86400 SI seconds.
+ * The actual length of any given day and the amount by which the Earth is slowing
+ * are not predictable and can only be determined by measurement.
+ * The UT1 time-scale captures the accurate length of day, but is only available some
+ * time after the day has completed.
+ * <p>
+ * The UTC time-scale is a standard approach to bundle up all the additional fractions
+ * of a second from UT1 into whole seconds, known as <i>leap-seconds</i>.
+ * A leap-second may be added or removed depending on the Earth's rotational changes.
+ * As such, UTC permits a day to have 86399 SI seconds or 86401 SI seconds where
+ * necessary in order to keep the day aligned with the Sun.
+ * <p>
+ * The modern UTC time-scale was introduced in 1972, introducing the concept of whole leap-seconds.
+ * Between 1958 and 1972, the definition of UTC was complex, with minor sub-second leaps and
+ * alterations to the length of the notional second. As of 2012, discussions are underway
+ * to change the definition of UTC again, with the potential to remove leap seconds or
+ * introduce other changes.
+ * <p>
+ * Given the complexity of accurate timekeeping described above, this Java API defines
+ * its own time-scale, the <i>Java Time-Scale</i>.
+ * <p>
+ * The Java Time-Scale divides each calendar day into exactly 86400
+ * subdivisions, known as seconds. These seconds may differ from the
+ * SI second. It closely matches the de facto international civil time
+ * scale, the definition of which changes from time to time.
+ * <p>
+ * The Java Time-Scale has slightly different definitions for different
+ * segments of the time-line, each based on the consensus international
+ * time scale that is used as the basis for civil time. Whenever the
+ * internationally-agreed time scale is modified or replaced, a new
+ * segment of the Java Time-Scale must be defined for it. Each segment
+ * must meet these requirements:
+ * <ul>
+ * <li>the Java Time-Scale shall closely match the underlying international
+ * civil time scale;</li>
+ * <li>the Java Time-Scale shall exactly match the international civil
+ * time scale at noon each day;</li>
+ * <li>the Java Time-Scale shall have a precisely-defined relationship to
+ * the international civil time scale.</li>
+ * </ul>
+ * There are currently, as of 2013, two segments in the Java time-scale.
+ * <p>
+ * For the segment from 1972-11-03 (exact boundary discussed below) until
+ * further notice, the consensus international time scale is UTC (with
+ * leap seconds). In this segment, the Java Time-Scale is identical to
+ * <a href="http://www.cl.cam.ac.uk/~mgk25/time/utc-sls/">UTC-SLS</a>.
+ * This is identical to UTC on days that do not have a leap second.
+ * On days that do have a leap second, the leap second is spread equally
+ * over the last 1000 seconds of the day, maintaining the appearance of
+ * exactly 86400 seconds per day.
+ * <p>
+ * For the segment prior to 1972-11-03, extending back arbitrarily far,
+ * the consensus international time scale is defined to be UT1, applied
+ * proleptically, which is equivalent to the (mean) solar time on the
+ * prime meridian (Greenwich). In this segment, the Java Time-Scale is
+ * identical to the consensus international time scale. The exact
+ * boundary between the two segments is the instant where UT1 = UTC
+ * between 1972-11-03T00:00 and 1972-11-04T12:00.
+ * <p>
+ * Implementations of the Java time-scale using the JSR-310 API are not
+ * required to provide any clock that is sub-second accurate, or that
+ * progresses monotonically or smoothly. Implementations are therefore
+ * not required to actually perform the UTC-SLS slew or to otherwise be
+ * aware of leap seconds. JSR-310 does, however, require that
+ * implementations must document the approach they use when defining a
+ * clock representing the current instant.
+ * See {@link Clock} for details on the available clocks.
+ * <p>
+ * The Java time-scale is used for all date-time classes.
+ * This includes {@code Instant}, {@code LocalDate}, {@code LocalTime}, {@code OffsetDateTime},
+ * {@code ZonedDateTime} and {@code Duration}.
+ *
+ * @implSpec
+ * This class is immutable and thread-safe.
+ *
+ * @since 1.8
+ */
+public final class Instant
+ implements Temporal, TemporalAdjuster, Comparable<Instant>, Serializable {
+
+ /**
+ * Constant for the 1970-01-01T00:00:00Z epoch instant.
+ */
+ public static final Instant EPOCH = new Instant(0, 0);
+ /**
+ * The minimum supported epoch second.
+ */
+ private static final long MIN_SECOND = -31557014167219200L;
+ /**
+ * The maximum supported epoch second.
+ */
+ private static final long MAX_SECOND = 31556889864403199L;
+ /**
+ * The minimum supported {@code Instant}, '-1000000000-01-01T00:00Z'.
+ * This could be used by an application as a "far past" instant.
+ * <p>
+ * This is one year earlier than the minimum {@code LocalDateTime}.
+ * This provides sufficient values to handle the range of {@code ZoneOffset}
+ * which affect the instant in addition to the local date-time.
+ * The value is also chosen such that the value of the year fits in
+ * an {@code int}.
+ */
+ public static final Instant MIN = Instant.ofEpochSecond(MIN_SECOND, 0);
+ /**
+ * The maximum supported {@code Instant}, '1000000000-12-31T23:59:59.999999999Z'.
+ * This could be used by an application as a "far future" instant.
+ * <p>
+ * This is one year later than the maximum {@code LocalDateTime}.
+ * This provides sufficient values to handle the range of {@code ZoneOffset}
+ * which affect the instant in addition to the local date-time.
+ * The value is also chosen such that the value of the year fits in
+ * an {@code int}.
+ */
+ public static final Instant MAX = Instant.ofEpochSecond(MAX_SECOND, 999_999_999);
+
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = -665713676816604388L;
+
+ /**
+ * The number of seconds from the epoch of 1970-01-01T00:00:00Z.
+ */
+ private final long seconds;
+ /**
+ * The number of nanoseconds, later along the time-line, from the seconds field.
+ * This is always positive, and never exceeds 999,999,999.
+ */
+ private final int nanos;
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains the current instant from the system clock.
+ * <p>
+ * This will query the {@link Clock#systemUTC() system UTC clock} to
+ * obtain the current instant.
+ * <p>
+ * Using this method will prevent the ability to use an alternate time-source for
+ * testing because the clock is effectively hard-coded.
+ *
+ * @return the current instant using the system clock, not null
+ */
+ public static Instant now() {
+ return Clock.systemUTC().instant();
+ }
+
+ /**
+ * Obtains the current instant from the specified clock.
+ * <p>
+ * This will query the specified clock to obtain the current time.
+ * <p>
+ * Using this method allows the use of an alternate clock for testing.
+ * The alternate clock may be introduced using {@link Clock dependency injection}.
+ *
+ * @param clock the clock to use, not null
+ * @return the current instant, not null
+ */
+ public static Instant now(Clock clock) {
+ Objects.requireNonNull(clock, "clock");
+ return clock.instant();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code Instant} using seconds from the
+ * epoch of 1970-01-01T00:00:00Z.
+ * <p>
+ * The nanosecond field is set to zero.
+ *
+ * @param epochSecond the number of seconds from 1970-01-01T00:00:00Z
+ * @return an instant, not null
+ * @throws DateTimeException if the instant exceeds the maximum or minimum instant
+ */
+ public static Instant ofEpochSecond(long epochSecond) {
+ return create(epochSecond, 0);
+ }
+
+ /**
+ * Obtains an instance of {@code Instant} using seconds from the
+ * epoch of 1970-01-01T00:00:00Z and nanosecond fraction of second.
+ * <p>
+ * This method allows an arbitrary number of nanoseconds to be passed in.
+ * The factory will alter the values of the second and nanosecond in order
+ * to ensure that the stored nanosecond is in the range 0 to 999,999,999.
+ * For example, the following will result in the exactly the same instant:
+ * <pre>
+ * Instant.ofEpochSecond(3, 1);
+ * Instant.ofEpochSecond(4, -999_999_999);
+ * Instant.ofEpochSecond(2, 1000_000_001);
+ * </pre>
+ *
+ * @param epochSecond the number of seconds from 1970-01-01T00:00:00Z
+ * @param nanoAdjustment the nanosecond adjustment to the number of seconds, positive or negative
+ * @return an instant, not null
+ * @throws DateTimeException if the instant exceeds the maximum or minimum instant
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public static Instant ofEpochSecond(long epochSecond, long nanoAdjustment) {
+ long secs = Math.addExact(epochSecond, Math.floorDiv(nanoAdjustment, NANOS_PER_SECOND));
+ int nos = (int)Math.floorMod(nanoAdjustment, NANOS_PER_SECOND);
+ return create(secs, nos);
+ }
+
+ /**
+ * Obtains an instance of {@code Instant} using milliseconds from the
+ * epoch of 1970-01-01T00:00:00Z.
+ * <p>
+ * The seconds and nanoseconds are extracted from the specified milliseconds.
+ *
+ * @param epochMilli the number of milliseconds from 1970-01-01T00:00:00Z
+ * @return an instant, not null
+ * @throws DateTimeException if the instant exceeds the maximum or minimum instant
+ */
+ public static Instant ofEpochMilli(long epochMilli) {
+ long secs = Math.floorDiv(epochMilli, 1000);
+ int mos = (int)Math.floorMod(epochMilli, 1000);
+ return create(secs, mos * 1000_000);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code Instant} from a temporal object.
+ * <p>
+ * This obtains an instant based on the specified temporal.
+ * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
+ * which this factory converts to an instance of {@code Instant}.
+ * <p>
+ * The conversion extracts the {@link ChronoField#INSTANT_SECONDS INSTANT_SECONDS}
+ * and {@link ChronoField#NANO_OF_SECOND NANO_OF_SECOND} fields.
+ * <p>
+ * This method matches the signature of the functional interface {@link TemporalQuery}
+ * allowing it to be used as a query via method reference, {@code Instant::from}.
+ *
+ * @param temporal the temporal object to convert, not null
+ * @return the instant, not null
+ * @throws DateTimeException if unable to convert to an {@code Instant}
+ */
+ public static Instant from(TemporalAccessor temporal) {
+ if (temporal instanceof Instant) {
+ return (Instant) temporal;
+ }
+ Objects.requireNonNull(temporal, "temporal");
+ try {
+ long instantSecs = temporal.getLong(INSTANT_SECONDS);
+ int nanoOfSecond = temporal.get(NANO_OF_SECOND);
+ return Instant.ofEpochSecond(instantSecs, nanoOfSecond);
+ } catch (DateTimeException ex) {
+ throw new DateTimeException("Unable to obtain Instant from TemporalAccessor: " +
+ temporal + " of type " + temporal.getClass().getName(), ex);
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code Instant} from a text string such as
+ * {@code 2007-12-03T10:15:30.00Z}.
+ * <p>
+ * The string must represent a valid instant in UTC and is parsed using
+ * {@link DateTimeFormatter#ISO_INSTANT}.
+ *
+ * @param text the text to parse, not null
+ * @return the parsed instant, not null
+ * @throws DateTimeParseException if the text cannot be parsed
+ */
+ public static Instant parse(final CharSequence text) {
+ return DateTimeFormatter.ISO_INSTANT.parse(text, Instant::from);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code Instant} using seconds and nanoseconds.
+ *
+ * @param seconds the length of the duration in seconds
+ * @param nanoOfSecond the nano-of-second, from 0 to 999,999,999
+ * @throws DateTimeException if the instant exceeds the maximum or minimum instant
+ */
+ private static Instant create(long seconds, int nanoOfSecond) {
+ if ((seconds | nanoOfSecond) == 0) {
+ return EPOCH;
+ }
+ if (seconds < MIN_SECOND || seconds > MAX_SECOND) {
+ throw new DateTimeException("Instant exceeds minimum or maximum instant");
+ }
+ return new Instant(seconds, nanoOfSecond);
+ }
+
+ /**
+ * Constructs an instance of {@code Instant} using seconds from the epoch of
+ * 1970-01-01T00:00:00Z and nanosecond fraction of second.
+ *
+ * @param epochSecond the number of seconds from 1970-01-01T00:00:00Z
+ * @param nanos the nanoseconds within the second, must be positive
+ */
+ private Instant(long epochSecond, int nanos) {
+ super();
+ this.seconds = epochSecond;
+ this.nanos = nanos;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if the specified field is supported.
+ * <p>
+ * This checks if this instant can be queried for the specified field.
+ * If false, then calling the {@link #range(TemporalField) range},
+ * {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}
+ * methods will throw an exception.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The supported fields are:
+ * <ul>
+ * <li>{@code NANO_OF_SECOND}
+ * <li>{@code MICRO_OF_SECOND}
+ * <li>{@code MILLI_OF_SECOND}
+ * <li>{@code INSTANT_SECONDS}
+ * </ul>
+ * All other {@code ChronoField} instances will return false.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}
+ * passing {@code this} as the argument.
+ * Whether the field is supported is determined by the field.
+ *
+ * @param field the field to check, null returns false
+ * @return true if the field is supported on this instant, false if not
+ */
+ @Override
+ public boolean isSupported(TemporalField field) {
+ if (field instanceof ChronoField) {
+ return field == INSTANT_SECONDS || field == NANO_OF_SECOND || field == MICRO_OF_SECOND || field == MILLI_OF_SECOND;
+ }
+ return field != null && field.isSupportedBy(this);
+ }
+
+ /**
+ * Checks if the specified unit is supported.
+ * <p>
+ * This checks if the specified unit can be added to, or subtracted from, this date-time.
+ * If false, then calling the {@link #plus(long, TemporalUnit)} and
+ * {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
+ * <p>
+ * If the unit is a {@link ChronoUnit} then the query is implemented here.
+ * The supported units are:
+ * <ul>
+ * <li>{@code NANOS}
+ * <li>{@code MICROS}
+ * <li>{@code MILLIS}
+ * <li>{@code SECONDS}
+ * <li>{@code MINUTES}
+ * <li>{@code HOURS}
+ * <li>{@code HALF_DAYS}
+ * <li>{@code DAYS}
+ * </ul>
+ * All other {@code ChronoUnit} instances will return false.
+ * <p>
+ * If the unit is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
+ * passing {@code this} as the argument.
+ * Whether the unit is supported is determined by the unit.
+ *
+ * @param unit the unit to check, null returns false
+ * @return true if the unit can be added/subtracted, false if not
+ */
+ @Override
+ public boolean isSupported(TemporalUnit unit) {
+ if (unit instanceof ChronoUnit) {
+ return unit.isTimeBased() || unit == DAYS;
+ }
+ return unit != null && unit.isSupportedBy(this);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the range of valid values for the specified field.
+ * <p>
+ * The range object expresses the minimum and maximum valid values for a field.
+ * This instant is used to enhance the accuracy of the returned range.
+ * If it is not possible to return the range, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The {@link #isSupported(TemporalField) supported fields} will return
+ * appropriate range instances.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.rangeRefinedBy(TemporalAccessor)}
+ * passing {@code this} as the argument.
+ * Whether the range can be obtained is determined by the field.
+ *
+ * @param field the field to query the range for, not null
+ * @return the range of valid values for the field, not null
+ * @throws DateTimeException if the range for the field cannot be obtained
+ * @throws UnsupportedTemporalTypeException if the field is not supported
+ */
+ @Override // override for Javadoc
+ public ValueRange range(TemporalField field) {
+ return Temporal.super.range(field);
+ }
+
+ /**
+ * Gets the value of the specified field from this instant as an {@code int}.
+ * <p>
+ * This queries this instant for the value of the specified field.
+ * The returned value will always be within the valid range of values for the field.
+ * If it is not possible to return the value, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The {@link #isSupported(TemporalField) supported fields} will return valid
+ * values based on this date-time, except {@code INSTANT_SECONDS} which is too
+ * large to fit in an {@code int} and throws a {@code DateTimeException}.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
+ * passing {@code this} as the argument. Whether the value can be obtained,
+ * and what the value represents, is determined by the field.
+ *
+ * @param field the field to get, not null
+ * @return the value for the field
+ * @throws DateTimeException if a value for the field cannot be obtained or
+ * the value is outside the range of valid values for the field
+ * @throws UnsupportedTemporalTypeException if the field is not supported or
+ * the range of values exceeds an {@code int}
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override // override for Javadoc and performance
+ public int get(TemporalField field) {
+ if (field instanceof ChronoField) {
+ switch ((ChronoField) field) {
+ case NANO_OF_SECOND: return nanos;
+ case MICRO_OF_SECOND: return nanos / 1000;
+ case MILLI_OF_SECOND: return nanos / 1000_000;
+ case INSTANT_SECONDS: INSTANT_SECONDS.checkValidIntValue(seconds);
+ }
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
+ }
+ return range(field).checkValidIntValue(field.getFrom(this), field);
+ }
+
+ /**
+ * Gets the value of the specified field from this instant as a {@code long}.
+ * <p>
+ * This queries this instant for the value of the specified field.
+ * If it is not possible to return the value, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The {@link #isSupported(TemporalField) supported fields} will return valid
+ * values based on this date-time.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
+ * passing {@code this} as the argument. Whether the value can be obtained,
+ * and what the value represents, is determined by the field.
+ *
+ * @param field the field to get, not null
+ * @return the value for the field
+ * @throws DateTimeException if a value for the field cannot be obtained
+ * @throws UnsupportedTemporalTypeException if the field is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public long getLong(TemporalField field) {
+ if (field instanceof ChronoField) {
+ switch ((ChronoField) field) {
+ case NANO_OF_SECOND: return nanos;
+ case MICRO_OF_SECOND: return nanos / 1000;
+ case MILLI_OF_SECOND: return nanos / 1000_000;
+ case INSTANT_SECONDS: return seconds;
+ }
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
+ }
+ return field.getFrom(this);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the number of seconds from the Java epoch of 1970-01-01T00:00:00Z.
+ * <p>
+ * The epoch second count is a simple incrementing count of seconds where
+ * second 0 is 1970-01-01T00:00:00Z.
+ * The nanosecond part of the day is returned by {@code getNanosOfSecond}.
+ *
+ * @return the seconds from the epoch of 1970-01-01T00:00:00Z
+ */
+ public long getEpochSecond() {
+ return seconds;
+ }
+
+ /**
+ * Gets the number of nanoseconds, later along the time-line, from the start
+ * of the second.
+ * <p>
+ * The nanosecond-of-second value measures the total number of nanoseconds from
+ * the second returned by {@code getEpochSecond}.
+ *
+ * @return the nanoseconds within the second, always positive, never exceeds 999,999,999
+ */
+ public int getNano() {
+ return nanos;
+ }
+
+ //-------------------------------------------------------------------------
+ /**
+ * Returns an adjusted copy of this instant.
+ * <p>
+ * This returns an {@code Instant}, based on this one, with the instant adjusted.
+ * The adjustment takes place using the specified adjuster strategy object.
+ * Read the documentation of the adjuster to understand what adjustment will be made.
+ * <p>
+ * The result of this method is obtained by invoking the
+ * {@link TemporalAdjuster#adjustInto(Temporal)} method on the
+ * specified adjuster passing {@code this} as the argument.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param adjuster the adjuster to use, not null
+ * @return an {@code Instant} based on {@code this} with the adjustment made, not null
+ * @throws DateTimeException if the adjustment cannot be made
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public Instant with(TemporalAdjuster adjuster) {
+ return (Instant) adjuster.adjustInto(this);
+ }
+
+ /**
+ * Returns a copy of this instant with the specified field set to a new value.
+ * <p>
+ * This returns an {@code Instant}, based on this one, with the value
+ * for the specified field changed.
+ * If it is not possible to set the value, because the field is not supported or for
+ * some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the adjustment is implemented here.
+ * The supported fields behave as follows:
+ * <ul>
+ * <li>{@code NANO_OF_SECOND} -
+ * Returns an {@code Instant} with the specified nano-of-second.
+ * The epoch-second will be unchanged.
+ * <li>{@code MICRO_OF_SECOND} -
+ * Returns an {@code Instant} with the nano-of-second replaced by the specified
+ * micro-of-second multiplied by 1,000. The epoch-second will be unchanged.
+ * <li>{@code MILLI_OF_SECOND} -
+ * Returns an {@code Instant} with the nano-of-second replaced by the specified
+ * milli-of-second multiplied by 1,000,000. The epoch-second will be unchanged.
+ * <li>{@code INSTANT_SECONDS} -
+ * Returns an {@code Instant} with the specified epoch-second.
+ * The nano-of-second will be unchanged.
+ * </ul>
+ * <p>
+ * In all cases, if the new value is outside the valid range of values for the field
+ * then a {@code DateTimeException} will be thrown.
+ * <p>
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.adjustInto(Temporal, long)}
+ * passing {@code this} as the argument. In this case, the field determines
+ * whether and how to adjust the instant.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param field the field to set in the result, not null
+ * @param newValue the new value of the field in the result
+ * @return an {@code Instant} based on {@code this} with the specified field set, not null
+ * @throws DateTimeException if the field cannot be set
+ * @throws UnsupportedTemporalTypeException if the field is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public Instant with(TemporalField field, long newValue) {
+ if (field instanceof ChronoField) {
+ ChronoField f = (ChronoField) field;
+ f.checkValidValue(newValue);
+ switch (f) {
+ case MILLI_OF_SECOND: {
+ int nval = (int) newValue * 1000_000;
+ return (nval != nanos ? create(seconds, nval) : this);
+ }
+ case MICRO_OF_SECOND: {
+ int nval = (int) newValue * 1000;
+ return (nval != nanos ? create(seconds, nval) : this);
+ }
+ case NANO_OF_SECOND: return (newValue != nanos ? create(seconds, (int) newValue) : this);
+ case INSTANT_SECONDS: return (newValue != seconds ? create(newValue, nanos) : this);
+ }
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
+ }
+ return field.adjustInto(this, newValue);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this {@code Instant} truncated to the specified unit.
+ * <p>
+ * Truncating the instant returns a copy of the original with fields
+ * smaller than the specified unit set to zero.
+ * The fields are calculated on the basis of using a UTC offset as seen
+ * in {@code toString}.
+ * For example, truncating with the {@link ChronoUnit#MINUTES MINUTES} unit will
+ * round down to the nearest minute, setting the seconds and nanoseconds to zero.
+ * <p>
+ * The unit must have a {@linkplain TemporalUnit#getDuration() duration}
+ * that divides into the length of a standard day without remainder.
+ * This includes all supplied time units on {@link ChronoUnit} and
+ * {@link ChronoUnit#DAYS DAYS}. Other units throw an exception.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param unit the unit to truncate to, not null
+ * @return an {@code Instant} based on this instant with the time truncated, not null
+ * @throws DateTimeException if the unit is invalid for truncation
+ * @throws UnsupportedTemporalTypeException if the unit is not supported
+ */
+ public Instant truncatedTo(TemporalUnit unit) {
+ if (unit == ChronoUnit.NANOS) {
+ return this;
+ }
+ Duration unitDur = unit.getDuration();
+ if (unitDur.getSeconds() > LocalTime.SECONDS_PER_DAY) {
+ throw new UnsupportedTemporalTypeException("Unit is too large to be used for truncation");
+ }
+ long dur = unitDur.toNanos();
+ if ((LocalTime.NANOS_PER_DAY % dur) != 0) {
+ throw new UnsupportedTemporalTypeException("Unit must divide into a standard day without remainder");
+ }
+ long nod = (seconds % LocalTime.SECONDS_PER_DAY) * LocalTime.NANOS_PER_SECOND + nanos;
+ long result = (nod / dur) * dur;
+ return plusNanos(result - nod);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this instant with the specified amount added.
+ * <p>
+ * This returns an {@code Instant}, based on this one, with the specified amount added.
+ * The amount is typically {@link Duration} but may be any other type implementing
+ * the {@link TemporalAmount} interface.
+ * <p>
+ * The calculation is delegated to the amount object by calling
+ * {@link TemporalAmount#addTo(Temporal)}. The amount implementation is free
+ * to implement the addition in any way it wishes, however it typically
+ * calls back to {@link #plus(long, TemporalUnit)}. Consult the documentation
+ * of the amount implementation to determine if it can be successfully added.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param amountToAdd the amount to add, not null
+ * @return an {@code Instant} based on this instant with the addition made, not null
+ * @throws DateTimeException if the addition cannot be made
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public Instant plus(TemporalAmount amountToAdd) {
+ return (Instant) amountToAdd.addTo(this);
+ }
+
+ /**
+ * Returns a copy of this instant with the specified amount added.
+ * <p>
+ * This returns an {@code Instant}, based on this one, with the amount
+ * in terms of the unit added. If it is not possible to add the amount, because the
+ * unit is not supported or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoUnit} then the addition is implemented here.
+ * The supported fields behave as follows:
+ * <ul>
+ * <li>{@code NANOS} -
+ * Returns a {@code Instant} with the specified number of nanoseconds added.
+ * This is equivalent to {@link #plusNanos(long)}.
+ * <li>{@code MICROS} -
+ * Returns a {@code Instant} with the specified number of microseconds added.
+ * This is equivalent to {@link #plusNanos(long)} with the amount
+ * multiplied by 1,000.
+ * <li>{@code MILLIS} -
+ * Returns a {@code Instant} with the specified number of milliseconds added.
+ * This is equivalent to {@link #plusNanos(long)} with the amount
+ * multiplied by 1,000,000.
+ * <li>{@code SECONDS} -
+ * Returns a {@code Instant} with the specified number of seconds added.
+ * This is equivalent to {@link #plusSeconds(long)}.
+ * <li>{@code MINUTES} -
+ * Returns a {@code Instant} with the specified number of minutes added.
+ * This is equivalent to {@link #plusSeconds(long)} with the amount
+ * multiplied by 60.
+ * <li>{@code HOURS} -
+ * Returns a {@code Instant} with the specified number of hours added.
+ * This is equivalent to {@link #plusSeconds(long)} with the amount
+ * multiplied by 3,600.
+ * <li>{@code HALF_DAYS} -
+ * Returns a {@code Instant} with the specified number of half-days added.
+ * This is equivalent to {@link #plusSeconds(long)} with the amount
+ * multiplied by 43,200 (12 hours).
+ * <li>{@code DAYS} -
+ * Returns a {@code Instant} with the specified number of days added.
+ * This is equivalent to {@link #plusSeconds(long)} with the amount
+ * multiplied by 86,400 (24 hours).
+ * </ul>
+ * <p>
+ * All other {@code ChronoUnit} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.addTo(Temporal, long)}
+ * passing {@code this} as the argument. In this case, the unit determines
+ * whether and how to perform the addition.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param amountToAdd the amount of the unit to add to the result, may be negative
+ * @param unit the unit of the amount to add, not null
+ * @return an {@code Instant} based on this instant with the specified amount added, not null
+ * @throws DateTimeException if the addition cannot be made
+ * @throws UnsupportedTemporalTypeException if the unit is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public Instant plus(long amountToAdd, TemporalUnit unit) {
+ if (unit instanceof ChronoUnit) {
+ switch ((ChronoUnit) unit) {
+ case NANOS: return plusNanos(amountToAdd);
+ case MICROS: return plus(amountToAdd / 1000_000, (amountToAdd % 1000_000) * 1000);
+ case MILLIS: return plusMillis(amountToAdd);
+ case SECONDS: return plusSeconds(amountToAdd);
+ case MINUTES: return plusSeconds(Math.multiplyExact(amountToAdd, SECONDS_PER_MINUTE));
+ case HOURS: return plusSeconds(Math.multiplyExact(amountToAdd, SECONDS_PER_HOUR));
+ case HALF_DAYS: return plusSeconds(Math.multiplyExact(amountToAdd, SECONDS_PER_DAY / 2));
+ case DAYS: return plusSeconds(Math.multiplyExact(amountToAdd, SECONDS_PER_DAY));
+ }
+ throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
+ }
+ return unit.addTo(this, amountToAdd);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this instant with the specified duration in seconds added.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param secondsToAdd the seconds to add, positive or negative
+ * @return an {@code Instant} based on this instant with the specified seconds added, not null
+ * @throws DateTimeException if the result exceeds the maximum or minimum instant
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public Instant plusSeconds(long secondsToAdd) {
+ return plus(secondsToAdd, 0);
+ }
+
+ /**
+ * Returns a copy of this instant with the specified duration in milliseconds added.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param millisToAdd the milliseconds to add, positive or negative
+ * @return an {@code Instant} based on this instant with the specified milliseconds added, not null
+ * @throws DateTimeException if the result exceeds the maximum or minimum instant
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public Instant plusMillis(long millisToAdd) {
+ return plus(millisToAdd / 1000, (millisToAdd % 1000) * 1000_000);
+ }
+
+ /**
+ * Returns a copy of this instant with the specified duration in nanoseconds added.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param nanosToAdd the nanoseconds to add, positive or negative
+ * @return an {@code Instant} based on this instant with the specified nanoseconds added, not null
+ * @throws DateTimeException if the result exceeds the maximum or minimum instant
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public Instant plusNanos(long nanosToAdd) {
+ return plus(0, nanosToAdd);
+ }
+
+ /**
+ * Returns a copy of this instant with the specified duration added.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param secondsToAdd the seconds to add, positive or negative
+ * @param nanosToAdd the nanos to add, positive or negative
+ * @return an {@code Instant} based on this instant with the specified seconds added, not null
+ * @throws DateTimeException if the result exceeds the maximum or minimum instant
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ private Instant plus(long secondsToAdd, long nanosToAdd) {
+ if ((secondsToAdd | nanosToAdd) == 0) {
+ return this;
+ }
+ long epochSec = Math.addExact(seconds, secondsToAdd);
+ epochSec = Math.addExact(epochSec, nanosToAdd / NANOS_PER_SECOND);
+ nanosToAdd = nanosToAdd % NANOS_PER_SECOND;
+ long nanoAdjustment = nanos + nanosToAdd; // safe int+NANOS_PER_SECOND
+ return ofEpochSecond(epochSec, nanoAdjustment);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this instant with the specified amount subtracted.
+ * <p>
+ * This returns an {@code Instant}, based on this one, with the specified amount subtracted.
+ * The amount is typically {@link Duration} but may be any other type implementing
+ * the {@link TemporalAmount} interface.
+ * <p>
+ * The calculation is delegated to the amount object by calling
+ * {@link TemporalAmount#subtractFrom(Temporal)}. The amount implementation is free
+ * to implement the subtraction in any way it wishes, however it typically
+ * calls back to {@link #minus(long, TemporalUnit)}. Consult the documentation
+ * of the amount implementation to determine if it can be successfully subtracted.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param amountToSubtract the amount to subtract, not null
+ * @return an {@code Instant} based on this instant with the subtraction made, not null
+ * @throws DateTimeException if the subtraction cannot be made
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public Instant minus(TemporalAmount amountToSubtract) {
+ return (Instant) amountToSubtract.subtractFrom(this);
+ }
+
+ /**
+ * Returns a copy of this instant with the specified amount subtracted.
+ * <p>
+ * This returns a {@code Instant}, based on this one, with the amount
+ * in terms of the unit subtracted. If it is not possible to subtract the amount,
+ * because the unit is not supported or for some other reason, an exception is thrown.
+ * <p>
+ * This method is equivalent to {@link #plus(long, TemporalUnit)} with the amount negated.
+ * See that method for a full description of how addition, and thus subtraction, works.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param amountToSubtract the amount of the unit to subtract from the result, may be negative
+ * @param unit the unit of the amount to subtract, not null
+ * @return an {@code Instant} based on this instant with the specified amount subtracted, not null
+ * @throws DateTimeException if the subtraction cannot be made
+ * @throws UnsupportedTemporalTypeException if the unit is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public Instant minus(long amountToSubtract, TemporalUnit unit) {
+ return (amountToSubtract == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this instant with the specified duration in seconds subtracted.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param secondsToSubtract the seconds to subtract, positive or negative
+ * @return an {@code Instant} based on this instant with the specified seconds subtracted, not null
+ * @throws DateTimeException if the result exceeds the maximum or minimum instant
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public Instant minusSeconds(long secondsToSubtract) {
+ if (secondsToSubtract == Long.MIN_VALUE) {
+ return plusSeconds(Long.MAX_VALUE).plusSeconds(1);
+ }
+ return plusSeconds(-secondsToSubtract);
+ }
+
+ /**
+ * Returns a copy of this instant with the specified duration in milliseconds subtracted.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param millisToSubtract the milliseconds to subtract, positive or negative
+ * @return an {@code Instant} based on this instant with the specified milliseconds subtracted, not null
+ * @throws DateTimeException if the result exceeds the maximum or minimum instant
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public Instant minusMillis(long millisToSubtract) {
+ if (millisToSubtract == Long.MIN_VALUE) {
+ return plusMillis(Long.MAX_VALUE).plusMillis(1);
+ }
+ return plusMillis(-millisToSubtract);
+ }
+
+ /**
+ * Returns a copy of this instant with the specified duration in nanoseconds subtracted.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param nanosToSubtract the nanoseconds to subtract, positive or negative
+ * @return an {@code Instant} based on this instant with the specified nanoseconds subtracted, not null
+ * @throws DateTimeException if the result exceeds the maximum or minimum instant
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public Instant minusNanos(long nanosToSubtract) {
+ if (nanosToSubtract == Long.MIN_VALUE) {
+ return plusNanos(Long.MAX_VALUE).plusNanos(1);
+ }
+ return plusNanos(-nanosToSubtract);
+ }
+
+ //-------------------------------------------------------------------------
+ /**
+ * Queries this instant using the specified query.
+ * <p>
+ * This queries this instant using the specified query strategy object.
+ * The {@code TemporalQuery} object defines the logic to be used to
+ * obtain the result. Read the documentation of the query to understand
+ * what the result of this method will be.
+ * <p>
+ * The result of this method is obtained by invoking the
+ * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the
+ * specified query passing {@code this} as the argument.
+ *
+ * @param <R> the type of the result
+ * @param query the query to invoke, not null
+ * @return the query result, null may be returned (defined by the query)
+ * @throws DateTimeException if unable to query (defined by the query)
+ * @throws ArithmeticException if numeric overflow occurs (defined by the query)
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public <R> R query(TemporalQuery<R> query) {
+ if (query == TemporalQueries.precision()) {
+ return (R) NANOS;
+ }
+ // inline TemporalAccessor.super.query(query) as an optimization
+ if (query == TemporalQueries.chronology() || query == TemporalQueries.zoneId() ||
+ query == TemporalQueries.zone() || query == TemporalQueries.offset() ||
+ query == TemporalQueries.localDate() || query == TemporalQueries.localTime()) {
+ return null;
+ }
+ return query.queryFrom(this);
+ }
+
+ /**
+ * Adjusts the specified temporal object to have this instant.
+ * <p>
+ * This returns a temporal object of the same observable type as the input
+ * with the instant changed to be the same as this.
+ * <p>
+ * The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)}
+ * twice, passing {@link ChronoField#INSTANT_SECONDS} and
+ * {@link ChronoField#NANO_OF_SECOND} as the fields.
+ * <p>
+ * In most cases, it is clearer to reverse the calling pattern by using
+ * {@link Temporal#with(TemporalAdjuster)}:
+ * <pre>
+ * // these two lines are equivalent, but the second approach is recommended
+ * temporal = thisInstant.adjustInto(temporal);
+ * temporal = temporal.with(thisInstant);
+ * </pre>
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param temporal the target object to be adjusted, not null
+ * @return the adjusted object, not null
+ * @throws DateTimeException if unable to make the adjustment
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public Temporal adjustInto(Temporal temporal) {
+ return temporal.with(INSTANT_SECONDS, seconds).with(NANO_OF_SECOND, nanos);
+ }
+
+ /**
+ * Calculates the amount of time until another instant in terms of the specified unit.
+ * <p>
+ * This calculates the amount of time between two {@code Instant}
+ * objects in terms of a single {@code TemporalUnit}.
+ * The start and end points are {@code this} and the specified instant.
+ * The result will be negative if the end is before the start.
+ * The calculation returns a whole number, representing the number of
+ * complete units between the two instants.
+ * The {@code Temporal} passed to this method is converted to a
+ * {@code Instant} using {@link #from(TemporalAccessor)}.
+ * For example, the amount in days between two dates can be calculated
+ * using {@code startInstant.until(endInstant, SECONDS)}.
+ * <p>
+ * There are two equivalent ways of using this method.
+ * The first is to invoke this method.
+ * The second is to use {@link TemporalUnit#between(Temporal, Temporal)}:
+ * <pre>
+ * // these two lines are equivalent
+ * amount = start.until(end, SECONDS);
+ * amount = SECONDS.between(start, end);
+ * </pre>
+ * The choice should be made based on which makes the code more readable.
+ * <p>
+ * The calculation is implemented in this method for {@link ChronoUnit}.
+ * The units {@code NANOS}, {@code MICROS}, {@code MILLIS}, {@code SECONDS},
+ * {@code MINUTES}, {@code HOURS}, {@code HALF_DAYS} and {@code DAYS}
+ * are supported. Other {@code ChronoUnit} values will throw an exception.
+ * <p>
+ * If the unit is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.between(Temporal, Temporal)}
+ * passing {@code this} as the first argument and the converted input temporal
+ * as the second argument.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param endExclusive the end date, exclusive, which is converted to an {@code Instant}, not null
+ * @param unit the unit to measure the amount in, not null
+ * @return the amount of time between this instant and the end instant
+ * @throws DateTimeException if the amount cannot be calculated, or the end
+ * temporal cannot be converted to an {@code Instant}
+ * @throws UnsupportedTemporalTypeException if the unit is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public long until(Temporal endExclusive, TemporalUnit unit) {
+ Instant end = Instant.from(endExclusive);
+ if (unit instanceof ChronoUnit) {
+ ChronoUnit f = (ChronoUnit) unit;
+ switch (f) {
+ case NANOS: return nanosUntil(end);
+ case MICROS: return nanosUntil(end) / 1000;
+ case MILLIS: return Math.subtractExact(end.toEpochMilli(), toEpochMilli());
+ case SECONDS: return secondsUntil(end);
+ case MINUTES: return secondsUntil(end) / SECONDS_PER_MINUTE;
+ case HOURS: return secondsUntil(end) / SECONDS_PER_HOUR;
+ case HALF_DAYS: return secondsUntil(end) / (12 * SECONDS_PER_HOUR);
+ case DAYS: return secondsUntil(end) / (SECONDS_PER_DAY);
+ }
+ throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
+ }
+ return unit.between(this, end);
+ }
+
+ private long nanosUntil(Instant end) {
+ long secsDiff = Math.subtractExact(end.seconds, seconds);
+ long totalNanos = Math.multiplyExact(secsDiff, NANOS_PER_SECOND);
+ return Math.addExact(totalNanos, end.nanos - nanos);
+ }
+
+ private long secondsUntil(Instant end) {
+ long secsDiff = Math.subtractExact(end.seconds, seconds);
+ long nanosDiff = end.nanos - nanos;
+ if (secsDiff > 0 && nanosDiff < 0) {
+ secsDiff--;
+ } else if (secsDiff < 0 && nanosDiff > 0) {
+ secsDiff++;
+ }
+ return secsDiff;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Combines this instant with an offset to create an {@code OffsetDateTime}.
+ * <p>
+ * This returns an {@code OffsetDateTime} formed from this instant at the
+ * specified offset from UTC/Greenwich. An exception will be thrown if the
+ * instant is too large to fit into an offset date-time.
+ * <p>
+ * This method is equivalent to
+ * {@link OffsetDateTime#ofInstant(Instant, ZoneId) OffsetDateTime.ofInstant(this, offset)}.
+ *
+ * @param offset the offset to combine with, not null
+ * @return the offset date-time formed from this instant and the specified offset, not null
+ * @throws DateTimeException if the result exceeds the supported range
+ */
+ public OffsetDateTime atOffset(ZoneOffset offset) {
+ return OffsetDateTime.ofInstant(this, offset);
+ }
+
+ /**
+ * Combines this instant with a time-zone to create a {@code ZonedDateTime}.
+ * <p>
+ * This returns an {@code ZonedDateTime} formed from this instant at the
+ * specified time-zone. An exception will be thrown if the instant is too
+ * large to fit into a zoned date-time.
+ * <p>
+ * This method is equivalent to
+ * {@link ZonedDateTime#ofInstant(Instant, ZoneId) ZonedDateTime.ofInstant(this, zone)}.
+ *
+ * @param zone the zone to combine with, not null
+ * @return the zoned date-time formed from this instant and the specified zone, not null
+ * @throws DateTimeException if the result exceeds the supported range
+ */
+ public ZonedDateTime atZone(ZoneId zone) {
+ return ZonedDateTime.ofInstant(this, zone);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Converts this instant to the number of milliseconds from the epoch
+ * of 1970-01-01T00:00:00Z.
+ * <p>
+ * If this instant represents a point on the time-line too far in the future
+ * or past to fit in a {@code long} milliseconds, then an exception is thrown.
+ * <p>
+ * If this instant has greater than millisecond precision, then the conversion
+ * will drop any excess precision information as though the amount in nanoseconds
+ * was subject to integer division by one million.
+ *
+ * @return the number of milliseconds since the epoch of 1970-01-01T00:00:00Z
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public long toEpochMilli() {
+ if (seconds < 0 && nanos > 0) {
+ long millis = Math.multiplyExact(seconds+1, 1000);
+ long adjustment = nanos / 1000_000 - 1000;
+ return Math.addExact(millis, adjustment);
+ } else {
+ long millis = Math.multiplyExact(seconds, 1000);
+ return Math.addExact(millis, nanos / 1000_000);
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Compares this instant to the specified instant.
+ * <p>
+ * The comparison is based on the time-line position of the instants.
+ * It is "consistent with equals", as defined by {@link Comparable}.
+ *
+ * @param otherInstant the other instant to compare to, not null
+ * @return the comparator value, negative if less, positive if greater
+ * @throws NullPointerException if otherInstant is null
+ */
+ @Override
+ public int compareTo(Instant otherInstant) {
+ int cmp = Long.compare(seconds, otherInstant.seconds);
+ if (cmp != 0) {
+ return cmp;
+ }
+ return nanos - otherInstant.nanos;
+ }
+
+ /**
+ * Checks if this instant is after the specified instant.
+ * <p>
+ * The comparison is based on the time-line position of the instants.
+ *
+ * @param otherInstant the other instant to compare to, not null
+ * @return true if this instant is after the specified instant
+ * @throws NullPointerException if otherInstant is null
+ */
+ public boolean isAfter(Instant otherInstant) {
+ return compareTo(otherInstant) > 0;
+ }
+
+ /**
+ * Checks if this instant is before the specified instant.
+ * <p>
+ * The comparison is based on the time-line position of the instants.
+ *
+ * @param otherInstant the other instant to compare to, not null
+ * @return true if this instant is before the specified instant
+ * @throws NullPointerException if otherInstant is null
+ */
+ public boolean isBefore(Instant otherInstant) {
+ return compareTo(otherInstant) < 0;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if this instant is equal to the specified instant.
+ * <p>
+ * The comparison is based on the time-line position of the instants.
+ *
+ * @param otherInstant the other instant, null returns false
+ * @return true if the other instant is equal to this one
+ */
+ @Override
+ public boolean equals(Object otherInstant) {
+ if (this == otherInstant) {
+ return true;
+ }
+ if (otherInstant instanceof Instant) {
+ Instant other = (Instant) otherInstant;
+ return this.seconds == other.seconds &&
+ this.nanos == other.nanos;
+ }
+ return false;
+ }
+
+ /**
+ * Returns a hash code for this instant.
+ *
+ * @return a suitable hash code
+ */
+ @Override
+ public int hashCode() {
+ return ((int) (seconds ^ (seconds >>> 32))) + 51 * nanos;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * A string representation of this instant using ISO-8601 representation.
+ * <p>
+ * The format used is the same as {@link DateTimeFormatter#ISO_INSTANT}.
+ *
+ * @return an ISO-8601 representation of this instant, not null
+ */
+ @Override
+ public String toString() {
+ return DateTimeFormatter.ISO_INSTANT.format(this);
+ }
+
+ // -----------------------------------------------------------------------
+ /**
+ * Writes the object using a
+ * <a href="../../serialized-form.html#java.time.Ser">dedicated serialized form</a>.
+ * @serialData
+ * <pre>
+ * out.writeByte(2); // identifies an Instant
+ * out.writeLong(seconds);
+ * out.writeInt(nanos);
+ * </pre>
+ *
+ * @return the instance of {@code Ser}, not null
+ */
+ private Object writeReplace() {
+ return new Ser(Ser.INSTANT_TYPE, this);
+ }
+
+ /**
+ * Defend against malicious streams.
+ *
+ * @param s the stream to read
+ * @throws InvalidObjectException always
+ */
+ private void readObject(ObjectInputStream s) throws InvalidObjectException {
+ throw new InvalidObjectException("Deserialization via serialization delegate");
+ }
+
+ void writeExternal(DataOutput out) throws IOException {
+ out.writeLong(seconds);
+ out.writeInt(nanos);
+ }
+
+ static Instant readExternal(DataInput in) throws IOException {
+ long seconds = in.readLong();
+ int nanos = in.readInt();
+ return Instant.ofEpochSecond(seconds, nanos);
+ }
+
+}
diff --git a/java/time/LocalDate.java b/java/time/LocalDate.java
new file mode 100644
index 0000000..c23b270
--- /dev/null
+++ b/java/time/LocalDate.java
@@ -0,0 +1,2076 @@
+/*
+ * Copyright (c) 2012, 2015, 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) 2007-2012, 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;
+
+import static java.time.LocalTime.SECONDS_PER_DAY;
+import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH;
+import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR;
+import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH;
+import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR;
+import static java.time.temporal.ChronoField.DAY_OF_MONTH;
+import static java.time.temporal.ChronoField.DAY_OF_YEAR;
+import static java.time.temporal.ChronoField.EPOCH_DAY;
+import static java.time.temporal.ChronoField.ERA;
+import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
+import static java.time.temporal.ChronoField.PROLEPTIC_MONTH;
+import static java.time.temporal.ChronoField.YEAR;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.time.chrono.ChronoLocalDate;
+import java.time.chrono.Era;
+import java.time.chrono.IsoChronology;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.time.temporal.ChronoField;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.Temporal;
+import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalAdjuster;
+import java.time.temporal.TemporalAmount;
+import java.time.temporal.TemporalField;
+import java.time.temporal.TemporalQueries;
+import java.time.temporal.TemporalQuery;
+import java.time.temporal.TemporalUnit;
+import java.time.temporal.UnsupportedTemporalTypeException;
+import java.time.temporal.ValueRange;
+import java.time.zone.ZoneOffsetTransition;
+import java.time.zone.ZoneRules;
+import java.util.Objects;
+
+// Android-changed: removed ValueBased paragraph.
+/**
+ * A date without a time-zone in the ISO-8601 calendar system,
+ * such as {@code 2007-12-03}.
+ * <p>
+ * {@code LocalDate} is an immutable date-time object that represents a date,
+ * often viewed as year-month-day. Other date fields, such as day-of-year,
+ * day-of-week and week-of-year, can also be accessed.
+ * For example, the value "2nd October 2007" can be stored in a {@code LocalDate}.
+ * <p>
+ * This class does not store or represent a time or time-zone.
+ * Instead, it is a description of the date, as used for birthdays.
+ * It cannot represent an instant on the time-line without additional information
+ * such as an offset or time-zone.
+ * <p>
+ * 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.
+ * For most applications written today, the ISO-8601 rules are entirely suitable.
+ * However, any application that makes use of historical dates, and requires them
+ * to be accurate will find the ISO-8601 approach unsuitable.
+ *
+ * @implSpec
+ * This class is immutable and thread-safe.
+ *
+ * @since 1.8
+ */
+public final class LocalDate
+ implements Temporal, TemporalAdjuster, ChronoLocalDate, Serializable {
+
+ /**
+ * The minimum supported {@code LocalDate}, '-999999999-01-01'.
+ * This could be used by an application as a "far past" date.
+ */
+ public static final LocalDate MIN = LocalDate.of(Year.MIN_VALUE, 1, 1);
+ /**
+ * The maximum supported {@code LocalDate}, '+999999999-12-31'.
+ * This could be used by an application as a "far future" date.
+ */
+ public static final LocalDate MAX = LocalDate.of(Year.MAX_VALUE, 12, 31);
+
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = 2942565459149668126L;
+ /**
+ * The number of days in a 400 year cycle.
+ */
+ private static final int DAYS_PER_CYCLE = 146097;
+ /**
+ * The number of days from year zero to year 1970.
+ * There are five 400 year cycles from year zero to 2000.
+ * There are 7 leap years from 1970 to 2000.
+ */
+ static final long DAYS_0000_TO_1970 = (DAYS_PER_CYCLE * 5L) - (30L * 365L + 7L);
+
+ /**
+ * The year.
+ */
+ private final int year;
+ /**
+ * The month-of-year.
+ */
+ private final short month;
+ /**
+ * The day-of-month.
+ */
+ private final short day;
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains the current date from the system clock in the default time-zone.
+ * <p>
+ * This will query the {@link Clock#systemDefaultZone() system clock} in the default
+ * time-zone to obtain the current date.
+ * <p>
+ * Using this method will prevent the ability to use an alternate clock for testing
+ * because the clock is hard-coded.
+ *
+ * @return the current date using the system clock and default time-zone, not null
+ */
+ public static LocalDate now() {
+ return now(Clock.systemDefaultZone());
+ }
+
+ /**
+ * Obtains the current date from the system clock in the specified time-zone.
+ * <p>
+ * This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date.
+ * Specifying the time-zone avoids dependence on the default time-zone.
+ * <p>
+ * Using this method will prevent the ability to use an alternate clock for testing
+ * because the clock is hard-coded.
+ *
+ * @param zone the zone ID to use, not null
+ * @return the current date using the system clock, not null
+ */
+ public static LocalDate now(ZoneId zone) {
+ return now(Clock.system(zone));
+ }
+
+ /**
+ * Obtains the current date from the specified clock.
+ * <p>
+ * This will query the specified clock to obtain the current date - today.
+ * Using this method allows the use of an alternate clock for testing.
+ * The alternate clock may be introduced using {@link Clock dependency injection}.
+ *
+ * @param clock the clock to use, not null
+ * @return the current date, not null
+ */
+ public static LocalDate now(Clock clock) {
+ Objects.requireNonNull(clock, "clock");
+ // inline to avoid creating object and Instant checks
+ final Instant now = clock.instant(); // called once
+ ZoneOffset offset = clock.getZone().getRules().getOffset(now);
+ long epochSec = now.getEpochSecond() + offset.getTotalSeconds(); // overflow caught later
+ long epochDay = Math.floorDiv(epochSec, SECONDS_PER_DAY);
+ return LocalDate.ofEpochDay(epochDay);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code LocalDate} from a year, month and day.
+ * <p>
+ * This returns a {@code LocalDate} with the specified year, month and day-of-month.
+ * The day must be valid for the year and month, otherwise an exception will be thrown.
+ *
+ * @param year the year to represent, from MIN_YEAR to MAX_YEAR
+ * @param month the month-of-year to represent, not null
+ * @param dayOfMonth the day-of-month to represent, from 1 to 31
+ * @return the local date, not null
+ * @throws DateTimeException if the value of any field is out of range,
+ * or if the day-of-month is invalid for the month-year
+ */
+ public static LocalDate of(int year, Month month, int dayOfMonth) {
+ YEAR.checkValidValue(year);
+ Objects.requireNonNull(month, "month");
+ DAY_OF_MONTH.checkValidValue(dayOfMonth);
+ return create(year, month.getValue(), dayOfMonth);
+ }
+
+ /**
+ * Obtains an instance of {@code LocalDate} from a year, month and day.
+ * <p>
+ * This returns a {@code LocalDate} with the specified year, month and day-of-month.
+ * The day must be valid for the year and month, otherwise an exception will be thrown.
+ *
+ * @param year the year to represent, from MIN_YEAR to MAX_YEAR
+ * @param month the month-of-year to represent, from 1 (January) to 12 (December)
+ * @param dayOfMonth the day-of-month to represent, from 1 to 31
+ * @return the local date, not null
+ * @throws DateTimeException if the value of any field is out of range,
+ * or if the day-of-month is invalid for the month-year
+ */
+ public static LocalDate of(int year, int month, int dayOfMonth) {
+ YEAR.checkValidValue(year);
+ MONTH_OF_YEAR.checkValidValue(month);
+ DAY_OF_MONTH.checkValidValue(dayOfMonth);
+ return create(year, month, dayOfMonth);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code LocalDate} from a year and day-of-year.
+ * <p>
+ * This returns a {@code LocalDate} with the specified year and day-of-year.
+ * The day-of-year must be valid for the year, otherwise an exception will be thrown.
+ *
+ * @param year the year to represent, from MIN_YEAR to MAX_YEAR
+ * @param dayOfYear the day-of-year to represent, from 1 to 366
+ * @return the local date, not null
+ * @throws DateTimeException if the value of any field is out of range,
+ * or if the day-of-year is invalid for the year
+ */
+ public static LocalDate ofYearDay(int year, int dayOfYear) {
+ YEAR.checkValidValue(year);
+ DAY_OF_YEAR.checkValidValue(dayOfYear);
+ boolean leap = IsoChronology.INSTANCE.isLeapYear(year);
+ if (dayOfYear == 366 && leap == false) {
+ throw new DateTimeException("Invalid date 'DayOfYear 366' as '" + year + "' is not a leap year");
+ }
+ Month moy = Month.of((dayOfYear - 1) / 31 + 1);
+ int monthEnd = moy.firstDayOfYear(leap) + moy.length(leap) - 1;
+ if (dayOfYear > monthEnd) {
+ moy = moy.plus(1);
+ }
+ int dom = dayOfYear - moy.firstDayOfYear(leap) + 1;
+ return new LocalDate(year, moy.getValue(), dom);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code LocalDate} from the epoch day count.
+ * <p>
+ * This returns a {@code LocalDate} with the specified epoch-day.
+ * The {@link ChronoField#EPOCH_DAY EPOCH_DAY} is a simple incrementing count
+ * of days where day 0 is 1970-01-01. Negative numbers represent earlier days.
+ *
+ * @param epochDay the Epoch Day to convert, based on the epoch 1970-01-01
+ * @return the local date, not null
+ * @throws DateTimeException if the epoch day exceeds the supported date range
+ */
+ public static LocalDate ofEpochDay(long epochDay) {
+ long zeroDay = epochDay + DAYS_0000_TO_1970;
+ // find the march-based year
+ zeroDay -= 60; // adjust to 0000-03-01 so leap day is at end of four year cycle
+ long adjust = 0;
+ if (zeroDay < 0) {
+ // adjust negative years to positive for calculation
+ long adjustCycles = (zeroDay + 1) / DAYS_PER_CYCLE - 1;
+ adjust = adjustCycles * 400;
+ zeroDay += -adjustCycles * DAYS_PER_CYCLE;
+ }
+ long yearEst = (400 * zeroDay + 591) / DAYS_PER_CYCLE;
+ long doyEst = zeroDay - (365 * yearEst + yearEst / 4 - yearEst / 100 + yearEst / 400);
+ if (doyEst < 0) {
+ // fix estimate
+ yearEst--;
+ doyEst = zeroDay - (365 * yearEst + yearEst / 4 - yearEst / 100 + yearEst / 400);
+ }
+ yearEst += adjust; // reset any negative year
+ int marchDoy0 = (int) doyEst;
+
+ // convert march-based values back to january-based
+ int marchMonth0 = (marchDoy0 * 5 + 2) / 153;
+ int month = (marchMonth0 + 2) % 12 + 1;
+ int dom = marchDoy0 - (marchMonth0 * 306 + 5) / 10 + 1;
+ yearEst += marchMonth0 / 10;
+
+ // check year now we are certain it is correct
+ int year = YEAR.checkValidIntValue(yearEst);
+ return new LocalDate(year, month, dom);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code LocalDate} from a temporal object.
+ * <p>
+ * This obtains a local date based on the specified temporal.
+ * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
+ * which this factory converts to an instance of {@code LocalDate}.
+ * <p>
+ * The conversion uses the {@link TemporalQueries#localDate()} query, which relies
+ * on extracting the {@link ChronoField#EPOCH_DAY EPOCH_DAY} field.
+ * <p>
+ * This method matches the signature of the functional interface {@link TemporalQuery}
+ * allowing it to be used as a query via method reference, {@code LocalDate::from}.
+ *
+ * @param temporal the temporal object to convert, not null
+ * @return the local date, not null
+ * @throws DateTimeException if unable to convert to a {@code LocalDate}
+ */
+ public static LocalDate from(TemporalAccessor temporal) {
+ Objects.requireNonNull(temporal, "temporal");
+ LocalDate date = temporal.query(TemporalQueries.localDate());
+ if (date == null) {
+ throw new DateTimeException("Unable to obtain LocalDate from TemporalAccessor: " +
+ temporal + " of type " + temporal.getClass().getName());
+ }
+ return date;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code LocalDate} from a text string such as {@code 2007-12-03}.
+ * <p>
+ * The string must represent a valid date and is parsed using
+ * {@link java.time.format.DateTimeFormatter#ISO_LOCAL_DATE}.
+ *
+ * @param text the text to parse such as "2007-12-03", not null
+ * @return the parsed local date, not null
+ * @throws DateTimeParseException if the text cannot be parsed
+ */
+ public static LocalDate parse(CharSequence text) {
+ return parse(text, DateTimeFormatter.ISO_LOCAL_DATE);
+ }
+
+ /**
+ * Obtains an instance of {@code LocalDate} from a text string using a specific formatter.
+ * <p>
+ * The text is parsed using the formatter, returning a date.
+ *
+ * @param text the text to parse, not null
+ * @param formatter the formatter to use, not null
+ * @return the parsed local date, not null
+ * @throws DateTimeParseException if the text cannot be parsed
+ */
+ public static LocalDate parse(CharSequence text, DateTimeFormatter formatter) {
+ Objects.requireNonNull(formatter, "formatter");
+ return formatter.parse(text, LocalDate::from);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Creates a local date from the year, month and day fields.
+ *
+ * @param year the year to represent, validated from MIN_YEAR to MAX_YEAR
+ * @param month the month-of-year to represent, from 1 to 12, validated
+ * @param dayOfMonth the day-of-month to represent, validated from 1 to 31
+ * @return the local date, not null
+ * @throws DateTimeException if the day-of-month is invalid for the month-year
+ */
+ private static LocalDate create(int year, int month, int dayOfMonth) {
+ if (dayOfMonth > 28) {
+ int dom = 31;
+ switch (month) {
+ case 2:
+ dom = (IsoChronology.INSTANCE.isLeapYear(year) ? 29 : 28);
+ break;
+ case 4:
+ case 6:
+ case 9:
+ case 11:
+ dom = 30;
+ break;
+ }
+ if (dayOfMonth > dom) {
+ if (dayOfMonth == 29) {
+ throw new DateTimeException("Invalid date 'February 29' as '" + year + "' is not a leap year");
+ } else {
+ throw new DateTimeException("Invalid date '" + Month.of(month).name() + " " + dayOfMonth + "'");
+ }
+ }
+ }
+ return new LocalDate(year, month, dayOfMonth);
+ }
+
+ /**
+ * Resolves the date, resolving days past the end of month.
+ *
+ * @param year the year to represent, validated from MIN_YEAR to MAX_YEAR
+ * @param month the month-of-year to represent, validated from 1 to 12
+ * @param day the day-of-month to represent, validated from 1 to 31
+ * @return the resolved date, not null
+ */
+ private static LocalDate resolvePreviousValid(int year, int month, int day) {
+ switch (month) {
+ case 2:
+ day = Math.min(day, IsoChronology.INSTANCE.isLeapYear(year) ? 29 : 28);
+ break;
+ case 4:
+ case 6:
+ case 9:
+ case 11:
+ day = Math.min(day, 30);
+ break;
+ }
+ return new LocalDate(year, month, day);
+ }
+
+ /**
+ * Constructor, previously validated.
+ *
+ * @param year the year to represent, from MIN_YEAR to MAX_YEAR
+ * @param month the month-of-year to represent, not null
+ * @param dayOfMonth the day-of-month to represent, valid for year-month, from 1 to 31
+ */
+ private LocalDate(int year, int month, int dayOfMonth) {
+ this.year = year;
+ this.month = (short) month;
+ this.day = (short) dayOfMonth;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if the specified field is supported.
+ * <p>
+ * This checks if this date can be queried for the specified field.
+ * If false, then calling the {@link #range(TemporalField) range},
+ * {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}
+ * methods will throw an exception.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The supported fields are:
+ * <ul>
+ * <li>{@code DAY_OF_WEEK}
+ * <li>{@code ALIGNED_DAY_OF_WEEK_IN_MONTH}
+ * <li>{@code ALIGNED_DAY_OF_WEEK_IN_YEAR}
+ * <li>{@code DAY_OF_MONTH}
+ * <li>{@code DAY_OF_YEAR}
+ * <li>{@code EPOCH_DAY}
+ * <li>{@code ALIGNED_WEEK_OF_MONTH}
+ * <li>{@code ALIGNED_WEEK_OF_YEAR}
+ * <li>{@code MONTH_OF_YEAR}
+ * <li>{@code PROLEPTIC_MONTH}
+ * <li>{@code YEAR_OF_ERA}
+ * <li>{@code YEAR}
+ * <li>{@code ERA}
+ * </ul>
+ * All other {@code ChronoField} instances will return false.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}
+ * passing {@code this} as the argument.
+ * Whether the field is supported is determined by the field.
+ *
+ * @param field the field to check, null returns false
+ * @return true if the field is supported on this date, false if not
+ */
+ @Override // override for Javadoc
+ public boolean isSupported(TemporalField field) {
+ return ChronoLocalDate.super.isSupported(field);
+ }
+
+ /**
+ * Checks if the specified unit is supported.
+ * <p>
+ * This checks if the specified unit can be added to, or subtracted from, this date.
+ * If false, then calling the {@link #plus(long, TemporalUnit)} and
+ * {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
+ * <p>
+ * If the unit is a {@link ChronoUnit} then the query is implemented here.
+ * The supported units are:
+ * <ul>
+ * <li>{@code DAYS}
+ * <li>{@code WEEKS}
+ * <li>{@code MONTHS}
+ * <li>{@code YEARS}
+ * <li>{@code DECADES}
+ * <li>{@code CENTURIES}
+ * <li>{@code MILLENNIA}
+ * <li>{@code ERAS}
+ * </ul>
+ * All other {@code ChronoUnit} instances will return false.
+ * <p>
+ * If the unit is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
+ * passing {@code this} as the argument.
+ * Whether the unit is supported is determined by the unit.
+ *
+ * @param unit the unit to check, null returns false
+ * @return true if the unit can be added/subtracted, false if not
+ */
+ @Override // override for Javadoc
+ public boolean isSupported(TemporalUnit unit) {
+ return ChronoLocalDate.super.isSupported(unit);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the range of valid values for the specified field.
+ * <p>
+ * The range object expresses the minimum and maximum valid values for a field.
+ * This date is used to enhance the accuracy of the returned range.
+ * If it is not possible to return the range, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The {@link #isSupported(TemporalField) supported fields} will return
+ * appropriate range instances.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.rangeRefinedBy(TemporalAccessor)}
+ * passing {@code this} as the argument.
+ * Whether the range can be obtained is determined by the field.
+ *
+ * @param field the field to query the range for, not null
+ * @return the range of valid values for the field, not null
+ * @throws DateTimeException if the range for the field cannot be obtained
+ * @throws UnsupportedTemporalTypeException if the field is not supported
+ */
+ @Override
+ public ValueRange range(TemporalField field) {
+ if (field instanceof ChronoField) {
+ ChronoField f = (ChronoField) field;
+ if (f.isDateBased()) {
+ switch (f) {
+ case DAY_OF_MONTH: return ValueRange.of(1, lengthOfMonth());
+ case DAY_OF_YEAR: return ValueRange.of(1, lengthOfYear());
+ case ALIGNED_WEEK_OF_MONTH: return ValueRange.of(1, getMonth() == Month.FEBRUARY && isLeapYear() == false ? 4 : 5);
+ case YEAR_OF_ERA:
+ return (getYear() <= 0 ? ValueRange.of(1, Year.MAX_VALUE + 1) : ValueRange.of(1, Year.MAX_VALUE));
+ }
+ return field.range();
+ }
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
+ }
+ return field.rangeRefinedBy(this);
+ }
+
+ /**
+ * Gets the value of the specified field from this date as an {@code int}.
+ * <p>
+ * This queries this date for the value of the specified field.
+ * The returned value will always be within the valid range of values for the field.
+ * If it is not possible to return the value, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The {@link #isSupported(TemporalField) supported fields} will return valid
+ * values based on this date, except {@code EPOCH_DAY} and {@code PROLEPTIC_MONTH}
+ * which are too large to fit in an {@code int} and throw a {@code DateTimeException}.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
+ * passing {@code this} as the argument. Whether the value can be obtained,
+ * and what the value represents, is determined by the field.
+ *
+ * @param field the field to get, not null
+ * @return the value for the field
+ * @throws DateTimeException if a value for the field cannot be obtained or
+ * the value is outside the range of valid values for the field
+ * @throws UnsupportedTemporalTypeException if the field is not supported or
+ * the range of values exceeds an {@code int}
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override // override for Javadoc and performance
+ public int get(TemporalField field) {
+ if (field instanceof ChronoField) {
+ return get0(field);
+ }
+ return ChronoLocalDate.super.get(field);
+ }
+
+ /**
+ * Gets the value of the specified field from this date as a {@code long}.
+ * <p>
+ * This queries this date for the value of the specified field.
+ * If it is not possible to return the value, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The {@link #isSupported(TemporalField) supported fields} will return valid
+ * values based on this date.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
+ * passing {@code this} as the argument. Whether the value can be obtained,
+ * and what the value represents, is determined by the field.
+ *
+ * @param field the field to get, not null
+ * @return the value for the field
+ * @throws DateTimeException if a value for the field cannot be obtained
+ * @throws UnsupportedTemporalTypeException if the field is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public long getLong(TemporalField field) {
+ if (field instanceof ChronoField) {
+ if (field == EPOCH_DAY) {
+ return toEpochDay();
+ }
+ if (field == PROLEPTIC_MONTH) {
+ return getProlepticMonth();
+ }
+ return get0(field);
+ }
+ return field.getFrom(this);
+ }
+
+ private int get0(TemporalField field) {
+ switch ((ChronoField) field) {
+ case DAY_OF_WEEK: return getDayOfWeek().getValue();
+ case ALIGNED_DAY_OF_WEEK_IN_MONTH: return ((day - 1) % 7) + 1;
+ case ALIGNED_DAY_OF_WEEK_IN_YEAR: return ((getDayOfYear() - 1) % 7) + 1;
+ case DAY_OF_MONTH: return day;
+ case DAY_OF_YEAR: return getDayOfYear();
+ case EPOCH_DAY: throw new UnsupportedTemporalTypeException("Invalid field 'EpochDay' for get() method, use getLong() instead");
+ case ALIGNED_WEEK_OF_MONTH: return ((day - 1) / 7) + 1;
+ case ALIGNED_WEEK_OF_YEAR: return ((getDayOfYear() - 1) / 7) + 1;
+ case MONTH_OF_YEAR: return month;
+ case PROLEPTIC_MONTH: throw new UnsupportedTemporalTypeException("Invalid field 'ProlepticMonth' for get() method, use getLong() instead");
+ case YEAR_OF_ERA: return (year >= 1 ? year : 1 - year);
+ case YEAR: return year;
+ case ERA: return (year >= 1 ? 1 : 0);
+ }
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
+ }
+
+ private long getProlepticMonth() {
+ return (year * 12L + month - 1);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the chronology of this date, 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;
+ }
+
+ /**
+ * Gets the era applicable at this date.
+ * <p>
+ * The official ISO-8601 standard does not define eras, however {@code IsoChronology} does.
+ * It defines two eras, 'CE' from year one onwards and 'BCE' from year zero backwards.
+ * Since dates before the Julian-Gregorian cutover are not in line with history,
+ * the cutover between 'BCE' and 'CE' is also not aligned with the commonly used
+ * eras, often referred to using 'BC' and 'AD'.
+ * <p>
+ * Users of this class should typically ignore this method as it exists primarily
+ * to fulfill the {@link ChronoLocalDate} contract where it is necessary to support
+ * the Japanese calendar system.
+ * <p>
+ * The returned era will be a singleton capable of being compared with the constants
+ * in {@link IsoChronology} using the {@code ==} operator.
+ *
+ * @return the {@code IsoChronology} era constant applicable at this date, not null
+ */
+ @Override // override for Javadoc
+ public Era getEra() {
+ return ChronoLocalDate.super.getEra();
+ }
+
+ /**
+ * Gets the year field.
+ * <p>
+ * This method returns the primitive {@code int} value for the year.
+ * <p>
+ * The year returned by this method is proleptic as per {@code get(YEAR)}.
+ * To obtain the year-of-era, use {@code get(YEAR_OF_ERA)}.
+ *
+ * @return the year, from MIN_YEAR to MAX_YEAR
+ */
+ public int getYear() {
+ return year;
+ }
+
+ /**
+ * Gets the month-of-year field from 1 to 12.
+ * <p>
+ * This method returns the month as an {@code int} from 1 to 12.
+ * Application code is frequently clearer if the enum {@link Month}
+ * is used by calling {@link #getMonth()}.
+ *
+ * @return the month-of-year, from 1 to 12
+ * @see #getMonth()
+ */
+ public int getMonthValue() {
+ return month;
+ }
+
+ /**
+ * Gets the month-of-year field using the {@code Month} enum.
+ * <p>
+ * This method returns the enum {@link Month} for the month.
+ * This avoids confusion as to what {@code int} values mean.
+ * If you need access to the primitive {@code int} value then the enum
+ * provides the {@link Month#getValue() int value}.
+ *
+ * @return the month-of-year, not null
+ * @see #getMonthValue()
+ */
+ public Month getMonth() {
+ return Month.of(month);
+ }
+
+ /**
+ * Gets the day-of-month field.
+ * <p>
+ * This method returns the primitive {@code int} value for the day-of-month.
+ *
+ * @return the day-of-month, from 1 to 31
+ */
+ public int getDayOfMonth() {
+ return day;
+ }
+
+ /**
+ * Gets the day-of-year field.
+ * <p>
+ * This method returns the primitive {@code int} value for the day-of-year.
+ *
+ * @return the day-of-year, from 1 to 365, or 366 in a leap year
+ */
+ public int getDayOfYear() {
+ return getMonth().firstDayOfYear(isLeapYear()) + day - 1;
+ }
+
+ /**
+ * Gets the day-of-week field, which is an enum {@code DayOfWeek}.
+ * <p>
+ * This method returns the enum {@link DayOfWeek} for the day-of-week.
+ * This avoids confusion as to what {@code int} values mean.
+ * If you need access to the primitive {@code int} value then the enum
+ * provides the {@link DayOfWeek#getValue() int value}.
+ * <p>
+ * Additional information can be obtained from the {@code DayOfWeek}.
+ * This includes textual names of the values.
+ *
+ * @return the day-of-week, not null
+ */
+ public DayOfWeek getDayOfWeek() {
+ int dow0 = (int)Math.floorMod(toEpochDay() + 3, 7);
+ return DayOfWeek.of(dow0 + 1);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if the year is a leap year, according to the ISO proleptic
+ * calendar system rules.
+ * <p>
+ * This method applies the current rules for leap years across the whole time-line.
+ * In general, a year is a leap year if it is divisible by four without
+ * remainder. However, years divisible by 100, are not leap years, with
+ * the exception of years divisible by 400 which are.
+ * <p>
+ * For example, 1904 is a leap year it is divisible by 4.
+ * 1900 was not a leap year as it is divisible by 100, however 2000 was a
+ * leap year as it is divisible by 400.
+ * <p>
+ * The calculation is proleptic - applying the same rules into the far future and far past.
+ * This is historically inaccurate, but is correct for the ISO-8601 standard.
+ *
+ * @return true if the year is leap, false otherwise
+ */
+ @Override // override for Javadoc and performance
+ public boolean isLeapYear() {
+ return IsoChronology.INSTANCE.isLeapYear(year);
+ }
+
+ /**
+ * Returns the length of the month represented by this date.
+ * <p>
+ * This returns the length of the month in days.
+ * For example, a date in January would return 31.
+ *
+ * @return the length of the month in days
+ */
+ @Override
+ public int lengthOfMonth() {
+ switch (month) {
+ case 2:
+ return (isLeapYear() ? 29 : 28);
+ case 4:
+ case 6:
+ case 9:
+ case 11:
+ return 30;
+ default:
+ return 31;
+ }
+ }
+
+ /**
+ * Returns the length of the year represented by this date.
+ * <p>
+ * This returns the length of the year in days, either 365 or 366.
+ *
+ * @return 366 if the year is leap, 365 otherwise
+ */
+ @Override // override for Javadoc and performance
+ public int lengthOfYear() {
+ return (isLeapYear() ? 366 : 365);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns an adjusted copy of this date.
+ * <p>
+ * This returns a {@code LocalDate}, based on this one, with the date adjusted.
+ * The adjustment takes place using the specified adjuster strategy object.
+ * Read the documentation of the adjuster to understand what adjustment will be made.
+ * <p>
+ * A simple adjuster might simply set the one of the fields, such as the year field.
+ * A more complex adjuster might set the date to the last day of the month.
+ * <p>
+ * A selection of common adjustments is provided in
+ * {@link java.time.temporal.TemporalAdjusters TemporalAdjusters}.
+ * These include finding the "last day of the month" and "next Wednesday".
+ * Key date-time classes also implement the {@code TemporalAdjuster} interface,
+ * such as {@link Month} and {@link java.time.MonthDay MonthDay}.
+ * The adjuster is responsible for handling special cases, such as the varying
+ * lengths of month and leap years.
+ * <p>
+ * For example this code returns a date on the last day of July:
+ * <pre>
+ * import static java.time.Month.*;
+ * import static java.time.temporal.TemporalAdjusters.*;
+ *
+ * result = localDate.with(JULY).with(lastDayOfMonth());
+ * </pre>
+ * <p>
+ * The result of this method is obtained by invoking the
+ * {@link TemporalAdjuster#adjustInto(Temporal)} method on the
+ * specified adjuster passing {@code this} as the argument.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param adjuster the adjuster to use, not null
+ * @return a {@code LocalDate} based on {@code this} with the adjustment made, not null
+ * @throws DateTimeException if the adjustment cannot be made
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public LocalDate with(TemporalAdjuster adjuster) {
+ // optimizations
+ if (adjuster instanceof LocalDate) {
+ return (LocalDate) adjuster;
+ }
+ return (LocalDate) adjuster.adjustInto(this);
+ }
+
+ /**
+ * Returns a copy of this date with the specified field set to a new value.
+ * <p>
+ * This returns a {@code LocalDate}, based on this one, with the value
+ * for the specified field changed.
+ * This can be used to change any supported field, such as the year, month or day-of-month.
+ * If it is not possible to set the value, because the field is not supported or for
+ * some other reason, an exception is thrown.
+ * <p>
+ * In some cases, changing the specified field can cause the resulting date to become invalid,
+ * such as changing the month from 31st January to February would make the day-of-month invalid.
+ * In cases like this, the field is responsible for resolving the date. Typically it will choose
+ * the previous valid date, which would be the last valid day of February in this example.
+ * <p>
+ * If the field is a {@link ChronoField} then the adjustment is implemented here.
+ * The supported fields behave as follows:
+ * <ul>
+ * <li>{@code DAY_OF_WEEK} -
+ * Returns a {@code LocalDate} with the specified day-of-week.
+ * The date is adjusted up to 6 days forward or backward within the boundary
+ * of a Monday to Sunday week.
+ * <li>{@code ALIGNED_DAY_OF_WEEK_IN_MONTH} -
+ * Returns a {@code LocalDate} with the specified aligned-day-of-week.
+ * The date is adjusted to the specified month-based aligned-day-of-week.
+ * Aligned weeks are counted such that the first week of a given month starts
+ * on the first day of that month.
+ * This may cause the date to be moved up to 6 days into the following month.
+ * <li>{@code ALIGNED_DAY_OF_WEEK_IN_YEAR} -
+ * Returns a {@code LocalDate} with the specified aligned-day-of-week.
+ * The date is adjusted to the specified year-based aligned-day-of-week.
+ * Aligned weeks are counted such that the first week of a given year starts
+ * on the first day of that year.
+ * This may cause the date to be moved up to 6 days into the following year.
+ * <li>{@code DAY_OF_MONTH} -
+ * Returns a {@code LocalDate} with the specified day-of-month.
+ * The month and year will be unchanged. If the day-of-month is invalid for the
+ * year and month, then a {@code DateTimeException} is thrown.
+ * <li>{@code DAY_OF_YEAR} -
+ * Returns a {@code LocalDate} with the specified day-of-year.
+ * The year will be unchanged. If the day-of-year is invalid for the
+ * year, then a {@code DateTimeException} is thrown.
+ * <li>{@code EPOCH_DAY} -
+ * Returns a {@code LocalDate} with the specified epoch-day.
+ * This completely replaces the date and is equivalent to {@link #ofEpochDay(long)}.
+ * <li>{@code ALIGNED_WEEK_OF_MONTH} -
+ * Returns a {@code LocalDate} with the specified aligned-week-of-month.
+ * Aligned weeks are counted such that the first week of a given month starts
+ * on the first day of that month.
+ * This adjustment moves the date in whole week chunks to match the specified week.
+ * The result will have the same day-of-week as this date.
+ * This may cause the date to be moved into the following month.
+ * <li>{@code ALIGNED_WEEK_OF_YEAR} -
+ * Returns a {@code LocalDate} with the specified aligned-week-of-year.
+ * Aligned weeks are counted such that the first week of a given year starts
+ * on the first day of that year.
+ * This adjustment moves the date in whole week chunks to match the specified week.
+ * The result will have the same day-of-week as this date.
+ * This may cause the date to be moved into the following year.
+ * <li>{@code MONTH_OF_YEAR} -
+ * Returns a {@code LocalDate} with the specified month-of-year.
+ * The year will be unchanged. The day-of-month will also be unchanged,
+ * unless it would be invalid for the new month and year. In that case, the
+ * day-of-month is adjusted to the maximum valid value for the new month and year.
+ * <li>{@code PROLEPTIC_MONTH} -
+ * Returns a {@code LocalDate} with the specified proleptic-month.
+ * The day-of-month will be unchanged, unless it would be invalid for the new month
+ * and year. In that case, the day-of-month is adjusted to the maximum valid value
+ * for the new month and year.
+ * <li>{@code YEAR_OF_ERA} -
+ * Returns a {@code LocalDate} with the specified year-of-era.
+ * The era and month will be unchanged. The day-of-month will also be unchanged,
+ * unless it would be invalid for the new month and year. In that case, the
+ * day-of-month is adjusted to the maximum valid value for the new month and year.
+ * <li>{@code YEAR} -
+ * Returns a {@code LocalDate} with the specified year.
+ * The month will be unchanged. The day-of-month will also be unchanged,
+ * unless it would be invalid for the new month and year. In that case, the
+ * day-of-month is adjusted to the maximum valid value for the new month and year.
+ * <li>{@code ERA} -
+ * Returns a {@code LocalDate} with the specified era.
+ * The year-of-era and month will be unchanged. The day-of-month will also be unchanged,
+ * unless it would be invalid for the new month and year. In that case, the
+ * day-of-month is adjusted to the maximum valid value for the new month and year.
+ * </ul>
+ * <p>
+ * In all cases, if the new value is outside the valid range of values for the field
+ * then a {@code DateTimeException} will be thrown.
+ * <p>
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.adjustInto(Temporal, long)}
+ * passing {@code this} as the argument. In this case, the field determines
+ * whether and how to adjust the instant.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param field the field to set in the result, not null
+ * @param newValue the new value of the field in the result
+ * @return a {@code LocalDate} based on {@code this} with the specified field set, not null
+ * @throws DateTimeException if the field cannot be set
+ * @throws UnsupportedTemporalTypeException if the field is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public LocalDate with(TemporalField field, long newValue) {
+ if (field instanceof ChronoField) {
+ ChronoField f = (ChronoField) field;
+ f.checkValidValue(newValue);
+ switch (f) {
+ case DAY_OF_WEEK: return plusDays(newValue - getDayOfWeek().getValue());
+ case ALIGNED_DAY_OF_WEEK_IN_MONTH: return plusDays(newValue - getLong(ALIGNED_DAY_OF_WEEK_IN_MONTH));
+ case ALIGNED_DAY_OF_WEEK_IN_YEAR: return plusDays(newValue - getLong(ALIGNED_DAY_OF_WEEK_IN_YEAR));
+ case DAY_OF_MONTH: return withDayOfMonth((int) newValue);
+ case DAY_OF_YEAR: return withDayOfYear((int) newValue);
+ case EPOCH_DAY: return LocalDate.ofEpochDay(newValue);
+ case ALIGNED_WEEK_OF_MONTH: return plusWeeks(newValue - getLong(ALIGNED_WEEK_OF_MONTH));
+ case ALIGNED_WEEK_OF_YEAR: return plusWeeks(newValue - getLong(ALIGNED_WEEK_OF_YEAR));
+ case MONTH_OF_YEAR: return withMonth((int) newValue);
+ case PROLEPTIC_MONTH: return plusMonths(newValue - getProlepticMonth());
+ case YEAR_OF_ERA: return withYear((int) (year >= 1 ? newValue : 1 - newValue));
+ case YEAR: return withYear((int) newValue);
+ case ERA: return (getLong(ERA) == newValue ? this : withYear(1 - year));
+ }
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
+ }
+ return field.adjustInto(this, newValue);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this {@code LocalDate} with the year altered.
+ * <p>
+ * If the day-of-month is invalid for the year, it will be changed to the last valid day of the month.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param year the year to set in the result, from MIN_YEAR to MAX_YEAR
+ * @return a {@code LocalDate} based on this date with the requested year, not null
+ * @throws DateTimeException if the year value is invalid
+ */
+ public LocalDate withYear(int year) {
+ if (this.year == year) {
+ return this;
+ }
+ YEAR.checkValidValue(year);
+ return resolvePreviousValid(year, month, day);
+ }
+
+ /**
+ * Returns a copy of this {@code LocalDate} with the month-of-year altered.
+ * <p>
+ * If the day-of-month is invalid for the year, it will be changed to the last valid day of the month.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param month the month-of-year to set in the result, from 1 (January) to 12 (December)
+ * @return a {@code LocalDate} based on this date with the requested month, not null
+ * @throws DateTimeException if the month-of-year value is invalid
+ */
+ public LocalDate withMonth(int month) {
+ if (this.month == month) {
+ return this;
+ }
+ MONTH_OF_YEAR.checkValidValue(month);
+ return resolvePreviousValid(year, month, day);
+ }
+
+ /**
+ * Returns a copy of this {@code LocalDate} with the day-of-month altered.
+ * <p>
+ * If the resulting date is invalid, an exception is thrown.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param dayOfMonth the day-of-month to set in the result, from 1 to 28-31
+ * @return a {@code LocalDate} based on this date with the requested day, not null
+ * @throws DateTimeException if the day-of-month value is invalid,
+ * or if the day-of-month is invalid for the month-year
+ */
+ public LocalDate withDayOfMonth(int dayOfMonth) {
+ if (this.day == dayOfMonth) {
+ return this;
+ }
+ return of(year, month, dayOfMonth);
+ }
+
+ /**
+ * Returns a copy of this {@code LocalDate} with the day-of-year altered.
+ * <p>
+ * If the resulting date is invalid, an exception is thrown.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param dayOfYear the day-of-year to set in the result, from 1 to 365-366
+ * @return a {@code LocalDate} based on this date with the requested day, not null
+ * @throws DateTimeException if the day-of-year value is invalid,
+ * or if the day-of-year is invalid for the year
+ */
+ public LocalDate withDayOfYear(int dayOfYear) {
+ if (this.getDayOfYear() == dayOfYear) {
+ return this;
+ }
+ return ofYearDay(year, dayOfYear);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this date with the specified amount added.
+ * <p>
+ * This returns a {@code LocalDate}, based on this one, with the specified amount added.
+ * The amount is typically {@link Period} but may be any other type implementing
+ * the {@link TemporalAmount} interface.
+ * <p>
+ * The calculation is delegated to the amount object by calling
+ * {@link TemporalAmount#addTo(Temporal)}. The amount implementation is free
+ * to implement the addition in any way it wishes, however it typically
+ * calls back to {@link #plus(long, TemporalUnit)}. Consult the documentation
+ * of the amount implementation to determine if it can be successfully added.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param amountToAdd the amount to add, not null
+ * @return a {@code LocalDate} based on this date with the addition made, not null
+ * @throws DateTimeException if the addition cannot be made
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public LocalDate plus(TemporalAmount amountToAdd) {
+ if (amountToAdd instanceof Period) {
+ Period periodToAdd = (Period) amountToAdd;
+ return plusMonths(periodToAdd.toTotalMonths()).plusDays(periodToAdd.getDays());
+ }
+ Objects.requireNonNull(amountToAdd, "amountToAdd");
+ return (LocalDate) amountToAdd.addTo(this);
+ }
+
+ /**
+ * Returns a copy of this date with the specified amount added.
+ * <p>
+ * This returns a {@code LocalDate}, based on this one, with the amount
+ * in terms of the unit added. If it is not possible to add the amount, because the
+ * unit is not supported or for some other reason, an exception is thrown.
+ * <p>
+ * In some cases, adding the amount can cause the resulting date to become invalid.
+ * For example, adding one month to 31st January would result in 31st February.
+ * In cases like this, the unit is responsible for resolving the date.
+ * Typically it will choose the previous valid date, which would be the last valid
+ * day of February in this example.
+ * <p>
+ * If the field is a {@link ChronoUnit} then the addition is implemented here.
+ * The supported fields behave as follows:
+ * <ul>
+ * <li>{@code DAYS} -
+ * Returns a {@code LocalDate} with the specified number of days added.
+ * This is equivalent to {@link #plusDays(long)}.
+ * <li>{@code WEEKS} -
+ * Returns a {@code LocalDate} with the specified number of weeks added.
+ * This is equivalent to {@link #plusWeeks(long)} and uses a 7 day week.
+ * <li>{@code MONTHS} -
+ * Returns a {@code LocalDate} with the specified number of months added.
+ * This is equivalent to {@link #plusMonths(long)}.
+ * The day-of-month will be unchanged unless it would be invalid for the new
+ * month and year. In that case, the day-of-month is adjusted to the maximum
+ * valid value for the new month and year.
+ * <li>{@code YEARS} -
+ * Returns a {@code LocalDate} with the specified number of years added.
+ * This is equivalent to {@link #plusYears(long)}.
+ * The day-of-month will be unchanged unless it would be invalid for the new
+ * month and year. In that case, the day-of-month is adjusted to the maximum
+ * valid value for the new month and year.
+ * <li>{@code DECADES} -
+ * Returns a {@code LocalDate} with the specified number of decades added.
+ * This is equivalent to calling {@link #plusYears(long)} with the amount
+ * multiplied by 10.
+ * The day-of-month will be unchanged unless it would be invalid for the new
+ * month and year. In that case, the day-of-month is adjusted to the maximum
+ * valid value for the new month and year.
+ * <li>{@code CENTURIES} -
+ * Returns a {@code LocalDate} with the specified number of centuries added.
+ * This is equivalent to calling {@link #plusYears(long)} with the amount
+ * multiplied by 100.
+ * The day-of-month will be unchanged unless it would be invalid for the new
+ * month and year. In that case, the day-of-month is adjusted to the maximum
+ * valid value for the new month and year.
+ * <li>{@code MILLENNIA} -
+ * Returns a {@code LocalDate} with the specified number of millennia added.
+ * This is equivalent to calling {@link #plusYears(long)} with the amount
+ * multiplied by 1,000.
+ * The day-of-month will be unchanged unless it would be invalid for the new
+ * month and year. In that case, the day-of-month is adjusted to the maximum
+ * valid value for the new month and year.
+ * <li>{@code ERAS} -
+ * Returns a {@code LocalDate} with the specified number of eras added.
+ * Only two eras are supported so the amount must be one, zero or minus one.
+ * If the amount is non-zero then the year is changed such that the year-of-era
+ * is unchanged.
+ * The day-of-month will be unchanged unless it would be invalid for the new
+ * month and year. In that case, the day-of-month is adjusted to the maximum
+ * valid value for the new month and year.
+ * </ul>
+ * <p>
+ * All other {@code ChronoUnit} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.addTo(Temporal, long)}
+ * passing {@code this} as the argument. In this case, the unit determines
+ * whether and how to perform the addition.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param amountToAdd the amount of the unit to add to the result, may be negative
+ * @param unit the unit of the amount to add, not null
+ * @return a {@code LocalDate} based on this date with the specified amount added, not null
+ * @throws DateTimeException if the addition cannot be made
+ * @throws UnsupportedTemporalTypeException if the unit is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public LocalDate plus(long amountToAdd, TemporalUnit unit) {
+ if (unit instanceof ChronoUnit) {
+ ChronoUnit f = (ChronoUnit) unit;
+ switch (f) {
+ case DAYS: return plusDays(amountToAdd);
+ case WEEKS: return plusWeeks(amountToAdd);
+ case MONTHS: return plusMonths(amountToAdd);
+ case YEARS: return plusYears(amountToAdd);
+ case DECADES: return plusYears(Math.multiplyExact(amountToAdd, 10));
+ case CENTURIES: return plusYears(Math.multiplyExact(amountToAdd, 100));
+ case MILLENNIA: return plusYears(Math.multiplyExact(amountToAdd, 1000));
+ case ERAS: return with(ERA, Math.addExact(getLong(ERA), amountToAdd));
+ }
+ throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
+ }
+ return unit.addTo(this, amountToAdd);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this {@code LocalDate} with the specified number of years added.
+ * <p>
+ * This method adds the specified amount to the years field in three steps:
+ * <ol>
+ * <li>Add the input years to the year field</li>
+ * <li>Check if the resulting date would be invalid</li>
+ * <li>Adjust the day-of-month to the last valid day if necessary</li>
+ * </ol>
+ * <p>
+ * For example, 2008-02-29 (leap year) plus one year would result in the
+ * invalid date 2009-02-29 (standard year). Instead of returning an invalid
+ * result, the last valid day of the month, 2009-02-28, is selected instead.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param yearsToAdd the years to add, may be negative
+ * @return a {@code LocalDate} based on this date with the years added, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public LocalDate plusYears(long yearsToAdd) {
+ if (yearsToAdd == 0) {
+ return this;
+ }
+ int newYear = YEAR.checkValidIntValue(year + yearsToAdd); // safe overflow
+ return resolvePreviousValid(newYear, month, day);
+ }
+
+ /**
+ * Returns a copy of this {@code LocalDate} with the specified number of months added.
+ * <p>
+ * This method adds the specified amount to the months field in three steps:
+ * <ol>
+ * <li>Add the input months to the month-of-year field</li>
+ * <li>Check if the resulting date would be invalid</li>
+ * <li>Adjust the day-of-month to the last valid day if necessary</li>
+ * </ol>
+ * <p>
+ * For example, 2007-03-31 plus one month would result in the invalid date
+ * 2007-04-31. Instead of returning an invalid result, the last valid day
+ * of the month, 2007-04-30, is selected instead.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param monthsToAdd the months to add, may be negative
+ * @return a {@code LocalDate} based on this date with the months added, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public LocalDate plusMonths(long monthsToAdd) {
+ if (monthsToAdd == 0) {
+ return this;
+ }
+ long monthCount = year * 12L + (month - 1);
+ long calcMonths = monthCount + monthsToAdd; // safe overflow
+ int newYear = YEAR.checkValidIntValue(Math.floorDiv(calcMonths, 12));
+ int newMonth = (int)Math.floorMod(calcMonths, 12) + 1;
+ return resolvePreviousValid(newYear, newMonth, day);
+ }
+
+ /**
+ * Returns a copy of this {@code LocalDate} with the specified number of weeks added.
+ * <p>
+ * This method adds the specified amount in weeks to the days field incrementing
+ * the month and year fields as necessary to ensure the result remains valid.
+ * The result is only invalid if the maximum/minimum year is exceeded.
+ * <p>
+ * For example, 2008-12-31 plus one week would result in 2009-01-07.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param weeksToAdd the weeks to add, may be negative
+ * @return a {@code LocalDate} based on this date with the weeks added, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public LocalDate plusWeeks(long weeksToAdd) {
+ return plusDays(Math.multiplyExact(weeksToAdd, 7));
+ }
+
+ /**
+ * Returns a copy of this {@code LocalDate} with the specified number of days added.
+ * <p>
+ * This method adds the specified amount to the days field incrementing the
+ * month and year fields as necessary to ensure the result remains valid.
+ * The result is only invalid if the maximum/minimum year is exceeded.
+ * <p>
+ * For example, 2008-12-31 plus one day would result in 2009-01-01.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param daysToAdd the days to add, may be negative
+ * @return a {@code LocalDate} based on this date with the days added, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public LocalDate plusDays(long daysToAdd) {
+ if (daysToAdd == 0) {
+ return this;
+ }
+ long mjDay = Math.addExact(toEpochDay(), daysToAdd);
+ return LocalDate.ofEpochDay(mjDay);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this date with the specified amount subtracted.
+ * <p>
+ * This returns a {@code LocalDate}, based on this one, with the specified amount subtracted.
+ * The amount is typically {@link Period} but may be any other type implementing
+ * the {@link TemporalAmount} interface.
+ * <p>
+ * The calculation is delegated to the amount object by calling
+ * {@link TemporalAmount#subtractFrom(Temporal)}. The amount implementation is free
+ * to implement the subtraction in any way it wishes, however it typically
+ * calls back to {@link #minus(long, TemporalUnit)}. Consult the documentation
+ * of the amount implementation to determine if it can be successfully subtracted.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param amountToSubtract the amount to subtract, not null
+ * @return a {@code LocalDate} based on this date with the subtraction made, not null
+ * @throws DateTimeException if the subtraction cannot be made
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public LocalDate minus(TemporalAmount amountToSubtract) {
+ if (amountToSubtract instanceof Period) {
+ Period periodToSubtract = (Period) amountToSubtract;
+ return minusMonths(periodToSubtract.toTotalMonths()).minusDays(periodToSubtract.getDays());
+ }
+ Objects.requireNonNull(amountToSubtract, "amountToSubtract");
+ return (LocalDate) amountToSubtract.subtractFrom(this);
+ }
+
+ /**
+ * Returns a copy of this date with the specified amount subtracted.
+ * <p>
+ * This returns a {@code LocalDate}, based on this one, with the amount
+ * in terms of the unit subtracted. If it is not possible to subtract the amount,
+ * because the unit is not supported or for some other reason, an exception is thrown.
+ * <p>
+ * This method is equivalent to {@link #plus(long, TemporalUnit)} with the amount negated.
+ * See that method for a full description of how addition, and thus subtraction, works.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param amountToSubtract the amount of the unit to subtract from the result, may be negative
+ * @param unit the unit of the amount to subtract, not null
+ * @return a {@code LocalDate} based on this date with the specified amount subtracted, not null
+ * @throws DateTimeException if the subtraction cannot be made
+ * @throws UnsupportedTemporalTypeException if the unit is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public LocalDate minus(long amountToSubtract, TemporalUnit unit) {
+ return (amountToSubtract == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this {@code LocalDate} with the specified number of years subtracted.
+ * <p>
+ * This method subtracts the specified amount from the years field in three steps:
+ * <ol>
+ * <li>Subtract the input years from the year field</li>
+ * <li>Check if the resulting date would be invalid</li>
+ * <li>Adjust the day-of-month to the last valid day if necessary</li>
+ * </ol>
+ * <p>
+ * For example, 2008-02-29 (leap year) minus one year would result in the
+ * invalid date 2007-02-29 (standard year). Instead of returning an invalid
+ * result, the last valid day of the month, 2007-02-28, is selected instead.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param yearsToSubtract the years to subtract, may be negative
+ * @return a {@code LocalDate} based on this date with the years subtracted, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public LocalDate minusYears(long yearsToSubtract) {
+ return (yearsToSubtract == Long.MIN_VALUE ? plusYears(Long.MAX_VALUE).plusYears(1) : plusYears(-yearsToSubtract));
+ }
+
+ /**
+ * Returns a copy of this {@code LocalDate} with the specified number of months subtracted.
+ * <p>
+ * This method subtracts the specified amount from the months field in three steps:
+ * <ol>
+ * <li>Subtract the input months from the month-of-year field</li>
+ * <li>Check if the resulting date would be invalid</li>
+ * <li>Adjust the day-of-month to the last valid day if necessary</li>
+ * </ol>
+ * <p>
+ * For example, 2007-03-31 minus one month would result in the invalid date
+ * 2007-02-31. Instead of returning an invalid result, the last valid day
+ * of the month, 2007-02-28, is selected instead.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param monthsToSubtract the months to subtract, may be negative
+ * @return a {@code LocalDate} based on this date with the months subtracted, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public LocalDate minusMonths(long monthsToSubtract) {
+ return (monthsToSubtract == Long.MIN_VALUE ? plusMonths(Long.MAX_VALUE).plusMonths(1) : plusMonths(-monthsToSubtract));
+ }
+
+ /**
+ * Returns a copy of this {@code LocalDate} with the specified number of weeks subtracted.
+ * <p>
+ * This method subtracts the specified amount in weeks from the days field decrementing
+ * the month and year fields as necessary to ensure the result remains valid.
+ * The result is only invalid if the maximum/minimum year is exceeded.
+ * <p>
+ * For example, 2009-01-07 minus one week would result in 2008-12-31.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param weeksToSubtract the weeks to subtract, may be negative
+ * @return a {@code LocalDate} based on this date with the weeks subtracted, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public LocalDate minusWeeks(long weeksToSubtract) {
+ return (weeksToSubtract == Long.MIN_VALUE ? plusWeeks(Long.MAX_VALUE).plusWeeks(1) : plusWeeks(-weeksToSubtract));
+ }
+
+ /**
+ * Returns a copy of this {@code LocalDate} with the specified number of days subtracted.
+ * <p>
+ * This method subtracts the specified amount from the days field decrementing the
+ * month and year fields as necessary to ensure the result remains valid.
+ * The result is only invalid if the maximum/minimum year is exceeded.
+ * <p>
+ * For example, 2009-01-01 minus one day would result in 2008-12-31.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param daysToSubtract the days to subtract, may be negative
+ * @return a {@code LocalDate} based on this date with the days subtracted, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public LocalDate minusDays(long daysToSubtract) {
+ return (daysToSubtract == Long.MIN_VALUE ? plusDays(Long.MAX_VALUE).plusDays(1) : plusDays(-daysToSubtract));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Queries this date using the specified query.
+ * <p>
+ * This queries this date using the specified query strategy object.
+ * The {@code TemporalQuery} object defines the logic to be used to
+ * obtain the result. Read the documentation of the query to understand
+ * what the result of this method will be.
+ * <p>
+ * The result of this method is obtained by invoking the
+ * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the
+ * specified query passing {@code this} as the argument.
+ *
+ * @param <R> the type of the result
+ * @param query the query to invoke, not null
+ * @return the query result, null may be returned (defined by the query)
+ * @throws DateTimeException if unable to query (defined by the query)
+ * @throws ArithmeticException if numeric overflow occurs (defined by the query)
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public <R> R query(TemporalQuery<R> query) {
+ if (query == TemporalQueries.localDate()) {
+ return (R) this;
+ }
+ return ChronoLocalDate.super.query(query);
+ }
+
+ /**
+ * Adjusts the specified temporal object to have the same date as this object.
+ * <p>
+ * This returns a temporal object of the same observable type as the input
+ * with the date changed to be the same as this.
+ * <p>
+ * The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)}
+ * passing {@link ChronoField#EPOCH_DAY} as the field.
+ * <p>
+ * In most cases, it is clearer to reverse the calling pattern by using
+ * {@link Temporal#with(TemporalAdjuster)}:
+ * <pre>
+ * // these two lines are equivalent, but the second approach is recommended
+ * temporal = thisLocalDate.adjustInto(temporal);
+ * temporal = temporal.with(thisLocalDate);
+ * </pre>
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param temporal the target object to be adjusted, not null
+ * @return the adjusted object, not null
+ * @throws DateTimeException if unable to make the adjustment
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override // override for Javadoc
+ public Temporal adjustInto(Temporal temporal) {
+ return ChronoLocalDate.super.adjustInto(temporal);
+ }
+
+ /**
+ * Calculates the amount of time until another date in terms of the specified unit.
+ * <p>
+ * This calculates the amount of time between two {@code LocalDate}
+ * objects in terms of a single {@code TemporalUnit}.
+ * 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 {@code Temporal} passed to this method is converted to a
+ * {@code LocalDate} using {@link #from(TemporalAccessor)}.
+ * For example, the amount in days between two dates can be calculated
+ * using {@code startDate.until(endDate, DAYS)}.
+ * <p>
+ * The calculation returns a whole number, representing the number of
+ * complete units between the two dates.
+ * For example, the amount in months between 2012-06-15 and 2012-08-14
+ * will only be one month as it is one day short of two months.
+ * <p>
+ * There are two equivalent ways of using this method.
+ * The first is to invoke this method.
+ * The second is to use {@link TemporalUnit#between(Temporal, Temporal)}:
+ * <pre>
+ * // these two lines are equivalent
+ * amount = start.until(end, MONTHS);
+ * amount = MONTHS.between(start, end);
+ * </pre>
+ * The choice should be made based on which makes the code more readable.
+ * <p>
+ * The calculation is implemented in this method for {@link ChronoUnit}.
+ * The units {@code DAYS}, {@code WEEKS}, {@code MONTHS}, {@code YEARS},
+ * {@code DECADES}, {@code CENTURIES}, {@code MILLENNIA} and {@code ERAS}
+ * are supported. Other {@code ChronoUnit} values will throw an exception.
+ * <p>
+ * If the unit is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.between(Temporal, Temporal)}
+ * passing {@code this} as the first argument and the converted input temporal
+ * as the second argument.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param endExclusive the end date, exclusive, which is converted to a {@code LocalDate}, not null
+ * @param unit the unit to measure the amount in, not null
+ * @return the amount of time between this date and the end date
+ * @throws DateTimeException if the amount cannot be calculated, or the end
+ * temporal cannot be converted to a {@code LocalDate}
+ * @throws UnsupportedTemporalTypeException if the unit is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public long until(Temporal endExclusive, TemporalUnit unit) {
+ LocalDate end = LocalDate.from(endExclusive);
+ if (unit instanceof ChronoUnit) {
+ switch ((ChronoUnit) unit) {
+ case DAYS: return daysUntil(end);
+ case WEEKS: return daysUntil(end) / 7;
+ case MONTHS: return monthsUntil(end);
+ case YEARS: return monthsUntil(end) / 12;
+ case DECADES: return monthsUntil(end) / 120;
+ case CENTURIES: return monthsUntil(end) / 1200;
+ case MILLENNIA: return monthsUntil(end) / 12000;
+ case ERAS: return end.getLong(ERA) - getLong(ERA);
+ }
+ throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
+ }
+ return unit.between(this, end);
+ }
+
+ long daysUntil(LocalDate end) {
+ return end.toEpochDay() - toEpochDay(); // no overflow
+ }
+
+ private long monthsUntil(LocalDate end) {
+ long packed1 = getProlepticMonth() * 32L + getDayOfMonth(); // no overflow
+ long packed2 = end.getProlepticMonth() * 32L + end.getDayOfMonth(); // no overflow
+ return (packed2 - packed1) / 32;
+ }
+
+ /**
+ * Calculates the period between this date and another date as a {@code Period}.
+ * <p>
+ * This calculates the period between two dates in terms of years, months and days.
+ * 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.
+ * <p>
+ * The calculation is performed using the ISO calendar system.
+ * If necessary, the input date will be converted to ISO.
+ * <p>
+ * The start date is included, but the end date is not.
+ * The period is calculated by removing complete months, then calculating
+ * the remaining number of days, adjusting to ensure that both have the same sign.
+ * The number of months is then normalized into years and months based on a 12 month year.
+ * A month is considered to be complete if the end day-of-month is greater
+ * than or equal to the start day-of-month.
+ * For example, from {@code 2010-01-15} to {@code 2011-03-18} is "1 year, 2 months and 3 days".
+ * <p>
+ * There are two equivalent ways of using this method.
+ * The first is to invoke this method.
+ * The second is to use {@link Period#between(LocalDate, LocalDate)}:
+ * <pre>
+ * // these two lines are equivalent
+ * period = start.until(end);
+ * period = Period.between(start, end);
+ * </pre>
+ * The choice should be made based on which makes the code more readable.
+ *
+ * @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 endDateExclusive) {
+ LocalDate end = LocalDate.from(endDateExclusive);
+ long totalMonths = end.getProlepticMonth() - this.getProlepticMonth(); // safe
+ int days = end.day - this.day;
+ if (totalMonths > 0 && days < 0) {
+ totalMonths--;
+ LocalDate calcDate = this.plusMonths(totalMonths);
+ days = (int) (end.toEpochDay() - calcDate.toEpochDay()); // safe
+ } else if (totalMonths < 0 && days > 0) {
+ totalMonths++;
+ days -= end.lengthOfMonth();
+ }
+ long years = totalMonths / 12; // safe
+ int months = (int) (totalMonths % 12); // safe
+ return Period.of(Math.toIntExact(years), months, days);
+ }
+
+ /**
+ * Formats this date using the specified formatter.
+ * <p>
+ * This date will be passed to the formatter to produce a string.
+ *
+ * @param formatter the formatter to use, not null
+ * @return the formatted date string, not null
+ * @throws DateTimeException if an error occurs during printing
+ */
+ @Override // override for Javadoc and performance
+ public String format(DateTimeFormatter formatter) {
+ Objects.requireNonNull(formatter, "formatter");
+ return formatter.format(this);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Combines this date with a time to create a {@code LocalDateTime}.
+ * <p>
+ * This returns a {@code LocalDateTime} formed from this date at the specified time.
+ * All possible combinations of date and time are valid.
+ *
+ * @param time the time to combine with, not null
+ * @return the local date-time formed from this date and the specified time, not null
+ */
+ @Override
+ public LocalDateTime atTime(LocalTime time) {
+ return LocalDateTime.of(this, time);
+ }
+
+ /**
+ * Combines this date with a time to create a {@code LocalDateTime}.
+ * <p>
+ * This returns a {@code LocalDateTime} formed from this date at the
+ * specified hour and minute.
+ * The seconds and nanosecond fields will be set to zero.
+ * The individual time fields must be within their valid range.
+ * All possible combinations of date and time are valid.
+ *
+ * @param hour the hour-of-day to use, from 0 to 23
+ * @param minute the minute-of-hour to use, from 0 to 59
+ * @return the local date-time formed from this date and the specified time, not null
+ * @throws DateTimeException if the value of any field is out of range
+ */
+ public LocalDateTime atTime(int hour, int minute) {
+ return atTime(LocalTime.of(hour, minute));
+ }
+
+ /**
+ * Combines this date with a time to create a {@code LocalDateTime}.
+ * <p>
+ * This returns a {@code LocalDateTime} formed from this date at the
+ * specified hour, minute and second.
+ * The nanosecond field will be set to zero.
+ * The individual time fields must be within their valid range.
+ * All possible combinations of date and time are valid.
+ *
+ * @param hour the hour-of-day to use, from 0 to 23
+ * @param minute the minute-of-hour to use, from 0 to 59
+ * @param second the second-of-minute to represent, from 0 to 59
+ * @return the local date-time formed from this date and the specified time, not null
+ * @throws DateTimeException if the value of any field is out of range
+ */
+ public LocalDateTime atTime(int hour, int minute, int second) {
+ return atTime(LocalTime.of(hour, minute, second));
+ }
+
+ /**
+ * Combines this date with a time to create a {@code LocalDateTime}.
+ * <p>
+ * This returns a {@code LocalDateTime} formed from this date at the
+ * specified hour, minute, second and nanosecond.
+ * The individual time fields must be within their valid range.
+ * All possible combinations of date and time are valid.
+ *
+ * @param hour the hour-of-day to use, from 0 to 23
+ * @param minute the minute-of-hour to use, from 0 to 59
+ * @param second the second-of-minute to represent, from 0 to 59
+ * @param nanoOfSecond the nano-of-second to represent, from 0 to 999,999,999
+ * @return the local date-time formed from this date and the specified time, not null
+ * @throws DateTimeException if the value of any field is out of range
+ */
+ public LocalDateTime atTime(int hour, int minute, int second, int nanoOfSecond) {
+ return atTime(LocalTime.of(hour, minute, second, nanoOfSecond));
+ }
+
+ /**
+ * Combines this date with an offset time to create an {@code OffsetDateTime}.
+ * <p>
+ * This returns an {@code OffsetDateTime} formed from this date at the specified time.
+ * All possible combinations of date and time are valid.
+ *
+ * @param time the time to combine with, not null
+ * @return the offset date-time formed from this date and the specified time, not null
+ */
+ public OffsetDateTime atTime(OffsetTime time) {
+ return OffsetDateTime.of(LocalDateTime.of(this, time.toLocalTime()), time.getOffset());
+ }
+
+ /**
+ * Combines this date with the time of midnight to create a {@code LocalDateTime}
+ * at the start of this date.
+ * <p>
+ * This returns a {@code LocalDateTime} formed from this date at the time of
+ * midnight, 00:00, at the start of this date.
+ *
+ * @return the local date-time of midnight at the start of this date, not null
+ */
+ public LocalDateTime atStartOfDay() {
+ return LocalDateTime.of(this, LocalTime.MIDNIGHT);
+ }
+
+ /**
+ * Returns a zoned date-time from this date at the earliest valid time according
+ * to the rules in the time-zone.
+ * <p>
+ * Time-zone rules, such as daylight savings, mean that not every local date-time
+ * is valid for the specified zone, thus the local date-time may not be midnight.
+ * <p>
+ * In most cases, there is only one valid offset for a local date-time.
+ * In the case of an overlap, there are two valid offsets, and the earlier one is used,
+ * corresponding to the first occurrence of midnight on the date.
+ * In the case of a gap, the zoned date-time will represent the instant just after the gap.
+ * <p>
+ * If the zone ID is a {@link ZoneOffset}, then the result always has a time of midnight.
+ * <p>
+ * To convert to a specific time in a given time-zone call {@link #atTime(LocalTime)}
+ * followed by {@link LocalDateTime#atZone(ZoneId)}.
+ *
+ * @param zone the zone ID to use, not null
+ * @return the zoned date-time formed from this date and the earliest valid time for the zone, not null
+ */
+ public ZonedDateTime atStartOfDay(ZoneId zone) {
+ Objects.requireNonNull(zone, "zone");
+ // need to handle case where there is a gap from 11:30 to 00:30
+ // standard ZDT factory would result in 01:00 rather than 00:30
+ LocalDateTime ldt = atTime(LocalTime.MIDNIGHT);
+ if (zone instanceof ZoneOffset == false) {
+ ZoneRules rules = zone.getRules();
+ ZoneOffsetTransition trans = rules.getTransition(ldt);
+ if (trans != null && trans.isGap()) {
+ ldt = trans.getDateTimeAfter();
+ }
+ }
+ return ZonedDateTime.of(ldt, zone);
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ public long toEpochDay() {
+ long y = year;
+ long m = month;
+ long total = 0;
+ total += 365 * y;
+ if (y >= 0) {
+ total += (y + 3) / 4 - (y + 99) / 100 + (y + 399) / 400;
+ } else {
+ total -= y / -4 - y / -100 + y / -400;
+ }
+ total += ((367 * m - 362) / 12);
+ total += day - 1;
+ if (m > 2) {
+ total--;
+ if (isLeapYear() == false) {
+ total--;
+ }
+ }
+ return total - DAYS_0000_TO_1970;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Compares this date to another date.
+ * <p>
+ * The comparison is primarily based on the date, from earliest to latest.
+ * It is "consistent with equals", as defined by {@link Comparable}.
+ * <p>
+ * If all the dates being compared are instances of {@code LocalDate},
+ * then the comparison will be entirely based on the date.
+ * If some dates being compared are in different chronologies, then the
+ * chronology is also considered, see {@link java.time.chrono.ChronoLocalDate#compareTo}.
+ *
+ * @param other the other date to compare to, not null
+ * @return the comparator value, negative if less, positive if greater
+ */
+ @Override // override for Javadoc and performance
+ public int compareTo(ChronoLocalDate other) {
+ if (other instanceof LocalDate) {
+ return compareTo0((LocalDate) other);
+ }
+ return ChronoLocalDate.super.compareTo(other);
+ }
+
+ int compareTo0(LocalDate otherDate) {
+ int cmp = (year - otherDate.year);
+ if (cmp == 0) {
+ cmp = (month - otherDate.month);
+ if (cmp == 0) {
+ cmp = (day - otherDate.day);
+ }
+ }
+ return cmp;
+ }
+
+ /**
+ * Checks if this date is after the specified date.
+ * <p>
+ * This checks to see if this date represents a point on the
+ * local time-line after the other date.
+ * <pre>
+ * LocalDate a = LocalDate.of(2012, 6, 30);
+ * LocalDate b = LocalDate.of(2012, 7, 1);
+ * a.isAfter(b) == false
+ * a.isAfter(a) == false
+ * b.isAfter(a) == true
+ * </pre>
+ * <p>
+ * This method only considers the position of the two dates on the local time-line.
+ * It does not take into account the chronology, or calendar system.
+ * This is different from the comparison in {@link #compareTo(ChronoLocalDate)},
+ * but is the same approach as {@link ChronoLocalDate#timeLineOrder()}.
+ *
+ * @param other the other date to compare to, not null
+ * @return true if this date is after the specified date
+ */
+ @Override // override for Javadoc and performance
+ public boolean isAfter(ChronoLocalDate other) {
+ if (other instanceof LocalDate) {
+ return compareTo0((LocalDate) other) > 0;
+ }
+ return ChronoLocalDate.super.isAfter(other);
+ }
+
+ /**
+ * Checks if this date is before the specified date.
+ * <p>
+ * This checks to see if this date represents a point on the
+ * local time-line before the other date.
+ * <pre>
+ * LocalDate a = LocalDate.of(2012, 6, 30);
+ * LocalDate b = LocalDate.of(2012, 7, 1);
+ * a.isBefore(b) == true
+ * a.isBefore(a) == false
+ * b.isBefore(a) == false
+ * </pre>
+ * <p>
+ * This method only considers the position of the two dates on the local time-line.
+ * It does not take into account the chronology, or calendar system.
+ * This is different from the comparison in {@link #compareTo(ChronoLocalDate)},
+ * but is the same approach as {@link ChronoLocalDate#timeLineOrder()}.
+ *
+ * @param other the other date to compare to, not null
+ * @return true if this date is before the specified date
+ */
+ @Override // override for Javadoc and performance
+ public boolean isBefore(ChronoLocalDate other) {
+ if (other instanceof LocalDate) {
+ return compareTo0((LocalDate) other) < 0;
+ }
+ return ChronoLocalDate.super.isBefore(other);
+ }
+
+ /**
+ * Checks if this date is equal to the specified date.
+ * <p>
+ * This checks to see if this date represents the same point on the
+ * local time-line as the other date.
+ * <pre>
+ * LocalDate a = LocalDate.of(2012, 6, 30);
+ * LocalDate b = LocalDate.of(2012, 7, 1);
+ * a.isEqual(b) == false
+ * a.isEqual(a) == true
+ * b.isEqual(a) == false
+ * </pre>
+ * <p>
+ * This method only considers the position of the two dates on the local time-line.
+ * It does not take into account the chronology, or calendar system.
+ * This is different from the comparison in {@link #compareTo(ChronoLocalDate)}
+ * but is the same approach as {@link ChronoLocalDate#timeLineOrder()}.
+ *
+ * @param other the other date to compare to, not null
+ * @return true if this date is equal to the specified date
+ */
+ @Override // override for Javadoc and performance
+ public boolean isEqual(ChronoLocalDate other) {
+ if (other instanceof LocalDate) {
+ return compareTo0((LocalDate) other) == 0;
+ }
+ return ChronoLocalDate.super.isEqual(other);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if this date is equal to another date.
+ * <p>
+ * Compares this {@code LocalDate} with another ensuring that the date is the same.
+ * <p>
+ * Only objects of type {@code LocalDate} are compared, other types return false.
+ * To compare the dates of two {@code TemporalAccessor} instances, including dates
+ * in two different chronologies, use {@link ChronoField#EPOCH_DAY} as a comparator.
+ *
+ * @param obj the object to check, null returns false
+ * @return true if this is equal to the other date
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof LocalDate) {
+ return compareTo0((LocalDate) obj) == 0;
+ }
+ return false;
+ }
+
+ /**
+ * A hash code for this date.
+ *
+ * @return a suitable hash code
+ */
+ @Override
+ public int hashCode() {
+ int yearValue = year;
+ int monthValue = month;
+ int dayValue = day;
+ return (yearValue & 0xFFFFF800) ^ ((yearValue << 11) + (monthValue << 6) + (dayValue));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Outputs this date as a {@code String}, such as {@code 2007-12-03}.
+ * <p>
+ * The output will be in the ISO-8601 format {@code uuuu-MM-dd}.
+ *
+ * @return a string representation of this date, not null
+ */
+ @Override
+ public String toString() {
+ int yearValue = year;
+ int monthValue = month;
+ int dayValue = day;
+ int absYear = Math.abs(yearValue);
+ StringBuilder buf = new StringBuilder(10);
+ if (absYear < 1000) {
+ if (yearValue < 0) {
+ buf.append(yearValue - 10000).deleteCharAt(1);
+ } else {
+ buf.append(yearValue + 10000).deleteCharAt(0);
+ }
+ } else {
+ if (yearValue > 9999) {
+ buf.append('+');
+ }
+ buf.append(yearValue);
+ }
+ return buf.append(monthValue < 10 ? "-0" : "-")
+ .append(monthValue)
+ .append(dayValue < 10 ? "-0" : "-")
+ .append(dayValue)
+ .toString();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Writes the object using a
+ * <a href="../../serialized-form.html#java.time.Ser">dedicated serialized form</a>.
+ * @serialData
+ * <pre>
+ * out.writeByte(3); // identifies a LocalDate
+ * out.writeInt(year);
+ * out.writeByte(month);
+ * out.writeByte(day);
+ * </pre>
+ *
+ * @return the instance of {@code Ser}, not null
+ */
+ private Object writeReplace() {
+ return new Ser(Ser.LOCAL_DATE_TYPE, this);
+ }
+
+ /**
+ * Defend against malicious streams.
+ *
+ * @param s the stream to read
+ * @throws InvalidObjectException always
+ */
+ private void readObject(ObjectInputStream s) throws InvalidObjectException {
+ throw new InvalidObjectException("Deserialization via serialization delegate");
+ }
+
+ void writeExternal(DataOutput out) throws IOException {
+ out.writeInt(year);
+ out.writeByte(month);
+ out.writeByte(day);
+ }
+
+ static LocalDate readExternal(DataInput in) throws IOException {
+ int year = in.readInt();
+ int month = in.readByte();
+ int dayOfMonth = in.readByte();
+ return LocalDate.of(year, month, dayOfMonth);
+ }
+
+}
diff --git a/java/time/LocalDateTime.java b/java/time/LocalDateTime.java
new file mode 100644
index 0000000..5821ffd
--- /dev/null
+++ b/java/time/LocalDateTime.java
@@ -0,0 +1,2007 @@
+/*
+ * Copyright (c) 2012, 2015, 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) 2007-2012, 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;
+
+import static java.time.LocalTime.HOURS_PER_DAY;
+import static java.time.LocalTime.MICROS_PER_DAY;
+import static java.time.LocalTime.MILLIS_PER_DAY;
+import static java.time.LocalTime.MINUTES_PER_DAY;
+import static java.time.LocalTime.NANOS_PER_DAY;
+import static java.time.LocalTime.NANOS_PER_HOUR;
+import static java.time.LocalTime.NANOS_PER_MINUTE;
+import static java.time.LocalTime.NANOS_PER_SECOND;
+import static java.time.LocalTime.SECONDS_PER_DAY;
+import static java.time.temporal.ChronoField.NANO_OF_SECOND;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.time.chrono.ChronoLocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.time.temporal.ChronoField;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.Temporal;
+import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalAdjuster;
+import java.time.temporal.TemporalAmount;
+import java.time.temporal.TemporalField;
+import java.time.temporal.TemporalQueries;
+import java.time.temporal.TemporalQuery;
+import java.time.temporal.TemporalUnit;
+import java.time.temporal.UnsupportedTemporalTypeException;
+import java.time.temporal.ValueRange;
+import java.time.zone.ZoneRules;
+import java.util.Objects;
+
+// Android-changed: removed ValueBased paragraph.
+/**
+ * A date-time without a time-zone in the ISO-8601 calendar system,
+ * such as {@code 2007-12-03T10:15:30}.
+ * <p>
+ * {@code LocalDateTime} is an immutable date-time object that represents a date-time,
+ * often viewed as year-month-day-hour-minute-second. Other date and time fields,
+ * such as day-of-year, day-of-week and week-of-year, can also be accessed.
+ * Time is represented to nanosecond precision.
+ * For example, the value "2nd October 2007 at 13:45.30.123456789" can be
+ * stored in a {@code LocalDateTime}.
+ * <p>
+ * This class does not store or represent a time-zone.
+ * Instead, it is a description of the date, as used for birthdays, combined with
+ * the local time as seen on a wall clock.
+ * It cannot represent an instant on the time-line without additional information
+ * such as an offset or time-zone.
+ * <p>
+ * 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.
+ * For most applications written today, the ISO-8601 rules are entirely suitable.
+ * However, any application that makes use of historical dates, and requires them
+ * to be accurate will find the ISO-8601 approach unsuitable.
+ *
+ * @implSpec
+ * This class is immutable and thread-safe.
+ *
+ * @since 1.8
+ */
+public final class LocalDateTime
+ implements Temporal, TemporalAdjuster, ChronoLocalDateTime<LocalDate>, Serializable {
+
+ /**
+ * The minimum supported {@code LocalDateTime}, '-999999999-01-01T00:00:00'.
+ * This is the local date-time of midnight at the start of the minimum date.
+ * This combines {@link LocalDate#MIN} and {@link LocalTime#MIN}.
+ * This could be used by an application as a "far past" date-time.
+ */
+ public static final LocalDateTime MIN = LocalDateTime.of(LocalDate.MIN, LocalTime.MIN);
+ /**
+ * The maximum supported {@code LocalDateTime}, '+999999999-12-31T23:59:59.999999999'.
+ * This is the local date-time just before midnight at the end of the maximum date.
+ * This combines {@link LocalDate#MAX} and {@link LocalTime#MAX}.
+ * This could be used by an application as a "far future" date-time.
+ */
+ public static final LocalDateTime MAX = LocalDateTime.of(LocalDate.MAX, LocalTime.MAX);
+
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = 6207766400415563566L;
+
+ /**
+ * The date part.
+ */
+ private final LocalDate date;
+ /**
+ * The time part.
+ */
+ private final LocalTime time;
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains the current date-time from the system clock in the default time-zone.
+ * <p>
+ * This will query the {@link Clock#systemDefaultZone() system clock} in the default
+ * time-zone to obtain the current date-time.
+ * <p>
+ * Using this method will prevent the ability to use an alternate clock for testing
+ * because the clock is hard-coded.
+ *
+ * @return the current date-time using the system clock and default time-zone, not null
+ */
+ public static LocalDateTime now() {
+ return now(Clock.systemDefaultZone());
+ }
+
+ /**
+ * Obtains the current date-time from the system clock in the specified time-zone.
+ * <p>
+ * This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date-time.
+ * Specifying the time-zone avoids dependence on the default time-zone.
+ * <p>
+ * Using this method will prevent the ability to use an alternate clock for testing
+ * because the clock is hard-coded.
+ *
+ * @param zone the zone ID to use, not null
+ * @return the current date-time using the system clock, not null
+ */
+ public static LocalDateTime now(ZoneId zone) {
+ return now(Clock.system(zone));
+ }
+
+ /**
+ * Obtains the current date-time from the specified clock.
+ * <p>
+ * This will query the specified clock to obtain the current date-time.
+ * Using this method allows the use of an alternate clock for testing.
+ * The alternate clock may be introduced using {@link Clock dependency injection}.
+ *
+ * @param clock the clock to use, not null
+ * @return the current date-time, not null
+ */
+ public static LocalDateTime now(Clock clock) {
+ Objects.requireNonNull(clock, "clock");
+ final Instant now = clock.instant(); // called once
+ ZoneOffset offset = clock.getZone().getRules().getOffset(now);
+ return ofEpochSecond(now.getEpochSecond(), now.getNano(), offset);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code LocalDateTime} from year, month,
+ * day, hour and minute, setting the second and nanosecond to zero.
+ * <p>
+ * This returns a {@code LocalDateTime} with the specified year, month,
+ * day-of-month, hour and minute.
+ * The day must be valid for the year and month, otherwise an exception will be thrown.
+ * The second and nanosecond fields will be set to zero.
+ *
+ * @param year the year to represent, from MIN_YEAR to MAX_YEAR
+ * @param month the month-of-year to represent, not null
+ * @param dayOfMonth the day-of-month to represent, from 1 to 31
+ * @param hour the hour-of-day to represent, from 0 to 23
+ * @param minute the minute-of-hour to represent, from 0 to 59
+ * @return the local date-time, not null
+ * @throws DateTimeException if the value of any field is out of range,
+ * or if the day-of-month is invalid for the month-year
+ */
+ public static LocalDateTime of(int year, Month month, int dayOfMonth, int hour, int minute) {
+ LocalDate date = LocalDate.of(year, month, dayOfMonth);
+ LocalTime time = LocalTime.of(hour, minute);
+ return new LocalDateTime(date, time);
+ }
+
+ /**
+ * Obtains an instance of {@code LocalDateTime} from year, month,
+ * day, hour, minute and second, setting the nanosecond to zero.
+ * <p>
+ * This returns a {@code LocalDateTime} with the specified year, month,
+ * day-of-month, hour, minute and second.
+ * The day must be valid for the year and month, otherwise an exception will be thrown.
+ * The nanosecond field will be set to zero.
+ *
+ * @param year the year to represent, from MIN_YEAR to MAX_YEAR
+ * @param month the month-of-year to represent, not null
+ * @param dayOfMonth the day-of-month to represent, from 1 to 31
+ * @param hour the hour-of-day to represent, from 0 to 23
+ * @param minute the minute-of-hour to represent, from 0 to 59
+ * @param second the second-of-minute to represent, from 0 to 59
+ * @return the local date-time, not null
+ * @throws DateTimeException if the value of any field is out of range,
+ * or if the day-of-month is invalid for the month-year
+ */
+ public static LocalDateTime of(int year, Month month, int dayOfMonth, int hour, int minute, int second) {
+ LocalDate date = LocalDate.of(year, month, dayOfMonth);
+ LocalTime time = LocalTime.of(hour, minute, second);
+ return new LocalDateTime(date, time);
+ }
+
+ /**
+ * Obtains an instance of {@code LocalDateTime} from year, month,
+ * day, hour, minute, second and nanosecond.
+ * <p>
+ * This returns a {@code LocalDateTime} with the specified year, month,
+ * day-of-month, hour, minute, second and nanosecond.
+ * The day must be valid for the year and month, otherwise an exception will be thrown.
+ *
+ * @param year the year to represent, from MIN_YEAR to MAX_YEAR
+ * @param month the month-of-year to represent, not null
+ * @param dayOfMonth the day-of-month to represent, from 1 to 31
+ * @param hour the hour-of-day to represent, from 0 to 23
+ * @param minute the minute-of-hour to represent, from 0 to 59
+ * @param second the second-of-minute to represent, from 0 to 59
+ * @param nanoOfSecond the nano-of-second to represent, from 0 to 999,999,999
+ * @return the local date-time, not null
+ * @throws DateTimeException if the value of any field is out of range,
+ * or if the day-of-month is invalid for the month-year
+ */
+ public static LocalDateTime of(int year, Month month, int dayOfMonth, int hour, int minute, int second, int nanoOfSecond) {
+ LocalDate date = LocalDate.of(year, month, dayOfMonth);
+ LocalTime time = LocalTime.of(hour, minute, second, nanoOfSecond);
+ return new LocalDateTime(date, time);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code LocalDateTime} from year, month,
+ * day, hour and minute, setting the second and nanosecond to zero.
+ * <p>
+ * This returns a {@code LocalDateTime} with the specified year, month,
+ * day-of-month, hour and minute.
+ * The day must be valid for the year and month, otherwise an exception will be thrown.
+ * The second and nanosecond fields will be set to zero.
+ *
+ * @param year the year to represent, from MIN_YEAR to MAX_YEAR
+ * @param month the month-of-year to represent, from 1 (January) to 12 (December)
+ * @param dayOfMonth the day-of-month to represent, from 1 to 31
+ * @param hour the hour-of-day to represent, from 0 to 23
+ * @param minute the minute-of-hour to represent, from 0 to 59
+ * @return the local date-time, not null
+ * @throws DateTimeException if the value of any field is out of range,
+ * or if the day-of-month is invalid for the month-year
+ */
+ public static LocalDateTime of(int year, int month, int dayOfMonth, int hour, int minute) {
+ LocalDate date = LocalDate.of(year, month, dayOfMonth);
+ LocalTime time = LocalTime.of(hour, minute);
+ return new LocalDateTime(date, time);
+ }
+
+ /**
+ * Obtains an instance of {@code LocalDateTime} from year, month,
+ * day, hour, minute and second, setting the nanosecond to zero.
+ * <p>
+ * This returns a {@code LocalDateTime} with the specified year, month,
+ * day-of-month, hour, minute and second.
+ * The day must be valid for the year and month, otherwise an exception will be thrown.
+ * The nanosecond field will be set to zero.
+ *
+ * @param year the year to represent, from MIN_YEAR to MAX_YEAR
+ * @param month the month-of-year to represent, from 1 (January) to 12 (December)
+ * @param dayOfMonth the day-of-month to represent, from 1 to 31
+ * @param hour the hour-of-day to represent, from 0 to 23
+ * @param minute the minute-of-hour to represent, from 0 to 59
+ * @param second the second-of-minute to represent, from 0 to 59
+ * @return the local date-time, not null
+ * @throws DateTimeException if the value of any field is out of range,
+ * or if the day-of-month is invalid for the month-year
+ */
+ public static LocalDateTime of(int year, int month, int dayOfMonth, int hour, int minute, int second) {
+ LocalDate date = LocalDate.of(year, month, dayOfMonth);
+ LocalTime time = LocalTime.of(hour, minute, second);
+ return new LocalDateTime(date, time);
+ }
+
+ /**
+ * Obtains an instance of {@code LocalDateTime} from year, month,
+ * day, hour, minute, second and nanosecond.
+ * <p>
+ * This returns a {@code LocalDateTime} with the specified year, month,
+ * day-of-month, hour, minute, second and nanosecond.
+ * The day must be valid for the year and month, otherwise an exception will be thrown.
+ *
+ * @param year the year to represent, from MIN_YEAR to MAX_YEAR
+ * @param month the month-of-year to represent, from 1 (January) to 12 (December)
+ * @param dayOfMonth the day-of-month to represent, from 1 to 31
+ * @param hour the hour-of-day to represent, from 0 to 23
+ * @param minute the minute-of-hour to represent, from 0 to 59
+ * @param second the second-of-minute to represent, from 0 to 59
+ * @param nanoOfSecond the nano-of-second to represent, from 0 to 999,999,999
+ * @return the local date-time, not null
+ * @throws DateTimeException if the value of any field is out of range,
+ * or if the day-of-month is invalid for the month-year
+ */
+ public static LocalDateTime of(int year, int month, int dayOfMonth, int hour, int minute, int second, int nanoOfSecond) {
+ LocalDate date = LocalDate.of(year, month, dayOfMonth);
+ LocalTime time = LocalTime.of(hour, minute, second, nanoOfSecond);
+ return new LocalDateTime(date, time);
+ }
+
+ /**
+ * Obtains an instance of {@code LocalDateTime} from a date and time.
+ *
+ * @param date the local date, not null
+ * @param time the local time, not null
+ * @return the local date-time, not null
+ */
+ public static LocalDateTime of(LocalDate date, LocalTime time) {
+ Objects.requireNonNull(date, "date");
+ Objects.requireNonNull(time, "time");
+ return new LocalDateTime(date, time);
+ }
+
+ //-------------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code LocalDateTime} from an {@code Instant} and zone ID.
+ * <p>
+ * This creates a local date-time based on the specified instant.
+ * First, the offset from UTC/Greenwich is obtained using the zone ID and instant,
+ * which is simple as there is only one valid offset for each instant.
+ * Then, the instant and offset are used to calculate the local date-time.
+ *
+ * @param instant the instant to create the date-time from, not null
+ * @param zone the time-zone, which may be an offset, not null
+ * @return the local date-time, not null
+ * @throws DateTimeException if the result exceeds the supported range
+ */
+ public static LocalDateTime ofInstant(Instant instant, ZoneId zone) {
+ Objects.requireNonNull(instant, "instant");
+ Objects.requireNonNull(zone, "zone");
+ ZoneRules rules = zone.getRules();
+ ZoneOffset offset = rules.getOffset(instant);
+ return ofEpochSecond(instant.getEpochSecond(), instant.getNano(), offset);
+ }
+
+ /**
+ * Obtains an instance of {@code LocalDateTime} using seconds from the
+ * epoch of 1970-01-01T00:00:00Z.
+ * <p>
+ * This allows the {@link ChronoField#INSTANT_SECONDS epoch-second} field
+ * to be converted to a local date-time. This is primarily intended for
+ * low-level conversions rather than general application usage.
+ *
+ * @param epochSecond the number of seconds from the epoch of 1970-01-01T00:00:00Z
+ * @param nanoOfSecond the nanosecond within the second, from 0 to 999,999,999
+ * @param offset the zone offset, not null
+ * @return the local date-time, not null
+ * @throws DateTimeException if the result exceeds the supported range,
+ * or if the nano-of-second is invalid
+ */
+ public static LocalDateTime ofEpochSecond(long epochSecond, int nanoOfSecond, ZoneOffset offset) {
+ Objects.requireNonNull(offset, "offset");
+ NANO_OF_SECOND.checkValidValue(nanoOfSecond);
+ long localSecond = epochSecond + offset.getTotalSeconds(); // overflow caught later
+ long localEpochDay = Math.floorDiv(localSecond, SECONDS_PER_DAY);
+ int secsOfDay = (int)Math.floorMod(localSecond, SECONDS_PER_DAY);
+ LocalDate date = LocalDate.ofEpochDay(localEpochDay);
+ LocalTime time = LocalTime.ofNanoOfDay(secsOfDay * NANOS_PER_SECOND + nanoOfSecond);
+ return new LocalDateTime(date, time);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code LocalDateTime} from a temporal object.
+ * <p>
+ * This obtains a local date-time based on the specified temporal.
+ * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
+ * which this factory converts to an instance of {@code LocalDateTime}.
+ * <p>
+ * The conversion extracts and combines the {@code LocalDate} and the
+ * {@code LocalTime} from the temporal object.
+ * Implementations are permitted to perform optimizations such as accessing
+ * those fields that are equivalent to the relevant objects.
+ * <p>
+ * This method matches the signature of the functional interface {@link TemporalQuery}
+ * allowing it to be used as a query via method reference, {@code LocalDateTime::from}.
+ *
+ * @param temporal the temporal object to convert, not null
+ * @return the local date-time, not null
+ * @throws DateTimeException if unable to convert to a {@code LocalDateTime}
+ */
+ public static LocalDateTime from(TemporalAccessor temporal) {
+ if (temporal instanceof LocalDateTime) {
+ return (LocalDateTime) temporal;
+ } else if (temporal instanceof ZonedDateTime) {
+ return ((ZonedDateTime) temporal).toLocalDateTime();
+ } else if (temporal instanceof OffsetDateTime) {
+ return ((OffsetDateTime) temporal).toLocalDateTime();
+ }
+ try {
+ LocalDate date = LocalDate.from(temporal);
+ LocalTime time = LocalTime.from(temporal);
+ return new LocalDateTime(date, time);
+ } catch (DateTimeException ex) {
+ throw new DateTimeException("Unable to obtain LocalDateTime from TemporalAccessor: " +
+ temporal + " of type " + temporal.getClass().getName(), ex);
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code LocalDateTime} from a text string such as {@code 2007-12-03T10:15:30}.
+ * <p>
+ * The string must represent a valid date-time and is parsed using
+ * {@link java.time.format.DateTimeFormatter#ISO_LOCAL_DATE_TIME}.
+ *
+ * @param text the text to parse such as "2007-12-03T10:15:30", not null
+ * @return the parsed local date-time, not null
+ * @throws DateTimeParseException if the text cannot be parsed
+ */
+ public static LocalDateTime parse(CharSequence text) {
+ return parse(text, DateTimeFormatter.ISO_LOCAL_DATE_TIME);
+ }
+
+ /**
+ * Obtains an instance of {@code LocalDateTime} from a text string using a specific formatter.
+ * <p>
+ * The text is parsed using the formatter, returning a date-time.
+ *
+ * @param text the text to parse, not null
+ * @param formatter the formatter to use, not null
+ * @return the parsed local date-time, not null
+ * @throws DateTimeParseException if the text cannot be parsed
+ */
+ public static LocalDateTime parse(CharSequence text, DateTimeFormatter formatter) {
+ Objects.requireNonNull(formatter, "formatter");
+ return formatter.parse(text, LocalDateTime::from);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Constructor.
+ *
+ * @param date the date part of the date-time, validated not null
+ * @param time the time part of the date-time, validated not null
+ */
+ private LocalDateTime(LocalDate date, LocalTime time) {
+ this.date = date;
+ this.time = time;
+ }
+
+ /**
+ * Returns a copy of this date-time with the new date and time, checking
+ * to see if a new object is in fact required.
+ *
+ * @param newDate the date of the new date-time, not null
+ * @param newTime the time of the new date-time, not null
+ * @return the date-time, not null
+ */
+ private LocalDateTime with(LocalDate newDate, LocalTime newTime) {
+ if (date == newDate && time == newTime) {
+ return this;
+ }
+ return new LocalDateTime(newDate, newTime);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if the specified field is supported.
+ * <p>
+ * This checks if this date-time can be queried for the specified field.
+ * If false, then calling the {@link #range(TemporalField) range},
+ * {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}
+ * methods will throw an exception.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The supported fields are:
+ * <ul>
+ * <li>{@code NANO_OF_SECOND}
+ * <li>{@code NANO_OF_DAY}
+ * <li>{@code MICRO_OF_SECOND}
+ * <li>{@code MICRO_OF_DAY}
+ * <li>{@code MILLI_OF_SECOND}
+ * <li>{@code MILLI_OF_DAY}
+ * <li>{@code SECOND_OF_MINUTE}
+ * <li>{@code SECOND_OF_DAY}
+ * <li>{@code MINUTE_OF_HOUR}
+ * <li>{@code MINUTE_OF_DAY}
+ * <li>{@code HOUR_OF_AMPM}
+ * <li>{@code CLOCK_HOUR_OF_AMPM}
+ * <li>{@code HOUR_OF_DAY}
+ * <li>{@code CLOCK_HOUR_OF_DAY}
+ * <li>{@code AMPM_OF_DAY}
+ * <li>{@code DAY_OF_WEEK}
+ * <li>{@code ALIGNED_DAY_OF_WEEK_IN_MONTH}
+ * <li>{@code ALIGNED_DAY_OF_WEEK_IN_YEAR}
+ * <li>{@code DAY_OF_MONTH}
+ * <li>{@code DAY_OF_YEAR}
+ * <li>{@code EPOCH_DAY}
+ * <li>{@code ALIGNED_WEEK_OF_MONTH}
+ * <li>{@code ALIGNED_WEEK_OF_YEAR}
+ * <li>{@code MONTH_OF_YEAR}
+ * <li>{@code PROLEPTIC_MONTH}
+ * <li>{@code YEAR_OF_ERA}
+ * <li>{@code YEAR}
+ * <li>{@code ERA}
+ * </ul>
+ * All other {@code ChronoField} instances will return false.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}
+ * passing {@code this} as the argument.
+ * Whether the field is supported is determined by the field.
+ *
+ * @param field the field to check, null returns false
+ * @return true if the field is supported on this date-time, false if not
+ */
+ @Override
+ public boolean isSupported(TemporalField field) {
+ if (field instanceof ChronoField) {
+ ChronoField f = (ChronoField) field;
+ return f.isDateBased() || f.isTimeBased();
+ }
+ return field != null && field.isSupportedBy(this);
+ }
+
+ /**
+ * Checks if the specified unit is supported.
+ * <p>
+ * This checks if the specified unit can be added to, or subtracted from, this date-time.
+ * If false, then calling the {@link #plus(long, TemporalUnit)} and
+ * {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
+ * <p>
+ * If the unit is a {@link ChronoUnit} then the query is implemented here.
+ * The supported units are:
+ * <ul>
+ * <li>{@code NANOS}
+ * <li>{@code MICROS}
+ * <li>{@code MILLIS}
+ * <li>{@code SECONDS}
+ * <li>{@code MINUTES}
+ * <li>{@code HOURS}
+ * <li>{@code HALF_DAYS}
+ * <li>{@code DAYS}
+ * <li>{@code WEEKS}
+ * <li>{@code MONTHS}
+ * <li>{@code YEARS}
+ * <li>{@code DECADES}
+ * <li>{@code CENTURIES}
+ * <li>{@code MILLENNIA}
+ * <li>{@code ERAS}
+ * </ul>
+ * All other {@code ChronoUnit} instances will return false.
+ * <p>
+ * If the unit is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
+ * passing {@code this} as the argument.
+ * Whether the unit is supported is determined by the unit.
+ *
+ * @param unit the unit to check, null returns false
+ * @return true if the unit can be added/subtracted, false if not
+ */
+ @Override // override for Javadoc
+ public boolean isSupported(TemporalUnit unit) {
+ return ChronoLocalDateTime.super.isSupported(unit);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the range of valid values for the specified field.
+ * <p>
+ * The range object expresses the minimum and maximum valid values for a field.
+ * This date-time is used to enhance the accuracy of the returned range.
+ * If it is not possible to return the range, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The {@link #isSupported(TemporalField) supported fields} will return
+ * appropriate range instances.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.rangeRefinedBy(TemporalAccessor)}
+ * passing {@code this} as the argument.
+ * Whether the range can be obtained is determined by the field.
+ *
+ * @param field the field to query the range for, not null
+ * @return the range of valid values for the field, not null
+ * @throws DateTimeException if the range for the field cannot be obtained
+ * @throws UnsupportedTemporalTypeException if the field is not supported
+ */
+ @Override
+ public ValueRange range(TemporalField field) {
+ if (field instanceof ChronoField) {
+ ChronoField f = (ChronoField) field;
+ return (f.isTimeBased() ? time.range(field) : date.range(field));
+ }
+ return field.rangeRefinedBy(this);
+ }
+
+ /**
+ * Gets the value of the specified field from this date-time as an {@code int}.
+ * <p>
+ * This queries this date-time for the value of the specified field.
+ * The returned value will always be within the valid range of values for the field.
+ * If it is not possible to return the value, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The {@link #isSupported(TemporalField) supported fields} will return valid
+ * values based on this date-time, except {@code NANO_OF_DAY}, {@code MICRO_OF_DAY},
+ * {@code EPOCH_DAY} and {@code PROLEPTIC_MONTH} which are too large to fit in
+ * an {@code int} and throw a {@code DateTimeException}.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
+ * passing {@code this} as the argument. Whether the value can be obtained,
+ * and what the value represents, is determined by the field.
+ *
+ * @param field the field to get, not null
+ * @return the value for the field
+ * @throws DateTimeException if a value for the field cannot be obtained or
+ * the value is outside the range of valid values for the field
+ * @throws UnsupportedTemporalTypeException if the field is not supported or
+ * the range of values exceeds an {@code int}
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public int get(TemporalField field) {
+ if (field instanceof ChronoField) {
+ ChronoField f = (ChronoField) field;
+ return (f.isTimeBased() ? time.get(field) : date.get(field));
+ }
+ return ChronoLocalDateTime.super.get(field);
+ }
+
+ /**
+ * Gets the value of the specified field from this date-time as a {@code long}.
+ * <p>
+ * This queries this date-time for the value of the specified field.
+ * If it is not possible to return the value, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The {@link #isSupported(TemporalField) supported fields} will return valid
+ * values based on this date-time.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
+ * passing {@code this} as the argument. Whether the value can be obtained,
+ * and what the value represents, is determined by the field.
+ *
+ * @param field the field to get, not null
+ * @return the value for the field
+ * @throws DateTimeException if a value for the field cannot be obtained
+ * @throws UnsupportedTemporalTypeException if the field is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public long getLong(TemporalField field) {
+ if (field instanceof ChronoField) {
+ ChronoField f = (ChronoField) field;
+ return (f.isTimeBased() ? time.getLong(field) : date.getLong(field));
+ }
+ return field.getFrom(this);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the {@code LocalDate} part of this date-time.
+ * <p>
+ * This returns a {@code LocalDate} with the same year, month and day
+ * as this date-time.
+ *
+ * @return the date part of this date-time, not null
+ */
+ @Override
+ public LocalDate toLocalDate() {
+ return date;
+ }
+
+ /**
+ * Gets the year field.
+ * <p>
+ * This method returns the primitive {@code int} value for the year.
+ * <p>
+ * The year returned by this method is proleptic as per {@code get(YEAR)}.
+ * To obtain the year-of-era, use {@code get(YEAR_OF_ERA)}.
+ *
+ * @return the year, from MIN_YEAR to MAX_YEAR
+ */
+ public int getYear() {
+ return date.getYear();
+ }
+
+ /**
+ * Gets the month-of-year field from 1 to 12.
+ * <p>
+ * This method returns the month as an {@code int} from 1 to 12.
+ * Application code is frequently clearer if the enum {@link Month}
+ * is used by calling {@link #getMonth()}.
+ *
+ * @return the month-of-year, from 1 to 12
+ * @see #getMonth()
+ */
+ public int getMonthValue() {
+ return date.getMonthValue();
+ }
+
+ /**
+ * Gets the month-of-year field using the {@code Month} enum.
+ * <p>
+ * This method returns the enum {@link Month} for the month.
+ * This avoids confusion as to what {@code int} values mean.
+ * If you need access to the primitive {@code int} value then the enum
+ * provides the {@link Month#getValue() int value}.
+ *
+ * @return the month-of-year, not null
+ * @see #getMonthValue()
+ */
+ public Month getMonth() {
+ return date.getMonth();
+ }
+
+ /**
+ * Gets the day-of-month field.
+ * <p>
+ * This method returns the primitive {@code int} value for the day-of-month.
+ *
+ * @return the day-of-month, from 1 to 31
+ */
+ public int getDayOfMonth() {
+ return date.getDayOfMonth();
+ }
+
+ /**
+ * Gets the day-of-year field.
+ * <p>
+ * This method returns the primitive {@code int} value for the day-of-year.
+ *
+ * @return the day-of-year, from 1 to 365, or 366 in a leap year
+ */
+ public int getDayOfYear() {
+ return date.getDayOfYear();
+ }
+
+ /**
+ * Gets the day-of-week field, which is an enum {@code DayOfWeek}.
+ * <p>
+ * This method returns the enum {@link DayOfWeek} for the day-of-week.
+ * This avoids confusion as to what {@code int} values mean.
+ * If you need access to the primitive {@code int} value then the enum
+ * provides the {@link DayOfWeek#getValue() int value}.
+ * <p>
+ * Additional information can be obtained from the {@code DayOfWeek}.
+ * This includes textual names of the values.
+ *
+ * @return the day-of-week, not null
+ */
+ public DayOfWeek getDayOfWeek() {
+ return date.getDayOfWeek();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the {@code LocalTime} part of this date-time.
+ * <p>
+ * This returns a {@code LocalTime} with the same hour, minute, second and
+ * nanosecond as this date-time.
+ *
+ * @return the time part of this date-time, not null
+ */
+ @Override
+ public LocalTime toLocalTime() {
+ return time;
+ }
+
+ /**
+ * Gets the hour-of-day field.
+ *
+ * @return the hour-of-day, from 0 to 23
+ */
+ public int getHour() {
+ return time.getHour();
+ }
+
+ /**
+ * Gets the minute-of-hour field.
+ *
+ * @return the minute-of-hour, from 0 to 59
+ */
+ public int getMinute() {
+ return time.getMinute();
+ }
+
+ /**
+ * Gets the second-of-minute field.
+ *
+ * @return the second-of-minute, from 0 to 59
+ */
+ public int getSecond() {
+ return time.getSecond();
+ }
+
+ /**
+ * Gets the nano-of-second field.
+ *
+ * @return the nano-of-second, from 0 to 999,999,999
+ */
+ public int getNano() {
+ return time.getNano();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns an adjusted copy of this date-time.
+ * <p>
+ * This returns a {@code LocalDateTime}, based on this one, with the date-time adjusted.
+ * The adjustment takes place using the specified adjuster strategy object.
+ * Read the documentation of the adjuster to understand what adjustment will be made.
+ * <p>
+ * A simple adjuster might simply set the one of the fields, such as the year field.
+ * A more complex adjuster might set the date to the last day of the month.
+ * <p>
+ * A selection of common adjustments is provided in
+ * {@link java.time.temporal.TemporalAdjusters TemporalAdjusters}.
+ * These include finding the "last day of the month" and "next Wednesday".
+ * Key date-time classes also implement the {@code TemporalAdjuster} interface,
+ * such as {@link Month} and {@link java.time.MonthDay MonthDay}.
+ * The adjuster is responsible for handling special cases, such as the varying
+ * lengths of month and leap years.
+ * <p>
+ * For example this code returns a date on the last day of July:
+ * <pre>
+ * import static java.time.Month.*;
+ * import static java.time.temporal.TemporalAdjusters.*;
+ *
+ * result = localDateTime.with(JULY).with(lastDayOfMonth());
+ * </pre>
+ * <p>
+ * The classes {@link LocalDate} and {@link LocalTime} implement {@code TemporalAdjuster},
+ * thus this method can be used to change the date, time or offset:
+ * <pre>
+ * result = localDateTime.with(date);
+ * result = localDateTime.with(time);
+ * </pre>
+ * <p>
+ * The result of this method is obtained by invoking the
+ * {@link TemporalAdjuster#adjustInto(Temporal)} method on the
+ * specified adjuster passing {@code this} as the argument.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param adjuster the adjuster to use, not null
+ * @return a {@code LocalDateTime} based on {@code this} with the adjustment made, not null
+ * @throws DateTimeException if the adjustment cannot be made
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public LocalDateTime with(TemporalAdjuster adjuster) {
+ // optimizations
+ if (adjuster instanceof LocalDate) {
+ return with((LocalDate) adjuster, time);
+ } else if (adjuster instanceof LocalTime) {
+ return with(date, (LocalTime) adjuster);
+ } else if (adjuster instanceof LocalDateTime) {
+ return (LocalDateTime) adjuster;
+ }
+ return (LocalDateTime) adjuster.adjustInto(this);
+ }
+
+ /**
+ * Returns a copy of this date-time with the specified field set to a new value.
+ * <p>
+ * This returns a {@code LocalDateTime}, based on this one, with the value
+ * for the specified field changed.
+ * This can be used to change any supported field, such as the year, month or day-of-month.
+ * If it is not possible to set the value, because the field is not supported or for
+ * some other reason, an exception is thrown.
+ * <p>
+ * In some cases, changing the specified field can cause the resulting date-time to become invalid,
+ * such as changing the month from 31st January to February would make the day-of-month invalid.
+ * In cases like this, the field is responsible for resolving the date. Typically it will choose
+ * the previous valid date, which would be the last valid day of February in this example.
+ * <p>
+ * If the field is a {@link ChronoField} then the adjustment is implemented here.
+ * The {@link #isSupported(TemporalField) supported fields} will behave as per
+ * the matching method on {@link LocalDate#with(TemporalField, long) LocalDate}
+ * or {@link LocalTime#with(TemporalField, long) LocalTime}.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.adjustInto(Temporal, long)}
+ * passing {@code this} as the argument. In this case, the field determines
+ * whether and how to adjust the instant.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param field the field to set in the result, not null
+ * @param newValue the new value of the field in the result
+ * @return a {@code LocalDateTime} based on {@code this} with the specified field set, not null
+ * @throws DateTimeException if the field cannot be set
+ * @throws UnsupportedTemporalTypeException if the field is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public LocalDateTime with(TemporalField field, long newValue) {
+ if (field instanceof ChronoField) {
+ ChronoField f = (ChronoField) field;
+ if (f.isTimeBased()) {
+ return with(date, time.with(field, newValue));
+ } else {
+ return with(date.with(field, newValue), time);
+ }
+ }
+ return field.adjustInto(this, newValue);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this {@code LocalDateTime} with the year altered.
+ * <p>
+ * The time does not affect the calculation and will be the same in the result.
+ * If the day-of-month is invalid for the year, it will be changed to the last valid day of the month.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param year the year to set in the result, from MIN_YEAR to MAX_YEAR
+ * @return a {@code LocalDateTime} based on this date-time with the requested year, not null
+ * @throws DateTimeException if the year value is invalid
+ */
+ public LocalDateTime withYear(int year) {
+ return with(date.withYear(year), time);
+ }
+
+ /**
+ * Returns a copy of this {@code LocalDateTime} with the month-of-year altered.
+ * <p>
+ * The time does not affect the calculation and will be the same in the result.
+ * If the day-of-month is invalid for the year, it will be changed to the last valid day of the month.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param month the month-of-year to set in the result, from 1 (January) to 12 (December)
+ * @return a {@code LocalDateTime} based on this date-time with the requested month, not null
+ * @throws DateTimeException if the month-of-year value is invalid
+ */
+ public LocalDateTime withMonth(int month) {
+ return with(date.withMonth(month), time);
+ }
+
+ /**
+ * Returns a copy of this {@code LocalDateTime} with the day-of-month altered.
+ * <p>
+ * If the resulting date-time is invalid, an exception is thrown.
+ * The time does not affect the calculation and will be the same in the result.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param dayOfMonth the day-of-month to set in the result, from 1 to 28-31
+ * @return a {@code LocalDateTime} based on this date-time with the requested day, not null
+ * @throws DateTimeException if the day-of-month value is invalid,
+ * or if the day-of-month is invalid for the month-year
+ */
+ public LocalDateTime withDayOfMonth(int dayOfMonth) {
+ return with(date.withDayOfMonth(dayOfMonth), time);
+ }
+
+ /**
+ * Returns a copy of this {@code LocalDateTime} with the day-of-year altered.
+ * <p>
+ * If the resulting date-time is invalid, an exception is thrown.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param dayOfYear the day-of-year to set in the result, from 1 to 365-366
+ * @return a {@code LocalDateTime} based on this date with the requested day, not null
+ * @throws DateTimeException if the day-of-year value is invalid,
+ * or if the day-of-year is invalid for the year
+ */
+ public LocalDateTime withDayOfYear(int dayOfYear) {
+ return with(date.withDayOfYear(dayOfYear), time);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this {@code LocalDateTime} with the hour-of-day altered.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param hour the hour-of-day to set in the result, from 0 to 23
+ * @return a {@code LocalDateTime} based on this date-time with the requested hour, not null
+ * @throws DateTimeException if the hour value is invalid
+ */
+ public LocalDateTime withHour(int hour) {
+ LocalTime newTime = time.withHour(hour);
+ return with(date, newTime);
+ }
+
+ /**
+ * Returns a copy of this {@code LocalDateTime} with the minute-of-hour altered.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param minute the minute-of-hour to set in the result, from 0 to 59
+ * @return a {@code LocalDateTime} based on this date-time with the requested minute, not null
+ * @throws DateTimeException if the minute value is invalid
+ */
+ public LocalDateTime withMinute(int minute) {
+ LocalTime newTime = time.withMinute(minute);
+ return with(date, newTime);
+ }
+
+ /**
+ * Returns a copy of this {@code LocalDateTime} with the second-of-minute altered.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param second the second-of-minute to set in the result, from 0 to 59
+ * @return a {@code LocalDateTime} based on this date-time with the requested second, not null
+ * @throws DateTimeException if the second value is invalid
+ */
+ public LocalDateTime withSecond(int second) {
+ LocalTime newTime = time.withSecond(second);
+ return with(date, newTime);
+ }
+
+ /**
+ * Returns a copy of this {@code LocalDateTime} with the nano-of-second altered.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param nanoOfSecond the nano-of-second to set in the result, from 0 to 999,999,999
+ * @return a {@code LocalDateTime} based on this date-time with the requested nanosecond, not null
+ * @throws DateTimeException if the nano value is invalid
+ */
+ public LocalDateTime withNano(int nanoOfSecond) {
+ LocalTime newTime = time.withNano(nanoOfSecond);
+ return with(date, newTime);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this {@code LocalDateTime} with the time truncated.
+ * <p>
+ * Truncation returns a copy of the original date-time with fields
+ * smaller than the specified unit set to zero.
+ * For example, truncating with the {@link ChronoUnit#MINUTES minutes} unit
+ * will set the second-of-minute and nano-of-second field to zero.
+ * <p>
+ * The unit must have a {@linkplain TemporalUnit#getDuration() duration}
+ * that divides into the length of a standard day without remainder.
+ * This includes all supplied time units on {@link ChronoUnit} and
+ * {@link ChronoUnit#DAYS DAYS}. Other units throw an exception.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param unit the unit to truncate to, not null
+ * @return a {@code LocalDateTime} based on this date-time with the time truncated, not null
+ * @throws DateTimeException if unable to truncate
+ * @throws UnsupportedTemporalTypeException if the unit is not supported
+ */
+ public LocalDateTime truncatedTo(TemporalUnit unit) {
+ return with(date, time.truncatedTo(unit));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this date-time with the specified amount added.
+ * <p>
+ * This returns a {@code LocalDateTime}, based on this one, with the specified amount added.
+ * The amount is typically {@link Period} or {@link Duration} but may be
+ * any other type implementing the {@link TemporalAmount} interface.
+ * <p>
+ * The calculation is delegated to the amount object by calling
+ * {@link TemporalAmount#addTo(Temporal)}. The amount implementation is free
+ * to implement the addition in any way it wishes, however it typically
+ * calls back to {@link #plus(long, TemporalUnit)}. Consult the documentation
+ * of the amount implementation to determine if it can be successfully added.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param amountToAdd the amount to add, not null
+ * @return a {@code LocalDateTime} based on this date-time with the addition made, not null
+ * @throws DateTimeException if the addition cannot be made
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public LocalDateTime plus(TemporalAmount amountToAdd) {
+ if (amountToAdd instanceof Period) {
+ Period periodToAdd = (Period) amountToAdd;
+ return with(date.plus(periodToAdd), time);
+ }
+ Objects.requireNonNull(amountToAdd, "amountToAdd");
+ return (LocalDateTime) amountToAdd.addTo(this);
+ }
+
+ /**
+ * Returns a copy of this date-time with the specified amount added.
+ * <p>
+ * This returns a {@code LocalDateTime}, based on this one, with the amount
+ * in terms of the unit added. If it is not possible to add the amount, because the
+ * unit is not supported or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoUnit} then the addition is implemented here.
+ * Date units are added as per {@link LocalDate#plus(long, TemporalUnit)}.
+ * Time units are added as per {@link LocalTime#plus(long, TemporalUnit)} with
+ * any overflow in days added equivalent to using {@link #plusDays(long)}.
+ * <p>
+ * If the field is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.addTo(Temporal, long)}
+ * passing {@code this} as the argument. In this case, the unit determines
+ * whether and how to perform the addition.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param amountToAdd the amount of the unit to add to the result, may be negative
+ * @param unit the unit of the amount to add, not null
+ * @return a {@code LocalDateTime} based on this date-time with the specified amount added, not null
+ * @throws DateTimeException if the addition cannot be made
+ * @throws UnsupportedTemporalTypeException if the unit is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public LocalDateTime plus(long amountToAdd, TemporalUnit unit) {
+ if (unit instanceof ChronoUnit) {
+ ChronoUnit f = (ChronoUnit) unit;
+ switch (f) {
+ case NANOS: return plusNanos(amountToAdd);
+ case MICROS: return plusDays(amountToAdd / MICROS_PER_DAY).plusNanos((amountToAdd % MICROS_PER_DAY) * 1000);
+ case MILLIS: return plusDays(amountToAdd / MILLIS_PER_DAY).plusNanos((amountToAdd % MILLIS_PER_DAY) * 1000_000);
+ case SECONDS: return plusSeconds(amountToAdd);
+ case MINUTES: return plusMinutes(amountToAdd);
+ case HOURS: return plusHours(amountToAdd);
+ case HALF_DAYS: return plusDays(amountToAdd / 256).plusHours((amountToAdd % 256) * 12); // no overflow (256 is multiple of 2)
+ }
+ return with(date.plus(amountToAdd, unit), time);
+ }
+ return unit.addTo(this, amountToAdd);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this {@code LocalDateTime} with the specified number of years added.
+ * <p>
+ * This method adds the specified amount to the years field in three steps:
+ * <ol>
+ * <li>Add the input years to the year field</li>
+ * <li>Check if the resulting date would be invalid</li>
+ * <li>Adjust the day-of-month to the last valid day if necessary</li>
+ * </ol>
+ * <p>
+ * For example, 2008-02-29 (leap year) plus one year would result in the
+ * invalid date 2009-02-29 (standard year). Instead of returning an invalid
+ * result, the last valid day of the month, 2009-02-28, is selected instead.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param years the years to add, may be negative
+ * @return a {@code LocalDateTime} based on this date-time with the years added, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public LocalDateTime plusYears(long years) {
+ LocalDate newDate = date.plusYears(years);
+ return with(newDate, time);
+ }
+
+ /**
+ * Returns a copy of this {@code LocalDateTime} with the specified number of months added.
+ * <p>
+ * This method adds the specified amount to the months field in three steps:
+ * <ol>
+ * <li>Add the input months to the month-of-year field</li>
+ * <li>Check if the resulting date would be invalid</li>
+ * <li>Adjust the day-of-month to the last valid day if necessary</li>
+ * </ol>
+ * <p>
+ * For example, 2007-03-31 plus one month would result in the invalid date
+ * 2007-04-31. Instead of returning an invalid result, the last valid day
+ * of the month, 2007-04-30, is selected instead.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param months the months to add, may be negative
+ * @return a {@code LocalDateTime} based on this date-time with the months added, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public LocalDateTime plusMonths(long months) {
+ LocalDate newDate = date.plusMonths(months);
+ return with(newDate, time);
+ }
+
+ /**
+ * Returns a copy of this {@code LocalDateTime} with the specified number of weeks added.
+ * <p>
+ * This method adds the specified amount in weeks to the days field incrementing
+ * the month and year fields as necessary to ensure the result remains valid.
+ * The result is only invalid if the maximum/minimum year is exceeded.
+ * <p>
+ * For example, 2008-12-31 plus one week would result in 2009-01-07.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param weeks the weeks to add, may be negative
+ * @return a {@code LocalDateTime} based on this date-time with the weeks added, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public LocalDateTime plusWeeks(long weeks) {
+ LocalDate newDate = date.plusWeeks(weeks);
+ return with(newDate, time);
+ }
+
+ /**
+ * Returns a copy of this {@code LocalDateTime} with the specified number of days added.
+ * <p>
+ * This method adds the specified amount to the days field incrementing the
+ * month and year fields as necessary to ensure the result remains valid.
+ * The result is only invalid if the maximum/minimum year is exceeded.
+ * <p>
+ * For example, 2008-12-31 plus one day would result in 2009-01-01.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param days the days to add, may be negative
+ * @return a {@code LocalDateTime} based on this date-time with the days added, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public LocalDateTime plusDays(long days) {
+ LocalDate newDate = date.plusDays(days);
+ return with(newDate, time);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this {@code LocalDateTime} with the specified number of hours added.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param hours the hours to add, may be negative
+ * @return a {@code LocalDateTime} based on this date-time with the hours added, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public LocalDateTime plusHours(long hours) {
+ return plusWithOverflow(date, hours, 0, 0, 0, 1);
+ }
+
+ /**
+ * Returns a copy of this {@code LocalDateTime} with the specified number of minutes added.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param minutes the minutes to add, may be negative
+ * @return a {@code LocalDateTime} based on this date-time with the minutes added, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public LocalDateTime plusMinutes(long minutes) {
+ return plusWithOverflow(date, 0, minutes, 0, 0, 1);
+ }
+
+ /**
+ * Returns a copy of this {@code LocalDateTime} with the specified number of seconds added.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param seconds the seconds to add, may be negative
+ * @return a {@code LocalDateTime} based on this date-time with the seconds added, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public LocalDateTime plusSeconds(long seconds) {
+ return plusWithOverflow(date, 0, 0, seconds, 0, 1);
+ }
+
+ /**
+ * Returns a copy of this {@code LocalDateTime} with the specified number of nanoseconds added.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param nanos the nanos to add, may be negative
+ * @return a {@code LocalDateTime} based on this date-time with the nanoseconds added, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public LocalDateTime plusNanos(long nanos) {
+ return plusWithOverflow(date, 0, 0, 0, nanos, 1);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this date-time with the specified amount subtracted.
+ * <p>
+ * This returns a {@code LocalDateTime}, based on this one, with the specified amount subtracted.
+ * The amount is typically {@link Period} or {@link Duration} but may be
+ * any other type implementing the {@link TemporalAmount} interface.
+ * <p>
+ * The calculation is delegated to the amount object by calling
+ * {@link TemporalAmount#subtractFrom(Temporal)}. The amount implementation is free
+ * to implement the subtraction in any way it wishes, however it typically
+ * calls back to {@link #minus(long, TemporalUnit)}. Consult the documentation
+ * of the amount implementation to determine if it can be successfully subtracted.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param amountToSubtract the amount to subtract, not null
+ * @return a {@code LocalDateTime} based on this date-time with the subtraction made, not null
+ * @throws DateTimeException if the subtraction cannot be made
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public LocalDateTime minus(TemporalAmount amountToSubtract) {
+ if (amountToSubtract instanceof Period) {
+ Period periodToSubtract = (Period) amountToSubtract;
+ return with(date.minus(periodToSubtract), time);
+ }
+ Objects.requireNonNull(amountToSubtract, "amountToSubtract");
+ return (LocalDateTime) amountToSubtract.subtractFrom(this);
+ }
+
+ /**
+ * Returns a copy of this date-time with the specified amount subtracted.
+ * <p>
+ * This returns a {@code LocalDateTime}, based on this one, with the amount
+ * in terms of the unit subtracted. If it is not possible to subtract the amount,
+ * because the unit is not supported or for some other reason, an exception is thrown.
+ * <p>
+ * This method is equivalent to {@link #plus(long, TemporalUnit)} with the amount negated.
+ * See that method for a full description of how addition, and thus subtraction, works.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param amountToSubtract the amount of the unit to subtract from the result, may be negative
+ * @param unit the unit of the amount to subtract, not null
+ * @return a {@code LocalDateTime} based on this date-time with the specified amount subtracted, not null
+ * @throws DateTimeException if the subtraction cannot be made
+ * @throws UnsupportedTemporalTypeException if the unit is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public LocalDateTime minus(long amountToSubtract, TemporalUnit unit) {
+ return (amountToSubtract == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this {@code LocalDateTime} with the specified number of years subtracted.
+ * <p>
+ * This method subtracts the specified amount from the years field in three steps:
+ * <ol>
+ * <li>Subtract the input years from the year field</li>
+ * <li>Check if the resulting date would be invalid</li>
+ * <li>Adjust the day-of-month to the last valid day if necessary</li>
+ * </ol>
+ * <p>
+ * For example, 2008-02-29 (leap year) minus one year would result in the
+ * invalid date 2009-02-29 (standard year). Instead of returning an invalid
+ * result, the last valid day of the month, 2009-02-28, is selected instead.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param years the years to subtract, may be negative
+ * @return a {@code LocalDateTime} based on this date-time with the years subtracted, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public LocalDateTime minusYears(long years) {
+ return (years == Long.MIN_VALUE ? plusYears(Long.MAX_VALUE).plusYears(1) : plusYears(-years));
+ }
+
+ /**
+ * Returns a copy of this {@code LocalDateTime} with the specified number of months subtracted.
+ * <p>
+ * This method subtracts the specified amount from the months field in three steps:
+ * <ol>
+ * <li>Subtract the input months from the month-of-year field</li>
+ * <li>Check if the resulting date would be invalid</li>
+ * <li>Adjust the day-of-month to the last valid day if necessary</li>
+ * </ol>
+ * <p>
+ * For example, 2007-03-31 minus one month would result in the invalid date
+ * 2007-04-31. Instead of returning an invalid result, the last valid day
+ * of the month, 2007-04-30, is selected instead.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param months the months to subtract, may be negative
+ * @return a {@code LocalDateTime} based on this date-time with the months subtracted, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public LocalDateTime minusMonths(long months) {
+ return (months == Long.MIN_VALUE ? plusMonths(Long.MAX_VALUE).plusMonths(1) : plusMonths(-months));
+ }
+
+ /**
+ * Returns a copy of this {@code LocalDateTime} with the specified number of weeks subtracted.
+ * <p>
+ * This method subtracts the specified amount in weeks from the days field decrementing
+ * the month and year fields as necessary to ensure the result remains valid.
+ * The result is only invalid if the maximum/minimum year is exceeded.
+ * <p>
+ * For example, 2009-01-07 minus one week would result in 2008-12-31.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param weeks the weeks to subtract, may be negative
+ * @return a {@code LocalDateTime} based on this date-time with the weeks subtracted, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public LocalDateTime minusWeeks(long weeks) {
+ return (weeks == Long.MIN_VALUE ? plusWeeks(Long.MAX_VALUE).plusWeeks(1) : plusWeeks(-weeks));
+ }
+
+ /**
+ * Returns a copy of this {@code LocalDateTime} with the specified number of days subtracted.
+ * <p>
+ * This method subtracts the specified amount from the days field decrementing the
+ * month and year fields as necessary to ensure the result remains valid.
+ * The result is only invalid if the maximum/minimum year is exceeded.
+ * <p>
+ * For example, 2009-01-01 minus one day would result in 2008-12-31.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param days the days to subtract, may be negative
+ * @return a {@code LocalDateTime} based on this date-time with the days subtracted, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public LocalDateTime minusDays(long days) {
+ return (days == Long.MIN_VALUE ? plusDays(Long.MAX_VALUE).plusDays(1) : plusDays(-days));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this {@code LocalDateTime} with the specified number of hours subtracted.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param hours the hours to subtract, may be negative
+ * @return a {@code LocalDateTime} based on this date-time with the hours subtracted, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public LocalDateTime minusHours(long hours) {
+ return plusWithOverflow(date, hours, 0, 0, 0, -1);
+ }
+
+ /**
+ * Returns a copy of this {@code LocalDateTime} with the specified number of minutes subtracted.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param minutes the minutes to subtract, may be negative
+ * @return a {@code LocalDateTime} based on this date-time with the minutes subtracted, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public LocalDateTime minusMinutes(long minutes) {
+ return plusWithOverflow(date, 0, minutes, 0, 0, -1);
+ }
+
+ /**
+ * Returns a copy of this {@code LocalDateTime} with the specified number of seconds subtracted.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param seconds the seconds to subtract, may be negative
+ * @return a {@code LocalDateTime} based on this date-time with the seconds subtracted, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public LocalDateTime minusSeconds(long seconds) {
+ return plusWithOverflow(date, 0, 0, seconds, 0, -1);
+ }
+
+ /**
+ * Returns a copy of this {@code LocalDateTime} with the specified number of nanoseconds subtracted.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param nanos the nanos to subtract, may be negative
+ * @return a {@code LocalDateTime} based on this date-time with the nanoseconds subtracted, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public LocalDateTime minusNanos(long nanos) {
+ return plusWithOverflow(date, 0, 0, 0, nanos, -1);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this {@code LocalDateTime} with the specified period added.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param newDate the new date to base the calculation on, not null
+ * @param hours the hours to add, may be negative
+ * @param minutes the minutes to add, may be negative
+ * @param seconds the seconds to add, may be negative
+ * @param nanos the nanos to add, may be negative
+ * @param sign the sign to determine add or subtract
+ * @return the combined result, not null
+ */
+ private LocalDateTime plusWithOverflow(LocalDate newDate, long hours, long minutes, long seconds, long nanos, int sign) {
+ // 9223372036854775808 long, 2147483648 int
+ if ((hours | minutes | seconds | nanos) == 0) {
+ return with(newDate, time);
+ }
+ long totDays = nanos / NANOS_PER_DAY + // max/24*60*60*1B
+ seconds / SECONDS_PER_DAY + // max/24*60*60
+ minutes / MINUTES_PER_DAY + // max/24*60
+ hours / HOURS_PER_DAY; // max/24
+ totDays *= sign; // total max*0.4237...
+ long totNanos = nanos % NANOS_PER_DAY + // max 86400000000000
+ (seconds % SECONDS_PER_DAY) * NANOS_PER_SECOND + // max 86400000000000
+ (minutes % MINUTES_PER_DAY) * NANOS_PER_MINUTE + // max 86400000000000
+ (hours % HOURS_PER_DAY) * NANOS_PER_HOUR; // max 86400000000000
+ long curNoD = time.toNanoOfDay(); // max 86400000000000
+ totNanos = totNanos * sign + curNoD; // total 432000000000000
+ totDays += Math.floorDiv(totNanos, NANOS_PER_DAY);
+ long newNoD = Math.floorMod(totNanos, NANOS_PER_DAY);
+ LocalTime newTime = (newNoD == curNoD ? time : LocalTime.ofNanoOfDay(newNoD));
+ return with(newDate.plusDays(totDays), newTime);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Queries this date-time using the specified query.
+ * <p>
+ * This queries this date-time using the specified query strategy object.
+ * The {@code TemporalQuery} object defines the logic to be used to
+ * obtain the result. Read the documentation of the query to understand
+ * what the result of this method will be.
+ * <p>
+ * The result of this method is obtained by invoking the
+ * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the
+ * specified query passing {@code this} as the argument.
+ *
+ * @param <R> the type of the result
+ * @param query the query to invoke, not null
+ * @return the query result, null may be returned (defined by the query)
+ * @throws DateTimeException if unable to query (defined by the query)
+ * @throws ArithmeticException if numeric overflow occurs (defined by the query)
+ */
+ @SuppressWarnings("unchecked")
+ @Override // override for Javadoc
+ public <R> R query(TemporalQuery<R> query) {
+ if (query == TemporalQueries.localDate()) {
+ return (R) date;
+ }
+ return ChronoLocalDateTime.super.query(query);
+ }
+
+ /**
+ * Adjusts the specified temporal object to have the same date and time as this object.
+ * <p>
+ * This returns a temporal object of the same observable type as the input
+ * with the date and time changed to be the same as this.
+ * <p>
+ * The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)}
+ * twice, passing {@link ChronoField#EPOCH_DAY} and
+ * {@link ChronoField#NANO_OF_DAY} as the fields.
+ * <p>
+ * In most cases, it is clearer to reverse the calling pattern by using
+ * {@link Temporal#with(TemporalAdjuster)}:
+ * <pre>
+ * // these two lines are equivalent, but the second approach is recommended
+ * temporal = thisLocalDateTime.adjustInto(temporal);
+ * temporal = temporal.with(thisLocalDateTime);
+ * </pre>
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param temporal the target object to be adjusted, not null
+ * @return the adjusted object, not null
+ * @throws DateTimeException if unable to make the adjustment
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override // override for Javadoc
+ public Temporal adjustInto(Temporal temporal) {
+ return ChronoLocalDateTime.super.adjustInto(temporal);
+ }
+
+ /**
+ * Calculates the amount of time until another date-time in terms of the specified unit.
+ * <p>
+ * This calculates the amount of time between two {@code LocalDateTime}
+ * objects in terms of a single {@code TemporalUnit}.
+ * The start and end points are {@code this} and the specified date-time.
+ * The result will be negative if the end is before the start.
+ * The {@code Temporal} passed to this method is converted to a
+ * {@code LocalDateTime} using {@link #from(TemporalAccessor)}.
+ * For example, the amount in days between two date-times can be calculated
+ * using {@code startDateTime.until(endDateTime, DAYS)}.
+ * <p>
+ * The calculation returns a whole number, representing the number of
+ * complete units between the two date-times.
+ * For example, the amount in months between 2012-06-15T00:00 and 2012-08-14T23:59
+ * will only be one month as it is one minute short of two months.
+ * <p>
+ * There are two equivalent ways of using this method.
+ * The first is to invoke this method.
+ * The second is to use {@link TemporalUnit#between(Temporal, Temporal)}:
+ * <pre>
+ * // these two lines are equivalent
+ * amount = start.until(end, MONTHS);
+ * amount = MONTHS.between(start, end);
+ * </pre>
+ * The choice should be made based on which makes the code more readable.
+ * <p>
+ * The calculation is implemented in this method for {@link ChronoUnit}.
+ * The units {@code NANOS}, {@code MICROS}, {@code MILLIS}, {@code SECONDS},
+ * {@code MINUTES}, {@code HOURS} and {@code HALF_DAYS}, {@code DAYS},
+ * {@code WEEKS}, {@code MONTHS}, {@code YEARS}, {@code DECADES},
+ * {@code CENTURIES}, {@code MILLENNIA} and {@code ERAS} are supported.
+ * Other {@code ChronoUnit} values will throw an exception.
+ * <p>
+ * If the unit is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.between(Temporal, Temporal)}
+ * passing {@code this} as the first argument and the converted input temporal
+ * as the second argument.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param endExclusive the end date, exclusive, which is converted to a {@code LocalDateTime}, not null
+ * @param unit the unit to measure the amount in, not null
+ * @return the amount of time between this date-time and the end date-time
+ * @throws DateTimeException if the amount cannot be calculated, or the end
+ * temporal cannot be converted to a {@code LocalDateTime}
+ * @throws UnsupportedTemporalTypeException if the unit is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public long until(Temporal endExclusive, TemporalUnit unit) {
+ LocalDateTime end = LocalDateTime.from(endExclusive);
+ if (unit instanceof ChronoUnit) {
+ if (unit.isTimeBased()) {
+ long amount = date.daysUntil(end.date);
+ if (amount == 0) {
+ return time.until(end.time, unit);
+ }
+ long timePart = end.time.toNanoOfDay() - time.toNanoOfDay();
+ if (amount > 0) {
+ amount--; // safe
+ timePart += NANOS_PER_DAY; // safe
+ } else {
+ amount++; // safe
+ timePart -= NANOS_PER_DAY; // safe
+ }
+ switch ((ChronoUnit) unit) {
+ case NANOS:
+ amount = Math.multiplyExact(amount, NANOS_PER_DAY);
+ break;
+ case MICROS:
+ amount = Math.multiplyExact(amount, MICROS_PER_DAY);
+ timePart = timePart / 1000;
+ break;
+ case MILLIS:
+ amount = Math.multiplyExact(amount, MILLIS_PER_DAY);
+ timePart = timePart / 1_000_000;
+ break;
+ case SECONDS:
+ amount = Math.multiplyExact(amount, SECONDS_PER_DAY);
+ timePart = timePart / NANOS_PER_SECOND;
+ break;
+ case MINUTES:
+ amount = Math.multiplyExact(amount, MINUTES_PER_DAY);
+ timePart = timePart / NANOS_PER_MINUTE;
+ break;
+ case HOURS:
+ amount = Math.multiplyExact(amount, HOURS_PER_DAY);
+ timePart = timePart / NANOS_PER_HOUR;
+ break;
+ case HALF_DAYS:
+ amount = Math.multiplyExact(amount, 2);
+ timePart = timePart / (NANOS_PER_HOUR * 12);
+ break;
+ }
+ return Math.addExact(amount, timePart);
+ }
+ LocalDate endDate = end.date;
+ if (endDate.isAfter(date) && end.time.isBefore(time)) {
+ endDate = endDate.minusDays(1);
+ } else if (endDate.isBefore(date) && end.time.isAfter(time)) {
+ endDate = endDate.plusDays(1);
+ }
+ return date.until(endDate, unit);
+ }
+ return unit.between(this, end);
+ }
+
+ /**
+ * Formats this date-time using the specified formatter.
+ * <p>
+ * This date-time will be passed to the formatter to produce a string.
+ *
+ * @param formatter the formatter to use, not null
+ * @return the formatted date-time string, not null
+ * @throws DateTimeException if an error occurs during printing
+ */
+ @Override // override for Javadoc and performance
+ public String format(DateTimeFormatter formatter) {
+ Objects.requireNonNull(formatter, "formatter");
+ return formatter.format(this);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Combines this date-time with an offset to create an {@code OffsetDateTime}.
+ * <p>
+ * This returns an {@code OffsetDateTime} formed from this date-time at the specified offset.
+ * All possible combinations of date-time and offset are valid.
+ *
+ * @param offset the offset to combine with, not null
+ * @return the offset date-time formed from this date-time and the specified offset, not null
+ */
+ public OffsetDateTime atOffset(ZoneOffset offset) {
+ return OffsetDateTime.of(this, offset);
+ }
+
+ /**
+ * Combines this date-time with a time-zone to create a {@code ZonedDateTime}.
+ * <p>
+ * This returns a {@code ZonedDateTime} formed from this date-time at the
+ * specified time-zone. The result will match this date-time as closely as possible.
+ * Time-zone rules, such as daylight savings, mean that not every local date-time
+ * is valid for the specified zone, thus the local date-time may be adjusted.
+ * <p>
+ * The local date-time is resolved to a single instant on the time-line.
+ * This is achieved by finding a valid offset from UTC/Greenwich for the local
+ * date-time as defined by the {@link ZoneRules rules} of the zone ID.
+ *<p>
+ * In most cases, there is only one valid offset for a local date-time.
+ * In the case of an overlap, where clocks are set back, there are two valid offsets.
+ * This method uses the earlier offset typically corresponding to "summer".
+ * <p>
+ * In the case of a gap, where clocks jump forward, there is no valid offset.
+ * Instead, the local date-time is adjusted to be later by the length of the gap.
+ * For a typical one hour daylight savings change, the local date-time will be
+ * moved one hour later into the offset typically corresponding to "summer".
+ * <p>
+ * To obtain the later offset during an overlap, call
+ * {@link ZonedDateTime#withLaterOffsetAtOverlap()} on the result of this method.
+ * To throw an exception when there is a gap or overlap, use
+ * {@link ZonedDateTime#ofStrict(LocalDateTime, ZoneOffset, ZoneId)}.
+ *
+ * @param zone the time-zone to use, not null
+ * @return the zoned date-time formed from this date-time, not null
+ */
+ @Override
+ public ZonedDateTime atZone(ZoneId zone) {
+ return ZonedDateTime.of(this, zone);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Compares this date-time to another date-time.
+ * <p>
+ * The comparison is primarily based on the date-time, from earliest to latest.
+ * It is "consistent with equals", as defined by {@link Comparable}.
+ * <p>
+ * If all the date-times being compared are instances of {@code LocalDateTime},
+ * then the comparison will be entirely based on the date-time.
+ * If some dates being compared are in different chronologies, then the
+ * chronology is also considered, see {@link ChronoLocalDateTime#compareTo}.
+ *
+ * @param other the other date-time to compare to, not null
+ * @return the comparator value, negative if less, positive if greater
+ */
+ @Override // override for Javadoc and performance
+ public int compareTo(ChronoLocalDateTime<?> other) {
+ if (other instanceof LocalDateTime) {
+ return compareTo0((LocalDateTime) other);
+ }
+ return ChronoLocalDateTime.super.compareTo(other);
+ }
+
+ private int compareTo0(LocalDateTime other) {
+ int cmp = date.compareTo0(other.toLocalDate());
+ if (cmp == 0) {
+ cmp = time.compareTo(other.toLocalTime());
+ }
+ return cmp;
+ }
+
+ /**
+ * Checks if this date-time is after the specified date-time.
+ * <p>
+ * This checks to see if this date-time represents a point on the
+ * local time-line after the other date-time.
+ * <pre>
+ * LocalDate a = LocalDateTime.of(2012, 6, 30, 12, 00);
+ * LocalDate b = LocalDateTime.of(2012, 7, 1, 12, 00);
+ * a.isAfter(b) == false
+ * a.isAfter(a) == false
+ * b.isAfter(a) == true
+ * </pre>
+ * <p>
+ * This method only considers the position of the two date-times on the local time-line.
+ * It does not take into account the chronology, or calendar system.
+ * This is different from the comparison in {@link #compareTo(ChronoLocalDateTime)},
+ * but is the same approach as {@link ChronoLocalDateTime#timeLineOrder()}.
+ *
+ * @param other the other date-time to compare to, not null
+ * @return true if this date-time is after the specified date-time
+ */
+ @Override // override for Javadoc and performance
+ public boolean isAfter(ChronoLocalDateTime<?> other) {
+ if (other instanceof LocalDateTime) {
+ return compareTo0((LocalDateTime) other) > 0;
+ }
+ return ChronoLocalDateTime.super.isAfter(other);
+ }
+
+ /**
+ * Checks if this date-time is before the specified date-time.
+ * <p>
+ * This checks to see if this date-time represents a point on the
+ * local time-line before the other date-time.
+ * <pre>
+ * LocalDate a = LocalDateTime.of(2012, 6, 30, 12, 00);
+ * LocalDate b = LocalDateTime.of(2012, 7, 1, 12, 00);
+ * a.isBefore(b) == true
+ * a.isBefore(a) == false
+ * b.isBefore(a) == false
+ * </pre>
+ * <p>
+ * This method only considers the position of the two date-times on the local time-line.
+ * It does not take into account the chronology, or calendar system.
+ * This is different from the comparison in {@link #compareTo(ChronoLocalDateTime)},
+ * but is the same approach as {@link ChronoLocalDateTime#timeLineOrder()}.
+ *
+ * @param other the other date-time to compare to, not null
+ * @return true if this date-time is before the specified date-time
+ */
+ @Override // override for Javadoc and performance
+ public boolean isBefore(ChronoLocalDateTime<?> other) {
+ if (other instanceof LocalDateTime) {
+ return compareTo0((LocalDateTime) other) < 0;
+ }
+ return ChronoLocalDateTime.super.isBefore(other);
+ }
+
+ /**
+ * Checks if this date-time is equal to the specified date-time.
+ * <p>
+ * This checks to see if this date-time represents the same point on the
+ * local time-line as the other date-time.
+ * <pre>
+ * LocalDate a = LocalDateTime.of(2012, 6, 30, 12, 00);
+ * LocalDate b = LocalDateTime.of(2012, 7, 1, 12, 00);
+ * a.isEqual(b) == false
+ * a.isEqual(a) == true
+ * b.isEqual(a) == false
+ * </pre>
+ * <p>
+ * This method only considers the position of the two date-times on the local time-line.
+ * It does not take into account the chronology, or calendar system.
+ * This is different from the comparison in {@link #compareTo(ChronoLocalDateTime)},
+ * but is the same approach as {@link ChronoLocalDateTime#timeLineOrder()}.
+ *
+ * @param other the other date-time to compare to, not null
+ * @return true if this date-time is equal to the specified date-time
+ */
+ @Override // override for Javadoc and performance
+ public boolean isEqual(ChronoLocalDateTime<?> other) {
+ if (other instanceof LocalDateTime) {
+ return compareTo0((LocalDateTime) other) == 0;
+ }
+ return ChronoLocalDateTime.super.isEqual(other);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if this date-time is equal to another date-time.
+ * <p>
+ * Compares this {@code LocalDateTime} with another ensuring that the date-time is the same.
+ * Only objects of type {@code LocalDateTime} are compared, other types return false.
+ *
+ * @param obj the object to check, null returns false
+ * @return true if this is equal to the other date-time
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof LocalDateTime) {
+ LocalDateTime other = (LocalDateTime) obj;
+ return date.equals(other.date) && time.equals(other.time);
+ }
+ return false;
+ }
+
+ /**
+ * A hash code for this date-time.
+ *
+ * @return a suitable hash code
+ */
+ @Override
+ public int hashCode() {
+ return date.hashCode() ^ time.hashCode();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Outputs this date-time as a {@code String}, such as {@code 2007-12-03T10:15:30}.
+ * <p>
+ * The output will be one of the following ISO-8601 formats:
+ * <ul>
+ * <li>{@code uuuu-MM-dd'T'HH:mm}</li>
+ * <li>{@code uuuu-MM-dd'T'HH:mm:ss}</li>
+ * <li>{@code uuuu-MM-dd'T'HH:mm:ss.SSS}</li>
+ * <li>{@code uuuu-MM-dd'T'HH:mm:ss.SSSSSS}</li>
+ * <li>{@code uuuu-MM-dd'T'HH:mm:ss.SSSSSSSSS}</li>
+ * </ul>
+ * The format used will be the shortest that outputs the full value of
+ * the time where the omitted parts are implied to be zero.
+ *
+ * @return a string representation of this date-time, not null
+ */
+ @Override
+ public String toString() {
+ return date.toString() + 'T' + time.toString();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Writes the object using a
+ * <a href="../../serialized-form.html#java.time.Ser">dedicated serialized form</a>.
+ * @serialData
+ * <pre>
+ * out.writeByte(5); // identifies a LocalDateTime
+ * // the <a href="../../serialized-form.html#java.time.LocalDate">date</a> excluding the one byte header
+ * // the <a href="../../serialized-form.html#java.time.LocalTime">time</a> excluding the one byte header
+ * </pre>
+ *
+ * @return the instance of {@code Ser}, not null
+ */
+ private Object writeReplace() {
+ return new Ser(Ser.LOCAL_DATE_TIME_TYPE, this);
+ }
+
+ /**
+ * Defend against malicious streams.
+ *
+ * @param s the stream to read
+ * @throws InvalidObjectException always
+ */
+ private void readObject(ObjectInputStream s) throws InvalidObjectException {
+ throw new InvalidObjectException("Deserialization via serialization delegate");
+ }
+
+ void writeExternal(DataOutput out) throws IOException {
+ date.writeExternal(out);
+ time.writeExternal(out);
+ }
+
+ static LocalDateTime readExternal(DataInput in) throws IOException {
+ LocalDate date = LocalDate.readExternal(in);
+ LocalTime time = LocalTime.readExternal(in);
+ return LocalDateTime.of(date, time);
+ }
+
+}
diff --git a/java/time/LocalTime.java b/java/time/LocalTime.java
new file mode 100644
index 0000000..b0b1b4d
--- /dev/null
+++ b/java/time/LocalTime.java
@@ -0,0 +1,1687 @@
+/*
+ * Copyright (c) 2012, 2015, 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) 2007-2012, 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;
+
+import static java.time.temporal.ChronoField.HOUR_OF_DAY;
+import static java.time.temporal.ChronoField.MICRO_OF_DAY;
+import static java.time.temporal.ChronoField.MINUTE_OF_HOUR;
+import static java.time.temporal.ChronoField.NANO_OF_DAY;
+import static java.time.temporal.ChronoField.NANO_OF_SECOND;
+import static java.time.temporal.ChronoField.SECOND_OF_DAY;
+import static java.time.temporal.ChronoField.SECOND_OF_MINUTE;
+import static java.time.temporal.ChronoUnit.NANOS;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.time.temporal.ChronoField;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.Temporal;
+import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalAdjuster;
+import java.time.temporal.TemporalAmount;
+import java.time.temporal.TemporalField;
+import java.time.temporal.TemporalQueries;
+import java.time.temporal.TemporalQuery;
+import java.time.temporal.TemporalUnit;
+import java.time.temporal.UnsupportedTemporalTypeException;
+import java.time.temporal.ValueRange;
+import java.util.Objects;
+
+// Android-changed: removed ValueBased paragraph.
+/**
+ * A time without a time-zone in the ISO-8601 calendar system,
+ * such as {@code 10:15:30}.
+ * <p>
+ * {@code LocalTime} is an immutable date-time object that represents a time,
+ * often viewed as hour-minute-second.
+ * Time is represented to nanosecond precision.
+ * For example, the value "13:45.30.123456789" can be stored in a {@code LocalTime}.
+ * <p>
+ * This class does not store or represent a date or time-zone.
+ * Instead, it is a description of the local time as seen on a wall clock.
+ * It cannot represent an instant on the time-line without additional information
+ * such as an offset or time-zone.
+ * <p>
+ * The ISO-8601 calendar system is the modern civil calendar system used today
+ * in most of the world. This API assumes that all calendar systems use the same
+ * representation, this class, for time-of-day.
+ *
+ * @implSpec
+ * This class is immutable and thread-safe.
+ *
+ * @since 1.8
+ */
+public final class LocalTime
+ implements Temporal, TemporalAdjuster, Comparable<LocalTime>, Serializable {
+
+ /**
+ * The minimum supported {@code LocalTime}, '00:00'.
+ * This is the time of midnight at the start of the day.
+ */
+ public static final LocalTime MIN;
+ /**
+ * The maximum supported {@code LocalTime}, '23:59:59.999999999'.
+ * This is the time just before midnight at the end of the day.
+ */
+ public static final LocalTime MAX;
+ /**
+ * The time of midnight at the start of the day, '00:00'.
+ */
+ public static final LocalTime MIDNIGHT;
+ /**
+ * The time of noon in the middle of the day, '12:00'.
+ */
+ public static final LocalTime NOON;
+ /**
+ * Constants for the local time of each hour.
+ */
+ private static final LocalTime[] HOURS = new LocalTime[24];
+ static {
+ for (int i = 0; i < HOURS.length; i++) {
+ HOURS[i] = new LocalTime(i, 0, 0, 0);
+ }
+ MIDNIGHT = HOURS[0];
+ NOON = HOURS[12];
+ MIN = HOURS[0];
+ MAX = new LocalTime(23, 59, 59, 999_999_999);
+ }
+
+ /**
+ * Hours per day.
+ */
+ static final int HOURS_PER_DAY = 24;
+ /**
+ * Minutes per hour.
+ */
+ static final int MINUTES_PER_HOUR = 60;
+ /**
+ * Minutes per day.
+ */
+ static final int MINUTES_PER_DAY = MINUTES_PER_HOUR * HOURS_PER_DAY;
+ /**
+ * Seconds per minute.
+ */
+ static final int SECONDS_PER_MINUTE = 60;
+ /**
+ * Seconds per hour.
+ */
+ static final int SECONDS_PER_HOUR = SECONDS_PER_MINUTE * MINUTES_PER_HOUR;
+ /**
+ * Seconds per day.
+ */
+ static final int SECONDS_PER_DAY = SECONDS_PER_HOUR * HOURS_PER_DAY;
+ /**
+ * Milliseconds per day.
+ */
+ static final long MILLIS_PER_DAY = SECONDS_PER_DAY * 1000L;
+ /**
+ * Microseconds per day.
+ */
+ static final long MICROS_PER_DAY = SECONDS_PER_DAY * 1000_000L;
+ /**
+ * Nanos per second.
+ */
+ static final long NANOS_PER_SECOND = 1000_000_000L;
+ /**
+ * Nanos per minute.
+ */
+ static final long NANOS_PER_MINUTE = NANOS_PER_SECOND * SECONDS_PER_MINUTE;
+ /**
+ * Nanos per hour.
+ */
+ static final long NANOS_PER_HOUR = NANOS_PER_MINUTE * MINUTES_PER_HOUR;
+ /**
+ * Nanos per day.
+ */
+ static final long NANOS_PER_DAY = NANOS_PER_HOUR * HOURS_PER_DAY;
+
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = 6414437269572265201L;
+
+ /**
+ * The hour.
+ */
+ private final byte hour;
+ /**
+ * The minute.
+ */
+ private final byte minute;
+ /**
+ * The second.
+ */
+ private final byte second;
+ /**
+ * The nanosecond.
+ */
+ private final int nano;
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains the current time from the system clock in the default time-zone.
+ * <p>
+ * This will query the {@link Clock#systemDefaultZone() system clock} in the default
+ * time-zone to obtain the current time.
+ * <p>
+ * Using this method will prevent the ability to use an alternate clock for testing
+ * because the clock is hard-coded.
+ *
+ * @return the current time using the system clock and default time-zone, not null
+ */
+ public static LocalTime now() {
+ return now(Clock.systemDefaultZone());
+ }
+
+ /**
+ * Obtains the current time from the system clock in the specified time-zone.
+ * <p>
+ * This will query the {@link Clock#system(ZoneId) system clock} to obtain the current time.
+ * Specifying the time-zone avoids dependence on the default time-zone.
+ * <p>
+ * Using this method will prevent the ability to use an alternate clock for testing
+ * because the clock is hard-coded.
+ *
+ * @param zone the zone ID to use, not null
+ * @return the current time using the system clock, not null
+ */
+ public static LocalTime now(ZoneId zone) {
+ return now(Clock.system(zone));
+ }
+
+ /**
+ * Obtains the current time from the specified clock.
+ * <p>
+ * This will query the specified clock to obtain the current time.
+ * Using this method allows the use of an alternate clock for testing.
+ * The alternate clock may be introduced using {@link Clock dependency injection}.
+ *
+ * @param clock the clock to use, not null
+ * @return the current time, not null
+ */
+ public static LocalTime now(Clock clock) {
+ Objects.requireNonNull(clock, "clock");
+ // inline OffsetTime factory to avoid creating object and InstantProvider checks
+ final Instant now = clock.instant(); // called once
+ ZoneOffset offset = clock.getZone().getRules().getOffset(now);
+ long localSecond = now.getEpochSecond() + offset.getTotalSeconds(); // overflow caught later
+ int secsOfDay = (int) Math.floorMod(localSecond, SECONDS_PER_DAY);
+ return ofNanoOfDay(secsOfDay * NANOS_PER_SECOND + now.getNano());
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code LocalTime} from an hour and minute.
+ * <p>
+ * This returns a {@code LocalTime} with the specified hour and minute.
+ * The second and nanosecond fields will be set to zero.
+ *
+ * @param hour the hour-of-day to represent, from 0 to 23
+ * @param minute the minute-of-hour to represent, from 0 to 59
+ * @return the local time, not null
+ * @throws DateTimeException if the value of any field is out of range
+ */
+ public static LocalTime of(int hour, int minute) {
+ HOUR_OF_DAY.checkValidValue(hour);
+ if (minute == 0) {
+ return HOURS[hour]; // for performance
+ }
+ MINUTE_OF_HOUR.checkValidValue(minute);
+ return new LocalTime(hour, minute, 0, 0);
+ }
+
+ /**
+ * Obtains an instance of {@code LocalTime} from an hour, minute and second.
+ * <p>
+ * This returns a {@code LocalTime} with the specified hour, minute and second.
+ * The nanosecond field will be set to zero.
+ *
+ * @param hour the hour-of-day to represent, from 0 to 23
+ * @param minute the minute-of-hour to represent, from 0 to 59
+ * @param second the second-of-minute to represent, from 0 to 59
+ * @return the local time, not null
+ * @throws DateTimeException if the value of any field is out of range
+ */
+ public static LocalTime of(int hour, int minute, int second) {
+ HOUR_OF_DAY.checkValidValue(hour);
+ if ((minute | second) == 0) {
+ return HOURS[hour]; // for performance
+ }
+ MINUTE_OF_HOUR.checkValidValue(minute);
+ SECOND_OF_MINUTE.checkValidValue(second);
+ return new LocalTime(hour, minute, second, 0);
+ }
+
+ /**
+ * Obtains an instance of {@code LocalTime} from an hour, minute, second and nanosecond.
+ * <p>
+ * This returns a {@code LocalTime} with the specified hour, minute, second and nanosecond.
+ *
+ * @param hour the hour-of-day to represent, from 0 to 23
+ * @param minute the minute-of-hour to represent, from 0 to 59
+ * @param second the second-of-minute to represent, from 0 to 59
+ * @param nanoOfSecond the nano-of-second to represent, from 0 to 999,999,999
+ * @return the local time, not null
+ * @throws DateTimeException if the value of any field is out of range
+ */
+ public static LocalTime of(int hour, int minute, int second, int nanoOfSecond) {
+ HOUR_OF_DAY.checkValidValue(hour);
+ MINUTE_OF_HOUR.checkValidValue(minute);
+ SECOND_OF_MINUTE.checkValidValue(second);
+ NANO_OF_SECOND.checkValidValue(nanoOfSecond);
+ return create(hour, minute, second, nanoOfSecond);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code LocalTime} from a second-of-day value.
+ * <p>
+ * This returns a {@code LocalTime} with the specified second-of-day.
+ * The nanosecond field will be set to zero.
+ *
+ * @param secondOfDay the second-of-day, from {@code 0} to {@code 24 * 60 * 60 - 1}
+ * @return the local time, not null
+ * @throws DateTimeException if the second-of-day value is invalid
+ */
+ public static LocalTime ofSecondOfDay(long secondOfDay) {
+ SECOND_OF_DAY.checkValidValue(secondOfDay);
+ int hours = (int) (secondOfDay / SECONDS_PER_HOUR);
+ secondOfDay -= hours * SECONDS_PER_HOUR;
+ int minutes = (int) (secondOfDay / SECONDS_PER_MINUTE);
+ secondOfDay -= minutes * SECONDS_PER_MINUTE;
+ return create(hours, minutes, (int) secondOfDay, 0);
+ }
+
+ /**
+ * Obtains an instance of {@code LocalTime} from a nanos-of-day value.
+ * <p>
+ * This returns a {@code LocalTime} with the specified nanosecond-of-day.
+ *
+ * @param nanoOfDay the nano of day, from {@code 0} to {@code 24 * 60 * 60 * 1,000,000,000 - 1}
+ * @return the local time, not null
+ * @throws DateTimeException if the nanos of day value is invalid
+ */
+ public static LocalTime ofNanoOfDay(long nanoOfDay) {
+ NANO_OF_DAY.checkValidValue(nanoOfDay);
+ int hours = (int) (nanoOfDay / NANOS_PER_HOUR);
+ nanoOfDay -= hours * NANOS_PER_HOUR;
+ int minutes = (int) (nanoOfDay / NANOS_PER_MINUTE);
+ nanoOfDay -= minutes * NANOS_PER_MINUTE;
+ int seconds = (int) (nanoOfDay / NANOS_PER_SECOND);
+ nanoOfDay -= seconds * NANOS_PER_SECOND;
+ return create(hours, minutes, seconds, (int) nanoOfDay);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code LocalTime} from a temporal object.
+ * <p>
+ * This obtains a local time based on the specified temporal.
+ * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
+ * which this factory converts to an instance of {@code LocalTime}.
+ * <p>
+ * The conversion uses the {@link TemporalQueries#localTime()} query, which relies
+ * on extracting the {@link ChronoField#NANO_OF_DAY NANO_OF_DAY} field.
+ * <p>
+ * This method matches the signature of the functional interface {@link TemporalQuery}
+ * allowing it to be used as a query via method reference, {@code LocalTime::from}.
+ *
+ * @param temporal the temporal object to convert, not null
+ * @return the local time, not null
+ * @throws DateTimeException if unable to convert to a {@code LocalTime}
+ */
+ public static LocalTime from(TemporalAccessor temporal) {
+ Objects.requireNonNull(temporal, "temporal");
+ LocalTime time = temporal.query(TemporalQueries.localTime());
+ if (time == null) {
+ throw new DateTimeException("Unable to obtain LocalTime from TemporalAccessor: " +
+ temporal + " of type " + temporal.getClass().getName());
+ }
+ return time;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code LocalTime} from a text string such as {@code 10:15}.
+ * <p>
+ * The string must represent a valid time and is parsed using
+ * {@link java.time.format.DateTimeFormatter#ISO_LOCAL_TIME}.
+ *
+ * @param text the text to parse such as "10:15:30", not null
+ * @return the parsed local time, not null
+ * @throws DateTimeParseException if the text cannot be parsed
+ */
+ public static LocalTime parse(CharSequence text) {
+ return parse(text, DateTimeFormatter.ISO_LOCAL_TIME);
+ }
+
+ /**
+ * Obtains an instance of {@code LocalTime} from a text string using a specific formatter.
+ * <p>
+ * The text is parsed using the formatter, returning a time.
+ *
+ * @param text the text to parse, not null
+ * @param formatter the formatter to use, not null
+ * @return the parsed local time, not null
+ * @throws DateTimeParseException if the text cannot be parsed
+ */
+ public static LocalTime parse(CharSequence text, DateTimeFormatter formatter) {
+ Objects.requireNonNull(formatter, "formatter");
+ return formatter.parse(text, LocalTime::from);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Creates a local time from the hour, minute, second and nanosecond fields.
+ * <p>
+ * This factory may return a cached value, but applications must not rely on this.
+ *
+ * @param hour the hour-of-day to represent, validated from 0 to 23
+ * @param minute the minute-of-hour to represent, validated from 0 to 59
+ * @param second the second-of-minute to represent, validated from 0 to 59
+ * @param nanoOfSecond the nano-of-second to represent, validated from 0 to 999,999,999
+ * @return the local time, not null
+ */
+ private static LocalTime create(int hour, int minute, int second, int nanoOfSecond) {
+ if ((minute | second | nanoOfSecond) == 0) {
+ return HOURS[hour];
+ }
+ return new LocalTime(hour, minute, second, nanoOfSecond);
+ }
+
+ /**
+ * Constructor, previously validated.
+ *
+ * @param hour the hour-of-day to represent, validated from 0 to 23
+ * @param minute the minute-of-hour to represent, validated from 0 to 59
+ * @param second the second-of-minute to represent, validated from 0 to 59
+ * @param nanoOfSecond the nano-of-second to represent, validated from 0 to 999,999,999
+ */
+ private LocalTime(int hour, int minute, int second, int nanoOfSecond) {
+ this.hour = (byte) hour;
+ this.minute = (byte) minute;
+ this.second = (byte) second;
+ this.nano = nanoOfSecond;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if the specified field is supported.
+ * <p>
+ * This checks if this time can be queried for the specified field.
+ * If false, then calling the {@link #range(TemporalField) range},
+ * {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}
+ * methods will throw an exception.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The supported fields are:
+ * <ul>
+ * <li>{@code NANO_OF_SECOND}
+ * <li>{@code NANO_OF_DAY}
+ * <li>{@code MICRO_OF_SECOND}
+ * <li>{@code MICRO_OF_DAY}
+ * <li>{@code MILLI_OF_SECOND}
+ * <li>{@code MILLI_OF_DAY}
+ * <li>{@code SECOND_OF_MINUTE}
+ * <li>{@code SECOND_OF_DAY}
+ * <li>{@code MINUTE_OF_HOUR}
+ * <li>{@code MINUTE_OF_DAY}
+ * <li>{@code HOUR_OF_AMPM}
+ * <li>{@code CLOCK_HOUR_OF_AMPM}
+ * <li>{@code HOUR_OF_DAY}
+ * <li>{@code CLOCK_HOUR_OF_DAY}
+ * <li>{@code AMPM_OF_DAY}
+ * </ul>
+ * All other {@code ChronoField} instances will return false.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}
+ * passing {@code this} as the argument.
+ * Whether the field is supported is determined by the field.
+ *
+ * @param field the field to check, null returns false
+ * @return true if the field is supported on this time, false if not
+ */
+ @Override
+ public boolean isSupported(TemporalField field) {
+ if (field instanceof ChronoField) {
+ return field.isTimeBased();
+ }
+ return field != null && field.isSupportedBy(this);
+ }
+
+ /**
+ * Checks if the specified unit is supported.
+ * <p>
+ * This checks if the specified unit can be added to, or subtracted from, this time.
+ * If false, then calling the {@link #plus(long, TemporalUnit)} and
+ * {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
+ * <p>
+ * If the unit is a {@link ChronoUnit} then the query is implemented here.
+ * The supported units are:
+ * <ul>
+ * <li>{@code NANOS}
+ * <li>{@code MICROS}
+ * <li>{@code MILLIS}
+ * <li>{@code SECONDS}
+ * <li>{@code MINUTES}
+ * <li>{@code HOURS}
+ * <li>{@code HALF_DAYS}
+ * </ul>
+ * All other {@code ChronoUnit} instances will return false.
+ * <p>
+ * If the unit is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
+ * passing {@code this} as the argument.
+ * Whether the unit is supported is determined by the unit.
+ *
+ * @param unit the unit to check, null returns false
+ * @return true if the unit can be added/subtracted, false if not
+ */
+ @Override // override for Javadoc
+ public boolean isSupported(TemporalUnit unit) {
+ if (unit instanceof ChronoUnit) {
+ return unit.isTimeBased();
+ }
+ return unit != null && unit.isSupportedBy(this);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the range of valid values for the specified field.
+ * <p>
+ * The range object expresses the minimum and maximum valid values for a field.
+ * This time is used to enhance the accuracy of the returned range.
+ * If it is not possible to return the range, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The {@link #isSupported(TemporalField) supported fields} will return
+ * appropriate range instances.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.rangeRefinedBy(TemporalAccessor)}
+ * passing {@code this} as the argument.
+ * Whether the range can be obtained is determined by the field.
+ *
+ * @param field the field to query the range for, not null
+ * @return the range of valid values for the field, not null
+ * @throws DateTimeException if the range for the field cannot be obtained
+ * @throws UnsupportedTemporalTypeException if the field is not supported
+ */
+ @Override // override for Javadoc
+ public ValueRange range(TemporalField field) {
+ return Temporal.super.range(field);
+ }
+
+ /**
+ * Gets the value of the specified field from this time as an {@code int}.
+ * <p>
+ * This queries this time for the value of the specified field.
+ * The returned value will always be within the valid range of values for the field.
+ * If it is not possible to return the value, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The {@link #isSupported(TemporalField) supported fields} will return valid
+ * values based on this time, except {@code NANO_OF_DAY} and {@code MICRO_OF_DAY}
+ * which are too large to fit in an {@code int} and throw a {@code DateTimeException}.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
+ * passing {@code this} as the argument. Whether the value can be obtained,
+ * and what the value represents, is determined by the field.
+ *
+ * @param field the field to get, not null
+ * @return the value for the field
+ * @throws DateTimeException if a value for the field cannot be obtained or
+ * the value is outside the range of valid values for the field
+ * @throws UnsupportedTemporalTypeException if the field is not supported or
+ * the range of values exceeds an {@code int}
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override // override for Javadoc and performance
+ public int get(TemporalField field) {
+ if (field instanceof ChronoField) {
+ return get0(field);
+ }
+ return Temporal.super.get(field);
+ }
+
+ /**
+ * Gets the value of the specified field from this time as a {@code long}.
+ * <p>
+ * This queries this time for the value of the specified field.
+ * If it is not possible to return the value, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The {@link #isSupported(TemporalField) supported fields} will return valid
+ * values based on this time.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
+ * passing {@code this} as the argument. Whether the value can be obtained,
+ * and what the value represents, is determined by the field.
+ *
+ * @param field the field to get, not null
+ * @return the value for the field
+ * @throws DateTimeException if a value for the field cannot be obtained
+ * @throws UnsupportedTemporalTypeException if the field is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public long getLong(TemporalField field) {
+ if (field instanceof ChronoField) {
+ if (field == NANO_OF_DAY) {
+ return toNanoOfDay();
+ }
+ if (field == MICRO_OF_DAY) {
+ return toNanoOfDay() / 1000;
+ }
+ return get0(field);
+ }
+ return field.getFrom(this);
+ }
+
+ private int get0(TemporalField field) {
+ switch ((ChronoField) field) {
+ case NANO_OF_SECOND: return nano;
+ case NANO_OF_DAY: throw new UnsupportedTemporalTypeException("Invalid field 'NanoOfDay' for get() method, use getLong() instead");
+ case MICRO_OF_SECOND: return nano / 1000;
+ case MICRO_OF_DAY: throw new UnsupportedTemporalTypeException("Invalid field 'MicroOfDay' for get() method, use getLong() instead");
+ case MILLI_OF_SECOND: return nano / 1000_000;
+ case MILLI_OF_DAY: return (int) (toNanoOfDay() / 1000_000);
+ case SECOND_OF_MINUTE: return second;
+ case SECOND_OF_DAY: return toSecondOfDay();
+ case MINUTE_OF_HOUR: return minute;
+ case MINUTE_OF_DAY: return hour * 60 + minute;
+ case HOUR_OF_AMPM: return hour % 12;
+ case CLOCK_HOUR_OF_AMPM: int ham = hour % 12; return (ham % 12 == 0 ? 12 : ham);
+ case HOUR_OF_DAY: return hour;
+ case CLOCK_HOUR_OF_DAY: return (hour == 0 ? 24 : hour);
+ case AMPM_OF_DAY: return hour / 12;
+ }
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the hour-of-day field.
+ *
+ * @return the hour-of-day, from 0 to 23
+ */
+ public int getHour() {
+ return hour;
+ }
+
+ /**
+ * Gets the minute-of-hour field.
+ *
+ * @return the minute-of-hour, from 0 to 59
+ */
+ public int getMinute() {
+ return minute;
+ }
+
+ /**
+ * Gets the second-of-minute field.
+ *
+ * @return the second-of-minute, from 0 to 59
+ */
+ public int getSecond() {
+ return second;
+ }
+
+ /**
+ * Gets the nano-of-second field.
+ *
+ * @return the nano-of-second, from 0 to 999,999,999
+ */
+ public int getNano() {
+ return nano;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns an adjusted copy of this time.
+ * <p>
+ * This returns a {@code LocalTime}, based on this one, with the time adjusted.
+ * The adjustment takes place using the specified adjuster strategy object.
+ * Read the documentation of the adjuster to understand what adjustment will be made.
+ * <p>
+ * A simple adjuster might simply set the one of the fields, such as the hour field.
+ * A more complex adjuster might set the time to the last hour of the day.
+ * <p>
+ * The result of this method is obtained by invoking the
+ * {@link TemporalAdjuster#adjustInto(Temporal)} method on the
+ * specified adjuster passing {@code this} as the argument.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param adjuster the adjuster to use, not null
+ * @return a {@code LocalTime} based on {@code this} with the adjustment made, not null
+ * @throws DateTimeException if the adjustment cannot be made
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public LocalTime with(TemporalAdjuster adjuster) {
+ // optimizations
+ if (adjuster instanceof LocalTime) {
+ return (LocalTime) adjuster;
+ }
+ return (LocalTime) adjuster.adjustInto(this);
+ }
+
+ /**
+ * Returns a copy of this time with the specified field set to a new value.
+ * <p>
+ * This returns a {@code LocalTime}, based on this one, with the value
+ * for the specified field changed.
+ * This can be used to change any supported field, such as the hour, minute or second.
+ * If it is not possible to set the value, because the field is not supported or for
+ * some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the adjustment is implemented here.
+ * The supported fields behave as follows:
+ * <ul>
+ * <li>{@code NANO_OF_SECOND} -
+ * Returns a {@code LocalTime} with the specified nano-of-second.
+ * The hour, minute and second will be unchanged.
+ * <li>{@code NANO_OF_DAY} -
+ * Returns a {@code LocalTime} with the specified nano-of-day.
+ * This completely replaces the time and is equivalent to {@link #ofNanoOfDay(long)}.
+ * <li>{@code MICRO_OF_SECOND} -
+ * Returns a {@code LocalTime} with the nano-of-second replaced by the specified
+ * micro-of-second multiplied by 1,000.
+ * The hour, minute and second will be unchanged.
+ * <li>{@code MICRO_OF_DAY} -
+ * Returns a {@code LocalTime} with the specified micro-of-day.
+ * This completely replaces the time and is equivalent to using {@link #ofNanoOfDay(long)}
+ * with the micro-of-day multiplied by 1,000.
+ * <li>{@code MILLI_OF_SECOND} -
+ * Returns a {@code LocalTime} with the nano-of-second replaced by the specified
+ * milli-of-second multiplied by 1,000,000.
+ * The hour, minute and second will be unchanged.
+ * <li>{@code MILLI_OF_DAY} -
+ * Returns a {@code LocalTime} with the specified milli-of-day.
+ * This completely replaces the time and is equivalent to using {@link #ofNanoOfDay(long)}
+ * with the milli-of-day multiplied by 1,000,000.
+ * <li>{@code SECOND_OF_MINUTE} -
+ * Returns a {@code LocalTime} with the specified second-of-minute.
+ * The hour, minute and nano-of-second will be unchanged.
+ * <li>{@code SECOND_OF_DAY} -
+ * Returns a {@code LocalTime} with the specified second-of-day.
+ * The nano-of-second will be unchanged.
+ * <li>{@code MINUTE_OF_HOUR} -
+ * Returns a {@code LocalTime} with the specified minute-of-hour.
+ * The hour, second-of-minute and nano-of-second will be unchanged.
+ * <li>{@code MINUTE_OF_DAY} -
+ * Returns a {@code LocalTime} with the specified minute-of-day.
+ * The second-of-minute and nano-of-second will be unchanged.
+ * <li>{@code HOUR_OF_AMPM} -
+ * Returns a {@code LocalTime} with the specified hour-of-am-pm.
+ * The AM/PM, minute-of-hour, second-of-minute and nano-of-second will be unchanged.
+ * <li>{@code CLOCK_HOUR_OF_AMPM} -
+ * Returns a {@code LocalTime} with the specified clock-hour-of-am-pm.
+ * The AM/PM, minute-of-hour, second-of-minute and nano-of-second will be unchanged.
+ * <li>{@code HOUR_OF_DAY} -
+ * Returns a {@code LocalTime} with the specified hour-of-day.
+ * The minute-of-hour, second-of-minute and nano-of-second will be unchanged.
+ * <li>{@code CLOCK_HOUR_OF_DAY} -
+ * Returns a {@code LocalTime} with the specified clock-hour-of-day.
+ * The minute-of-hour, second-of-minute and nano-of-second will be unchanged.
+ * <li>{@code AMPM_OF_DAY} -
+ * Returns a {@code LocalTime} with the specified AM/PM.
+ * The hour-of-am-pm, minute-of-hour, second-of-minute and nano-of-second will be unchanged.
+ * </ul>
+ * <p>
+ * In all cases, if the new value is outside the valid range of values for the field
+ * then a {@code DateTimeException} will be thrown.
+ * <p>
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.adjustInto(Temporal, long)}
+ * passing {@code this} as the argument. In this case, the field determines
+ * whether and how to adjust the instant.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param field the field to set in the result, not null
+ * @param newValue the new value of the field in the result
+ * @return a {@code LocalTime} based on {@code this} with the specified field set, not null
+ * @throws DateTimeException if the field cannot be set
+ * @throws UnsupportedTemporalTypeException if the field is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public LocalTime with(TemporalField field, long newValue) {
+ if (field instanceof ChronoField) {
+ ChronoField f = (ChronoField) field;
+ f.checkValidValue(newValue);
+ switch (f) {
+ case NANO_OF_SECOND: return withNano((int) newValue);
+ case NANO_OF_DAY: return LocalTime.ofNanoOfDay(newValue);
+ case MICRO_OF_SECOND: return withNano((int) newValue * 1000);
+ case MICRO_OF_DAY: return LocalTime.ofNanoOfDay(newValue * 1000);
+ case MILLI_OF_SECOND: return withNano((int) newValue * 1000_000);
+ case MILLI_OF_DAY: return LocalTime.ofNanoOfDay(newValue * 1000_000);
+ case SECOND_OF_MINUTE: return withSecond((int) newValue);
+ case SECOND_OF_DAY: return plusSeconds(newValue - toSecondOfDay());
+ case MINUTE_OF_HOUR: return withMinute((int) newValue);
+ case MINUTE_OF_DAY: return plusMinutes(newValue - (hour * 60 + minute));
+ case HOUR_OF_AMPM: return plusHours(newValue - (hour % 12));
+ case CLOCK_HOUR_OF_AMPM: return plusHours((newValue == 12 ? 0 : newValue) - (hour % 12));
+ case HOUR_OF_DAY: return withHour((int) newValue);
+ case CLOCK_HOUR_OF_DAY: return withHour((int) (newValue == 24 ? 0 : newValue));
+ case AMPM_OF_DAY: return plusHours((newValue - (hour / 12)) * 12);
+ }
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
+ }
+ return field.adjustInto(this, newValue);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this {@code LocalTime} with the hour-of-day altered.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param hour the hour-of-day to set in the result, from 0 to 23
+ * @return a {@code LocalTime} based on this time with the requested hour, not null
+ * @throws DateTimeException if the hour value is invalid
+ */
+ public LocalTime withHour(int hour) {
+ if (this.hour == hour) {
+ return this;
+ }
+ HOUR_OF_DAY.checkValidValue(hour);
+ return create(hour, minute, second, nano);
+ }
+
+ /**
+ * Returns a copy of this {@code LocalTime} with the minute-of-hour altered.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param minute the minute-of-hour to set in the result, from 0 to 59
+ * @return a {@code LocalTime} based on this time with the requested minute, not null
+ * @throws DateTimeException if the minute value is invalid
+ */
+ public LocalTime withMinute(int minute) {
+ if (this.minute == minute) {
+ return this;
+ }
+ MINUTE_OF_HOUR.checkValidValue(minute);
+ return create(hour, minute, second, nano);
+ }
+
+ /**
+ * Returns a copy of this {@code LocalTime} with the second-of-minute altered.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param second the second-of-minute to set in the result, from 0 to 59
+ * @return a {@code LocalTime} based on this time with the requested second, not null
+ * @throws DateTimeException if the second value is invalid
+ */
+ public LocalTime withSecond(int second) {
+ if (this.second == second) {
+ return this;
+ }
+ SECOND_OF_MINUTE.checkValidValue(second);
+ return create(hour, minute, second, nano);
+ }
+
+ /**
+ * Returns a copy of this {@code LocalTime} with the nano-of-second altered.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param nanoOfSecond the nano-of-second to set in the result, from 0 to 999,999,999
+ * @return a {@code LocalTime} based on this time with the requested nanosecond, not null
+ * @throws DateTimeException if the nanos value is invalid
+ */
+ public LocalTime withNano(int nanoOfSecond) {
+ if (this.nano == nanoOfSecond) {
+ return this;
+ }
+ NANO_OF_SECOND.checkValidValue(nanoOfSecond);
+ return create(hour, minute, second, nanoOfSecond);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this {@code LocalTime} with the time truncated.
+ * <p>
+ * Truncation returns a copy of the original time with fields
+ * smaller than the specified unit set to zero.
+ * For example, truncating with the {@link ChronoUnit#MINUTES minutes} unit
+ * will set the second-of-minute and nano-of-second field to zero.
+ * <p>
+ * The unit must have a {@linkplain TemporalUnit#getDuration() duration}
+ * that divides into the length of a standard day without remainder.
+ * This includes all supplied time units on {@link ChronoUnit} and
+ * {@link ChronoUnit#DAYS DAYS}. Other units throw an exception.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param unit the unit to truncate to, not null
+ * @return a {@code LocalTime} based on this time with the time truncated, not null
+ * @throws DateTimeException if unable to truncate
+ * @throws UnsupportedTemporalTypeException if the unit is not supported
+ */
+ public LocalTime truncatedTo(TemporalUnit unit) {
+ if (unit == ChronoUnit.NANOS) {
+ return this;
+ }
+ Duration unitDur = unit.getDuration();
+ if (unitDur.getSeconds() > SECONDS_PER_DAY) {
+ throw new UnsupportedTemporalTypeException("Unit is too large to be used for truncation");
+ }
+ long dur = unitDur.toNanos();
+ if ((NANOS_PER_DAY % dur) != 0) {
+ throw new UnsupportedTemporalTypeException("Unit must divide into a standard day without remainder");
+ }
+ long nod = toNanoOfDay();
+ return ofNanoOfDay((nod / dur) * dur);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this time with the specified amount added.
+ * <p>
+ * This returns a {@code LocalTime}, based on this one, with the specified amount added.
+ * The amount is typically {@link Duration} but may be any other type implementing
+ * the {@link TemporalAmount} interface.
+ * <p>
+ * The calculation is delegated to the amount object by calling
+ * {@link TemporalAmount#addTo(Temporal)}. The amount implementation is free
+ * to implement the addition in any way it wishes, however it typically
+ * calls back to {@link #plus(long, TemporalUnit)}. Consult the documentation
+ * of the amount implementation to determine if it can be successfully added.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param amountToAdd the amount to add, not null
+ * @return a {@code LocalTime} based on this time with the addition made, not null
+ * @throws DateTimeException if the addition cannot be made
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public LocalTime plus(TemporalAmount amountToAdd) {
+ return (LocalTime) amountToAdd.addTo(this);
+ }
+
+ /**
+ * Returns a copy of this time with the specified amount added.
+ * <p>
+ * This returns a {@code LocalTime}, based on this one, with the amount
+ * in terms of the unit added. If it is not possible to add the amount, because the
+ * unit is not supported or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoUnit} then the addition is implemented here.
+ * The supported fields behave as follows:
+ * <ul>
+ * <li>{@code NANOS} -
+ * Returns a {@code LocalTime} with the specified number of nanoseconds added.
+ * This is equivalent to {@link #plusNanos(long)}.
+ * <li>{@code MICROS} -
+ * Returns a {@code LocalTime} with the specified number of microseconds added.
+ * This is equivalent to {@link #plusNanos(long)} with the amount
+ * multiplied by 1,000.
+ * <li>{@code MILLIS} -
+ * Returns a {@code LocalTime} with the specified number of milliseconds added.
+ * This is equivalent to {@link #plusNanos(long)} with the amount
+ * multiplied by 1,000,000.
+ * <li>{@code SECONDS} -
+ * Returns a {@code LocalTime} with the specified number of seconds added.
+ * This is equivalent to {@link #plusSeconds(long)}.
+ * <li>{@code MINUTES} -
+ * Returns a {@code LocalTime} with the specified number of minutes added.
+ * This is equivalent to {@link #plusMinutes(long)}.
+ * <li>{@code HOURS} -
+ * Returns a {@code LocalTime} with the specified number of hours added.
+ * This is equivalent to {@link #plusHours(long)}.
+ * <li>{@code HALF_DAYS} -
+ * Returns a {@code LocalTime} with the specified number of half-days added.
+ * This is equivalent to {@link #plusHours(long)} with the amount
+ * multiplied by 12.
+ * </ul>
+ * <p>
+ * All other {@code ChronoUnit} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.addTo(Temporal, long)}
+ * passing {@code this} as the argument. In this case, the unit determines
+ * whether and how to perform the addition.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param amountToAdd the amount of the unit to add to the result, may be negative
+ * @param unit the unit of the amount to add, not null
+ * @return a {@code LocalTime} based on this time with the specified amount added, not null
+ * @throws DateTimeException if the addition cannot be made
+ * @throws UnsupportedTemporalTypeException if the unit is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public LocalTime plus(long amountToAdd, TemporalUnit unit) {
+ if (unit instanceof ChronoUnit) {
+ switch ((ChronoUnit) unit) {
+ case NANOS: return plusNanos(amountToAdd);
+ case MICROS: return plusNanos((amountToAdd % MICROS_PER_DAY) * 1000);
+ case MILLIS: return plusNanos((amountToAdd % MILLIS_PER_DAY) * 1000_000);
+ case SECONDS: return plusSeconds(amountToAdd);
+ case MINUTES: return plusMinutes(amountToAdd);
+ case HOURS: return plusHours(amountToAdd);
+ case HALF_DAYS: return plusHours((amountToAdd % 2) * 12);
+ }
+ throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
+ }
+ return unit.addTo(this, amountToAdd);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this {@code LocalTime} with the specified number of hours added.
+ * <p>
+ * This adds the specified number of hours to this time, returning a new time.
+ * The calculation wraps around midnight.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param hoursToAdd the hours to add, may be negative
+ * @return a {@code LocalTime} based on this time with the hours added, not null
+ */
+ public LocalTime plusHours(long hoursToAdd) {
+ if (hoursToAdd == 0) {
+ return this;
+ }
+ int newHour = ((int) (hoursToAdd % HOURS_PER_DAY) + hour + HOURS_PER_DAY) % HOURS_PER_DAY;
+ return create(newHour, minute, second, nano);
+ }
+
+ /**
+ * Returns a copy of this {@code LocalTime} with the specified number of minutes added.
+ * <p>
+ * This adds the specified number of minutes to this time, returning a new time.
+ * The calculation wraps around midnight.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param minutesToAdd the minutes to add, may be negative
+ * @return a {@code LocalTime} based on this time with the minutes added, not null
+ */
+ public LocalTime plusMinutes(long minutesToAdd) {
+ if (minutesToAdd == 0) {
+ return this;
+ }
+ int mofd = hour * MINUTES_PER_HOUR + minute;
+ int newMofd = ((int) (minutesToAdd % MINUTES_PER_DAY) + mofd + MINUTES_PER_DAY) % MINUTES_PER_DAY;
+ if (mofd == newMofd) {
+ return this;
+ }
+ int newHour = newMofd / MINUTES_PER_HOUR;
+ int newMinute = newMofd % MINUTES_PER_HOUR;
+ return create(newHour, newMinute, second, nano);
+ }
+
+ /**
+ * Returns a copy of this {@code LocalTime} with the specified number of seconds added.
+ * <p>
+ * This adds the specified number of seconds to this time, returning a new time.
+ * The calculation wraps around midnight.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param secondstoAdd the seconds to add, may be negative
+ * @return a {@code LocalTime} based on this time with the seconds added, not null
+ */
+ public LocalTime plusSeconds(long secondstoAdd) {
+ if (secondstoAdd == 0) {
+ return this;
+ }
+ int sofd = hour * SECONDS_PER_HOUR +
+ minute * SECONDS_PER_MINUTE + second;
+ int newSofd = ((int) (secondstoAdd % SECONDS_PER_DAY) + sofd + SECONDS_PER_DAY) % SECONDS_PER_DAY;
+ if (sofd == newSofd) {
+ return this;
+ }
+ int newHour = newSofd / SECONDS_PER_HOUR;
+ int newMinute = (newSofd / SECONDS_PER_MINUTE) % MINUTES_PER_HOUR;
+ int newSecond = newSofd % SECONDS_PER_MINUTE;
+ return create(newHour, newMinute, newSecond, nano);
+ }
+
+ /**
+ * Returns a copy of this {@code LocalTime} with the specified number of nanoseconds added.
+ * <p>
+ * This adds the specified number of nanoseconds to this time, returning a new time.
+ * The calculation wraps around midnight.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param nanosToAdd the nanos to add, may be negative
+ * @return a {@code LocalTime} based on this time with the nanoseconds added, not null
+ */
+ public LocalTime plusNanos(long nanosToAdd) {
+ if (nanosToAdd == 0) {
+ return this;
+ }
+ long nofd = toNanoOfDay();
+ long newNofd = ((nanosToAdd % NANOS_PER_DAY) + nofd + NANOS_PER_DAY) % NANOS_PER_DAY;
+ if (nofd == newNofd) {
+ return this;
+ }
+ int newHour = (int) (newNofd / NANOS_PER_HOUR);
+ int newMinute = (int) ((newNofd / NANOS_PER_MINUTE) % MINUTES_PER_HOUR);
+ int newSecond = (int) ((newNofd / NANOS_PER_SECOND) % SECONDS_PER_MINUTE);
+ int newNano = (int) (newNofd % NANOS_PER_SECOND);
+ return create(newHour, newMinute, newSecond, newNano);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this time with the specified amount subtracted.
+ * <p>
+ * This returns a {@code LocalTime}, based on this one, with the specified amount subtracted.
+ * The amount is typically {@link Duration} but may be any other type implementing
+ * the {@link TemporalAmount} interface.
+ * <p>
+ * The calculation is delegated to the amount object by calling
+ * {@link TemporalAmount#subtractFrom(Temporal)}. The amount implementation is free
+ * to implement the subtraction in any way it wishes, however it typically
+ * calls back to {@link #minus(long, TemporalUnit)}. Consult the documentation
+ * of the amount implementation to determine if it can be successfully subtracted.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param amountToSubtract the amount to subtract, not null
+ * @return a {@code LocalTime} based on this time with the subtraction made, not null
+ * @throws DateTimeException if the subtraction cannot be made
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public LocalTime minus(TemporalAmount amountToSubtract) {
+ return (LocalTime) amountToSubtract.subtractFrom(this);
+ }
+
+ /**
+ * Returns a copy of this time with the specified amount subtracted.
+ * <p>
+ * This returns a {@code LocalTime}, based on this one, with the amount
+ * in terms of the unit subtracted. If it is not possible to subtract the amount,
+ * because the unit is not supported or for some other reason, an exception is thrown.
+ * <p>
+ * This method is equivalent to {@link #plus(long, TemporalUnit)} with the amount negated.
+ * See that method for a full description of how addition, and thus subtraction, works.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param amountToSubtract the amount of the unit to subtract from the result, may be negative
+ * @param unit the unit of the amount to subtract, not null
+ * @return a {@code LocalTime} based on this time with the specified amount subtracted, not null
+ * @throws DateTimeException if the subtraction cannot be made
+ * @throws UnsupportedTemporalTypeException if the unit is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public LocalTime minus(long amountToSubtract, TemporalUnit unit) {
+ return (amountToSubtract == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this {@code LocalTime} with the specified number of hours subtracted.
+ * <p>
+ * This subtracts the specified number of hours from this time, returning a new time.
+ * The calculation wraps around midnight.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param hoursToSubtract the hours to subtract, may be negative
+ * @return a {@code LocalTime} based on this time with the hours subtracted, not null
+ */
+ public LocalTime minusHours(long hoursToSubtract) {
+ return plusHours(-(hoursToSubtract % HOURS_PER_DAY));
+ }
+
+ /**
+ * Returns a copy of this {@code LocalTime} with the specified number of minutes subtracted.
+ * <p>
+ * This subtracts the specified number of minutes from this time, returning a new time.
+ * The calculation wraps around midnight.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param minutesToSubtract the minutes to subtract, may be negative
+ * @return a {@code LocalTime} based on this time with the minutes subtracted, not null
+ */
+ public LocalTime minusMinutes(long minutesToSubtract) {
+ return plusMinutes(-(minutesToSubtract % MINUTES_PER_DAY));
+ }
+
+ /**
+ * Returns a copy of this {@code LocalTime} with the specified number of seconds subtracted.
+ * <p>
+ * This subtracts the specified number of seconds from this time, returning a new time.
+ * The calculation wraps around midnight.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param secondsToSubtract the seconds to subtract, may be negative
+ * @return a {@code LocalTime} based on this time with the seconds subtracted, not null
+ */
+ public LocalTime minusSeconds(long secondsToSubtract) {
+ return plusSeconds(-(secondsToSubtract % SECONDS_PER_DAY));
+ }
+
+ /**
+ * Returns a copy of this {@code LocalTime} with the specified number of nanoseconds subtracted.
+ * <p>
+ * This subtracts the specified number of nanoseconds from this time, returning a new time.
+ * The calculation wraps around midnight.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param nanosToSubtract the nanos to subtract, may be negative
+ * @return a {@code LocalTime} based on this time with the nanoseconds subtracted, not null
+ */
+ public LocalTime minusNanos(long nanosToSubtract) {
+ return plusNanos(-(nanosToSubtract % NANOS_PER_DAY));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Queries this time using the specified query.
+ * <p>
+ * This queries this time using the specified query strategy object.
+ * The {@code TemporalQuery} object defines the logic to be used to
+ * obtain the result. Read the documentation of the query to understand
+ * what the result of this method will be.
+ * <p>
+ * The result of this method is obtained by invoking the
+ * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the
+ * specified query passing {@code this} as the argument.
+ *
+ * @param <R> the type of the result
+ * @param query the query to invoke, not null
+ * @return the query result, null may be returned (defined by the query)
+ * @throws DateTimeException if unable to query (defined by the query)
+ * @throws ArithmeticException if numeric overflow occurs (defined by the query)
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public <R> R query(TemporalQuery<R> query) {
+ if (query == TemporalQueries.chronology() || query == TemporalQueries.zoneId() ||
+ query == TemporalQueries.zone() || query == TemporalQueries.offset()) {
+ return null;
+ } else if (query == TemporalQueries.localTime()) {
+ return (R) this;
+ } else if (query == TemporalQueries.localDate()) {
+ return null;
+ } else if (query == TemporalQueries.precision()) {
+ return (R) NANOS;
+ }
+ // inline TemporalAccessor.super.query(query) as an optimization
+ // non-JDK classes are not permitted to make this optimization
+ return query.queryFrom(this);
+ }
+
+ /**
+ * Adjusts the specified temporal object to have the same time as this object.
+ * <p>
+ * This returns a temporal object of the same observable type as the input
+ * with the time changed to be the same as this.
+ * <p>
+ * The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)}
+ * passing {@link ChronoField#NANO_OF_DAY} as the field.
+ * <p>
+ * In most cases, it is clearer to reverse the calling pattern by using
+ * {@link Temporal#with(TemporalAdjuster)}:
+ * <pre>
+ * // these two lines are equivalent, but the second approach is recommended
+ * temporal = thisLocalTime.adjustInto(temporal);
+ * temporal = temporal.with(thisLocalTime);
+ * </pre>
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param temporal the target object to be adjusted, not null
+ * @return the adjusted object, not null
+ * @throws DateTimeException if unable to make the adjustment
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public Temporal adjustInto(Temporal temporal) {
+ return temporal.with(NANO_OF_DAY, toNanoOfDay());
+ }
+
+ /**
+ * Calculates the amount of time until another time in terms of the specified unit.
+ * <p>
+ * This calculates the amount of time between two {@code LocalTime}
+ * objects in terms of a single {@code TemporalUnit}.
+ * The start and end points are {@code this} and the specified time.
+ * The result will be negative if the end is before the start.
+ * The {@code Temporal} passed to this method is converted to a
+ * {@code LocalTime} using {@link #from(TemporalAccessor)}.
+ * For example, the amount in hours between two times can be calculated
+ * using {@code startTime.until(endTime, HOURS)}.
+ * <p>
+ * The calculation returns a whole number, representing the number of
+ * complete units between the two times.
+ * For example, the amount in hours between 11:30 and 13:29 will only
+ * be one hour as it is one minute short of two hours.
+ * <p>
+ * There are two equivalent ways of using this method.
+ * The first is to invoke this method.
+ * The second is to use {@link TemporalUnit#between(Temporal, Temporal)}:
+ * <pre>
+ * // these two lines are equivalent
+ * amount = start.until(end, MINUTES);
+ * amount = MINUTES.between(start, end);
+ * </pre>
+ * The choice should be made based on which makes the code more readable.
+ * <p>
+ * The calculation is implemented in this method for {@link ChronoUnit}.
+ * The units {@code NANOS}, {@code MICROS}, {@code MILLIS}, {@code SECONDS},
+ * {@code MINUTES}, {@code HOURS} and {@code HALF_DAYS} are supported.
+ * Other {@code ChronoUnit} values will throw an exception.
+ * <p>
+ * If the unit is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.between(Temporal, Temporal)}
+ * passing {@code this} as the first argument and the converted input temporal
+ * as the second argument.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param endExclusive the end time, exclusive, which is converted to a {@code LocalTime}, not null
+ * @param unit the unit to measure the amount in, not null
+ * @return the amount of time between this time and the end time
+ * @throws DateTimeException if the amount cannot be calculated, or the end
+ * temporal cannot be converted to a {@code LocalTime}
+ * @throws UnsupportedTemporalTypeException if the unit is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public long until(Temporal endExclusive, TemporalUnit unit) {
+ LocalTime end = LocalTime.from(endExclusive);
+ if (unit instanceof ChronoUnit) {
+ long nanosUntil = end.toNanoOfDay() - toNanoOfDay(); // no overflow
+ switch ((ChronoUnit) unit) {
+ case NANOS: return nanosUntil;
+ case MICROS: return nanosUntil / 1000;
+ case MILLIS: return nanosUntil / 1000_000;
+ case SECONDS: return nanosUntil / NANOS_PER_SECOND;
+ case MINUTES: return nanosUntil / NANOS_PER_MINUTE;
+ case HOURS: return nanosUntil / NANOS_PER_HOUR;
+ case HALF_DAYS: return nanosUntil / (12 * NANOS_PER_HOUR);
+ }
+ throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
+ }
+ return unit.between(this, end);
+ }
+
+ /**
+ * Formats this time using the specified formatter.
+ * <p>
+ * This time will be passed to the formatter to produce a string.
+ *
+ * @param formatter the formatter to use, not null
+ * @return the formatted time string, not null
+ * @throws DateTimeException if an error occurs during printing
+ */
+ public String format(DateTimeFormatter formatter) {
+ Objects.requireNonNull(formatter, "formatter");
+ return formatter.format(this);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Combines this time with a date to create a {@code LocalDateTime}.
+ * <p>
+ * This returns a {@code LocalDateTime} formed from this time at the specified date.
+ * All possible combinations of date and time are valid.
+ *
+ * @param date the date to combine with, not null
+ * @return the local date-time formed from this time and the specified date, not null
+ */
+ public LocalDateTime atDate(LocalDate date) {
+ return LocalDateTime.of(date, this);
+ }
+
+ /**
+ * Combines this time with an offset to create an {@code OffsetTime}.
+ * <p>
+ * This returns an {@code OffsetTime} formed from this time at the specified offset.
+ * All possible combinations of time and offset are valid.
+ *
+ * @param offset the offset to combine with, not null
+ * @return the offset time formed from this time and the specified offset, not null
+ */
+ public OffsetTime atOffset(ZoneOffset offset) {
+ return OffsetTime.of(this, offset);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Extracts the time as seconds of day,
+ * from {@code 0} to {@code 24 * 60 * 60 - 1}.
+ *
+ * @return the second-of-day equivalent to this time
+ */
+ public int toSecondOfDay() {
+ int total = hour * SECONDS_PER_HOUR;
+ total += minute * SECONDS_PER_MINUTE;
+ total += second;
+ return total;
+ }
+
+ /**
+ * Extracts the time as nanos of day,
+ * from {@code 0} to {@code 24 * 60 * 60 * 1,000,000,000 - 1}.
+ *
+ * @return the nano of day equivalent to this time
+ */
+ public long toNanoOfDay() {
+ long total = hour * NANOS_PER_HOUR;
+ total += minute * NANOS_PER_MINUTE;
+ total += second * NANOS_PER_SECOND;
+ total += nano;
+ return total;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Compares this time to another time.
+ * <p>
+ * The comparison is based on the time-line position of the local times within a day.
+ * It is "consistent with equals", as defined by {@link Comparable}.
+ *
+ * @param other the other time to compare to, not null
+ * @return the comparator value, negative if less, positive if greater
+ * @throws NullPointerException if {@code other} is null
+ */
+ @Override
+ public int compareTo(LocalTime other) {
+ int cmp = Integer.compare(hour, other.hour);
+ if (cmp == 0) {
+ cmp = Integer.compare(minute, other.minute);
+ if (cmp == 0) {
+ cmp = Integer.compare(second, other.second);
+ if (cmp == 0) {
+ cmp = Integer.compare(nano, other.nano);
+ }
+ }
+ }
+ return cmp;
+ }
+
+ /**
+ * Checks if this time is after the specified time.
+ * <p>
+ * The comparison is based on the time-line position of the time within a day.
+ *
+ * @param other the other time to compare to, not null
+ * @return true if this is after the specified time
+ * @throws NullPointerException if {@code other} is null
+ */
+ public boolean isAfter(LocalTime other) {
+ return compareTo(other) > 0;
+ }
+
+ /**
+ * Checks if this time is before the specified time.
+ * <p>
+ * The comparison is based on the time-line position of the time within a day.
+ *
+ * @param other the other time to compare to, not null
+ * @return true if this point is before the specified time
+ * @throws NullPointerException if {@code other} is null
+ */
+ public boolean isBefore(LocalTime other) {
+ return compareTo(other) < 0;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if this time is equal to another time.
+ * <p>
+ * The comparison is based on the time-line position of the time within a day.
+ * <p>
+ * Only objects of type {@code LocalTime} are compared, other types return false.
+ * To compare the date of two {@code TemporalAccessor} instances, use
+ * {@link ChronoField#NANO_OF_DAY} as a comparator.
+ *
+ * @param obj the object to check, null returns false
+ * @return true if this is equal to the other time
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof LocalTime) {
+ LocalTime other = (LocalTime) obj;
+ return hour == other.hour && minute == other.minute &&
+ second == other.second && nano == other.nano;
+ }
+ return false;
+ }
+
+ /**
+ * A hash code for this time.
+ *
+ * @return a suitable hash code
+ */
+ @Override
+ public int hashCode() {
+ long nod = toNanoOfDay();
+ return (int) (nod ^ (nod >>> 32));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Outputs this time as a {@code String}, such as {@code 10:15}.
+ * <p>
+ * The output will be one of the following ISO-8601 formats:
+ * <ul>
+ * <li>{@code HH:mm}</li>
+ * <li>{@code HH:mm:ss}</li>
+ * <li>{@code HH:mm:ss.SSS}</li>
+ * <li>{@code HH:mm:ss.SSSSSS}</li>
+ * <li>{@code HH:mm:ss.SSSSSSSSS}</li>
+ * </ul>
+ * The format used will be the shortest that outputs the full value of
+ * the time where the omitted parts are implied to be zero.
+ *
+ * @return a string representation of this time, not null
+ */
+ @Override
+ public String toString() {
+ StringBuilder buf = new StringBuilder(18);
+ int hourValue = hour;
+ int minuteValue = minute;
+ int secondValue = second;
+ int nanoValue = nano;
+ buf.append(hourValue < 10 ? "0" : "").append(hourValue)
+ .append(minuteValue < 10 ? ":0" : ":").append(minuteValue);
+ if (secondValue > 0 || nanoValue > 0) {
+ buf.append(secondValue < 10 ? ":0" : ":").append(secondValue);
+ if (nanoValue > 0) {
+ buf.append('.');
+ if (nanoValue % 1000_000 == 0) {
+ buf.append(Integer.toString((nanoValue / 1000_000) + 1000).substring(1));
+ } else if (nanoValue % 1000 == 0) {
+ buf.append(Integer.toString((nanoValue / 1000) + 1000_000).substring(1));
+ } else {
+ buf.append(Integer.toString((nanoValue) + 1000_000_000).substring(1));
+ }
+ }
+ }
+ return buf.toString();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Writes the object using a
+ * <a href="../../serialized-form.html#java.time.Ser">dedicated serialized form</a>.
+ * @serialData
+ * A twos-complement value indicates the remaining values are not in the stream
+ * and should be set to zero.
+ * <pre>
+ * out.writeByte(4); // identifies a LocalTime
+ * if (nano == 0) {
+ * if (second == 0) {
+ * if (minute == 0) {
+ * out.writeByte(~hour);
+ * } else {
+ * out.writeByte(hour);
+ * out.writeByte(~minute);
+ * }
+ * } else {
+ * out.writeByte(hour);
+ * out.writeByte(minute);
+ * out.writeByte(~second);
+ * }
+ * } else {
+ * out.writeByte(hour);
+ * out.writeByte(minute);
+ * out.writeByte(second);
+ * out.writeInt(nano);
+ * }
+ * </pre>
+ *
+ * @return the instance of {@code Ser}, not null
+ */
+ private Object writeReplace() {
+ return new Ser(Ser.LOCAL_TIME_TYPE, this);
+ }
+
+ /**
+ * Defend against malicious streams.
+ *
+ * @param s the stream to read
+ * @throws InvalidObjectException always
+ */
+ private void readObject(ObjectInputStream s) throws InvalidObjectException {
+ throw new InvalidObjectException("Deserialization via serialization delegate");
+ }
+
+ void writeExternal(DataOutput out) throws IOException {
+ if (nano == 0) {
+ if (second == 0) {
+ if (minute == 0) {
+ out.writeByte(~hour);
+ } else {
+ out.writeByte(hour);
+ out.writeByte(~minute);
+ }
+ } else {
+ out.writeByte(hour);
+ out.writeByte(minute);
+ out.writeByte(~second);
+ }
+ } else {
+ out.writeByte(hour);
+ out.writeByte(minute);
+ out.writeByte(second);
+ out.writeInt(nano);
+ }
+ }
+
+ static LocalTime readExternal(DataInput in) throws IOException {
+ int hour = in.readByte();
+ int minute = 0;
+ int second = 0;
+ int nano = 0;
+ if (hour < 0) {
+ hour = ~hour;
+ } else {
+ minute = in.readByte();
+ if (minute < 0) {
+ minute = ~minute;
+ } else {
+ second = in.readByte();
+ if (second < 0) {
+ second = ~second;
+ } else {
+ nano = in.readInt();
+ }
+ }
+ }
+ return LocalTime.of(hour, minute, second, nano);
+ }
+
+}
diff --git a/java/time/Month.java b/java/time/Month.java
new file mode 100644
index 0000000..57382cd
--- /dev/null
+++ b/java/time/Month.java
@@ -0,0 +1,615 @@
+/*
+ * Copyright (c) 2012, 2015, 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) 2007-2012, 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;
+
+import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
+import static java.time.temporal.ChronoUnit.MONTHS;
+
+import java.time.chrono.Chronology;
+import java.time.chrono.IsoChronology;
+import java.time.format.DateTimeFormatterBuilder;
+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;
+import java.time.temporal.TemporalQueries;
+import java.time.temporal.TemporalQuery;
+import java.time.temporal.UnsupportedTemporalTypeException;
+import java.time.temporal.ValueRange;
+import java.util.Locale;
+
+/**
+ * A month-of-year, such as 'July'.
+ * <p>
+ * {@code Month} is an enum representing the 12 months of the year -
+ * January, February, March, April, May, June, July, August, September, October,
+ * November and December.
+ * <p>
+ * In addition to the textual enum name, each month-of-year has an {@code int} value.
+ * The {@code int} value follows normal usage and the ISO-8601 standard,
+ * from 1 (January) to 12 (December). It is recommended that applications use the enum
+ * rather than the {@code int} value to ensure code clarity.
+ * <p>
+ * <b>Do not use {@code ordinal()} to obtain the numeric representation of {@code Month}.
+ * Use {@code getValue()} instead.</b>
+ * <p>
+ * This enum represents a common concept that is found in many calendar systems.
+ * As such, this enum may be used by any calendar system that has the month-of-year
+ * concept defined exactly equivalent to the ISO-8601 calendar system.
+ *
+ * @implSpec
+ * This is an immutable and thread-safe enum.
+ *
+ * @since 1.8
+ */
+public enum Month implements TemporalAccessor, TemporalAdjuster {
+
+ /**
+ * The singleton instance for the month of January with 31 days.
+ * This has the numeric value of {@code 1}.
+ */
+ JANUARY,
+ /**
+ * The singleton instance for the month of February with 28 days, or 29 in a leap year.
+ * This has the numeric value of {@code 2}.
+ */
+ FEBRUARY,
+ /**
+ * The singleton instance for the month of March with 31 days.
+ * This has the numeric value of {@code 3}.
+ */
+ MARCH,
+ /**
+ * The singleton instance for the month of April with 30 days.
+ * This has the numeric value of {@code 4}.
+ */
+ APRIL,
+ /**
+ * The singleton instance for the month of May with 31 days.
+ * This has the numeric value of {@code 5}.
+ */
+ MAY,
+ /**
+ * The singleton instance for the month of June with 30 days.
+ * This has the numeric value of {@code 6}.
+ */
+ JUNE,
+ /**
+ * The singleton instance for the month of July with 31 days.
+ * This has the numeric value of {@code 7}.
+ */
+ JULY,
+ /**
+ * The singleton instance for the month of August with 31 days.
+ * This has the numeric value of {@code 8}.
+ */
+ AUGUST,
+ /**
+ * The singleton instance for the month of September with 30 days.
+ * This has the numeric value of {@code 9}.
+ */
+ SEPTEMBER,
+ /**
+ * The singleton instance for the month of October with 31 days.
+ * This has the numeric value of {@code 10}.
+ */
+ OCTOBER,
+ /**
+ * The singleton instance for the month of November with 30 days.
+ * This has the numeric value of {@code 11}.
+ */
+ NOVEMBER,
+ /**
+ * The singleton instance for the month of December with 31 days.
+ * This has the numeric value of {@code 12}.
+ */
+ DECEMBER;
+ /**
+ * Private cache of all the constants.
+ */
+ private static final Month[] ENUMS = Month.values();
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code Month} from an {@code int} value.
+ * <p>
+ * {@code Month} is an enum representing the 12 months of the year.
+ * This factory allows the enum to be obtained from the {@code int} value.
+ * The {@code int} value follows the ISO-8601 standard, from 1 (January) to 12 (December).
+ *
+ * @param month the month-of-year to represent, from 1 (January) to 12 (December)
+ * @return the month-of-year, not null
+ * @throws DateTimeException if the month-of-year is invalid
+ */
+ public static Month of(int month) {
+ if (month < 1 || month > 12) {
+ throw new DateTimeException("Invalid value for MonthOfYear: " + month);
+ }
+ return ENUMS[month - 1];
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code Month} from a temporal object.
+ * <p>
+ * This obtains a month based on the specified temporal.
+ * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
+ * which this factory converts to an instance of {@code Month}.
+ * <p>
+ * The conversion extracts the {@link ChronoField#MONTH_OF_YEAR MONTH_OF_YEAR} field.
+ * The extraction is only permitted if the temporal object has an ISO
+ * chronology, or can be converted to a {@code LocalDate}.
+ * <p>
+ * This method matches the signature of the functional interface {@link TemporalQuery}
+ * allowing it to be used as a query via method reference, {@code Month::from}.
+ *
+ * @param temporal the temporal object to convert, not null
+ * @return the month-of-year, not null
+ * @throws DateTimeException if unable to convert to a {@code Month}
+ */
+ public static Month from(TemporalAccessor temporal) {
+ if (temporal instanceof Month) {
+ return (Month) temporal;
+ }
+ try {
+ if (IsoChronology.INSTANCE.equals(Chronology.from(temporal)) == false) {
+ temporal = LocalDate.from(temporal);
+ }
+ return of(temporal.get(MONTH_OF_YEAR));
+ } catch (DateTimeException ex) {
+ throw new DateTimeException("Unable to obtain Month from TemporalAccessor: " +
+ temporal + " of type " + temporal.getClass().getName(), ex);
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the month-of-year {@code int} value.
+ * <p>
+ * The values are numbered following the ISO-8601 standard,
+ * from 1 (January) to 12 (December).
+ *
+ * @return the month-of-year, from 1 (January) to 12 (December)
+ */
+ public int getValue() {
+ return ordinal() + 1;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the textual representation, such as 'Jan' or 'December'.
+ * <p>
+ * This returns the textual name used to identify the month-of-year,
+ * suitable for presentation to the user.
+ * The parameters control the style of the returned text and the locale.
+ * <p>
+ * If no textual mapping is found then the {@link #getValue() numeric value} is returned.
+ *
+ * @param style the length of the text required, not null
+ * @param locale the locale to use, not null
+ * @return the text value of the month-of-year, not null
+ */
+ public String getDisplayName(TextStyle style, Locale locale) {
+ return new DateTimeFormatterBuilder().appendText(MONTH_OF_YEAR, style).toFormatter(locale).format(this);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if the specified field is supported.
+ * <p>
+ * This checks if this month-of-year can be queried for the specified field.
+ * If false, then calling the {@link #range(TemporalField) range} and
+ * {@link #get(TemporalField) get} methods will throw an exception.
+ * <p>
+ * If the field is {@link ChronoField#MONTH_OF_YEAR MONTH_OF_YEAR} then
+ * this method returns true.
+ * All other {@code ChronoField} instances will return false.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}
+ * passing {@code this} as the argument.
+ * Whether the field is supported is determined by the field.
+ *
+ * @param field the field to check, null returns false
+ * @return true if the field is supported on this month-of-year, false if not
+ */
+ @Override
+ public boolean isSupported(TemporalField field) {
+ if (field instanceof ChronoField) {
+ return field == MONTH_OF_YEAR;
+ }
+ return field != null && field.isSupportedBy(this);
+ }
+
+ /**
+ * Gets the range of valid values for the specified field.
+ * <p>
+ * The range object expresses the minimum and maximum valid values for a field.
+ * This month is used to enhance the accuracy of the returned range.
+ * If it is not possible to return the range, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is {@link ChronoField#MONTH_OF_YEAR MONTH_OF_YEAR} then the
+ * range of the month-of-year, from 1 to 12, will be returned.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.rangeRefinedBy(TemporalAccessor)}
+ * passing {@code this} as the argument.
+ * Whether the range can be obtained is determined by the field.
+ *
+ * @param field the field to query the range for, not null
+ * @return the range of valid values for the field, not null
+ * @throws DateTimeException if the range for the field cannot be obtained
+ * @throws UnsupportedTemporalTypeException if the field is not supported
+ */
+ @Override
+ public ValueRange range(TemporalField field) {
+ if (field == MONTH_OF_YEAR) {
+ return field.range();
+ }
+ return TemporalAccessor.super.range(field);
+ }
+
+ /**
+ * Gets the value of the specified field from this month-of-year as an {@code int}.
+ * <p>
+ * This queries this month for the value of the specified field.
+ * The returned value will always be within the valid range of values for the field.
+ * If it is not possible to return the value, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is {@link ChronoField#MONTH_OF_YEAR MONTH_OF_YEAR} then the
+ * value of the month-of-year, from 1 to 12, will be returned.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
+ * passing {@code this} as the argument. Whether the value can be obtained,
+ * and what the value represents, is determined by the field.
+ *
+ * @param field the field to get, not null
+ * @return the value for the field, within the valid range of values
+ * @throws DateTimeException if a value for the field cannot be obtained or
+ * the value is outside the range of valid values for the field
+ * @throws UnsupportedTemporalTypeException if the field is not supported or
+ * the range of values exceeds an {@code int}
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public int get(TemporalField field) {
+ if (field == MONTH_OF_YEAR) {
+ return getValue();
+ }
+ return TemporalAccessor.super.get(field);
+ }
+
+ /**
+ * Gets the value of the specified field from this month-of-year as a {@code long}.
+ * <p>
+ * This queries this month for the value of the specified field.
+ * If it is not possible to return the value, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is {@link ChronoField#MONTH_OF_YEAR MONTH_OF_YEAR} then the
+ * value of the month-of-year, from 1 to 12, will be returned.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
+ * passing {@code this} as the argument. Whether the value can be obtained,
+ * and what the value represents, is determined by the field.
+ *
+ * @param field the field to get, not null
+ * @return the value for the field
+ * @throws DateTimeException if a value for the field cannot be obtained
+ * @throws UnsupportedTemporalTypeException if the field is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public long getLong(TemporalField field) {
+ if (field == MONTH_OF_YEAR) {
+ return getValue();
+ } else if (field instanceof ChronoField) {
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
+ }
+ return field.getFrom(this);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns the month-of-year that is the specified number of quarters after this one.
+ * <p>
+ * The calculation rolls around the end of the year from December to January.
+ * The specified period may be negative.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param months the months to add, positive or negative
+ * @return the resulting month, not null
+ */
+ public Month plus(long months) {
+ int amount = (int) (months % 12);
+ return ENUMS[(ordinal() + (amount + 12)) % 12];
+ }
+
+ /**
+ * Returns the month-of-year that is the specified number of months before this one.
+ * <p>
+ * The calculation rolls around the start of the year from January to December.
+ * The specified period may be negative.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param months the months to subtract, positive or negative
+ * @return the resulting month, not null
+ */
+ public Month minus(long months) {
+ return plus(-(months % 12));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the length of this month in days.
+ * <p>
+ * This takes a flag to determine whether to return the length for a leap year or not.
+ * <p>
+ * February has 28 days in a standard year and 29 days in a leap year.
+ * April, June, September and November have 30 days.
+ * All other months have 31 days.
+ *
+ * @param leapYear true if the length is required for a leap year
+ * @return the length of this month in days, from 28 to 31
+ */
+ public int length(boolean leapYear) {
+ switch (this) {
+ case FEBRUARY:
+ return (leapYear ? 29 : 28);
+ case APRIL:
+ case JUNE:
+ case SEPTEMBER:
+ case NOVEMBER:
+ return 30;
+ default:
+ return 31;
+ }
+ }
+
+ /**
+ * Gets the minimum length of this month in days.
+ * <p>
+ * February has a minimum length of 28 days.
+ * April, June, September and November have 30 days.
+ * All other months have 31 days.
+ *
+ * @return the minimum length of this month in days, from 28 to 31
+ */
+ public int minLength() {
+ switch (this) {
+ case FEBRUARY:
+ return 28;
+ case APRIL:
+ case JUNE:
+ case SEPTEMBER:
+ case NOVEMBER:
+ return 30;
+ default:
+ return 31;
+ }
+ }
+
+ /**
+ * Gets the maximum length of this month in days.
+ * <p>
+ * February has a maximum length of 29 days.
+ * April, June, September and November have 30 days.
+ * All other months have 31 days.
+ *
+ * @return the maximum length of this month in days, from 29 to 31
+ */
+ public int maxLength() {
+ switch (this) {
+ case FEBRUARY:
+ return 29;
+ case APRIL:
+ case JUNE:
+ case SEPTEMBER:
+ case NOVEMBER:
+ return 30;
+ default:
+ return 31;
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the day-of-year corresponding to the first day of this month.
+ * <p>
+ * This returns the day-of-year that this month begins on, using the leap
+ * year flag to determine the length of February.
+ *
+ * @param leapYear true if the length is required for a leap year
+ * @return the day of year corresponding to the first day of this month, from 1 to 336
+ */
+ public int firstDayOfYear(boolean leapYear) {
+ int leap = leapYear ? 1 : 0;
+ switch (this) {
+ case JANUARY:
+ return 1;
+ case FEBRUARY:
+ return 32;
+ case MARCH:
+ return 60 + leap;
+ case APRIL:
+ return 91 + leap;
+ case MAY:
+ return 121 + leap;
+ case JUNE:
+ return 152 + leap;
+ case JULY:
+ return 182 + leap;
+ case AUGUST:
+ return 213 + leap;
+ case SEPTEMBER:
+ return 244 + leap;
+ case OCTOBER:
+ return 274 + leap;
+ case NOVEMBER:
+ return 305 + leap;
+ case DECEMBER:
+ default:
+ return 335 + leap;
+ }
+ }
+
+ /**
+ * Gets the month corresponding to the first month of this quarter.
+ * <p>
+ * The year can be divided into four quarters.
+ * This method returns the first month of the quarter for the base month.
+ * January, February and March return January.
+ * April, May and June return April.
+ * July, August and September return July.
+ * October, November and December return October.
+ *
+ * @return the first month of the quarter corresponding to this month, not null
+ */
+ public Month firstMonthOfQuarter() {
+ return ENUMS[(ordinal() / 3) * 3];
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Queries this month-of-year using the specified query.
+ * <p>
+ * This queries this month-of-year using the specified query strategy object.
+ * The {@code TemporalQuery} object defines the logic to be used to
+ * obtain the result. Read the documentation of the query to understand
+ * what the result of this method will be.
+ * <p>
+ * The result of this method is obtained by invoking the
+ * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the
+ * specified query passing {@code this} as the argument.
+ *
+ * @param <R> the type of the result
+ * @param query the query to invoke, not null
+ * @return the query result, null may be returned (defined by the query)
+ * @throws DateTimeException if unable to query (defined by the query)
+ * @throws ArithmeticException if numeric overflow occurs (defined by the query)
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public <R> R query(TemporalQuery<R> query) {
+ if (query == TemporalQueries.chronology()) {
+ return (R) IsoChronology.INSTANCE;
+ } else if (query == TemporalQueries.precision()) {
+ return (R) MONTHS;
+ }
+ return TemporalAccessor.super.query(query);
+ }
+
+ /**
+ * Adjusts the specified temporal object to have this month-of-year.
+ * <p>
+ * This returns a temporal object of the same observable type as the input
+ * with the month-of-year changed to be the same as this.
+ * <p>
+ * The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)}
+ * passing {@link ChronoField#MONTH_OF_YEAR} as the field.
+ * If the specified temporal object does not use the ISO calendar system then
+ * a {@code DateTimeException} is thrown.
+ * <p>
+ * In most cases, it is clearer to reverse the calling pattern by using
+ * {@link Temporal#with(TemporalAdjuster)}:
+ * <pre>
+ * // these two lines are equivalent, but the second approach is recommended
+ * temporal = thisMonth.adjustInto(temporal);
+ * temporal = temporal.with(thisMonth);
+ * </pre>
+ * <p>
+ * For example, given a date in May, the following are output:
+ * <pre>
+ * dateInMay.with(JANUARY); // four months earlier
+ * dateInMay.with(APRIL); // one months earlier
+ * dateInMay.with(MAY); // same date
+ * dateInMay.with(JUNE); // one month later
+ * dateInMay.with(DECEMBER); // seven months later
+ * </pre>
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param temporal the target object to be adjusted, not null
+ * @return the adjusted object, not null
+ * @throws DateTimeException if unable to make the adjustment
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public Temporal adjustInto(Temporal temporal) {
+ if (Chronology.from(temporal).equals(IsoChronology.INSTANCE) == false) {
+ throw new DateTimeException("Adjustment only supported on ISO date-time");
+ }
+ return temporal.with(MONTH_OF_YEAR, getValue());
+ }
+
+}
diff --git a/java/time/MonthDay.java b/java/time/MonthDay.java
new file mode 100644
index 0000000..50bc2c8
--- /dev/null
+++ b/java/time/MonthDay.java
@@ -0,0 +1,786 @@
+/*
+ * Copyright (c) 2012, 2015, 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) 2007-2012, 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;
+
+import static java.time.temporal.ChronoField.DAY_OF_MONTH;
+import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.time.chrono.Chronology;
+import java.time.chrono.IsoChronology;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
+import java.time.format.DateTimeParseException;
+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;
+import java.time.temporal.TemporalQueries;
+import java.time.temporal.TemporalQuery;
+import java.time.temporal.UnsupportedTemporalTypeException;
+import java.time.temporal.ValueRange;
+import java.util.Objects;
+
+// Android-changed: removed ValueBased paragraph.
+/**
+ * A month-day in the ISO-8601 calendar system, such as {@code --12-03}.
+ * <p>
+ * {@code MonthDay} is an immutable date-time object that represents the combination
+ * of a month and day-of-month. Any field that can be derived from a month and day,
+ * such as quarter-of-year, can be obtained.
+ * <p>
+ * This class does not store or represent a year, time or time-zone.
+ * For example, the value "December 3rd" can be stored in a {@code MonthDay}.
+ * <p>
+ * Since a {@code MonthDay} does not possess a year, the leap day of
+ * February 29th is considered valid.
+ * <p>
+ * This class implements {@link TemporalAccessor} rather than {@link Temporal}.
+ * This is because it is not possible to define whether February 29th is valid or not
+ * without external information, preventing the implementation of plus/minus.
+ * Related to this, {@code MonthDay} only provides access to query and set the fields
+ * {@code MONTH_OF_YEAR} and {@code DAY_OF_MONTH}.
+ * <p>
+ * 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.
+ * For most applications written today, the ISO-8601 rules are entirely suitable.
+ * However, any application that makes use of historical dates, and requires them
+ * to be accurate will find the ISO-8601 approach unsuitable.
+ *
+ * @implSpec
+ * This class is immutable and thread-safe.
+ *
+ * @since 1.8
+ */
+public final class MonthDay
+ implements TemporalAccessor, TemporalAdjuster, Comparable<MonthDay>, Serializable {
+
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = -939150713474957432L;
+ /**
+ * Parser.
+ */
+ private static final DateTimeFormatter PARSER = new DateTimeFormatterBuilder()
+ .appendLiteral("--")
+ .appendValue(MONTH_OF_YEAR, 2)
+ .appendLiteral('-')
+ .appendValue(DAY_OF_MONTH, 2)
+ .toFormatter();
+
+ /**
+ * The month-of-year, not null.
+ */
+ private final int month;
+ /**
+ * The day-of-month.
+ */
+ private final int day;
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains the current month-day from the system clock in the default time-zone.
+ * <p>
+ * This will query the {@link Clock#systemDefaultZone() system clock} in the default
+ * time-zone to obtain the current month-day.
+ * <p>
+ * Using this method will prevent the ability to use an alternate clock for testing
+ * because the clock is hard-coded.
+ *
+ * @return the current month-day using the system clock and default time-zone, not null
+ */
+ public static MonthDay now() {
+ return now(Clock.systemDefaultZone());
+ }
+
+ /**
+ * Obtains the current month-day from the system clock in the specified time-zone.
+ * <p>
+ * This will query the {@link Clock#system(ZoneId) system clock} to obtain the current month-day.
+ * Specifying the time-zone avoids dependence on the default time-zone.
+ * <p>
+ * Using this method will prevent the ability to use an alternate clock for testing
+ * because the clock is hard-coded.
+ *
+ * @param zone the zone ID to use, not null
+ * @return the current month-day using the system clock, not null
+ */
+ public static MonthDay now(ZoneId zone) {
+ return now(Clock.system(zone));
+ }
+
+ /**
+ * Obtains the current month-day from the specified clock.
+ * <p>
+ * This will query the specified clock to obtain the current month-day.
+ * Using this method allows the use of an alternate clock for testing.
+ * The alternate clock may be introduced using {@link Clock dependency injection}.
+ *
+ * @param clock the clock to use, not null
+ * @return the current month-day, not null
+ */
+ public static MonthDay now(Clock clock) {
+ final LocalDate now = LocalDate.now(clock); // called once
+ return MonthDay.of(now.getMonth(), now.getDayOfMonth());
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code MonthDay}.
+ * <p>
+ * The day-of-month must be valid for the month within a leap year.
+ * Hence, for February, day 29 is valid.
+ * <p>
+ * For example, passing in April and day 31 will throw an exception, as
+ * there can never be April 31st in any year. By contrast, passing in
+ * February 29th is permitted, as that month-day can sometimes be valid.
+ *
+ * @param month the month-of-year to represent, not null
+ * @param dayOfMonth the day-of-month to represent, from 1 to 31
+ * @return the month-day, not null
+ * @throws DateTimeException if the value of any field is out of range,
+ * or if the day-of-month is invalid for the month
+ */
+ public static MonthDay of(Month month, int dayOfMonth) {
+ Objects.requireNonNull(month, "month");
+ DAY_OF_MONTH.checkValidValue(dayOfMonth);
+ if (dayOfMonth > month.maxLength()) {
+ throw new DateTimeException("Illegal value for DayOfMonth field, value " + dayOfMonth +
+ " is not valid for month " + month.name());
+ }
+ return new MonthDay(month.getValue(), dayOfMonth);
+ }
+
+ /**
+ * Obtains an instance of {@code MonthDay}.
+ * <p>
+ * The day-of-month must be valid for the month within a leap year.
+ * Hence, for month 2 (February), day 29 is valid.
+ * <p>
+ * For example, passing in month 4 (April) and day 31 will throw an exception, as
+ * there can never be April 31st in any year. By contrast, passing in
+ * February 29th is permitted, as that month-day can sometimes be valid.
+ *
+ * @param month the month-of-year to represent, from 1 (January) to 12 (December)
+ * @param dayOfMonth the day-of-month to represent, from 1 to 31
+ * @return the month-day, not null
+ * @throws DateTimeException if the value of any field is out of range,
+ * or if the day-of-month is invalid for the month
+ */
+ public static MonthDay of(int month, int dayOfMonth) {
+ return of(Month.of(month), dayOfMonth);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code MonthDay} from a temporal object.
+ * <p>
+ * This obtains a month-day based on the specified temporal.
+ * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
+ * which this factory converts to an instance of {@code MonthDay}.
+ * <p>
+ * The conversion extracts the {@link ChronoField#MONTH_OF_YEAR MONTH_OF_YEAR} and
+ * {@link ChronoField#DAY_OF_MONTH DAY_OF_MONTH} fields.
+ * The extraction is only permitted if the temporal object has an ISO
+ * chronology, or can be converted to a {@code LocalDate}.
+ * <p>
+ * This method matches the signature of the functional interface {@link TemporalQuery}
+ * allowing it to be used as a query via method reference, {@code MonthDay::from}.
+ *
+ * @param temporal the temporal object to convert, not null
+ * @return the month-day, not null
+ * @throws DateTimeException if unable to convert to a {@code MonthDay}
+ */
+ public static MonthDay from(TemporalAccessor temporal) {
+ if (temporal instanceof MonthDay) {
+ return (MonthDay) temporal;
+ }
+ try {
+ if (IsoChronology.INSTANCE.equals(Chronology.from(temporal)) == false) {
+ temporal = LocalDate.from(temporal);
+ }
+ return of(temporal.get(MONTH_OF_YEAR), temporal.get(DAY_OF_MONTH));
+ } catch (DateTimeException ex) {
+ throw new DateTimeException("Unable to obtain MonthDay from TemporalAccessor: " +
+ temporal + " of type " + temporal.getClass().getName(), ex);
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code MonthDay} from a text string such as {@code --12-03}.
+ * <p>
+ * The string must represent a valid month-day.
+ * The format is {@code --MM-dd}.
+ *
+ * @param text the text to parse such as "--12-03", not null
+ * @return the parsed month-day, not null
+ * @throws DateTimeParseException if the text cannot be parsed
+ */
+ public static MonthDay parse(CharSequence text) {
+ return parse(text, PARSER);
+ }
+
+ /**
+ * Obtains an instance of {@code MonthDay} from a text string using a specific formatter.
+ * <p>
+ * The text is parsed using the formatter, returning a month-day.
+ *
+ * @param text the text to parse, not null
+ * @param formatter the formatter to use, not null
+ * @return the parsed month-day, not null
+ * @throws DateTimeParseException if the text cannot be parsed
+ */
+ public static MonthDay parse(CharSequence text, DateTimeFormatter formatter) {
+ Objects.requireNonNull(formatter, "formatter");
+ return formatter.parse(text, MonthDay::from);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Constructor, previously validated.
+ *
+ * @param month the month-of-year to represent, validated from 1 to 12
+ * @param dayOfMonth the day-of-month to represent, validated from 1 to 29-31
+ */
+ private MonthDay(int month, int dayOfMonth) {
+ this.month = month;
+ this.day = dayOfMonth;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if the specified field is supported.
+ * <p>
+ * This checks if this month-day can be queried for the specified field.
+ * If false, then calling the {@link #range(TemporalField) range} and
+ * {@link #get(TemporalField) get} methods will throw an exception.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The supported fields are:
+ * <ul>
+ * <li>{@code MONTH_OF_YEAR}
+ * <li>{@code YEAR}
+ * </ul>
+ * All other {@code ChronoField} instances will return false.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}
+ * passing {@code this} as the argument.
+ * Whether the field is supported is determined by the field.
+ *
+ * @param field the field to check, null returns false
+ * @return true if the field is supported on this month-day, false if not
+ */
+ @Override
+ public boolean isSupported(TemporalField field) {
+ if (field instanceof ChronoField) {
+ return field == MONTH_OF_YEAR || field == DAY_OF_MONTH;
+ }
+ return field != null && field.isSupportedBy(this);
+ }
+
+ /**
+ * Gets the range of valid values for the specified field.
+ * <p>
+ * The range object expresses the minimum and maximum valid values for a field.
+ * This month-day is used to enhance the accuracy of the returned range.
+ * If it is not possible to return the range, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The {@link #isSupported(TemporalField) supported fields} will return
+ * appropriate range instances.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.rangeRefinedBy(TemporalAccessor)}
+ * passing {@code this} as the argument.
+ * Whether the range can be obtained is determined by the field.
+ *
+ * @param field the field to query the range for, not null
+ * @return the range of valid values for the field, not null
+ * @throws DateTimeException if the range for the field cannot be obtained
+ * @throws UnsupportedTemporalTypeException if the field is not supported
+ */
+ @Override
+ public ValueRange range(TemporalField field) {
+ if (field == MONTH_OF_YEAR) {
+ return field.range();
+ } else if (field == DAY_OF_MONTH) {
+ return ValueRange.of(1, getMonth().minLength(), getMonth().maxLength());
+ }
+ return TemporalAccessor.super.range(field);
+ }
+
+ /**
+ * Gets the value of the specified field from this month-day as an {@code int}.
+ * <p>
+ * This queries this month-day for the value of the specified field.
+ * The returned value will always be within the valid range of values for the field.
+ * If it is not possible to return the value, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The {@link #isSupported(TemporalField) supported fields} will return valid
+ * values based on this month-day.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
+ * passing {@code this} as the argument. Whether the value can be obtained,
+ * and what the value represents, is determined by the field.
+ *
+ * @param field the field to get, not null
+ * @return the value for the field
+ * @throws DateTimeException if a value for the field cannot be obtained or
+ * the value is outside the range of valid values for the field
+ * @throws UnsupportedTemporalTypeException if the field is not supported or
+ * the range of values exceeds an {@code int}
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override // override for Javadoc
+ public int get(TemporalField field) {
+ return range(field).checkValidIntValue(getLong(field), field);
+ }
+
+ /**
+ * Gets the value of the specified field from this month-day as a {@code long}.
+ * <p>
+ * This queries this month-day for the value of the specified field.
+ * If it is not possible to return the value, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The {@link #isSupported(TemporalField) supported fields} will return valid
+ * values based on this month-day.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
+ * passing {@code this} as the argument. Whether the value can be obtained,
+ * and what the value represents, is determined by the field.
+ *
+ * @param field the field to get, not null
+ * @return the value for the field
+ * @throws DateTimeException if a value for the field cannot be obtained
+ * @throws UnsupportedTemporalTypeException if the field is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public long getLong(TemporalField field) {
+ if (field instanceof ChronoField) {
+ switch ((ChronoField) field) {
+ // alignedDOW and alignedWOM not supported because they cannot be set in with()
+ case DAY_OF_MONTH: return day;
+ case MONTH_OF_YEAR: return month;
+ }
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
+ }
+ return field.getFrom(this);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the month-of-year field from 1 to 12.
+ * <p>
+ * This method returns the month as an {@code int} from 1 to 12.
+ * Application code is frequently clearer if the enum {@link Month}
+ * is used by calling {@link #getMonth()}.
+ *
+ * @return the month-of-year, from 1 to 12
+ * @see #getMonth()
+ */
+ public int getMonthValue() {
+ return month;
+ }
+
+ /**
+ * Gets the month-of-year field using the {@code Month} enum.
+ * <p>
+ * This method returns the enum {@link Month} for the month.
+ * This avoids confusion as to what {@code int} values mean.
+ * If you need access to the primitive {@code int} value then the enum
+ * provides the {@link Month#getValue() int value}.
+ *
+ * @return the month-of-year, not null
+ * @see #getMonthValue()
+ */
+ public Month getMonth() {
+ return Month.of(month);
+ }
+
+ /**
+ * Gets the day-of-month field.
+ * <p>
+ * This method returns the primitive {@code int} value for the day-of-month.
+ *
+ * @return the day-of-month, from 1 to 31
+ */
+ public int getDayOfMonth() {
+ return day;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if the year is valid for this month-day.
+ * <p>
+ * This method checks whether this month and day and the input year form
+ * a valid date. This can only return false for February 29th.
+ *
+ * @param year the year to validate
+ * @return true if the year is valid for this month-day
+ * @see Year#isValidMonthDay(MonthDay)
+ */
+ public boolean isValidYear(int year) {
+ return (day == 29 && month == 2 && Year.isLeap(year) == false) == false;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this {@code MonthDay} with the month-of-year altered.
+ * <p>
+ * This returns a month-day with the specified month.
+ * If the day-of-month is invalid for the specified month, the day will
+ * be adjusted to the last valid day-of-month.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param month the month-of-year to set in the returned month-day, from 1 (January) to 12 (December)
+ * @return a {@code MonthDay} based on this month-day with the requested month, not null
+ * @throws DateTimeException if the month-of-year value is invalid
+ */
+ public MonthDay withMonth(int month) {
+ return with(Month.of(month));
+ }
+
+ /**
+ * Returns a copy of this {@code MonthDay} with the month-of-year altered.
+ * <p>
+ * This returns a month-day with the specified month.
+ * If the day-of-month is invalid for the specified month, the day will
+ * be adjusted to the last valid day-of-month.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param month the month-of-year to set in the returned month-day, not null
+ * @return a {@code MonthDay} based on this month-day with the requested month, not null
+ */
+ public MonthDay with(Month month) {
+ Objects.requireNonNull(month, "month");
+ if (month.getValue() == this.month) {
+ return this;
+ }
+ int day = Math.min(this.day, month.maxLength());
+ return new MonthDay(month.getValue(), day);
+ }
+
+ /**
+ * Returns a copy of this {@code MonthDay} with the day-of-month altered.
+ * <p>
+ * This returns a month-day with the specified day-of-month.
+ * If the day-of-month is invalid for the month, an exception is thrown.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param dayOfMonth the day-of-month to set in the return month-day, from 1 to 31
+ * @return a {@code MonthDay} based on this month-day with the requested day, not null
+ * @throws DateTimeException if the day-of-month value is invalid,
+ * or if the day-of-month is invalid for the month
+ */
+ public MonthDay withDayOfMonth(int dayOfMonth) {
+ if (dayOfMonth == this.day) {
+ return this;
+ }
+ return of(month, dayOfMonth);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Queries this month-day using the specified query.
+ * <p>
+ * This queries this month-day using the specified query strategy object.
+ * The {@code TemporalQuery} object defines the logic to be used to
+ * obtain the result. Read the documentation of the query to understand
+ * what the result of this method will be.
+ * <p>
+ * The result of this method is obtained by invoking the
+ * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the
+ * specified query passing {@code this} as the argument.
+ *
+ * @param <R> the type of the result
+ * @param query the query to invoke, not null
+ * @return the query result, null may be returned (defined by the query)
+ * @throws DateTimeException if unable to query (defined by the query)
+ * @throws ArithmeticException if numeric overflow occurs (defined by the query)
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public <R> R query(TemporalQuery<R> query) {
+ if (query == TemporalQueries.chronology()) {
+ return (R) IsoChronology.INSTANCE;
+ }
+ return TemporalAccessor.super.query(query);
+ }
+
+ /**
+ * Adjusts the specified temporal object to have this month-day.
+ * <p>
+ * This returns a temporal object of the same observable type as the input
+ * with the month and day-of-month changed to be the same as this.
+ * <p>
+ * The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)}
+ * twice, passing {@link ChronoField#MONTH_OF_YEAR} and
+ * {@link ChronoField#DAY_OF_MONTH} as the fields.
+ * If the specified temporal object does not use the ISO calendar system then
+ * a {@code DateTimeException} is thrown.
+ * <p>
+ * In most cases, it is clearer to reverse the calling pattern by using
+ * {@link Temporal#with(TemporalAdjuster)}:
+ * <pre>
+ * // these two lines are equivalent, but the second approach is recommended
+ * temporal = thisMonthDay.adjustInto(temporal);
+ * temporal = temporal.with(thisMonthDay);
+ * </pre>
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param temporal the target object to be adjusted, not null
+ * @return the adjusted object, not null
+ * @throws DateTimeException if unable to make the adjustment
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public Temporal adjustInto(Temporal temporal) {
+ if (Chronology.from(temporal).equals(IsoChronology.INSTANCE) == false) {
+ throw new DateTimeException("Adjustment only supported on ISO date-time");
+ }
+ temporal = temporal.with(MONTH_OF_YEAR, month);
+ return temporal.with(DAY_OF_MONTH, Math.min(temporal.range(DAY_OF_MONTH).getMaximum(), day));
+ }
+
+ /**
+ * Formats this month-day using the specified formatter.
+ * <p>
+ * This month-day will be passed to the formatter to produce a string.
+ *
+ * @param formatter the formatter to use, not null
+ * @return the formatted month-day string, not null
+ * @throws DateTimeException if an error occurs during printing
+ */
+ public String format(DateTimeFormatter formatter) {
+ Objects.requireNonNull(formatter, "formatter");
+ return formatter.format(this);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Combines this month-day with a year to create a {@code LocalDate}.
+ * <p>
+ * This returns a {@code LocalDate} formed from this month-day and the specified year.
+ * <p>
+ * A month-day of February 29th will be adjusted to February 28th in the resulting
+ * date if the year is not a leap year.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param year the year to use, from MIN_YEAR to MAX_YEAR
+ * @return the local date formed from this month-day and the specified year, not null
+ * @throws DateTimeException if the year is outside the valid range of years
+ */
+ public LocalDate atYear(int year) {
+ return LocalDate.of(year, month, isValidYear(year) ? day : 28);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Compares this month-day to another month-day.
+ * <p>
+ * The comparison is based first on value of the month, then on the value of the day.
+ * It is "consistent with equals", as defined by {@link Comparable}.
+ *
+ * @param other the other month-day to compare to, not null
+ * @return the comparator value, negative if less, positive if greater
+ */
+ @Override
+ public int compareTo(MonthDay other) {
+ int cmp = (month - other.month);
+ if (cmp == 0) {
+ cmp = (day - other.day);
+ }
+ return cmp;
+ }
+
+ /**
+ * Checks if this month-day is after the specified month-day.
+ *
+ * @param other the other month-day to compare to, not null
+ * @return true if this is after the specified month-day
+ */
+ public boolean isAfter(MonthDay other) {
+ return compareTo(other) > 0;
+ }
+
+ /**
+ * Checks if this month-day is before the specified month-day.
+ *
+ * @param other the other month-day to compare to, not null
+ * @return true if this point is before the specified month-day
+ */
+ public boolean isBefore(MonthDay other) {
+ return compareTo(other) < 0;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if this month-day is equal to another month-day.
+ * <p>
+ * The comparison is based on the time-line position of the month-day within a year.
+ *
+ * @param obj the object to check, null returns false
+ * @return true if this is equal to the other month-day
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof MonthDay) {
+ MonthDay other = (MonthDay) obj;
+ return month == other.month && day == other.day;
+ }
+ return false;
+ }
+
+ /**
+ * A hash code for this month-day.
+ *
+ * @return a suitable hash code
+ */
+ @Override
+ public int hashCode() {
+ return (month << 6) + day;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Outputs this month-day as a {@code String}, such as {@code --12-03}.
+ * <p>
+ * The output will be in the format {@code --MM-dd}:
+ *
+ * @return a string representation of this month-day, not null
+ */
+ @Override
+ public String toString() {
+ return new StringBuilder(10).append("--")
+ .append(month < 10 ? "0" : "").append(month)
+ .append(day < 10 ? "-0" : "-").append(day)
+ .toString();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Writes the object using a
+ * <a href="../../serialized-form.html#java.time.Ser">dedicated serialized form</a>.
+ * @serialData
+ * <pre>
+ * out.writeByte(13); // identifies a MonthDay
+ * out.writeByte(month);
+ * out.writeByte(day);
+ * </pre>
+ *
+ * @return the instance of {@code Ser}, not null
+ */
+ private Object writeReplace() {
+ return new Ser(Ser.MONTH_DAY_TYPE, this);
+ }
+
+ /**
+ * Defend against malicious streams.
+ *
+ * @param s the stream to read
+ * @throws InvalidObjectException always
+ */
+ private void readObject(ObjectInputStream s) throws InvalidObjectException {
+ throw new InvalidObjectException("Deserialization via serialization delegate");
+ }
+
+ void writeExternal(DataOutput out) throws IOException {
+ out.writeByte(month);
+ out.writeByte(day);
+ }
+
+ static MonthDay readExternal(DataInput in) throws IOException {
+ byte month = in.readByte();
+ byte day = in.readByte();
+ return MonthDay.of(month, day);
+ }
+
+}
diff --git a/java/time/OffsetDateTime.java b/java/time/OffsetDateTime.java
new file mode 100644
index 0000000..c2c8e0a
--- /dev/null
+++ b/java/time/OffsetDateTime.java
@@ -0,0 +1,1947 @@
+/*
+ * Copyright (c) 2012, 2015, 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) 2007-2012, 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;
+
+import static java.time.temporal.ChronoField.EPOCH_DAY;
+import static java.time.temporal.ChronoField.INSTANT_SECONDS;
+import static java.time.temporal.ChronoField.NANO_OF_DAY;
+import static java.time.temporal.ChronoField.OFFSET_SECONDS;
+import static java.time.temporal.ChronoUnit.FOREVER;
+import static java.time.temporal.ChronoUnit.NANOS;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.time.chrono.IsoChronology;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.time.temporal.ChronoField;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.Temporal;
+import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalAdjuster;
+import java.time.temporal.TemporalAmount;
+import java.time.temporal.TemporalField;
+import java.time.temporal.TemporalQueries;
+import java.time.temporal.TemporalQuery;
+import java.time.temporal.TemporalUnit;
+import java.time.temporal.UnsupportedTemporalTypeException;
+import java.time.temporal.ValueRange;
+import java.time.zone.ZoneRules;
+import java.util.Comparator;
+import java.util.Objects;
+
+// Android-changed: removed ValueBased paragraph.
+/**
+ * A date-time with an offset from UTC/Greenwich in the ISO-8601 calendar system,
+ * such as {@code 2007-12-03T10:15:30+01:00}.
+ * <p>
+ * {@code OffsetDateTime} is an immutable representation of a date-time with an offset.
+ * This class stores all date and time fields, to a precision of nanoseconds,
+ * as well as the offset from UTC/Greenwich. For example, the value
+ * "2nd October 2007 at 13:45.30.123456789 +02:00" can be stored in an {@code OffsetDateTime}.
+ * <p>
+ * {@code OffsetDateTime}, {@link java.time.ZonedDateTime} and {@link java.time.Instant} all store an instant
+ * on the time-line to nanosecond precision.
+ * {@code Instant} is the simplest, simply representing the instant.
+ * {@code OffsetDateTime} adds to the instant the offset from UTC/Greenwich, which allows
+ * the local date-time to be obtained.
+ * {@code ZonedDateTime} adds full time-zone rules.
+ * <p>
+ * It is intended that {@code ZonedDateTime} or {@code Instant} is used to model data
+ * in simpler applications. This class may be used when modeling date-time concepts in
+ * more detail, or when communicating to a database or in a network protocol.
+ *
+ * @implSpec
+ * This class is immutable and thread-safe.
+ *
+ * @since 1.8
+ */
+public final class OffsetDateTime
+ implements Temporal, TemporalAdjuster, Comparable<OffsetDateTime>, Serializable {
+
+ /**
+ * The minimum supported {@code OffsetDateTime}, '-999999999-01-01T00:00:00+18:00'.
+ * This is the local date-time of midnight at the start of the minimum date
+ * in the maximum offset (larger offsets are earlier on the time-line).
+ * This combines {@link LocalDateTime#MIN} and {@link ZoneOffset#MAX}.
+ * This could be used by an application as a "far past" date-time.
+ */
+ public static final OffsetDateTime MIN = LocalDateTime.MIN.atOffset(ZoneOffset.MAX);
+ /**
+ * The maximum supported {@code OffsetDateTime}, '+999999999-12-31T23:59:59.999999999-18:00'.
+ * This is the local date-time just before midnight at the end of the maximum date
+ * in the minimum offset (larger negative offsets are later on the time-line).
+ * This combines {@link LocalDateTime#MAX} and {@link ZoneOffset#MIN}.
+ * This could be used by an application as a "far future" date-time.
+ */
+ public static final OffsetDateTime MAX = LocalDateTime.MAX.atOffset(ZoneOffset.MIN);
+
+ /**
+ * Gets a comparator that compares two {@code OffsetDateTime} instances
+ * based solely on the instant.
+ * <p>
+ * This method differs from the comparison in {@link #compareTo} in that it
+ * only compares the underlying instant.
+ *
+ * @return a comparator that compares in time-line order
+ *
+ * @see #isAfter
+ * @see #isBefore
+ * @see #isEqual
+ */
+ public static Comparator<OffsetDateTime> timeLineOrder() {
+ return OffsetDateTime::compareInstant;
+ }
+
+ /**
+ * Compares this {@code OffsetDateTime} to another date-time.
+ * The comparison is based on the instant.
+ *
+ * @param datetime1 the first date-time to compare, not null
+ * @param datetime2 the other date-time to compare to, not null
+ * @return the comparator value, negative if less, positive if greater
+ */
+ private static int compareInstant(OffsetDateTime datetime1, OffsetDateTime datetime2) {
+ if (datetime1.getOffset().equals(datetime2.getOffset())) {
+ return datetime1.toLocalDateTime().compareTo(datetime2.toLocalDateTime());
+ }
+ int cmp = Long.compare(datetime1.toEpochSecond(), datetime2.toEpochSecond());
+ if (cmp == 0) {
+ cmp = datetime1.toLocalTime().getNano() - datetime2.toLocalTime().getNano();
+ }
+ return cmp;
+ }
+
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = 2287754244819255394L;
+
+ /**
+ * The local date-time.
+ */
+ private final LocalDateTime dateTime;
+ /**
+ * The offset from UTC/Greenwich.
+ */
+ private final ZoneOffset offset;
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains the current date-time from the system clock in the default time-zone.
+ * <p>
+ * This will query the {@link Clock#systemDefaultZone() system clock} in the default
+ * time-zone to obtain the current date-time.
+ * The offset will be calculated from the time-zone in the clock.
+ * <p>
+ * Using this method will prevent the ability to use an alternate clock for testing
+ * because the clock is hard-coded.
+ *
+ * @return the current date-time using the system clock, not null
+ */
+ public static OffsetDateTime now() {
+ return now(Clock.systemDefaultZone());
+ }
+
+ /**
+ * Obtains the current date-time from the system clock in the specified time-zone.
+ * <p>
+ * This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date-time.
+ * Specifying the time-zone avoids dependence on the default time-zone.
+ * The offset will be calculated from the specified time-zone.
+ * <p>
+ * Using this method will prevent the ability to use an alternate clock for testing
+ * because the clock is hard-coded.
+ *
+ * @param zone the zone ID to use, not null
+ * @return the current date-time using the system clock, not null
+ */
+ public static OffsetDateTime now(ZoneId zone) {
+ return now(Clock.system(zone));
+ }
+
+ /**
+ * Obtains the current date-time from the specified clock.
+ * <p>
+ * This will query the specified clock to obtain the current date-time.
+ * The offset will be calculated from the time-zone in the clock.
+ * <p>
+ * Using this method allows the use of an alternate clock for testing.
+ * The alternate clock may be introduced using {@link Clock dependency injection}.
+ *
+ * @param clock the clock to use, not null
+ * @return the current date-time, not null
+ */
+ public static OffsetDateTime now(Clock clock) {
+ Objects.requireNonNull(clock, "clock");
+ final Instant now = clock.instant(); // called once
+ return ofInstant(now, clock.getZone().getRules().getOffset(now));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code OffsetDateTime} from a date, time and offset.
+ * <p>
+ * This creates an offset date-time with the specified local date, time and offset.
+ *
+ * @param date the local date, not null
+ * @param time the local time, not null
+ * @param offset the zone offset, not null
+ * @return the offset date-time, not null
+ */
+ public static OffsetDateTime of(LocalDate date, LocalTime time, ZoneOffset offset) {
+ LocalDateTime dt = LocalDateTime.of(date, time);
+ return new OffsetDateTime(dt, offset);
+ }
+
+ /**
+ * Obtains an instance of {@code OffsetDateTime} from a date-time and offset.
+ * <p>
+ * This creates an offset date-time with the specified local date-time and offset.
+ *
+ * @param dateTime the local date-time, not null
+ * @param offset the zone offset, not null
+ * @return the offset date-time, not null
+ */
+ public static OffsetDateTime of(LocalDateTime dateTime, ZoneOffset offset) {
+ return new OffsetDateTime(dateTime, offset);
+ }
+
+ /**
+ * Obtains an instance of {@code OffsetDateTime} from a year, month, day,
+ * hour, minute, second, nanosecond and offset.
+ * <p>
+ * This creates an offset date-time with the seven specified fields.
+ * <p>
+ * This method exists primarily for writing test cases.
+ * Non test-code will typically use other methods to create an offset time.
+ * {@code LocalDateTime} has five additional convenience variants of the
+ * equivalent factory method taking fewer arguments.
+ * They are not provided here to reduce the footprint of the API.
+ *
+ * @param year the year to represent, from MIN_YEAR to MAX_YEAR
+ * @param month the month-of-year to represent, from 1 (January) to 12 (December)
+ * @param dayOfMonth the day-of-month to represent, from 1 to 31
+ * @param hour the hour-of-day to represent, from 0 to 23
+ * @param minute the minute-of-hour to represent, from 0 to 59
+ * @param second the second-of-minute to represent, from 0 to 59
+ * @param nanoOfSecond the nano-of-second to represent, from 0 to 999,999,999
+ * @param offset the zone offset, not null
+ * @return the offset date-time, not null
+ * @throws DateTimeException if the value of any field is out of range, or
+ * if the day-of-month is invalid for the month-year
+ */
+ public static OffsetDateTime of(
+ int year, int month, int dayOfMonth,
+ int hour, int minute, int second, int nanoOfSecond, ZoneOffset offset) {
+ LocalDateTime dt = LocalDateTime.of(year, month, dayOfMonth, hour, minute, second, nanoOfSecond);
+ return new OffsetDateTime(dt, offset);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code OffsetDateTime} from an {@code Instant} and zone ID.
+ * <p>
+ * This creates an offset date-time with the same instant as that specified.
+ * Finding the offset from UTC/Greenwich is simple as there is only one valid
+ * offset for each instant.
+ *
+ * @param instant the instant to create the date-time from, not null
+ * @param zone the time-zone, which may be an offset, not null
+ * @return the offset date-time, not null
+ * @throws DateTimeException if the result exceeds the supported range
+ */
+ public static OffsetDateTime ofInstant(Instant instant, ZoneId zone) {
+ Objects.requireNonNull(instant, "instant");
+ Objects.requireNonNull(zone, "zone");
+ ZoneRules rules = zone.getRules();
+ ZoneOffset offset = rules.getOffset(instant);
+ LocalDateTime ldt = LocalDateTime.ofEpochSecond(instant.getEpochSecond(), instant.getNano(), offset);
+ return new OffsetDateTime(ldt, offset);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code OffsetDateTime} from a temporal object.
+ * <p>
+ * This obtains an offset date-time based on the specified temporal.
+ * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
+ * which this factory converts to an instance of {@code OffsetDateTime}.
+ * <p>
+ * The conversion will first obtain a {@code ZoneOffset} from the temporal object.
+ * It will then try to obtain a {@code LocalDateTime}, falling back to an {@code Instant} if necessary.
+ * The result will be the combination of {@code ZoneOffset} with either
+ * with {@code LocalDateTime} or {@code Instant}.
+ * Implementations are permitted to perform optimizations such as accessing
+ * those fields that are equivalent to the relevant objects.
+ * <p>
+ * This method matches the signature of the functional interface {@link TemporalQuery}
+ * allowing it to be used as a query via method reference, {@code OffsetDateTime::from}.
+ *
+ * @param temporal the temporal object to convert, not null
+ * @return the offset date-time, not null
+ * @throws DateTimeException if unable to convert to an {@code OffsetDateTime}
+ */
+ public static OffsetDateTime from(TemporalAccessor temporal) {
+ if (temporal instanceof OffsetDateTime) {
+ return (OffsetDateTime) temporal;
+ }
+ try {
+ ZoneOffset offset = ZoneOffset.from(temporal);
+ LocalDate date = temporal.query(TemporalQueries.localDate());
+ LocalTime time = temporal.query(TemporalQueries.localTime());
+ if (date != null && time != null) {
+ return OffsetDateTime.of(date, time, offset);
+ } else {
+ Instant instant = Instant.from(temporal);
+ return OffsetDateTime.ofInstant(instant, offset);
+ }
+ } catch (DateTimeException ex) {
+ throw new DateTimeException("Unable to obtain OffsetDateTime from TemporalAccessor: " +
+ temporal + " of type " + temporal.getClass().getName(), ex);
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code OffsetDateTime} from a text string
+ * such as {@code 2007-12-03T10:15:30+01:00}.
+ * <p>
+ * The string must represent a valid date-time and is parsed using
+ * {@link java.time.format.DateTimeFormatter#ISO_OFFSET_DATE_TIME}.
+ *
+ * @param text the text to parse such as "2007-12-03T10:15:30+01:00", not null
+ * @return the parsed offset date-time, not null
+ * @throws DateTimeParseException if the text cannot be parsed
+ */
+ public static OffsetDateTime parse(CharSequence text) {
+ return parse(text, DateTimeFormatter.ISO_OFFSET_DATE_TIME);
+ }
+
+ /**
+ * Obtains an instance of {@code OffsetDateTime} from a text string using a specific formatter.
+ * <p>
+ * The text is parsed using the formatter, returning a date-time.
+ *
+ * @param text the text to parse, not null
+ * @param formatter the formatter to use, not null
+ * @return the parsed offset date-time, not null
+ * @throws DateTimeParseException if the text cannot be parsed
+ */
+ public static OffsetDateTime parse(CharSequence text, DateTimeFormatter formatter) {
+ Objects.requireNonNull(formatter, "formatter");
+ return formatter.parse(text, OffsetDateTime::from);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Constructor.
+ *
+ * @param dateTime the local date-time, not null
+ * @param offset the zone offset, not null
+ */
+ private OffsetDateTime(LocalDateTime dateTime, ZoneOffset offset) {
+ this.dateTime = Objects.requireNonNull(dateTime, "dateTime");
+ this.offset = Objects.requireNonNull(offset, "offset");
+ }
+
+ /**
+ * Returns a new date-time based on this one, returning {@code this} where possible.
+ *
+ * @param dateTime the date-time to create with, not null
+ * @param offset the zone offset to create with, not null
+ */
+ private OffsetDateTime with(LocalDateTime dateTime, ZoneOffset offset) {
+ if (this.dateTime == dateTime && this.offset.equals(offset)) {
+ return this;
+ }
+ return new OffsetDateTime(dateTime, offset);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if the specified field is supported.
+ * <p>
+ * This checks if this date-time can be queried for the specified field.
+ * If false, then calling the {@link #range(TemporalField) range},
+ * {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}
+ * methods will throw an exception.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The supported fields are:
+ * <ul>
+ * <li>{@code NANO_OF_SECOND}
+ * <li>{@code NANO_OF_DAY}
+ * <li>{@code MICRO_OF_SECOND}
+ * <li>{@code MICRO_OF_DAY}
+ * <li>{@code MILLI_OF_SECOND}
+ * <li>{@code MILLI_OF_DAY}
+ * <li>{@code SECOND_OF_MINUTE}
+ * <li>{@code SECOND_OF_DAY}
+ * <li>{@code MINUTE_OF_HOUR}
+ * <li>{@code MINUTE_OF_DAY}
+ * <li>{@code HOUR_OF_AMPM}
+ * <li>{@code CLOCK_HOUR_OF_AMPM}
+ * <li>{@code HOUR_OF_DAY}
+ * <li>{@code CLOCK_HOUR_OF_DAY}
+ * <li>{@code AMPM_OF_DAY}
+ * <li>{@code DAY_OF_WEEK}
+ * <li>{@code ALIGNED_DAY_OF_WEEK_IN_MONTH}
+ * <li>{@code ALIGNED_DAY_OF_WEEK_IN_YEAR}
+ * <li>{@code DAY_OF_MONTH}
+ * <li>{@code DAY_OF_YEAR}
+ * <li>{@code EPOCH_DAY}
+ * <li>{@code ALIGNED_WEEK_OF_MONTH}
+ * <li>{@code ALIGNED_WEEK_OF_YEAR}
+ * <li>{@code MONTH_OF_YEAR}
+ * <li>{@code PROLEPTIC_MONTH}
+ * <li>{@code YEAR_OF_ERA}
+ * <li>{@code YEAR}
+ * <li>{@code ERA}
+ * <li>{@code INSTANT_SECONDS}
+ * <li>{@code OFFSET_SECONDS}
+ * </ul>
+ * All other {@code ChronoField} instances will return false.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}
+ * passing {@code this} as the argument.
+ * Whether the field is supported is determined by the field.
+ *
+ * @param field the field to check, null returns false
+ * @return true if the field is supported on this date-time, false if not
+ */
+ @Override
+ public boolean isSupported(TemporalField field) {
+ return field instanceof ChronoField || (field != null && field.isSupportedBy(this));
+ }
+
+ /**
+ * Checks if the specified unit is supported.
+ * <p>
+ * This checks if the specified unit can be added to, or subtracted from, this date-time.
+ * If false, then calling the {@link #plus(long, TemporalUnit)} and
+ * {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
+ * <p>
+ * If the unit is a {@link ChronoUnit} then the query is implemented here.
+ * The supported units are:
+ * <ul>
+ * <li>{@code NANOS}
+ * <li>{@code MICROS}
+ * <li>{@code MILLIS}
+ * <li>{@code SECONDS}
+ * <li>{@code MINUTES}
+ * <li>{@code HOURS}
+ * <li>{@code HALF_DAYS}
+ * <li>{@code DAYS}
+ * <li>{@code WEEKS}
+ * <li>{@code MONTHS}
+ * <li>{@code YEARS}
+ * <li>{@code DECADES}
+ * <li>{@code CENTURIES}
+ * <li>{@code MILLENNIA}
+ * <li>{@code ERAS}
+ * </ul>
+ * All other {@code ChronoUnit} instances will return false.
+ * <p>
+ * If the unit is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
+ * passing {@code this} as the argument.
+ * Whether the unit is supported is determined by the unit.
+ *
+ * @param unit the unit to check, null returns false
+ * @return true if the unit can be added/subtracted, false if not
+ */
+ @Override // override for Javadoc
+ public boolean isSupported(TemporalUnit unit) {
+ if (unit instanceof ChronoUnit) {
+ return unit != FOREVER;
+ }
+ return unit != null && unit.isSupportedBy(this);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the range of valid values for the specified field.
+ * <p>
+ * The range object expresses the minimum and maximum valid values for a field.
+ * This date-time is used to enhance the accuracy of the returned range.
+ * If it is not possible to return the range, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The {@link #isSupported(TemporalField) supported fields} will return
+ * appropriate range instances.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.rangeRefinedBy(TemporalAccessor)}
+ * passing {@code this} as the argument.
+ * Whether the range can be obtained is determined by the field.
+ *
+ * @param field the field to query the range for, not null
+ * @return the range of valid values for the field, not null
+ * @throws DateTimeException if the range for the field cannot be obtained
+ * @throws UnsupportedTemporalTypeException if the field is not supported
+ */
+ @Override
+ public ValueRange range(TemporalField field) {
+ if (field instanceof ChronoField) {
+ if (field == INSTANT_SECONDS || field == OFFSET_SECONDS) {
+ return field.range();
+ }
+ return dateTime.range(field);
+ }
+ return field.rangeRefinedBy(this);
+ }
+
+ /**
+ * Gets the value of the specified field from this date-time as an {@code int}.
+ * <p>
+ * This queries this date-time for the value of the specified field.
+ * The returned value will always be within the valid range of values for the field.
+ * If it is not possible to return the value, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The {@link #isSupported(TemporalField) supported fields} will return valid
+ * values based on this date-time, except {@code NANO_OF_DAY}, {@code MICRO_OF_DAY},
+ * {@code EPOCH_DAY}, {@code PROLEPTIC_MONTH} and {@code INSTANT_SECONDS} which are too
+ * large to fit in an {@code int} and throw a {@code DateTimeException}.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
+ * passing {@code this} as the argument. Whether the value can be obtained,
+ * and what the value represents, is determined by the field.
+ *
+ * @param field the field to get, not null
+ * @return the value for the field
+ * @throws DateTimeException if a value for the field cannot be obtained or
+ * the value is outside the range of valid values for the field
+ * @throws UnsupportedTemporalTypeException if the field is not supported or
+ * the range of values exceeds an {@code int}
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public int get(TemporalField field) {
+ if (field instanceof ChronoField) {
+ switch ((ChronoField) field) {
+ case INSTANT_SECONDS:
+ throw new UnsupportedTemporalTypeException("Invalid field 'InstantSeconds' for get() method, use getLong() instead");
+ case OFFSET_SECONDS:
+ return getOffset().getTotalSeconds();
+ }
+ return dateTime.get(field);
+ }
+ return Temporal.super.get(field);
+ }
+
+ /**
+ * Gets the value of the specified field from this date-time as a {@code long}.
+ * <p>
+ * This queries this date-time for the value of the specified field.
+ * If it is not possible to return the value, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The {@link #isSupported(TemporalField) supported fields} will return valid
+ * values based on this date-time.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
+ * passing {@code this} as the argument. Whether the value can be obtained,
+ * and what the value represents, is determined by the field.
+ *
+ * @param field the field to get, not null
+ * @return the value for the field
+ * @throws DateTimeException if a value for the field cannot be obtained
+ * @throws UnsupportedTemporalTypeException if the field is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public long getLong(TemporalField field) {
+ if (field instanceof ChronoField) {
+ switch ((ChronoField) field) {
+ case INSTANT_SECONDS: return toEpochSecond();
+ case OFFSET_SECONDS: return getOffset().getTotalSeconds();
+ }
+ return dateTime.getLong(field);
+ }
+ return field.getFrom(this);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the zone offset, such as '+01:00'.
+ * <p>
+ * This is the offset of the local date-time from UTC/Greenwich.
+ *
+ * @return the zone offset, not null
+ */
+ public ZoneOffset getOffset() {
+ return offset;
+ }
+
+ /**
+ * Returns a copy of this {@code OffsetDateTime} with the specified offset ensuring
+ * that the result has the same local date-time.
+ * <p>
+ * This method returns an object with the same {@code LocalDateTime} and the specified {@code ZoneOffset}.
+ * No calculation is needed or performed.
+ * For example, if this time represents {@code 2007-12-03T10:30+02:00} and the offset specified is
+ * {@code +03:00}, then this method will return {@code 2007-12-03T10:30+03:00}.
+ * <p>
+ * To take into account the difference between the offsets, and adjust the time fields,
+ * use {@link #withOffsetSameInstant}.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param offset the zone offset to change to, not null
+ * @return an {@code OffsetDateTime} based on this date-time with the requested offset, not null
+ */
+ public OffsetDateTime withOffsetSameLocal(ZoneOffset offset) {
+ return with(dateTime, offset);
+ }
+
+ /**
+ * Returns a copy of this {@code OffsetDateTime} with the specified offset ensuring
+ * that the result is at the same instant.
+ * <p>
+ * This method returns an object with the specified {@code ZoneOffset} and a {@code LocalDateTime}
+ * adjusted by the difference between the two offsets.
+ * This will result in the old and new objects representing the same instant.
+ * This is useful for finding the local time in a different offset.
+ * For example, if this time represents {@code 2007-12-03T10:30+02:00} and the offset specified is
+ * {@code +03:00}, then this method will return {@code 2007-12-03T11:30+03:00}.
+ * <p>
+ * To change the offset without adjusting the local time use {@link #withOffsetSameLocal}.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param offset the zone offset to change to, not null
+ * @return an {@code OffsetDateTime} based on this date-time with the requested offset, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public OffsetDateTime withOffsetSameInstant(ZoneOffset offset) {
+ if (offset.equals(this.offset)) {
+ return this;
+ }
+ int difference = offset.getTotalSeconds() - this.offset.getTotalSeconds();
+ LocalDateTime adjusted = dateTime.plusSeconds(difference);
+ return new OffsetDateTime(adjusted, offset);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the {@code LocalDateTime} part of this date-time.
+ * <p>
+ * This returns a {@code LocalDateTime} with the same year, month, day and time
+ * as this date-time.
+ *
+ * @return the local date-time part of this date-time, not null
+ */
+ public LocalDateTime toLocalDateTime() {
+ return dateTime;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the {@code LocalDate} part of this date-time.
+ * <p>
+ * This returns a {@code LocalDate} with the same year, month and day
+ * as this date-time.
+ *
+ * @return the date part of this date-time, not null
+ */
+ public LocalDate toLocalDate() {
+ return dateTime.toLocalDate();
+ }
+
+ /**
+ * Gets the year field.
+ * <p>
+ * This method returns the primitive {@code int} value for the year.
+ * <p>
+ * The year returned by this method is proleptic as per {@code get(YEAR)}.
+ * To obtain the year-of-era, use {@code get(YEAR_OF_ERA)}.
+ *
+ * @return the year, from MIN_YEAR to MAX_YEAR
+ */
+ public int getYear() {
+ return dateTime.getYear();
+ }
+
+ /**
+ * Gets the month-of-year field from 1 to 12.
+ * <p>
+ * This method returns the month as an {@code int} from 1 to 12.
+ * Application code is frequently clearer if the enum {@link Month}
+ * is used by calling {@link #getMonth()}.
+ *
+ * @return the month-of-year, from 1 to 12
+ * @see #getMonth()
+ */
+ public int getMonthValue() {
+ return dateTime.getMonthValue();
+ }
+
+ /**
+ * Gets the month-of-year field using the {@code Month} enum.
+ * <p>
+ * This method returns the enum {@link Month} for the month.
+ * This avoids confusion as to what {@code int} values mean.
+ * If you need access to the primitive {@code int} value then the enum
+ * provides the {@link Month#getValue() int value}.
+ *
+ * @return the month-of-year, not null
+ * @see #getMonthValue()
+ */
+ public Month getMonth() {
+ return dateTime.getMonth();
+ }
+
+ /**
+ * Gets the day-of-month field.
+ * <p>
+ * This method returns the primitive {@code int} value for the day-of-month.
+ *
+ * @return the day-of-month, from 1 to 31
+ */
+ public int getDayOfMonth() {
+ return dateTime.getDayOfMonth();
+ }
+
+ /**
+ * Gets the day-of-year field.
+ * <p>
+ * This method returns the primitive {@code int} value for the day-of-year.
+ *
+ * @return the day-of-year, from 1 to 365, or 366 in a leap year
+ */
+ public int getDayOfYear() {
+ return dateTime.getDayOfYear();
+ }
+
+ /**
+ * Gets the day-of-week field, which is an enum {@code DayOfWeek}.
+ * <p>
+ * This method returns the enum {@link DayOfWeek} for the day-of-week.
+ * This avoids confusion as to what {@code int} values mean.
+ * If you need access to the primitive {@code int} value then the enum
+ * provides the {@link DayOfWeek#getValue() int value}.
+ * <p>
+ * Additional information can be obtained from the {@code DayOfWeek}.
+ * This includes textual names of the values.
+ *
+ * @return the day-of-week, not null
+ */
+ public DayOfWeek getDayOfWeek() {
+ return dateTime.getDayOfWeek();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the {@code LocalTime} part of this date-time.
+ * <p>
+ * This returns a {@code LocalTime} with the same hour, minute, second and
+ * nanosecond as this date-time.
+ *
+ * @return the time part of this date-time, not null
+ */
+ public LocalTime toLocalTime() {
+ return dateTime.toLocalTime();
+ }
+
+ /**
+ * Gets the hour-of-day field.
+ *
+ * @return the hour-of-day, from 0 to 23
+ */
+ public int getHour() {
+ return dateTime.getHour();
+ }
+
+ /**
+ * Gets the minute-of-hour field.
+ *
+ * @return the minute-of-hour, from 0 to 59
+ */
+ public int getMinute() {
+ return dateTime.getMinute();
+ }
+
+ /**
+ * Gets the second-of-minute field.
+ *
+ * @return the second-of-minute, from 0 to 59
+ */
+ public int getSecond() {
+ return dateTime.getSecond();
+ }
+
+ /**
+ * Gets the nano-of-second field.
+ *
+ * @return the nano-of-second, from 0 to 999,999,999
+ */
+ public int getNano() {
+ return dateTime.getNano();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns an adjusted copy of this date-time.
+ * <p>
+ * This returns an {@code OffsetDateTime}, based on this one, with the date-time adjusted.
+ * The adjustment takes place using the specified adjuster strategy object.
+ * Read the documentation of the adjuster to understand what adjustment will be made.
+ * <p>
+ * A simple adjuster might simply set the one of the fields, such as the year field.
+ * A more complex adjuster might set the date to the last day of the month.
+ * A selection of common adjustments is provided in
+ * {@link java.time.temporal.TemporalAdjusters TemporalAdjusters}.
+ * These include finding the "last day of the month" and "next Wednesday".
+ * Key date-time classes also implement the {@code TemporalAdjuster} interface,
+ * such as {@link Month} and {@link java.time.MonthDay MonthDay}.
+ * The adjuster is responsible for handling special cases, such as the varying
+ * lengths of month and leap years.
+ * <p>
+ * For example this code returns a date on the last day of July:
+ * <pre>
+ * import static java.time.Month.*;
+ * import static java.time.temporal.TemporalAdjusters.*;
+ *
+ * result = offsetDateTime.with(JULY).with(lastDayOfMonth());
+ * </pre>
+ * <p>
+ * The classes {@link LocalDate}, {@link LocalTime} and {@link ZoneOffset} implement
+ * {@code TemporalAdjuster}, thus this method can be used to change the date, time or offset:
+ * <pre>
+ * result = offsetDateTime.with(date);
+ * result = offsetDateTime.with(time);
+ * result = offsetDateTime.with(offset);
+ * </pre>
+ * <p>
+ * The result of this method is obtained by invoking the
+ * {@link TemporalAdjuster#adjustInto(Temporal)} method on the
+ * specified adjuster passing {@code this} as the argument.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param adjuster the adjuster to use, not null
+ * @return an {@code OffsetDateTime} based on {@code this} with the adjustment made, not null
+ * @throws DateTimeException if the adjustment cannot be made
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public OffsetDateTime with(TemporalAdjuster adjuster) {
+ // optimizations
+ if (adjuster instanceof LocalDate || adjuster instanceof LocalTime || adjuster instanceof LocalDateTime) {
+ return with(dateTime.with(adjuster), offset);
+ } else if (adjuster instanceof Instant) {
+ return ofInstant((Instant) adjuster, offset);
+ } else if (adjuster instanceof ZoneOffset) {
+ return with(dateTime, (ZoneOffset) adjuster);
+ } else if (adjuster instanceof OffsetDateTime) {
+ return (OffsetDateTime) adjuster;
+ }
+ return (OffsetDateTime) adjuster.adjustInto(this);
+ }
+
+ /**
+ * Returns a copy of this date-time with the specified field set to a new value.
+ * <p>
+ * This returns an {@code OffsetDateTime}, based on this one, with the value
+ * for the specified field changed.
+ * This can be used to change any supported field, such as the year, month or day-of-month.
+ * If it is not possible to set the value, because the field is not supported or for
+ * some other reason, an exception is thrown.
+ * <p>
+ * In some cases, changing the specified field can cause the resulting date-time to become invalid,
+ * such as changing the month from 31st January to February would make the day-of-month invalid.
+ * In cases like this, the field is responsible for resolving the date. Typically it will choose
+ * the previous valid date, which would be the last valid day of February in this example.
+ * <p>
+ * If the field is a {@link ChronoField} then the adjustment is implemented here.
+ * <p>
+ * The {@code INSTANT_SECONDS} field will return a date-time with the specified instant.
+ * The offset and nano-of-second are unchanged.
+ * If the new instant value is outside the valid range then a {@code DateTimeException} will be thrown.
+ * <p>
+ * The {@code OFFSET_SECONDS} field will return a date-time with the specified offset.
+ * The local date-time is unaltered. If the new offset value is outside the valid range
+ * then a {@code DateTimeException} will be thrown.
+ * <p>
+ * The other {@link #isSupported(TemporalField) supported fields} will behave as per
+ * the matching method on {@link LocalDateTime#with(TemporalField, long) LocalDateTime}.
+ * In this case, the offset is not part of the calculation and will be unchanged.
+ * <p>
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.adjustInto(Temporal, long)}
+ * passing {@code this} as the argument. In this case, the field determines
+ * whether and how to adjust the instant.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param field the field to set in the result, not null
+ * @param newValue the new value of the field in the result
+ * @return an {@code OffsetDateTime} based on {@code this} with the specified field set, not null
+ * @throws DateTimeException if the field cannot be set
+ * @throws UnsupportedTemporalTypeException if the field is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public OffsetDateTime with(TemporalField field, long newValue) {
+ if (field instanceof ChronoField) {
+ ChronoField f = (ChronoField) field;
+ switch (f) {
+ case INSTANT_SECONDS: return ofInstant(Instant.ofEpochSecond(newValue, getNano()), offset);
+ case OFFSET_SECONDS: {
+ return with(dateTime, ZoneOffset.ofTotalSeconds(f.checkValidIntValue(newValue)));
+ }
+ }
+ return with(dateTime.with(field, newValue), offset);
+ }
+ return field.adjustInto(this, newValue);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this {@code OffsetDateTime} with the year altered.
+ * <p>
+ * The time and offset do not affect the calculation and will be the same in the result.
+ * If the day-of-month is invalid for the year, it will be changed to the last valid day of the month.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param year the year to set in the result, from MIN_YEAR to MAX_YEAR
+ * @return an {@code OffsetDateTime} based on this date-time with the requested year, not null
+ * @throws DateTimeException if the year value is invalid
+ */
+ public OffsetDateTime withYear(int year) {
+ return with(dateTime.withYear(year), offset);
+ }
+
+ /**
+ * Returns a copy of this {@code OffsetDateTime} with the month-of-year altered.
+ * <p>
+ * The time and offset do not affect the calculation and will be the same in the result.
+ * If the day-of-month is invalid for the year, it will be changed to the last valid day of the month.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param month the month-of-year to set in the result, from 1 (January) to 12 (December)
+ * @return an {@code OffsetDateTime} based on this date-time with the requested month, not null
+ * @throws DateTimeException if the month-of-year value is invalid
+ */
+ public OffsetDateTime withMonth(int month) {
+ return with(dateTime.withMonth(month), offset);
+ }
+
+ /**
+ * Returns a copy of this {@code OffsetDateTime} with the day-of-month altered.
+ * <p>
+ * If the resulting {@code OffsetDateTime} is invalid, an exception is thrown.
+ * The time and offset do not affect the calculation and will be the same in the result.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param dayOfMonth the day-of-month to set in the result, from 1 to 28-31
+ * @return an {@code OffsetDateTime} based on this date-time with the requested day, not null
+ * @throws DateTimeException if the day-of-month value is invalid,
+ * or if the day-of-month is invalid for the month-year
+ */
+ public OffsetDateTime withDayOfMonth(int dayOfMonth) {
+ return with(dateTime.withDayOfMonth(dayOfMonth), offset);
+ }
+
+ /**
+ * Returns a copy of this {@code OffsetDateTime} with the day-of-year altered.
+ * <p>
+ * The time and offset do not affect the calculation and will be the same in the result.
+ * If the resulting {@code OffsetDateTime} is invalid, an exception is thrown.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param dayOfYear the day-of-year to set in the result, from 1 to 365-366
+ * @return an {@code OffsetDateTime} based on this date with the requested day, not null
+ * @throws DateTimeException if the day-of-year value is invalid,
+ * or if the day-of-year is invalid for the year
+ */
+ public OffsetDateTime withDayOfYear(int dayOfYear) {
+ return with(dateTime.withDayOfYear(dayOfYear), offset);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this {@code OffsetDateTime} with the hour-of-day altered.
+ * <p>
+ * The date and offset do not affect the calculation and will be the same in the result.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param hour the hour-of-day to set in the result, from 0 to 23
+ * @return an {@code OffsetDateTime} based on this date-time with the requested hour, not null
+ * @throws DateTimeException if the hour value is invalid
+ */
+ public OffsetDateTime withHour(int hour) {
+ return with(dateTime.withHour(hour), offset);
+ }
+
+ /**
+ * Returns a copy of this {@code OffsetDateTime} with the minute-of-hour altered.
+ * <p>
+ * The date and offset do not affect the calculation and will be the same in the result.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param minute the minute-of-hour to set in the result, from 0 to 59
+ * @return an {@code OffsetDateTime} based on this date-time with the requested minute, not null
+ * @throws DateTimeException if the minute value is invalid
+ */
+ public OffsetDateTime withMinute(int minute) {
+ return with(dateTime.withMinute(minute), offset);
+ }
+
+ /**
+ * Returns a copy of this {@code OffsetDateTime} with the second-of-minute altered.
+ * <p>
+ * The date and offset do not affect the calculation and will be the same in the result.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param second the second-of-minute to set in the result, from 0 to 59
+ * @return an {@code OffsetDateTime} based on this date-time with the requested second, not null
+ * @throws DateTimeException if the second value is invalid
+ */
+ public OffsetDateTime withSecond(int second) {
+ return with(dateTime.withSecond(second), offset);
+ }
+
+ /**
+ * Returns a copy of this {@code OffsetDateTime} with the nano-of-second altered.
+ * <p>
+ * The date and offset do not affect the calculation and will be the same in the result.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param nanoOfSecond the nano-of-second to set in the result, from 0 to 999,999,999
+ * @return an {@code OffsetDateTime} based on this date-time with the requested nanosecond, not null
+ * @throws DateTimeException if the nano value is invalid
+ */
+ public OffsetDateTime withNano(int nanoOfSecond) {
+ return with(dateTime.withNano(nanoOfSecond), offset);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this {@code OffsetDateTime} with the time truncated.
+ * <p>
+ * Truncation returns a copy of the original date-time with fields
+ * smaller than the specified unit set to zero.
+ * For example, truncating with the {@link ChronoUnit#MINUTES minutes} unit
+ * will set the second-of-minute and nano-of-second field to zero.
+ * <p>
+ * The unit must have a {@linkplain TemporalUnit#getDuration() duration}
+ * that divides into the length of a standard day without remainder.
+ * This includes all supplied time units on {@link ChronoUnit} and
+ * {@link ChronoUnit#DAYS DAYS}. Other units throw an exception.
+ * <p>
+ * The offset does not affect the calculation and will be the same in the result.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param unit the unit to truncate to, not null
+ * @return an {@code OffsetDateTime} based on this date-time with the time truncated, not null
+ * @throws DateTimeException if unable to truncate
+ * @throws UnsupportedTemporalTypeException if the unit is not supported
+ */
+ public OffsetDateTime truncatedTo(TemporalUnit unit) {
+ return with(dateTime.truncatedTo(unit), offset);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this date-time with the specified amount added.
+ * <p>
+ * This returns an {@code OffsetDateTime}, based on this one, with the specified amount added.
+ * The amount is typically {@link Period} or {@link Duration} but may be
+ * any other type implementing the {@link TemporalAmount} interface.
+ * <p>
+ * The calculation is delegated to the amount object by calling
+ * {@link TemporalAmount#addTo(Temporal)}. The amount implementation is free
+ * to implement the addition in any way it wishes, however it typically
+ * calls back to {@link #plus(long, TemporalUnit)}. Consult the documentation
+ * of the amount implementation to determine if it can be successfully added.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param amountToAdd the amount to add, not null
+ * @return an {@code OffsetDateTime} based on this date-time with the addition made, not null
+ * @throws DateTimeException if the addition cannot be made
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public OffsetDateTime plus(TemporalAmount amountToAdd) {
+ return (OffsetDateTime) amountToAdd.addTo(this);
+ }
+
+ /**
+ * Returns a copy of this date-time with the specified amount added.
+ * <p>
+ * This returns an {@code OffsetDateTime}, based on this one, with the amount
+ * in terms of the unit added. If it is not possible to add the amount, because the
+ * unit is not supported or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoUnit} then the addition is implemented by
+ * {@link LocalDateTime#plus(long, TemporalUnit)}.
+ * The offset is not part of the calculation and will be unchanged in the result.
+ * <p>
+ * If the field is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.addTo(Temporal, long)}
+ * passing {@code this} as the argument. In this case, the unit determines
+ * whether and how to perform the addition.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param amountToAdd the amount of the unit to add to the result, may be negative
+ * @param unit the unit of the amount to add, not null
+ * @return an {@code OffsetDateTime} based on this date-time with the specified amount added, not null
+ * @throws DateTimeException if the addition cannot be made
+ * @throws UnsupportedTemporalTypeException if the unit is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public OffsetDateTime plus(long amountToAdd, TemporalUnit unit) {
+ if (unit instanceof ChronoUnit) {
+ return with(dateTime.plus(amountToAdd, unit), offset);
+ }
+ return unit.addTo(this, amountToAdd);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this {@code OffsetDateTime} with the specified number of years added.
+ * <p>
+ * This method adds the specified amount to the years field in three steps:
+ * <ol>
+ * <li>Add the input years to the year field</li>
+ * <li>Check if the resulting date would be invalid</li>
+ * <li>Adjust the day-of-month to the last valid day if necessary</li>
+ * </ol>
+ * <p>
+ * For example, 2008-02-29 (leap year) plus one year would result in the
+ * invalid date 2009-02-29 (standard year). Instead of returning an invalid
+ * result, the last valid day of the month, 2009-02-28, is selected instead.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param years the years to add, may be negative
+ * @return an {@code OffsetDateTime} based on this date-time with the years added, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public OffsetDateTime plusYears(long years) {
+ return with(dateTime.plusYears(years), offset);
+ }
+
+ /**
+ * Returns a copy of this {@code OffsetDateTime} with the specified number of months added.
+ * <p>
+ * This method adds the specified amount to the months field in three steps:
+ * <ol>
+ * <li>Add the input months to the month-of-year field</li>
+ * <li>Check if the resulting date would be invalid</li>
+ * <li>Adjust the day-of-month to the last valid day if necessary</li>
+ * </ol>
+ * <p>
+ * For example, 2007-03-31 plus one month would result in the invalid date
+ * 2007-04-31. Instead of returning an invalid result, the last valid day
+ * of the month, 2007-04-30, is selected instead.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param months the months to add, may be negative
+ * @return an {@code OffsetDateTime} based on this date-time with the months added, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public OffsetDateTime plusMonths(long months) {
+ return with(dateTime.plusMonths(months), offset);
+ }
+
+ /**
+ * Returns a copy of this OffsetDateTime with the specified number of weeks added.
+ * <p>
+ * This method adds the specified amount in weeks to the days field incrementing
+ * the month and year fields as necessary to ensure the result remains valid.
+ * The result is only invalid if the maximum/minimum year is exceeded.
+ * <p>
+ * For example, 2008-12-31 plus one week would result in 2009-01-07.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param weeks the weeks to add, may be negative
+ * @return an {@code OffsetDateTime} based on this date-time with the weeks added, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public OffsetDateTime plusWeeks(long weeks) {
+ return with(dateTime.plusWeeks(weeks), offset);
+ }
+
+ /**
+ * Returns a copy of this OffsetDateTime with the specified number of days added.
+ * <p>
+ * This method adds the specified amount to the days field incrementing the
+ * month and year fields as necessary to ensure the result remains valid.
+ * The result is only invalid if the maximum/minimum year is exceeded.
+ * <p>
+ * For example, 2008-12-31 plus one day would result in 2009-01-01.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param days the days to add, may be negative
+ * @return an {@code OffsetDateTime} based on this date-time with the days added, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public OffsetDateTime plusDays(long days) {
+ return with(dateTime.plusDays(days), offset);
+ }
+
+ /**
+ * Returns a copy of this {@code OffsetDateTime} with the specified number of hours added.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param hours the hours to add, may be negative
+ * @return an {@code OffsetDateTime} based on this date-time with the hours added, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public OffsetDateTime plusHours(long hours) {
+ return with(dateTime.plusHours(hours), offset);
+ }
+
+ /**
+ * Returns a copy of this {@code OffsetDateTime} with the specified number of minutes added.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param minutes the minutes to add, may be negative
+ * @return an {@code OffsetDateTime} based on this date-time with the minutes added, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public OffsetDateTime plusMinutes(long minutes) {
+ return with(dateTime.plusMinutes(minutes), offset);
+ }
+
+ /**
+ * Returns a copy of this {@code OffsetDateTime} with the specified number of seconds added.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param seconds the seconds to add, may be negative
+ * @return an {@code OffsetDateTime} based on this date-time with the seconds added, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public OffsetDateTime plusSeconds(long seconds) {
+ return with(dateTime.plusSeconds(seconds), offset);
+ }
+
+ /**
+ * Returns a copy of this {@code OffsetDateTime} with the specified number of nanoseconds added.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param nanos the nanos to add, may be negative
+ * @return an {@code OffsetDateTime} based on this date-time with the nanoseconds added, not null
+ * @throws DateTimeException if the unit cannot be added to this type
+ */
+ public OffsetDateTime plusNanos(long nanos) {
+ return with(dateTime.plusNanos(nanos), offset);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this date-time with the specified amount subtracted.
+ * <p>
+ * This returns an {@code OffsetDateTime}, based on this one, with the specified amount subtracted.
+ * The amount is typically {@link Period} or {@link Duration} but may be
+ * any other type implementing the {@link TemporalAmount} interface.
+ * <p>
+ * The calculation is delegated to the amount object by calling
+ * {@link TemporalAmount#subtractFrom(Temporal)}. The amount implementation is free
+ * to implement the subtraction in any way it wishes, however it typically
+ * calls back to {@link #minus(long, TemporalUnit)}. Consult the documentation
+ * of the amount implementation to determine if it can be successfully subtracted.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param amountToSubtract the amount to subtract, not null
+ * @return an {@code OffsetDateTime} based on this date-time with the subtraction made, not null
+ * @throws DateTimeException if the subtraction cannot be made
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public OffsetDateTime minus(TemporalAmount amountToSubtract) {
+ return (OffsetDateTime) amountToSubtract.subtractFrom(this);
+ }
+
+ /**
+ * Returns a copy of this date-time with the specified amount subtracted.
+ * <p>
+ * This returns an {@code OffsetDateTime}, based on this one, with the amount
+ * in terms of the unit subtracted. If it is not possible to subtract the amount,
+ * because the unit is not supported or for some other reason, an exception is thrown.
+ * <p>
+ * This method is equivalent to {@link #plus(long, TemporalUnit)} with the amount negated.
+ * See that method for a full description of how addition, and thus subtraction, works.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param amountToSubtract the amount of the unit to subtract from the result, may be negative
+ * @param unit the unit of the amount to subtract, not null
+ * @return an {@code OffsetDateTime} based on this date-time with the specified amount subtracted, not null
+ * @throws DateTimeException if the subtraction cannot be made
+ * @throws UnsupportedTemporalTypeException if the unit is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public OffsetDateTime minus(long amountToSubtract, TemporalUnit unit) {
+ return (amountToSubtract == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this {@code OffsetDateTime} with the specified number of years subtracted.
+ * <p>
+ * This method subtracts the specified amount from the years field in three steps:
+ * <ol>
+ * <li>Subtract the input years from the year field</li>
+ * <li>Check if the resulting date would be invalid</li>
+ * <li>Adjust the day-of-month to the last valid day if necessary</li>
+ * </ol>
+ * <p>
+ * For example, 2008-02-29 (leap year) minus one year would result in the
+ * invalid date 2009-02-29 (standard year). Instead of returning an invalid
+ * result, the last valid day of the month, 2009-02-28, is selected instead.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param years the years to subtract, may be negative
+ * @return an {@code OffsetDateTime} based on this date-time with the years subtracted, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public OffsetDateTime minusYears(long years) {
+ return (years == Long.MIN_VALUE ? plusYears(Long.MAX_VALUE).plusYears(1) : plusYears(-years));
+ }
+
+ /**
+ * Returns a copy of this {@code OffsetDateTime} with the specified number of months subtracted.
+ * <p>
+ * This method subtracts the specified amount from the months field in three steps:
+ * <ol>
+ * <li>Subtract the input months from the month-of-year field</li>
+ * <li>Check if the resulting date would be invalid</li>
+ * <li>Adjust the day-of-month to the last valid day if necessary</li>
+ * </ol>
+ * <p>
+ * For example, 2007-03-31 minus one month would result in the invalid date
+ * 2007-04-31. Instead of returning an invalid result, the last valid day
+ * of the month, 2007-04-30, is selected instead.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param months the months to subtract, may be negative
+ * @return an {@code OffsetDateTime} based on this date-time with the months subtracted, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public OffsetDateTime minusMonths(long months) {
+ return (months == Long.MIN_VALUE ? plusMonths(Long.MAX_VALUE).plusMonths(1) : plusMonths(-months));
+ }
+
+ /**
+ * Returns a copy of this {@code OffsetDateTime} with the specified number of weeks subtracted.
+ * <p>
+ * This method subtracts the specified amount in weeks from the days field decrementing
+ * the month and year fields as necessary to ensure the result remains valid.
+ * The result is only invalid if the maximum/minimum year is exceeded.
+ * <p>
+ * For example, 2008-12-31 minus one week would result in 2009-01-07.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param weeks the weeks to subtract, may be negative
+ * @return an {@code OffsetDateTime} based on this date-time with the weeks subtracted, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public OffsetDateTime minusWeeks(long weeks) {
+ return (weeks == Long.MIN_VALUE ? plusWeeks(Long.MAX_VALUE).plusWeeks(1) : plusWeeks(-weeks));
+ }
+
+ /**
+ * Returns a copy of this {@code OffsetDateTime} with the specified number of days subtracted.
+ * <p>
+ * This method subtracts the specified amount from the days field decrementing the
+ * month and year fields as necessary to ensure the result remains valid.
+ * The result is only invalid if the maximum/minimum year is exceeded.
+ * <p>
+ * For example, 2008-12-31 minus one day would result in 2009-01-01.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param days the days to subtract, may be negative
+ * @return an {@code OffsetDateTime} based on this date-time with the days subtracted, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public OffsetDateTime minusDays(long days) {
+ return (days == Long.MIN_VALUE ? plusDays(Long.MAX_VALUE).plusDays(1) : plusDays(-days));
+ }
+
+ /**
+ * Returns a copy of this {@code OffsetDateTime} with the specified number of hours subtracted.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param hours the hours to subtract, may be negative
+ * @return an {@code OffsetDateTime} based on this date-time with the hours subtracted, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public OffsetDateTime minusHours(long hours) {
+ return (hours == Long.MIN_VALUE ? plusHours(Long.MAX_VALUE).plusHours(1) : plusHours(-hours));
+ }
+
+ /**
+ * Returns a copy of this {@code OffsetDateTime} with the specified number of minutes subtracted.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param minutes the minutes to subtract, may be negative
+ * @return an {@code OffsetDateTime} based on this date-time with the minutes subtracted, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public OffsetDateTime minusMinutes(long minutes) {
+ return (minutes == Long.MIN_VALUE ? plusMinutes(Long.MAX_VALUE).plusMinutes(1) : plusMinutes(-minutes));
+ }
+
+ /**
+ * Returns a copy of this {@code OffsetDateTime} with the specified number of seconds subtracted.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param seconds the seconds to subtract, may be negative
+ * @return an {@code OffsetDateTime} based on this date-time with the seconds subtracted, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public OffsetDateTime minusSeconds(long seconds) {
+ return (seconds == Long.MIN_VALUE ? plusSeconds(Long.MAX_VALUE).plusSeconds(1) : plusSeconds(-seconds));
+ }
+
+ /**
+ * Returns a copy of this {@code OffsetDateTime} with the specified number of nanoseconds subtracted.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param nanos the nanos to subtract, may be negative
+ * @return an {@code OffsetDateTime} based on this date-time with the nanoseconds subtracted, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public OffsetDateTime minusNanos(long nanos) {
+ return (nanos == Long.MIN_VALUE ? plusNanos(Long.MAX_VALUE).plusNanos(1) : plusNanos(-nanos));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Queries this date-time using the specified query.
+ * <p>
+ * This queries this date-time using the specified query strategy object.
+ * The {@code TemporalQuery} object defines the logic to be used to
+ * obtain the result. Read the documentation of the query to understand
+ * what the result of this method will be.
+ * <p>
+ * The result of this method is obtained by invoking the
+ * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the
+ * specified query passing {@code this} as the argument.
+ *
+ * @param <R> the type of the result
+ * @param query the query to invoke, not null
+ * @return the query result, null may be returned (defined by the query)
+ * @throws DateTimeException if unable to query (defined by the query)
+ * @throws ArithmeticException if numeric overflow occurs (defined by the query)
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public <R> R query(TemporalQuery<R> query) {
+ if (query == TemporalQueries.offset() || query == TemporalQueries.zone()) {
+ return (R) getOffset();
+ } else if (query == TemporalQueries.zoneId()) {
+ return null;
+ } else if (query == TemporalQueries.localDate()) {
+ return (R) toLocalDate();
+ } else if (query == TemporalQueries.localTime()) {
+ return (R) toLocalTime();
+ } else if (query == TemporalQueries.chronology()) {
+ return (R) IsoChronology.INSTANCE;
+ } else if (query == TemporalQueries.precision()) {
+ return (R) NANOS;
+ }
+ // inline TemporalAccessor.super.query(query) as an optimization
+ // non-JDK classes are not permitted to make this optimization
+ return query.queryFrom(this);
+ }
+
+ /**
+ * Adjusts the specified temporal object to have the same offset, date
+ * and time as this object.
+ * <p>
+ * This returns a temporal object of the same observable type as the input
+ * with the offset, date and time changed to be the same as this.
+ * <p>
+ * The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)}
+ * three times, passing {@link ChronoField#EPOCH_DAY},
+ * {@link ChronoField#NANO_OF_DAY} and {@link ChronoField#OFFSET_SECONDS} as the fields.
+ * <p>
+ * In most cases, it is clearer to reverse the calling pattern by using
+ * {@link Temporal#with(TemporalAdjuster)}:
+ * <pre>
+ * // these two lines are equivalent, but the second approach is recommended
+ * temporal = thisOffsetDateTime.adjustInto(temporal);
+ * temporal = temporal.with(thisOffsetDateTime);
+ * </pre>
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param temporal the target object to be adjusted, not null
+ * @return the adjusted object, not null
+ * @throws DateTimeException if unable to make the adjustment
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public Temporal adjustInto(Temporal temporal) {
+ // OffsetDateTime is treated as three separate fields, not an instant
+ // this produces the most consistent set of results overall
+ // the offset is set after the date and time, as it is typically a small
+ // tweak to the result, with ZonedDateTime frequently ignoring the offset
+ return temporal
+ .with(EPOCH_DAY, toLocalDate().toEpochDay())
+ .with(NANO_OF_DAY, toLocalTime().toNanoOfDay())
+ .with(OFFSET_SECONDS, getOffset().getTotalSeconds());
+ }
+
+ /**
+ * Calculates the amount of time until another date-time in terms of the specified unit.
+ * <p>
+ * This calculates the amount of time between two {@code OffsetDateTime}
+ * objects in terms of a single {@code TemporalUnit}.
+ * The start and end points are {@code this} and the specified date-time.
+ * The result will be negative if the end is before the start.
+ * For example, the amount in days between two date-times can be calculated
+ * using {@code startDateTime.until(endDateTime, DAYS)}.
+ * <p>
+ * The {@code Temporal} passed to this method is converted to a
+ * {@code OffsetDateTime} using {@link #from(TemporalAccessor)}.
+ * If the offset differs between the two date-times, the specified
+ * end date-time is normalized to have the same offset as this date-time.
+ * <p>
+ * The calculation returns a whole number, representing the number of
+ * complete units between the two date-times.
+ * For example, the amount in months between 2012-06-15T00:00Z and 2012-08-14T23:59Z
+ * will only be one month as it is one minute short of two months.
+ * <p>
+ * There are two equivalent ways of using this method.
+ * The first is to invoke this method.
+ * The second is to use {@link TemporalUnit#between(Temporal, Temporal)}:
+ * <pre>
+ * // these two lines are equivalent
+ * amount = start.until(end, MONTHS);
+ * amount = MONTHS.between(start, end);
+ * </pre>
+ * The choice should be made based on which makes the code more readable.
+ * <p>
+ * The calculation is implemented in this method for {@link ChronoUnit}.
+ * The units {@code NANOS}, {@code MICROS}, {@code MILLIS}, {@code SECONDS},
+ * {@code MINUTES}, {@code HOURS} and {@code HALF_DAYS}, {@code DAYS},
+ * {@code WEEKS}, {@code MONTHS}, {@code YEARS}, {@code DECADES},
+ * {@code CENTURIES}, {@code MILLENNIA} and {@code ERAS} are supported.
+ * Other {@code ChronoUnit} values will throw an exception.
+ * <p>
+ * If the unit is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.between(Temporal, Temporal)}
+ * passing {@code this} as the first argument and the converted input temporal
+ * as the second argument.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param endExclusive the end date, exclusive, which is converted to an {@code OffsetDateTime}, not null
+ * @param unit the unit to measure the amount in, not null
+ * @return the amount of time between this date-time and the end date-time
+ * @throws DateTimeException if the amount cannot be calculated, or the end
+ * temporal cannot be converted to an {@code OffsetDateTime}
+ * @throws UnsupportedTemporalTypeException if the unit is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public long until(Temporal endExclusive, TemporalUnit unit) {
+ OffsetDateTime end = OffsetDateTime.from(endExclusive);
+ if (unit instanceof ChronoUnit) {
+ end = end.withOffsetSameInstant(offset);
+ return dateTime.until(end.dateTime, unit);
+ }
+ return unit.between(this, end);
+ }
+
+ /**
+ * Formats this date-time using the specified formatter.
+ * <p>
+ * This date-time will be passed to the formatter to produce a string.
+ *
+ * @param formatter the formatter to use, not null
+ * @return the formatted date-time string, not null
+ * @throws DateTimeException if an error occurs during printing
+ */
+ public String format(DateTimeFormatter formatter) {
+ Objects.requireNonNull(formatter, "formatter");
+ return formatter.format(this);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Combines this date-time with a time-zone to create a {@code ZonedDateTime}
+ * ensuring that the result has the same instant.
+ * <p>
+ * This returns a {@code ZonedDateTime} formed from this date-time and the specified time-zone.
+ * This conversion will ignore the visible local date-time and use the underlying instant instead.
+ * This avoids any problems with local time-line gaps or overlaps.
+ * The result might have different values for fields such as hour, minute an even day.
+ * <p>
+ * To attempt to retain the values of the fields, use {@link #atZoneSimilarLocal(ZoneId)}.
+ * To use the offset as the zone ID, use {@link #toZonedDateTime()}.
+ *
+ * @param zone the time-zone to use, not null
+ * @return the zoned date-time formed from this date-time, not null
+ */
+ public ZonedDateTime atZoneSameInstant(ZoneId zone) {
+ return ZonedDateTime.ofInstant(dateTime, offset, zone);
+ }
+
+ /**
+ * Combines this date-time with a time-zone to create a {@code ZonedDateTime}
+ * trying to keep the same local date and time.
+ * <p>
+ * This returns a {@code ZonedDateTime} formed from this date-time and the specified time-zone.
+ * Where possible, the result will have the same local date-time as this object.
+ * <p>
+ * Time-zone rules, such as daylight savings, mean that not every time on the
+ * local time-line exists. If the local date-time is in a gap or overlap according to
+ * the rules then a resolver is used to determine the resultant local time and offset.
+ * This method uses {@link ZonedDateTime#ofLocal(LocalDateTime, ZoneId, ZoneOffset)}
+ * to retain the offset from this instance if possible.
+ * <p>
+ * Finer control over gaps and overlaps is available in two ways.
+ * If you simply want to use the later offset at overlaps then call
+ * {@link ZonedDateTime#withLaterOffsetAtOverlap()} immediately after this method.
+ * <p>
+ * To create a zoned date-time at the same instant irrespective of the local time-line,
+ * use {@link #atZoneSameInstant(ZoneId)}.
+ * To use the offset as the zone ID, use {@link #toZonedDateTime()}.
+ *
+ * @param zone the time-zone to use, not null
+ * @return the zoned date-time formed from this date and the earliest valid time for the zone, not null
+ */
+ public ZonedDateTime atZoneSimilarLocal(ZoneId zone) {
+ return ZonedDateTime.ofLocal(dateTime, zone, offset);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Converts this date-time to an {@code OffsetTime}.
+ * <p>
+ * This returns an offset time with the same local time and offset.
+ *
+ * @return an OffsetTime representing the time and offset, not null
+ */
+ public OffsetTime toOffsetTime() {
+ return OffsetTime.of(dateTime.toLocalTime(), offset);
+ }
+
+ /**
+ * Converts this date-time to a {@code ZonedDateTime} using the offset as the zone ID.
+ * <p>
+ * This creates the simplest possible {@code ZonedDateTime} using the offset
+ * as the zone ID.
+ * <p>
+ * To control the time-zone used, see {@link #atZoneSameInstant(ZoneId)} and
+ * {@link #atZoneSimilarLocal(ZoneId)}.
+ *
+ * @return a zoned date-time representing the same local date-time and offset, not null
+ */
+ public ZonedDateTime toZonedDateTime() {
+ return ZonedDateTime.of(dateTime, offset);
+ }
+
+ /**
+ * Converts this date-time to an {@code Instant}.
+ * <p>
+ * This returns an {@code Instant} representing the same point on the
+ * time-line as this date-time.
+ *
+ * @return an {@code Instant} representing the same instant, not null
+ */
+ public Instant toInstant() {
+ return dateTime.toInstant(offset);
+ }
+
+ /**
+ * Converts this date-time to the number of seconds from the epoch of 1970-01-01T00:00:00Z.
+ * <p>
+ * This allows this date-time to be converted to a value of the
+ * {@link ChronoField#INSTANT_SECONDS epoch-seconds} field. This is primarily
+ * intended for low-level conversions rather than general application usage.
+ *
+ * @return the number of seconds from the epoch of 1970-01-01T00:00:00Z
+ */
+ public long toEpochSecond() {
+ return dateTime.toEpochSecond(offset);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Compares this date-time to another date-time.
+ * <p>
+ * The comparison is based on the instant then on the local date-time.
+ * It is "consistent with equals", as defined by {@link Comparable}.
+ * <p>
+ * For example, the following is the comparator order:
+ * <ol>
+ * <li>{@code 2008-12-03T10:30+01:00}</li>
+ * <li>{@code 2008-12-03T11:00+01:00}</li>
+ * <li>{@code 2008-12-03T12:00+02:00}</li>
+ * <li>{@code 2008-12-03T11:30+01:00}</li>
+ * <li>{@code 2008-12-03T12:00+01:00}</li>
+ * <li>{@code 2008-12-03T12:30+01:00}</li>
+ * </ol>
+ * Values #2 and #3 represent the same instant on the time-line.
+ * When two values represent the same instant, the local date-time is compared
+ * to distinguish them. This step is needed to make the ordering
+ * consistent with {@code equals()}.
+ *
+ * @param other the other date-time to compare to, not null
+ * @return the comparator value, negative if less, positive if greater
+ */
+ @Override
+ public int compareTo(OffsetDateTime other) {
+ int cmp = compareInstant(this, other);
+ if (cmp == 0) {
+ cmp = toLocalDateTime().compareTo(other.toLocalDateTime());
+ }
+ return cmp;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if the instant of this date-time is after that of the specified date-time.
+ * <p>
+ * This method differs from the comparison in {@link #compareTo} and {@link #equals} in that it
+ * only compares the instant of the date-time. This is equivalent to using
+ * {@code dateTime1.toInstant().isAfter(dateTime2.toInstant());}.
+ *
+ * @param other the other date-time to compare to, not null
+ * @return true if this is after the instant of the specified date-time
+ */
+ public boolean isAfter(OffsetDateTime other) {
+ long thisEpochSec = toEpochSecond();
+ long otherEpochSec = other.toEpochSecond();
+ return thisEpochSec > otherEpochSec ||
+ (thisEpochSec == otherEpochSec && toLocalTime().getNano() > other.toLocalTime().getNano());
+ }
+
+ /**
+ * Checks if the instant of this date-time is before that of the specified date-time.
+ * <p>
+ * This method differs from the comparison in {@link #compareTo} in that it
+ * only compares the instant of the date-time. This is equivalent to using
+ * {@code dateTime1.toInstant().isBefore(dateTime2.toInstant());}.
+ *
+ * @param other the other date-time to compare to, not null
+ * @return true if this is before the instant of the specified date-time
+ */
+ public boolean isBefore(OffsetDateTime other) {
+ long thisEpochSec = toEpochSecond();
+ long otherEpochSec = other.toEpochSecond();
+ return thisEpochSec < otherEpochSec ||
+ (thisEpochSec == otherEpochSec && toLocalTime().getNano() < other.toLocalTime().getNano());
+ }
+
+ /**
+ * Checks if the instant of this date-time is equal to that of the specified date-time.
+ * <p>
+ * This method differs from the comparison in {@link #compareTo} and {@link #equals}
+ * in that it only compares the instant of the date-time. This is equivalent to using
+ * {@code dateTime1.toInstant().equals(dateTime2.toInstant());}.
+ *
+ * @param other the other date-time to compare to, not null
+ * @return true if the instant equals the instant of the specified date-time
+ */
+ public boolean isEqual(OffsetDateTime other) {
+ return toEpochSecond() == other.toEpochSecond() &&
+ toLocalTime().getNano() == other.toLocalTime().getNano();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if this date-time is equal to another date-time.
+ * <p>
+ * The comparison is based on the local date-time and the offset.
+ * To compare for the same instant on the time-line, use {@link #isEqual}.
+ * Only objects of type {@code OffsetDateTime} are compared, other types return false.
+ *
+ * @param obj the object to check, null returns false
+ * @return true if this is equal to the other date-time
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof OffsetDateTime) {
+ OffsetDateTime other = (OffsetDateTime) obj;
+ return dateTime.equals(other.dateTime) && offset.equals(other.offset);
+ }
+ return false;
+ }
+
+ /**
+ * A hash code for this date-time.
+ *
+ * @return a suitable hash code
+ */
+ @Override
+ public int hashCode() {
+ return dateTime.hashCode() ^ offset.hashCode();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Outputs this date-time as a {@code String}, such as {@code 2007-12-03T10:15:30+01:00}.
+ * <p>
+ * The output will be one of the following ISO-8601 formats:
+ * <ul>
+ * <li>{@code uuuu-MM-dd'T'HH:mmXXXXX}</li>
+ * <li>{@code uuuu-MM-dd'T'HH:mm:ssXXXXX}</li>
+ * <li>{@code uuuu-MM-dd'T'HH:mm:ss.SSSXXXXX}</li>
+ * <li>{@code uuuu-MM-dd'T'HH:mm:ss.SSSSSSXXXXX}</li>
+ * <li>{@code uuuu-MM-dd'T'HH:mm:ss.SSSSSSSSSXXXXX}</li>
+ * </ul>
+ * The format used will be the shortest that outputs the full value of
+ * the time where the omitted parts are implied to be zero.
+ *
+ * @return a string representation of this date-time, not null
+ */
+ @Override
+ public String toString() {
+ return dateTime.toString() + offset.toString();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Writes the object using a
+ * <a href="../../serialized-form.html#java.time.Ser">dedicated serialized form</a>.
+ * @serialData
+ * <pre>
+ * out.writeByte(10); // identifies an OffsetDateTime
+ * // the <a href="../../serialized-form.html#java.time.LocalDateTime">datetime</a> excluding the one byte header
+ * // the <a href="../../serialized-form.html#java.time.ZoneOffset">offset</a> excluding the one byte header
+ * </pre>
+ *
+ * @return the instance of {@code Ser}, not null
+ */
+ private Object writeReplace() {
+ return new Ser(Ser.OFFSET_DATE_TIME_TYPE, this);
+ }
+
+ /**
+ * Defend against malicious streams.
+ *
+ * @param s the stream to read
+ * @throws InvalidObjectException always
+ */
+ private void readObject(ObjectInputStream s) throws InvalidObjectException {
+ throw new InvalidObjectException("Deserialization via serialization delegate");
+ }
+
+ void writeExternal(ObjectOutput out) throws IOException {
+ dateTime.writeExternal(out);
+ offset.writeExternal(out);
+ }
+
+ static OffsetDateTime readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+ LocalDateTime dateTime = LocalDateTime.readExternal(in);
+ ZoneOffset offset = ZoneOffset.readExternal(in);
+ return OffsetDateTime.of(dateTime, offset);
+ }
+
+}
diff --git a/java/time/OffsetTime.java b/java/time/OffsetTime.java
new file mode 100644
index 0000000..c54b926
--- /dev/null
+++ b/java/time/OffsetTime.java
@@ -0,0 +1,1411 @@
+/*
+ * Copyright (c) 2012, 2015, 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) 2007-2012, 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;
+
+import static java.time.LocalTime.NANOS_PER_HOUR;
+import static java.time.LocalTime.NANOS_PER_MINUTE;
+import static java.time.LocalTime.NANOS_PER_SECOND;
+import static java.time.LocalTime.SECONDS_PER_DAY;
+import static java.time.temporal.ChronoField.NANO_OF_DAY;
+import static java.time.temporal.ChronoField.OFFSET_SECONDS;
+import static java.time.temporal.ChronoUnit.NANOS;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.time.temporal.ChronoField;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.Temporal;
+import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalAdjuster;
+import java.time.temporal.TemporalAmount;
+import java.time.temporal.TemporalField;
+import java.time.temporal.TemporalQueries;
+import java.time.temporal.TemporalQuery;
+import java.time.temporal.TemporalUnit;
+import java.time.temporal.UnsupportedTemporalTypeException;
+import java.time.temporal.ValueRange;
+import java.time.zone.ZoneRules;
+import java.util.Objects;
+
+// Android-changed: removed ValueBased paragraph.
+/**
+ * A time with an offset from UTC/Greenwich in the ISO-8601 calendar system,
+ * such as {@code 10:15:30+01:00}.
+ * <p>
+ * {@code OffsetTime} is an immutable date-time object that represents a time, often
+ * viewed as hour-minute-second-offset.
+ * This class stores all time fields, to a precision of nanoseconds,
+ * as well as a zone offset.
+ * For example, the value "13:45.30.123456789+02:00" can be stored
+ * in an {@code OffsetTime}.
+ *
+ * @implSpec
+ * This class is immutable and thread-safe.
+ *
+ * @since 1.8
+ */
+public final class OffsetTime
+ implements Temporal, TemporalAdjuster, Comparable<OffsetTime>, Serializable {
+
+ /**
+ * The minimum supported {@code OffsetTime}, '00:00:00+18:00'.
+ * This is the time of midnight at the start of the day in the maximum offset
+ * (larger offsets are earlier on the time-line).
+ * This combines {@link LocalTime#MIN} and {@link ZoneOffset#MAX}.
+ * This could be used by an application as a "far past" date.
+ */
+ public static final OffsetTime MIN = LocalTime.MIN.atOffset(ZoneOffset.MAX);
+ /**
+ * The maximum supported {@code OffsetTime}, '23:59:59.999999999-18:00'.
+ * This is the time just before midnight at the end of the day in the minimum offset
+ * (larger negative offsets are later on the time-line).
+ * This combines {@link LocalTime#MAX} and {@link ZoneOffset#MIN}.
+ * This could be used by an application as a "far future" date.
+ */
+ public static final OffsetTime MAX = LocalTime.MAX.atOffset(ZoneOffset.MIN);
+
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = 7264499704384272492L;
+
+ /**
+ * The local date-time.
+ */
+ private final LocalTime time;
+ /**
+ * The offset from UTC/Greenwich.
+ */
+ private final ZoneOffset offset;
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains the current time from the system clock in the default time-zone.
+ * <p>
+ * This will query the {@link Clock#systemDefaultZone() system clock} in the default
+ * time-zone to obtain the current time.
+ * The offset will be calculated from the time-zone in the clock.
+ * <p>
+ * Using this method will prevent the ability to use an alternate clock for testing
+ * because the clock is hard-coded.
+ *
+ * @return the current time using the system clock and default time-zone, not null
+ */
+ public static OffsetTime now() {
+ return now(Clock.systemDefaultZone());
+ }
+
+ /**
+ * Obtains the current time from the system clock in the specified time-zone.
+ * <p>
+ * This will query the {@link Clock#system(ZoneId) system clock} to obtain the current time.
+ * Specifying the time-zone avoids dependence on the default time-zone.
+ * The offset will be calculated from the specified time-zone.
+ * <p>
+ * Using this method will prevent the ability to use an alternate clock for testing
+ * because the clock is hard-coded.
+ *
+ * @param zone the zone ID to use, not null
+ * @return the current time using the system clock, not null
+ */
+ public static OffsetTime now(ZoneId zone) {
+ return now(Clock.system(zone));
+ }
+
+ /**
+ * Obtains the current time from the specified clock.
+ * <p>
+ * This will query the specified clock to obtain the current time.
+ * The offset will be calculated from the time-zone in the clock.
+ * <p>
+ * Using this method allows the use of an alternate clock for testing.
+ * The alternate clock may be introduced using {@link Clock dependency injection}.
+ *
+ * @param clock the clock to use, not null
+ * @return the current time, not null
+ */
+ public static OffsetTime now(Clock clock) {
+ Objects.requireNonNull(clock, "clock");
+ final Instant now = clock.instant(); // called once
+ return ofInstant(now, clock.getZone().getRules().getOffset(now));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code OffsetTime} from a local time and an offset.
+ *
+ * @param time the local time, not null
+ * @param offset the zone offset, not null
+ * @return the offset time, not null
+ */
+ public static OffsetTime of(LocalTime time, ZoneOffset offset) {
+ return new OffsetTime(time, offset);
+ }
+
+ /**
+ * Obtains an instance of {@code OffsetTime} from an hour, minute, second and nanosecond.
+ * <p>
+ * This creates an offset time with the four specified fields.
+ * <p>
+ * This method exists primarily for writing test cases.
+ * Non test-code will typically use other methods to create an offset time.
+ * {@code LocalTime} has two additional convenience variants of the
+ * equivalent factory method taking fewer arguments.
+ * They are not provided here to reduce the footprint of the API.
+ *
+ * @param hour the hour-of-day to represent, from 0 to 23
+ * @param minute the minute-of-hour to represent, from 0 to 59
+ * @param second the second-of-minute to represent, from 0 to 59
+ * @param nanoOfSecond the nano-of-second to represent, from 0 to 999,999,999
+ * @param offset the zone offset, not null
+ * @return the offset time, not null
+ * @throws DateTimeException if the value of any field is out of range
+ */
+ public static OffsetTime of(int hour, int minute, int second, int nanoOfSecond, ZoneOffset offset) {
+ return new OffsetTime(LocalTime.of(hour, minute, second, nanoOfSecond), offset);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code OffsetTime} from an {@code Instant} and zone ID.
+ * <p>
+ * This creates an offset time with the same instant as that specified.
+ * Finding the offset from UTC/Greenwich is simple as there is only one valid
+ * offset for each instant.
+ * <p>
+ * The date component of the instant is dropped during the conversion.
+ * This means that the conversion can never fail due to the instant being
+ * out of the valid range of dates.
+ *
+ * @param instant the instant to create the time from, not null
+ * @param zone the time-zone, which may be an offset, not null
+ * @return the offset time, not null
+ */
+ public static OffsetTime ofInstant(Instant instant, ZoneId zone) {
+ Objects.requireNonNull(instant, "instant");
+ Objects.requireNonNull(zone, "zone");
+ ZoneRules rules = zone.getRules();
+ ZoneOffset offset = rules.getOffset(instant);
+ long localSecond = instant.getEpochSecond() + offset.getTotalSeconds(); // overflow caught later
+ int secsOfDay = (int) Math.floorMod(localSecond, SECONDS_PER_DAY);
+ LocalTime time = LocalTime.ofNanoOfDay(secsOfDay * NANOS_PER_SECOND + instant.getNano());
+ return new OffsetTime(time, offset);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code OffsetTime} from a temporal object.
+ * <p>
+ * This obtains an offset time based on the specified temporal.
+ * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
+ * which this factory converts to an instance of {@code OffsetTime}.
+ * <p>
+ * The conversion extracts and combines the {@code ZoneOffset} and the
+ * {@code LocalTime} from the temporal object.
+ * Implementations are permitted to perform optimizations such as accessing
+ * those fields that are equivalent to the relevant objects.
+ * <p>
+ * This method matches the signature of the functional interface {@link TemporalQuery}
+ * allowing it to be used as a query via method reference, {@code OffsetTime::from}.
+ *
+ * @param temporal the temporal object to convert, not null
+ * @return the offset time, not null
+ * @throws DateTimeException if unable to convert to an {@code OffsetTime}
+ */
+ public static OffsetTime from(TemporalAccessor temporal) {
+ if (temporal instanceof OffsetTime) {
+ return (OffsetTime) temporal;
+ }
+ try {
+ LocalTime time = LocalTime.from(temporal);
+ ZoneOffset offset = ZoneOffset.from(temporal);
+ return new OffsetTime(time, offset);
+ } catch (DateTimeException ex) {
+ throw new DateTimeException("Unable to obtain OffsetTime from TemporalAccessor: " +
+ temporal + " of type " + temporal.getClass().getName(), ex);
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code OffsetTime} from a text string such as {@code 10:15:30+01:00}.
+ * <p>
+ * The string must represent a valid time and is parsed using
+ * {@link java.time.format.DateTimeFormatter#ISO_OFFSET_TIME}.
+ *
+ * @param text the text to parse such as "10:15:30+01:00", not null
+ * @return the parsed local time, not null
+ * @throws DateTimeParseException if the text cannot be parsed
+ */
+ public static OffsetTime parse(CharSequence text) {
+ return parse(text, DateTimeFormatter.ISO_OFFSET_TIME);
+ }
+
+ /**
+ * Obtains an instance of {@code OffsetTime} from a text string using a specific formatter.
+ * <p>
+ * The text is parsed using the formatter, returning a time.
+ *
+ * @param text the text to parse, not null
+ * @param formatter the formatter to use, not null
+ * @return the parsed offset time, not null
+ * @throws DateTimeParseException if the text cannot be parsed
+ */
+ public static OffsetTime parse(CharSequence text, DateTimeFormatter formatter) {
+ Objects.requireNonNull(formatter, "formatter");
+ return formatter.parse(text, OffsetTime::from);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Constructor.
+ *
+ * @param time the local time, not null
+ * @param offset the zone offset, not null
+ */
+ private OffsetTime(LocalTime time, ZoneOffset offset) {
+ this.time = Objects.requireNonNull(time, "time");
+ this.offset = Objects.requireNonNull(offset, "offset");
+ }
+
+ /**
+ * Returns a new time based on this one, returning {@code this} where possible.
+ *
+ * @param time the time to create with, not null
+ * @param offset the zone offset to create with, not null
+ */
+ private OffsetTime with(LocalTime time, ZoneOffset offset) {
+ if (this.time == time && this.offset.equals(offset)) {
+ return this;
+ }
+ return new OffsetTime(time, offset);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if the specified field is supported.
+ * <p>
+ * This checks if this time can be queried for the specified field.
+ * If false, then calling the {@link #range(TemporalField) range},
+ * {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}
+ * methods will throw an exception.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The supported fields are:
+ * <ul>
+ * <li>{@code NANO_OF_SECOND}
+ * <li>{@code NANO_OF_DAY}
+ * <li>{@code MICRO_OF_SECOND}
+ * <li>{@code MICRO_OF_DAY}
+ * <li>{@code MILLI_OF_SECOND}
+ * <li>{@code MILLI_OF_DAY}
+ * <li>{@code SECOND_OF_MINUTE}
+ * <li>{@code SECOND_OF_DAY}
+ * <li>{@code MINUTE_OF_HOUR}
+ * <li>{@code MINUTE_OF_DAY}
+ * <li>{@code HOUR_OF_AMPM}
+ * <li>{@code CLOCK_HOUR_OF_AMPM}
+ * <li>{@code HOUR_OF_DAY}
+ * <li>{@code CLOCK_HOUR_OF_DAY}
+ * <li>{@code AMPM_OF_DAY}
+ * <li>{@code OFFSET_SECONDS}
+ * </ul>
+ * All other {@code ChronoField} instances will return false.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}
+ * passing {@code this} as the argument.
+ * Whether the field is supported is determined by the field.
+ *
+ * @param field the field to check, null returns false
+ * @return true if the field is supported on this time, false if not
+ */
+ @Override
+ public boolean isSupported(TemporalField field) {
+ if (field instanceof ChronoField) {
+ return field.isTimeBased() || field == OFFSET_SECONDS;
+ }
+ return field != null && field.isSupportedBy(this);
+ }
+
+ /**
+ * Checks if the specified unit is supported.
+ * <p>
+ * This checks if the specified unit can be added to, or subtracted from, this offset-time.
+ * If false, then calling the {@link #plus(long, TemporalUnit)} and
+ * {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
+ * <p>
+ * If the unit is a {@link ChronoUnit} then the query is implemented here.
+ * The supported units are:
+ * <ul>
+ * <li>{@code NANOS}
+ * <li>{@code MICROS}
+ * <li>{@code MILLIS}
+ * <li>{@code SECONDS}
+ * <li>{@code MINUTES}
+ * <li>{@code HOURS}
+ * <li>{@code HALF_DAYS}
+ * </ul>
+ * All other {@code ChronoUnit} instances will return false.
+ * <p>
+ * If the unit is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
+ * passing {@code this} as the argument.
+ * Whether the unit is supported is determined by the unit.
+ *
+ * @param unit the unit to check, null returns false
+ * @return true if the unit can be added/subtracted, false if not
+ */
+ @Override // override for Javadoc
+ public boolean isSupported(TemporalUnit unit) {
+ if (unit instanceof ChronoUnit) {
+ return unit.isTimeBased();
+ }
+ return unit != null && unit.isSupportedBy(this);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the range of valid values for the specified field.
+ * <p>
+ * The range object expresses the minimum and maximum valid values for a field.
+ * This time is used to enhance the accuracy of the returned range.
+ * If it is not possible to return the range, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The {@link #isSupported(TemporalField) supported fields} will return
+ * appropriate range instances.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.rangeRefinedBy(TemporalAccessor)}
+ * passing {@code this} as the argument.
+ * Whether the range can be obtained is determined by the field.
+ *
+ * @param field the field to query the range for, not null
+ * @return the range of valid values for the field, not null
+ * @throws DateTimeException if the range for the field cannot be obtained
+ * @throws UnsupportedTemporalTypeException if the field is not supported
+ */
+ @Override
+ public ValueRange range(TemporalField field) {
+ if (field instanceof ChronoField) {
+ if (field == OFFSET_SECONDS) {
+ return field.range();
+ }
+ return time.range(field);
+ }
+ return field.rangeRefinedBy(this);
+ }
+
+ /**
+ * Gets the value of the specified field from this time as an {@code int}.
+ * <p>
+ * This queries this time for the value of the specified field.
+ * The returned value will always be within the valid range of values for the field.
+ * If it is not possible to return the value, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The {@link #isSupported(TemporalField) supported fields} will return valid
+ * values based on this time, except {@code NANO_OF_DAY} and {@code MICRO_OF_DAY}
+ * which are too large to fit in an {@code int} and throw a {@code DateTimeException}.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
+ * passing {@code this} as the argument. Whether the value can be obtained,
+ * and what the value represents, is determined by the field.
+ *
+ * @param field the field to get, not null
+ * @return the value for the field
+ * @throws DateTimeException if a value for the field cannot be obtained or
+ * the value is outside the range of valid values for the field
+ * @throws UnsupportedTemporalTypeException if the field is not supported or
+ * the range of values exceeds an {@code int}
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override // override for Javadoc
+ public int get(TemporalField field) {
+ return Temporal.super.get(field);
+ }
+
+ /**
+ * Gets the value of the specified field from this time as a {@code long}.
+ * <p>
+ * This queries this time for the value of the specified field.
+ * If it is not possible to return the value, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The {@link #isSupported(TemporalField) supported fields} will return valid
+ * values based on this time.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
+ * passing {@code this} as the argument. Whether the value can be obtained,
+ * and what the value represents, is determined by the field.
+ *
+ * @param field the field to get, not null
+ * @return the value for the field
+ * @throws DateTimeException if a value for the field cannot be obtained
+ * @throws UnsupportedTemporalTypeException if the field is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public long getLong(TemporalField field) {
+ if (field instanceof ChronoField) {
+ if (field == OFFSET_SECONDS) {
+ return offset.getTotalSeconds();
+ }
+ return time.getLong(field);
+ }
+ return field.getFrom(this);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the zone offset, such as '+01:00'.
+ * <p>
+ * This is the offset of the local time from UTC/Greenwich.
+ *
+ * @return the zone offset, not null
+ */
+ public ZoneOffset getOffset() {
+ return offset;
+ }
+
+ /**
+ * Returns a copy of this {@code OffsetTime} with the specified offset ensuring
+ * that the result has the same local time.
+ * <p>
+ * This method returns an object with the same {@code LocalTime} and the specified {@code ZoneOffset}.
+ * No calculation is needed or performed.
+ * For example, if this time represents {@code 10:30+02:00} and the offset specified is
+ * {@code +03:00}, then this method will return {@code 10:30+03:00}.
+ * <p>
+ * To take into account the difference between the offsets, and adjust the time fields,
+ * use {@link #withOffsetSameInstant}.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param offset the zone offset to change to, not null
+ * @return an {@code OffsetTime} based on this time with the requested offset, not null
+ */
+ public OffsetTime withOffsetSameLocal(ZoneOffset offset) {
+ return offset != null && offset.equals(this.offset) ? this : new OffsetTime(time, offset);
+ }
+
+ /**
+ * Returns a copy of this {@code OffsetTime} with the specified offset ensuring
+ * that the result is at the same instant on an implied day.
+ * <p>
+ * This method returns an object with the specified {@code ZoneOffset} and a {@code LocalTime}
+ * adjusted by the difference between the two offsets.
+ * This will result in the old and new objects representing the same instant on an implied day.
+ * This is useful for finding the local time in a different offset.
+ * For example, if this time represents {@code 10:30+02:00} and the offset specified is
+ * {@code +03:00}, then this method will return {@code 11:30+03:00}.
+ * <p>
+ * To change the offset without adjusting the local time use {@link #withOffsetSameLocal}.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param offset the zone offset to change to, not null
+ * @return an {@code OffsetTime} based on this time with the requested offset, not null
+ */
+ public OffsetTime withOffsetSameInstant(ZoneOffset offset) {
+ if (offset.equals(this.offset)) {
+ return this;
+ }
+ int difference = offset.getTotalSeconds() - this.offset.getTotalSeconds();
+ LocalTime adjusted = time.plusSeconds(difference);
+ return new OffsetTime(adjusted, offset);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the {@code LocalTime} part of this date-time.
+ * <p>
+ * This returns a {@code LocalTime} with the same hour, minute, second and
+ * nanosecond as this date-time.
+ *
+ * @return the time part of this date-time, not null
+ */
+ public LocalTime toLocalTime() {
+ return time;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the hour-of-day field.
+ *
+ * @return the hour-of-day, from 0 to 23
+ */
+ public int getHour() {
+ return time.getHour();
+ }
+
+ /**
+ * Gets the minute-of-hour field.
+ *
+ * @return the minute-of-hour, from 0 to 59
+ */
+ public int getMinute() {
+ return time.getMinute();
+ }
+
+ /**
+ * Gets the second-of-minute field.
+ *
+ * @return the second-of-minute, from 0 to 59
+ */
+ public int getSecond() {
+ return time.getSecond();
+ }
+
+ /**
+ * Gets the nano-of-second field.
+ *
+ * @return the nano-of-second, from 0 to 999,999,999
+ */
+ public int getNano() {
+ return time.getNano();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns an adjusted copy of this time.
+ * <p>
+ * This returns an {@code OffsetTime}, based on this one, with the time adjusted.
+ * The adjustment takes place using the specified adjuster strategy object.
+ * Read the documentation of the adjuster to understand what adjustment will be made.
+ * <p>
+ * A simple adjuster might simply set the one of the fields, such as the hour field.
+ * A more complex adjuster might set the time to the last hour of the day.
+ * <p>
+ * The classes {@link LocalTime} and {@link ZoneOffset} implement {@code TemporalAdjuster},
+ * thus this method can be used to change the time or offset:
+ * <pre>
+ * result = offsetTime.with(time);
+ * result = offsetTime.with(offset);
+ * </pre>
+ * <p>
+ * The result of this method is obtained by invoking the
+ * {@link TemporalAdjuster#adjustInto(Temporal)} method on the
+ * specified adjuster passing {@code this} as the argument.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param adjuster the adjuster to use, not null
+ * @return an {@code OffsetTime} based on {@code this} with the adjustment made, not null
+ * @throws DateTimeException if the adjustment cannot be made
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public OffsetTime with(TemporalAdjuster adjuster) {
+ // optimizations
+ if (adjuster instanceof LocalTime) {
+ return with((LocalTime) adjuster, offset);
+ } else if (adjuster instanceof ZoneOffset) {
+ return with(time, (ZoneOffset) adjuster);
+ } else if (adjuster instanceof OffsetTime) {
+ return (OffsetTime) adjuster;
+ }
+ return (OffsetTime) adjuster.adjustInto(this);
+ }
+
+ /**
+ * Returns a copy of this time with the specified field set to a new value.
+ * <p>
+ * This returns an {@code OffsetTime}, based on this one, with the value
+ * for the specified field changed.
+ * This can be used to change any supported field, such as the hour, minute or second.
+ * If it is not possible to set the value, because the field is not supported or for
+ * some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the adjustment is implemented here.
+ * <p>
+ * The {@code OFFSET_SECONDS} field will return a time with the specified offset.
+ * The local time is unaltered. If the new offset value is outside the valid range
+ * then a {@code DateTimeException} will be thrown.
+ * <p>
+ * The other {@link #isSupported(TemporalField) supported fields} will behave as per
+ * the matching method on {@link LocalTime#with(TemporalField, long)} LocalTime}.
+ * In this case, the offset is not part of the calculation and will be unchanged.
+ * <p>
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.adjustInto(Temporal, long)}
+ * passing {@code this} as the argument. In this case, the field determines
+ * whether and how to adjust the instant.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param field the field to set in the result, not null
+ * @param newValue the new value of the field in the result
+ * @return an {@code OffsetTime} based on {@code this} with the specified field set, not null
+ * @throws DateTimeException if the field cannot be set
+ * @throws UnsupportedTemporalTypeException if the field is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public OffsetTime with(TemporalField field, long newValue) {
+ if (field instanceof ChronoField) {
+ if (field == OFFSET_SECONDS) {
+ ChronoField f = (ChronoField) field;
+ return with(time, ZoneOffset.ofTotalSeconds(f.checkValidIntValue(newValue)));
+ }
+ return with(time.with(field, newValue), offset);
+ }
+ return field.adjustInto(this, newValue);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this {@code OffsetTime} with the hour-of-day altered.
+ * <p>
+ * The offset does not affect the calculation and will be the same in the result.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param hour the hour-of-day to set in the result, from 0 to 23
+ * @return an {@code OffsetTime} based on this time with the requested hour, not null
+ * @throws DateTimeException if the hour value is invalid
+ */
+ public OffsetTime withHour(int hour) {
+ return with(time.withHour(hour), offset);
+ }
+
+ /**
+ * Returns a copy of this {@code OffsetTime} with the minute-of-hour altered.
+ * <p>
+ * The offset does not affect the calculation and will be the same in the result.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param minute the minute-of-hour to set in the result, from 0 to 59
+ * @return an {@code OffsetTime} based on this time with the requested minute, not null
+ * @throws DateTimeException if the minute value is invalid
+ */
+ public OffsetTime withMinute(int minute) {
+ return with(time.withMinute(minute), offset);
+ }
+
+ /**
+ * Returns a copy of this {@code OffsetTime} with the second-of-minute altered.
+ * <p>
+ * The offset does not affect the calculation and will be the same in the result.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param second the second-of-minute to set in the result, from 0 to 59
+ * @return an {@code OffsetTime} based on this time with the requested second, not null
+ * @throws DateTimeException if the second value is invalid
+ */
+ public OffsetTime withSecond(int second) {
+ return with(time.withSecond(second), offset);
+ }
+
+ /**
+ * Returns a copy of this {@code OffsetTime} with the nano-of-second altered.
+ * <p>
+ * The offset does not affect the calculation and will be the same in the result.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param nanoOfSecond the nano-of-second to set in the result, from 0 to 999,999,999
+ * @return an {@code OffsetTime} based on this time with the requested nanosecond, not null
+ * @throws DateTimeException if the nanos value is invalid
+ */
+ public OffsetTime withNano(int nanoOfSecond) {
+ return with(time.withNano(nanoOfSecond), offset);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this {@code OffsetTime} with the time truncated.
+ * <p>
+ * Truncation returns a copy of the original time with fields
+ * smaller than the specified unit set to zero.
+ * For example, truncating with the {@link ChronoUnit#MINUTES minutes} unit
+ * will set the second-of-minute and nano-of-second field to zero.
+ * <p>
+ * The unit must have a {@linkplain TemporalUnit#getDuration() duration}
+ * that divides into the length of a standard day without remainder.
+ * This includes all supplied time units on {@link ChronoUnit} and
+ * {@link ChronoUnit#DAYS DAYS}. Other units throw an exception.
+ * <p>
+ * The offset does not affect the calculation and will be the same in the result.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param unit the unit to truncate to, not null
+ * @return an {@code OffsetTime} based on this time with the time truncated, not null
+ * @throws DateTimeException if unable to truncate
+ * @throws UnsupportedTemporalTypeException if the unit is not supported
+ */
+ public OffsetTime truncatedTo(TemporalUnit unit) {
+ return with(time.truncatedTo(unit), offset);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this time with the specified amount added.
+ * <p>
+ * This returns an {@code OffsetTime}, based on this one, with the specified amount added.
+ * The amount is typically {@link Duration} but may be any other type implementing
+ * the {@link TemporalAmount} interface.
+ * <p>
+ * The calculation is delegated to the amount object by calling
+ * {@link TemporalAmount#addTo(Temporal)}. The amount implementation is free
+ * to implement the addition in any way it wishes, however it typically
+ * calls back to {@link #plus(long, TemporalUnit)}. Consult the documentation
+ * of the amount implementation to determine if it can be successfully added.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param amountToAdd the amount to add, not null
+ * @return an {@code OffsetTime} based on this time with the addition made, not null
+ * @throws DateTimeException if the addition cannot be made
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public OffsetTime plus(TemporalAmount amountToAdd) {
+ return (OffsetTime) amountToAdd.addTo(this);
+ }
+
+ /**
+ * Returns a copy of this time with the specified amount added.
+ * <p>
+ * This returns an {@code OffsetTime}, based on this one, with the amount
+ * in terms of the unit added. If it is not possible to add the amount, because the
+ * unit is not supported or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoUnit} then the addition is implemented by
+ * {@link LocalTime#plus(long, TemporalUnit)}.
+ * The offset is not part of the calculation and will be unchanged in the result.
+ * <p>
+ * If the field is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.addTo(Temporal, long)}
+ * passing {@code this} as the argument. In this case, the unit determines
+ * whether and how to perform the addition.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param amountToAdd the amount of the unit to add to the result, may be negative
+ * @param unit the unit of the amount to add, not null
+ * @return an {@code OffsetTime} based on this time with the specified amount added, not null
+ * @throws DateTimeException if the addition cannot be made
+ * @throws UnsupportedTemporalTypeException if the unit is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public OffsetTime plus(long amountToAdd, TemporalUnit unit) {
+ if (unit instanceof ChronoUnit) {
+ return with(time.plus(amountToAdd, unit), offset);
+ }
+ return unit.addTo(this, amountToAdd);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this {@code OffsetTime} with the specified number of hours added.
+ * <p>
+ * This adds the specified number of hours to this time, returning a new time.
+ * The calculation wraps around midnight.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param hours the hours to add, may be negative
+ * @return an {@code OffsetTime} based on this time with the hours added, not null
+ */
+ public OffsetTime plusHours(long hours) {
+ return with(time.plusHours(hours), offset);
+ }
+
+ /**
+ * Returns a copy of this {@code OffsetTime} with the specified number of minutes added.
+ * <p>
+ * This adds the specified number of minutes to this time, returning a new time.
+ * The calculation wraps around midnight.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param minutes the minutes to add, may be negative
+ * @return an {@code OffsetTime} based on this time with the minutes added, not null
+ */
+ public OffsetTime plusMinutes(long minutes) {
+ return with(time.plusMinutes(minutes), offset);
+ }
+
+ /**
+ * Returns a copy of this {@code OffsetTime} with the specified number of seconds added.
+ * <p>
+ * This adds the specified number of seconds to this time, returning a new time.
+ * The calculation wraps around midnight.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param seconds the seconds to add, may be negative
+ * @return an {@code OffsetTime} based on this time with the seconds added, not null
+ */
+ public OffsetTime plusSeconds(long seconds) {
+ return with(time.plusSeconds(seconds), offset);
+ }
+
+ /**
+ * Returns a copy of this {@code OffsetTime} with the specified number of nanoseconds added.
+ * <p>
+ * This adds the specified number of nanoseconds to this time, returning a new time.
+ * The calculation wraps around midnight.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param nanos the nanos to add, may be negative
+ * @return an {@code OffsetTime} based on this time with the nanoseconds added, not null
+ */
+ public OffsetTime plusNanos(long nanos) {
+ return with(time.plusNanos(nanos), offset);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this time with the specified amount subtracted.
+ * <p>
+ * This returns an {@code OffsetTime}, based on this one, with the specified amount subtracted.
+ * The amount is typically {@link Duration} but may be any other type implementing
+ * the {@link TemporalAmount} interface.
+ * <p>
+ * The calculation is delegated to the amount object by calling
+ * {@link TemporalAmount#subtractFrom(Temporal)}. The amount implementation is free
+ * to implement the subtraction in any way it wishes, however it typically
+ * calls back to {@link #minus(long, TemporalUnit)}. Consult the documentation
+ * of the amount implementation to determine if it can be successfully subtracted.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param amountToSubtract the amount to subtract, not null
+ * @return an {@code OffsetTime} based on this time with the subtraction made, not null
+ * @throws DateTimeException if the subtraction cannot be made
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public OffsetTime minus(TemporalAmount amountToSubtract) {
+ return (OffsetTime) amountToSubtract.subtractFrom(this);
+ }
+
+ /**
+ * Returns a copy of this time with the specified amount subtracted.
+ * <p>
+ * This returns an {@code OffsetTime}, based on this one, with the amount
+ * in terms of the unit subtracted. If it is not possible to subtract the amount,
+ * because the unit is not supported or for some other reason, an exception is thrown.
+ * <p>
+ * This method is equivalent to {@link #plus(long, TemporalUnit)} with the amount negated.
+ * See that method for a full description of how addition, and thus subtraction, works.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param amountToSubtract the amount of the unit to subtract from the result, may be negative
+ * @param unit the unit of the amount to subtract, not null
+ * @return an {@code OffsetTime} based on this time with the specified amount subtracted, not null
+ * @throws DateTimeException if the subtraction cannot be made
+ * @throws UnsupportedTemporalTypeException if the unit is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public OffsetTime minus(long amountToSubtract, TemporalUnit unit) {
+ return (amountToSubtract == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this {@code OffsetTime} with the specified number of hours subtracted.
+ * <p>
+ * This subtracts the specified number of hours from this time, returning a new time.
+ * The calculation wraps around midnight.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param hours the hours to subtract, may be negative
+ * @return an {@code OffsetTime} based on this time with the hours subtracted, not null
+ */
+ public OffsetTime minusHours(long hours) {
+ return with(time.minusHours(hours), offset);
+ }
+
+ /**
+ * Returns a copy of this {@code OffsetTime} with the specified number of minutes subtracted.
+ * <p>
+ * This subtracts the specified number of minutes from this time, returning a new time.
+ * The calculation wraps around midnight.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param minutes the minutes to subtract, may be negative
+ * @return an {@code OffsetTime} based on this time with the minutes subtracted, not null
+ */
+ public OffsetTime minusMinutes(long minutes) {
+ return with(time.minusMinutes(minutes), offset);
+ }
+
+ /**
+ * Returns a copy of this {@code OffsetTime} with the specified number of seconds subtracted.
+ * <p>
+ * This subtracts the specified number of seconds from this time, returning a new time.
+ * The calculation wraps around midnight.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param seconds the seconds to subtract, may be negative
+ * @return an {@code OffsetTime} based on this time with the seconds subtracted, not null
+ */
+ public OffsetTime minusSeconds(long seconds) {
+ return with(time.minusSeconds(seconds), offset);
+ }
+
+ /**
+ * Returns a copy of this {@code OffsetTime} with the specified number of nanoseconds subtracted.
+ * <p>
+ * This subtracts the specified number of nanoseconds from this time, returning a new time.
+ * The calculation wraps around midnight.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param nanos the nanos to subtract, may be negative
+ * @return an {@code OffsetTime} based on this time with the nanoseconds subtracted, not null
+ */
+ public OffsetTime minusNanos(long nanos) {
+ return with(time.minusNanos(nanos), offset);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Queries this time using the specified query.
+ * <p>
+ * This queries this time using the specified query strategy object.
+ * The {@code TemporalQuery} object defines the logic to be used to
+ * obtain the result. Read the documentation of the query to understand
+ * what the result of this method will be.
+ * <p>
+ * The result of this method is obtained by invoking the
+ * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the
+ * specified query passing {@code this} as the argument.
+ *
+ * @param <R> the type of the result
+ * @param query the query to invoke, not null
+ * @return the query result, null may be returned (defined by the query)
+ * @throws DateTimeException if unable to query (defined by the query)
+ * @throws ArithmeticException if numeric overflow occurs (defined by the query)
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public <R> R query(TemporalQuery<R> query) {
+ if (query == TemporalQueries.offset() || query == TemporalQueries.zone()) {
+ return (R) offset;
+ } else if (query == TemporalQueries.zoneId() | query == TemporalQueries.chronology() || query == TemporalQueries.localDate()) {
+ return null;
+ } else if (query == TemporalQueries.localTime()) {
+ return (R) time;
+ } else if (query == TemporalQueries.precision()) {
+ return (R) NANOS;
+ }
+ // inline TemporalAccessor.super.query(query) as an optimization
+ // non-JDK classes are not permitted to make this optimization
+ return query.queryFrom(this);
+ }
+
+ /**
+ * Adjusts the specified temporal object to have the same offset and time
+ * as this object.
+ * <p>
+ * This returns a temporal object of the same observable type as the input
+ * with the offset and time changed to be the same as this.
+ * <p>
+ * The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)}
+ * twice, passing {@link ChronoField#NANO_OF_DAY} and
+ * {@link ChronoField#OFFSET_SECONDS} as the fields.
+ * <p>
+ * In most cases, it is clearer to reverse the calling pattern by using
+ * {@link Temporal#with(TemporalAdjuster)}:
+ * <pre>
+ * // these two lines are equivalent, but the second approach is recommended
+ * temporal = thisOffsetTime.adjustInto(temporal);
+ * temporal = temporal.with(thisOffsetTime);
+ * </pre>
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param temporal the target object to be adjusted, not null
+ * @return the adjusted object, not null
+ * @throws DateTimeException if unable to make the adjustment
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public Temporal adjustInto(Temporal temporal) {
+ return temporal
+ .with(NANO_OF_DAY, time.toNanoOfDay())
+ .with(OFFSET_SECONDS, offset.getTotalSeconds());
+ }
+
+ /**
+ * Calculates the amount of time until another time in terms of the specified unit.
+ * <p>
+ * This calculates the amount of time between two {@code OffsetTime}
+ * objects in terms of a single {@code TemporalUnit}.
+ * The start and end points are {@code this} and the specified time.
+ * The result will be negative if the end is before the start.
+ * For example, the amount in hours between two times can be calculated
+ * using {@code startTime.until(endTime, HOURS)}.
+ * <p>
+ * The {@code Temporal} passed to this method is converted to a
+ * {@code OffsetTime} using {@link #from(TemporalAccessor)}.
+ * If the offset differs between the two times, then the specified
+ * end time is normalized to have the same offset as this time.
+ * <p>
+ * The calculation returns a whole number, representing the number of
+ * complete units between the two times.
+ * For example, the amount in hours between 11:30Z and 13:29Z will only
+ * be one hour as it is one minute short of two hours.
+ * <p>
+ * There are two equivalent ways of using this method.
+ * The first is to invoke this method.
+ * The second is to use {@link TemporalUnit#between(Temporal, Temporal)}:
+ * <pre>
+ * // these two lines are equivalent
+ * amount = start.until(end, MINUTES);
+ * amount = MINUTES.between(start, end);
+ * </pre>
+ * The choice should be made based on which makes the code more readable.
+ * <p>
+ * The calculation is implemented in this method for {@link ChronoUnit}.
+ * The units {@code NANOS}, {@code MICROS}, {@code MILLIS}, {@code SECONDS},
+ * {@code MINUTES}, {@code HOURS} and {@code HALF_DAYS} are supported.
+ * Other {@code ChronoUnit} values will throw an exception.
+ * <p>
+ * If the unit is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.between(Temporal, Temporal)}
+ * passing {@code this} as the first argument and the converted input temporal
+ * as the second argument.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param endExclusive the end time, exclusive, which is converted to an {@code OffsetTime}, not null
+ * @param unit the unit to measure the amount in, not null
+ * @return the amount of time between this time and the end time
+ * @throws DateTimeException if the amount cannot be calculated, or the end
+ * temporal cannot be converted to an {@code OffsetTime}
+ * @throws UnsupportedTemporalTypeException if the unit is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public long until(Temporal endExclusive, TemporalUnit unit) {
+ OffsetTime end = OffsetTime.from(endExclusive);
+ if (unit instanceof ChronoUnit) {
+ long nanosUntil = end.toEpochNano() - toEpochNano(); // no overflow
+ switch ((ChronoUnit) unit) {
+ case NANOS: return nanosUntil;
+ case MICROS: return nanosUntil / 1000;
+ case MILLIS: return nanosUntil / 1000_000;
+ case SECONDS: return nanosUntil / NANOS_PER_SECOND;
+ case MINUTES: return nanosUntil / NANOS_PER_MINUTE;
+ case HOURS: return nanosUntil / NANOS_PER_HOUR;
+ case HALF_DAYS: return nanosUntil / (12 * NANOS_PER_HOUR);
+ }
+ throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
+ }
+ return unit.between(this, end);
+ }
+
+ /**
+ * Formats this time using the specified formatter.
+ * <p>
+ * This time will be passed to the formatter to produce a string.
+ *
+ * @param formatter the formatter to use, not null
+ * @return the formatted time string, not null
+ * @throws DateTimeException if an error occurs during printing
+ */
+ public String format(DateTimeFormatter formatter) {
+ Objects.requireNonNull(formatter, "formatter");
+ return formatter.format(this);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Combines this time with a date to create an {@code OffsetDateTime}.
+ * <p>
+ * This returns an {@code OffsetDateTime} formed from this time and the specified date.
+ * All possible combinations of date and time are valid.
+ *
+ * @param date the date to combine with, not null
+ * @return the offset date-time formed from this time and the specified date, not null
+ */
+ public OffsetDateTime atDate(LocalDate date) {
+ return OffsetDateTime.of(date, time, offset);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Converts this time to epoch nanos based on 1970-01-01Z.
+ *
+ * @return the epoch nanos value
+ */
+ private long toEpochNano() {
+ long nod = time.toNanoOfDay();
+ long offsetNanos = offset.getTotalSeconds() * NANOS_PER_SECOND;
+ return nod - offsetNanos;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Compares this {@code OffsetTime} to another time.
+ * <p>
+ * The comparison is based first on the UTC equivalent instant, then on the local time.
+ * It is "consistent with equals", as defined by {@link Comparable}.
+ * <p>
+ * For example, the following is the comparator order:
+ * <ol>
+ * <li>{@code 10:30+01:00}</li>
+ * <li>{@code 11:00+01:00}</li>
+ * <li>{@code 12:00+02:00}</li>
+ * <li>{@code 11:30+01:00}</li>
+ * <li>{@code 12:00+01:00}</li>
+ * <li>{@code 12:30+01:00}</li>
+ * </ol>
+ * Values #2 and #3 represent the same instant on the time-line.
+ * When two values represent the same instant, the local time is compared
+ * to distinguish them. This step is needed to make the ordering
+ * consistent with {@code equals()}.
+ * <p>
+ * To compare the underlying local time of two {@code TemporalAccessor} instances,
+ * use {@link ChronoField#NANO_OF_DAY} as a comparator.
+ *
+ * @param other the other time to compare to, not null
+ * @return the comparator value, negative if less, positive if greater
+ * @throws NullPointerException if {@code other} is null
+ */
+ @Override
+ public int compareTo(OffsetTime other) {
+ if (offset.equals(other.offset)) {
+ return time.compareTo(other.time);
+ }
+ int compare = Long.compare(toEpochNano(), other.toEpochNano());
+ if (compare == 0) {
+ compare = time.compareTo(other.time);
+ }
+ return compare;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if the instant of this {@code OffsetTime} is after that of the
+ * specified time applying both times to a common date.
+ * <p>
+ * This method differs from the comparison in {@link #compareTo} in that it
+ * only compares the instant of the time. This is equivalent to converting both
+ * times to an instant using the same date and comparing the instants.
+ *
+ * @param other the other time to compare to, not null
+ * @return true if this is after the instant of the specified time
+ */
+ public boolean isAfter(OffsetTime other) {
+ return toEpochNano() > other.toEpochNano();
+ }
+
+ /**
+ * Checks if the instant of this {@code OffsetTime} is before that of the
+ * specified time applying both times to a common date.
+ * <p>
+ * This method differs from the comparison in {@link #compareTo} in that it
+ * only compares the instant of the time. This is equivalent to converting both
+ * times to an instant using the same date and comparing the instants.
+ *
+ * @param other the other time to compare to, not null
+ * @return true if this is before the instant of the specified time
+ */
+ public boolean isBefore(OffsetTime other) {
+ return toEpochNano() < other.toEpochNano();
+ }
+
+ /**
+ * Checks if the instant of this {@code OffsetTime} is equal to that of the
+ * specified time applying both times to a common date.
+ * <p>
+ * This method differs from the comparison in {@link #compareTo} and {@link #equals}
+ * in that it only compares the instant of the time. This is equivalent to converting both
+ * times to an instant using the same date and comparing the instants.
+ *
+ * @param other the other time to compare to, not null
+ * @return true if this is equal to the instant of the specified time
+ */
+ public boolean isEqual(OffsetTime other) {
+ return toEpochNano() == other.toEpochNano();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if this time is equal to another time.
+ * <p>
+ * The comparison is based on the local-time and the offset.
+ * To compare for the same instant on the time-line, use {@link #isEqual(OffsetTime)}.
+ * <p>
+ * Only objects of type {@code OffsetTime} are compared, other types return false.
+ * To compare the underlying local time of two {@code TemporalAccessor} instances,
+ * use {@link ChronoField#NANO_OF_DAY} as a comparator.
+ *
+ * @param obj the object to check, null returns false
+ * @return true if this is equal to the other time
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof OffsetTime) {
+ OffsetTime other = (OffsetTime) obj;
+ return time.equals(other.time) && offset.equals(other.offset);
+ }
+ return false;
+ }
+
+ /**
+ * A hash code for this time.
+ *
+ * @return a suitable hash code
+ */
+ @Override
+ public int hashCode() {
+ return time.hashCode() ^ offset.hashCode();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Outputs this time as a {@code String}, such as {@code 10:15:30+01:00}.
+ * <p>
+ * The output will be one of the following ISO-8601 formats:
+ * <ul>
+ * <li>{@code HH:mmXXXXX}</li>
+ * <li>{@code HH:mm:ssXXXXX}</li>
+ * <li>{@code HH:mm:ss.SSSXXXXX}</li>
+ * <li>{@code HH:mm:ss.SSSSSSXXXXX}</li>
+ * <li>{@code HH:mm:ss.SSSSSSSSSXXXXX}</li>
+ * </ul>
+ * The format used will be the shortest that outputs the full value of
+ * the time where the omitted parts are implied to be zero.
+ *
+ * @return a string representation of this time, not null
+ */
+ @Override
+ public String toString() {
+ return time.toString() + offset.toString();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Writes the object using a
+ * <a href="../../serialized-form.html#java.time.Ser">dedicated serialized form</a>.
+ * @serialData
+ * <pre>
+ * out.writeByte(9); // identifies an OffsetTime
+ * // the <a href="../../serialized-form.html#java.time.LocalTime">time</a> excluding the one byte header
+ * // the <a href="../../serialized-form.html#java.time.ZoneOffset">offset</a> excluding the one byte header
+ * </pre>
+ *
+ * @return the instance of {@code Ser}, not null
+ */
+ private Object writeReplace() {
+ return new Ser(Ser.OFFSET_TIME_TYPE, this);
+ }
+
+ /**
+ * Defend against malicious streams.
+ *
+ * @param s the stream to read
+ * @throws InvalidObjectException always
+ */
+ private void readObject(ObjectInputStream s) throws InvalidObjectException {
+ throw new InvalidObjectException("Deserialization via serialization delegate");
+ }
+
+ void writeExternal(ObjectOutput out) throws IOException {
+ time.writeExternal(out);
+ offset.writeExternal(out);
+ }
+
+ static OffsetTime readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+ LocalTime time = LocalTime.readExternal(in);
+ ZoneOffset offset = ZoneOffset.readExternal(in);
+ return OffsetTime.of(time, offset);
+ }
+
+}
diff --git a/java/time/Period.java b/java/time/Period.java
new file mode 100644
index 0000000..f264994
--- /dev/null
+++ b/java/time/Period.java
@@ -0,0 +1,1075 @@
+/*
+ * Copyright (c) 2012, 2015, 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) 2008-2012, 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;
+
+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.ObjectInputStream;
+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.TemporalQueries;
+import java.time.temporal.TemporalUnit;
+import java.time.temporal.UnsupportedTemporalTypeException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+// Android-changed: removed ValueBased paragraph.
+/**
+ * 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 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
+ * the local time.
+ * <p>
+ * For example, consider adding a period of one day and a duration of one day to
+ * 18:00 on the evening before a daylight savings gap. The {@code Period} will add
+ * the conceptual day and result in a {@code ZonedDateTime} at 18:00 the following day.
+ * By contrast, the {@code Duration} will add exactly 24 hours, resulting in a
+ * {@code ZonedDateTime} at 19:00 the following day (assuming a one hour DST gap).
+ * <p>
+ * The supported units of a period are {@link ChronoUnit#YEARS YEARS},
+ * {@link ChronoUnit#MONTHS MONTHS} and {@link ChronoUnit#DAYS DAYS}.
+ * All three fields are always present, but may be set to zero.
+ * <p>
+ * 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.
+ *
+ * @implSpec
+ * This class is immutable and thread-safe.
+ *
+ * @since 1.8
+ */
+public final class Period
+ implements ChronoPeriod, Serializable {
+
+ /**
+ * A constant for a period of zero.
+ */
+ public static final Period ZERO = new Period(0, 0, 0);
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = -3587258372562876L;
+ /**
+ * The pattern for parsing.
+ */
+ private static final 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.
+ */
+ private static final List<TemporalUnit> SUPPORTED_UNITS =
+ Collections.unmodifiableList(Arrays.<TemporalUnit>asList(YEARS, MONTHS, DAYS));
+
+ /**
+ * The number of years.
+ */
+ private final int years;
+ /**
+ * The number of months.
+ */
+ private final int months;
+ /**
+ * The number of days.
+ */
+ private final int days;
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains a {@code Period} representing a number of years.
+ * <p>
+ * The resulting period will have the specified years.
+ * The months and days units will be zero.
+ *
+ * @param years the number of years, positive or negative
+ * @return the period of years, not null
+ */
+ public static Period ofYears(int years) {
+ return create(years, 0, 0);
+ }
+
+ /**
+ * Obtains a {@code Period} representing a number of months.
+ * <p>
+ * The resulting period will have the specified months.
+ * The years and days units will be zero.
+ *
+ * @param months the number of months, positive or negative
+ * @return the period of months, not null
+ */
+ public static Period ofMonths(int months) {
+ return create(0, months, 0);
+ }
+
+ /**
+ * Obtains a {@code Period} representing a number of weeks.
+ * <p>
+ * The resulting period will be day-based, with the amount of days
+ * equal to the number of weeks multiplied by 7.
+ * The years and months units will be zero.
+ *
+ * @param weeks the number of weeks, positive or negative
+ * @return the period, with the input weeks converted to days, not null
+ */
+ public static Period ofWeeks(int weeks) {
+ return create(0, 0, Math.multiplyExact(weeks, 7));
+ }
+
+ /**
+ * Obtains a {@code Period} representing a number of days.
+ * <p>
+ * The resulting period will have the specified days.
+ * The years and months units will be zero.
+ *
+ * @param days the number of days, positive or negative
+ * @return the period of days, not null
+ */
+ public static Period ofDays(int days) {
+ return create(0, 0, days);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains a {@code Period} representing a number of years, months and days.
+ * <p>
+ * This creates an instance based on years, months and days.
+ *
+ * @param years the amount of years, may be negative
+ * @param months the amount of months, may be negative
+ * @param days the amount of days, may be negative
+ * @return the period of years, months and days, not null
+ */
+ public static Period of(int years, int months, int days) {
+ return create(years, months, days);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code Period} from a temporal amount.
+ * <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 {@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
+ * @throws DateTimeException if unable to convert to a {@code Period}
+ * @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;
+ int days = 0;
+ for (TemporalUnit unit : amount.getUnits()) {
+ long unitAmount = amount.get(unit);
+ if (unit == ChronoUnit.YEARS) {
+ years = Math.toIntExact(unitAmount);
+ } else if (unit == ChronoUnit.MONTHS) {
+ months = Math.toIntExact(unitAmount);
+ } else if (unit == ChronoUnit.DAYS) {
+ days = Math.toIntExact(unitAmount);
+ } else {
+ throw new DateTimeException("Unit must be Years, Months or Days, but was " + unit);
+ }
+ }
+ return create(years, months, days);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains a {@code Period} from a text string such as {@code PnYnMnD}.
+ * <p>
+ * This will parse the string produced by {@code toString()} which is
+ * based on the ISO-8601 period formats {@code PnYnMnD} and {@code PnW}.
+ * <p>
+ * The string starts with an optional sign, denoted by the ASCII negative
+ * or positive symbol. If negative, the whole period is negated.
+ * The ASCII letter "P" is next in upper or lower case.
+ * There are then four sections, each consisting of a number and a suffix.
+ * At least one of the four sections must be present.
+ * The sections have suffixes in ASCII of "Y", "M", "W" and "D" for
+ * years, months, weeks and days, accepted in upper or lower case.
+ * The suffixes must occur in order.
+ * The number part of each section must consist of ASCII digits.
+ * The number may be prefixed by the ASCII negative or positive symbol.
+ * The number must parse to an {@code int}.
+ * <p>
+ * The leading plus/minus sign, and negative values for other units are
+ * not part of the ISO-8601 standard. In addition, ISO-8601 does not
+ * permit mixing between the {@code PnYnMnD} and {@code PnW} formats.
+ * Any week-based input is multiplied by 7 and treated as a number of days.
+ * <p>
+ * For example, the following are valid inputs:
+ * <pre>
+ * "P2Y" -- Period.ofYears(2)
+ * "P3M" -- Period.ofMonths(3)
+ * "P4W" -- Period.ofWeeks(4)
+ * "P5D" -- Period.ofDays(5)
+ * "P1Y2M3D" -- Period.of(1, 2, 3)
+ * "P1Y2M3W4D" -- Period.of(1, 2, 25)
+ * "P-1Y2M" -- Period.of(-1, 2, 0)
+ * "-P1Y2M" -- Period.of(-1, -2, 0)
+ * </pre>
+ *
+ * @param text the text to parse, not null
+ * @return the parsed period, not null
+ * @throws DateTimeParseException if the text cannot be parsed to a period
+ */
+ public static Period parse(CharSequence text) {
+ Objects.requireNonNull(text, "text");
+ Matcher matcher = PATTERN.matcher(text);
+ if (matcher.matches()) {
+ int negate = ("-".equals(matcher.group(1)) ? -1 : 1);
+ String yearMatch = matcher.group(2);
+ String monthMatch = matcher.group(3);
+ String weekMatch = matcher.group(4);
+ String dayMatch = matcher.group(5);
+ if (yearMatch != null || monthMatch != null || dayMatch != null || weekMatch != null) {
+ try {
+ int years = parseNumber(text, yearMatch, negate);
+ int months = parseNumber(text, monthMatch, negate);
+ int weeks = parseNumber(text, weekMatch, negate);
+ int days = parseNumber(text, dayMatch, negate);
+ days = Math.addExact(days, Math.multiplyExact(weeks, 7));
+ return create(years, months, days);
+ } catch (NumberFormatException ex) {
+ throw new DateTimeParseException("Text cannot be parsed to a Period", text, 0, ex);
+ }
+ }
+ }
+ throw new DateTimeParseException("Text cannot be parsed to a Period", text, 0);
+ }
+
+ private static int parseNumber(CharSequence text, String str, int negate) {
+ if (str == null) {
+ return 0;
+ }
+ int val = Integer.parseInt(str);
+ try {
+ return Math.multiplyExact(val, negate);
+ } catch (ArithmeticException ex) {
+ throw new DateTimeParseException("Text cannot be parsed to a Period", text, 0, ex);
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains a {@code Period} consisting of the number of years, months,
+ * and days between two dates.
+ * <p>
+ * The start date is included, but the end date is not.
+ * The period is calculated by removing complete months, then calculating
+ * the remaining number of days, adjusting to ensure that both have the same sign.
+ * The number of months is then split into years and months based on a 12 month year.
+ * A month is considered if the end day-of-month is greater than or equal to the start day-of-month.
+ * For example, from {@code 2010-01-15} to {@code 2011-03-18} is one year, two months and three days.
+ * <p>
+ * 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 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 startDateInclusive, LocalDate endDateExclusive) {
+ return startDateInclusive.until(endDateExclusive);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Creates an instance.
+ *
+ * @param years the amount
+ * @param months the amount
+ * @param days the amount
+ */
+ private static Period create(int years, int months, int days) {
+ if ((years | months | days) == 0) {
+ return ZERO;
+ }
+ return new Period(years, months, days);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param years the amount
+ * @param months the amount
+ * @param days the amount
+ */
+ private Period(int years, int months, int days) {
+ this.years = years;
+ this.months = months;
+ this.days = days;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the value of the requested unit.
+ * <p>
+ * This returns a value for each of the three supported units,
+ * {@link ChronoUnit#YEARS YEARS}, {@link ChronoUnit#MONTHS MONTHS} and
+ * {@link ChronoUnit#DAYS DAYS}.
+ * All other units 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
+ public long get(TemporalUnit unit) {
+ if (unit == ChronoUnit.YEARS) {
+ return getYears();
+ } else if (unit == ChronoUnit.MONTHS) {
+ return getMonths();
+ } else if (unit == ChronoUnit.DAYS) {
+ return getDays();
+ } else {
+ throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
+ }
+ }
+
+ /**
+ * Gets the set of units supported by this period.
+ * <p>
+ * The supported units are {@link ChronoUnit#YEARS YEARS},
+ * {@link ChronoUnit#MONTHS MONTHS} and {@link ChronoUnit#DAYS DAYS}.
+ * They are returned in the order years, months, days.
+ * <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 years, months and days units, not null
+ */
+ @Override
+ public List<TemporalUnit> getUnits() {
+ 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.
+ * <p>
+ * A zero period has the value zero for the years, months and days units.
+ *
+ * @return true if this period is zero-length
+ */
+ public boolean isZero() {
+ return (this == ZERO);
+ }
+
+ /**
+ * Checks if any of the three units of this period are negative.
+ * <p>
+ * This checks whether the years, months or days units are less than zero.
+ *
+ * @return true if any unit of this period is negative
+ */
+ public boolean isNegative() {
+ return years < 0 || months < 0 || days < 0;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the amount of years of this period.
+ * <p>
+ * This returns the years unit.
+ * <p>
+ * 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".
+ *
+ * @return the amount of years of this period, may be negative
+ */
+ public int getYears() {
+ return years;
+ }
+
+ /**
+ * Gets the amount of months of this period.
+ * <p>
+ * This returns the months unit.
+ * <p>
+ * 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".
+ *
+ * @return the amount of months of this period, may be negative
+ */
+ public int getMonths() {
+ return months;
+ }
+
+ /**
+ * Gets the amount of days of this period.
+ * <p>
+ * This returns the days unit.
+ *
+ * @return the amount of days of this period, may be negative
+ */
+ public int getDays() {
+ return days;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this period with the specified amount of years.
+ * <p>
+ * 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 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>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param years the years to represent, may be negative
+ * @return a {@code Period} based on this period with the requested years, not null
+ */
+ public Period withYears(int years) {
+ if (years == this.years) {
+ return this;
+ }
+ return create(years, months, days);
+ }
+
+ /**
+ * Returns a copy of this period with the specified amount of months.
+ * <p>
+ * 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 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>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param months the months to represent, may be negative
+ * @return a {@code Period} based on this period with the requested months, not null
+ */
+ public Period withMonths(int months) {
+ if (months == this.months) {
+ return this;
+ }
+ return create(years, months, days);
+ }
+
+ /**
+ * Returns a copy of this period with the specified amount of days.
+ * <p>
+ * This sets the amount of the days unit in a copy of this period.
+ * The years and months units are unaffected.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param days the days to represent, may be negative
+ * @return a {@code Period} based on this period with the requested days, not null
+ */
+ public Period withDays(int days) {
+ if (days == this.days) {
+ return this;
+ }
+ return create(years, months, days);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * 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 {@code Period}.
+ * Other types are interpreted using {@link Period#from(TemporalAmount)}.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param amountToAdd the amount 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(TemporalAmount amountToAdd) {
+ Period isoAmount = Period.from(amountToAdd);
+ return create(
+ Math.addExact(years, isoAmount.years),
+ Math.addExact(months, isoAmount.months),
+ Math.addExact(days, isoAmount.days));
+ }
+
+ /**
+ * Returns a copy of this period with the specified years added.
+ * <p>
+ * This adds the amount to the years unit in a copy of this period.
+ * The months and days units are unaffected.
+ * For example, "1 year, 6 months and 3 days" plus 2 years returns "3 years, 6 months and 3 days".
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param yearsToAdd the years to add, positive or negative
+ * @return a {@code Period} based on this period with the specified years added, not null
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public Period plusYears(long yearsToAdd) {
+ if (yearsToAdd == 0) {
+ return this;
+ }
+ return create(Math.toIntExact(Math.addExact(years, yearsToAdd)), months, days);
+ }
+
+ /**
+ * Returns a copy of this period with the specified months added.
+ * <p>
+ * This adds the amount to the months unit in a copy of this period.
+ * The years and days units are unaffected.
+ * For example, "1 year, 6 months and 3 days" plus 2 months returns "1 year, 8 months and 3 days".
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param monthsToAdd the months to add, positive or negative
+ * @return a {@code Period} based on this period with the specified months added, not null
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public Period plusMonths(long monthsToAdd) {
+ if (monthsToAdd == 0) {
+ return this;
+ }
+ return create(years, Math.toIntExact(Math.addExact(months, monthsToAdd)), days);
+ }
+
+ /**
+ * Returns a copy of this period with the specified days added.
+ * <p>
+ * This adds the amount to the days unit in a copy of this period.
+ * The years and months units are unaffected.
+ * For example, "1 year, 6 months and 3 days" plus 2 days returns "1 year, 6 months and 5 days".
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param daysToAdd the days to add, positive or negative
+ * @return a {@code Period} based on this period with the specified days added, not null
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public Period plusDays(long daysToAdd) {
+ if (daysToAdd == 0) {
+ return this;
+ }
+ return create(years, months, Math.toIntExact(Math.addExact(days, daysToAdd)));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * 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 {@code Period}.
+ * Other types are interpreted using {@link Period#from(TemporalAmount)}.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param amountToSubtract the amount 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(TemporalAmount amountToSubtract) {
+ Period isoAmount = Period.from(amountToSubtract);
+ return create(
+ Math.subtractExact(years, isoAmount.years),
+ Math.subtractExact(months, isoAmount.months),
+ Math.subtractExact(days, isoAmount.days));
+ }
+
+ /**
+ * Returns a copy of this period with the specified years subtracted.
+ * <p>
+ * This subtracts the amount from the years unit in a copy of this period.
+ * The months and days units are unaffected.
+ * For example, "1 year, 6 months and 3 days" minus 2 years returns "-1 years, 6 months and 3 days".
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param yearsToSubtract the years to subtract, positive or negative
+ * @return a {@code Period} based on this period with the specified years subtracted, not null
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public Period minusYears(long yearsToSubtract) {
+ return (yearsToSubtract == Long.MIN_VALUE ? plusYears(Long.MAX_VALUE).plusYears(1) : plusYears(-yearsToSubtract));
+ }
+
+ /**
+ * Returns a copy of this period with the specified months subtracted.
+ * <p>
+ * This subtracts the amount from the months unit in a copy of this period.
+ * The years and days units are unaffected.
+ * For example, "1 year, 6 months and 3 days" minus 2 months returns "1 year, 4 months and 3 days".
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param monthsToSubtract the years to subtract, positive or negative
+ * @return a {@code Period} based on this period with the specified months subtracted, not null
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public Period minusMonths(long monthsToSubtract) {
+ return (monthsToSubtract == Long.MIN_VALUE ? plusMonths(Long.MAX_VALUE).plusMonths(1) : plusMonths(-monthsToSubtract));
+ }
+
+ /**
+ * Returns a copy of this period with the specified days subtracted.
+ * <p>
+ * This subtracts the amount from the days unit in a copy of this period.
+ * The years and months units are unaffected.
+ * For example, "1 year, 6 months and 3 days" minus 2 days returns "1 year, 6 months and 1 day".
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param daysToSubtract the months to subtract, positive or negative
+ * @return a {@code Period} based on this period with the specified days subtracted, not null
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public Period minusDays(long daysToSubtract) {
+ return (daysToSubtract == Long.MIN_VALUE ? plusDays(Long.MAX_VALUE).plusDays(1) : plusDays(-daysToSubtract));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a new instance with each element in this period multiplied
+ * by the specified scalar.
+ * <p>
+ * This returns a period with each of the years, months and days units
+ * 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
+ */
+ public Period multipliedBy(int scalar) {
+ if (this == ZERO || scalar == 1) {
+ return this;
+ }
+ return create(
+ Math.multiplyExact(years, scalar),
+ Math.multiplyExact(months, scalar),
+ Math.multiplyExact(days, scalar));
+ }
+
+ /**
+ * Returns a new instance with each amount in this period negated.
+ * <p>
+ * This returns a period with each of the years, months and days units
+ * 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}
+ */
+ public Period negated() {
+ return multipliedBy(-1);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * 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,
+ * with the years unit being adjusted to compensate. For example, a period of
+ * "1 Year and 15 months" will be normalized to "2 years and 3 months".
+ * <p>
+ * The sign of the years and months units will be the same after normalization.
+ * For example, a period of "1 year and -25 months" will be normalized to
+ * "-1 year and -1 month".
+ * <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
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public Period normalized() {
+ long totalMonths = toTotalMonths();
+ long splitYears = totalMonths / 12;
+ int splitMonths = (int) (totalMonths % 12); // no overflow
+ if (splitYears == years && splitMonths == months) {
+ return this;
+ }
+ return create(Math.toIntExact(splitYears), splitMonths, days);
+ }
+
+ /**
+ * 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 instance is immutable and unaffected by this method call.
+ *
+ * @return the total number of months in the period, may be negative
+ */
+ public long toTotalMonths() {
+ return years * 12L + months; // no overflow
+ }
+
+ //-------------------------------------------------------------------------
+ /**
+ * 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.
+ * 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)}.
+ * <pre>
+ * // these two lines are equivalent, but the second approach is recommended
+ * dateTime = thisPeriod.addTo(dateTime);
+ * dateTime = dateTime.plus(thisPeriod);
+ * </pre>
+ * <p>
+ * 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.
+ *
+ * @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
+ public Temporal addTo(Temporal temporal) {
+ 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) {
+ temporal = temporal.plus(days, DAYS);
+ }
+ return 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.
+ * 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)}.
+ * <pre>
+ * // these two lines are equivalent, but the second approach is recommended
+ * dateTime = thisPeriod.subtractFrom(dateTime);
+ * dateTime = dateTime.minus(thisPeriod);
+ * </pre>
+ * <p>
+ * 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.
+ *
+ * @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
+ public Temporal subtractFrom(Temporal temporal) {
+ 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) {
+ 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(TemporalQueries.chronology());
+ if (temporalChrono != null && IsoChronology.INSTANCE.equals(temporalChrono) == false) {
+ throw new DateTimeException("Chronology mismatch, expected: ISO, actual: " + temporalChrono.getId());
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if this period is equal to another period.
+ * <p>
+ * The comparison is based on the type {@code 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".
+ *
+ * @param obj the object to check, null returns false
+ * @return true if this is equal to the other period
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof Period) {
+ Period other = (Period) obj;
+ return years == other.years &&
+ months == other.months &&
+ days == other.days;
+ }
+ return false;
+ }
+
+ /**
+ * A hash code for this period.
+ *
+ * @return a suitable hash code
+ */
+ @Override
+ public int hashCode() {
+ return years + Integer.rotateLeft(months, 8) + Integer.rotateLeft(days, 16);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Outputs this period as a {@code String}, such as {@code P6Y3M1D}.
+ * <p>
+ * The output will be in the ISO-8601 period format.
+ * A zero period will be represented as zero days, 'P0D'.
+ *
+ * @return a string representation of this period, not null
+ */
+ @Override
+ public String toString() {
+ if (this == ZERO) {
+ return "P0D";
+ } else {
+ StringBuilder buf = new StringBuilder();
+ buf.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 object using a
+ * <a href="../../serialized-form.html#java.time.Ser">dedicated serialized form</a>.
+ * @serialData
+ * <pre>
+ * out.writeByte(14); // identifies a Period
+ * out.writeInt(years);
+ * out.writeInt(months);
+ * out.writeInt(days);
+ * </pre>
+ *
+ * @return the instance of {@code Ser}, not null
+ */
+ private Object writeReplace() {
+ return new Ser(Ser.PERIOD_TYPE, this);
+ }
+
+ /**
+ * Defend against malicious streams.
+ *
+ * @param s the stream to read
+ * @throws java.io.InvalidObjectException always
+ */
+ private void readObject(ObjectInputStream s) throws InvalidObjectException {
+ throw new InvalidObjectException("Deserialization via serialization delegate");
+ }
+
+ void writeExternal(DataOutput out) throws IOException {
+ out.writeInt(years);
+ out.writeInt(months);
+ out.writeInt(days);
+ }
+
+ static Period readExternal(DataInput in) throws IOException {
+ int years = in.readInt();
+ int months = in.readInt();
+ int days = in.readInt();
+ return Period.of(years, months, days);
+ }
+
+}
diff --git a/java/time/Ser.java b/java/time/Ser.java
new file mode 100644
index 0000000..885b43a
--- /dev/null
+++ b/java/time/Ser.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 2012, 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) 2011-2012, 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;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.InvalidClassException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.io.StreamCorruptedException;
+
+/**
+ * The shared serialization delegate for this package.
+ *
+ * @implNote
+ * This class wraps the object being serialized, and takes a byte representing the type of the class to
+ * be serialized. This byte can also be used for versioning the serialization format. In this case another
+ * byte flag would be used in order to specify an alternative version of the type format.
+ * For example {@code LOCAL_DATE_TYPE_VERSION_2 = 21}.
+ * <p>
+ * In order to serialize the object it writes its byte and then calls back to the appropriate class where
+ * the serialization is performed. In order to deserialize the object it read in the type byte, switching
+ * in order to select which class to call back into.
+ * <p>
+ * The serialization format is determined on a per class basis. In the case of field based classes each
+ * of the fields is written out with an appropriate size format in descending order of the field's size. For
+ * example in the case of {@link LocalDate} year is written before month. Composite classes, such as
+ * {@link LocalDateTime} are serialized as one object.
+ * <p>
+ * This class is mutable and should be created once per serialization.
+ *
+ * @serial include
+ * @since 1.8
+ */
+final class Ser implements Externalizable {
+
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = -7683839454370182990L;
+
+ static final byte DURATION_TYPE = 1;
+ static final byte INSTANT_TYPE = 2;
+ static final byte LOCAL_DATE_TYPE = 3;
+ static final byte LOCAL_TIME_TYPE = 4;
+ static final byte LOCAL_DATE_TIME_TYPE = 5;
+ static final byte ZONE_DATE_TIME_TYPE = 6;
+ static final byte ZONE_REGION_TYPE = 7;
+ static final byte ZONE_OFFSET_TYPE = 8;
+ static final byte OFFSET_TIME_TYPE = 9;
+ static final byte OFFSET_DATE_TIME_TYPE = 10;
+ static final byte YEAR_TYPE = 11;
+ static final byte YEAR_MONTH_TYPE = 12;
+ static final byte MONTH_DAY_TYPE = 13;
+ static final byte PERIOD_TYPE = 14;
+
+ /** The type being serialized. */
+ private byte type;
+ /** The object being serialized. */
+ private Object object;
+
+ /**
+ * Constructor for deserialization.
+ */
+ public Ser() {
+ }
+
+ /**
+ * Creates an instance for serialization.
+ *
+ * @param type the type
+ * @param object the object
+ */
+ Ser(byte type, Object object) {
+ this.type = type;
+ this.object = object;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Implements the {@code Externalizable} interface to write the object.
+ * @serialData
+ *
+ * Each serializable class is mapped to a type that is the first byte
+ * in the stream. Refer to each class {@code writeReplace}
+ * serialized form for the value of the type and sequence of values for the type.
+ * <ul>
+ * <li><a href="../../serialized-form.html#java.time.Duration">Duration.writeReplace</a>
+ * <li><a href="../../serialized-form.html#java.time.Instant">Instant.writeReplace</a>
+ * <li><a href="../../serialized-form.html#java.time.LocalDate">LocalDate.writeReplace</a>
+ * <li><a href="../../serialized-form.html#java.time.LocalDateTime">LocalDateTime.writeReplace</a>
+ * <li><a href="../../serialized-form.html#java.time.LocalTime">LocalTime.writeReplace</a>
+ * <li><a href="../../serialized-form.html#java.time.MonthDay">MonthDay.writeReplace</a>
+ * <li><a href="../../serialized-form.html#java.time.OffsetTime">OffsetTime.writeReplace</a>
+ * <li><a href="../../serialized-form.html#java.time.OffsetDateTime">OffsetDateTime.writeReplace</a>
+ * <li><a href="../../serialized-form.html#java.time.Period">Period.writeReplace</a>
+ * <li><a href="../../serialized-form.html#java.time.Year">Year.writeReplace</a>
+ * <li><a href="../../serialized-form.html#java.time.YearMonth">YearMonth.writeReplace</a>
+ * <li><a href="../../serialized-form.html#java.time.ZoneId">ZoneId.writeReplace</a>
+ * <li><a href="../../serialized-form.html#java.time.ZoneOffset">ZoneOffset.writeReplace</a>
+ * <li><a href="../../serialized-form.html#java.time.ZonedDateTime">ZonedDateTime.writeReplace</a>
+ * </ul>
+ *
+ * @param out the data stream to write to, not null
+ */
+ @Override
+ public void writeExternal(ObjectOutput out) throws IOException {
+ writeInternal(type, object, out);
+ }
+
+ static void writeInternal(byte type, Object object, ObjectOutput out) throws IOException {
+ out.writeByte(type);
+ switch (type) {
+ case DURATION_TYPE:
+ ((Duration) object).writeExternal(out);
+ break;
+ case INSTANT_TYPE:
+ ((Instant) object).writeExternal(out);
+ break;
+ case LOCAL_DATE_TYPE:
+ ((LocalDate) object).writeExternal(out);
+ break;
+ case LOCAL_DATE_TIME_TYPE:
+ ((LocalDateTime) object).writeExternal(out);
+ break;
+ case LOCAL_TIME_TYPE:
+ ((LocalTime) object).writeExternal(out);
+ break;
+ case ZONE_REGION_TYPE:
+ ((ZoneRegion) object).writeExternal(out);
+ break;
+ case ZONE_OFFSET_TYPE:
+ ((ZoneOffset) object).writeExternal(out);
+ break;
+ case ZONE_DATE_TIME_TYPE:
+ ((ZonedDateTime) object).writeExternal(out);
+ break;
+ case OFFSET_TIME_TYPE:
+ ((OffsetTime) object).writeExternal(out);
+ break;
+ case OFFSET_DATE_TIME_TYPE:
+ ((OffsetDateTime) object).writeExternal(out);
+ break;
+ case YEAR_TYPE:
+ ((Year) object).writeExternal(out);
+ break;
+ case YEAR_MONTH_TYPE:
+ ((YearMonth) object).writeExternal(out);
+ break;
+ case MONTH_DAY_TYPE:
+ ((MonthDay) object).writeExternal(out);
+ break;
+ case PERIOD_TYPE:
+ ((Period) object).writeExternal(out);
+ break;
+ default:
+ throw new InvalidClassException("Unknown serialized type");
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Implements the {@code Externalizable} interface to read the object.
+ * @serialData
+ *
+ * The streamed type and parameters defined by the type's {@code writeReplace}
+ * method are read and passed to the corresponding static factory for the type
+ * to create a new instance. That instance is returned as the de-serialized
+ * {@code Ser} object.
+ *
+ * <ul>
+ * <li><a href="../../serialized-form.html#java.time.Duration">Duration</a> - {@code Duration.ofSeconds(seconds, nanos);}
+ * <li><a href="../../serialized-form.html#java.time.Instant">Instant</a> - {@code Instant.ofEpochSecond(seconds, nanos);}
+ * <li><a href="../../serialized-form.html#java.time.LocalDate">LocalDate</a> - {@code LocalDate.of(year, month, day);}
+ * <li><a href="../../serialized-form.html#java.time.LocalDateTime">LocalDateTime</a> - {@code LocalDateTime.of(date, time);}
+ * <li><a href="../../serialized-form.html#java.time.LocalTime">LocalTime</a> - {@code LocalTime.of(hour, minute, second, nano);}
+ * <li><a href="../../serialized-form.html#java.time.MonthDay">MonthDay</a> - {@code MonthDay.of(month, day);}
+ * <li><a href="../../serialized-form.html#java.time.OffsetTime">OffsetTime</a> - {@code OffsetTime.of(time, offset);}
+ * <li><a href="../../serialized-form.html#java.time.OffsetDateTime">OffsetDateTime</a> - {@code OffsetDateTime.of(dateTime, offset);}
+ * <li><a href="../../serialized-form.html#java.time.Period">Period</a> - {@code Period.of(years, months, days);}
+ * <li><a href="../../serialized-form.html#java.time.Year">Year</a> - {@code Year.of(year);}
+ * <li><a href="../../serialized-form.html#java.time.YearMonth">YearMonth</a> - {@code YearMonth.of(year, month);}
+ * <li><a href="../../serialized-form.html#java.time.ZonedDateTime">ZonedDateTime</a> - {@code ZonedDateTime.ofLenient(dateTime, offset, zone);}
+ * <li><a href="../../serialized-form.html#java.time.ZoneId">ZoneId</a> - {@code ZoneId.of(id);}
+ * <li><a href="../../serialized-form.html#java.time.ZoneOffset">ZoneOffset</a> - {@code (offsetByte == 127 ? ZoneOffset.ofTotalSeconds(in.readInt()) : ZoneOffset.ofTotalSeconds(offsetByte * 900));}
+ * </ul>
+ *
+ * @param in the data to read, not null
+ */
+ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+ type = in.readByte();
+ object = readInternal(type, in);
+ }
+
+ static Object read(ObjectInput in) throws IOException, ClassNotFoundException {
+ byte type = in.readByte();
+ return readInternal(type, in);
+ }
+
+ private static Object readInternal(byte type, ObjectInput in) throws IOException, ClassNotFoundException {
+ switch (type) {
+ case DURATION_TYPE: return Duration.readExternal(in);
+ case INSTANT_TYPE: return Instant.readExternal(in);
+ case LOCAL_DATE_TYPE: return LocalDate.readExternal(in);
+ case LOCAL_DATE_TIME_TYPE: return LocalDateTime.readExternal(in);
+ case LOCAL_TIME_TYPE: return LocalTime.readExternal(in);
+ case ZONE_DATE_TIME_TYPE: return ZonedDateTime.readExternal(in);
+ case ZONE_OFFSET_TYPE: return ZoneOffset.readExternal(in);
+ case ZONE_REGION_TYPE: return ZoneRegion.readExternal(in);
+ case OFFSET_TIME_TYPE: return OffsetTime.readExternal(in);
+ case OFFSET_DATE_TIME_TYPE: return OffsetDateTime.readExternal(in);
+ case YEAR_TYPE: return Year.readExternal(in);
+ case YEAR_MONTH_TYPE: return YearMonth.readExternal(in);
+ case MONTH_DAY_TYPE: return MonthDay.readExternal(in);
+ case PERIOD_TYPE: return Period.readExternal(in);
+ default:
+ throw new StreamCorruptedException("Unknown serialized type");
+ }
+ }
+
+ /**
+ * Returns the object that will replace this one.
+ *
+ * @return the read object, should never be null
+ */
+ private Object readResolve() {
+ return object;
+ }
+
+}
diff --git a/java/time/Year.java b/java/time/Year.java
new file mode 100644
index 0000000..a1d5679
--- /dev/null
+++ b/java/time/Year.java
@@ -0,0 +1,1116 @@
+/*
+ * Copyright (c) 2012, 2015, 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) 2007-2012, 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;
+
+import static java.time.temporal.ChronoField.ERA;
+import static java.time.temporal.ChronoField.YEAR;
+import static java.time.temporal.ChronoField.YEAR_OF_ERA;
+import static java.time.temporal.ChronoUnit.CENTURIES;
+import static java.time.temporal.ChronoUnit.DECADES;
+import static java.time.temporal.ChronoUnit.ERAS;
+import static java.time.temporal.ChronoUnit.MILLENNIA;
+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.ObjectInputStream;
+import java.io.Serializable;
+import java.time.chrono.Chronology;
+import java.time.chrono.IsoChronology;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
+import java.time.format.DateTimeParseException;
+import java.time.format.SignStyle;
+import java.time.temporal.ChronoField;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.Temporal;
+import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalAdjuster;
+import java.time.temporal.TemporalAmount;
+import java.time.temporal.TemporalField;
+import java.time.temporal.TemporalQueries;
+import java.time.temporal.TemporalQuery;
+import java.time.temporal.TemporalUnit;
+import java.time.temporal.UnsupportedTemporalTypeException;
+import java.time.temporal.ValueRange;
+import java.util.Objects;
+
+// Android-changed: removed ValueBased paragraph.
+/**
+ * A year in the ISO-8601 calendar system, such as {@code 2007}.
+ * <p>
+ * {@code Year} is an immutable date-time object that represents a year.
+ * Any field that can be derived from a year can be obtained.
+ * <p>
+ * <b>Note that years in the ISO chronology only align with years in the
+ * Gregorian-Julian system for modern years. Parts of Russia did not switch to the
+ * modern Gregorian/ISO rules until 1920.
+ * As such, historical years must be treated with caution.</b>
+ * <p>
+ * This class does not store or represent a month, day, time or time-zone.
+ * For example, the value "2007" can be stored in a {@code Year}.
+ * <p>
+ * Years represented by this class follow the ISO-8601 standard and use
+ * the proleptic numbering system. Year 1 is preceded by year 0, then by year -1.
+ * <p>
+ * 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.
+ * For most applications written today, the ISO-8601 rules are entirely suitable.
+ * However, any application that makes use of historical dates, and requires them
+ * to be accurate will find the ISO-8601 approach unsuitable.
+ *
+ * @implSpec
+ * This class is immutable and thread-safe.
+ *
+ * @since 1.8
+ */
+public final class Year
+ implements Temporal, TemporalAdjuster, Comparable<Year>, Serializable {
+
+ /**
+ * The minimum supported year, '-999,999,999'.
+ */
+ public static final int MIN_VALUE = -999_999_999;
+ /**
+ * The maximum supported year, '+999,999,999'.
+ */
+ public static final int MAX_VALUE = 999_999_999;
+
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = -23038383694477807L;
+ /**
+ * Parser.
+ */
+ private static final DateTimeFormatter PARSER = new DateTimeFormatterBuilder()
+ .appendValue(YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
+ .toFormatter();
+
+ /**
+ * The year being represented.
+ */
+ private final int year;
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains the current year from the system clock in the default time-zone.
+ * <p>
+ * This will query the {@link Clock#systemDefaultZone() system clock} in the default
+ * time-zone to obtain the current year.
+ * <p>
+ * Using this method will prevent the ability to use an alternate clock for testing
+ * because the clock is hard-coded.
+ *
+ * @return the current year using the system clock and default time-zone, not null
+ */
+ public static Year now() {
+ return now(Clock.systemDefaultZone());
+ }
+
+ /**
+ * Obtains the current year from the system clock in the specified time-zone.
+ * <p>
+ * This will query the {@link Clock#system(ZoneId) system clock} to obtain the current year.
+ * Specifying the time-zone avoids dependence on the default time-zone.
+ * <p>
+ * Using this method will prevent the ability to use an alternate clock for testing
+ * because the clock is hard-coded.
+ *
+ * @param zone the zone ID to use, not null
+ * @return the current year using the system clock, not null
+ */
+ public static Year now(ZoneId zone) {
+ return now(Clock.system(zone));
+ }
+
+ /**
+ * Obtains the current year from the specified clock.
+ * <p>
+ * This will query the specified clock to obtain the current year.
+ * Using this method allows the use of an alternate clock for testing.
+ * The alternate clock may be introduced using {@link Clock dependency injection}.
+ *
+ * @param clock the clock to use, not null
+ * @return the current year, not null
+ */
+ public static Year now(Clock clock) {
+ final LocalDate now = LocalDate.now(clock); // called once
+ return Year.of(now.getYear());
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code Year}.
+ * <p>
+ * This method accepts a year value from the proleptic ISO calendar system.
+ * <p>
+ * The year 2AD/CE is represented by 2.<br>
+ * The year 1AD/CE is represented by 1.<br>
+ * The year 1BC/BCE is represented by 0.<br>
+ * The year 2BC/BCE is represented by -1.<br>
+ *
+ * @param isoYear the ISO proleptic year to represent, from {@code MIN_VALUE} to {@code MAX_VALUE}
+ * @return the year, not null
+ * @throws DateTimeException if the field is invalid
+ */
+ public static Year of(int isoYear) {
+ YEAR.checkValidValue(isoYear);
+ return new Year(isoYear);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code Year} from a temporal object.
+ * <p>
+ * This obtains a year based on the specified temporal.
+ * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
+ * which this factory converts to an instance of {@code Year}.
+ * <p>
+ * The conversion extracts the {@link ChronoField#YEAR year} field.
+ * The extraction is only permitted if the temporal object has an ISO
+ * chronology, or can be converted to a {@code LocalDate}.
+ * <p>
+ * This method matches the signature of the functional interface {@link TemporalQuery}
+ * allowing it to be used as a query via method reference, {@code Year::from}.
+ *
+ * @param temporal the temporal object to convert, not null
+ * @return the year, not null
+ * @throws DateTimeException if unable to convert to a {@code Year}
+ */
+ public static Year from(TemporalAccessor temporal) {
+ if (temporal instanceof Year) {
+ return (Year) temporal;
+ }
+ Objects.requireNonNull(temporal, "temporal");
+ try {
+ if (IsoChronology.INSTANCE.equals(Chronology.from(temporal)) == false) {
+ temporal = LocalDate.from(temporal);
+ }
+ return of(temporal.get(YEAR));
+ } catch (DateTimeException ex) {
+ throw new DateTimeException("Unable to obtain Year from TemporalAccessor: " +
+ temporal + " of type " + temporal.getClass().getName(), ex);
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code Year} from a text string such as {@code 2007}.
+ * <p>
+ * The string must represent a valid year.
+ * Years outside the range 0000 to 9999 must be prefixed by the plus or minus symbol.
+ *
+ * @param text the text to parse such as "2007", not null
+ * @return the parsed year, not null
+ * @throws DateTimeParseException if the text cannot be parsed
+ */
+ public static Year parse(CharSequence text) {
+ return parse(text, PARSER);
+ }
+
+ /**
+ * Obtains an instance of {@code Year} from a text string using a specific formatter.
+ * <p>
+ * The text is parsed using the formatter, returning a year.
+ *
+ * @param text the text to parse, not null
+ * @param formatter the formatter to use, not null
+ * @return the parsed year, not null
+ * @throws DateTimeParseException if the text cannot be parsed
+ */
+ public static Year parse(CharSequence text, DateTimeFormatter formatter) {
+ Objects.requireNonNull(formatter, "formatter");
+ return formatter.parse(text, Year::from);
+ }
+
+ //-------------------------------------------------------------------------
+ /**
+ * Checks if the year is a leap year, according to the ISO proleptic
+ * calendar system rules.
+ * <p>
+ * This method applies the current rules for leap years across the whole time-line.
+ * In general, a year is a leap year if it is divisible by four without
+ * remainder. However, years divisible by 100, are not leap years, with
+ * the exception of years divisible by 400 which are.
+ * <p>
+ * For example, 1904 is a leap year it is divisible by 4.
+ * 1900 was not a leap year as it is divisible by 100, however 2000 was a
+ * leap year as it is divisible by 400.
+ * <p>
+ * The calculation is proleptic - applying the same rules into the far future and far past.
+ * This is historically inaccurate, but is correct for the ISO-8601 standard.
+ *
+ * @param year the year to check
+ * @return true if the year is leap, false otherwise
+ */
+ public static boolean isLeap(long year) {
+ return ((year & 3) == 0) && ((year % 100) != 0 || (year % 400) == 0);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Constructor.
+ *
+ * @param year the year to represent
+ */
+ private Year(int year) {
+ this.year = year;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the year value.
+ * <p>
+ * The year returned by this method is proleptic as per {@code get(YEAR)}.
+ *
+ * @return the year, {@code MIN_VALUE} to {@code MAX_VALUE}
+ */
+ public int getValue() {
+ return year;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if the specified field is supported.
+ * <p>
+ * This checks if this year can be queried for the specified field.
+ * If false, then calling the {@link #range(TemporalField) range},
+ * {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}
+ * methods will throw an exception.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The supported fields are:
+ * <ul>
+ * <li>{@code YEAR_OF_ERA}
+ * <li>{@code YEAR}
+ * <li>{@code ERA}
+ * </ul>
+ * All other {@code ChronoField} instances will return false.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}
+ * passing {@code this} as the argument.
+ * Whether the field is supported is determined by the field.
+ *
+ * @param field the field to check, null returns false
+ * @return true if the field is supported on this year, false if not
+ */
+ @Override
+ public boolean isSupported(TemporalField field) {
+ if (field instanceof ChronoField) {
+ return field == YEAR || field == YEAR_OF_ERA || field == ERA;
+ }
+ return field != null && field.isSupportedBy(this);
+ }
+
+ /**
+ * Checks if the specified unit is supported.
+ * <p>
+ * This checks if the specified unit can be added to, or subtracted from, this year.
+ * If false, then calling the {@link #plus(long, TemporalUnit)} and
+ * {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
+ * <p>
+ * If the unit is a {@link ChronoUnit} then the query is implemented here.
+ * The supported units are:
+ * <ul>
+ * <li>{@code YEARS}
+ * <li>{@code DECADES}
+ * <li>{@code CENTURIES}
+ * <li>{@code MILLENNIA}
+ * <li>{@code ERAS}
+ * </ul>
+ * All other {@code ChronoUnit} instances will return false.
+ * <p>
+ * If the unit is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
+ * passing {@code this} as the argument.
+ * Whether the unit is supported is determined by the unit.
+ *
+ * @param unit the unit to check, null returns false
+ * @return true if the unit can be added/subtracted, false if not
+ */
+ @Override
+ public boolean isSupported(TemporalUnit unit) {
+ if (unit instanceof ChronoUnit) {
+ return unit == YEARS || unit == DECADES || unit == CENTURIES || unit == MILLENNIA || unit == ERAS;
+ }
+ return unit != null && unit.isSupportedBy(this);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the range of valid values for the specified field.
+ * <p>
+ * The range object expresses the minimum and maximum valid values for a field.
+ * This year is used to enhance the accuracy of the returned range.
+ * If it is not possible to return the range, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The {@link #isSupported(TemporalField) supported fields} will return
+ * appropriate range instances.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.rangeRefinedBy(TemporalAccessor)}
+ * passing {@code this} as the argument.
+ * Whether the range can be obtained is determined by the field.
+ *
+ * @param field the field to query the range for, not null
+ * @return the range of valid values for the field, not null
+ * @throws DateTimeException if the range for the field cannot be obtained
+ * @throws UnsupportedTemporalTypeException if the field is not supported
+ */
+ @Override
+ public ValueRange range(TemporalField field) {
+ if (field == YEAR_OF_ERA) {
+ return (year <= 0 ? ValueRange.of(1, MAX_VALUE + 1) : ValueRange.of(1, MAX_VALUE));
+ }
+ return Temporal.super.range(field);
+ }
+
+ /**
+ * Gets the value of the specified field from this year as an {@code int}.
+ * <p>
+ * This queries this year for the value of the specified field.
+ * The returned value will always be within the valid range of values for the field.
+ * If it is not possible to return the value, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The {@link #isSupported(TemporalField) supported fields} will return valid
+ * values based on this year.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
+ * passing {@code this} as the argument. Whether the value can be obtained,
+ * and what the value represents, is determined by the field.
+ *
+ * @param field the field to get, not null
+ * @return the value for the field
+ * @throws DateTimeException if a value for the field cannot be obtained or
+ * the value is outside the range of valid values for the field
+ * @throws UnsupportedTemporalTypeException if the field is not supported or
+ * the range of values exceeds an {@code int}
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override // override for Javadoc
+ public int get(TemporalField field) {
+ return range(field).checkValidIntValue(getLong(field), field);
+ }
+
+ /**
+ * Gets the value of the specified field from this year as a {@code long}.
+ * <p>
+ * This queries this year for the value of the specified field.
+ * If it is not possible to return the value, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The {@link #isSupported(TemporalField) supported fields} will return valid
+ * values based on this year.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
+ * passing {@code this} as the argument. Whether the value can be obtained,
+ * and what the value represents, is determined by the field.
+ *
+ * @param field the field to get, not null
+ * @return the value for the field
+ * @throws DateTimeException if a value for the field cannot be obtained
+ * @throws UnsupportedTemporalTypeException if the field is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public long getLong(TemporalField field) {
+ if (field instanceof ChronoField) {
+ switch ((ChronoField) field) {
+ case YEAR_OF_ERA: return (year < 1 ? 1 - year : year);
+ case YEAR: return year;
+ case ERA: return (year < 1 ? 0 : 1);
+ }
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
+ }
+ return field.getFrom(this);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if the year is a leap year, according to the ISO proleptic
+ * calendar system rules.
+ * <p>
+ * This method applies the current rules for leap years across the whole time-line.
+ * In general, a year is a leap year if it is divisible by four without
+ * remainder. However, years divisible by 100, are not leap years, with
+ * the exception of years divisible by 400 which are.
+ * <p>
+ * For example, 1904 is a leap year it is divisible by 4.
+ * 1900 was not a leap year as it is divisible by 100, however 2000 was a
+ * leap year as it is divisible by 400.
+ * <p>
+ * The calculation is proleptic - applying the same rules into the far future and far past.
+ * This is historically inaccurate, but is correct for the ISO-8601 standard.
+ *
+ * @return true if the year is leap, false otherwise
+ */
+ public boolean isLeap() {
+ return Year.isLeap(year);
+ }
+
+ /**
+ * Checks if the month-day is valid for this year.
+ * <p>
+ * This method checks whether this year and the input month and day form
+ * a valid date.
+ *
+ * @param monthDay the month-day to validate, null returns false
+ * @return true if the month and day are valid for this year
+ */
+ public boolean isValidMonthDay(MonthDay monthDay) {
+ return monthDay != null && monthDay.isValidYear(year);
+ }
+
+ /**
+ * Gets the length of this year in days.
+ *
+ * @return the length of this year in days, 365 or 366
+ */
+ public int length() {
+ return isLeap() ? 366 : 365;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns an adjusted copy of this year.
+ * <p>
+ * This returns a {@code Year}, based on this one, with the year adjusted.
+ * The adjustment takes place using the specified adjuster strategy object.
+ * Read the documentation of the adjuster to understand what adjustment will be made.
+ * <p>
+ * The result of this method is obtained by invoking the
+ * {@link TemporalAdjuster#adjustInto(Temporal)} method on the
+ * specified adjuster passing {@code this} as the argument.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param adjuster the adjuster to use, not null
+ * @return a {@code Year} based on {@code this} with the adjustment made, not null
+ * @throws DateTimeException if the adjustment cannot be made
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public Year with(TemporalAdjuster adjuster) {
+ return (Year) adjuster.adjustInto(this);
+ }
+
+ /**
+ * Returns a copy of this year with the specified field set to a new value.
+ * <p>
+ * This returns a {@code Year}, based on this one, with the value
+ * for the specified field changed.
+ * If it is not possible to set the value, because the field is not supported or for
+ * some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the adjustment is implemented here.
+ * The supported fields behave as follows:
+ * <ul>
+ * <li>{@code YEAR_OF_ERA} -
+ * Returns a {@code Year} with the specified year-of-era
+ * The era will be unchanged.
+ * <li>{@code YEAR} -
+ * Returns a {@code Year} with the specified year.
+ * This completely replaces the date and is equivalent to {@link #of(int)}.
+ * <li>{@code ERA} -
+ * Returns a {@code Year} with the specified era.
+ * The year-of-era will be unchanged.
+ * </ul>
+ * <p>
+ * In all cases, if the new value is outside the valid range of values for the field
+ * then a {@code DateTimeException} will be thrown.
+ * <p>
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.adjustInto(Temporal, long)}
+ * passing {@code this} as the argument. In this case, the field determines
+ * whether and how to adjust the instant.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param field the field to set in the result, not null
+ * @param newValue the new value of the field in the result
+ * @return a {@code Year} based on {@code this} with the specified field set, not null
+ * @throws DateTimeException if the field cannot be set
+ * @throws UnsupportedTemporalTypeException if the field is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public Year with(TemporalField field, long newValue) {
+ if (field instanceof ChronoField) {
+ ChronoField f = (ChronoField) field;
+ f.checkValidValue(newValue);
+ switch (f) {
+ case YEAR_OF_ERA: return Year.of((int) (year < 1 ? 1 - newValue : newValue));
+ case YEAR: return Year.of((int) newValue);
+ case ERA: return (getLong(ERA) == newValue ? this : Year.of(1 - year));
+ }
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
+ }
+ return field.adjustInto(this, newValue);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this year with the specified amount added.
+ * <p>
+ * This returns a {@code Year}, based on this one, with the specified amount added.
+ * The amount is typically {@link Period} but may be any other type implementing
+ * the {@link TemporalAmount} interface.
+ * <p>
+ * The calculation is delegated to the amount object by calling
+ * {@link TemporalAmount#addTo(Temporal)}. The amount implementation is free
+ * to implement the addition in any way it wishes, however it typically
+ * calls back to {@link #plus(long, TemporalUnit)}. Consult the documentation
+ * of the amount implementation to determine if it can be successfully added.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param amountToAdd the amount to add, not null
+ * @return a {@code Year} based on this year with the addition made, not null
+ * @throws DateTimeException if the addition cannot be made
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public Year plus(TemporalAmount amountToAdd) {
+ return (Year) amountToAdd.addTo(this);
+ }
+
+ /**
+ * Returns a copy of this year with the specified amount added.
+ * <p>
+ * This returns a {@code Year}, based on this one, with the amount
+ * in terms of the unit added. If it is not possible to add the amount, because the
+ * unit is not supported or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoUnit} then the addition is implemented here.
+ * The supported fields behave as follows:
+ * <ul>
+ * <li>{@code YEARS} -
+ * Returns a {@code Year} with the specified number of years added.
+ * This is equivalent to {@link #plusYears(long)}.
+ * <li>{@code DECADES} -
+ * Returns a {@code Year} with the specified number of decades added.
+ * This is equivalent to calling {@link #plusYears(long)} with the amount
+ * multiplied by 10.
+ * <li>{@code CENTURIES} -
+ * Returns a {@code Year} with the specified number of centuries added.
+ * This is equivalent to calling {@link #plusYears(long)} with the amount
+ * multiplied by 100.
+ * <li>{@code MILLENNIA} -
+ * Returns a {@code Year} with the specified number of millennia added.
+ * This is equivalent to calling {@link #plusYears(long)} with the amount
+ * multiplied by 1,000.
+ * <li>{@code ERAS} -
+ * Returns a {@code Year} with the specified number of eras added.
+ * Only two eras are supported so the amount must be one, zero or minus one.
+ * If the amount is non-zero then the year is changed such that the year-of-era
+ * is unchanged.
+ * </ul>
+ * <p>
+ * All other {@code ChronoUnit} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.addTo(Temporal, long)}
+ * passing {@code this} as the argument. In this case, the unit determines
+ * whether and how to perform the addition.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param amountToAdd the amount of the unit to add to the result, may be negative
+ * @param unit the unit of the amount to add, not null
+ * @return a {@code Year} based on this year with the specified amount added, not null
+ * @throws DateTimeException if the addition cannot be made
+ * @throws UnsupportedTemporalTypeException if the unit is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public Year plus(long amountToAdd, TemporalUnit unit) {
+ if (unit instanceof ChronoUnit) {
+ switch ((ChronoUnit) unit) {
+ case YEARS: return plusYears(amountToAdd);
+ case DECADES: return plusYears(Math.multiplyExact(amountToAdd, 10));
+ case CENTURIES: return plusYears(Math.multiplyExact(amountToAdd, 100));
+ case MILLENNIA: return plusYears(Math.multiplyExact(amountToAdd, 1000));
+ case ERAS: return with(ERA, Math.addExact(getLong(ERA), amountToAdd));
+ }
+ throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
+ }
+ return unit.addTo(this, amountToAdd);
+ }
+
+ /**
+ * Returns a copy of this {@code Year} with the specified number of years added.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param yearsToAdd the years to add, may be negative
+ * @return a {@code Year} based on this year with the years added, not null
+ * @throws DateTimeException if the result exceeds the supported range
+ */
+ public Year plusYears(long yearsToAdd) {
+ if (yearsToAdd == 0) {
+ return this;
+ }
+ return of(YEAR.checkValidIntValue(year + yearsToAdd)); // overflow safe
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this year with the specified amount subtracted.
+ * <p>
+ * This returns a {@code Year}, based on this one, with the specified amount subtracted.
+ * The amount is typically {@link Period} but may be any other type implementing
+ * the {@link TemporalAmount} interface.
+ * <p>
+ * The calculation is delegated to the amount object by calling
+ * {@link TemporalAmount#subtractFrom(Temporal)}. The amount implementation is free
+ * to implement the subtraction in any way it wishes, however it typically
+ * calls back to {@link #minus(long, TemporalUnit)}. Consult the documentation
+ * of the amount implementation to determine if it can be successfully subtracted.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param amountToSubtract the amount to subtract, not null
+ * @return a {@code Year} based on this year with the subtraction made, not null
+ * @throws DateTimeException if the subtraction cannot be made
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public Year minus(TemporalAmount amountToSubtract) {
+ return (Year) amountToSubtract.subtractFrom(this);
+ }
+
+ /**
+ * Returns a copy of this year with the specified amount subtracted.
+ * <p>
+ * This returns a {@code Year}, based on this one, with the amount
+ * in terms of the unit subtracted. If it is not possible to subtract the amount,
+ * because the unit is not supported or for some other reason, an exception is thrown.
+ * <p>
+ * This method is equivalent to {@link #plus(long, TemporalUnit)} with the amount negated.
+ * See that method for a full description of how addition, and thus subtraction, works.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param amountToSubtract the amount of the unit to subtract from the result, may be negative
+ * @param unit the unit of the amount to subtract, not null
+ * @return a {@code Year} based on this year with the specified amount subtracted, not null
+ * @throws DateTimeException if the subtraction cannot be made
+ * @throws UnsupportedTemporalTypeException if the unit is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public Year minus(long amountToSubtract, TemporalUnit unit) {
+ return (amountToSubtract == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit));
+ }
+
+ /**
+ * Returns a copy of this {@code Year} with the specified number of years subtracted.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param yearsToSubtract the years to subtract, may be negative
+ * @return a {@code Year} based on this year with the year subtracted, not null
+ * @throws DateTimeException if the result exceeds the supported range
+ */
+ public Year minusYears(long yearsToSubtract) {
+ return (yearsToSubtract == Long.MIN_VALUE ? plusYears(Long.MAX_VALUE).plusYears(1) : plusYears(-yearsToSubtract));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Queries this year using the specified query.
+ * <p>
+ * This queries this year using the specified query strategy object.
+ * The {@code TemporalQuery} object defines the logic to be used to
+ * obtain the result. Read the documentation of the query to understand
+ * what the result of this method will be.
+ * <p>
+ * The result of this method is obtained by invoking the
+ * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the
+ * specified query passing {@code this} as the argument.
+ *
+ * @param <R> the type of the result
+ * @param query the query to invoke, not null
+ * @return the query result, null may be returned (defined by the query)
+ * @throws DateTimeException if unable to query (defined by the query)
+ * @throws ArithmeticException if numeric overflow occurs (defined by the query)
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public <R> R query(TemporalQuery<R> query) {
+ if (query == TemporalQueries.chronology()) {
+ return (R) IsoChronology.INSTANCE;
+ } else if (query == TemporalQueries.precision()) {
+ return (R) YEARS;
+ }
+ return Temporal.super.query(query);
+ }
+
+ /**
+ * Adjusts the specified temporal object to have this year.
+ * <p>
+ * This returns a temporal object of the same observable type as the input
+ * with the year changed to be the same as this.
+ * <p>
+ * The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)}
+ * passing {@link ChronoField#YEAR} as the field.
+ * If the specified temporal object does not use the ISO calendar system then
+ * a {@code DateTimeException} is thrown.
+ * <p>
+ * In most cases, it is clearer to reverse the calling pattern by using
+ * {@link Temporal#with(TemporalAdjuster)}:
+ * <pre>
+ * // these two lines are equivalent, but the second approach is recommended
+ * temporal = thisYear.adjustInto(temporal);
+ * temporal = temporal.with(thisYear);
+ * </pre>
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param temporal the target object to be adjusted, not null
+ * @return the adjusted object, not null
+ * @throws DateTimeException if unable to make the adjustment
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public Temporal adjustInto(Temporal temporal) {
+ if (Chronology.from(temporal).equals(IsoChronology.INSTANCE) == false) {
+ throw new DateTimeException("Adjustment only supported on ISO date-time");
+ }
+ return temporal.with(YEAR, year);
+ }
+
+ /**
+ * Calculates the amount of time until another year in terms of the specified unit.
+ * <p>
+ * This calculates the amount of time between two {@code Year}
+ * objects in terms of a single {@code TemporalUnit}.
+ * The start and end points are {@code this} and the specified year.
+ * The result will be negative if the end is before the start.
+ * The {@code Temporal} passed to this method is converted to a
+ * {@code Year} using {@link #from(TemporalAccessor)}.
+ * For example, the amount in decades between two year can be calculated
+ * using {@code startYear.until(endYear, DECADES)}.
+ * <p>
+ * The calculation returns a whole number, representing the number of
+ * complete units between the two years.
+ * For example, the amount in decades between 2012 and 2031
+ * will only be one decade as it is one year short of two decades.
+ * <p>
+ * There are two equivalent ways of using this method.
+ * The first is to invoke this method.
+ * The second is to use {@link TemporalUnit#between(Temporal, Temporal)}:
+ * <pre>
+ * // these two lines are equivalent
+ * amount = start.until(end, YEARS);
+ * amount = YEARS.between(start, end);
+ * </pre>
+ * The choice should be made based on which makes the code more readable.
+ * <p>
+ * The calculation is implemented in this method for {@link ChronoUnit}.
+ * The units {@code YEARS}, {@code DECADES}, {@code CENTURIES},
+ * {@code MILLENNIA} and {@code ERAS} are supported.
+ * Other {@code ChronoUnit} values will throw an exception.
+ * <p>
+ * If the unit is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.between(Temporal, Temporal)}
+ * passing {@code this} as the first argument and the converted input temporal
+ * as the second argument.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param endExclusive the end date, exclusive, which is converted to a {@code Year}, not null
+ * @param unit the unit to measure the amount in, not null
+ * @return the amount of time between this year and the end year
+ * @throws DateTimeException if the amount cannot be calculated, or the end
+ * temporal cannot be converted to a {@code Year}
+ * @throws UnsupportedTemporalTypeException if the unit is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public long until(Temporal endExclusive, TemporalUnit unit) {
+ Year end = Year.from(endExclusive);
+ if (unit instanceof ChronoUnit) {
+ long yearsUntil = ((long) end.year) - year; // no overflow
+ switch ((ChronoUnit) unit) {
+ case YEARS: return yearsUntil;
+ case DECADES: return yearsUntil / 10;
+ case CENTURIES: return yearsUntil / 100;
+ case MILLENNIA: return yearsUntil / 1000;
+ case ERAS: return end.getLong(ERA) - getLong(ERA);
+ }
+ throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
+ }
+ return unit.between(this, end);
+ }
+
+ /**
+ * Formats this year using the specified formatter.
+ * <p>
+ * This year will be passed to the formatter to produce a string.
+ *
+ * @param formatter the formatter to use, not null
+ * @return the formatted year string, not null
+ * @throws DateTimeException if an error occurs during printing
+ */
+ public String format(DateTimeFormatter formatter) {
+ Objects.requireNonNull(formatter, "formatter");
+ return formatter.format(this);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Combines this year with a day-of-year to create a {@code LocalDate}.
+ * <p>
+ * This returns a {@code LocalDate} formed from this year and the specified day-of-year.
+ * <p>
+ * The day-of-year value 366 is only valid in a leap year.
+ *
+ * @param dayOfYear the day-of-year to use, from 1 to 365-366
+ * @return the local date formed from this year and the specified date of year, not null
+ * @throws DateTimeException if the day of year is zero or less, 366 or greater or equal
+ * to 366 and this is not a leap year
+ */
+ public LocalDate atDay(int dayOfYear) {
+ return LocalDate.ofYearDay(year, dayOfYear);
+ }
+
+ /**
+ * Combines this year with a month to create a {@code YearMonth}.
+ * <p>
+ * This returns a {@code YearMonth} formed from this year and the specified month.
+ * All possible combinations of year and month are valid.
+ * <p>
+ * This method can be used as part of a chain to produce a date:
+ * <pre>
+ * LocalDate date = year.atMonth(month).atDay(day);
+ * </pre>
+ *
+ * @param month the month-of-year to use, not null
+ * @return the year-month formed from this year and the specified month, not null
+ */
+ public YearMonth atMonth(Month month) {
+ return YearMonth.of(year, month);
+ }
+
+ /**
+ * Combines this year with a month to create a {@code YearMonth}.
+ * <p>
+ * This returns a {@code YearMonth} formed from this year and the specified month.
+ * All possible combinations of year and month are valid.
+ * <p>
+ * This method can be used as part of a chain to produce a date:
+ * <pre>
+ * LocalDate date = year.atMonth(month).atDay(day);
+ * </pre>
+ *
+ * @param month the month-of-year to use, from 1 (January) to 12 (December)
+ * @return the year-month formed from this year and the specified month, not null
+ * @throws DateTimeException if the month is invalid
+ */
+ public YearMonth atMonth(int month) {
+ return YearMonth.of(year, month);
+ }
+
+ /**
+ * Combines this year with a month-day to create a {@code LocalDate}.
+ * <p>
+ * This returns a {@code LocalDate} formed from this year and the specified month-day.
+ * <p>
+ * A month-day of February 29th will be adjusted to February 28th in the resulting
+ * date if the year is not a leap year.
+ *
+ * @param monthDay the month-day to use, not null
+ * @return the local date formed from this year and the specified month-day, not null
+ */
+ public LocalDate atMonthDay(MonthDay monthDay) {
+ return monthDay.atYear(year);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Compares this year to another year.
+ * <p>
+ * The comparison is based on the value of the year.
+ * It is "consistent with equals", as defined by {@link Comparable}.
+ *
+ * @param other the other year to compare to, not null
+ * @return the comparator value, negative if less, positive if greater
+ */
+ @Override
+ public int compareTo(Year other) {
+ return year - other.year;
+ }
+
+ /**
+ * Checks if this year is after the specified year.
+ *
+ * @param other the other year to compare to, not null
+ * @return true if this is after the specified year
+ */
+ public boolean isAfter(Year other) {
+ return year > other.year;
+ }
+
+ /**
+ * Checks if this year is before the specified year.
+ *
+ * @param other the other year to compare to, not null
+ * @return true if this point is before the specified year
+ */
+ public boolean isBefore(Year other) {
+ return year < other.year;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if this year is equal to another year.
+ * <p>
+ * The comparison is based on the time-line position of the years.
+ *
+ * @param obj the object to check, null returns false
+ * @return true if this is equal to the other year
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof Year) {
+ return year == ((Year) obj).year;
+ }
+ return false;
+ }
+
+ /**
+ * A hash code for this year.
+ *
+ * @return a suitable hash code
+ */
+ @Override
+ public int hashCode() {
+ return year;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Outputs this year as a {@code String}.
+ *
+ * @return a string representation of this year, not null
+ */
+ @Override
+ public String toString() {
+ return Integer.toString(year);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Writes the object using a
+ * <a href="../../serialized-form.html#java.time.Ser">dedicated serialized form</a>.
+ * @serialData
+ * <pre>
+ * out.writeByte(11); // identifies a Year
+ * out.writeInt(year);
+ * </pre>
+ *
+ * @return the instance of {@code Ser}, not null
+ */
+ private Object writeReplace() {
+ return new Ser(Ser.YEAR_TYPE, this);
+ }
+
+ /**
+ * Defend against malicious streams.
+ *
+ * @param s the stream to read
+ * @throws InvalidObjectException always
+ */
+ private void readObject(ObjectInputStream s) throws InvalidObjectException {
+ throw new InvalidObjectException("Deserialization via serialization delegate");
+ }
+
+ void writeExternal(DataOutput out) throws IOException {
+ out.writeInt(year);
+ }
+
+ static Year readExternal(DataInput in) throws IOException {
+ return Year.of(in.readInt());
+ }
+
+}
diff --git a/java/time/YearMonth.java b/java/time/YearMonth.java
new file mode 100644
index 0000000..582a9d8
--- /dev/null
+++ b/java/time/YearMonth.java
@@ -0,0 +1,1244 @@
+/*
+ * Copyright (c) 2012, 2015, 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) 2007-2012, 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;
+
+import static java.time.temporal.ChronoField.ERA;
+import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
+import static java.time.temporal.ChronoField.PROLEPTIC_MONTH;
+import static java.time.temporal.ChronoField.YEAR;
+import static java.time.temporal.ChronoField.YEAR_OF_ERA;
+import static java.time.temporal.ChronoUnit.CENTURIES;
+import static java.time.temporal.ChronoUnit.DECADES;
+import static java.time.temporal.ChronoUnit.ERAS;
+import static java.time.temporal.ChronoUnit.MILLENNIA;
+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.ObjectInputStream;
+import java.io.Serializable;
+import java.time.chrono.Chronology;
+import java.time.chrono.IsoChronology;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
+import java.time.format.DateTimeParseException;
+import java.time.format.SignStyle;
+import java.time.temporal.ChronoField;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.Temporal;
+import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalAdjuster;
+import java.time.temporal.TemporalAmount;
+import java.time.temporal.TemporalField;
+import java.time.temporal.TemporalQueries;
+import java.time.temporal.TemporalQuery;
+import java.time.temporal.TemporalUnit;
+import java.time.temporal.UnsupportedTemporalTypeException;
+import java.time.temporal.ValueRange;
+import java.util.Objects;
+
+// Android-changed: removed ValueBased paragraph.
+/**
+ * A year-month in the ISO-8601 calendar system, such as {@code 2007-12}.
+ * <p>
+ * {@code YearMonth} is an immutable date-time object that represents the combination
+ * of a year and month. Any field that can be derived from a year and month, such as
+ * quarter-of-year, can be obtained.
+ * <p>
+ * This class does not store or represent a day, time or time-zone.
+ * For example, the value "October 2007" can be stored in a {@code YearMonth}.
+ * <p>
+ * 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.
+ * For most applications written today, the ISO-8601 rules are entirely suitable.
+ * However, any application that makes use of historical dates, and requires them
+ * to be accurate will find the ISO-8601 approach unsuitable.
+ *
+ * @implSpec
+ * This class is immutable and thread-safe.
+ *
+ * @since 1.8
+ */
+public final class YearMonth
+ implements Temporal, TemporalAdjuster, Comparable<YearMonth>, Serializable {
+
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = 4183400860270640070L;
+ /**
+ * Parser.
+ */
+ private static final DateTimeFormatter PARSER = new DateTimeFormatterBuilder()
+ .appendValue(YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
+ .appendLiteral('-')
+ .appendValue(MONTH_OF_YEAR, 2)
+ .toFormatter();
+
+ /**
+ * The year.
+ */
+ private final int year;
+ /**
+ * The month-of-year, not null.
+ */
+ private final int month;
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains the current year-month from the system clock in the default time-zone.
+ * <p>
+ * This will query the {@link Clock#systemDefaultZone() system clock} in the default
+ * time-zone to obtain the current year-month.
+ * <p>
+ * Using this method will prevent the ability to use an alternate clock for testing
+ * because the clock is hard-coded.
+ *
+ * @return the current year-month using the system clock and default time-zone, not null
+ */
+ public static YearMonth now() {
+ return now(Clock.systemDefaultZone());
+ }
+
+ /**
+ * Obtains the current year-month from the system clock in the specified time-zone.
+ * <p>
+ * This will query the {@link Clock#system(ZoneId) system clock} to obtain the current year-month.
+ * Specifying the time-zone avoids dependence on the default time-zone.
+ * <p>
+ * Using this method will prevent the ability to use an alternate clock for testing
+ * because the clock is hard-coded.
+ *
+ * @param zone the zone ID to use, not null
+ * @return the current year-month using the system clock, not null
+ */
+ public static YearMonth now(ZoneId zone) {
+ return now(Clock.system(zone));
+ }
+
+ /**
+ * Obtains the current year-month from the specified clock.
+ * <p>
+ * This will query the specified clock to obtain the current year-month.
+ * Using this method allows the use of an alternate clock for testing.
+ * The alternate clock may be introduced using {@link Clock dependency injection}.
+ *
+ * @param clock the clock to use, not null
+ * @return the current year-month, not null
+ */
+ public static YearMonth now(Clock clock) {
+ final LocalDate now = LocalDate.now(clock); // called once
+ return YearMonth.of(now.getYear(), now.getMonth());
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code YearMonth} from a year and month.
+ *
+ * @param year the year to represent, from MIN_YEAR to MAX_YEAR
+ * @param month the month-of-year to represent, not null
+ * @return the year-month, not null
+ * @throws DateTimeException if the year value is invalid
+ */
+ public static YearMonth of(int year, Month month) {
+ Objects.requireNonNull(month, "month");
+ return of(year, month.getValue());
+ }
+
+ /**
+ * Obtains an instance of {@code YearMonth} from a year and month.
+ *
+ * @param year the year to represent, from MIN_YEAR to MAX_YEAR
+ * @param month the month-of-year to represent, from 1 (January) to 12 (December)
+ * @return the year-month, not null
+ * @throws DateTimeException if either field value is invalid
+ */
+ public static YearMonth of(int year, int month) {
+ YEAR.checkValidValue(year);
+ MONTH_OF_YEAR.checkValidValue(month);
+ return new YearMonth(year, month);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code YearMonth} from a temporal object.
+ * <p>
+ * This obtains a year-month based on the specified temporal.
+ * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
+ * which this factory converts to an instance of {@code YearMonth}.
+ * <p>
+ * The conversion extracts the {@link ChronoField#YEAR YEAR} and
+ * {@link ChronoField#MONTH_OF_YEAR MONTH_OF_YEAR} fields.
+ * The extraction is only permitted if the temporal object has an ISO
+ * chronology, or can be converted to a {@code LocalDate}.
+ * <p>
+ * This method matches the signature of the functional interface {@link TemporalQuery}
+ * allowing it to be used as a query via method reference, {@code YearMonth::from}.
+ *
+ * @param temporal the temporal object to convert, not null
+ * @return the year-month, not null
+ * @throws DateTimeException if unable to convert to a {@code YearMonth}
+ */
+ public static YearMonth from(TemporalAccessor temporal) {
+ if (temporal instanceof YearMonth) {
+ return (YearMonth) temporal;
+ }
+ Objects.requireNonNull(temporal, "temporal");
+ try {
+ if (IsoChronology.INSTANCE.equals(Chronology.from(temporal)) == false) {
+ temporal = LocalDate.from(temporal);
+ }
+ return of(temporal.get(YEAR), temporal.get(MONTH_OF_YEAR));
+ } catch (DateTimeException ex) {
+ throw new DateTimeException("Unable to obtain YearMonth from TemporalAccessor: " +
+ temporal + " of type " + temporal.getClass().getName(), ex);
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code YearMonth} from a text string such as {@code 2007-12}.
+ * <p>
+ * The string must represent a valid year-month.
+ * The format must be {@code uuuu-MM}.
+ * Years outside the range 0000 to 9999 must be prefixed by the plus or minus symbol.
+ *
+ * @param text the text to parse such as "2007-12", not null
+ * @return the parsed year-month, not null
+ * @throws DateTimeParseException if the text cannot be parsed
+ */
+ public static YearMonth parse(CharSequence text) {
+ return parse(text, PARSER);
+ }
+
+ /**
+ * Obtains an instance of {@code YearMonth} from a text string using a specific formatter.
+ * <p>
+ * The text is parsed using the formatter, returning a year-month.
+ *
+ * @param text the text to parse, not null
+ * @param formatter the formatter to use, not null
+ * @return the parsed year-month, not null
+ * @throws DateTimeParseException if the text cannot be parsed
+ */
+ public static YearMonth parse(CharSequence text, DateTimeFormatter formatter) {
+ Objects.requireNonNull(formatter, "formatter");
+ return formatter.parse(text, YearMonth::from);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Constructor.
+ *
+ * @param year the year to represent, validated from MIN_YEAR to MAX_YEAR
+ * @param month the month-of-year to represent, validated from 1 (January) to 12 (December)
+ */
+ private YearMonth(int year, int month) {
+ this.year = year;
+ this.month = month;
+ }
+
+ /**
+ * Returns a copy of this year-month with the new year and month, checking
+ * to see if a new object is in fact required.
+ *
+ * @param newYear the year to represent, validated from MIN_YEAR to MAX_YEAR
+ * @param newMonth the month-of-year to represent, validated not null
+ * @return the year-month, not null
+ */
+ private YearMonth with(int newYear, int newMonth) {
+ if (year == newYear && month == newMonth) {
+ return this;
+ }
+ return new YearMonth(newYear, newMonth);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if the specified field is supported.
+ * <p>
+ * This checks if this year-month can be queried for the specified field.
+ * If false, then calling the {@link #range(TemporalField) range},
+ * {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}
+ * methods will throw an exception.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The supported fields are:
+ * <ul>
+ * <li>{@code MONTH_OF_YEAR}
+ * <li>{@code PROLEPTIC_MONTH}
+ * <li>{@code YEAR_OF_ERA}
+ * <li>{@code YEAR}
+ * <li>{@code ERA}
+ * </ul>
+ * All other {@code ChronoField} instances will return false.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}
+ * passing {@code this} as the argument.
+ * Whether the field is supported is determined by the field.
+ *
+ * @param field the field to check, null returns false
+ * @return true if the field is supported on this year-month, false if not
+ */
+ @Override
+ public boolean isSupported(TemporalField field) {
+ if (field instanceof ChronoField) {
+ return field == YEAR || field == MONTH_OF_YEAR ||
+ field == PROLEPTIC_MONTH || field == YEAR_OF_ERA || field == ERA;
+ }
+ return field != null && field.isSupportedBy(this);
+ }
+
+ /**
+ * Checks if the specified unit is supported.
+ * <p>
+ * This checks if the specified unit can be added to, or subtracted from, this year-month.
+ * If false, then calling the {@link #plus(long, TemporalUnit)} and
+ * {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
+ * <p>
+ * If the unit is a {@link ChronoUnit} then the query is implemented here.
+ * The supported units are:
+ * <ul>
+ * <li>{@code MONTHS}
+ * <li>{@code YEARS}
+ * <li>{@code DECADES}
+ * <li>{@code CENTURIES}
+ * <li>{@code MILLENNIA}
+ * <li>{@code ERAS}
+ * </ul>
+ * All other {@code ChronoUnit} instances will return false.
+ * <p>
+ * If the unit is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
+ * passing {@code this} as the argument.
+ * Whether the unit is supported is determined by the unit.
+ *
+ * @param unit the unit to check, null returns false
+ * @return true if the unit can be added/subtracted, false if not
+ */
+ @Override
+ public boolean isSupported(TemporalUnit unit) {
+ if (unit instanceof ChronoUnit) {
+ return unit == MONTHS || unit == YEARS || unit == DECADES || unit == CENTURIES || unit == MILLENNIA || unit == ERAS;
+ }
+ return unit != null && unit.isSupportedBy(this);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the range of valid values for the specified field.
+ * <p>
+ * The range object expresses the minimum and maximum valid values for a field.
+ * This year-month is used to enhance the accuracy of the returned range.
+ * If it is not possible to return the range, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The {@link #isSupported(TemporalField) supported fields} will return
+ * appropriate range instances.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.rangeRefinedBy(TemporalAccessor)}
+ * passing {@code this} as the argument.
+ * Whether the range can be obtained is determined by the field.
+ *
+ * @param field the field to query the range for, not null
+ * @return the range of valid values for the field, not null
+ * @throws DateTimeException if the range for the field cannot be obtained
+ * @throws UnsupportedTemporalTypeException if the field is not supported
+ */
+ @Override
+ public ValueRange range(TemporalField field) {
+ if (field == YEAR_OF_ERA) {
+ return (getYear() <= 0 ? ValueRange.of(1, Year.MAX_VALUE + 1) : ValueRange.of(1, Year.MAX_VALUE));
+ }
+ return Temporal.super.range(field);
+ }
+
+ /**
+ * Gets the value of the specified field from this year-month as an {@code int}.
+ * <p>
+ * This queries this year-month for the value of the specified field.
+ * The returned value will always be within the valid range of values for the field.
+ * If it is not possible to return the value, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The {@link #isSupported(TemporalField) supported fields} will return valid
+ * values based on this year-month, except {@code PROLEPTIC_MONTH} which is too
+ * large to fit in an {@code int} and throw a {@code DateTimeException}.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
+ * passing {@code this} as the argument. Whether the value can be obtained,
+ * and what the value represents, is determined by the field.
+ *
+ * @param field the field to get, not null
+ * @return the value for the field
+ * @throws DateTimeException if a value for the field cannot be obtained or
+ * the value is outside the range of valid values for the field
+ * @throws UnsupportedTemporalTypeException if the field is not supported or
+ * the range of values exceeds an {@code int}
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override // override for Javadoc
+ public int get(TemporalField field) {
+ return range(field).checkValidIntValue(getLong(field), field);
+ }
+
+ /**
+ * Gets the value of the specified field from this year-month as a {@code long}.
+ * <p>
+ * This queries this year-month for the value of the specified field.
+ * If it is not possible to return the value, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The {@link #isSupported(TemporalField) supported fields} will return valid
+ * values based on this year-month.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
+ * passing {@code this} as the argument. Whether the value can be obtained,
+ * and what the value represents, is determined by the field.
+ *
+ * @param field the field to get, not null
+ * @return the value for the field
+ * @throws DateTimeException if a value for the field cannot be obtained
+ * @throws UnsupportedTemporalTypeException if the field is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public long getLong(TemporalField field) {
+ if (field instanceof ChronoField) {
+ switch ((ChronoField) field) {
+ case MONTH_OF_YEAR: return month;
+ case PROLEPTIC_MONTH: return getProlepticMonth();
+ case YEAR_OF_ERA: return (year < 1 ? 1 - year : year);
+ case YEAR: return year;
+ case ERA: return (year < 1 ? 0 : 1);
+ }
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
+ }
+ return field.getFrom(this);
+ }
+
+ private long getProlepticMonth() {
+ return (year * 12L + month - 1);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the year field.
+ * <p>
+ * This method returns the primitive {@code int} value for the year.
+ * <p>
+ * The year returned by this method is proleptic as per {@code get(YEAR)}.
+ *
+ * @return the year, from MIN_YEAR to MAX_YEAR
+ */
+ public int getYear() {
+ return year;
+ }
+
+ /**
+ * Gets the month-of-year field from 1 to 12.
+ * <p>
+ * This method returns the month as an {@code int} from 1 to 12.
+ * Application code is frequently clearer if the enum {@link Month}
+ * is used by calling {@link #getMonth()}.
+ *
+ * @return the month-of-year, from 1 to 12
+ * @see #getMonth()
+ */
+ public int getMonthValue() {
+ return month;
+ }
+
+ /**
+ * Gets the month-of-year field using the {@code Month} enum.
+ * <p>
+ * This method returns the enum {@link Month} for the month.
+ * This avoids confusion as to what {@code int} values mean.
+ * If you need access to the primitive {@code int} value then the enum
+ * provides the {@link Month#getValue() int value}.
+ *
+ * @return the month-of-year, not null
+ * @see #getMonthValue()
+ */
+ public Month getMonth() {
+ return Month.of(month);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if the year is a leap year, according to the ISO proleptic
+ * calendar system rules.
+ * <p>
+ * This method applies the current rules for leap years across the whole time-line.
+ * In general, a year is a leap year if it is divisible by four without
+ * remainder. However, years divisible by 100, are not leap years, with
+ * the exception of years divisible by 400 which are.
+ * <p>
+ * For example, 1904 is a leap year it is divisible by 4.
+ * 1900 was not a leap year as it is divisible by 100, however 2000 was a
+ * leap year as it is divisible by 400.
+ * <p>
+ * The calculation is proleptic - applying the same rules into the far future and far past.
+ * This is historically inaccurate, but is correct for the ISO-8601 standard.
+ *
+ * @return true if the year is leap, false otherwise
+ */
+ public boolean isLeapYear() {
+ return IsoChronology.INSTANCE.isLeapYear(year);
+ }
+
+ /**
+ * Checks if the day-of-month is valid for this year-month.
+ * <p>
+ * This method checks whether this year and month and the input day form
+ * a valid date.
+ *
+ * @param dayOfMonth the day-of-month to validate, from 1 to 31, invalid value returns false
+ * @return true if the day is valid for this year-month
+ */
+ public boolean isValidDay(int dayOfMonth) {
+ return dayOfMonth >= 1 && dayOfMonth <= lengthOfMonth();
+ }
+
+ /**
+ * Returns the length of the month, taking account of the year.
+ * <p>
+ * This returns the length of the month in days.
+ * For example, a date in January would return 31.
+ *
+ * @return the length of the month in days, from 28 to 31
+ */
+ public int lengthOfMonth() {
+ return getMonth().length(isLeapYear());
+ }
+
+ /**
+ * Returns the length of the year.
+ * <p>
+ * This returns the length of the year in days, either 365 or 366.
+ *
+ * @return 366 if the year is leap, 365 otherwise
+ */
+ public int lengthOfYear() {
+ return (isLeapYear() ? 366 : 365);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns an adjusted copy of this year-month.
+ * <p>
+ * This returns a {@code YearMonth}, based on this one, with the year-month adjusted.
+ * The adjustment takes place using the specified adjuster strategy object.
+ * Read the documentation of the adjuster to understand what adjustment will be made.
+ * <p>
+ * A simple adjuster might simply set the one of the fields, such as the year field.
+ * A more complex adjuster might set the year-month to the next month that
+ * Halley's comet will pass the Earth.
+ * <p>
+ * The result of this method is obtained by invoking the
+ * {@link TemporalAdjuster#adjustInto(Temporal)} method on the
+ * specified adjuster passing {@code this} as the argument.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param adjuster the adjuster to use, not null
+ * @return a {@code YearMonth} based on {@code this} with the adjustment made, not null
+ * @throws DateTimeException if the adjustment cannot be made
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public YearMonth with(TemporalAdjuster adjuster) {
+ return (YearMonth) adjuster.adjustInto(this);
+ }
+
+ /**
+ * Returns a copy of this year-month with the specified field set to a new value.
+ * <p>
+ * This returns a {@code YearMonth}, based on this one, with the value
+ * for the specified field changed.
+ * This can be used to change any supported field, such as the year or month.
+ * If it is not possible to set the value, because the field is not supported or for
+ * some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the adjustment is implemented here.
+ * The supported fields behave as follows:
+ * <ul>
+ * <li>{@code MONTH_OF_YEAR} -
+ * Returns a {@code YearMonth} with the specified month-of-year.
+ * The year will be unchanged.
+ * <li>{@code PROLEPTIC_MONTH} -
+ * Returns a {@code YearMonth} with the specified proleptic-month.
+ * This completely replaces the year and month of this object.
+ * <li>{@code YEAR_OF_ERA} -
+ * Returns a {@code YearMonth} with the specified year-of-era
+ * The month and era will be unchanged.
+ * <li>{@code YEAR} -
+ * Returns a {@code YearMonth} with the specified year.
+ * The month will be unchanged.
+ * <li>{@code ERA} -
+ * Returns a {@code YearMonth} with the specified era.
+ * The month and year-of-era will be unchanged.
+ * </ul>
+ * <p>
+ * In all cases, if the new value is outside the valid range of values for the field
+ * then a {@code DateTimeException} will be thrown.
+ * <p>
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.adjustInto(Temporal, long)}
+ * passing {@code this} as the argument. In this case, the field determines
+ * whether and how to adjust the instant.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param field the field to set in the result, not null
+ * @param newValue the new value of the field in the result
+ * @return a {@code YearMonth} based on {@code this} with the specified field set, not null
+ * @throws DateTimeException if the field cannot be set
+ * @throws UnsupportedTemporalTypeException if the field is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public YearMonth with(TemporalField field, long newValue) {
+ if (field instanceof ChronoField) {
+ ChronoField f = (ChronoField) field;
+ f.checkValidValue(newValue);
+ switch (f) {
+ case MONTH_OF_YEAR: return withMonth((int) newValue);
+ case PROLEPTIC_MONTH: return plusMonths(newValue - getProlepticMonth());
+ case YEAR_OF_ERA: return withYear((int) (year < 1 ? 1 - newValue : newValue));
+ case YEAR: return withYear((int) newValue);
+ case ERA: return (getLong(ERA) == newValue ? this : withYear(1 - year));
+ }
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
+ }
+ return field.adjustInto(this, newValue);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this {@code YearMonth} with the year altered.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param year the year to set in the returned year-month, from MIN_YEAR to MAX_YEAR
+ * @return a {@code YearMonth} based on this year-month with the requested year, not null
+ * @throws DateTimeException if the year value is invalid
+ */
+ public YearMonth withYear(int year) {
+ YEAR.checkValidValue(year);
+ return with(year, month);
+ }
+
+ /**
+ * Returns a copy of this {@code YearMonth} with the month-of-year altered.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param month the month-of-year to set in the returned year-month, from 1 (January) to 12 (December)
+ * @return a {@code YearMonth} based on this year-month with the requested month, not null
+ * @throws DateTimeException if the month-of-year value is invalid
+ */
+ public YearMonth withMonth(int month) {
+ MONTH_OF_YEAR.checkValidValue(month);
+ return with(year, month);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this year-month with the specified amount added.
+ * <p>
+ * This returns a {@code YearMonth}, based on this one, with the specified amount added.
+ * The amount is typically {@link Period} but may be any other type implementing
+ * the {@link TemporalAmount} interface.
+ * <p>
+ * The calculation is delegated to the amount object by calling
+ * {@link TemporalAmount#addTo(Temporal)}. The amount implementation is free
+ * to implement the addition in any way it wishes, however it typically
+ * calls back to {@link #plus(long, TemporalUnit)}. Consult the documentation
+ * of the amount implementation to determine if it can be successfully added.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param amountToAdd the amount to add, not null
+ * @return a {@code YearMonth} based on this year-month with the addition made, not null
+ * @throws DateTimeException if the addition cannot be made
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public YearMonth plus(TemporalAmount amountToAdd) {
+ return (YearMonth) amountToAdd.addTo(this);
+ }
+
+ /**
+ * Returns a copy of this year-month with the specified amount added.
+ * <p>
+ * This returns a {@code YearMonth}, based on this one, with the amount
+ * in terms of the unit added. If it is not possible to add the amount, because the
+ * unit is not supported or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoUnit} then the addition is implemented here.
+ * The supported fields behave as follows:
+ * <ul>
+ * <li>{@code MONTHS} -
+ * Returns a {@code YearMonth} with the specified number of months added.
+ * This is equivalent to {@link #plusMonths(long)}.
+ * <li>{@code YEARS} -
+ * Returns a {@code YearMonth} with the specified number of years added.
+ * This is equivalent to {@link #plusYears(long)}.
+ * <li>{@code DECADES} -
+ * Returns a {@code YearMonth} with the specified number of decades added.
+ * This is equivalent to calling {@link #plusYears(long)} with the amount
+ * multiplied by 10.
+ * <li>{@code CENTURIES} -
+ * Returns a {@code YearMonth} with the specified number of centuries added.
+ * This is equivalent to calling {@link #plusYears(long)} with the amount
+ * multiplied by 100.
+ * <li>{@code MILLENNIA} -
+ * Returns a {@code YearMonth} with the specified number of millennia added.
+ * This is equivalent to calling {@link #plusYears(long)} with the amount
+ * multiplied by 1,000.
+ * <li>{@code ERAS} -
+ * Returns a {@code YearMonth} with the specified number of eras added.
+ * Only two eras are supported so the amount must be one, zero or minus one.
+ * If the amount is non-zero then the year is changed such that the year-of-era
+ * is unchanged.
+ * </ul>
+ * <p>
+ * All other {@code ChronoUnit} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.addTo(Temporal, long)}
+ * passing {@code this} as the argument. In this case, the unit determines
+ * whether and how to perform the addition.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param amountToAdd the amount of the unit to add to the result, may be negative
+ * @param unit the unit of the amount to add, not null
+ * @return a {@code YearMonth} based on this year-month with the specified amount added, not null
+ * @throws DateTimeException if the addition cannot be made
+ * @throws UnsupportedTemporalTypeException if the unit is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public YearMonth plus(long amountToAdd, TemporalUnit unit) {
+ if (unit instanceof ChronoUnit) {
+ switch ((ChronoUnit) unit) {
+ case MONTHS: return plusMonths(amountToAdd);
+ case YEARS: return plusYears(amountToAdd);
+ case DECADES: return plusYears(Math.multiplyExact(amountToAdd, 10));
+ case CENTURIES: return plusYears(Math.multiplyExact(amountToAdd, 100));
+ case MILLENNIA: return plusYears(Math.multiplyExact(amountToAdd, 1000));
+ case ERAS: return with(ERA, Math.addExact(getLong(ERA), amountToAdd));
+ }
+ throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
+ }
+ return unit.addTo(this, amountToAdd);
+ }
+
+ /**
+ * Returns a copy of this {@code YearMonth} with the specified number of years added.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param yearsToAdd the years to add, may be negative
+ * @return a {@code YearMonth} based on this year-month with the years added, not null
+ * @throws DateTimeException if the result exceeds the supported range
+ */
+ public YearMonth plusYears(long yearsToAdd) {
+ if (yearsToAdd == 0) {
+ return this;
+ }
+ int newYear = YEAR.checkValidIntValue(year + yearsToAdd); // safe overflow
+ return with(newYear, month);
+ }
+
+ /**
+ * Returns a copy of this {@code YearMonth} with the specified number of months added.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param monthsToAdd the months to add, may be negative
+ * @return a {@code YearMonth} based on this year-month with the months added, not null
+ * @throws DateTimeException if the result exceeds the supported range
+ */
+ public YearMonth plusMonths(long monthsToAdd) {
+ if (monthsToAdd == 0) {
+ return this;
+ }
+ long monthCount = year * 12L + (month - 1);
+ long calcMonths = monthCount + monthsToAdd; // safe overflow
+ int newYear = YEAR.checkValidIntValue(Math.floorDiv(calcMonths, 12));
+ int newMonth = (int)Math.floorMod(calcMonths, 12) + 1;
+ return with(newYear, newMonth);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this year-month with the specified amount subtracted.
+ * <p>
+ * This returns a {@code YearMonth}, based on this one, with the specified amount subtracted.
+ * The amount is typically {@link Period} but may be any other type implementing
+ * the {@link TemporalAmount} interface.
+ * <p>
+ * The calculation is delegated to the amount object by calling
+ * {@link TemporalAmount#subtractFrom(Temporal)}. The amount implementation is free
+ * to implement the subtraction in any way it wishes, however it typically
+ * calls back to {@link #minus(long, TemporalUnit)}. Consult the documentation
+ * of the amount implementation to determine if it can be successfully subtracted.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param amountToSubtract the amount to subtract, not null
+ * @return a {@code YearMonth} based on this year-month with the subtraction made, not null
+ * @throws DateTimeException if the subtraction cannot be made
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public YearMonth minus(TemporalAmount amountToSubtract) {
+ return (YearMonth) amountToSubtract.subtractFrom(this);
+ }
+
+ /**
+ * Returns a copy of this year-month with the specified amount subtracted.
+ * <p>
+ * This returns a {@code YearMonth}, based on this one, with the amount
+ * in terms of the unit subtracted. If it is not possible to subtract the amount,
+ * because the unit is not supported or for some other reason, an exception is thrown.
+ * <p>
+ * This method is equivalent to {@link #plus(long, TemporalUnit)} with the amount negated.
+ * See that method for a full description of how addition, and thus subtraction, works.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param amountToSubtract the amount of the unit to subtract from the result, may be negative
+ * @param unit the unit of the amount to subtract, not null
+ * @return a {@code YearMonth} based on this year-month with the specified amount subtracted, not null
+ * @throws DateTimeException if the subtraction cannot be made
+ * @throws UnsupportedTemporalTypeException if the unit is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public YearMonth minus(long amountToSubtract, TemporalUnit unit) {
+ return (amountToSubtract == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit));
+ }
+
+ /**
+ * Returns a copy of this {@code YearMonth} with the specified number of years subtracted.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param yearsToSubtract the years to subtract, may be negative
+ * @return a {@code YearMonth} based on this year-month with the years subtracted, not null
+ * @throws DateTimeException if the result exceeds the supported range
+ */
+ public YearMonth minusYears(long yearsToSubtract) {
+ return (yearsToSubtract == Long.MIN_VALUE ? plusYears(Long.MAX_VALUE).plusYears(1) : plusYears(-yearsToSubtract));
+ }
+
+ /**
+ * Returns a copy of this {@code YearMonth} with the specified number of months subtracted.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param monthsToSubtract the months to subtract, may be negative
+ * @return a {@code YearMonth} based on this year-month with the months subtracted, not null
+ * @throws DateTimeException if the result exceeds the supported range
+ */
+ public YearMonth minusMonths(long monthsToSubtract) {
+ return (monthsToSubtract == Long.MIN_VALUE ? plusMonths(Long.MAX_VALUE).plusMonths(1) : plusMonths(-monthsToSubtract));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Queries this year-month using the specified query.
+ * <p>
+ * This queries this year-month using the specified query strategy object.
+ * The {@code TemporalQuery} object defines the logic to be used to
+ * obtain the result. Read the documentation of the query to understand
+ * what the result of this method will be.
+ * <p>
+ * The result of this method is obtained by invoking the
+ * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the
+ * specified query passing {@code this} as the argument.
+ *
+ * @param <R> the type of the result
+ * @param query the query to invoke, not null
+ * @return the query result, null may be returned (defined by the query)
+ * @throws DateTimeException if unable to query (defined by the query)
+ * @throws ArithmeticException if numeric overflow occurs (defined by the query)
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public <R> R query(TemporalQuery<R> query) {
+ if (query == TemporalQueries.chronology()) {
+ return (R) IsoChronology.INSTANCE;
+ } else if (query == TemporalQueries.precision()) {
+ return (R) MONTHS;
+ }
+ return Temporal.super.query(query);
+ }
+
+ /**
+ * Adjusts the specified temporal object to have this year-month.
+ * <p>
+ * This returns a temporal object of the same observable type as the input
+ * with the year and month changed to be the same as this.
+ * <p>
+ * The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)}
+ * passing {@link ChronoField#PROLEPTIC_MONTH} as the field.
+ * If the specified temporal object does not use the ISO calendar system then
+ * a {@code DateTimeException} is thrown.
+ * <p>
+ * In most cases, it is clearer to reverse the calling pattern by using
+ * {@link Temporal#with(TemporalAdjuster)}:
+ * <pre>
+ * // these two lines are equivalent, but the second approach is recommended
+ * temporal = thisYearMonth.adjustInto(temporal);
+ * temporal = temporal.with(thisYearMonth);
+ * </pre>
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param temporal the target object to be adjusted, not null
+ * @return the adjusted object, not null
+ * @throws DateTimeException if unable to make the adjustment
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public Temporal adjustInto(Temporal temporal) {
+ if (Chronology.from(temporal).equals(IsoChronology.INSTANCE) == false) {
+ throw new DateTimeException("Adjustment only supported on ISO date-time");
+ }
+ return temporal.with(PROLEPTIC_MONTH, getProlepticMonth());
+ }
+
+ /**
+ * Calculates the amount of time until another year-month in terms of the specified unit.
+ * <p>
+ * This calculates the amount of time between two {@code YearMonth}
+ * objects in terms of a single {@code TemporalUnit}.
+ * The start and end points are {@code this} and the specified year-month.
+ * The result will be negative if the end is before the start.
+ * The {@code Temporal} passed to this method is converted to a
+ * {@code YearMonth} using {@link #from(TemporalAccessor)}.
+ * For example, the amount in years between two year-months can be calculated
+ * using {@code startYearMonth.until(endYearMonth, YEARS)}.
+ * <p>
+ * The calculation returns a whole number, representing the number of
+ * complete units between the two year-months.
+ * For example, the amount in decades between 2012-06 and 2032-05
+ * will only be one decade as it is one month short of two decades.
+ * <p>
+ * There are two equivalent ways of using this method.
+ * The first is to invoke this method.
+ * The second is to use {@link TemporalUnit#between(Temporal, Temporal)}:
+ * <pre>
+ * // these two lines are equivalent
+ * amount = start.until(end, MONTHS);
+ * amount = MONTHS.between(start, end);
+ * </pre>
+ * The choice should be made based on which makes the code more readable.
+ * <p>
+ * The calculation is implemented in this method for {@link ChronoUnit}.
+ * The units {@code MONTHS}, {@code YEARS}, {@code DECADES},
+ * {@code CENTURIES}, {@code MILLENNIA} and {@code ERAS} are supported.
+ * Other {@code ChronoUnit} values will throw an exception.
+ * <p>
+ * If the unit is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.between(Temporal, Temporal)}
+ * passing {@code this} as the first argument and the converted input temporal
+ * as the second argument.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param endExclusive the end date, exclusive, which is converted to a {@code YearMonth}, not null
+ * @param unit the unit to measure the amount in, not null
+ * @return the amount of time between this year-month and the end year-month
+ * @throws DateTimeException if the amount cannot be calculated, or the end
+ * temporal cannot be converted to a {@code YearMonth}
+ * @throws UnsupportedTemporalTypeException if the unit is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public long until(Temporal endExclusive, TemporalUnit unit) {
+ YearMonth end = YearMonth.from(endExclusive);
+ if (unit instanceof ChronoUnit) {
+ long monthsUntil = end.getProlepticMonth() - getProlepticMonth(); // no overflow
+ switch ((ChronoUnit) unit) {
+ case MONTHS: return monthsUntil;
+ case YEARS: return monthsUntil / 12;
+ case DECADES: return monthsUntil / 120;
+ case CENTURIES: return monthsUntil / 1200;
+ case MILLENNIA: return monthsUntil / 12000;
+ case ERAS: return end.getLong(ERA) - getLong(ERA);
+ }
+ throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
+ }
+ return unit.between(this, end);
+ }
+
+ /**
+ * Formats this year-month using the specified formatter.
+ * <p>
+ * This year-month will be passed to the formatter to produce a string.
+ *
+ * @param formatter the formatter to use, not null
+ * @return the formatted year-month string, not null
+ * @throws DateTimeException if an error occurs during printing
+ */
+ public String format(DateTimeFormatter formatter) {
+ Objects.requireNonNull(formatter, "formatter");
+ return formatter.format(this);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Combines this year-month with a day-of-month to create a {@code LocalDate}.
+ * <p>
+ * This returns a {@code LocalDate} formed from this year-month and the specified day-of-month.
+ * <p>
+ * The day-of-month value must be valid for the year-month.
+ * <p>
+ * This method can be used as part of a chain to produce a date:
+ * <pre>
+ * LocalDate date = year.atMonth(month).atDay(day);
+ * </pre>
+ *
+ * @param dayOfMonth the day-of-month to use, from 1 to 31
+ * @return the date formed from this year-month and the specified day, not null
+ * @throws DateTimeException if the day is invalid for the year-month
+ * @see #isValidDay(int)
+ */
+ public LocalDate atDay(int dayOfMonth) {
+ return LocalDate.of(year, month, dayOfMonth);
+ }
+
+ /**
+ * Returns a {@code LocalDate} at the end of the month.
+ * <p>
+ * This returns a {@code LocalDate} based on this year-month.
+ * The day-of-month is set to the last valid day of the month, taking
+ * into account leap years.
+ * <p>
+ * This method can be used as part of a chain to produce a date:
+ * <pre>
+ * LocalDate date = year.atMonth(month).atEndOfMonth();
+ * </pre>
+ *
+ * @return the last valid date of this year-month, not null
+ */
+ public LocalDate atEndOfMonth() {
+ return LocalDate.of(year, month, lengthOfMonth());
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Compares this year-month to another year-month.
+ * <p>
+ * The comparison is based first on the value of the year, then on the value of the month.
+ * It is "consistent with equals", as defined by {@link Comparable}.
+ *
+ * @param other the other year-month to compare to, not null
+ * @return the comparator value, negative if less, positive if greater
+ */
+ @Override
+ public int compareTo(YearMonth other) {
+ int cmp = (year - other.year);
+ if (cmp == 0) {
+ cmp = (month - other.month);
+ }
+ return cmp;
+ }
+
+ /**
+ * Checks if this year-month is after the specified year-month.
+ *
+ * @param other the other year-month to compare to, not null
+ * @return true if this is after the specified year-month
+ */
+ public boolean isAfter(YearMonth other) {
+ return compareTo(other) > 0;
+ }
+
+ /**
+ * Checks if this year-month is before the specified year-month.
+ *
+ * @param other the other year-month to compare to, not null
+ * @return true if this point is before the specified year-month
+ */
+ public boolean isBefore(YearMonth other) {
+ return compareTo(other) < 0;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if this year-month is equal to another year-month.
+ * <p>
+ * The comparison is based on the time-line position of the year-months.
+ *
+ * @param obj the object to check, null returns false
+ * @return true if this is equal to the other year-month
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof YearMonth) {
+ YearMonth other = (YearMonth) obj;
+ return year == other.year && month == other.month;
+ }
+ return false;
+ }
+
+ /**
+ * A hash code for this year-month.
+ *
+ * @return a suitable hash code
+ */
+ @Override
+ public int hashCode() {
+ return year ^ (month << 27);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Outputs this year-month as a {@code String}, such as {@code 2007-12}.
+ * <p>
+ * The output will be in the format {@code uuuu-MM}:
+ *
+ * @return a string representation of this year-month, not null
+ */
+ @Override
+ public String toString() {
+ int absYear = Math.abs(year);
+ StringBuilder buf = new StringBuilder(9);
+ if (absYear < 1000) {
+ if (year < 0) {
+ buf.append(year - 10000).deleteCharAt(1);
+ } else {
+ buf.append(year + 10000).deleteCharAt(0);
+ }
+ } else {
+ buf.append(year);
+ }
+ return buf.append(month < 10 ? "-0" : "-")
+ .append(month)
+ .toString();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Writes the object using a
+ * <a href="../../serialized-form.html#java.time.Ser">dedicated serialized form</a>.
+ * @serialData
+ * <pre>
+ * out.writeByte(12); // identifies a YearMonth
+ * out.writeInt(year);
+ * out.writeByte(month);
+ * </pre>
+ *
+ * @return the instance of {@code Ser}, not null
+ */
+ private Object writeReplace() {
+ return new Ser(Ser.YEAR_MONTH_TYPE, this);
+ }
+
+ /**
+ * Defend against malicious streams.
+ *
+ * @param s the stream to read
+ * @throws InvalidObjectException always
+ */
+ private void readObject(ObjectInputStream s) throws InvalidObjectException {
+ throw new InvalidObjectException("Deserialization via serialization delegate");
+ }
+
+ void writeExternal(DataOutput out) throws IOException {
+ out.writeInt(year);
+ out.writeByte(month);
+ }
+
+ static YearMonth readExternal(DataInput in) throws IOException {
+ int year = in.readInt();
+ byte month = in.readByte();
+ return YearMonth.of(year, month);
+ }
+
+}
diff --git a/java/time/ZoneId.java b/java/time/ZoneId.java
new file mode 100644
index 0000000..11e34e0
--- /dev/null
+++ b/java/time/ZoneId.java
@@ -0,0 +1,658 @@
+/*
+ * Copyright (c) 2012, 2015, 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) 2007-2012, 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;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.time.format.DateTimeFormatterBuilder;
+import java.time.format.TextStyle;
+import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalField;
+import java.time.temporal.TemporalQueries;
+import java.time.temporal.TemporalQuery;
+import java.time.temporal.UnsupportedTemporalTypeException;
+import java.time.zone.ZoneRules;
+import java.time.zone.ZoneRulesException;
+import java.time.zone.ZoneRulesProvider;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.TimeZone;
+
+// Android-changed: removed ValueBased paragraph.
+// Android-changed: removed {@link ZoneRulesProvider}.
+/**
+ * A time-zone ID, such as {@code Europe/Paris}.
+ * <p>
+ * A {@code ZoneId} is used to identify the rules used to convert between
+ * an {@link Instant} and a {@link LocalDateTime}.
+ * There are two distinct types of ID:
+ * <ul>
+ * <li>Fixed offsets - a fully resolved offset from UTC/Greenwich, that uses
+ * the same offset for all local date-times
+ * <li>Geographical regions - an area where a specific set of rules for finding
+ * the offset from UTC/Greenwich apply
+ * </ul>
+ * Most fixed offsets are represented by {@link ZoneOffset}.
+ * Calling {@link #normalized()} on any {@code ZoneId} will ensure that a
+ * fixed offset ID will be represented as a {@code ZoneOffset}.
+ * <p>
+ * The actual rules, describing when and how the offset changes, are defined by {@link ZoneRules}.
+ * This class is simply an ID used to obtain the underlying rules.
+ * This approach is taken because rules are defined by governments and change
+ * frequently, whereas the ID is stable.
+ * <p>
+ * The distinction has other effects. Serializing the {@code ZoneId} will only send
+ * the ID, whereas serializing the rules sends the entire data set.
+ * Similarly, a comparison of two IDs only examines the ID, whereas
+ * a comparison of two rules examines the entire data set.
+ *
+ * <h3>Time-zone IDs</h3>
+ * The ID is unique within the system.
+ * There are three types of ID.
+ * <p>
+ * The simplest type of ID is that from {@code ZoneOffset}.
+ * This consists of 'Z' and IDs starting with '+' or '-'.
+ * <p>
+ * The next type of ID are offset-style IDs with some form of prefix,
+ * such as 'GMT+2' or 'UTC+01:00'.
+ * The recognised prefixes are 'UTC', 'GMT' and 'UT'.
+ * The offset is the suffix and will be normalized during creation.
+ * These IDs can be normalized to a {@code ZoneOffset} using {@code normalized()}.
+ * <p>
+ * The third type of ID are region-based IDs. A region-based ID must be of
+ * two or more characters, and not start with 'UTC', 'GMT', 'UT' '+' or '-'.
+ * Region-based IDs are defined by configuration.
+ * The configuration focuses on providing the lookup from the ID to the
+ * underlying {@code ZoneRules}.
+ * <p>
+ * Time-zone rules are defined by governments and change frequently.
+ * There are a number of organizations, known here as groups, that monitor
+ * time-zone changes and collate them.
+ * The default group is the IANA Time Zone Database (TZDB).
+ * Other organizations include IATA (the airline industry body) and Microsoft.
+ * <p>
+ * Each group defines its own format for the region ID it provides.
+ * The TZDB group defines IDs such as 'Europe/London' or 'America/New_York'.
+ * TZDB IDs take precedence over other groups.
+ * <p>
+ * It is strongly recommended that the group name is included in all IDs supplied by
+ * groups other than TZDB to avoid conflicts. For example, IATA airline time-zone
+ * region IDs are typically the same as the three letter airport code.
+ * However, the airport of Utrecht has the code 'UTC', which is obviously a conflict.
+ * The recommended format for region IDs from groups other than TZDB is 'group~region'.
+ * Thus if IATA data were defined, Utrecht airport would be 'IATA~UTC'.
+ *
+ * <h3>Serialization</h3>
+ * This class can be serialized and stores the string zone ID in the external form.
+ * The {@code ZoneOffset} subclass uses a dedicated format that only stores the
+ * offset from UTC/Greenwich.
+ * <p>
+ * A {@code ZoneId} can be deserialized in a Java Runtime where the ID is unknown.
+ * For example, if a server-side Java Runtime has been updated with a new zone ID, but
+ * the client-side Java Runtime has not been updated. In this case, the {@code ZoneId}
+ * object will exist, and can be queried using {@code getId}, {@code equals},
+ * {@code hashCode}, {@code toString}, {@code getDisplayName} and {@code normalized}.
+ * However, any call to {@code getRules} will fail with {@code ZoneRulesException}.
+ * This approach is designed to allow a {@link ZonedDateTime} to be loaded and
+ * queried, but not modified, on a Java Runtime with incomplete time-zone information.
+ *
+ * @implSpec
+ * This abstract class has two implementations, both of which are immutable and thread-safe.
+ * One implementation models region-based IDs, the other is {@code ZoneOffset} modelling
+ * offset-based IDs. This difference is visible in serialization.
+ *
+ * @since 1.8
+ */
+public abstract class ZoneId implements Serializable {
+
+ /**
+ * A map of zone overrides to enable the short time-zone names to be used.
+ * <p>
+ * Use of short zone IDs has been deprecated in {@code java.util.TimeZone}.
+ * This map allows the IDs to continue to be used via the
+ * {@link #of(String, Map)} factory method.
+ * <p>
+ * This map contains a mapping of the IDs that is in line with TZDB 2005r and
+ * later, where 'EST', 'MST' and 'HST' map to IDs which do not include daylight
+ * savings.
+ * <p>
+ * This maps as follows:
+ * <ul>
+ * <li>EST - -05:00</li>
+ * <li>HST - -10:00</li>
+ * <li>MST - -07:00</li>
+ * <li>ACT - Australia/Darwin</li>
+ * <li>AET - Australia/Sydney</li>
+ * <li>AGT - America/Argentina/Buenos_Aires</li>
+ * <li>ART - Africa/Cairo</li>
+ * <li>AST - America/Anchorage</li>
+ * <li>BET - America/Sao_Paulo</li>
+ * <li>BST - Asia/Dhaka</li>
+ * <li>CAT - Africa/Harare</li>
+ * <li>CNT - America/St_Johns</li>
+ * <li>CST - America/Chicago</li>
+ * <li>CTT - Asia/Shanghai</li>
+ * <li>EAT - Africa/Addis_Ababa</li>
+ * <li>ECT - Europe/Paris</li>
+ * <li>IET - America/Indiana/Indianapolis</li>
+ * <li>IST - Asia/Kolkata</li>
+ * <li>JST - Asia/Tokyo</li>
+ * <li>MIT - Pacific/Apia</li>
+ * <li>NET - Asia/Yerevan</li>
+ * <li>NST - Pacific/Auckland</li>
+ * <li>PLT - Asia/Karachi</li>
+ * <li>PNT - America/Phoenix</li>
+ * <li>PRT - America/Puerto_Rico</li>
+ * <li>PST - America/Los_Angeles</li>
+ * <li>SST - Pacific/Guadalcanal</li>
+ * <li>VST - Asia/Ho_Chi_Minh</li>
+ * </ul>
+ * The map is unmodifiable.
+ */
+ public static final Map<String, String> SHORT_IDS;
+ static {
+ Map<String, String> map = new HashMap<>(64);
+ map.put("ACT", "Australia/Darwin");
+ map.put("AET", "Australia/Sydney");
+ map.put("AGT", "America/Argentina/Buenos_Aires");
+ map.put("ART", "Africa/Cairo");
+ map.put("AST", "America/Anchorage");
+ map.put("BET", "America/Sao_Paulo");
+ map.put("BST", "Asia/Dhaka");
+ map.put("CAT", "Africa/Harare");
+ map.put("CNT", "America/St_Johns");
+ map.put("CST", "America/Chicago");
+ map.put("CTT", "Asia/Shanghai");
+ map.put("EAT", "Africa/Addis_Ababa");
+ map.put("ECT", "Europe/Paris");
+ map.put("IET", "America/Indiana/Indianapolis");
+ map.put("IST", "Asia/Kolkata");
+ map.put("JST", "Asia/Tokyo");
+ map.put("MIT", "Pacific/Apia");
+ map.put("NET", "Asia/Yerevan");
+ map.put("NST", "Pacific/Auckland");
+ map.put("PLT", "Asia/Karachi");
+ map.put("PNT", "America/Phoenix");
+ map.put("PRT", "America/Puerto_Rico");
+ map.put("PST", "America/Los_Angeles");
+ map.put("SST", "Pacific/Guadalcanal");
+ map.put("VST", "Asia/Ho_Chi_Minh");
+ map.put("EST", "-05:00");
+ map.put("MST", "-07:00");
+ map.put("HST", "-10:00");
+ SHORT_IDS = Collections.unmodifiableMap(map);
+ }
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = 8352817235686L;
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the system default time-zone.
+ * <p>
+ * This queries {@link TimeZone#getDefault()} to find the default time-zone
+ * and converts it to a {@code ZoneId}. If the system default time-zone is changed,
+ * then the result of this method will also change.
+ *
+ * @return the zone ID, not null
+ * @throws DateTimeException if the converted zone ID has an invalid format
+ * @throws ZoneRulesException if the converted zone region ID cannot be found
+ */
+ public static ZoneId systemDefault() {
+ return TimeZone.getDefault().toZoneId();
+ }
+
+ /**
+ * Gets the set of available zone IDs.
+ * <p>
+ * This set includes the string form of all available region-based IDs.
+ * Offset-based zone IDs are not included in the returned set.
+ * The ID can be passed to {@link #of(String)} to create a {@code ZoneId}.
+ * <p>
+ * The set of zone IDs can increase over time, although in a typical application
+ * the set of IDs is fixed. Each call to this method is thread-safe.
+ *
+ * @return a modifiable copy of the set of zone IDs, not null
+ */
+ public static Set<String> getAvailableZoneIds() {
+ return ZoneRulesProvider.getAvailableZoneIds();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code ZoneId} using its ID using a map
+ * of aliases to supplement the standard zone IDs.
+ * <p>
+ * Many users of time-zones use short abbreviations, such as PST for
+ * 'Pacific Standard Time' and PDT for 'Pacific Daylight Time'.
+ * These abbreviations are not unique, and so cannot be used as IDs.
+ * This method allows a map of string to time-zone to be setup and reused
+ * within an application.
+ *
+ * @param zoneId the time-zone ID, not null
+ * @param aliasMap a map of alias zone IDs (typically abbreviations) to real zone IDs, not null
+ * @return the zone ID, not null
+ * @throws DateTimeException if the zone ID has an invalid format
+ * @throws ZoneRulesException if the zone ID is a region ID that cannot be found
+ */
+ public static ZoneId of(String zoneId, Map<String, String> aliasMap) {
+ Objects.requireNonNull(zoneId, "zoneId");
+ Objects.requireNonNull(aliasMap, "aliasMap");
+ String id = aliasMap.get(zoneId);
+ id = (id != null ? id : zoneId);
+ return of(id);
+ }
+
+ /**
+ * Obtains an instance of {@code ZoneId} from an ID ensuring that the
+ * ID is valid and available for use.
+ * <p>
+ * This method parses the ID producing a {@code ZoneId} or {@code ZoneOffset}.
+ * A {@code ZoneOffset} is returned if the ID is 'Z', or starts with '+' or '-'.
+ * The result will always be a valid ID for which {@link ZoneRules} can be obtained.
+ * <p>
+ * Parsing matches the zone ID step by step as follows.
+ * <ul>
+ * <li>If the zone ID equals 'Z', the result is {@code ZoneOffset.UTC}.
+ * <li>If the zone ID consists of a single letter, the zone ID is invalid
+ * and {@code DateTimeException} is thrown.
+ * <li>If the zone ID starts with '+' or '-', the ID is parsed as a
+ * {@code ZoneOffset} using {@link ZoneOffset#of(String)}.
+ * <li>If the zone ID equals 'GMT', 'UTC' or 'UT' then the result is a {@code ZoneId}
+ * with the same ID and rules equivalent to {@code ZoneOffset.UTC}.
+ * <li>If the zone ID starts with 'UTC+', 'UTC-', 'GMT+', 'GMT-', 'UT+' or 'UT-'
+ * then the ID is a prefixed offset-based ID. The ID is split in two, with
+ * a two or three letter prefix and a suffix starting with the sign.
+ * The suffix is parsed as a {@link ZoneOffset#of(String) ZoneOffset}.
+ * The result will be a {@code ZoneId} with the specified UTC/GMT/UT prefix
+ * and the normalized offset ID as per {@link ZoneOffset#getId()}.
+ * The rules of the returned {@code ZoneId} will be equivalent to the
+ * parsed {@code ZoneOffset}.
+ * <li>All other IDs are parsed as region-based zone IDs. Region IDs must
+ * match the regular expression <code>[A-Za-z][A-Za-z0-9~/._+-]+</code>
+ * otherwise a {@code DateTimeException} is thrown. If the zone ID is not
+ * in the configured set of IDs, {@code ZoneRulesException} is thrown.
+ * The detailed format of the region ID depends on the group supplying the data.
+ * The default set of data is supplied by the IANA Time Zone Database (TZDB).
+ * This has region IDs of the form '{area}/{city}', such as 'Europe/Paris' or 'America/New_York'.
+ * This is compatible with most IDs from {@link java.util.TimeZone}.
+ * </ul>
+ *
+ * @param zoneId the time-zone ID, not null
+ * @return the zone ID, not null
+ * @throws DateTimeException if the zone ID has an invalid format
+ * @throws ZoneRulesException if the zone ID is a region ID that cannot be found
+ */
+ public static ZoneId of(String zoneId) {
+ return of(zoneId, true);
+ }
+
+ /**
+ * Obtains an instance of {@code ZoneId} wrapping an offset.
+ * <p>
+ * If the prefix is "GMT", "UTC", or "UT" a {@code ZoneId}
+ * with the prefix and the non-zero offset is returned.
+ * If the prefix is empty {@code ""} the {@code ZoneOffset} is returned.
+ *
+ * @param prefix the time-zone ID, not null
+ * @param offset the offset, not null
+ * @return the zone ID, not null
+ * @throws IllegalArgumentException if the prefix is not one of
+ * "GMT", "UTC", or "UT", or ""
+ */
+ public static ZoneId ofOffset(String prefix, ZoneOffset offset) {
+ Objects.requireNonNull(prefix, "prefix");
+ Objects.requireNonNull(offset, "offset");
+ if (prefix.length() == 0) {
+ return offset;
+ }
+
+ if (!prefix.equals("GMT") && !prefix.equals("UTC") && !prefix.equals("UT")) {
+ throw new IllegalArgumentException("prefix should be GMT, UTC or UT, is: " + prefix);
+ }
+
+ if (offset.getTotalSeconds() != 0) {
+ prefix = prefix.concat(offset.getId());
+ }
+ return new ZoneRegion(prefix, offset.getRules());
+ }
+
+ /**
+ * Parses the ID, taking a flag to indicate whether {@code ZoneRulesException}
+ * should be thrown or not, used in deserialization.
+ *
+ * @param zoneId the time-zone ID, not null
+ * @param checkAvailable whether to check if the zone ID is available
+ * @return the zone ID, not null
+ * @throws DateTimeException if the ID format is invalid
+ * @throws ZoneRulesException if checking availability and the ID cannot be found
+ */
+ static ZoneId of(String zoneId, boolean checkAvailable) {
+ Objects.requireNonNull(zoneId, "zoneId");
+ if (zoneId.length() <= 1 || zoneId.startsWith("+") || zoneId.startsWith("-")) {
+ return ZoneOffset.of(zoneId);
+ } else if (zoneId.startsWith("UTC") || zoneId.startsWith("GMT")) {
+ return ofWithPrefix(zoneId, 3, checkAvailable);
+ } else if (zoneId.startsWith("UT")) {
+ return ofWithPrefix(zoneId, 2, checkAvailable);
+ }
+ return ZoneRegion.ofId(zoneId, checkAvailable);
+ }
+
+ /**
+ * Parse once a prefix is established.
+ *
+ * @param zoneId the time-zone ID, not null
+ * @param prefixLength the length of the prefix, 2 or 3
+ * @return the zone ID, not null
+ * @throws DateTimeException if the zone ID has an invalid format
+ */
+ private static ZoneId ofWithPrefix(String zoneId, int prefixLength, boolean checkAvailable) {
+ String prefix = zoneId.substring(0, prefixLength);
+ if (zoneId.length() == prefixLength) {
+ return ofOffset(prefix, ZoneOffset.UTC);
+ }
+ if (zoneId.charAt(prefixLength) != '+' && zoneId.charAt(prefixLength) != '-') {
+ return ZoneRegion.ofId(zoneId, checkAvailable); // drop through to ZoneRulesProvider
+ }
+ try {
+ ZoneOffset offset = ZoneOffset.of(zoneId.substring(prefixLength));
+ if (offset == ZoneOffset.UTC) {
+ return ofOffset(prefix, offset);
+ }
+ return ofOffset(prefix, offset);
+ } catch (DateTimeException ex) {
+ throw new DateTimeException("Invalid ID for offset-based ZoneId: " + zoneId, ex);
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code ZoneId} from a temporal object.
+ * <p>
+ * This obtains a zone based on the specified temporal.
+ * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
+ * which this factory converts to an instance of {@code ZoneId}.
+ * <p>
+ * A {@code TemporalAccessor} represents some form of date and time information.
+ * This factory converts the arbitrary temporal object to an instance of {@code ZoneId}.
+ * <p>
+ * The conversion will try to obtain the zone in a way that favours region-based
+ * zones over offset-based zones using {@link TemporalQueries#zone()}.
+ * <p>
+ * This method matches the signature of the functional interface {@link TemporalQuery}
+ * allowing it to be used as a query via method reference, {@code ZoneId::from}.
+ *
+ * @param temporal the temporal object to convert, not null
+ * @return the zone ID, not null
+ * @throws DateTimeException if unable to convert to a {@code ZoneId}
+ */
+ public static ZoneId from(TemporalAccessor temporal) {
+ ZoneId obj = temporal.query(TemporalQueries.zone());
+ if (obj == null) {
+ throw new DateTimeException("Unable to obtain ZoneId from TemporalAccessor: " +
+ temporal + " of type " + temporal.getClass().getName());
+ }
+ return obj;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Constructor only accessible within the package.
+ */
+ ZoneId() {
+ if (getClass() != ZoneOffset.class && getClass() != ZoneRegion.class) {
+ throw new AssertionError("Invalid subclass");
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the unique time-zone ID.
+ * <p>
+ * This ID uniquely defines this object.
+ * The format of an offset based ID is defined by {@link ZoneOffset#getId()}.
+ *
+ * @return the time-zone unique ID, not null
+ */
+ public abstract String getId();
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the textual representation of the zone, such as 'British Time' or
+ * '+02:00'.
+ * <p>
+ * This returns the textual name used to identify the time-zone ID,
+ * suitable for presentation to the user.
+ * The parameters control the style of the returned text and the locale.
+ * <p>
+ * If no textual mapping is found then the {@link #getId() full ID} is returned.
+ *
+ * @param style the length of the text required, not null
+ * @param locale the locale to use, not null
+ * @return the text value of the zone, not null
+ */
+ public String getDisplayName(TextStyle style, Locale locale) {
+ return new DateTimeFormatterBuilder().appendZoneText(style).toFormatter(locale).format(toTemporal());
+ }
+
+ /**
+ * Converts this zone to a {@code TemporalAccessor}.
+ * <p>
+ * A {@code ZoneId} can be fully represented as a {@code TemporalAccessor}.
+ * However, the interface is not implemented by this class as most of the
+ * methods on the interface have no meaning to {@code ZoneId}.
+ * <p>
+ * The returned temporal has no supported fields, with the query method
+ * supporting the return of the zone using {@link TemporalQueries#zoneId()}.
+ *
+ * @return a temporal equivalent to this zone, not null
+ */
+ private TemporalAccessor toTemporal() {
+ return new TemporalAccessor() {
+ @Override
+ public boolean isSupported(TemporalField field) {
+ return false;
+ }
+ @Override
+ public long getLong(TemporalField field) {
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
+ }
+ @SuppressWarnings("unchecked")
+ @Override
+ public <R> R query(TemporalQuery<R> query) {
+ if (query == TemporalQueries.zoneId()) {
+ return (R) ZoneId.this;
+ }
+ return TemporalAccessor.super.query(query);
+ }
+ };
+ }
+
+ //-----------------------------------------------------------------------
+ // Android-removed: ZoneRulesProvider related paragraph
+ /**
+ * Gets the time-zone rules for this ID allowing calculations to be performed.
+ * <p>
+ * The rules provide the functionality associated with a time-zone,
+ * such as finding the offset for a given instant or local date-time.
+ * <p>
+ * A time-zone can be invalid if it is deserialized in a Java Runtime which
+ * does not have the same rules loaded as the Java Runtime that stored it.
+ * In this case, calling this method will throw a {@code ZoneRulesException}.
+ * <p>
+ * {@link ZoneOffset} will always return a set of rules where the offset never changes.
+ *
+ * @return the rules, not null
+ * @throws ZoneRulesException if no rules are available for this ID
+ */
+ public abstract ZoneRules getRules();
+
+ /**
+ * Normalizes the time-zone ID, returning a {@code ZoneOffset} where possible.
+ * <p>
+ * The returns a normalized {@code ZoneId} that can be used in place of this ID.
+ * The result will have {@code ZoneRules} equivalent to those returned by this object,
+ * however the ID returned by {@code getId()} may be different.
+ * <p>
+ * The normalization checks if the rules of this {@code ZoneId} have a fixed offset.
+ * If they do, then the {@code ZoneOffset} equal to that offset is returned.
+ * Otherwise {@code this} is returned.
+ *
+ * @return the time-zone unique ID, not null
+ */
+ public ZoneId normalized() {
+ try {
+ ZoneRules rules = getRules();
+ if (rules.isFixedOffset()) {
+ return rules.getOffset(Instant.EPOCH);
+ }
+ } catch (ZoneRulesException ex) {
+ // invalid ZoneRegion is not important to this method
+ }
+ return this;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if this time-zone ID is equal to another time-zone ID.
+ * <p>
+ * The comparison is based on the ID.
+ *
+ * @param obj the object to check, null returns false
+ * @return true if this is equal to the other time-zone ID
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof ZoneId) {
+ ZoneId other = (ZoneId) obj;
+ return getId().equals(other.getId());
+ }
+ return false;
+ }
+
+ /**
+ * A hash code for this time-zone ID.
+ *
+ * @return a suitable hash code
+ */
+ @Override
+ public int hashCode() {
+ return getId().hashCode();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Defend against malicious streams.
+ *
+ * @param s the stream to read
+ * @throws InvalidObjectException always
+ */
+ private void readObject(ObjectInputStream s) throws InvalidObjectException {
+ throw new InvalidObjectException("Deserialization via serialization delegate");
+ }
+
+ /**
+ * Outputs this zone as a {@code String}, using the ID.
+ *
+ * @return a string representation of this time-zone ID, not null
+ */
+ @Override
+ public String toString() {
+ return getId();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Writes the object using a
+ * <a href="../../serialized-form.html#java.time.Ser">dedicated serialized form</a>.
+ * @serialData
+ * <pre>
+ * out.writeByte(7); // identifies a ZoneId (not ZoneOffset)
+ * out.writeUTF(getId());
+ * </pre>
+ * <p>
+ * When read back in, the {@code ZoneId} will be created as though using
+ * {@link #of(String)}, but without any exception in the case where the
+ * ID has a valid format, but is not in the known set of region-based IDs.
+ *
+ * @return the instance of {@code Ser}, not null
+ */
+ // this is here for serialization Javadoc
+ private Object writeReplace() {
+ return new Ser(Ser.ZONE_REGION_TYPE, this);
+ }
+
+ abstract void write(DataOutput out) throws IOException;
+
+}
diff --git a/java/time/ZoneOffset.java b/java/time/ZoneOffset.java
new file mode 100644
index 0000000..4dfb94d
--- /dev/null
+++ b/java/time/ZoneOffset.java
@@ -0,0 +1,793 @@
+/*
+ * Copyright (c) 2012, 2015, 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) 2007-2012, 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;
+
+import static java.time.LocalTime.MINUTES_PER_HOUR;
+import static java.time.LocalTime.SECONDS_PER_HOUR;
+import static java.time.LocalTime.SECONDS_PER_MINUTE;
+import static java.time.temporal.ChronoField.OFFSET_SECONDS;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+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;
+import java.time.temporal.TemporalQueries;
+import java.time.temporal.TemporalQuery;
+import java.time.temporal.UnsupportedTemporalTypeException;
+import java.time.temporal.ValueRange;
+import java.time.zone.ZoneRules;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+// Android-changed: removed ValueBased paragraph.
+/**
+ * A time-zone offset from Greenwich/UTC, such as {@code +02:00}.
+ * <p>
+ * A time-zone offset is the amount of time that a time-zone differs from Greenwich/UTC.
+ * This is usually a fixed number of hours and minutes.
+ * <p>
+ * Different parts of the world have different time-zone offsets.
+ * The rules for how offsets vary by place and time of year are captured in the
+ * {@link ZoneId} class.
+ * <p>
+ * For example, Paris is one hour ahead of Greenwich/UTC in winter and two hours
+ * ahead in summer. The {@code ZoneId} instance for Paris will reference two
+ * {@code ZoneOffset} instances - a {@code +01:00} instance for winter,
+ * and a {@code +02:00} instance for summer.
+ * <p>
+ * In 2008, time-zone offsets around the world extended from -12:00 to +14:00.
+ * To prevent any problems with that range being extended, yet still provide
+ * validation, the range of offsets is restricted to -18:00 to 18:00 inclusive.
+ * <p>
+ * This class is designed for use with the ISO calendar system.
+ * The fields of hours, minutes and seconds make assumptions that are valid for the
+ * standard ISO definitions of those fields. This class may be used with other
+ * calendar systems providing the definition of the time fields matches those
+ * of the ISO calendar system.
+ * <p>
+ * Instances of {@code ZoneOffset} must be compared using {@link #equals}.
+ * Implementations may choose to cache certain common offsets, however
+ * applications must not rely on such caching.
+ *
+ * @implSpec
+ * This class is immutable and thread-safe.
+ *
+ * @since 1.8
+ */
+public final class ZoneOffset
+ extends ZoneId
+ implements TemporalAccessor, TemporalAdjuster, Comparable<ZoneOffset>, Serializable {
+
+ /** Cache of time-zone offset by offset in seconds. */
+ private static final ConcurrentMap<Integer, ZoneOffset> SECONDS_CACHE = new ConcurrentHashMap<>(16, 0.75f, 4);
+ /** Cache of time-zone offset by ID. */
+ private static final ConcurrentMap<String, ZoneOffset> ID_CACHE = new ConcurrentHashMap<>(16, 0.75f, 4);
+
+ /**
+ * The abs maximum seconds.
+ */
+ private static final int MAX_SECONDS = 18 * SECONDS_PER_HOUR;
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = 2357656521762053153L;
+
+ /**
+ * The time-zone offset for UTC, with an ID of 'Z'.
+ */
+ public static final ZoneOffset UTC = ZoneOffset.ofTotalSeconds(0);
+ /**
+ * Constant for the maximum supported offset.
+ */
+ public static final ZoneOffset MIN = ZoneOffset.ofTotalSeconds(-MAX_SECONDS);
+ /**
+ * Constant for the maximum supported offset.
+ */
+ public static final ZoneOffset MAX = ZoneOffset.ofTotalSeconds(MAX_SECONDS);
+
+ /**
+ * The total offset in seconds.
+ */
+ private final int totalSeconds;
+ /**
+ * The string form of the time-zone offset.
+ */
+ private final transient String id;
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code ZoneOffset} using the ID.
+ * <p>
+ * This method parses the string ID of a {@code ZoneOffset} to
+ * return an instance. The parsing accepts all the formats generated by
+ * {@link #getId()}, plus some additional formats:
+ * <ul>
+ * <li>{@code Z} - for UTC
+ * <li>{@code +h}
+ * <li>{@code +hh}
+ * <li>{@code +hh:mm}
+ * <li>{@code -hh:mm}
+ * <li>{@code +hhmm}
+ * <li>{@code -hhmm}
+ * <li>{@code +hh:mm:ss}
+ * <li>{@code -hh:mm:ss}
+ * <li>{@code +hhmmss}
+ * <li>{@code -hhmmss}
+ * </ul>
+ * Note that ± means either the plus or minus symbol.
+ * <p>
+ * The ID of the returned offset will be normalized to one of the formats
+ * described by {@link #getId()}.
+ * <p>
+ * The maximum supported range is from +18:00 to -18:00 inclusive.
+ *
+ * @param offsetId the offset ID, not null
+ * @return the zone-offset, not null
+ * @throws DateTimeException if the offset ID is invalid
+ */
+ @SuppressWarnings("fallthrough")
+ public static ZoneOffset of(String offsetId) {
+ Objects.requireNonNull(offsetId, "offsetId");
+ // "Z" is always in the cache
+ ZoneOffset offset = ID_CACHE.get(offsetId);
+ if (offset != null) {
+ return offset;
+ }
+
+ // parse - +h, +hh, +hhmm, +hh:mm, +hhmmss, +hh:mm:ss
+ final int hours, minutes, seconds;
+ switch (offsetId.length()) {
+ case 2:
+ offsetId = offsetId.charAt(0) + "0" + offsetId.charAt(1); // fallthru
+ case 3:
+ hours = parseNumber(offsetId, 1, false);
+ minutes = 0;
+ seconds = 0;
+ break;
+ case 5:
+ hours = parseNumber(offsetId, 1, false);
+ minutes = parseNumber(offsetId, 3, false);
+ seconds = 0;
+ break;
+ case 6:
+ hours = parseNumber(offsetId, 1, false);
+ minutes = parseNumber(offsetId, 4, true);
+ seconds = 0;
+ break;
+ case 7:
+ hours = parseNumber(offsetId, 1, false);
+ minutes = parseNumber(offsetId, 3, false);
+ seconds = parseNumber(offsetId, 5, false);
+ break;
+ case 9:
+ hours = parseNumber(offsetId, 1, false);
+ minutes = parseNumber(offsetId, 4, true);
+ seconds = parseNumber(offsetId, 7, true);
+ break;
+ default:
+ throw new DateTimeException("Invalid ID for ZoneOffset, invalid format: " + offsetId);
+ }
+ char first = offsetId.charAt(0);
+ if (first != '+' && first != '-') {
+ throw new DateTimeException("Invalid ID for ZoneOffset, plus/minus not found when expected: " + offsetId);
+ }
+ if (first == '-') {
+ return ofHoursMinutesSeconds(-hours, -minutes, -seconds);
+ } else {
+ return ofHoursMinutesSeconds(hours, minutes, seconds);
+ }
+ }
+
+ /**
+ * Parse a two digit zero-prefixed number.
+ *
+ * @param offsetId the offset ID, not null
+ * @param pos the position to parse, valid
+ * @param precededByColon should this number be prefixed by a precededByColon
+ * @return the parsed number, from 0 to 99
+ */
+ private static int parseNumber(CharSequence offsetId, int pos, boolean precededByColon) {
+ if (precededByColon && offsetId.charAt(pos - 1) != ':') {
+ throw new DateTimeException("Invalid ID for ZoneOffset, colon not found when expected: " + offsetId);
+ }
+ char ch1 = offsetId.charAt(pos);
+ char ch2 = offsetId.charAt(pos + 1);
+ if (ch1 < '0' || ch1 > '9' || ch2 < '0' || ch2 > '9') {
+ throw new DateTimeException("Invalid ID for ZoneOffset, non numeric characters found: " + offsetId);
+ }
+ return (ch1 - 48) * 10 + (ch2 - 48);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code ZoneOffset} using an offset in hours.
+ *
+ * @param hours the time-zone offset in hours, from -18 to +18
+ * @return the zone-offset, not null
+ * @throws DateTimeException if the offset is not in the required range
+ */
+ public static ZoneOffset ofHours(int hours) {
+ return ofHoursMinutesSeconds(hours, 0, 0);
+ }
+
+ /**
+ * Obtains an instance of {@code ZoneOffset} using an offset in
+ * hours and minutes.
+ * <p>
+ * The sign of the hours and minutes components must match.
+ * Thus, if the hours is negative, the minutes must be negative or zero.
+ * If the hours is zero, the minutes may be positive, negative or zero.
+ *
+ * @param hours the time-zone offset in hours, from -18 to +18
+ * @param minutes the time-zone offset in minutes, from 0 to ±59, sign matches hours
+ * @return the zone-offset, not null
+ * @throws DateTimeException if the offset is not in the required range
+ */
+ public static ZoneOffset ofHoursMinutes(int hours, int minutes) {
+ return ofHoursMinutesSeconds(hours, minutes, 0);
+ }
+
+ /**
+ * Obtains an instance of {@code ZoneOffset} using an offset in
+ * hours, minutes and seconds.
+ * <p>
+ * The sign of the hours, minutes and seconds components must match.
+ * Thus, if the hours is negative, the minutes and seconds must be negative or zero.
+ *
+ * @param hours the time-zone offset in hours, from -18 to +18
+ * @param minutes the time-zone offset in minutes, from 0 to ±59, sign matches hours and seconds
+ * @param seconds the time-zone offset in seconds, from 0 to ±59, sign matches hours and minutes
+ * @return the zone-offset, not null
+ * @throws DateTimeException if the offset is not in the required range
+ */
+ public static ZoneOffset ofHoursMinutesSeconds(int hours, int minutes, int seconds) {
+ validate(hours, minutes, seconds);
+ int totalSeconds = totalSeconds(hours, minutes, seconds);
+ return ofTotalSeconds(totalSeconds);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code ZoneOffset} from a temporal object.
+ * <p>
+ * This obtains an offset based on the specified temporal.
+ * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
+ * which this factory converts to an instance of {@code ZoneOffset}.
+ * <p>
+ * A {@code TemporalAccessor} represents some form of date and time information.
+ * This factory converts the arbitrary temporal object to an instance of {@code ZoneOffset}.
+ * <p>
+ * The conversion uses the {@link TemporalQueries#offset()} query, which relies
+ * on extracting the {@link ChronoField#OFFSET_SECONDS OFFSET_SECONDS} field.
+ * <p>
+ * This method matches the signature of the functional interface {@link TemporalQuery}
+ * allowing it to be used as a query via method reference, {@code ZoneOffset::from}.
+ *
+ * @param temporal the temporal object to convert, not null
+ * @return the zone-offset, not null
+ * @throws DateTimeException if unable to convert to an {@code ZoneOffset}
+ */
+ public static ZoneOffset from(TemporalAccessor temporal) {
+ Objects.requireNonNull(temporal, "temporal");
+ ZoneOffset offset = temporal.query(TemporalQueries.offset());
+ if (offset == null) {
+ throw new DateTimeException("Unable to obtain ZoneOffset from TemporalAccessor: " +
+ temporal + " of type " + temporal.getClass().getName());
+ }
+ return offset;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Validates the offset fields.
+ *
+ * @param hours the time-zone offset in hours, from -18 to +18
+ * @param minutes the time-zone offset in minutes, from 0 to ±59
+ * @param seconds the time-zone offset in seconds, from 0 to ±59
+ * @throws DateTimeException if the offset is not in the required range
+ */
+ private static void validate(int hours, int minutes, int seconds) {
+ if (hours < -18 || hours > 18) {
+ throw new DateTimeException("Zone offset hours not in valid range: value " + hours +
+ " is not in the range -18 to 18");
+ }
+ if (hours > 0) {
+ if (minutes < 0 || seconds < 0) {
+ throw new DateTimeException("Zone offset minutes and seconds must be positive because hours is positive");
+ }
+ } else if (hours < 0) {
+ if (minutes > 0 || seconds > 0) {
+ throw new DateTimeException("Zone offset minutes and seconds must be negative because hours is negative");
+ }
+ } else if ((minutes > 0 && seconds < 0) || (minutes < 0 && seconds > 0)) {
+ throw new DateTimeException("Zone offset minutes and seconds must have the same sign");
+ }
+ if (Math.abs(minutes) > 59) {
+ throw new DateTimeException("Zone offset minutes not in valid range: abs(value) " +
+ Math.abs(minutes) + " is not in the range 0 to 59");
+ }
+ if (Math.abs(seconds) > 59) {
+ throw new DateTimeException("Zone offset seconds not in valid range: abs(value) " +
+ Math.abs(seconds) + " is not in the range 0 to 59");
+ }
+ if (Math.abs(hours) == 18 && (Math.abs(minutes) > 0 || Math.abs(seconds) > 0)) {
+ throw new DateTimeException("Zone offset not in valid range: -18:00 to +18:00");
+ }
+ }
+
+ /**
+ * Calculates the total offset in seconds.
+ *
+ * @param hours the time-zone offset in hours, from -18 to +18
+ * @param minutes the time-zone offset in minutes, from 0 to ±59, sign matches hours and seconds
+ * @param seconds the time-zone offset in seconds, from 0 to ±59, sign matches hours and minutes
+ * @return the total in seconds
+ */
+ private static int totalSeconds(int hours, int minutes, int seconds) {
+ return hours * SECONDS_PER_HOUR + minutes * SECONDS_PER_MINUTE + seconds;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code ZoneOffset} specifying the total offset in seconds
+ * <p>
+ * The offset must be in the range {@code -18:00} to {@code +18:00}, which corresponds to -64800 to +64800.
+ *
+ * @param totalSeconds the total time-zone offset in seconds, from -64800 to +64800
+ * @return the ZoneOffset, not null
+ * @throws DateTimeException if the offset is not in the required range
+ */
+ public static ZoneOffset ofTotalSeconds(int totalSeconds) {
+ if (Math.abs(totalSeconds) > MAX_SECONDS) {
+ throw new DateTimeException("Zone offset not in valid range: -18:00 to +18:00");
+ }
+ if (totalSeconds % (15 * SECONDS_PER_MINUTE) == 0) {
+ Integer totalSecs = totalSeconds;
+ ZoneOffset result = SECONDS_CACHE.get(totalSecs);
+ if (result == null) {
+ result = new ZoneOffset(totalSeconds);
+ SECONDS_CACHE.putIfAbsent(totalSecs, result);
+ result = SECONDS_CACHE.get(totalSecs);
+ ID_CACHE.putIfAbsent(result.getId(), result);
+ }
+ return result;
+ } else {
+ return new ZoneOffset(totalSeconds);
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Constructor.
+ *
+ * @param totalSeconds the total time-zone offset in seconds, from -64800 to +64800
+ */
+ private ZoneOffset(int totalSeconds) {
+ super();
+ this.totalSeconds = totalSeconds;
+ id = buildId(totalSeconds);
+ }
+
+ private static String buildId(int totalSeconds) {
+ if (totalSeconds == 0) {
+ return "Z";
+ } else {
+ int absTotalSeconds = Math.abs(totalSeconds);
+ StringBuilder buf = new StringBuilder();
+ int absHours = absTotalSeconds / SECONDS_PER_HOUR;
+ int absMinutes = (absTotalSeconds / SECONDS_PER_MINUTE) % MINUTES_PER_HOUR;
+ buf.append(totalSeconds < 0 ? "-" : "+")
+ .append(absHours < 10 ? "0" : "").append(absHours)
+ .append(absMinutes < 10 ? ":0" : ":").append(absMinutes);
+ int absSeconds = absTotalSeconds % SECONDS_PER_MINUTE;
+ if (absSeconds != 0) {
+ buf.append(absSeconds < 10 ? ":0" : ":").append(absSeconds);
+ }
+ return buf.toString();
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the total zone offset in seconds.
+ * <p>
+ * This is the primary way to access the offset amount.
+ * It returns the total of the hours, minutes and seconds fields as a
+ * single offset that can be added to a time.
+ *
+ * @return the total zone offset amount in seconds
+ */
+ public int getTotalSeconds() {
+ return totalSeconds;
+ }
+
+ /**
+ * Gets the normalized zone offset ID.
+ * <p>
+ * The ID is minor variation to the standard ISO-8601 formatted string
+ * for the offset. There are three formats:
+ * <ul>
+ * <li>{@code Z} - for UTC (ISO-8601)
+ * <li>{@code +hh:mm} or {@code -hh:mm} - if the seconds are zero (ISO-8601)
+ * <li>{@code +hh:mm:ss} or {@code -hh:mm:ss} - if the seconds are non-zero (not ISO-8601)
+ * </ul>
+ *
+ * @return the zone offset ID, not null
+ */
+ @Override
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Gets the associated time-zone rules.
+ * <p>
+ * The rules will always return this offset when queried.
+ * The implementation class is immutable, thread-safe and serializable.
+ *
+ * @return the rules, not null
+ */
+ @Override
+ public ZoneRules getRules() {
+ return ZoneRules.of(this);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if the specified field is supported.
+ * <p>
+ * This checks if this offset can be queried for the specified field.
+ * If false, then calling the {@link #range(TemporalField) range} and
+ * {@link #get(TemporalField) get} methods will throw an exception.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The {@code OFFSET_SECONDS} field returns true.
+ * All other {@code ChronoField} instances will return false.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}
+ * passing {@code this} as the argument.
+ * Whether the field is supported is determined by the field.
+ *
+ * @param field the field to check, null returns false
+ * @return true if the field is supported on this offset, false if not
+ */
+ @Override
+ public boolean isSupported(TemporalField field) {
+ if (field instanceof ChronoField) {
+ return field == OFFSET_SECONDS;
+ }
+ return field != null && field.isSupportedBy(this);
+ }
+
+ /**
+ * Gets the range of valid values for the specified field.
+ * <p>
+ * The range object expresses the minimum and maximum valid values for a field.
+ * This offset is used to enhance the accuracy of the returned range.
+ * If it is not possible to return the range, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The {@link #isSupported(TemporalField) supported fields} will return
+ * appropriate range instances.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.rangeRefinedBy(TemporalAccessor)}
+ * passing {@code this} as the argument.
+ * Whether the range can be obtained is determined by the field.
+ *
+ * @param field the field to query the range for, not null
+ * @return the range of valid values for the field, not null
+ * @throws DateTimeException if the range for the field cannot be obtained
+ * @throws UnsupportedTemporalTypeException if the field is not supported
+ */
+ @Override // override for Javadoc
+ public ValueRange range(TemporalField field) {
+ return TemporalAccessor.super.range(field);
+ }
+
+ /**
+ * Gets the value of the specified field from this offset as an {@code int}.
+ * <p>
+ * This queries this offset for the value of the specified field.
+ * The returned value will always be within the valid range of values for the field.
+ * If it is not possible to return the value, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The {@code OFFSET_SECONDS} field returns the value of the offset.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
+ * passing {@code this} as the argument. Whether the value can be obtained,
+ * and what the value represents, is determined by the field.
+ *
+ * @param field the field to get, not null
+ * @return the value for the field
+ * @throws DateTimeException if a value for the field cannot be obtained or
+ * the value is outside the range of valid values for the field
+ * @throws UnsupportedTemporalTypeException if the field is not supported or
+ * the range of values exceeds an {@code int}
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override // override for Javadoc and performance
+ public int get(TemporalField field) {
+ if (field == OFFSET_SECONDS) {
+ return totalSeconds;
+ } else if (field instanceof ChronoField) {
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
+ }
+ return range(field).checkValidIntValue(getLong(field), field);
+ }
+
+ /**
+ * Gets the value of the specified field from this offset as a {@code long}.
+ * <p>
+ * This queries this offset for the value of the specified field.
+ * If it is not possible to return the value, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The {@code OFFSET_SECONDS} field returns the value of the offset.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
+ * passing {@code this} as the argument. Whether the value can be obtained,
+ * and what the value represents, is determined by the field.
+ *
+ * @param field the field to get, not null
+ * @return the value for the field
+ * @throws DateTimeException if a value for the field cannot be obtained
+ * @throws UnsupportedTemporalTypeException if the field is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public long getLong(TemporalField field) {
+ if (field == OFFSET_SECONDS) {
+ return totalSeconds;
+ } else if (field instanceof ChronoField) {
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
+ }
+ return field.getFrom(this);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Queries this offset using the specified query.
+ * <p>
+ * This queries this offset using the specified query strategy object.
+ * The {@code TemporalQuery} object defines the logic to be used to
+ * obtain the result. Read the documentation of the query to understand
+ * what the result of this method will be.
+ * <p>
+ * The result of this method is obtained by invoking the
+ * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the
+ * specified query passing {@code this} as the argument.
+ *
+ * @param <R> the type of the result
+ * @param query the query to invoke, not null
+ * @return the query result, null may be returned (defined by the query)
+ * @throws DateTimeException if unable to query (defined by the query)
+ * @throws ArithmeticException if numeric overflow occurs (defined by the query)
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public <R> R query(TemporalQuery<R> query) {
+ if (query == TemporalQueries.offset() || query == TemporalQueries.zone()) {
+ return (R) this;
+ }
+ return TemporalAccessor.super.query(query);
+ }
+
+ /**
+ * Adjusts the specified temporal object to have the same offset as this object.
+ * <p>
+ * This returns a temporal object of the same observable type as the input
+ * with the offset changed to be the same as this.
+ * <p>
+ * The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)}
+ * passing {@link ChronoField#OFFSET_SECONDS} as the field.
+ * <p>
+ * In most cases, it is clearer to reverse the calling pattern by using
+ * {@link Temporal#with(TemporalAdjuster)}:
+ * <pre>
+ * // these two lines are equivalent, but the second approach is recommended
+ * temporal = thisOffset.adjustInto(temporal);
+ * temporal = temporal.with(thisOffset);
+ * </pre>
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param temporal the target object to be adjusted, not null
+ * @return the adjusted object, not null
+ * @throws DateTimeException if unable to make the adjustment
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public Temporal adjustInto(Temporal temporal) {
+ return temporal.with(OFFSET_SECONDS, totalSeconds);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Compares this offset to another offset in descending order.
+ * <p>
+ * The offsets are compared in the order that they occur for the same time
+ * of day around the world. Thus, an offset of {@code +10:00} comes before an
+ * offset of {@code +09:00} and so on down to {@code -18:00}.
+ * <p>
+ * The comparison is "consistent with equals", as defined by {@link Comparable}.
+ *
+ * @param other the other date to compare to, not null
+ * @return the comparator value, negative if less, postive if greater
+ * @throws NullPointerException if {@code other} is null
+ */
+ @Override
+ public int compareTo(ZoneOffset other) {
+ return other.totalSeconds - totalSeconds;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if this offset is equal to another offset.
+ * <p>
+ * The comparison is based on the amount of the offset in seconds.
+ * This is equivalent to a comparison by ID.
+ *
+ * @param obj the object to check, null returns false
+ * @return true if this is equal to the other offset
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof ZoneOffset) {
+ return totalSeconds == ((ZoneOffset) obj).totalSeconds;
+ }
+ return false;
+ }
+
+ /**
+ * A hash code for this offset.
+ *
+ * @return a suitable hash code
+ */
+ @Override
+ public int hashCode() {
+ return totalSeconds;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Outputs this offset as a {@code String}, using the normalized ID.
+ *
+ * @return a string representation of this offset, not null
+ */
+ @Override
+ public String toString() {
+ return id;
+ }
+
+ // -----------------------------------------------------------------------
+ /**
+ * Writes the object using a
+ * <a href="../../serialized-form.html#java.time.Ser">dedicated serialized form</a>.
+ * @serialData
+ * <pre>
+ * out.writeByte(8); // identifies a ZoneOffset
+ * int offsetByte = totalSeconds % 900 == 0 ? totalSeconds / 900 : 127;
+ * out.writeByte(offsetByte);
+ * if (offsetByte == 127) {
+ * out.writeInt(totalSeconds);
+ * }
+ * </pre>
+ *
+ * @return the instance of {@code Ser}, not null
+ */
+ private Object writeReplace() {
+ return new Ser(Ser.ZONE_OFFSET_TYPE, this);
+ }
+
+ /**
+ * Defend against malicious streams.
+ *
+ * @param s the stream to read
+ * @throws InvalidObjectException always
+ */
+ private void readObject(ObjectInputStream s) throws InvalidObjectException {
+ throw new InvalidObjectException("Deserialization via serialization delegate");
+ }
+
+ @Override
+ void write(DataOutput out) throws IOException {
+ out.writeByte(Ser.ZONE_OFFSET_TYPE);
+ writeExternal(out);
+ }
+
+ void writeExternal(DataOutput out) throws IOException {
+ final int offsetSecs = totalSeconds;
+ int offsetByte = offsetSecs % 900 == 0 ? offsetSecs / 900 : 127; // compress to -72 to +72
+ out.writeByte(offsetByte);
+ if (offsetByte == 127) {
+ out.writeInt(offsetSecs);
+ }
+ }
+
+ static ZoneOffset readExternal(DataInput in) throws IOException {
+ int offsetByte = in.readByte();
+ return (offsetByte == 127 ? ZoneOffset.ofTotalSeconds(in.readInt()) : ZoneOffset.ofTotalSeconds(offsetByte * 900));
+ }
+
+}
diff --git a/java/time/ZoneRegion.java b/java/time/ZoneRegion.java
new file mode 100644
index 0000000..83f0b43
--- /dev/null
+++ b/java/time/ZoneRegion.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2012, 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) 2007-2012, 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;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.time.zone.ZoneRules;
+import java.time.zone.ZoneRulesException;
+import java.time.zone.ZoneRulesProvider;
+import java.util.Objects;
+
+/**
+ * A geographical region where the same time-zone rules apply.
+ * <p>
+ * Time-zone information is categorized as a set of rules defining when and
+ * how the offset from UTC/Greenwich changes. These rules are accessed using
+ * identifiers based on geographical regions, such as countries or states.
+ * The most common region classification is the Time Zone Database (TZDB),
+ * which defines regions such as 'Europe/Paris' and 'Asia/Tokyo'.
+ * <p>
+ * The region identifier, modeled by this class, is distinct from the
+ * underlying rules, modeled by {@link ZoneRules}.
+ * The rules are defined by governments and change frequently.
+ * By contrast, the region identifier is well-defined and long-lived.
+ * This separation also allows rules to be shared between regions if appropriate.
+ *
+ * @implSpec
+ * This class is immutable and thread-safe.
+ *
+ * @since 1.8
+ */
+final class ZoneRegion extends ZoneId implements Serializable {
+
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = 8386373296231747096L;
+ /**
+ * The time-zone ID, not null.
+ */
+ private final String id;
+ /**
+ * The time-zone rules, null if zone ID was loaded leniently.
+ */
+ private final transient ZoneRules rules;
+
+ /**
+ * Obtains an instance of {@code ZoneId} from an identifier.
+ *
+ * @param zoneId the time-zone ID, not null
+ * @param checkAvailable whether to check if the zone ID is available
+ * @return the zone ID, not null
+ * @throws DateTimeException if the ID format is invalid
+ * @throws ZoneRulesException if checking availability and the ID cannot be found
+ */
+ static ZoneRegion ofId(String zoneId, boolean checkAvailable) {
+ Objects.requireNonNull(zoneId, "zoneId");
+ checkName(zoneId);
+ ZoneRules rules = null;
+ try {
+ // always attempt load for better behavior after deserialization
+ rules = ZoneRulesProvider.getRules(zoneId, true);
+ } catch (ZoneRulesException ex) {
+ if (checkAvailable) {
+ throw ex;
+ }
+ }
+ return new ZoneRegion(zoneId, rules);
+ }
+
+ /**
+ * Checks that the given string is a legal ZondId name.
+ *
+ * @param zoneId the time-zone ID, not null
+ * @throws DateTimeException if the ID format is invalid
+ */
+ private static void checkName(String zoneId) {
+ int n = zoneId.length();
+ if (n < 2) {
+ throw new DateTimeException("Invalid ID for region-based ZoneId, invalid format: " + zoneId);
+ }
+ for (int i = 0; i < n; i++) {
+ char c = zoneId.charAt(i);
+ if (c >= 'a' && c <= 'z') continue;
+ if (c >= 'A' && c <= 'Z') continue;
+ if (c == '/' && i != 0) continue;
+ if (c >= '0' && c <= '9' && i != 0) continue;
+ if (c == '~' && i != 0) continue;
+ if (c == '.' && i != 0) continue;
+ if (c == '_' && i != 0) continue;
+ if (c == '+' && i != 0) continue;
+ if (c == '-' && i != 0) continue;
+ throw new DateTimeException("Invalid ID for region-based ZoneId, invalid format: " + zoneId);
+ }
+ }
+
+ //-------------------------------------------------------------------------
+ /**
+ * Constructor.
+ *
+ * @param id the time-zone ID, not null
+ * @param rules the rules, null for lazy lookup
+ */
+ ZoneRegion(String id, ZoneRules rules) {
+ this.id = id;
+ this.rules = rules;
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ public String getId() {
+ return id;
+ }
+
+ @Override
+ public ZoneRules getRules() {
+ // additional query for group provider when null allows for possibility
+ // that the provider was updated after the ZoneId was created
+ return (rules != null ? rules : ZoneRulesProvider.getRules(id, false));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Writes the object using a
+ * <a href="../../serialized-form.html#java.time.Ser">dedicated serialized form</a>.
+ * @serialData
+ * <pre>
+ * out.writeByte(7); // identifies a ZoneId (not ZoneOffset)
+ * out.writeUTF(zoneId);
+ * </pre>
+ *
+ * @return the instance of {@code Ser}, not null
+ */
+ private Object writeReplace() {
+ return new Ser(Ser.ZONE_REGION_TYPE, this);
+ }
+
+ /**
+ * Defend against malicious streams.
+ *
+ * @param s the stream to read
+ * @throws InvalidObjectException always
+ */
+ private void readObject(ObjectInputStream s) throws InvalidObjectException {
+ throw new InvalidObjectException("Deserialization via serialization delegate");
+ }
+
+ @Override
+ void write(DataOutput out) throws IOException {
+ out.writeByte(Ser.ZONE_REGION_TYPE);
+ writeExternal(out);
+ }
+
+ void writeExternal(DataOutput out) throws IOException {
+ out.writeUTF(id);
+ }
+
+ static ZoneId readExternal(DataInput in) throws IOException {
+ String id = in.readUTF();
+ return ZoneId.of(id, false);
+ }
+
+}
diff --git a/java/time/ZonedDateTime.java b/java/time/ZonedDateTime.java
new file mode 100644
index 0000000..4af3bda
--- /dev/null
+++ b/java/time/ZonedDateTime.java
@@ -0,0 +1,2248 @@
+/*
+ * Copyright (c) 2012, 2015, 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) 2007-2012, 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;
+
+import static java.time.temporal.ChronoField.INSTANT_SECONDS;
+import static java.time.temporal.ChronoField.NANO_OF_SECOND;
+import static java.time.temporal.ChronoField.OFFSET_SECONDS;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.time.chrono.ChronoZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.time.temporal.ChronoField;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.Temporal;
+import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalAdjuster;
+import java.time.temporal.TemporalAmount;
+import java.time.temporal.TemporalField;
+import java.time.temporal.TemporalQueries;
+import java.time.temporal.TemporalQuery;
+import java.time.temporal.TemporalUnit;
+import java.time.temporal.UnsupportedTemporalTypeException;
+import java.time.temporal.ValueRange;
+import java.time.zone.ZoneOffsetTransition;
+import java.time.zone.ZoneRules;
+import java.util.List;
+import java.util.Objects;
+
+// Android-changed: removed ValueBased paragraph.
+/**
+ * A date-time with a time-zone in the ISO-8601 calendar system,
+ * such as {@code 2007-12-03T10:15:30+01:00 Europe/Paris}.
+ * <p>
+ * {@code ZonedDateTime} is an immutable representation of a date-time with a time-zone.
+ * This class stores all date and time fields, to a precision of nanoseconds,
+ * and a time-zone, with a zone offset used to handle ambiguous local date-times.
+ * For example, the value
+ * "2nd October 2007 at 13:45.30.123456789 +02:00 in the Europe/Paris time-zone"
+ * can be stored in a {@code ZonedDateTime}.
+ * <p>
+ * This class handles conversion from the local time-line of {@code LocalDateTime}
+ * to the instant time-line of {@code Instant}.
+ * The difference between the two time-lines is the offset from UTC/Greenwich,
+ * represented by a {@code ZoneOffset}.
+ * <p>
+ * Converting between the two time-lines involves calculating the offset using the
+ * {@link ZoneRules rules} accessed from the {@code ZoneId}.
+ * Obtaining the offset for an instant is simple, as there is exactly one valid
+ * offset for each instant. By contrast, obtaining the offset for a local date-time
+ * is not straightforward. There are three cases:
+ * <ul>
+ * <li>Normal, with one valid offset. For the vast majority of the year, the normal
+ * case applies, where there is a single valid offset for the local date-time.</li>
+ * <li>Gap, with zero valid offsets. This is when clocks jump forward typically
+ * due to the spring daylight savings change from "winter" to "summer".
+ * In a gap there are local date-time values with no valid offset.</li>
+ * <li>Overlap, with two valid offsets. This is when clocks are set back typically
+ * due to the autumn daylight savings change from "summer" to "winter".
+ * In an overlap there are local date-time values with two valid offsets.</li>
+ * </ul>
+ * <p>
+ * Any method that converts directly or implicitly from a local date-time to an
+ * instant by obtaining the offset has the potential to be complicated.
+ * <p>
+ * For Gaps, the general strategy is that if the local date-time falls in the
+ * middle of a Gap, then the resulting zoned date-time will have a local date-time
+ * shifted forwards by the length of the Gap, resulting in a date-time in the later
+ * offset, typically "summer" time.
+ * <p>
+ * For Overlaps, the general strategy is that if the local date-time falls in the
+ * middle of an Overlap, then the previous offset will be retained. If there is no
+ * previous offset, or the previous offset is invalid, then the earlier offset is
+ * used, typically "summer" time.. Two additional methods,
+ * {@link #withEarlierOffsetAtOverlap()} and {@link #withLaterOffsetAtOverlap()},
+ * help manage the case of an overlap.
+ * <p>
+ * In terms of design, this class should be viewed primarily as the combination
+ * of a {@code LocalDateTime} and a {@code ZoneId}. The {@code ZoneOffset} is
+ * a vital, but secondary, piece of information, used to ensure that the class
+ * represents an instant, especially during a daylight savings overlap.
+ *
+ * @implSpec
+ * A {@code ZonedDateTime} holds state equivalent to three separate objects,
+ * a {@code LocalDateTime}, a {@code ZoneId} and the resolved {@code ZoneOffset}.
+ * The offset and local date-time are used to define an instant when necessary.
+ * The zone ID is used to obtain the rules for how and when the offset changes.
+ * The offset cannot be freely set, as the zone controls which offsets are valid.
+ * <p>
+ * This class is immutable and thread-safe.
+ *
+ * @since 1.8
+ */
+public final class ZonedDateTime
+ implements Temporal, ChronoZonedDateTime<LocalDate>, Serializable {
+
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = -6260982410461394882L;
+
+ /**
+ * The local date-time.
+ */
+ private final LocalDateTime dateTime;
+ /**
+ * The offset from UTC/Greenwich.
+ */
+ private final ZoneOffset offset;
+ /**
+ * The time-zone.
+ */
+ private final ZoneId zone;
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains the current date-time from the system clock in the default time-zone.
+ * <p>
+ * This will query the {@link Clock#systemDefaultZone() system clock} in the default
+ * time-zone to obtain the current date-time.
+ * The zone and offset will be set based on the time-zone in the clock.
+ * <p>
+ * Using this method will prevent the ability to use an alternate clock for testing
+ * because the clock is hard-coded.
+ *
+ * @return the current date-time using the system clock, not null
+ */
+ public static ZonedDateTime now() {
+ return now(Clock.systemDefaultZone());
+ }
+
+ /**
+ * Obtains the current date-time from the system clock in the specified time-zone.
+ * <p>
+ * This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date-time.
+ * Specifying the time-zone avoids dependence on the default time-zone.
+ * The offset will be calculated from the specified time-zone.
+ * <p>
+ * Using this method will prevent the ability to use an alternate clock for testing
+ * because the clock is hard-coded.
+ *
+ * @param zone the zone ID to use, not null
+ * @return the current date-time using the system clock, not null
+ */
+ public static ZonedDateTime now(ZoneId zone) {
+ return now(Clock.system(zone));
+ }
+
+ /**
+ * Obtains the current date-time from the specified clock.
+ * <p>
+ * This will query the specified clock to obtain the current date-time.
+ * The zone and offset will be set based on the time-zone in the clock.
+ * <p>
+ * Using this method allows the use of an alternate clock for testing.
+ * The alternate clock may be introduced using {@link Clock dependency injection}.
+ *
+ * @param clock the clock to use, not null
+ * @return the current date-time, not null
+ */
+ public static ZonedDateTime now(Clock clock) {
+ Objects.requireNonNull(clock, "clock");
+ final Instant now = clock.instant(); // called once
+ return ofInstant(now, clock.getZone());
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code ZonedDateTime} from a local date and time.
+ * <p>
+ * This creates a zoned date-time matching the input local date and time as closely as possible.
+ * Time-zone rules, such as daylight savings, mean that not every local date-time
+ * is valid for the specified zone, thus the local date-time may be adjusted.
+ * <p>
+ * The local date time and first combined to form a local date-time.
+ * The local date-time is then resolved to a single instant on the time-line.
+ * This is achieved by finding a valid offset from UTC/Greenwich for the local
+ * date-time as defined by the {@link ZoneRules rules} of the zone ID.
+ *<p>
+ * In most cases, there is only one valid offset for a local date-time.
+ * In the case of an overlap, when clocks are set back, there are two valid offsets.
+ * This method uses the earlier offset typically corresponding to "summer".
+ * <p>
+ * In the case of a gap, when clocks jump forward, there is no valid offset.
+ * Instead, the local date-time is adjusted to be later by the length of the gap.
+ * For a typical one hour daylight savings change, the local date-time will be
+ * moved one hour later into the offset typically corresponding to "summer".
+ *
+ * @param date the local date, not null
+ * @param time the local time, not null
+ * @param zone the time-zone, not null
+ * @return the offset date-time, not null
+ */
+ public static ZonedDateTime of(LocalDate date, LocalTime time, ZoneId zone) {
+ return of(LocalDateTime.of(date, time), zone);
+ }
+
+ /**
+ * Obtains an instance of {@code ZonedDateTime} from a local date-time.
+ * <p>
+ * This creates a zoned date-time matching the input local date-time as closely as possible.
+ * Time-zone rules, such as daylight savings, mean that not every local date-time
+ * is valid for the specified zone, thus the local date-time may be adjusted.
+ * <p>
+ * The local date-time is resolved to a single instant on the time-line.
+ * This is achieved by finding a valid offset from UTC/Greenwich for the local
+ * date-time as defined by the {@link ZoneRules rules} of the zone ID.
+ *<p>
+ * In most cases, there is only one valid offset for a local date-time.
+ * In the case of an overlap, when clocks are set back, there are two valid offsets.
+ * This method uses the earlier offset typically corresponding to "summer".
+ * <p>
+ * In the case of a gap, when clocks jump forward, there is no valid offset.
+ * Instead, the local date-time is adjusted to be later by the length of the gap.
+ * For a typical one hour daylight savings change, the local date-time will be
+ * moved one hour later into the offset typically corresponding to "summer".
+ *
+ * @param localDateTime the local date-time, not null
+ * @param zone the time-zone, not null
+ * @return the zoned date-time, not null
+ */
+ public static ZonedDateTime of(LocalDateTime localDateTime, ZoneId zone) {
+ return ofLocal(localDateTime, zone, null);
+ }
+
+ /**
+ * Obtains an instance of {@code ZonedDateTime} from a year, month, day,
+ * hour, minute, second, nanosecond and time-zone.
+ * <p>
+ * This creates a zoned date-time matching the local date-time of the seven
+ * specified fields as closely as possible.
+ * Time-zone rules, such as daylight savings, mean that not every local date-time
+ * is valid for the specified zone, thus the local date-time may be adjusted.
+ * <p>
+ * The local date-time is resolved to a single instant on the time-line.
+ * This is achieved by finding a valid offset from UTC/Greenwich for the local
+ * date-time as defined by the {@link ZoneRules rules} of the zone ID.
+ *<p>
+ * In most cases, there is only one valid offset for a local date-time.
+ * In the case of an overlap, when clocks are set back, there are two valid offsets.
+ * This method uses the earlier offset typically corresponding to "summer".
+ * <p>
+ * In the case of a gap, when clocks jump forward, there is no valid offset.
+ * Instead, the local date-time is adjusted to be later by the length of the gap.
+ * For a typical one hour daylight savings change, the local date-time will be
+ * moved one hour later into the offset typically corresponding to "summer".
+ * <p>
+ * This method exists primarily for writing test cases.
+ * Non test-code will typically use other methods to create an offset time.
+ * {@code LocalDateTime} has five additional convenience variants of the
+ * equivalent factory method taking fewer arguments.
+ * They are not provided here to reduce the footprint of the API.
+ *
+ * @param year the year to represent, from MIN_YEAR to MAX_YEAR
+ * @param month the month-of-year to represent, from 1 (January) to 12 (December)
+ * @param dayOfMonth the day-of-month to represent, from 1 to 31
+ * @param hour the hour-of-day to represent, from 0 to 23
+ * @param minute the minute-of-hour to represent, from 0 to 59
+ * @param second the second-of-minute to represent, from 0 to 59
+ * @param nanoOfSecond the nano-of-second to represent, from 0 to 999,999,999
+ * @param zone the time-zone, not null
+ * @return the offset date-time, not null
+ * @throws DateTimeException if the value of any field is out of range, or
+ * if the day-of-month is invalid for the month-year
+ */
+ public static ZonedDateTime of(
+ int year, int month, int dayOfMonth,
+ int hour, int minute, int second, int nanoOfSecond, ZoneId zone) {
+ LocalDateTime dt = LocalDateTime.of(year, month, dayOfMonth, hour, minute, second, nanoOfSecond);
+ return ofLocal(dt, zone, null);
+ }
+
+ /**
+ * Obtains an instance of {@code ZonedDateTime} from a local date-time
+ * using the preferred offset if possible.
+ * <p>
+ * The local date-time is resolved to a single instant on the time-line.
+ * This is achieved by finding a valid offset from UTC/Greenwich for the local
+ * date-time as defined by the {@link ZoneRules rules} of the zone ID.
+ *<p>
+ * In most cases, there is only one valid offset for a local date-time.
+ * In the case of an overlap, where clocks are set back, there are two valid offsets.
+ * If the preferred offset is one of the valid offsets then it is used.
+ * Otherwise the earlier valid offset is used, typically corresponding to "summer".
+ * <p>
+ * In the case of a gap, where clocks jump forward, there is no valid offset.
+ * Instead, the local date-time is adjusted to be later by the length of the gap.
+ * For a typical one hour daylight savings change, the local date-time will be
+ * moved one hour later into the offset typically corresponding to "summer".
+ *
+ * @param localDateTime the local date-time, not null
+ * @param zone the time-zone, not null
+ * @param preferredOffset the zone offset, null if no preference
+ * @return the zoned date-time, not null
+ */
+ public static ZonedDateTime ofLocal(LocalDateTime localDateTime, ZoneId zone, ZoneOffset preferredOffset) {
+ Objects.requireNonNull(localDateTime, "localDateTime");
+ Objects.requireNonNull(zone, "zone");
+ if (zone instanceof ZoneOffset) {
+ return new ZonedDateTime(localDateTime, (ZoneOffset) zone, zone);
+ }
+ ZoneRules rules = zone.getRules();
+ List<ZoneOffset> validOffsets = rules.getValidOffsets(localDateTime);
+ ZoneOffset offset;
+ if (validOffsets.size() == 1) {
+ offset = validOffsets.get(0);
+ } else if (validOffsets.size() == 0) {
+ ZoneOffsetTransition trans = rules.getTransition(localDateTime);
+ localDateTime = localDateTime.plusSeconds(trans.getDuration().getSeconds());
+ offset = trans.getOffsetAfter();
+ } else {
+ if (preferredOffset != null && validOffsets.contains(preferredOffset)) {
+ offset = preferredOffset;
+ } else {
+ offset = Objects.requireNonNull(validOffsets.get(0), "offset"); // protect against bad ZoneRules
+ }
+ }
+ return new ZonedDateTime(localDateTime, offset, zone);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code ZonedDateTime} from an {@code Instant}.
+ * <p>
+ * This creates a zoned date-time with the same instant as that specified.
+ * Calling {@link #toInstant()} will return an instant equal to the one used here.
+ * <p>
+ * Converting an instant to a zoned date-time is simple as there is only one valid
+ * offset for each instant.
+ *
+ * @param instant the instant to create the date-time from, not null
+ * @param zone the time-zone, not null
+ * @return the zoned date-time, not null
+ * @throws DateTimeException if the result exceeds the supported range
+ */
+ public static ZonedDateTime ofInstant(Instant instant, ZoneId zone) {
+ Objects.requireNonNull(instant, "instant");
+ Objects.requireNonNull(zone, "zone");
+ return create(instant.getEpochSecond(), instant.getNano(), zone);
+ }
+
+ /**
+ * Obtains an instance of {@code ZonedDateTime} from the instant formed by combining
+ * the local date-time and offset.
+ * <p>
+ * This creates a zoned date-time by {@link LocalDateTime#toInstant(ZoneOffset) combining}
+ * the {@code LocalDateTime} and {@code ZoneOffset}.
+ * This combination uniquely specifies an instant without ambiguity.
+ * <p>
+ * Converting an instant to a zoned date-time is simple as there is only one valid
+ * offset for each instant. If the valid offset is different to the offset specified,
+ * then the date-time and offset of the zoned date-time will differ from those specified.
+ * <p>
+ * If the {@code ZoneId} to be used is a {@code ZoneOffset}, this method is equivalent
+ * to {@link #of(LocalDateTime, ZoneId)}.
+ *
+ * @param localDateTime the local date-time, not null
+ * @param offset the zone offset, not null
+ * @param zone the time-zone, not null
+ * @return the zoned date-time, not null
+ */
+ public static ZonedDateTime ofInstant(LocalDateTime localDateTime, ZoneOffset offset, ZoneId zone) {
+ Objects.requireNonNull(localDateTime, "localDateTime");
+ Objects.requireNonNull(offset, "offset");
+ Objects.requireNonNull(zone, "zone");
+ if (zone.getRules().isValidOffset(localDateTime, offset)) {
+ return new ZonedDateTime(localDateTime, offset, zone);
+ }
+ return create(localDateTime.toEpochSecond(offset), localDateTime.getNano(), zone);
+ }
+
+ /**
+ * Obtains an instance of {@code ZonedDateTime} using seconds from the
+ * epoch of 1970-01-01T00:00:00Z.
+ *
+ * @param epochSecond the number of seconds from the epoch of 1970-01-01T00:00:00Z
+ * @param nanoOfSecond the nanosecond within the second, from 0 to 999,999,999
+ * @param zone the time-zone, not null
+ * @return the zoned date-time, not null
+ * @throws DateTimeException if the result exceeds the supported range
+ */
+ private static ZonedDateTime create(long epochSecond, int nanoOfSecond, ZoneId zone) {
+ ZoneRules rules = zone.getRules();
+ Instant instant = Instant.ofEpochSecond(epochSecond, nanoOfSecond); // TODO: rules should be queryable by epochSeconds
+ ZoneOffset offset = rules.getOffset(instant);
+ LocalDateTime ldt = LocalDateTime.ofEpochSecond(epochSecond, nanoOfSecond, offset);
+ return new ZonedDateTime(ldt, offset, zone);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code ZonedDateTime} strictly validating the
+ * combination of local date-time, offset and zone ID.
+ * <p>
+ * This creates a zoned date-time ensuring that the offset is valid for the
+ * local date-time according to the rules of the specified zone.
+ * If the offset is invalid, an exception is thrown.
+ *
+ * @param localDateTime the local date-time, not null
+ * @param offset the zone offset, not null
+ * @param zone the time-zone, not null
+ * @return the zoned date-time, not null
+ */
+ public static ZonedDateTime ofStrict(LocalDateTime localDateTime, ZoneOffset offset, ZoneId zone) {
+ Objects.requireNonNull(localDateTime, "localDateTime");
+ Objects.requireNonNull(offset, "offset");
+ Objects.requireNonNull(zone, "zone");
+ ZoneRules rules = zone.getRules();
+ if (rules.isValidOffset(localDateTime, offset) == false) {
+ ZoneOffsetTransition trans = rules.getTransition(localDateTime);
+ if (trans != null && trans.isGap()) {
+ // error message says daylight savings for simplicity
+ // even though there are other kinds of gaps
+ throw new DateTimeException("LocalDateTime '" + localDateTime +
+ "' does not exist in zone '" + zone +
+ "' due to a gap in the local time-line, typically caused by daylight savings");
+ }
+ throw new DateTimeException("ZoneOffset '" + offset + "' is not valid for LocalDateTime '" +
+ localDateTime + "' in zone '" + zone + "'");
+ }
+ return new ZonedDateTime(localDateTime, offset, zone);
+ }
+
+ /**
+ * Obtains an instance of {@code ZonedDateTime} leniently, for advanced use cases,
+ * allowing any combination of local date-time, offset and zone ID.
+ * <p>
+ * This creates a zoned date-time with no checks other than no nulls.
+ * This means that the resulting zoned date-time may have an offset that is in conflict
+ * with the zone ID.
+ * <p>
+ * This method is intended for advanced use cases.
+ * For example, consider the case where a zoned date-time with valid fields is created
+ * and then stored in a database or serialization-based store. At some later point,
+ * the object is then re-loaded. However, between those points in time, the government
+ * that defined the time-zone has changed the rules, such that the originally stored
+ * local date-time now does not occur. This method can be used to create the object
+ * in an "invalid" state, despite the change in rules.
+ *
+ * @param localDateTime the local date-time, not null
+ * @param offset the zone offset, not null
+ * @param zone the time-zone, not null
+ * @return the zoned date-time, not null
+ */
+ private static ZonedDateTime ofLenient(LocalDateTime localDateTime, ZoneOffset offset, ZoneId zone) {
+ Objects.requireNonNull(localDateTime, "localDateTime");
+ Objects.requireNonNull(offset, "offset");
+ Objects.requireNonNull(zone, "zone");
+ if (zone instanceof ZoneOffset && offset.equals(zone) == false) {
+ throw new IllegalArgumentException("ZoneId must match ZoneOffset");
+ }
+ return new ZonedDateTime(localDateTime, offset, zone);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code ZonedDateTime} from a temporal object.
+ * <p>
+ * This obtains a zoned date-time based on the specified temporal.
+ * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
+ * which this factory converts to an instance of {@code ZonedDateTime}.
+ * <p>
+ * The conversion will first obtain a {@code ZoneId} from the temporal object,
+ * falling back to a {@code ZoneOffset} if necessary. It will then try to obtain
+ * an {@code Instant}, falling back to a {@code LocalDateTime} if necessary.
+ * The result will be either the combination of {@code ZoneId} or {@code ZoneOffset}
+ * with {@code Instant} or {@code LocalDateTime}.
+ * Implementations are permitted to perform optimizations such as accessing
+ * those fields that are equivalent to the relevant objects.
+ * <p>
+ * This method matches the signature of the functional interface {@link TemporalQuery}
+ * allowing it to be used as a query via method reference, {@code ZonedDateTime::from}.
+ *
+ * @param temporal the temporal object to convert, not null
+ * @return the zoned date-time, not null
+ * @throws DateTimeException if unable to convert to an {@code ZonedDateTime}
+ */
+ public static ZonedDateTime from(TemporalAccessor temporal) {
+ if (temporal instanceof ZonedDateTime) {
+ return (ZonedDateTime) temporal;
+ }
+ try {
+ ZoneId zone = ZoneId.from(temporal);
+ if (temporal.isSupported(INSTANT_SECONDS)) {
+ long epochSecond = temporal.getLong(INSTANT_SECONDS);
+ int nanoOfSecond = temporal.get(NANO_OF_SECOND);
+ return create(epochSecond, nanoOfSecond, zone);
+ } else {
+ LocalDate date = LocalDate.from(temporal);
+ LocalTime time = LocalTime.from(temporal);
+ return of(date, time, zone);
+ }
+ } catch (DateTimeException ex) {
+ throw new DateTimeException("Unable to obtain ZonedDateTime from TemporalAccessor: " +
+ temporal + " of type " + temporal.getClass().getName(), ex);
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code ZonedDateTime} from a text string such as
+ * {@code 2007-12-03T10:15:30+01:00[Europe/Paris]}.
+ * <p>
+ * The string must represent a valid date-time and is parsed using
+ * {@link java.time.format.DateTimeFormatter#ISO_ZONED_DATE_TIME}.
+ *
+ * @param text the text to parse such as "2007-12-03T10:15:30+01:00[Europe/Paris]", not null
+ * @return the parsed zoned date-time, not null
+ * @throws DateTimeParseException if the text cannot be parsed
+ */
+ public static ZonedDateTime parse(CharSequence text) {
+ return parse(text, DateTimeFormatter.ISO_ZONED_DATE_TIME);
+ }
+
+ /**
+ * Obtains an instance of {@code ZonedDateTime} from a text string using a specific formatter.
+ * <p>
+ * The text is parsed using the formatter, returning a date-time.
+ *
+ * @param text the text to parse, not null
+ * @param formatter the formatter to use, not null
+ * @return the parsed zoned date-time, not null
+ * @throws DateTimeParseException if the text cannot be parsed
+ */
+ public static ZonedDateTime parse(CharSequence text, DateTimeFormatter formatter) {
+ Objects.requireNonNull(formatter, "formatter");
+ return formatter.parse(text, ZonedDateTime::from);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Constructor.
+ *
+ * @param dateTime the date-time, validated as not null
+ * @param offset the zone offset, validated as not null
+ * @param zone the time-zone, validated as not null
+ */
+ private ZonedDateTime(LocalDateTime dateTime, ZoneOffset offset, ZoneId zone) {
+ this.dateTime = dateTime;
+ this.offset = offset;
+ this.zone = zone;
+ }
+
+ /**
+ * Resolves the new local date-time using this zone ID, retaining the offset if possible.
+ *
+ * @param newDateTime the new local date-time, not null
+ * @return the zoned date-time, not null
+ */
+ private ZonedDateTime resolveLocal(LocalDateTime newDateTime) {
+ return ofLocal(newDateTime, zone, offset);
+ }
+
+ /**
+ * Resolves the new local date-time using the offset to identify the instant.
+ *
+ * @param newDateTime the new local date-time, not null
+ * @return the zoned date-time, not null
+ */
+ private ZonedDateTime resolveInstant(LocalDateTime newDateTime) {
+ return ofInstant(newDateTime, offset, zone);
+ }
+
+ /**
+ * Resolves the offset into this zoned date-time for the with methods.
+ * <p>
+ * This typically ignores the offset, unless it can be used to switch offset in a DST overlap.
+ *
+ * @param offset the offset, not null
+ * @return the zoned date-time, not null
+ */
+ private ZonedDateTime resolveOffset(ZoneOffset offset) {
+ if (offset.equals(this.offset) == false && zone.getRules().isValidOffset(dateTime, offset)) {
+ return new ZonedDateTime(dateTime, offset, zone);
+ }
+ return this;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if the specified field is supported.
+ * <p>
+ * This checks if this date-time can be queried for the specified field.
+ * If false, then calling the {@link #range(TemporalField) range},
+ * {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}
+ * methods will throw an exception.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The supported fields are:
+ * <ul>
+ * <li>{@code NANO_OF_SECOND}
+ * <li>{@code NANO_OF_DAY}
+ * <li>{@code MICRO_OF_SECOND}
+ * <li>{@code MICRO_OF_DAY}
+ * <li>{@code MILLI_OF_SECOND}
+ * <li>{@code MILLI_OF_DAY}
+ * <li>{@code SECOND_OF_MINUTE}
+ * <li>{@code SECOND_OF_DAY}
+ * <li>{@code MINUTE_OF_HOUR}
+ * <li>{@code MINUTE_OF_DAY}
+ * <li>{@code HOUR_OF_AMPM}
+ * <li>{@code CLOCK_HOUR_OF_AMPM}
+ * <li>{@code HOUR_OF_DAY}
+ * <li>{@code CLOCK_HOUR_OF_DAY}
+ * <li>{@code AMPM_OF_DAY}
+ * <li>{@code DAY_OF_WEEK}
+ * <li>{@code ALIGNED_DAY_OF_WEEK_IN_MONTH}
+ * <li>{@code ALIGNED_DAY_OF_WEEK_IN_YEAR}
+ * <li>{@code DAY_OF_MONTH}
+ * <li>{@code DAY_OF_YEAR}
+ * <li>{@code EPOCH_DAY}
+ * <li>{@code ALIGNED_WEEK_OF_MONTH}
+ * <li>{@code ALIGNED_WEEK_OF_YEAR}
+ * <li>{@code MONTH_OF_YEAR}
+ * <li>{@code PROLEPTIC_MONTH}
+ * <li>{@code YEAR_OF_ERA}
+ * <li>{@code YEAR}
+ * <li>{@code ERA}
+ * <li>{@code INSTANT_SECONDS}
+ * <li>{@code OFFSET_SECONDS}
+ * </ul>
+ * All other {@code ChronoField} instances will return false.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}
+ * passing {@code this} as the argument.
+ * Whether the field is supported is determined by the field.
+ *
+ * @param field the field to check, null returns false
+ * @return true if the field is supported on this date-time, false if not
+ */
+ @Override
+ public boolean isSupported(TemporalField field) {
+ return field instanceof ChronoField || (field != null && field.isSupportedBy(this));
+ }
+
+ /**
+ * Checks if the specified unit is supported.
+ * <p>
+ * This checks if the specified unit can be added to, or subtracted from, this date-time.
+ * If false, then calling the {@link #plus(long, TemporalUnit)} and
+ * {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
+ * <p>
+ * If the unit is a {@link ChronoUnit} then the query is implemented here.
+ * The supported units are:
+ * <ul>
+ * <li>{@code NANOS}
+ * <li>{@code MICROS}
+ * <li>{@code MILLIS}
+ * <li>{@code SECONDS}
+ * <li>{@code MINUTES}
+ * <li>{@code HOURS}
+ * <li>{@code HALF_DAYS}
+ * <li>{@code DAYS}
+ * <li>{@code WEEKS}
+ * <li>{@code MONTHS}
+ * <li>{@code YEARS}
+ * <li>{@code DECADES}
+ * <li>{@code CENTURIES}
+ * <li>{@code MILLENNIA}
+ * <li>{@code ERAS}
+ * </ul>
+ * All other {@code ChronoUnit} instances will return false.
+ * <p>
+ * If the unit is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
+ * passing {@code this} as the argument.
+ * Whether the unit is supported is determined by the unit.
+ *
+ * @param unit the unit to check, null returns false
+ * @return true if the unit can be added/subtracted, false if not
+ */
+ @Override // override for Javadoc
+ public boolean isSupported(TemporalUnit unit) {
+ return ChronoZonedDateTime.super.isSupported(unit);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the range of valid values for the specified field.
+ * <p>
+ * The range object expresses the minimum and maximum valid values for a field.
+ * This date-time is used to enhance the accuracy of the returned range.
+ * If it is not possible to return the range, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The {@link #isSupported(TemporalField) supported fields} will return
+ * appropriate range instances.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.rangeRefinedBy(TemporalAccessor)}
+ * passing {@code this} as the argument.
+ * Whether the range can be obtained is determined by the field.
+ *
+ * @param field the field to query the range for, not null
+ * @return the range of valid values for the field, not null
+ * @throws DateTimeException if the range for the field cannot be obtained
+ * @throws UnsupportedTemporalTypeException if the field is not supported
+ */
+ @Override
+ public ValueRange range(TemporalField field) {
+ if (field instanceof ChronoField) {
+ if (field == INSTANT_SECONDS || field == OFFSET_SECONDS) {
+ return field.range();
+ }
+ return dateTime.range(field);
+ }
+ return field.rangeRefinedBy(this);
+ }
+
+ /**
+ * Gets the value of the specified field from this date-time as an {@code int}.
+ * <p>
+ * This queries this date-time for the value of the specified field.
+ * The returned value will always be within the valid range of values for the field.
+ * If it is not possible to return the value, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The {@link #isSupported(TemporalField) supported fields} will return valid
+ * values based on this date-time, except {@code NANO_OF_DAY}, {@code MICRO_OF_DAY},
+ * {@code EPOCH_DAY}, {@code PROLEPTIC_MONTH} and {@code INSTANT_SECONDS} which are too
+ * large to fit in an {@code int} and throw a {@code DateTimeException}.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
+ * passing {@code this} as the argument. Whether the value can be obtained,
+ * and what the value represents, is determined by the field.
+ *
+ * @param field the field to get, not null
+ * @return the value for the field
+ * @throws DateTimeException if a value for the field cannot be obtained or
+ * the value is outside the range of valid values for the field
+ * @throws UnsupportedTemporalTypeException if the field is not supported or
+ * the range of values exceeds an {@code int}
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override // override for Javadoc and performance
+ public int get(TemporalField field) {
+ if (field instanceof ChronoField) {
+ switch ((ChronoField) field) {
+ case INSTANT_SECONDS:
+ throw new UnsupportedTemporalTypeException("Invalid field 'InstantSeconds' for get() method, use getLong() instead");
+ case OFFSET_SECONDS:
+ return getOffset().getTotalSeconds();
+ }
+ return dateTime.get(field);
+ }
+ return ChronoZonedDateTime.super.get(field);
+ }
+
+ /**
+ * Gets the value of the specified field from this date-time as a {@code long}.
+ * <p>
+ * This queries this date-time for the value of the specified field.
+ * If it is not possible to return the value, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The {@link #isSupported(TemporalField) supported fields} will return valid
+ * values based on this date-time.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
+ * passing {@code this} as the argument. Whether the value can be obtained,
+ * and what the value represents, is determined by the field.
+ *
+ * @param field the field to get, not null
+ * @return the value for the field
+ * @throws DateTimeException if a value for the field cannot be obtained
+ * @throws UnsupportedTemporalTypeException if the field is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public long getLong(TemporalField field) {
+ if (field instanceof ChronoField) {
+ switch ((ChronoField) field) {
+ case INSTANT_SECONDS: return toEpochSecond();
+ case OFFSET_SECONDS: return getOffset().getTotalSeconds();
+ }
+ return dateTime.getLong(field);
+ }
+ return field.getFrom(this);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the zone offset, such as '+01:00'.
+ * <p>
+ * This is the offset of the local date-time from UTC/Greenwich.
+ *
+ * @return the zone offset, not null
+ */
+ @Override
+ public ZoneOffset getOffset() {
+ return offset;
+ }
+
+ /**
+ * Returns a copy of this date-time changing the zone offset to the
+ * earlier of the two valid offsets at a local time-line overlap.
+ * <p>
+ * This method only has any effect when the local time-line overlaps, such as
+ * at an autumn daylight savings cutover. In this scenario, there are two
+ * valid offsets for the local date-time. Calling this method will return
+ * a zoned date-time with the earlier of the two selected.
+ * <p>
+ * If this method is called when it is not an overlap, {@code this}
+ * is returned.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @return a {@code ZonedDateTime} based on this date-time with the earlier offset, not null
+ */
+ @Override
+ public ZonedDateTime withEarlierOffsetAtOverlap() {
+ ZoneOffsetTransition trans = getZone().getRules().getTransition(dateTime);
+ if (trans != null && trans.isOverlap()) {
+ ZoneOffset earlierOffset = trans.getOffsetBefore();
+ if (earlierOffset.equals(offset) == false) {
+ return new ZonedDateTime(dateTime, earlierOffset, zone);
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Returns a copy of this date-time changing the zone offset to the
+ * later of the two valid offsets at a local time-line overlap.
+ * <p>
+ * This method only has any effect when the local time-line overlaps, such as
+ * at an autumn daylight savings cutover. In this scenario, there are two
+ * valid offsets for the local date-time. Calling this method will return
+ * a zoned date-time with the later of the two selected.
+ * <p>
+ * If this method is called when it is not an overlap, {@code this}
+ * is returned.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @return a {@code ZonedDateTime} based on this date-time with the later offset, not null
+ */
+ @Override
+ public ZonedDateTime withLaterOffsetAtOverlap() {
+ ZoneOffsetTransition trans = getZone().getRules().getTransition(toLocalDateTime());
+ if (trans != null) {
+ ZoneOffset laterOffset = trans.getOffsetAfter();
+ if (laterOffset.equals(offset) == false) {
+ return new ZonedDateTime(dateTime, laterOffset, zone);
+ }
+ }
+ return this;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the time-zone, such as 'Europe/Paris'.
+ * <p>
+ * This returns the zone ID. This identifies the time-zone {@link ZoneRules rules}
+ * that determine when and how the offset from UTC/Greenwich changes.
+ * <p>
+ * The zone ID may be same as the {@linkplain #getOffset() offset}.
+ * If this is true, then any future calculations, such as addition or subtraction,
+ * have no complex edge cases due to time-zone rules.
+ * See also {@link #withFixedOffsetZone()}.
+ *
+ * @return the time-zone, not null
+ */
+ @Override
+ public ZoneId getZone() {
+ return zone;
+ }
+
+ /**
+ * Returns a copy of this date-time with a different time-zone,
+ * retaining the local date-time if possible.
+ * <p>
+ * This method changes the time-zone and retains the local date-time.
+ * The local date-time is only changed if it is invalid for the new zone,
+ * determined using the same approach as
+ * {@link #ofLocal(LocalDateTime, ZoneId, ZoneOffset)}.
+ * <p>
+ * To change the zone and adjust the local date-time,
+ * use {@link #withZoneSameInstant(ZoneId)}.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param zone the time-zone to change to, not null
+ * @return a {@code ZonedDateTime} based on this date-time with the requested zone, not null
+ */
+ @Override
+ public ZonedDateTime withZoneSameLocal(ZoneId zone) {
+ Objects.requireNonNull(zone, "zone");
+ return this.zone.equals(zone) ? this : ofLocal(dateTime, zone, offset);
+ }
+
+ /**
+ * Returns a copy of this date-time with a different time-zone,
+ * retaining the instant.
+ * <p>
+ * This method changes the time-zone and retains the instant.
+ * This normally results in a change to the local date-time.
+ * <p>
+ * This method is based on retaining the same instant, thus gaps and overlaps
+ * in the local time-line have no effect on the result.
+ * <p>
+ * To change the offset while keeping the local time,
+ * use {@link #withZoneSameLocal(ZoneId)}.
+ *
+ * @param zone the time-zone to change to, not null
+ * @return a {@code ZonedDateTime} based on this date-time with the requested zone, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ @Override
+ public ZonedDateTime withZoneSameInstant(ZoneId zone) {
+ Objects.requireNonNull(zone, "zone");
+ return this.zone.equals(zone) ? this :
+ create(dateTime.toEpochSecond(offset), dateTime.getNano(), zone);
+ }
+
+ /**
+ * Returns a copy of this date-time with the zone ID set to the offset.
+ * <p>
+ * This returns a zoned date-time where the zone ID is the same as {@link #getOffset()}.
+ * The local date-time, offset and instant of the result will be the same as in this date-time.
+ * <p>
+ * Setting the date-time to a fixed single offset means that any future
+ * calculations, such as addition or subtraction, have no complex edge cases
+ * due to time-zone rules.
+ * This might also be useful when sending a zoned date-time across a network,
+ * as most protocols, such as ISO-8601, only handle offsets,
+ * and not region-based zone IDs.
+ * <p>
+ * This is equivalent to {@code ZonedDateTime.of(zdt.toLocalDateTime(), zdt.getOffset())}.
+ *
+ * @return a {@code ZonedDateTime} with the zone ID set to the offset, not null
+ */
+ public ZonedDateTime withFixedOffsetZone() {
+ return this.zone.equals(offset) ? this : new ZonedDateTime(dateTime, offset, offset);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the {@code LocalDateTime} part of this date-time.
+ * <p>
+ * This returns a {@code LocalDateTime} with the same year, month, day and time
+ * as this date-time.
+ *
+ * @return the local date-time part of this date-time, not null
+ */
+ @Override // override for return type
+ public LocalDateTime toLocalDateTime() {
+ return dateTime;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the {@code LocalDate} part of this date-time.
+ * <p>
+ * This returns a {@code LocalDate} with the same year, month and day
+ * as this date-time.
+ *
+ * @return the date part of this date-time, not null
+ */
+ @Override // override for return type
+ public LocalDate toLocalDate() {
+ return dateTime.toLocalDate();
+ }
+
+ /**
+ * Gets the year field.
+ * <p>
+ * This method returns the primitive {@code int} value for the year.
+ * <p>
+ * The year returned by this method is proleptic as per {@code get(YEAR)}.
+ * To obtain the year-of-era, use {@code get(YEAR_OF_ERA)}.
+ *
+ * @return the year, from MIN_YEAR to MAX_YEAR
+ */
+ public int getYear() {
+ return dateTime.getYear();
+ }
+
+ /**
+ * Gets the month-of-year field from 1 to 12.
+ * <p>
+ * This method returns the month as an {@code int} from 1 to 12.
+ * Application code is frequently clearer if the enum {@link Month}
+ * is used by calling {@link #getMonth()}.
+ *
+ * @return the month-of-year, from 1 to 12
+ * @see #getMonth()
+ */
+ public int getMonthValue() {
+ return dateTime.getMonthValue();
+ }
+
+ /**
+ * Gets the month-of-year field using the {@code Month} enum.
+ * <p>
+ * This method returns the enum {@link Month} for the month.
+ * This avoids confusion as to what {@code int} values mean.
+ * If you need access to the primitive {@code int} value then the enum
+ * provides the {@link Month#getValue() int value}.
+ *
+ * @return the month-of-year, not null
+ * @see #getMonthValue()
+ */
+ public Month getMonth() {
+ return dateTime.getMonth();
+ }
+
+ /**
+ * Gets the day-of-month field.
+ * <p>
+ * This method returns the primitive {@code int} value for the day-of-month.
+ *
+ * @return the day-of-month, from 1 to 31
+ */
+ public int getDayOfMonth() {
+ return dateTime.getDayOfMonth();
+ }
+
+ /**
+ * Gets the day-of-year field.
+ * <p>
+ * This method returns the primitive {@code int} value for the day-of-year.
+ *
+ * @return the day-of-year, from 1 to 365, or 366 in a leap year
+ */
+ public int getDayOfYear() {
+ return dateTime.getDayOfYear();
+ }
+
+ /**
+ * Gets the day-of-week field, which is an enum {@code DayOfWeek}.
+ * <p>
+ * This method returns the enum {@link DayOfWeek} for the day-of-week.
+ * This avoids confusion as to what {@code int} values mean.
+ * If you need access to the primitive {@code int} value then the enum
+ * provides the {@link DayOfWeek#getValue() int value}.
+ * <p>
+ * Additional information can be obtained from the {@code DayOfWeek}.
+ * This includes textual names of the values.
+ *
+ * @return the day-of-week, not null
+ */
+ public DayOfWeek getDayOfWeek() {
+ return dateTime.getDayOfWeek();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the {@code LocalTime} part of this date-time.
+ * <p>
+ * This returns a {@code LocalTime} with the same hour, minute, second and
+ * nanosecond as this date-time.
+ *
+ * @return the time part of this date-time, not null
+ */
+ @Override // override for Javadoc and performance
+ public LocalTime toLocalTime() {
+ return dateTime.toLocalTime();
+ }
+
+ /**
+ * Gets the hour-of-day field.
+ *
+ * @return the hour-of-day, from 0 to 23
+ */
+ public int getHour() {
+ return dateTime.getHour();
+ }
+
+ /**
+ * Gets the minute-of-hour field.
+ *
+ * @return the minute-of-hour, from 0 to 59
+ */
+ public int getMinute() {
+ return dateTime.getMinute();
+ }
+
+ /**
+ * Gets the second-of-minute field.
+ *
+ * @return the second-of-minute, from 0 to 59
+ */
+ public int getSecond() {
+ return dateTime.getSecond();
+ }
+
+ /**
+ * Gets the nano-of-second field.
+ *
+ * @return the nano-of-second, from 0 to 999,999,999
+ */
+ public int getNano() {
+ return dateTime.getNano();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns an adjusted copy of this date-time.
+ * <p>
+ * This returns a {@code ZonedDateTime}, based on this one, with the date-time adjusted.
+ * The adjustment takes place using the specified adjuster strategy object.
+ * Read the documentation of the adjuster to understand what adjustment will be made.
+ * <p>
+ * A simple adjuster might simply set the one of the fields, such as the year field.
+ * A more complex adjuster might set the date to the last day of the month.
+ * A selection of common adjustments is provided in
+ * {@link java.time.temporal.TemporalAdjusters TemporalAdjusters}.
+ * These include finding the "last day of the month" and "next Wednesday".
+ * Key date-time classes also implement the {@code TemporalAdjuster} interface,
+ * such as {@link Month} and {@link java.time.MonthDay MonthDay}.
+ * The adjuster is responsible for handling special cases, such as the varying
+ * lengths of month and leap years.
+ * <p>
+ * For example this code returns a date on the last day of July:
+ * <pre>
+ * import static java.time.Month.*;
+ * import static java.time.temporal.TemporalAdjusters.*;
+ *
+ * result = zonedDateTime.with(JULY).with(lastDayOfMonth());
+ * </pre>
+ * <p>
+ * The classes {@link LocalDate} and {@link LocalTime} implement {@code TemporalAdjuster},
+ * thus this method can be used to change the date, time or offset:
+ * <pre>
+ * result = zonedDateTime.with(date);
+ * result = zonedDateTime.with(time);
+ * </pre>
+ * <p>
+ * {@link ZoneOffset} also implements {@code TemporalAdjuster} however using it
+ * as an argument typically has no effect. The offset of a {@code ZonedDateTime} is
+ * controlled primarily by the time-zone. As such, changing the offset does not generally
+ * make sense, because there is only one valid offset for the local date-time and zone.
+ * If the zoned date-time is in a daylight savings overlap, then the offset is used
+ * to switch between the two valid offsets. In all other cases, the offset is ignored.
+ * <p>
+ * The result of this method is obtained by invoking the
+ * {@link TemporalAdjuster#adjustInto(Temporal)} method on the
+ * specified adjuster passing {@code this} as the argument.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param adjuster the adjuster to use, not null
+ * @return a {@code ZonedDateTime} based on {@code this} with the adjustment made, not null
+ * @throws DateTimeException if the adjustment cannot be made
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public ZonedDateTime with(TemporalAdjuster adjuster) {
+ // optimizations
+ if (adjuster instanceof LocalDate) {
+ return resolveLocal(LocalDateTime.of((LocalDate) adjuster, dateTime.toLocalTime()));
+ } else if (adjuster instanceof LocalTime) {
+ return resolveLocal(LocalDateTime.of(dateTime.toLocalDate(), (LocalTime) adjuster));
+ } else if (adjuster instanceof LocalDateTime) {
+ return resolveLocal((LocalDateTime) adjuster);
+ } else if (adjuster instanceof OffsetDateTime) {
+ OffsetDateTime odt = (OffsetDateTime) adjuster;
+ return ofLocal(odt.toLocalDateTime(), zone, odt.getOffset());
+ } else if (adjuster instanceof Instant) {
+ Instant instant = (Instant) adjuster;
+ return create(instant.getEpochSecond(), instant.getNano(), zone);
+ } else if (adjuster instanceof ZoneOffset) {
+ return resolveOffset((ZoneOffset) adjuster);
+ }
+ return (ZonedDateTime) adjuster.adjustInto(this);
+ }
+
+ /**
+ * Returns a copy of this date-time with the specified field set to a new value.
+ * <p>
+ * This returns a {@code ZonedDateTime}, based on this one, with the value
+ * for the specified field changed.
+ * This can be used to change any supported field, such as the year, month or day-of-month.
+ * If it is not possible to set the value, because the field is not supported or for
+ * some other reason, an exception is thrown.
+ * <p>
+ * In some cases, changing the specified field can cause the resulting date-time to become invalid,
+ * such as changing the month from 31st January to February would make the day-of-month invalid.
+ * In cases like this, the field is responsible for resolving the date. Typically it will choose
+ * the previous valid date, which would be the last valid day of February in this example.
+ * <p>
+ * If the field is a {@link ChronoField} then the adjustment is implemented here.
+ * <p>
+ * The {@code INSTANT_SECONDS} field will return a date-time with the specified instant.
+ * The zone and nano-of-second are unchanged.
+ * The result will have an offset derived from the new instant and original zone.
+ * If the new instant value is outside the valid range then a {@code DateTimeException} will be thrown.
+ * <p>
+ * The {@code OFFSET_SECONDS} field will typically be ignored.
+ * The offset of a {@code ZonedDateTime} is controlled primarily by the time-zone.
+ * As such, changing the offset does not generally make sense, because there is only
+ * one valid offset for the local date-time and zone.
+ * If the zoned date-time is in a daylight savings overlap, then the offset is used
+ * to switch between the two valid offsets. In all other cases, the offset is ignored.
+ * If the new offset value is outside the valid range then a {@code DateTimeException} will be thrown.
+ * <p>
+ * The other {@link #isSupported(TemporalField) supported fields} will behave as per
+ * the matching method on {@link LocalDateTime#with(TemporalField, long) LocalDateTime}.
+ * The zone is not part of the calculation and will be unchanged.
+ * When converting back to {@code ZonedDateTime}, if the local date-time is in an overlap,
+ * then the offset will be retained if possible, otherwise the earlier offset will be used.
+ * If in a gap, the local date-time will be adjusted forward by the length of the gap.
+ * <p>
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.adjustInto(Temporal, long)}
+ * passing {@code this} as the argument. In this case, the field determines
+ * whether and how to adjust the instant.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param field the field to set in the result, not null
+ * @param newValue the new value of the field in the result
+ * @return a {@code ZonedDateTime} based on {@code this} with the specified field set, not null
+ * @throws DateTimeException if the field cannot be set
+ * @throws UnsupportedTemporalTypeException if the field is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public ZonedDateTime with(TemporalField field, long newValue) {
+ if (field instanceof ChronoField) {
+ ChronoField f = (ChronoField) field;
+ switch (f) {
+ case INSTANT_SECONDS:
+ return create(newValue, getNano(), zone);
+ case OFFSET_SECONDS:
+ ZoneOffset offset = ZoneOffset.ofTotalSeconds(f.checkValidIntValue(newValue));
+ return resolveOffset(offset);
+ }
+ return resolveLocal(dateTime.with(field, newValue));
+ }
+ return field.adjustInto(this, newValue);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this {@code ZonedDateTime} with the year altered.
+ * <p>
+ * This operates on the local time-line,
+ * {@link LocalDateTime#withYear(int) changing the year} of the local date-time.
+ * This is then converted back to a {@code ZonedDateTime}, using the zone ID
+ * to obtain the offset.
+ * <p>
+ * When converting back to {@code ZonedDateTime}, if the local date-time is in an overlap,
+ * then the offset will be retained if possible, otherwise the earlier offset will be used.
+ * If in a gap, the local date-time will be adjusted forward by the length of the gap.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param year the year to set in the result, from MIN_YEAR to MAX_YEAR
+ * @return a {@code ZonedDateTime} based on this date-time with the requested year, not null
+ * @throws DateTimeException if the year value is invalid
+ */
+ public ZonedDateTime withYear(int year) {
+ return resolveLocal(dateTime.withYear(year));
+ }
+
+ /**
+ * Returns a copy of this {@code ZonedDateTime} with the month-of-year altered.
+ * <p>
+ * This operates on the local time-line,
+ * {@link LocalDateTime#withMonth(int) changing the month} of the local date-time.
+ * This is then converted back to a {@code ZonedDateTime}, using the zone ID
+ * to obtain the offset.
+ * <p>
+ * When converting back to {@code ZonedDateTime}, if the local date-time is in an overlap,
+ * then the offset will be retained if possible, otherwise the earlier offset will be used.
+ * If in a gap, the local date-time will be adjusted forward by the length of the gap.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param month the month-of-year to set in the result, from 1 (January) to 12 (December)
+ * @return a {@code ZonedDateTime} based on this date-time with the requested month, not null
+ * @throws DateTimeException if the month-of-year value is invalid
+ */
+ public ZonedDateTime withMonth(int month) {
+ return resolveLocal(dateTime.withMonth(month));
+ }
+
+ /**
+ * Returns a copy of this {@code ZonedDateTime} with the day-of-month altered.
+ * <p>
+ * This operates on the local time-line,
+ * {@link LocalDateTime#withDayOfMonth(int) changing the day-of-month} of the local date-time.
+ * This is then converted back to a {@code ZonedDateTime}, using the zone ID
+ * to obtain the offset.
+ * <p>
+ * When converting back to {@code ZonedDateTime}, if the local date-time is in an overlap,
+ * then the offset will be retained if possible, otherwise the earlier offset will be used.
+ * If in a gap, the local date-time will be adjusted forward by the length of the gap.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param dayOfMonth the day-of-month to set in the result, from 1 to 28-31
+ * @return a {@code ZonedDateTime} based on this date-time with the requested day, not null
+ * @throws DateTimeException if the day-of-month value is invalid,
+ * or if the day-of-month is invalid for the month-year
+ */
+ public ZonedDateTime withDayOfMonth(int dayOfMonth) {
+ return resolveLocal(dateTime.withDayOfMonth(dayOfMonth));
+ }
+
+ /**
+ * Returns a copy of this {@code ZonedDateTime} with the day-of-year altered.
+ * <p>
+ * This operates on the local time-line,
+ * {@link LocalDateTime#withDayOfYear(int) changing the day-of-year} of the local date-time.
+ * This is then converted back to a {@code ZonedDateTime}, using the zone ID
+ * to obtain the offset.
+ * <p>
+ * When converting back to {@code ZonedDateTime}, if the local date-time is in an overlap,
+ * then the offset will be retained if possible, otherwise the earlier offset will be used.
+ * If in a gap, the local date-time will be adjusted forward by the length of the gap.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param dayOfYear the day-of-year to set in the result, from 1 to 365-366
+ * @return a {@code ZonedDateTime} based on this date with the requested day, not null
+ * @throws DateTimeException if the day-of-year value is invalid,
+ * or if the day-of-year is invalid for the year
+ */
+ public ZonedDateTime withDayOfYear(int dayOfYear) {
+ return resolveLocal(dateTime.withDayOfYear(dayOfYear));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this {@code ZonedDateTime} with the hour-of-day altered.
+ * <p>
+ * This operates on the local time-line,
+ * {@linkplain LocalDateTime#withHour(int) changing the time} of the local date-time.
+ * This is then converted back to a {@code ZonedDateTime}, using the zone ID
+ * to obtain the offset.
+ * <p>
+ * When converting back to {@code ZonedDateTime}, if the local date-time is in an overlap,
+ * then the offset will be retained if possible, otherwise the earlier offset will be used.
+ * If in a gap, the local date-time will be adjusted forward by the length of the gap.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param hour the hour-of-day to set in the result, from 0 to 23
+ * @return a {@code ZonedDateTime} based on this date-time with the requested hour, not null
+ * @throws DateTimeException if the hour value is invalid
+ */
+ public ZonedDateTime withHour(int hour) {
+ return resolveLocal(dateTime.withHour(hour));
+ }
+
+ /**
+ * Returns a copy of this {@code ZonedDateTime} with the minute-of-hour altered.
+ * <p>
+ * This operates on the local time-line,
+ * {@linkplain LocalDateTime#withMinute(int) changing the time} of the local date-time.
+ * This is then converted back to a {@code ZonedDateTime}, using the zone ID
+ * to obtain the offset.
+ * <p>
+ * When converting back to {@code ZonedDateTime}, if the local date-time is in an overlap,
+ * then the offset will be retained if possible, otherwise the earlier offset will be used.
+ * If in a gap, the local date-time will be adjusted forward by the length of the gap.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param minute the minute-of-hour to set in the result, from 0 to 59
+ * @return a {@code ZonedDateTime} based on this date-time with the requested minute, not null
+ * @throws DateTimeException if the minute value is invalid
+ */
+ public ZonedDateTime withMinute(int minute) {
+ return resolveLocal(dateTime.withMinute(minute));
+ }
+
+ /**
+ * Returns a copy of this {@code ZonedDateTime} with the second-of-minute altered.
+ * <p>
+ * This operates on the local time-line,
+ * {@linkplain LocalDateTime#withSecond(int) changing the time} of the local date-time.
+ * This is then converted back to a {@code ZonedDateTime}, using the zone ID
+ * to obtain the offset.
+ * <p>
+ * When converting back to {@code ZonedDateTime}, if the local date-time is in an overlap,
+ * then the offset will be retained if possible, otherwise the earlier offset will be used.
+ * If in a gap, the local date-time will be adjusted forward by the length of the gap.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param second the second-of-minute to set in the result, from 0 to 59
+ * @return a {@code ZonedDateTime} based on this date-time with the requested second, not null
+ * @throws DateTimeException if the second value is invalid
+ */
+ public ZonedDateTime withSecond(int second) {
+ return resolveLocal(dateTime.withSecond(second));
+ }
+
+ /**
+ * Returns a copy of this {@code ZonedDateTime} with the nano-of-second altered.
+ * <p>
+ * This operates on the local time-line,
+ * {@linkplain LocalDateTime#withNano(int) changing the time} of the local date-time.
+ * This is then converted back to a {@code ZonedDateTime}, using the zone ID
+ * to obtain the offset.
+ * <p>
+ * When converting back to {@code ZonedDateTime}, if the local date-time is in an overlap,
+ * then the offset will be retained if possible, otherwise the earlier offset will be used.
+ * If in a gap, the local date-time will be adjusted forward by the length of the gap.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param nanoOfSecond the nano-of-second to set in the result, from 0 to 999,999,999
+ * @return a {@code ZonedDateTime} based on this date-time with the requested nanosecond, not null
+ * @throws DateTimeException if the nano value is invalid
+ */
+ public ZonedDateTime withNano(int nanoOfSecond) {
+ return resolveLocal(dateTime.withNano(nanoOfSecond));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this {@code ZonedDateTime} with the time truncated.
+ * <p>
+ * Truncation returns a copy of the original date-time with fields
+ * smaller than the specified unit set to zero.
+ * For example, truncating with the {@link ChronoUnit#MINUTES minutes} unit
+ * will set the second-of-minute and nano-of-second field to zero.
+ * <p>
+ * The unit must have a {@linkplain TemporalUnit#getDuration() duration}
+ * that divides into the length of a standard day without remainder.
+ * This includes all supplied time units on {@link ChronoUnit} and
+ * {@link ChronoUnit#DAYS DAYS}. Other units throw an exception.
+ * <p>
+ * This operates on the local time-line,
+ * {@link LocalDateTime#truncatedTo(TemporalUnit) truncating}
+ * the underlying local date-time. This is then converted back to a
+ * {@code ZonedDateTime}, using the zone ID to obtain the offset.
+ * <p>
+ * When converting back to {@code ZonedDateTime}, if the local date-time is in an overlap,
+ * then the offset will be retained if possible, otherwise the earlier offset will be used.
+ * If in a gap, the local date-time will be adjusted forward by the length of the gap.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param unit the unit to truncate to, not null
+ * @return a {@code ZonedDateTime} based on this date-time with the time truncated, not null
+ * @throws DateTimeException if unable to truncate
+ * @throws UnsupportedTemporalTypeException if the unit is not supported
+ */
+ public ZonedDateTime truncatedTo(TemporalUnit unit) {
+ return resolveLocal(dateTime.truncatedTo(unit));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this date-time with the specified amount added.
+ * <p>
+ * This returns a {@code ZonedDateTime}, based on this one, with the specified amount added.
+ * The amount is typically {@link Period} or {@link Duration} but may be
+ * any other type implementing the {@link TemporalAmount} interface.
+ * <p>
+ * The calculation is delegated to the amount object by calling
+ * {@link TemporalAmount#addTo(Temporal)}. The amount implementation is free
+ * to implement the addition in any way it wishes, however it typically
+ * calls back to {@link #plus(long, TemporalUnit)}. Consult the documentation
+ * of the amount implementation to determine if it can be successfully added.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param amountToAdd the amount to add, not null
+ * @return a {@code ZonedDateTime} based on this date-time with the addition made, not null
+ * @throws DateTimeException if the addition cannot be made
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public ZonedDateTime plus(TemporalAmount amountToAdd) {
+ if (amountToAdd instanceof Period) {
+ Period periodToAdd = (Period) amountToAdd;
+ return resolveLocal(dateTime.plus(periodToAdd));
+ }
+ Objects.requireNonNull(amountToAdd, "amountToAdd");
+ return (ZonedDateTime) amountToAdd.addTo(this);
+ }
+
+ /**
+ * Returns a copy of this date-time with the specified amount added.
+ * <p>
+ * This returns a {@code ZonedDateTime}, based on this one, with the amount
+ * in terms of the unit added. If it is not possible to add the amount, because the
+ * unit is not supported or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoUnit} then the addition is implemented here.
+ * The zone is not part of the calculation and will be unchanged in the result.
+ * The calculation for date and time units differ.
+ * <p>
+ * Date units operate on the local time-line.
+ * The period is first added to the local date-time, then converted back
+ * to a zoned date-time using the zone ID.
+ * The conversion uses {@link #ofLocal(LocalDateTime, ZoneId, ZoneOffset)}
+ * with the offset before the addition.
+ * <p>
+ * Time units operate on the instant time-line.
+ * The period is first added to the local date-time, then converted back to
+ * a zoned date-time using the zone ID.
+ * The conversion uses {@link #ofInstant(LocalDateTime, ZoneOffset, ZoneId)}
+ * with the offset before the addition.
+ * <p>
+ * If the field is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.addTo(Temporal, long)}
+ * passing {@code this} as the argument. In this case, the unit determines
+ * whether and how to perform the addition.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param amountToAdd the amount of the unit to add to the result, may be negative
+ * @param unit the unit of the amount to add, not null
+ * @return a {@code ZonedDateTime} based on this date-time with the specified amount added, not null
+ * @throws DateTimeException if the addition cannot be made
+ * @throws UnsupportedTemporalTypeException if the unit is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public ZonedDateTime plus(long amountToAdd, TemporalUnit unit) {
+ if (unit instanceof ChronoUnit) {
+ if (unit.isDateBased()) {
+ return resolveLocal(dateTime.plus(amountToAdd, unit));
+ } else {
+ return resolveInstant(dateTime.plus(amountToAdd, unit));
+ }
+ }
+ return unit.addTo(this, amountToAdd);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this {@code ZonedDateTime} with the specified number of years added.
+ * <p>
+ * This operates on the local time-line,
+ * {@link LocalDateTime#plusYears(long) adding years} to the local date-time.
+ * This is then converted back to a {@code ZonedDateTime}, using the zone ID
+ * to obtain the offset.
+ * <p>
+ * When converting back to {@code ZonedDateTime}, if the local date-time is in an overlap,
+ * then the offset will be retained if possible, otherwise the earlier offset will be used.
+ * If in a gap, the local date-time will be adjusted forward by the length of the gap.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param years the years to add, may be negative
+ * @return a {@code ZonedDateTime} based on this date-time with the years added, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public ZonedDateTime plusYears(long years) {
+ return resolveLocal(dateTime.plusYears(years));
+ }
+
+ /**
+ * Returns a copy of this {@code ZonedDateTime} with the specified number of months added.
+ * <p>
+ * This operates on the local time-line,
+ * {@link LocalDateTime#plusMonths(long) adding months} to the local date-time.
+ * This is then converted back to a {@code ZonedDateTime}, using the zone ID
+ * to obtain the offset.
+ * <p>
+ * When converting back to {@code ZonedDateTime}, if the local date-time is in an overlap,
+ * then the offset will be retained if possible, otherwise the earlier offset will be used.
+ * If in a gap, the local date-time will be adjusted forward by the length of the gap.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param months the months to add, may be negative
+ * @return a {@code ZonedDateTime} based on this date-time with the months added, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public ZonedDateTime plusMonths(long months) {
+ return resolveLocal(dateTime.plusMonths(months));
+ }
+
+ /**
+ * Returns a copy of this {@code ZonedDateTime} with the specified number of weeks added.
+ * <p>
+ * This operates on the local time-line,
+ * {@link LocalDateTime#plusWeeks(long) adding weeks} to the local date-time.
+ * This is then converted back to a {@code ZonedDateTime}, using the zone ID
+ * to obtain the offset.
+ * <p>
+ * When converting back to {@code ZonedDateTime}, if the local date-time is in an overlap,
+ * then the offset will be retained if possible, otherwise the earlier offset will be used.
+ * If in a gap, the local date-time will be adjusted forward by the length of the gap.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param weeks the weeks to add, may be negative
+ * @return a {@code ZonedDateTime} based on this date-time with the weeks added, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public ZonedDateTime plusWeeks(long weeks) {
+ return resolveLocal(dateTime.plusWeeks(weeks));
+ }
+
+ /**
+ * Returns a copy of this {@code ZonedDateTime} with the specified number of days added.
+ * <p>
+ * This operates on the local time-line,
+ * {@link LocalDateTime#plusDays(long) adding days} to the local date-time.
+ * This is then converted back to a {@code ZonedDateTime}, using the zone ID
+ * to obtain the offset.
+ * <p>
+ * When converting back to {@code ZonedDateTime}, if the local date-time is in an overlap,
+ * then the offset will be retained if possible, otherwise the earlier offset will be used.
+ * If in a gap, the local date-time will be adjusted forward by the length of the gap.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param days the days to add, may be negative
+ * @return a {@code ZonedDateTime} based on this date-time with the days added, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public ZonedDateTime plusDays(long days) {
+ return resolveLocal(dateTime.plusDays(days));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this {@code ZonedDateTime} with the specified number of hours added.
+ * <p>
+ * This operates on the instant time-line, such that adding one hour will
+ * always be a duration of one hour later.
+ * This may cause the local date-time to change by an amount other than one hour.
+ * Note that this is a different approach to that used by days, months and years,
+ * thus adding one day is not the same as adding 24 hours.
+ * <p>
+ * For example, consider a time-zone where the spring DST cutover means that the
+ * local times 01:00 to 01:59 occur twice changing from offset +02:00 to +01:00.
+ * <ul>
+ * <li>Adding one hour to 00:30+02:00 will result in 01:30+02:00
+ * <li>Adding one hour to 01:30+02:00 will result in 01:30+01:00
+ * <li>Adding one hour to 01:30+01:00 will result in 02:30+01:00
+ * <li>Adding three hours to 00:30+02:00 will result in 02:30+01:00
+ * </ul>
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param hours the hours to add, may be negative
+ * @return a {@code ZonedDateTime} based on this date-time with the hours added, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public ZonedDateTime plusHours(long hours) {
+ return resolveInstant(dateTime.plusHours(hours));
+ }
+
+ /**
+ * Returns a copy of this {@code ZonedDateTime} with the specified number of minutes added.
+ * <p>
+ * This operates on the instant time-line, such that adding one minute will
+ * always be a duration of one minute later.
+ * This may cause the local date-time to change by an amount other than one minute.
+ * Note that this is a different approach to that used by days, months and years.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param minutes the minutes to add, may be negative
+ * @return a {@code ZonedDateTime} based on this date-time with the minutes added, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public ZonedDateTime plusMinutes(long minutes) {
+ return resolveInstant(dateTime.plusMinutes(minutes));
+ }
+
+ /**
+ * Returns a copy of this {@code ZonedDateTime} with the specified number of seconds added.
+ * <p>
+ * This operates on the instant time-line, such that adding one second will
+ * always be a duration of one second later.
+ * This may cause the local date-time to change by an amount other than one second.
+ * Note that this is a different approach to that used by days, months and years.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param seconds the seconds to add, may be negative
+ * @return a {@code ZonedDateTime} based on this date-time with the seconds added, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public ZonedDateTime plusSeconds(long seconds) {
+ return resolveInstant(dateTime.plusSeconds(seconds));
+ }
+
+ /**
+ * Returns a copy of this {@code ZonedDateTime} with the specified number of nanoseconds added.
+ * <p>
+ * This operates on the instant time-line, such that adding one nano will
+ * always be a duration of one nano later.
+ * This may cause the local date-time to change by an amount other than one nano.
+ * Note that this is a different approach to that used by days, months and years.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param nanos the nanos to add, may be negative
+ * @return a {@code ZonedDateTime} based on this date-time with the nanoseconds added, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public ZonedDateTime plusNanos(long nanos) {
+ return resolveInstant(dateTime.plusNanos(nanos));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this date-time with the specified amount subtracted.
+ * <p>
+ * This returns a {@code ZonedDateTime}, based on this one, with the specified amount subtracted.
+ * The amount is typically {@link Period} or {@link Duration} but may be
+ * any other type implementing the {@link TemporalAmount} interface.
+ * <p>
+ * The calculation is delegated to the amount object by calling
+ * {@link TemporalAmount#subtractFrom(Temporal)}. The amount implementation is free
+ * to implement the subtraction in any way it wishes, however it typically
+ * calls back to {@link #minus(long, TemporalUnit)}. Consult the documentation
+ * of the amount implementation to determine if it can be successfully subtracted.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param amountToSubtract the amount to subtract, not null
+ * @return a {@code ZonedDateTime} based on this date-time with the subtraction made, not null
+ * @throws DateTimeException if the subtraction cannot be made
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public ZonedDateTime minus(TemporalAmount amountToSubtract) {
+ if (amountToSubtract instanceof Period) {
+ Period periodToSubtract = (Period) amountToSubtract;
+ return resolveLocal(dateTime.minus(periodToSubtract));
+ }
+ Objects.requireNonNull(amountToSubtract, "amountToSubtract");
+ return (ZonedDateTime) amountToSubtract.subtractFrom(this);
+ }
+
+ /**
+ * Returns a copy of this date-time with the specified amount subtracted.
+ * <p>
+ * This returns a {@code ZonedDateTime}, based on this one, with the amount
+ * in terms of the unit subtracted. If it is not possible to subtract the amount,
+ * because the unit is not supported or for some other reason, an exception is thrown.
+ * <p>
+ * The calculation for date and time units differ.
+ * <p>
+ * Date units operate on the local time-line.
+ * The period is first subtracted from the local date-time, then converted back
+ * to a zoned date-time using the zone ID.
+ * The conversion uses {@link #ofLocal(LocalDateTime, ZoneId, ZoneOffset)}
+ * with the offset before the subtraction.
+ * <p>
+ * Time units operate on the instant time-line.
+ * The period is first subtracted from the local date-time, then converted back to
+ * a zoned date-time using the zone ID.
+ * The conversion uses {@link #ofInstant(LocalDateTime, ZoneOffset, ZoneId)}
+ * with the offset before the subtraction.
+ * <p>
+ * This method is equivalent to {@link #plus(long, TemporalUnit)} with the amount negated.
+ * See that method for a full description of how addition, and thus subtraction, works.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param amountToSubtract the amount of the unit to subtract from the result, may be negative
+ * @param unit the unit of the amount to subtract, not null
+ * @return a {@code ZonedDateTime} based on this date-time with the specified amount subtracted, not null
+ * @throws DateTimeException if the subtraction cannot be made
+ * @throws UnsupportedTemporalTypeException if the unit is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public ZonedDateTime minus(long amountToSubtract, TemporalUnit unit) {
+ return (amountToSubtract == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this {@code ZonedDateTime} with the specified number of years subtracted.
+ * <p>
+ * This operates on the local time-line,
+ * {@link LocalDateTime#minusYears(long) subtracting years} to the local date-time.
+ * This is then converted back to a {@code ZonedDateTime}, using the zone ID
+ * to obtain the offset.
+ * <p>
+ * When converting back to {@code ZonedDateTime}, if the local date-time is in an overlap,
+ * then the offset will be retained if possible, otherwise the earlier offset will be used.
+ * If in a gap, the local date-time will be adjusted forward by the length of the gap.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param years the years to subtract, may be negative
+ * @return a {@code ZonedDateTime} based on this date-time with the years subtracted, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public ZonedDateTime minusYears(long years) {
+ return (years == Long.MIN_VALUE ? plusYears(Long.MAX_VALUE).plusYears(1) : plusYears(-years));
+ }
+
+ /**
+ * Returns a copy of this {@code ZonedDateTime} with the specified number of months subtracted.
+ * <p>
+ * This operates on the local time-line,
+ * {@link LocalDateTime#minusMonths(long) subtracting months} to the local date-time.
+ * This is then converted back to a {@code ZonedDateTime}, using the zone ID
+ * to obtain the offset.
+ * <p>
+ * When converting back to {@code ZonedDateTime}, if the local date-time is in an overlap,
+ * then the offset will be retained if possible, otherwise the earlier offset will be used.
+ * If in a gap, the local date-time will be adjusted forward by the length of the gap.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param months the months to subtract, may be negative
+ * @return a {@code ZonedDateTime} based on this date-time with the months subtracted, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public ZonedDateTime minusMonths(long months) {
+ return (months == Long.MIN_VALUE ? plusMonths(Long.MAX_VALUE).plusMonths(1) : plusMonths(-months));
+ }
+
+ /**
+ * Returns a copy of this {@code ZonedDateTime} with the specified number of weeks subtracted.
+ * <p>
+ * This operates on the local time-line,
+ * {@link LocalDateTime#minusWeeks(long) subtracting weeks} to the local date-time.
+ * This is then converted back to a {@code ZonedDateTime}, using the zone ID
+ * to obtain the offset.
+ * <p>
+ * When converting back to {@code ZonedDateTime}, if the local date-time is in an overlap,
+ * then the offset will be retained if possible, otherwise the earlier offset will be used.
+ * If in a gap, the local date-time will be adjusted forward by the length of the gap.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param weeks the weeks to subtract, may be negative
+ * @return a {@code ZonedDateTime} based on this date-time with the weeks subtracted, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public ZonedDateTime minusWeeks(long weeks) {
+ return (weeks == Long.MIN_VALUE ? plusWeeks(Long.MAX_VALUE).plusWeeks(1) : plusWeeks(-weeks));
+ }
+
+ /**
+ * Returns a copy of this {@code ZonedDateTime} with the specified number of days subtracted.
+ * <p>
+ * This operates on the local time-line,
+ * {@link LocalDateTime#minusDays(long) subtracting days} to the local date-time.
+ * This is then converted back to a {@code ZonedDateTime}, using the zone ID
+ * to obtain the offset.
+ * <p>
+ * When converting back to {@code ZonedDateTime}, if the local date-time is in an overlap,
+ * then the offset will be retained if possible, otherwise the earlier offset will be used.
+ * If in a gap, the local date-time will be adjusted forward by the length of the gap.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param days the days to subtract, may be negative
+ * @return a {@code ZonedDateTime} based on this date-time with the days subtracted, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public ZonedDateTime minusDays(long days) {
+ return (days == Long.MIN_VALUE ? plusDays(Long.MAX_VALUE).plusDays(1) : plusDays(-days));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this {@code ZonedDateTime} with the specified number of hours subtracted.
+ * <p>
+ * This operates on the instant time-line, such that subtracting one hour will
+ * always be a duration of one hour earlier.
+ * This may cause the local date-time to change by an amount other than one hour.
+ * Note that this is a different approach to that used by days, months and years,
+ * thus subtracting one day is not the same as adding 24 hours.
+ * <p>
+ * For example, consider a time-zone where the spring DST cutover means that the
+ * local times 01:00 to 01:59 occur twice changing from offset +02:00 to +01:00.
+ * <ul>
+ * <li>Subtracting one hour from 02:30+01:00 will result in 01:30+02:00
+ * <li>Subtracting one hour from 01:30+01:00 will result in 01:30+02:00
+ * <li>Subtracting one hour from 01:30+02:00 will result in 00:30+01:00
+ * <li>Subtracting three hours from 02:30+01:00 will result in 00:30+02:00
+ * </ul>
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param hours the hours to subtract, may be negative
+ * @return a {@code ZonedDateTime} based on this date-time with the hours subtracted, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public ZonedDateTime minusHours(long hours) {
+ return (hours == Long.MIN_VALUE ? plusHours(Long.MAX_VALUE).plusHours(1) : plusHours(-hours));
+ }
+
+ /**
+ * Returns a copy of this {@code ZonedDateTime} with the specified number of minutes subtracted.
+ * <p>
+ * This operates on the instant time-line, such that subtracting one minute will
+ * always be a duration of one minute earlier.
+ * This may cause the local date-time to change by an amount other than one minute.
+ * Note that this is a different approach to that used by days, months and years.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param minutes the minutes to subtract, may be negative
+ * @return a {@code ZonedDateTime} based on this date-time with the minutes subtracted, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public ZonedDateTime minusMinutes(long minutes) {
+ return (minutes == Long.MIN_VALUE ? plusMinutes(Long.MAX_VALUE).plusMinutes(1) : plusMinutes(-minutes));
+ }
+
+ /**
+ * Returns a copy of this {@code ZonedDateTime} with the specified number of seconds subtracted.
+ * <p>
+ * This operates on the instant time-line, such that subtracting one second will
+ * always be a duration of one second earlier.
+ * This may cause the local date-time to change by an amount other than one second.
+ * Note that this is a different approach to that used by days, months and years.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param seconds the seconds to subtract, may be negative
+ * @return a {@code ZonedDateTime} based on this date-time with the seconds subtracted, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public ZonedDateTime minusSeconds(long seconds) {
+ return (seconds == Long.MIN_VALUE ? plusSeconds(Long.MAX_VALUE).plusSeconds(1) : plusSeconds(-seconds));
+ }
+
+ /**
+ * Returns a copy of this {@code ZonedDateTime} with the specified number of nanoseconds subtracted.
+ * <p>
+ * This operates on the instant time-line, such that subtracting one nano will
+ * always be a duration of one nano earlier.
+ * This may cause the local date-time to change by an amount other than one nano.
+ * Note that this is a different approach to that used by days, months and years.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param nanos the nanos to subtract, may be negative
+ * @return a {@code ZonedDateTime} based on this date-time with the nanoseconds subtracted, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ public ZonedDateTime minusNanos(long nanos) {
+ return (nanos == Long.MIN_VALUE ? plusNanos(Long.MAX_VALUE).plusNanos(1) : plusNanos(-nanos));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Queries this date-time using the specified query.
+ * <p>
+ * This queries this date-time using the specified query strategy object.
+ * The {@code TemporalQuery} object defines the logic to be used to
+ * obtain the result. Read the documentation of the query to understand
+ * what the result of this method will be.
+ * <p>
+ * The result of this method is obtained by invoking the
+ * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the
+ * specified query passing {@code this} as the argument.
+ *
+ * @param <R> the type of the result
+ * @param query the query to invoke, not null
+ * @return the query result, null may be returned (defined by the query)
+ * @throws DateTimeException if unable to query (defined by the query)
+ * @throws ArithmeticException if numeric overflow occurs (defined by the query)
+ */
+ @SuppressWarnings("unchecked")
+ @Override // override for Javadoc
+ public <R> R query(TemporalQuery<R> query) {
+ if (query == TemporalQueries.localDate()) {
+ return (R) toLocalDate();
+ }
+ return ChronoZonedDateTime.super.query(query);
+ }
+
+ /**
+ * Calculates the amount of time until another date-time in terms of the specified unit.
+ * <p>
+ * This calculates the amount of time between two {@code ZonedDateTime}
+ * objects in terms of a single {@code TemporalUnit}.
+ * The start and end points are {@code this} and the specified date-time.
+ * The result will be negative if the end is before the start.
+ * For example, the amount in days between two date-times can be calculated
+ * using {@code startDateTime.until(endDateTime, DAYS)}.
+ * <p>
+ * The {@code Temporal} passed to this method is converted to a
+ * {@code ZonedDateTime} using {@link #from(TemporalAccessor)}.
+ * If the time-zone differs between the two zoned date-times, the specified
+ * end date-time is normalized to have the same zone as this date-time.
+ * <p>
+ * The calculation returns a whole number, representing the number of
+ * complete units between the two date-times.
+ * For example, the amount in months between 2012-06-15T00:00Z and 2012-08-14T23:59Z
+ * will only be one month as it is one minute short of two months.
+ * <p>
+ * There are two equivalent ways of using this method.
+ * The first is to invoke this method.
+ * The second is to use {@link TemporalUnit#between(Temporal, Temporal)}:
+ * <pre>
+ * // these two lines are equivalent
+ * amount = start.until(end, MONTHS);
+ * amount = MONTHS.between(start, end);
+ * </pre>
+ * The choice should be made based on which makes the code more readable.
+ * <p>
+ * The calculation is implemented in this method for {@link ChronoUnit}.
+ * The units {@code NANOS}, {@code MICROS}, {@code MILLIS}, {@code SECONDS},
+ * {@code MINUTES}, {@code HOURS} and {@code HALF_DAYS}, {@code DAYS},
+ * {@code WEEKS}, {@code MONTHS}, {@code YEARS}, {@code DECADES},
+ * {@code CENTURIES}, {@code MILLENNIA} and {@code ERAS} are supported.
+ * Other {@code ChronoUnit} values will throw an exception.
+ * <p>
+ * The calculation for date and time units differ.
+ * <p>
+ * Date units operate on the local time-line, using the local date-time.
+ * For example, the period from noon on day 1 to noon the following day
+ * in days will always be counted as exactly one day, irrespective of whether
+ * there was a daylight savings change or not.
+ * <p>
+ * Time units operate on the instant time-line.
+ * The calculation effectively converts both zoned date-times to instants
+ * and then calculates the period between the instants.
+ * For example, the period from noon on day 1 to noon the following day
+ * in hours may be 23, 24 or 25 hours (or some other amount) depending on
+ * whether there was a daylight savings change or not.
+ * <p>
+ * If the unit is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.between(Temporal, Temporal)}
+ * passing {@code this} as the first argument and the converted input temporal
+ * as the second argument.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param endExclusive the end date, exclusive, which is converted to a {@code ZonedDateTime}, not null
+ * @param unit the unit to measure the amount in, not null
+ * @return the amount of time between this date-time and the end date-time
+ * @throws DateTimeException if the amount cannot be calculated, or the end
+ * temporal cannot be converted to a {@code ZonedDateTime}
+ * @throws UnsupportedTemporalTypeException if the unit is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ public long until(Temporal endExclusive, TemporalUnit unit) {
+ ZonedDateTime end = ZonedDateTime.from(endExclusive);
+ if (unit instanceof ChronoUnit) {
+ end = end.withZoneSameInstant(zone);
+ if (unit.isDateBased()) {
+ return dateTime.until(end.dateTime, unit);
+ } else {
+ return toOffsetDateTime().until(end.toOffsetDateTime(), unit);
+ }
+ }
+ return unit.between(this, end);
+ }
+
+ /**
+ * Formats this date-time using the specified formatter.
+ * <p>
+ * This date-time will be passed to the formatter to produce a string.
+ *
+ * @param formatter the formatter to use, not null
+ * @return the formatted date-time string, not null
+ * @throws DateTimeException if an error occurs during printing
+ */
+ @Override // override for Javadoc and performance
+ public String format(DateTimeFormatter formatter) {
+ Objects.requireNonNull(formatter, "formatter");
+ return formatter.format(this);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Converts this date-time to an {@code OffsetDateTime}.
+ * <p>
+ * This creates an offset date-time using the local date-time and offset.
+ * The zone ID is ignored.
+ *
+ * @return an offset date-time representing the same local date-time and offset, not null
+ */
+ public OffsetDateTime toOffsetDateTime() {
+ return OffsetDateTime.of(dateTime, offset);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if this date-time is equal to another date-time.
+ * <p>
+ * The comparison is based on the offset date-time and the zone.
+ * Only objects of type {@code ZonedDateTime} are compared, other types return false.
+ *
+ * @param obj the object to check, null returns false
+ * @return true if this is equal to the other date-time
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof ZonedDateTime) {
+ ZonedDateTime other = (ZonedDateTime) obj;
+ return dateTime.equals(other.dateTime) &&
+ offset.equals(other.offset) &&
+ zone.equals(other.zone);
+ }
+ return false;
+ }
+
+ /**
+ * A hash code for this date-time.
+ *
+ * @return a suitable hash code
+ */
+ @Override
+ public int hashCode() {
+ return dateTime.hashCode() ^ offset.hashCode() ^ Integer.rotateLeft(zone.hashCode(), 3);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Outputs this date-time as a {@code String}, such as
+ * {@code 2007-12-03T10:15:30+01:00[Europe/Paris]}.
+ * <p>
+ * The format consists of the {@code LocalDateTime} followed by the {@code ZoneOffset}.
+ * If the {@code ZoneId} is not the same as the offset, then the ID is output.
+ * The output is compatible with ISO-8601 if the offset and ID are the same.
+ *
+ * @return a string representation of this date-time, not null
+ */
+ @Override // override for Javadoc
+ public String toString() {
+ String str = dateTime.toString() + offset.toString();
+ if (offset != zone) {
+ str += '[' + zone.toString() + ']';
+ }
+ return str;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Writes the object using a
+ * <a href="../../serialized-form.html#java.time.Ser">dedicated serialized form</a>.
+ * @serialData
+ * <pre>
+ * out.writeByte(6); // identifies a ZonedDateTime
+ * // the <a href="../../serialized-form.html#java.time.LocalDateTime">dateTime</a> excluding the one byte header
+ * // the <a href="../../serialized-form.html#java.time.ZoneOffset">offset</a> excluding the one byte header
+ * // the <a href="../../serialized-form.html#java.time.ZoneId">zone ID</a> excluding the one byte header
+ * </pre>
+ *
+ * @return the instance of {@code Ser}, not null
+ */
+ private Object writeReplace() {
+ return new Ser(Ser.ZONE_DATE_TIME_TYPE, this);
+ }
+
+ /**
+ * Defend against malicious streams.
+ *
+ * @param s the stream to read
+ * @throws InvalidObjectException always
+ */
+ private void readObject(ObjectInputStream s) throws InvalidObjectException {
+ throw new InvalidObjectException("Deserialization via serialization delegate");
+ }
+
+ void writeExternal(DataOutput out) throws IOException {
+ dateTime.writeExternal(out);
+ offset.writeExternal(out);
+ zone.write(out);
+ }
+
+ static ZonedDateTime readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+ LocalDateTime dateTime = LocalDateTime.readExternal(in);
+ ZoneOffset offset = ZoneOffset.readExternal(in);
+ ZoneId zone = (ZoneId) Ser.read(in);
+ return ZonedDateTime.ofLenient(dateTime, offset, zone);
+ }
+
+}
diff --git a/java/time/chrono/AbstractChronology.java b/java/time/chrono/AbstractChronology.java
new file mode 100644
index 0000000..c2e91d7
--- /dev/null
+++ b/java/time/chrono/AbstractChronology.java
@@ -0,0 +1,785 @@
+/*
+ * Copyright (c) 2012, 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) 2012, 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.ALIGNED_DAY_OF_WEEK_IN_MONTH;
+import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR;
+import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH;
+import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR;
+import static java.time.temporal.ChronoField.DAY_OF_MONTH;
+import static java.time.temporal.ChronoField.DAY_OF_WEEK;
+import static java.time.temporal.ChronoField.DAY_OF_YEAR;
+import static java.time.temporal.ChronoField.EPOCH_DAY;
+import static java.time.temporal.ChronoField.ERA;
+import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
+import static java.time.temporal.ChronoField.PROLEPTIC_MONTH;
+import static java.time.temporal.ChronoField.YEAR;
+import static java.time.temporal.ChronoField.YEAR_OF_ERA;
+import static java.time.temporal.ChronoUnit.DAYS;
+import static java.time.temporal.ChronoUnit.MONTHS;
+import static java.time.temporal.ChronoUnit.WEEKS;
+import static java.time.temporal.TemporalAdjusters.nextOrSame;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.time.DateTimeException;
+import java.time.DayOfWeek;
+import java.time.format.ResolverStyle;
+import java.time.temporal.ChronoField;
+import java.time.temporal.TemporalAdjusters;
+import java.time.temporal.TemporalField;
+import java.time.temporal.ValueRange;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+import java.util.ServiceLoader;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import sun.util.logging.PlatformLogger;
+
+/**
+ * An abstract implementation of a calendar system, used to organize and identify dates.
+ * <p>
+ * The main date and time API is built on the ISO calendar system.
+ * The chronology operates behind the scenes to represent the general concept of a calendar system.
+ * <p>
+ * See {@link Chronology} for more details.
+ *
+ * @implSpec
+ * This class is separated from the {@code Chronology} interface so that the static methods
+ * are not inherited. While {@code Chronology} can be implemented directly, it is strongly
+ * recommended to extend this abstract class instead.
+ * <p>
+ * This class 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 abstract class AbstractChronology implements Chronology {
+
+ /**
+ * ChronoLocalDate order constant.
+ */
+ static final Comparator<ChronoLocalDate> DATE_ORDER =
+ (Comparator<ChronoLocalDate> & Serializable) (date1, date2) -> {
+ return Long.compare(date1.toEpochDay(), date2.toEpochDay());
+ };
+ /**
+ * ChronoLocalDateTime order constant.
+ */
+ static final Comparator<ChronoLocalDateTime<? extends ChronoLocalDate>> DATE_TIME_ORDER =
+ (Comparator<ChronoLocalDateTime<? extends ChronoLocalDate>> & Serializable) (dateTime1, dateTime2) -> {
+ int cmp = Long.compare(dateTime1.toLocalDate().toEpochDay(), dateTime2.toLocalDate().toEpochDay());
+ if (cmp == 0) {
+ cmp = Long.compare(dateTime1.toLocalTime().toNanoOfDay(), dateTime2.toLocalTime().toNanoOfDay());
+ }
+ return cmp;
+ };
+ /**
+ * ChronoZonedDateTime order constant.
+ */
+ static final Comparator<ChronoZonedDateTime<?>> INSTANT_ORDER =
+ (Comparator<ChronoZonedDateTime<?>> & Serializable) (dateTime1, dateTime2) -> {
+ int cmp = Long.compare(dateTime1.toEpochSecond(), dateTime2.toEpochSecond());
+ if (cmp == 0) {
+ cmp = Long.compare(dateTime1.toLocalTime().getNano(), dateTime2.toLocalTime().getNano());
+ }
+ return cmp;
+ };
+
+ /**
+ * Map of available calendars by ID.
+ */
+ private static final ConcurrentHashMap<String, Chronology> CHRONOS_BY_ID = new ConcurrentHashMap<>();
+ /**
+ * Map of available calendars by calendar type.
+ */
+ private static final ConcurrentHashMap<String, Chronology> CHRONOS_BY_TYPE = new ConcurrentHashMap<>();
+
+ /**
+ * Register a Chronology by its ID and type for lookup by {@link #of(String)}.
+ * Chronologies must not be registered until they are completely constructed.
+ * Specifically, not in the constructor of Chronology.
+ *
+ * @param chrono the chronology to register; not null
+ * @return the already registered Chronology if any, may be null
+ */
+ static Chronology registerChrono(Chronology chrono) {
+ return registerChrono(chrono, chrono.getId());
+ }
+
+ /**
+ * Register a Chronology by ID and type for lookup by {@link #of(String)}.
+ * Chronos must not be registered until they are completely constructed.
+ * Specifically, not in the constructor of Chronology.
+ *
+ * @param chrono the chronology to register; not null
+ * @param id the ID to register the chronology; not null
+ * @return the already registered Chronology if any, may be null
+ */
+ static Chronology registerChrono(Chronology chrono, String id) {
+ Chronology prev = CHRONOS_BY_ID.putIfAbsent(id, chrono);
+ if (prev == null) {
+ String type = chrono.getCalendarType();
+ if (type != null) {
+ CHRONOS_BY_TYPE.putIfAbsent(type, chrono);
+ }
+ }
+ return prev;
+ }
+
+ /**
+ * Initialization of the maps from id and type to Chronology.
+ * The ServiceLoader is used to find and register any implementations
+ * of {@link java.time.chrono.AbstractChronology} found in the bootclass loader.
+ * The built-in chronologies are registered explicitly.
+ * Calendars configured via the Thread's context classloader are local
+ * to that thread and are ignored.
+ * <p>
+ * The initialization is done only once using the registration
+ * of the IsoChronology as the test and the final step.
+ * Multiple threads may perform the initialization concurrently.
+ * Only the first registration of each Chronology is retained by the
+ * ConcurrentHashMap.
+ * @return true if the cache was initialized
+ */
+ private static boolean initCache() {
+ if (CHRONOS_BY_ID.get("ISO") == null) {
+ // Initialization is incomplete
+
+ // Register built-in Chronologies
+ registerChrono(HijrahChronology.INSTANCE);
+ registerChrono(JapaneseChronology.INSTANCE);
+ registerChrono(MinguoChronology.INSTANCE);
+ registerChrono(ThaiBuddhistChronology.INSTANCE);
+
+ // Register Chronologies from the ServiceLoader
+ @SuppressWarnings("rawtypes")
+ ServiceLoader<AbstractChronology> loader = ServiceLoader.load(AbstractChronology.class, null);
+ for (AbstractChronology chrono : loader) {
+ String id = chrono.getId();
+ if (id.equals("ISO") || registerChrono(chrono) != null) {
+ // Log the attempt to replace an existing Chronology
+ PlatformLogger logger = PlatformLogger.getLogger("java.time.chrono");
+ logger.warning("Ignoring duplicate Chronology, from ServiceLoader configuration " + id);
+ }
+ }
+
+ // finally, register IsoChronology to mark initialization is complete
+ registerChrono(IsoChronology.INSTANCE);
+ return true;
+ }
+ return false;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code Chronology} from a locale.
+ * <p>
+ * See {@link Chronology#ofLocale(Locale)}.
+ *
+ * @param locale the locale to use to obtain the calendar system, not null
+ * @return the calendar system associated with the locale, not null
+ * @throws java.time.DateTimeException if the locale-specified calendar cannot be found
+ */
+ static Chronology ofLocale(Locale locale) {
+ Objects.requireNonNull(locale, "locale");
+ String type = locale.getUnicodeLocaleType("ca");
+ if (type == null || "iso".equals(type) || "iso8601".equals(type)) {
+ return IsoChronology.INSTANCE;
+ }
+ // Not pre-defined; lookup by the type
+ do {
+ Chronology chrono = CHRONOS_BY_TYPE.get(type);
+ if (chrono != null) {
+ return chrono;
+ }
+ // If not found, do the initialization (once) and repeat the lookup
+ } while (initCache());
+
+ // Look for a Chronology using ServiceLoader of the Thread's ContextClassLoader
+ // Application provided Chronologies must not be cached
+ @SuppressWarnings("rawtypes")
+ ServiceLoader<Chronology> loader = ServiceLoader.load(Chronology.class);
+ for (Chronology chrono : loader) {
+ if (type.equals(chrono.getCalendarType())) {
+ return chrono;
+ }
+ }
+ throw new DateTimeException("Unknown calendar system: " + type);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code Chronology} from a chronology ID or
+ * calendar system type.
+ * <p>
+ * See {@link Chronology#of(String)}.
+ *
+ * @param id the chronology ID or calendar system type, not null
+ * @return the chronology with the identifier requested, not null
+ * @throws java.time.DateTimeException if the chronology cannot be found
+ */
+ static Chronology of(String id) {
+ Objects.requireNonNull(id, "id");
+ do {
+ Chronology chrono = of0(id);
+ if (chrono != null) {
+ return chrono;
+ }
+ // If not found, do the initialization (once) and repeat the lookup
+ } while (initCache());
+
+ // Look for a Chronology using ServiceLoader of the Thread's ContextClassLoader
+ // Application provided Chronologies must not be cached
+ @SuppressWarnings("rawtypes")
+ ServiceLoader<Chronology> loader = ServiceLoader.load(Chronology.class);
+ for (Chronology chrono : loader) {
+ if (id.equals(chrono.getId()) || id.equals(chrono.getCalendarType())) {
+ return chrono;
+ }
+ }
+ throw new DateTimeException("Unknown chronology: " + id);
+ }
+
+ /**
+ * Obtains an instance of {@code Chronology} from a chronology ID or
+ * calendar system type.
+ *
+ * @param id the chronology ID or calendar system type, not null
+ * @return the chronology with the identifier requested, or {@code null} if not found
+ */
+ private static Chronology of0(String id) {
+ Chronology chrono = CHRONOS_BY_ID.get(id);
+ if (chrono == null) {
+ chrono = CHRONOS_BY_TYPE.get(id);
+ }
+ return chrono;
+ }
+
+ /**
+ * Returns the available chronologies.
+ * <p>
+ * Each returned {@code Chronology} is available for use in the system.
+ * The set of chronologies includes the system chronologies and
+ * any chronologies provided by the application via ServiceLoader
+ * configuration.
+ *
+ * @return the independent, modifiable set of the available chronology IDs, not null
+ */
+ static Set<Chronology> getAvailableChronologies() {
+ initCache(); // force initialization
+ HashSet<Chronology> chronos = new HashSet<>(CHRONOS_BY_ID.values());
+
+ /// Add in Chronologies from the ServiceLoader configuration
+ @SuppressWarnings("rawtypes")
+ ServiceLoader<Chronology> loader = ServiceLoader.load(Chronology.class);
+ for (Chronology chrono : loader) {
+ chronos.add(chrono);
+ }
+ return chronos;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Creates an instance.
+ */
+ protected AbstractChronology() {
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Resolves parsed {@code ChronoField} values into a date during parsing.
+ * <p>
+ * Most {@code TemporalField} implementations are resolved using the
+ * resolve method on the field. By contrast, the {@code ChronoField} class
+ * defines fields that only have meaning relative to the chronology.
+ * As such, {@code ChronoField} date fields are resolved here in the
+ * context of a specific chronology.
+ * <p>
+ * {@code ChronoField} instances are resolved by this method, which may
+ * be overridden in subclasses.
+ * <ul>
+ * <li>{@code EPOCH_DAY} - If present, this is converted to a date and
+ * all other date fields are then cross-checked against the date.
+ * <li>{@code PROLEPTIC_MONTH} - If present, then it is split into the
+ * {@code YEAR} and {@code MONTH_OF_YEAR}. If the mode is strict or smart
+ * then the field is validated.
+ * <li>{@code YEAR_OF_ERA} and {@code ERA} - If both are present, then they
+ * are combined to form a {@code YEAR}. In lenient mode, the {@code YEAR_OF_ERA}
+ * range is not validated, in smart and strict mode it is. The {@code ERA} is
+ * validated for range in all three modes. If only the {@code YEAR_OF_ERA} is
+ * present, and the mode is smart or lenient, then the last available era
+ * is assumed. In strict mode, no era is assumed and the {@code YEAR_OF_ERA} is
+ * left untouched. If only the {@code ERA} is present, then it is left untouched.
+ * <li>{@code YEAR}, {@code MONTH_OF_YEAR} and {@code DAY_OF_MONTH} -
+ * If all three are present, then they are combined to form a date.
+ * In all three modes, the {@code YEAR} is validated.
+ * If the mode is smart or strict, then the month and day are validated.
+ * If the mode is lenient, then the date is combined in a manner equivalent to
+ * creating a date on the first day of the first month in the requested year,
+ * then adding the difference in months, then the difference in days.
+ * If the mode is smart, and the day-of-month is greater than the maximum for
+ * the year-month, then the day-of-month is adjusted to the last day-of-month.
+ * If the mode is strict, then the three fields must form a valid date.
+ * <li>{@code YEAR} and {@code DAY_OF_YEAR} -
+ * If both are present, then they are combined to form a date.
+ * In all three modes, the {@code YEAR} is validated.
+ * If the mode is lenient, then the date is combined in a manner equivalent to
+ * creating a date on the first day of the requested year, then adding
+ * the difference in days.
+ * If the mode is smart or strict, then the two fields must form a valid date.
+ * <li>{@code YEAR}, {@code MONTH_OF_YEAR}, {@code ALIGNED_WEEK_OF_MONTH} and
+ * {@code ALIGNED_DAY_OF_WEEK_IN_MONTH} -
+ * If all four are present, then they are combined to form a date.
+ * In all three modes, the {@code YEAR} is validated.
+ * If the mode is lenient, then the date is combined in a manner equivalent to
+ * creating a date on the first day of the first month in the requested year, then adding
+ * the difference in months, then the difference in weeks, then in days.
+ * If the mode is smart or strict, then the all four fields are validated to
+ * their outer ranges. The date is then combined in a manner equivalent to
+ * creating a date on the first day of the requested year and month, then adding
+ * the amount in weeks and days to reach their values. If the mode is strict,
+ * the date is additionally validated to check that the day and week adjustment
+ * did not change the month.
+ * <li>{@code YEAR}, {@code MONTH_OF_YEAR}, {@code ALIGNED_WEEK_OF_MONTH} and
+ * {@code DAY_OF_WEEK} - If all four are present, then they are combined to
+ * form a date. The approach is the same as described above for
+ * years, months and weeks in {@code ALIGNED_DAY_OF_WEEK_IN_MONTH}.
+ * The day-of-week is adjusted as the next or same matching day-of-week once
+ * the years, months and weeks have been handled.
+ * <li>{@code YEAR}, {@code ALIGNED_WEEK_OF_YEAR} and {@code ALIGNED_DAY_OF_WEEK_IN_YEAR} -
+ * If all three are present, then they are combined to form a date.
+ * In all three modes, the {@code YEAR} is validated.
+ * If the mode is lenient, then the date is combined in a manner equivalent to
+ * creating a date on the first day of the requested year, then adding
+ * the difference in weeks, then in days.
+ * If the mode is smart or strict, then the all three fields are validated to
+ * their outer ranges. The date is then combined in a manner equivalent to
+ * creating a date on the first day of the requested year, then adding
+ * the amount in weeks and days to reach their values. If the mode is strict,
+ * the date is additionally validated to check that the day and week adjustment
+ * did not change the year.
+ * <li>{@code YEAR}, {@code ALIGNED_WEEK_OF_YEAR} and {@code DAY_OF_WEEK} -
+ * If all three are present, then they are combined to form a date.
+ * The approach is the same as described above for years and weeks in
+ * {@code ALIGNED_DAY_OF_WEEK_IN_YEAR}. The day-of-week is adjusted as the
+ * next or same matching day-of-week once the years and weeks have been handled.
+ * </ul>
+ * <p>
+ * The default implementation is suitable for most calendar systems.
+ * If {@link java.time.temporal.ChronoField#YEAR_OF_ERA} is found without an {@link java.time.temporal.ChronoField#ERA}
+ * then the last era in {@link #eras()} is used.
+ * The implementation assumes a 7 day week, that the first day-of-month
+ * has the value 1, that first day-of-year has the value 1, and that the
+ * first of the month and year always exists.
+ *
+ * @param fieldValues the map of fields to values, which can be updated, not null
+ * @param resolverStyle the requested type of resolve, not null
+ * @return the resolved date, null if insufficient information to create a date
+ * @throws java.time.DateTimeException if the date cannot be resolved, typically
+ * because of a conflict in the input data
+ */
+ @Override
+ public ChronoLocalDate resolveDate(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
+ // check epoch-day before inventing era
+ if (fieldValues.containsKey(EPOCH_DAY)) {
+ return dateEpochDay(fieldValues.remove(EPOCH_DAY));
+ }
+
+ // fix proleptic month before inventing era
+ resolveProlepticMonth(fieldValues, resolverStyle);
+
+ // invent era if necessary to resolve year-of-era
+ ChronoLocalDate resolved = resolveYearOfEra(fieldValues, resolverStyle);
+ if (resolved != null) {
+ return resolved;
+ }
+
+ // build date
+ if (fieldValues.containsKey(YEAR)) {
+ if (fieldValues.containsKey(MONTH_OF_YEAR)) {
+ if (fieldValues.containsKey(DAY_OF_MONTH)) {
+ return resolveYMD(fieldValues, resolverStyle);
+ }
+ if (fieldValues.containsKey(ALIGNED_WEEK_OF_MONTH)) {
+ if (fieldValues.containsKey(ALIGNED_DAY_OF_WEEK_IN_MONTH)) {
+ return resolveYMAA(fieldValues, resolverStyle);
+ }
+ if (fieldValues.containsKey(DAY_OF_WEEK)) {
+ return resolveYMAD(fieldValues, resolverStyle);
+ }
+ }
+ }
+ if (fieldValues.containsKey(DAY_OF_YEAR)) {
+ return resolveYD(fieldValues, resolverStyle);
+ }
+ if (fieldValues.containsKey(ALIGNED_WEEK_OF_YEAR)) {
+ if (fieldValues.containsKey(ALIGNED_DAY_OF_WEEK_IN_YEAR)) {
+ return resolveYAA(fieldValues, resolverStyle);
+ }
+ if (fieldValues.containsKey(DAY_OF_WEEK)) {
+ return resolveYAD(fieldValues, resolverStyle);
+ }
+ }
+ }
+ return null;
+ }
+
+ void resolveProlepticMonth(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
+ Long pMonth = fieldValues.remove(PROLEPTIC_MONTH);
+ if (pMonth != null) {
+ if (resolverStyle != ResolverStyle.LENIENT) {
+ PROLEPTIC_MONTH.checkValidValue(pMonth);
+ }
+ // first day-of-month is likely to be safest for setting proleptic-month
+ // cannot add to year zero, as not all chronologies have a year zero
+ ChronoLocalDate chronoDate = dateNow()
+ .with(DAY_OF_MONTH, 1).with(PROLEPTIC_MONTH, pMonth);
+ addFieldValue(fieldValues, MONTH_OF_YEAR, chronoDate.get(MONTH_OF_YEAR));
+ addFieldValue(fieldValues, YEAR, chronoDate.get(YEAR));
+ }
+ }
+
+ ChronoLocalDate resolveYearOfEra(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
+ Long yoeLong = fieldValues.remove(YEAR_OF_ERA);
+ if (yoeLong != null) {
+ Long eraLong = fieldValues.remove(ERA);
+ int yoe;
+ if (resolverStyle != ResolverStyle.LENIENT) {
+ yoe = range(YEAR_OF_ERA).checkValidIntValue(yoeLong, YEAR_OF_ERA);
+ } else {
+ yoe = Math.toIntExact(yoeLong);
+ }
+ if (eraLong != null) {
+ Era eraObj = eraOf(range(ERA).checkValidIntValue(eraLong, ERA));
+ addFieldValue(fieldValues, YEAR, prolepticYear(eraObj, yoe));
+ } else {
+ if (fieldValues.containsKey(YEAR)) {
+ int year = range(YEAR).checkValidIntValue(fieldValues.get(YEAR), YEAR);
+ ChronoLocalDate chronoDate = dateYearDay(year, 1);
+ addFieldValue(fieldValues, YEAR, prolepticYear(chronoDate.getEra(), yoe));
+ } else if (resolverStyle == ResolverStyle.STRICT) {
+ // do not invent era if strict
+ // reinstate the field removed earlier, no cross-check issues
+ fieldValues.put(YEAR_OF_ERA, yoeLong);
+ } else {
+ List<Era> eras = eras();
+ if (eras.isEmpty()) {
+ addFieldValue(fieldValues, YEAR, yoe);
+ } else {
+ Era eraObj = eras.get(eras.size() - 1);
+ addFieldValue(fieldValues, YEAR, prolepticYear(eraObj, yoe));
+ }
+ }
+ }
+ } else if (fieldValues.containsKey(ERA)) {
+ range(ERA).checkValidValue(fieldValues.get(ERA), ERA); // always validated
+ }
+ return null;
+ }
+
+ ChronoLocalDate resolveYMD(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
+ int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
+ if (resolverStyle == ResolverStyle.LENIENT) {
+ long months = Math.subtractExact(fieldValues.remove(MONTH_OF_YEAR), 1);
+ long days = Math.subtractExact(fieldValues.remove(DAY_OF_MONTH), 1);
+ return date(y, 1, 1).plus(months, MONTHS).plus(days, DAYS);
+ }
+ int moy = range(MONTH_OF_YEAR).checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR), MONTH_OF_YEAR);
+ ValueRange domRange = range(DAY_OF_MONTH);
+ int dom = domRange.checkValidIntValue(fieldValues.remove(DAY_OF_MONTH), DAY_OF_MONTH);
+ if (resolverStyle == ResolverStyle.SMART) { // previous valid
+ try {
+ return date(y, moy, dom);
+ } catch (DateTimeException ex) {
+ return date(y, moy, 1).with(TemporalAdjusters.lastDayOfMonth());
+ }
+ }
+ return date(y, moy, dom);
+ }
+
+ ChronoLocalDate resolveYD(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
+ int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
+ if (resolverStyle == ResolverStyle.LENIENT) {
+ long days = Math.subtractExact(fieldValues.remove(DAY_OF_YEAR), 1);
+ return dateYearDay(y, 1).plus(days, DAYS);
+ }
+ int doy = range(DAY_OF_YEAR).checkValidIntValue(fieldValues.remove(DAY_OF_YEAR), DAY_OF_YEAR);
+ return dateYearDay(y, doy); // smart is same as strict
+ }
+
+ ChronoLocalDate resolveYMAA(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
+ int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
+ if (resolverStyle == ResolverStyle.LENIENT) {
+ long months = Math.subtractExact(fieldValues.remove(MONTH_OF_YEAR), 1);
+ long weeks = Math.subtractExact(fieldValues.remove(ALIGNED_WEEK_OF_MONTH), 1);
+ long days = Math.subtractExact(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_MONTH), 1);
+ return date(y, 1, 1).plus(months, MONTHS).plus(weeks, WEEKS).plus(days, DAYS);
+ }
+ int moy = range(MONTH_OF_YEAR).checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR), MONTH_OF_YEAR);
+ int aw = range(ALIGNED_WEEK_OF_MONTH).checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_MONTH), ALIGNED_WEEK_OF_MONTH);
+ int ad = range(ALIGNED_DAY_OF_WEEK_IN_MONTH).checkValidIntValue(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_MONTH), ALIGNED_DAY_OF_WEEK_IN_MONTH);
+ ChronoLocalDate date = date(y, moy, 1).plus((aw - 1) * 7 + (ad - 1), DAYS);
+ if (resolverStyle == ResolverStyle.STRICT && date.get(MONTH_OF_YEAR) != moy) {
+ throw new DateTimeException("Strict mode rejected resolved date as it is in a different month");
+ }
+ return date;
+ }
+
+ ChronoLocalDate resolveYMAD(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
+ int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
+ if (resolverStyle == ResolverStyle.LENIENT) {
+ long months = Math.subtractExact(fieldValues.remove(MONTH_OF_YEAR), 1);
+ long weeks = Math.subtractExact(fieldValues.remove(ALIGNED_WEEK_OF_MONTH), 1);
+ long dow = Math.subtractExact(fieldValues.remove(DAY_OF_WEEK), 1);
+ return resolveAligned(date(y, 1, 1), months, weeks, dow);
+ }
+ int moy = range(MONTH_OF_YEAR).checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR), MONTH_OF_YEAR);
+ int aw = range(ALIGNED_WEEK_OF_MONTH).checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_MONTH), ALIGNED_WEEK_OF_MONTH);
+ int dow = range(DAY_OF_WEEK).checkValidIntValue(fieldValues.remove(DAY_OF_WEEK), DAY_OF_WEEK);
+ ChronoLocalDate date = date(y, moy, 1).plus((aw - 1) * 7, DAYS).with(nextOrSame(DayOfWeek.of(dow)));
+ if (resolverStyle == ResolverStyle.STRICT && date.get(MONTH_OF_YEAR) != moy) {
+ throw new DateTimeException("Strict mode rejected resolved date as it is in a different month");
+ }
+ return date;
+ }
+
+ ChronoLocalDate resolveYAA(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
+ int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
+ if (resolverStyle == ResolverStyle.LENIENT) {
+ long weeks = Math.subtractExact(fieldValues.remove(ALIGNED_WEEK_OF_YEAR), 1);
+ long days = Math.subtractExact(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_YEAR), 1);
+ return dateYearDay(y, 1).plus(weeks, WEEKS).plus(days, DAYS);
+ }
+ int aw = range(ALIGNED_WEEK_OF_YEAR).checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_YEAR), ALIGNED_WEEK_OF_YEAR);
+ int ad = range(ALIGNED_DAY_OF_WEEK_IN_YEAR).checkValidIntValue(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_YEAR), ALIGNED_DAY_OF_WEEK_IN_YEAR);
+ ChronoLocalDate date = dateYearDay(y, 1).plus((aw - 1) * 7 + (ad - 1), DAYS);
+ if (resolverStyle == ResolverStyle.STRICT && date.get(YEAR) != y) {
+ throw new DateTimeException("Strict mode rejected resolved date as it is in a different year");
+ }
+ return date;
+ }
+
+ ChronoLocalDate resolveYAD(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
+ int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
+ if (resolverStyle == ResolverStyle.LENIENT) {
+ long weeks = Math.subtractExact(fieldValues.remove(ALIGNED_WEEK_OF_YEAR), 1);
+ long dow = Math.subtractExact(fieldValues.remove(DAY_OF_WEEK), 1);
+ return resolveAligned(dateYearDay(y, 1), 0, weeks, dow);
+ }
+ int aw = range(ALIGNED_WEEK_OF_YEAR).checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_YEAR), ALIGNED_WEEK_OF_YEAR);
+ int dow = range(DAY_OF_WEEK).checkValidIntValue(fieldValues.remove(DAY_OF_WEEK), DAY_OF_WEEK);
+ ChronoLocalDate date = dateYearDay(y, 1).plus((aw - 1) * 7, DAYS).with(nextOrSame(DayOfWeek.of(dow)));
+ if (resolverStyle == ResolverStyle.STRICT && date.get(YEAR) != y) {
+ throw new DateTimeException("Strict mode rejected resolved date as it is in a different year");
+ }
+ return date;
+ }
+
+ ChronoLocalDate resolveAligned(ChronoLocalDate base, long months, long weeks, long dow) {
+ ChronoLocalDate date = base.plus(months, MONTHS).plus(weeks, WEEKS);
+ if (dow > 7) {
+ date = date.plus((dow - 1) / 7, WEEKS);
+ dow = ((dow - 1) % 7) + 1;
+ } else if (dow < 1) {
+ date = date.plus(Math.subtractExact(dow, 7) / 7, WEEKS);
+ dow = ((dow + 6) % 7) + 1;
+ }
+ return date.with(nextOrSame(DayOfWeek.of((int) dow)));
+ }
+
+ /**
+ * Adds a field-value pair to the map, checking for conflicts.
+ * <p>
+ * If the field is not already present, then the field-value pair is added to the map.
+ * If the field is already present and it has the same value as that specified, no action occurs.
+ * If the field is already present and it has a different value to that specified, then
+ * an exception is thrown.
+ *
+ * @param field the field to add, not null
+ * @param value the value to add, not null
+ * @throws java.time.DateTimeException if the field is already present with a different value
+ */
+ void addFieldValue(Map<TemporalField, Long> fieldValues, ChronoField field, long value) {
+ Long old = fieldValues.get(field); // check first for better error message
+ if (old != null && old.longValue() != value) {
+ throw new DateTimeException("Conflict found: " + field + " " + old + " differs from " + field + " " + value);
+ }
+ fieldValues.put(field, value);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Compares this chronology to another chronology.
+ * <p>
+ * The comparison order first by the chronology ID string, then by any
+ * additional information specific to the subclass.
+ * It is "consistent with equals", as defined by {@link Comparable}.
+ *
+ * @implSpec
+ * This implementation compares the chronology ID.
+ * Subclasses must compare any additional state that they store.
+ *
+ * @param other the other chronology to compare to, not null
+ * @return the comparator value, negative if less, positive if greater
+ */
+ @Override
+ public int compareTo(Chronology other) {
+ return getId().compareTo(other.getId());
+ }
+
+ /**
+ * Checks if this chronology is equal to another chronology.
+ * <p>
+ * The comparison is based on the entire state of the object.
+ *
+ * @implSpec
+ * This implementation checks the type and calls
+ * {@link #compareTo(java.time.chrono.Chronology)}.
+ *
+ * @param obj the object to check, null returns false
+ * @return true if this is equal to the other chronology
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof AbstractChronology) {
+ return compareTo((AbstractChronology) obj) == 0;
+ }
+ return false;
+ }
+
+ /**
+ * A hash code for this chronology.
+ * <p>
+ * The hash code should be based on the entire state of the object.
+ *
+ * @implSpec
+ * This implementation is based on the chronology ID and class.
+ * Subclasses should add any additional state that they store.
+ *
+ * @return a suitable hash code
+ */
+ @Override
+ public int hashCode() {
+ return getClass().hashCode() ^ getId().hashCode();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Outputs this chronology as a {@code String}, using the chronology ID.
+ *
+ * @return a string representation of this chronology, not null
+ */
+ @Override
+ public String toString() {
+ return getId();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Writes the Chronology using a
+ * <a href="../../../serialized-form.html#java.time.chrono.Ser">dedicated serialized form</a>.
+ * <pre>
+ * out.writeByte(1); // identifies this as a Chronology
+ * out.writeUTF(getId());
+ * </pre>
+ *
+ * @return the instance of {@code Ser}, not null
+ */
+ Object writeReplace() {
+ return new Ser(Ser.CHRONO_TYPE, this);
+ }
+
+ /**
+ * Defend against malicious streams.
+ *
+ * @param s the stream to read
+ * @throws java.io.InvalidObjectException always
+ */
+ private void readObject(ObjectInputStream s) throws ObjectStreamException {
+ throw new InvalidObjectException("Deserialization via serialization delegate");
+ }
+
+ void writeExternal(DataOutput out) throws IOException {
+ out.writeUTF(getId());
+ }
+
+ static Chronology readExternal(DataInput in) throws IOException {
+ String id = in.readUTF();
+ return Chronology.of(id);
+ }
+
+}
diff --git a/java/time/chrono/ChronoLocalDate.java b/java/time/chrono/ChronoLocalDate.java
new file mode 100644
index 0000000..5848e94
--- /dev/null
+++ b/java/time/chrono/ChronoLocalDate.java
@@ -0,0 +1,799 @@
+/*
+ * Copyright (c) 2012, 2015, 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) 2012, 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.EPOCH_DAY;
+import static java.time.temporal.ChronoField.ERA;
+import static java.time.temporal.ChronoField.YEAR;
+import static java.time.temporal.ChronoUnit.DAYS;
+
+import java.time.DateTimeException;
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoField;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.Temporal;
+import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalAdjuster;
+import java.time.temporal.TemporalAmount;
+import java.time.temporal.TemporalField;
+import java.time.temporal.TemporalQueries;
+import java.time.temporal.TemporalQuery;
+import java.time.temporal.TemporalUnit;
+import java.time.temporal.UnsupportedTemporalTypeException;
+import java.util.Comparator;
+import java.util.Objects;
+
+/**
+ * A date without time-of-day or time-zone in an arbitrary chronology, intended
+ * for advanced globalization use cases.
+ * <p>
+ * <b>Most applications should declare method signatures, fields and variables
+ * as {@link LocalDate}, not this interface.</b>
+ * <p>
+ * A {@code ChronoLocalDate} is the abstract representation of a date where the
+ * {@code Chronology chronology}, or calendar system, is pluggable.
+ * The date is defined in terms of fields expressed by {@link TemporalField},
+ * where most common implementations are defined in {@link ChronoField}.
+ * The chronology defines how the calendar system operates and the meaning of
+ * the standard fields.
+ *
+ * <h3>When to use this interface</h3>
+ * The design of the API encourages the use of {@code LocalDate} rather than this
+ * interface, even in the case where the application needs to deal with multiple
+ * calendar systems.
+ * <p>
+ * This concept can seem surprising at first, as the natural way to globalize an
+ * application might initially appear to be to abstract the calendar system.
+ * However, as explored below, abstracting the calendar system is usually the wrong
+ * approach, resulting in logic errors and hard to find bugs.
+ * As such, it should be considered an application-wide architectural decision to choose
+ * to use this interface as opposed to {@code LocalDate}.
+ *
+ * <h3>Architectural issues to consider</h3>
+ * These are some of the points that must be considered before using this interface
+ * throughout an application.
+ * <p>
+ * 1) Applications using this interface, as opposed to using just {@code LocalDate},
+ * face a significantly higher probability of bugs. This is because the calendar system
+ * in use is not known at development time. A key cause of bugs is where the developer
+ * applies assumptions from their day-to-day knowledge of the ISO calendar system
+ * to code that is intended to deal with any arbitrary calendar system.
+ * The section below outlines how those assumptions can cause problems
+ * The primary mechanism for reducing this increased risk of bugs is a strong code review process.
+ * This should also be considered a extra cost in maintenance for the lifetime of the code.
+ * <p>
+ * 2) This interface does not enforce immutability of implementations.
+ * While the implementation notes indicate that all implementations must be immutable
+ * there is nothing in the code or type system to enforce this. Any method declared
+ * to accept a {@code ChronoLocalDate} could therefore be passed a poorly or
+ * maliciously written mutable implementation.
+ * <p>
+ * 3) Applications using this interface must consider the impact of eras.
+ * {@code LocalDate} shields users from the concept of eras, by ensuring that {@code getYear()}
+ * returns the proleptic year. That decision ensures that developers can think of
+ * {@code LocalDate} instances as consisting of three fields - year, month-of-year and day-of-month.
+ * By contrast, users of this interface must think of dates as consisting of four fields -
+ * era, year-of-era, month-of-year and day-of-month. The extra era field is frequently
+ * forgotten, yet it is of vital importance to dates in an arbitrary calendar system.
+ * For example, in the Japanese calendar system, the era represents the reign of an Emperor.
+ * Whenever one reign ends and another starts, the year-of-era is reset to one.
+ * <p>
+ * 4) The only agreed international standard for passing a date between two systems
+ * is the ISO-8601 standard which requires the ISO calendar system. Using this interface
+ * throughout the application will inevitably lead to the requirement to pass the date
+ * across a network or component boundary, requiring an application specific protocol or format.
+ * <p>
+ * 5) Long term persistence, such as a database, will almost always only accept dates in the
+ * ISO-8601 calendar system (or the related Julian-Gregorian). Passing around dates in other
+ * calendar systems increases the complications of interacting with persistence.
+ * <p>
+ * 6) Most of the time, passing a {@code ChronoLocalDate} throughout an application
+ * is unnecessary, as discussed in the last section below.
+ *
+ * <h3>False assumptions causing bugs in multi-calendar system code</h3>
+ * As indicated above, there are many issues to consider when try to use and manipulate a
+ * date in an arbitrary calendar system. These are some of the key issues.
+ * <p>
+ * Code that queries the day-of-month and assumes that the value will never be more than
+ * 31 is invalid. Some calendar systems have more than 31 days in some months.
+ * <p>
+ * Code that adds 12 months to a date and assumes that a year has been added is invalid.
+ * Some calendar systems have a different number of months, such as 13 in the Coptic or Ethiopic.
+ * <p>
+ * Code that adds one month to a date and assumes that the month-of-year value will increase
+ * by one or wrap to the next year is invalid. Some calendar systems have a variable number
+ * of months in a year, such as the Hebrew.
+ * <p>
+ * Code that adds one month, then adds a second one month and assumes that the day-of-month
+ * will remain close to its original value is invalid. Some calendar systems have a large difference
+ * between the length of the longest month and the length of the shortest month.
+ * For example, the Coptic or Ethiopic have 12 months of 30 days and 1 month of 5 days.
+ * <p>
+ * Code that adds seven days and assumes that a week has been added is invalid.
+ * Some calendar systems have weeks of other than seven days, such as the French Revolutionary.
+ * <p>
+ * Code that assumes that because the year of {@code date1} is greater than the year of {@code date2}
+ * then {@code date1} is after {@code date2} is invalid. This is invalid for all calendar systems
+ * when referring to the year-of-era, and especially untrue of the Japanese calendar system
+ * where the year-of-era restarts with the reign of every new Emperor.
+ * <p>
+ * Code that treats month-of-year one and day-of-month one as the start of the year is invalid.
+ * Not all calendar systems start the year when the month value is one.
+ * <p>
+ * In general, manipulating a date, and even querying a date, is wide open to bugs when the
+ * calendar system is unknown at development time. This is why it is essential that code using
+ * this interface is subjected to additional code reviews. It is also why an architectural
+ * decision to avoid this interface type is usually the correct one.
+ *
+ * <h3>Using LocalDate instead</h3>
+ * The primary alternative to using this interface throughout your application is as follows.
+ * <ul>
+ * <li>Declare all method signatures referring to dates in terms of {@code LocalDate}.
+ * <li>Either store the chronology (calendar system) in the user profile or lookup
+ * the chronology from the user locale
+ * <li>Convert the ISO {@code LocalDate} to and from the user's preferred calendar system during
+ * printing and parsing
+ * </ul>
+ * This approach treats the problem of globalized calendar systems as a localization issue
+ * and confines it to the UI layer. This approach is in keeping with other localization
+ * issues in the java platform.
+ * <p>
+ * As discussed above, performing calculations on a date where the rules of the calendar system
+ * are pluggable requires skill and is not recommended.
+ * Fortunately, the need to perform calculations on a date in an arbitrary calendar system
+ * is extremely rare. For example, it is highly unlikely that the business rules of a library
+ * book rental scheme will allow rentals to be for one month, where meaning of the month
+ * is dependent on the user's preferred calendar system.
+ * <p>
+ * A key use case for calculations on a date in an arbitrary calendar system is producing
+ * a month-by-month calendar for display and user interaction. Again, this is a UI issue,
+ * and use of this interface solely within a few methods of the UI layer may be justified.
+ * <p>
+ * In any other part of the system, where a date must be manipulated in a calendar system
+ * other than ISO, the use case will generally specify the calendar system to use.
+ * For example, an application may need to calculate the next Islamic or Hebrew holiday
+ * which may require manipulating the date.
+ * This kind of use case can be handled as follows:
+ * <ul>
+ * <li>start from the ISO {@code LocalDate} being passed to the method
+ * <li>convert the date to the alternate calendar system, which for this use case is known
+ * rather than arbitrary
+ * <li>perform the calculation
+ * <li>convert back to {@code LocalDate}
+ * </ul>
+ * Developers writing low-level frameworks or libraries should also avoid this interface.
+ * Instead, one of the two general purpose access interfaces should be used.
+ * Use {@link TemporalAccessor} if read-only access is required, or use {@link Temporal}
+ * if read-write access is required.
+ *
+ * @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.
+ * <p>
+ * Additional calendar systems may be added to the system.
+ * See {@link Chronology} for more details.
+ *
+ * @since 1.8
+ */
+public interface ChronoLocalDate
+ extends Temporal, TemporalAdjuster, Comparable<ChronoLocalDate> {
+
+ /**
+ * Gets a comparator that compares {@code ChronoLocalDate} in
+ * time-line order ignoring the chronology.
+ * <p>
+ * This comparator differs from the comparison in {@link #compareTo} in that it
+ * only compares the underlying date and not the chronology.
+ * This allows dates in different calendar systems to be compared based
+ * on the position of the date on the local time-line.
+ * The underlying comparison is equivalent to comparing the epoch-day.
+ *
+ * @return a comparator that compares in time-line order ignoring the chronology
+ * @see #isAfter
+ * @see #isBefore
+ * @see #isEqual
+ */
+ static Comparator<ChronoLocalDate> timeLineOrder() {
+ return AbstractChronology.DATE_ORDER;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code ChronoLocalDate} from a temporal object.
+ * <p>
+ * This obtains a local date based on the specified temporal.
+ * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
+ * which this factory converts to an instance of {@code ChronoLocalDate}.
+ * <p>
+ * The conversion extracts and combines the chronology and the date
+ * from the temporal object. The behavior is equivalent to using
+ * {@link Chronology#date(TemporalAccessor)} with the extracted chronology.
+ * Implementations are permitted to perform optimizations such as accessing
+ * those fields that are equivalent to the relevant objects.
+ * <p>
+ * This method matches the signature of the functional interface {@link TemporalQuery}
+ * allowing it to be used as a query via method reference, {@code ChronoLocalDate::from}.
+ *
+ * @param temporal the temporal object to convert, not null
+ * @return the date, not null
+ * @throws DateTimeException if unable to convert to a {@code ChronoLocalDate}
+ * @see Chronology#date(TemporalAccessor)
+ */
+ static ChronoLocalDate from(TemporalAccessor temporal) {
+ if (temporal instanceof ChronoLocalDate) {
+ return (ChronoLocalDate) temporal;
+ }
+ Objects.requireNonNull(temporal, "temporal");
+ Chronology chrono = temporal.query(TemporalQueries.chronology());
+ if (chrono == null) {
+ throw new DateTimeException("Unable to obtain ChronoLocalDate from TemporalAccessor: " + temporal.getClass());
+ }
+ return chrono.date(temporal);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the chronology of this date.
+ * <p>
+ * The {@code Chronology} represents the calendar system in use.
+ * The era and other fields in {@link ChronoField} are defined by the chronology.
+ *
+ * @return the chronology, not null
+ */
+ Chronology getChronology();
+
+ /**
+ * Gets the era, as defined by the chronology.
+ * <p>
+ * The era is, conceptually, the largest division of the time-line.
+ * Most calendar systems have a single epoch dividing the time-line into two eras.
+ * However, some have multiple eras, such as one for the reign of each leader.
+ * The exact meaning is determined by the {@code Chronology}.
+ * <p>
+ * All correctly implemented {@code Era} classes are singletons, thus it
+ * is valid code to write {@code date.getEra() == SomeChrono.ERA_NAME)}.
+ * <p>
+ * This default implementation uses {@link Chronology#eraOf(int)}.
+ *
+ * @return the chronology specific era constant applicable at this date, not null
+ */
+ default Era getEra() {
+ return getChronology().eraOf(get(ERA));
+ }
+
+ /**
+ * Checks if the year is a leap year, as defined by the calendar system.
+ * <p>
+ * A leap-year is a year of a longer length than normal.
+ * The exact meaning is determined by the chronology with the constraint that
+ * a leap-year must imply a year-length longer than a non leap-year.
+ * <p>
+ * This default implementation uses {@link Chronology#isLeapYear(long)}.
+ *
+ * @return true if this date is in a leap year, false otherwise
+ */
+ default boolean isLeapYear() {
+ return getChronology().isLeapYear(getLong(YEAR));
+ }
+
+ /**
+ * Returns the length of the month represented by this date, as defined by the calendar system.
+ * <p>
+ * This returns the length of the month in days.
+ *
+ * @return the length of the month in days
+ */
+ int lengthOfMonth();
+
+ /**
+ * Returns the length of the year represented by this date, as defined by the calendar system.
+ * <p>
+ * This returns the length of the year in days.
+ * <p>
+ * The default implementation uses {@link #isLeapYear()} and returns 365 or 366.
+ *
+ * @return the length of the year in days
+ */
+ default int lengthOfYear() {
+ return (isLeapYear() ? 366 : 365);
+ }
+
+ /**
+ * Checks if the specified field is supported.
+ * <p>
+ * This checks if the specified field can be queried on this date.
+ * If false, then calling the {@link #range(TemporalField) range},
+ * {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}
+ * methods will throw an exception.
+ * <p>
+ * The set of supported fields is defined by the chronology and normally includes
+ * all {@code ChronoField} date fields.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}
+ * passing {@code this} as the argument.
+ * Whether the field is supported is determined by the field.
+ *
+ * @param field the field to check, null returns false
+ * @return true if the field can be queried, false if not
+ */
+ @Override
+ default boolean isSupported(TemporalField field) {
+ if (field instanceof ChronoField) {
+ return field.isDateBased();
+ }
+ return field != null && field.isSupportedBy(this);
+ }
+
+ /**
+ * Checks if the specified unit is supported.
+ * <p>
+ * This checks if the specified unit can be added to or subtracted from this date.
+ * If false, then calling the {@link #plus(long, TemporalUnit)} and
+ * {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
+ * <p>
+ * The set of supported units is defined by the chronology and normally includes
+ * all {@code ChronoUnit} date units except {@code FOREVER}.
+ * <p>
+ * If the unit is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
+ * passing {@code this} as the argument.
+ * Whether the unit is supported is determined by the unit.
+ *
+ * @param unit the unit to check, null returns false
+ * @return true if the unit can be added/subtracted, false if not
+ */
+ @Override
+ default boolean isSupported(TemporalUnit unit) {
+ if (unit instanceof ChronoUnit) {
+ return unit.isDateBased();
+ }
+ return unit != null && unit.isSupportedBy(this);
+ }
+
+ //-----------------------------------------------------------------------
+ // override for covariant return type
+ /**
+ * {@inheritDoc}
+ * @throws DateTimeException {@inheritDoc}
+ * @throws ArithmeticException {@inheritDoc}
+ */
+ @Override
+ default ChronoLocalDate with(TemporalAdjuster adjuster) {
+ return ChronoLocalDateImpl.ensureValid(getChronology(), Temporal.super.with(adjuster));
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws DateTimeException {@inheritDoc}
+ * @throws UnsupportedTemporalTypeException {@inheritDoc}
+ * @throws ArithmeticException {@inheritDoc}
+ */
+ @Override
+ default ChronoLocalDate with(TemporalField field, long newValue) {
+ if (field instanceof ChronoField) {
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
+ }
+ return ChronoLocalDateImpl.ensureValid(getChronology(), field.adjustInto(this, newValue));
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws DateTimeException {@inheritDoc}
+ * @throws ArithmeticException {@inheritDoc}
+ */
+ @Override
+ default ChronoLocalDate plus(TemporalAmount amount) {
+ return ChronoLocalDateImpl.ensureValid(getChronology(), Temporal.super.plus(amount));
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws DateTimeException {@inheritDoc}
+ * @throws ArithmeticException {@inheritDoc}
+ */
+ @Override
+ default ChronoLocalDate plus(long amountToAdd, TemporalUnit unit) {
+ if (unit instanceof ChronoUnit) {
+ throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
+ }
+ return ChronoLocalDateImpl.ensureValid(getChronology(), unit.addTo(this, amountToAdd));
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws DateTimeException {@inheritDoc}
+ * @throws ArithmeticException {@inheritDoc}
+ */
+ @Override
+ default ChronoLocalDate minus(TemporalAmount amount) {
+ return ChronoLocalDateImpl.ensureValid(getChronology(), Temporal.super.minus(amount));
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws DateTimeException {@inheritDoc}
+ * @throws UnsupportedTemporalTypeException {@inheritDoc}
+ * @throws ArithmeticException {@inheritDoc}
+ */
+ @Override
+ default ChronoLocalDate minus(long amountToSubtract, TemporalUnit unit) {
+ return ChronoLocalDateImpl.ensureValid(getChronology(), Temporal.super.minus(amountToSubtract, unit));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Queries this date using the specified query.
+ * <p>
+ * This queries this date using the specified query strategy object.
+ * The {@code TemporalQuery} object defines the logic to be used to
+ * obtain the result. Read the documentation of the query to understand
+ * what the result of this method will be.
+ * <p>
+ * The result of this method is obtained by invoking the
+ * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the
+ * specified query passing {@code this} as the argument.
+ *
+ * @param <R> the type of the result
+ * @param query the query to invoke, not null
+ * @return the query result, null may be returned (defined by the query)
+ * @throws DateTimeException if unable to query (defined by the query)
+ * @throws ArithmeticException if numeric overflow occurs (defined by the query)
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ default <R> R query(TemporalQuery<R> query) {
+ if (query == TemporalQueries.zoneId() || query == TemporalQueries.zone() || query == TemporalQueries.offset()) {
+ return null;
+ } else if (query == TemporalQueries.localTime()) {
+ return null;
+ } else if (query == TemporalQueries.chronology()) {
+ return (R) getChronology();
+ } else if (query == TemporalQueries.precision()) {
+ return (R) DAYS;
+ }
+ // inline TemporalAccessor.super.query(query) as an optimization
+ // non-JDK classes are not permitted to make this optimization
+ return query.queryFrom(this);
+ }
+
+ /**
+ * Adjusts the specified temporal object to have the same date as this object.
+ * <p>
+ * This returns a temporal object of the same observable type as the input
+ * with the date changed to be the same as this.
+ * <p>
+ * The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)}
+ * passing {@link ChronoField#EPOCH_DAY} as the field.
+ * <p>
+ * In most cases, it is clearer to reverse the calling pattern by using
+ * {@link Temporal#with(TemporalAdjuster)}:
+ * <pre>
+ * // these two lines are equivalent, but the second approach is recommended
+ * temporal = thisLocalDate.adjustInto(temporal);
+ * temporal = temporal.with(thisLocalDate);
+ * </pre>
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param temporal the target object to be adjusted, not null
+ * @return the adjusted object, not null
+ * @throws DateTimeException if unable to make the adjustment
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ default Temporal adjustInto(Temporal temporal) {
+ return temporal.with(EPOCH_DAY, toEpochDay());
+ }
+
+ /**
+ * Calculates the amount of time until another date in terms of the specified unit.
+ * <p>
+ * This calculates the amount of time between two {@code ChronoLocalDate}
+ * objects in terms of a single {@code TemporalUnit}.
+ * 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 {@code Temporal} passed to this method is converted to a
+ * {@code ChronoLocalDate} using {@link Chronology#date(TemporalAccessor)}.
+ * The calculation returns a whole number, representing the number of
+ * complete units between the two dates.
+ * For example, the amount in days between two dates can be calculated
+ * using {@code startDate.until(endDate, DAYS)}.
+ * <p>
+ * There are two equivalent ways of using this method.
+ * The first is to invoke this method.
+ * The second is to use {@link TemporalUnit#between(Temporal, Temporal)}:
+ * <pre>
+ * // these two lines are equivalent
+ * amount = start.until(end, MONTHS);
+ * amount = MONTHS.between(start, end);
+ * </pre>
+ * The choice should be made based on which makes the code more readable.
+ * <p>
+ * The calculation is implemented in this method for {@link ChronoUnit}.
+ * The units {@code DAYS}, {@code WEEKS}, {@code MONTHS}, {@code YEARS},
+ * {@code DECADES}, {@code CENTURIES}, {@code MILLENNIA} and {@code ERAS}
+ * should be supported by all implementations.
+ * Other {@code ChronoUnit} values will throw an exception.
+ * <p>
+ * If the unit is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.between(Temporal, Temporal)}
+ * passing {@code this} as the first argument and the converted input temporal as
+ * the second argument.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param endExclusive the end date, exclusive, which is converted to a
+ * {@code ChronoLocalDate} in the same chronology, not null
+ * @param unit the unit to measure the amount in, not null
+ * @return the amount of time between this date and the end date
+ * @throws DateTimeException if the amount cannot be calculated, or the end
+ * temporal cannot be converted to a {@code ChronoLocalDate}
+ * @throws UnsupportedTemporalTypeException if the unit is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override // override for Javadoc
+ long until(Temporal endExclusive, TemporalUnit unit);
+
+ /**
+ * Calculates the period between this date and another date as a {@code ChronoPeriod}.
+ * <p>
+ * This calculates the period between two dates. All supplied chronologies
+ * calculate the period using years, months and days, however the
+ * {@code 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.
+ * <p>
+ * The calculation is performed using the chronology of this date.
+ * If necessary, the input date will be converted to match.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @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
+ */
+ ChronoPeriod until(ChronoLocalDate endDateExclusive);
+
+ /**
+ * Formats this date using the specified formatter.
+ * <p>
+ * This date will be passed to the formatter to produce a string.
+ * <p>
+ * The default implementation must behave as follows:
+ * <pre>
+ * return formatter.format(this);
+ * </pre>
+ *
+ * @param formatter the formatter to use, not null
+ * @return the formatted date string, not null
+ * @throws DateTimeException if an error occurs during printing
+ */
+ default String format(DateTimeFormatter formatter) {
+ Objects.requireNonNull(formatter, "formatter");
+ return formatter.format(this);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Combines this date with a time to create a {@code ChronoLocalDateTime}.
+ * <p>
+ * This returns a {@code ChronoLocalDateTime} formed from this date at the specified time.
+ * All possible combinations of date and time are valid.
+ *
+ * @param localTime the local time to use, not null
+ * @return the local date-time formed from this date and the specified time, not null
+ */
+ @SuppressWarnings("unchecked")
+ default ChronoLocalDateTime<?> atTime(LocalTime localTime) {
+ return ChronoLocalDateTimeImpl.of(this, localTime);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Converts this date to the Epoch Day.
+ * <p>
+ * The {@link ChronoField#EPOCH_DAY Epoch Day count} is a simple
+ * incrementing count of days where day 0 is 1970-01-01 (ISO).
+ * This definition is the same for all chronologies, enabling conversion.
+ * <p>
+ * This default implementation queries the {@code EPOCH_DAY} field.
+ *
+ * @return the Epoch Day equivalent to this date
+ */
+ default long toEpochDay() {
+ return getLong(EPOCH_DAY);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Compares this date to another date, including the chronology.
+ * <p>
+ * The comparison is based first on the underlying time-line date, then
+ * on the chronology.
+ * It is "consistent with equals", as defined by {@link Comparable}.
+ * <p>
+ * For example, the following is the comparator order:
+ * <ol>
+ * <li>{@code 2012-12-03 (ISO)}</li>
+ * <li>{@code 2012-12-04 (ISO)}</li>
+ * <li>{@code 2555-12-04 (ThaiBuddhist)}</li>
+ * <li>{@code 2012-12-05 (ISO)}</li>
+ * </ol>
+ * Values #2 and #3 represent the same date on the time-line.
+ * When two values represent the same date, the chronology ID is compared to distinguish them.
+ * This step is needed to make the ordering "consistent with equals".
+ * <p>
+ * If all the date objects being compared are in the same chronology, then the
+ * additional chronology stage is not required and only the local date is used.
+ * To compare the dates of two {@code TemporalAccessor} instances, including dates
+ * in two different chronologies, use {@link ChronoField#EPOCH_DAY} as a comparator.
+ * <p>
+ * This default implementation performs the comparison defined above.
+ *
+ * @param other the other date to compare to, not null
+ * @return the comparator value, negative if less, positive if greater
+ */
+ @Override
+ default int compareTo(ChronoLocalDate other) {
+ int cmp = Long.compare(toEpochDay(), other.toEpochDay());
+ if (cmp == 0) {
+ cmp = getChronology().compareTo(other.getChronology());
+ }
+ return cmp;
+ }
+
+ /**
+ * Checks if this date is after the specified date ignoring the chronology.
+ * <p>
+ * This method differs from the comparison in {@link #compareTo} in that it
+ * only compares the underlying date and not the chronology.
+ * This allows dates in different calendar systems to be compared based
+ * on the time-line position.
+ * This is equivalent to using {@code date1.toEpochDay() > date2.toEpochDay()}.
+ * <p>
+ * This default implementation performs the comparison based on the epoch-day.
+ *
+ * @param other the other date to compare to, not null
+ * @return true if this is after the specified date
+ */
+ default boolean isAfter(ChronoLocalDate other) {
+ return this.toEpochDay() > other.toEpochDay();
+ }
+
+ /**
+ * Checks if this date is before the specified date ignoring the chronology.
+ * <p>
+ * This method differs from the comparison in {@link #compareTo} in that it
+ * only compares the underlying date and not the chronology.
+ * This allows dates in different calendar systems to be compared based
+ * on the time-line position.
+ * This is equivalent to using {@code date1.toEpochDay() < date2.toEpochDay()}.
+ * <p>
+ * This default implementation performs the comparison based on the epoch-day.
+ *
+ * @param other the other date to compare to, not null
+ * @return true if this is before the specified date
+ */
+ default boolean isBefore(ChronoLocalDate other) {
+ return this.toEpochDay() < other.toEpochDay();
+ }
+
+ /**
+ * Checks if this date is equal to the specified date ignoring the chronology.
+ * <p>
+ * This method differs from the comparison in {@link #compareTo} in that it
+ * only compares the underlying date and not the chronology.
+ * This allows dates in different calendar systems to be compared based
+ * on the time-line position.
+ * This is equivalent to using {@code date1.toEpochDay() == date2.toEpochDay()}.
+ * <p>
+ * This default implementation performs the comparison based on the epoch-day.
+ *
+ * @param other the other date to compare to, not null
+ * @return true if the underlying date is equal to the specified date
+ */
+ default boolean isEqual(ChronoLocalDate other) {
+ return this.toEpochDay() == other.toEpochDay();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if this date is equal to another date, including the chronology.
+ * <p>
+ * Compares this date with another ensuring that the date and chronology are the same.
+ * <p>
+ * To compare the dates of two {@code TemporalAccessor} instances, including dates
+ * in two different chronologies, use {@link ChronoField#EPOCH_DAY} as a comparator.
+ *
+ * @param obj the object to check, null returns false
+ * @return true if this is equal to the other date
+ */
+ @Override
+ boolean equals(Object obj);
+
+ /**
+ * A hash code for this date.
+ *
+ * @return a suitable hash code
+ */
+ @Override
+ int hashCode();
+
+ //-----------------------------------------------------------------------
+ /**
+ * Outputs this date as a {@code String}.
+ * <p>
+ * The output will include the full local date.
+ *
+ * @return the formatted date, not null
+ */
+ @Override
+ String toString();
+
+}
diff --git a/java/time/chrono/ChronoLocalDateImpl.java b/java/time/chrono/ChronoLocalDateImpl.java
new file mode 100644
index 0000000..8fe8d19
--- /dev/null
+++ b/java/time/chrono/ChronoLocalDateImpl.java
@@ -0,0 +1,444 @@
+/*
+ * Copyright (c) 2012, 2015, 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) 2012, 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.DAY_OF_MONTH;
+import static java.time.temporal.ChronoField.ERA;
+import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
+import static java.time.temporal.ChronoField.PROLEPTIC_MONTH;
+import static java.time.temporal.ChronoField.YEAR_OF_ERA;
+
+import java.io.Serializable;
+import java.time.DateTimeException;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.Temporal;
+import java.time.temporal.TemporalAdjuster;
+import java.time.temporal.TemporalAmount;
+import java.time.temporal.TemporalField;
+import java.time.temporal.TemporalUnit;
+import java.time.temporal.UnsupportedTemporalTypeException;
+import java.time.temporal.ValueRange;
+import java.util.Objects;
+
+/**
+ * A date 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.
+ * <p>
+ * {@code ChronoLocalDate} is built on the generic concepts of year, month and day.
+ * The calendar system, represented by a {@link java.time.chrono.Chronology}, expresses the relationship between
+ * the fields and this class allows the resulting date to be manipulated.
+ * <p>
+ * Note that not all calendar systems are suitable for use with this class.
+ * For example, the Mayan calendar uses a system that bears no relation to years, months and days.
+ * <p>
+ * The API design encourages the use of {@code LocalDate} for the majority of the application.
+ * This includes code to read and write from a persistent data store, such as a database,
+ * and to send dates and times across a network. The {@code ChronoLocalDate} instance is then used
+ * at the user interface level to deal with localized input/output.
+ *
+ * <P>Example: </p>
+ * <pre>
+ * System.out.printf("Example()%n");
+ * // Enumerate the list of available calendars and print today for each
+ * Set<Chronology> chronos = Chronology.getAvailableChronologies();
+ * for (Chronology chrono : chronos) {
+ * ChronoLocalDate date = chrono.dateNow();
+ * System.out.printf(" %20s: %s%n", chrono.getID(), date.toString());
+ * }
+ *
+ * // Print the Hijrah date and calendar
+ * ChronoLocalDate date = Chronology.of("Hijrah").dateNow();
+ * int day = date.get(ChronoField.DAY_OF_MONTH);
+ * int dow = date.get(ChronoField.DAY_OF_WEEK);
+ * int month = date.get(ChronoField.MONTH_OF_YEAR);
+ * int year = date.get(ChronoField.YEAR);
+ * System.out.printf(" Today is %s %s %d-%s-%d%n", date.getChronology().getID(),
+ * dow, day, month, year);
+
+ * // Print today's date and the last day of the year
+ * ChronoLocalDate now1 = Chronology.of("Hijrah").dateNow();
+ * ChronoLocalDate first = now1.with(ChronoField.DAY_OF_MONTH, 1)
+ * .with(ChronoField.MONTH_OF_YEAR, 1);
+ * ChronoLocalDate last = first.plus(1, ChronoUnit.YEARS)
+ * .minus(1, ChronoUnit.DAYS);
+ * System.out.printf(" Today is %s: start: %s; end: %s%n", last.getChronology().getID(),
+ * first, last);
+ * </pre>
+ *
+ * <h3>Adding Calendars</h3>
+ * <p> The set of calendars is extensible by defining a subclass of {@link ChronoLocalDate}
+ * to represent a date instance and an implementation of {@code Chronology}
+ * to be the factory for the ChronoLocalDate subclass.
+ * </p>
+ * <p> To permit the discovery of the additional calendar types the implementation of
+ * {@code Chronology} must be registered as a Service implementing the {@code Chronology} interface
+ * in the {@code META-INF/Services} file as per the specification of {@link java.util.ServiceLoader}.
+ * The subclass must function according to the {@code Chronology} class description and must provide its
+ * {@link java.time.chrono.Chronology#getId() chronlogy ID} and {@link Chronology#getCalendarType() calendar type}. </p>
+ *
+ * @implSpec
+ * This abstract class 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.
+ *
+ * @param <D> the ChronoLocalDate of this date-time
+ * @since 1.8
+ */
+abstract class ChronoLocalDateImpl<D extends ChronoLocalDate>
+ implements ChronoLocalDate, Temporal, TemporalAdjuster, Serializable {
+
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = 6282433883239719096L;
+
+ /**
+ * Casts the {@code Temporal} to {@code ChronoLocalDate} ensuring it bas the specified chronology.
+ *
+ * @param chrono the chronology to check for, not null
+ * @param temporal a date-time to cast, not null
+ * @return the date-time checked and cast to {@code ChronoLocalDate}, not null
+ * @throws ClassCastException if the date-time cannot be cast to ChronoLocalDate
+ * or the chronology is not equal this Chronology
+ */
+ static <D extends ChronoLocalDate> D ensureValid(Chronology chrono, Temporal temporal) {
+ @SuppressWarnings("unchecked")
+ D other = (D) temporal;
+ if (chrono.equals(other.getChronology()) == false) {
+ throw new ClassCastException("Chronology mismatch, expected: " + chrono.getId() + ", actual: " + other.getChronology().getId());
+ }
+ return other;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Creates an instance.
+ */
+ ChronoLocalDateImpl() {
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public D with(TemporalAdjuster adjuster) {
+ return (D) ChronoLocalDate.super.with(adjuster);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public D with(TemporalField field, long value) {
+ return (D) ChronoLocalDate.super.with(field, value);
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ @SuppressWarnings("unchecked")
+ public D plus(TemporalAmount amount) {
+ return (D) ChronoLocalDate.super.plus(amount);
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ @SuppressWarnings("unchecked")
+ public D plus(long amountToAdd, TemporalUnit unit) {
+ if (unit instanceof ChronoUnit) {
+ ChronoUnit f = (ChronoUnit) unit;
+ switch (f) {
+ case DAYS: return plusDays(amountToAdd);
+ case WEEKS: return plusDays(Math.multiplyExact(amountToAdd, 7));
+ case MONTHS: return plusMonths(amountToAdd);
+ case YEARS: return plusYears(amountToAdd);
+ case DECADES: return plusYears(Math.multiplyExact(amountToAdd, 10));
+ case CENTURIES: return plusYears(Math.multiplyExact(amountToAdd, 100));
+ case MILLENNIA: return plusYears(Math.multiplyExact(amountToAdd, 1000));
+ case ERAS: return with(ERA, Math.addExact(getLong(ERA), amountToAdd));
+ }
+ throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
+ }
+ return (D) ChronoLocalDate.super.plus(amountToAdd, unit);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public D minus(TemporalAmount amount) {
+ return (D) ChronoLocalDate.super.minus(amount);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public D minus(long amountToSubtract, TemporalUnit unit) {
+ return (D) ChronoLocalDate.super.minus(amountToSubtract, unit);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this date with the specified number of years added.
+ * <p>
+ * This adds the specified period in years to the date.
+ * In some cases, adding years can cause the resulting date to become invalid.
+ * If this occurs, then other fields, typically the day-of-month, will be adjusted to ensure
+ * that the result is valid. Typically this will select the last valid day of the month.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param yearsToAdd the years to add, may be negative
+ * @return a date based on this one with the years added, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ abstract D plusYears(long yearsToAdd);
+
+ /**
+ * Returns a copy of this date with the specified number of months added.
+ * <p>
+ * This adds the specified period in months to the date.
+ * In some cases, adding months can cause the resulting date to become invalid.
+ * If this occurs, then other fields, typically the day-of-month, will be adjusted to ensure
+ * that the result is valid. Typically this will select the last valid day of the month.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param monthsToAdd the months to add, may be negative
+ * @return a date based on this one with the months added, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ abstract D plusMonths(long monthsToAdd);
+
+ /**
+ * Returns a copy of this date with the specified number of weeks added.
+ * <p>
+ * This adds the specified period in weeks to the date.
+ * In some cases, adding weeks can cause the resulting date to become invalid.
+ * If this occurs, then other fields will be adjusted to ensure that the result is valid.
+ * <p>
+ * The default implementation uses {@link #plusDays(long)} using a 7 day week.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param weeksToAdd the weeks to add, may be negative
+ * @return a date based on this one with the weeks added, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ D plusWeeks(long weeksToAdd) {
+ return plusDays(Math.multiplyExact(weeksToAdd, 7));
+ }
+
+ /**
+ * Returns a copy of this date with the specified number of days added.
+ * <p>
+ * This adds the specified period in days to the date.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param daysToAdd the days to add, may be negative
+ * @return a date based on this one with the days added, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ abstract D plusDays(long daysToAdd);
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this date with the specified number of years subtracted.
+ * <p>
+ * This subtracts the specified period in years to the date.
+ * In some cases, subtracting years can cause the resulting date to become invalid.
+ * If this occurs, then other fields, typically the day-of-month, will be adjusted to ensure
+ * that the result is valid. Typically this will select the last valid day of the month.
+ * <p>
+ * The default implementation uses {@link #plusYears(long)}.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param yearsToSubtract the years to subtract, may be negative
+ * @return a date based on this one with the years subtracted, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ @SuppressWarnings("unchecked")
+ D minusYears(long yearsToSubtract) {
+ return (yearsToSubtract == Long.MIN_VALUE ? ((ChronoLocalDateImpl<D>)plusYears(Long.MAX_VALUE)).plusYears(1) : plusYears(-yearsToSubtract));
+ }
+
+ /**
+ * Returns a copy of this date with the specified number of months subtracted.
+ * <p>
+ * This subtracts the specified period in months to the date.
+ * In some cases, subtracting months can cause the resulting date to become invalid.
+ * If this occurs, then other fields, typically the day-of-month, will be adjusted to ensure
+ * that the result is valid. Typically this will select the last valid day of the month.
+ * <p>
+ * The default implementation uses {@link #plusMonths(long)}.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param monthsToSubtract the months to subtract, may be negative
+ * @return a date based on this one with the months subtracted, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ @SuppressWarnings("unchecked")
+ D minusMonths(long monthsToSubtract) {
+ return (monthsToSubtract == Long.MIN_VALUE ? ((ChronoLocalDateImpl<D>)plusMonths(Long.MAX_VALUE)).plusMonths(1) : plusMonths(-monthsToSubtract));
+ }
+
+ /**
+ * Returns a copy of this date with the specified number of weeks subtracted.
+ * <p>
+ * This subtracts the specified period in weeks to the date.
+ * In some cases, subtracting weeks can cause the resulting date to become invalid.
+ * If this occurs, then other fields will be adjusted to ensure that the result is valid.
+ * <p>
+ * The default implementation uses {@link #plusWeeks(long)}.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param weeksToSubtract the weeks to subtract, may be negative
+ * @return a date based on this one with the weeks subtracted, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ @SuppressWarnings("unchecked")
+ D minusWeeks(long weeksToSubtract) {
+ return (weeksToSubtract == Long.MIN_VALUE ? ((ChronoLocalDateImpl<D>)plusWeeks(Long.MAX_VALUE)).plusWeeks(1) : plusWeeks(-weeksToSubtract));
+ }
+
+ /**
+ * Returns a copy of this date with the specified number of days subtracted.
+ * <p>
+ * This subtracts the specified period in days to the date.
+ * <p>
+ * The default implementation uses {@link #plusDays(long)}.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param daysToSubtract the days to subtract, may be negative
+ * @return a date based on this one with the days subtracted, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ @SuppressWarnings("unchecked")
+ D minusDays(long daysToSubtract) {
+ return (daysToSubtract == Long.MIN_VALUE ? ((ChronoLocalDateImpl<D>)plusDays(Long.MAX_VALUE)).plusDays(1) : plusDays(-daysToSubtract));
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ public long until(Temporal endExclusive, TemporalUnit unit) {
+ Objects.requireNonNull(endExclusive, "endExclusive");
+ ChronoLocalDate end = getChronology().date(endExclusive);
+ if (unit instanceof ChronoUnit) {
+ switch ((ChronoUnit) unit) {
+ case DAYS: return daysUntil(end);
+ case WEEKS: return daysUntil(end) / 7;
+ case MONTHS: return monthsUntil(end);
+ case YEARS: return monthsUntil(end) / 12;
+ case DECADES: return monthsUntil(end) / 120;
+ case CENTURIES: return monthsUntil(end) / 1200;
+ case MILLENNIA: return monthsUntil(end) / 12000;
+ case ERAS: return end.getLong(ERA) - getLong(ERA);
+ }
+ throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
+ }
+ Objects.requireNonNull(unit, "unit");
+ return unit.between(this, end);
+ }
+
+ private long daysUntil(ChronoLocalDate end) {
+ return end.toEpochDay() - toEpochDay(); // no overflow
+ }
+
+ private long monthsUntil(ChronoLocalDate end) {
+ ValueRange range = getChronology().range(MONTH_OF_YEAR);
+ if (range.getMaximum() != 12) {
+ throw new IllegalStateException("ChronoLocalDateImpl only supports Chronologies with 12 months per year");
+ }
+ long packed1 = getLong(PROLEPTIC_MONTH) * 32L + get(DAY_OF_MONTH); // no overflow
+ long packed2 = end.getLong(PROLEPTIC_MONTH) * 32L + end.get(DAY_OF_MONTH); // no overflow
+ return (packed2 - packed1) / 32;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof ChronoLocalDate) {
+ return compareTo((ChronoLocalDate) obj) == 0;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ long epDay = toEpochDay();
+ return getChronology().hashCode() ^ ((int) (epDay ^ (epDay >>> 32)));
+ }
+
+ @Override
+ public String toString() {
+ // getLong() reduces chances of exceptions in toString()
+ long yoe = getLong(YEAR_OF_ERA);
+ long moy = getLong(MONTH_OF_YEAR);
+ long dom = getLong(DAY_OF_MONTH);
+ StringBuilder buf = new StringBuilder(30);
+ buf.append(getChronology().toString())
+ .append(" ")
+ .append(getEra())
+ .append(" ")
+ .append(yoe)
+ .append(moy < 10 ? "-0" : "-").append(moy)
+ .append(dom < 10 ? "-0" : "-").append(dom);
+ return buf.toString();
+ }
+
+}
diff --git a/java/time/chrono/ChronoLocalDateTime.java b/java/time/chrono/ChronoLocalDateTime.java
new file mode 100644
index 0000000..ce77f4d
--- /dev/null
+++ b/java/time/chrono/ChronoLocalDateTime.java
@@ -0,0 +1,602 @@
+/*
+ * Copyright (c) 2012, 2015, 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) 2007-2012, 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.EPOCH_DAY;
+import static java.time.temporal.ChronoField.NANO_OF_DAY;
+import static java.time.temporal.ChronoUnit.FOREVER;
+import static java.time.temporal.ChronoUnit.NANOS;
+
+import java.time.DateTimeException;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoField;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.Temporal;
+import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalAdjuster;
+import java.time.temporal.TemporalAmount;
+import java.time.temporal.TemporalField;
+import java.time.temporal.TemporalQueries;
+import java.time.temporal.TemporalQuery;
+import java.time.temporal.TemporalUnit;
+import java.time.zone.ZoneRules;
+import java.util.Comparator;
+import java.util.Objects;
+
+/**
+ * A date-time without a time-zone in an arbitrary chronology, intended
+ * for advanced globalization use cases.
+ * <p>
+ * <b>Most applications should declare method signatures, fields and variables
+ * as {@link LocalDateTime}, not this interface.</b>
+ * <p>
+ * A {@code ChronoLocalDateTime} is the abstract representation of a local date-time
+ * where the {@code Chronology chronology}, or calendar system, is pluggable.
+ * The date-time is defined in terms of fields expressed by {@link TemporalField},
+ * where most common implementations are defined in {@link ChronoField}.
+ * The chronology defines how the calendar system operates and the meaning of
+ * the standard fields.
+ *
+ * <h3>When to use this interface</h3>
+ * The design of the API encourages the use of {@code LocalDateTime} rather than this
+ * interface, even in the case where the application needs to deal with multiple
+ * calendar systems. The rationale for this is explored in detail in {@link ChronoLocalDate}.
+ * <p>
+ * Ensure that the discussion in {@code ChronoLocalDate} has been read and understood
+ * before using this interface.
+ *
+ * @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.
+ *
+ * @param <D> the concrete type for the date of this date-time
+ * @since 1.8
+ */
+public interface ChronoLocalDateTime<D extends ChronoLocalDate>
+ extends Temporal, TemporalAdjuster, Comparable<ChronoLocalDateTime<?>> {
+
+ /**
+ * Gets a comparator that compares {@code ChronoLocalDateTime} in
+ * time-line order ignoring the chronology.
+ * <p>
+ * This comparator differs from the comparison in {@link #compareTo} in that it
+ * only compares the underlying date-time and not the chronology.
+ * This allows dates in different calendar systems to be compared based
+ * on the position of the date-time on the local time-line.
+ * The underlying comparison is equivalent to comparing the epoch-day and nano-of-day.
+ *
+ * @return a comparator that compares in time-line order ignoring the chronology
+ * @see #isAfter
+ * @see #isBefore
+ * @see #isEqual
+ */
+ static Comparator<ChronoLocalDateTime<?>> timeLineOrder() {
+ return AbstractChronology.DATE_TIME_ORDER;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code ChronoLocalDateTime} from a temporal object.
+ * <p>
+ * This obtains a local date-time based on the specified temporal.
+ * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
+ * which this factory converts to an instance of {@code ChronoLocalDateTime}.
+ * <p>
+ * The conversion extracts and combines the chronology and the date-time
+ * from the temporal object. The behavior is equivalent to using
+ * {@link Chronology#localDateTime(TemporalAccessor)} with the extracted chronology.
+ * Implementations are permitted to perform optimizations such as accessing
+ * those fields that are equivalent to the relevant objects.
+ * <p>
+ * This method matches the signature of the functional interface {@link TemporalQuery}
+ * allowing it to be used as a query via method reference, {@code ChronoLocalDateTime::from}.
+ *
+ * @param temporal the temporal object to convert, not null
+ * @return the date-time, not null
+ * @throws DateTimeException if unable to convert to a {@code ChronoLocalDateTime}
+ * @see Chronology#localDateTime(TemporalAccessor)
+ */
+ static ChronoLocalDateTime<?> from(TemporalAccessor temporal) {
+ if (temporal instanceof ChronoLocalDateTime) {
+ return (ChronoLocalDateTime<?>) temporal;
+ }
+ Objects.requireNonNull(temporal, "temporal");
+ Chronology chrono = temporal.query(TemporalQueries.chronology());
+ if (chrono == null) {
+ throw new DateTimeException("Unable to obtain ChronoLocalDateTime from TemporalAccessor: " + temporal.getClass());
+ }
+ return chrono.localDateTime(temporal);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the chronology of this date-time.
+ * <p>
+ * The {@code Chronology} represents the calendar system in use.
+ * The era and other fields in {@link ChronoField} are defined by the chronology.
+ *
+ * @return the chronology, not null
+ */
+ default Chronology getChronology() {
+ return toLocalDate().getChronology();
+ }
+
+ /**
+ * Gets the local date part of this date-time.
+ * <p>
+ * This returns a local date with the same year, month and day
+ * as this date-time.
+ *
+ * @return the date part of this date-time, not null
+ */
+ D toLocalDate() ;
+
+ /**
+ * Gets the local time part of this date-time.
+ * <p>
+ * This returns a local time with the same hour, minute, second and
+ * nanosecond as this date-time.
+ *
+ * @return the time part of this date-time, not null
+ */
+ LocalTime toLocalTime();
+
+ /**
+ * Checks if the specified field is supported.
+ * <p>
+ * This checks if the specified field can be queried on this date-time.
+ * If false, then calling the {@link #range(TemporalField) range},
+ * {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}
+ * methods will throw an exception.
+ * <p>
+ * The set of supported fields is defined by the chronology and normally includes
+ * all {@code ChronoField} date and time fields.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}
+ * passing {@code this} as the argument.
+ * Whether the field is supported is determined by the field.
+ *
+ * @param field the field to check, null returns false
+ * @return true if the field can be queried, false if not
+ */
+ @Override
+ boolean isSupported(TemporalField field);
+
+ /**
+ * Checks if the specified unit is supported.
+ * <p>
+ * This checks if the specified unit can be added to or subtracted from this date-time.
+ * If false, then calling the {@link #plus(long, TemporalUnit)} and
+ * {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
+ * <p>
+ * The set of supported units is defined by the chronology and normally includes
+ * all {@code ChronoUnit} units except {@code FOREVER}.
+ * <p>
+ * If the unit is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
+ * passing {@code this} as the argument.
+ * Whether the unit is supported is determined by the unit.
+ *
+ * @param unit the unit to check, null returns false
+ * @return true if the unit can be added/subtracted, false if not
+ */
+ @Override
+ default boolean isSupported(TemporalUnit unit) {
+ if (unit instanceof ChronoUnit) {
+ return unit != FOREVER;
+ }
+ return unit != null && unit.isSupportedBy(this);
+ }
+
+ //-----------------------------------------------------------------------
+ // override for covariant return type
+ /**
+ * {@inheritDoc}
+ * @throws DateTimeException {@inheritDoc}
+ * @throws ArithmeticException {@inheritDoc}
+ */
+ @Override
+ default ChronoLocalDateTime<D> with(TemporalAdjuster adjuster) {
+ return ChronoLocalDateTimeImpl.ensureValid(getChronology(), Temporal.super.with(adjuster));
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws DateTimeException {@inheritDoc}
+ * @throws ArithmeticException {@inheritDoc}
+ */
+ @Override
+ ChronoLocalDateTime<D> with(TemporalField field, long newValue);
+
+ /**
+ * {@inheritDoc}
+ * @throws DateTimeException {@inheritDoc}
+ * @throws ArithmeticException {@inheritDoc}
+ */
+ @Override
+ default ChronoLocalDateTime<D> plus(TemporalAmount amount) {
+ return ChronoLocalDateTimeImpl.ensureValid(getChronology(), Temporal.super.plus(amount));
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws DateTimeException {@inheritDoc}
+ * @throws ArithmeticException {@inheritDoc}
+ */
+ @Override
+ ChronoLocalDateTime<D> plus(long amountToAdd, TemporalUnit unit);
+
+ /**
+ * {@inheritDoc}
+ * @throws DateTimeException {@inheritDoc}
+ * @throws ArithmeticException {@inheritDoc}
+ */
+ @Override
+ default ChronoLocalDateTime<D> minus(TemporalAmount amount) {
+ return ChronoLocalDateTimeImpl.ensureValid(getChronology(), Temporal.super.minus(amount));
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws DateTimeException {@inheritDoc}
+ * @throws ArithmeticException {@inheritDoc}
+ */
+ @Override
+ default ChronoLocalDateTime<D> minus(long amountToSubtract, TemporalUnit unit) {
+ return ChronoLocalDateTimeImpl.ensureValid(getChronology(), Temporal.super.minus(amountToSubtract, unit));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Queries this date-time using the specified query.
+ * <p>
+ * This queries this date-time using the specified query strategy object.
+ * The {@code TemporalQuery} object defines the logic to be used to
+ * obtain the result. Read the documentation of the query to understand
+ * what the result of this method will be.
+ * <p>
+ * The result of this method is obtained by invoking the
+ * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the
+ * specified query passing {@code this} as the argument.
+ *
+ * @param <R> the type of the result
+ * @param query the query to invoke, not null
+ * @return the query result, null may be returned (defined by the query)
+ * @throws DateTimeException if unable to query (defined by the query)
+ * @throws ArithmeticException if numeric overflow occurs (defined by the query)
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ default <R> R query(TemporalQuery<R> query) {
+ if (query == TemporalQueries.zoneId() || query == TemporalQueries.zone() || query == TemporalQueries.offset()) {
+ return null;
+ } else if (query == TemporalQueries.localTime()) {
+ return (R) toLocalTime();
+ } else if (query == TemporalQueries.chronology()) {
+ return (R) getChronology();
+ } else if (query == TemporalQueries.precision()) {
+ return (R) NANOS;
+ }
+ // inline TemporalAccessor.super.query(query) as an optimization
+ // non-JDK classes are not permitted to make this optimization
+ return query.queryFrom(this);
+ }
+
+ /**
+ * Adjusts the specified temporal object to have the same date and time as this object.
+ * <p>
+ * This returns a temporal object of the same observable type as the input
+ * with the date and time changed to be the same as this.
+ * <p>
+ * The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)}
+ * twice, passing {@link ChronoField#EPOCH_DAY} and
+ * {@link ChronoField#NANO_OF_DAY} as the fields.
+ * <p>
+ * In most cases, it is clearer to reverse the calling pattern by using
+ * {@link Temporal#with(TemporalAdjuster)}:
+ * <pre>
+ * // these two lines are equivalent, but the second approach is recommended
+ * temporal = thisLocalDateTime.adjustInto(temporal);
+ * temporal = temporal.with(thisLocalDateTime);
+ * </pre>
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param temporal the target object to be adjusted, not null
+ * @return the adjusted object, not null
+ * @throws DateTimeException if unable to make the adjustment
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ default Temporal adjustInto(Temporal temporal) {
+ return temporal
+ .with(EPOCH_DAY, toLocalDate().toEpochDay())
+ .with(NANO_OF_DAY, toLocalTime().toNanoOfDay());
+ }
+
+ /**
+ * Formats this date-time using the specified formatter.
+ * <p>
+ * This date-time will be passed to the formatter to produce a string.
+ * <p>
+ * The default implementation must behave as follows:
+ * <pre>
+ * return formatter.format(this);
+ * </pre>
+ *
+ * @param formatter the formatter to use, not null
+ * @return the formatted date-time string, not null
+ * @throws DateTimeException if an error occurs during printing
+ */
+ default String format(DateTimeFormatter formatter) {
+ Objects.requireNonNull(formatter, "formatter");
+ return formatter.format(this);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Combines this time with a time-zone to create a {@code ChronoZonedDateTime}.
+ * <p>
+ * This returns a {@code ChronoZonedDateTime} formed from this date-time at the
+ * specified time-zone. The result will match this date-time as closely as possible.
+ * Time-zone rules, such as daylight savings, mean that not every local date-time
+ * is valid for the specified zone, thus the local date-time may be adjusted.
+ * <p>
+ * The local date-time is resolved to a single instant on the time-line.
+ * This is achieved by finding a valid offset from UTC/Greenwich for the local
+ * date-time as defined by the {@link ZoneRules rules} of the zone ID.
+ *<p>
+ * In most cases, there is only one valid offset for a local date-time.
+ * In the case of an overlap, where clocks are set back, there are two valid offsets.
+ * This method uses the earlier offset typically corresponding to "summer".
+ * <p>
+ * In the case of a gap, where clocks jump forward, there is no valid offset.
+ * Instead, the local date-time is adjusted to be later by the length of the gap.
+ * For a typical one hour daylight savings change, the local date-time will be
+ * moved one hour later into the offset typically corresponding to "summer".
+ * <p>
+ * To obtain the later offset during an overlap, call
+ * {@link ChronoZonedDateTime#withLaterOffsetAtOverlap()} on the result of this method.
+ *
+ * @param zone the time-zone to use, not null
+ * @return the zoned date-time formed from this date-time, not null
+ */
+ ChronoZonedDateTime<D> atZone(ZoneId zone);
+
+ //-----------------------------------------------------------------------
+ /**
+ * Converts this date-time to an {@code Instant}.
+ * <p>
+ * This combines this local date-time and the specified offset to form
+ * an {@code Instant}.
+ * <p>
+ * This default implementation calculates from the epoch-day of the date and the
+ * second-of-day of the time.
+ *
+ * @param offset the offset to use for the conversion, not null
+ * @return an {@code Instant} representing the same instant, not null
+ */
+ default Instant toInstant(ZoneOffset offset) {
+ return Instant.ofEpochSecond(toEpochSecond(offset), toLocalTime().getNano());
+ }
+
+ /**
+ * Converts this date-time to the number of seconds from the epoch
+ * of 1970-01-01T00:00:00Z.
+ * <p>
+ * This combines this local date-time and the specified offset to calculate the
+ * epoch-second value, which is the number of elapsed seconds from 1970-01-01T00:00:00Z.
+ * Instants on the time-line after the epoch are positive, earlier are negative.
+ * <p>
+ * This default implementation calculates from the epoch-day of the date and the
+ * second-of-day of the time.
+ *
+ * @param offset the offset to use for the conversion, not null
+ * @return the number of seconds from the epoch of 1970-01-01T00:00:00Z
+ */
+ default long toEpochSecond(ZoneOffset offset) {
+ Objects.requireNonNull(offset, "offset");
+ long epochDay = toLocalDate().toEpochDay();
+ long secs = epochDay * 86400 + toLocalTime().toSecondOfDay();
+ secs -= offset.getTotalSeconds();
+ return secs;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Compares this date-time to another date-time, including the chronology.
+ * <p>
+ * The comparison is based first on the underlying time-line date-time, then
+ * on the chronology.
+ * It is "consistent with equals", as defined by {@link Comparable}.
+ * <p>
+ * For example, the following is the comparator order:
+ * <ol>
+ * <li>{@code 2012-12-03T12:00 (ISO)}</li>
+ * <li>{@code 2012-12-04T12:00 (ISO)}</li>
+ * <li>{@code 2555-12-04T12:00 (ThaiBuddhist)}</li>
+ * <li>{@code 2012-12-05T12:00 (ISO)}</li>
+ * </ol>
+ * Values #2 and #3 represent the same date-time on the time-line.
+ * When two values represent the same date-time, the chronology ID is compared to distinguish them.
+ * This step is needed to make the ordering "consistent with equals".
+ * <p>
+ * If all the date-time objects being compared are in the same chronology, then the
+ * additional chronology stage is not required and only the local date-time is used.
+ * <p>
+ * This default implementation performs the comparison defined above.
+ *
+ * @param other the other date-time to compare to, not null
+ * @return the comparator value, negative if less, positive if greater
+ */
+ @Override
+ default int compareTo(ChronoLocalDateTime<?> other) {
+ int cmp = toLocalDate().compareTo(other.toLocalDate());
+ if (cmp == 0) {
+ cmp = toLocalTime().compareTo(other.toLocalTime());
+ if (cmp == 0) {
+ cmp = getChronology().compareTo(other.getChronology());
+ }
+ }
+ return cmp;
+ }
+
+ /**
+ * Checks if this date-time is after the specified date-time ignoring the chronology.
+ * <p>
+ * This method differs from the comparison in {@link #compareTo} in that it
+ * only compares the underlying date-time and not the chronology.
+ * This allows dates in different calendar systems to be compared based
+ * on the time-line position.
+ * <p>
+ * This default implementation performs the comparison based on the epoch-day
+ * and nano-of-day.
+ *
+ * @param other the other date-time to compare to, not null
+ * @return true if this is after the specified date-time
+ */
+ default boolean isAfter(ChronoLocalDateTime<?> other) {
+ long thisEpDay = this.toLocalDate().toEpochDay();
+ long otherEpDay = other.toLocalDate().toEpochDay();
+ return thisEpDay > otherEpDay ||
+ (thisEpDay == otherEpDay && this.toLocalTime().toNanoOfDay() > other.toLocalTime().toNanoOfDay());
+ }
+
+ /**
+ * Checks if this date-time is before the specified date-time ignoring the chronology.
+ * <p>
+ * This method differs from the comparison in {@link #compareTo} in that it
+ * only compares the underlying date-time and not the chronology.
+ * This allows dates in different calendar systems to be compared based
+ * on the time-line position.
+ * <p>
+ * This default implementation performs the comparison based on the epoch-day
+ * and nano-of-day.
+ *
+ * @param other the other date-time to compare to, not null
+ * @return true if this is before the specified date-time
+ */
+ default boolean isBefore(ChronoLocalDateTime<?> other) {
+ long thisEpDay = this.toLocalDate().toEpochDay();
+ long otherEpDay = other.toLocalDate().toEpochDay();
+ return thisEpDay < otherEpDay ||
+ (thisEpDay == otherEpDay && this.toLocalTime().toNanoOfDay() < other.toLocalTime().toNanoOfDay());
+ }
+
+ /**
+ * Checks if this date-time is equal to the specified date-time ignoring the chronology.
+ * <p>
+ * This method differs from the comparison in {@link #compareTo} in that it
+ * only compares the underlying date and time and not the chronology.
+ * This allows date-times in different calendar systems to be compared based
+ * on the time-line position.
+ * <p>
+ * This default implementation performs the comparison based on the epoch-day
+ * and nano-of-day.
+ *
+ * @param other the other date-time to compare to, not null
+ * @return true if the underlying date-time is equal to the specified date-time on the timeline
+ */
+ default boolean isEqual(ChronoLocalDateTime<?> other) {
+ // Do the time check first, it is cheaper than computing EPOCH day.
+ return this.toLocalTime().toNanoOfDay() == other.toLocalTime().toNanoOfDay() &&
+ this.toLocalDate().toEpochDay() == other.toLocalDate().toEpochDay();
+ }
+
+ /**
+ * Checks if this date-time is equal to another date-time, including the chronology.
+ * <p>
+ * Compares this date-time with another ensuring that the date-time and chronology are the same.
+ *
+ * @param obj the object to check, null returns false
+ * @return true if this is equal to the other date
+ */
+ @Override
+ boolean equals(Object obj);
+
+ /**
+ * A hash code for this date-time.
+ *
+ * @return a suitable hash code
+ */
+ @Override
+ int hashCode();
+
+ //-----------------------------------------------------------------------
+ /**
+ * Outputs this date-time as a {@code String}.
+ * <p>
+ * The output will include the full local date-time.
+ *
+ * @return a string representation of this date-time, not null
+ */
+ @Override
+ String toString();
+
+}
diff --git a/java/time/chrono/ChronoLocalDateTimeImpl.java b/java/time/chrono/ChronoLocalDateTimeImpl.java
new file mode 100644
index 0000000..ac5f7b4
--- /dev/null
+++ b/java/time/chrono/ChronoLocalDateTimeImpl.java
@@ -0,0 +1,459 @@
+/*
+ * Copyright (c) 2012, 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) 2007-2012, 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.EPOCH_DAY;
+
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInput;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutput;
+import java.io.Serializable;
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.time.temporal.ChronoField;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.Temporal;
+import java.time.temporal.TemporalAdjuster;
+import java.time.temporal.TemporalField;
+import java.time.temporal.TemporalUnit;
+import java.time.temporal.ValueRange;
+import java.util.Objects;
+
+/**
+ * A date-time without a time-zone for the calendar neutral API.
+ * <p>
+ * {@code ChronoLocalDateTime} is an immutable date-time object that represents a date-time, often
+ * viewed as year-month-day-hour-minute-second. This object can also access other
+ * fields such as day-of-year, day-of-week and week-of-year.
+ * <p>
+ * This class stores all date and time fields, to a precision of nanoseconds.
+ * It does not store or represent a time-zone. For example, the value
+ * "2nd October 2007 at 13:45.30.123456789" can be stored in an {@code ChronoLocalDateTime}.
+ *
+ * @implSpec
+ * This class is immutable and thread-safe.
+ * @serial
+ * @param <D> the concrete type for the date of this date-time
+ * @since 1.8
+ */
+final class ChronoLocalDateTimeImpl<D extends ChronoLocalDate>
+ implements ChronoLocalDateTime<D>, Temporal, TemporalAdjuster, Serializable {
+
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = 4556003607393004514L;
+ /**
+ * Hours per day.
+ */
+ static final int HOURS_PER_DAY = 24;
+ /**
+ * Minutes per hour.
+ */
+ static final int MINUTES_PER_HOUR = 60;
+ /**
+ * Minutes per day.
+ */
+ static final int MINUTES_PER_DAY = MINUTES_PER_HOUR * HOURS_PER_DAY;
+ /**
+ * Seconds per minute.
+ */
+ static final int SECONDS_PER_MINUTE = 60;
+ /**
+ * Seconds per hour.
+ */
+ static final int SECONDS_PER_HOUR = SECONDS_PER_MINUTE * MINUTES_PER_HOUR;
+ /**
+ * Seconds per day.
+ */
+ static final int SECONDS_PER_DAY = SECONDS_PER_HOUR * HOURS_PER_DAY;
+ /**
+ * Milliseconds per day.
+ */
+ static final long MILLIS_PER_DAY = SECONDS_PER_DAY * 1000L;
+ /**
+ * Microseconds per day.
+ */
+ static final long MICROS_PER_DAY = SECONDS_PER_DAY * 1000_000L;
+ /**
+ * Nanos per second.
+ */
+ static final long NANOS_PER_SECOND = 1000_000_000L;
+ /**
+ * Nanos per minute.
+ */
+ static final long NANOS_PER_MINUTE = NANOS_PER_SECOND * SECONDS_PER_MINUTE;
+ /**
+ * Nanos per hour.
+ */
+ static final long NANOS_PER_HOUR = NANOS_PER_MINUTE * MINUTES_PER_HOUR;
+ /**
+ * Nanos per day.
+ */
+ static final long NANOS_PER_DAY = NANOS_PER_HOUR * HOURS_PER_DAY;
+
+ /**
+ * The date part.
+ */
+ private final transient D date;
+ /**
+ * The time part.
+ */
+ private final transient LocalTime time;
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code ChronoLocalDateTime} from a date and time.
+ *
+ * @param date the local date, not null
+ * @param time the local time, not null
+ * @return the local date-time, not null
+ */
+ static <R extends ChronoLocalDate> ChronoLocalDateTimeImpl<R> of(R date, LocalTime time) {
+ return new ChronoLocalDateTimeImpl<>(date, time);
+ }
+
+ /**
+ * Casts the {@code Temporal} to {@code ChronoLocalDateTime} ensuring it bas the specified chronology.
+ *
+ * @param chrono the chronology to check for, not null
+ * @param temporal a date-time to cast, not null
+ * @return the date-time checked and cast to {@code ChronoLocalDateTime}, not null
+ * @throws ClassCastException if the date-time cannot be cast to ChronoLocalDateTimeImpl
+ * or the chronology is not equal this Chronology
+ */
+ static <R extends ChronoLocalDate> ChronoLocalDateTimeImpl<R> ensureValid(Chronology chrono, Temporal temporal) {
+ @SuppressWarnings("unchecked")
+ ChronoLocalDateTimeImpl<R> other = (ChronoLocalDateTimeImpl<R>) temporal;
+ if (chrono.equals(other.getChronology()) == false) {
+ throw new ClassCastException("Chronology mismatch, required: " + chrono.getId()
+ + ", actual: " + other.getChronology().getId());
+ }
+ return other;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param date the date part of the date-time, not null
+ * @param time the time part of the date-time, not null
+ */
+ private ChronoLocalDateTimeImpl(D date, LocalTime time) {
+ Objects.requireNonNull(date, "date");
+ Objects.requireNonNull(time, "time");
+ this.date = date;
+ this.time = time;
+ }
+
+ /**
+ * Returns a copy of this date-time with the new date and time, checking
+ * to see if a new object is in fact required.
+ *
+ * @param newDate the date of the new date-time, not null
+ * @param newTime the time of the new date-time, not null
+ * @return the date-time, not null
+ */
+ private ChronoLocalDateTimeImpl<D> with(Temporal newDate, LocalTime newTime) {
+ if (date == newDate && time == newTime) {
+ return this;
+ }
+ // Validate that the new Temporal is a ChronoLocalDate (and not something else)
+ D cd = ChronoLocalDateImpl.ensureValid(date.getChronology(), newDate);
+ return new ChronoLocalDateTimeImpl<>(cd, newTime);
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ public D toLocalDate() {
+ return date;
+ }
+
+ @Override
+ public LocalTime toLocalTime() {
+ return time;
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ public boolean isSupported(TemporalField field) {
+ if (field instanceof ChronoField) {
+ ChronoField f = (ChronoField) field;
+ return f.isDateBased() || f.isTimeBased();
+ }
+ return field != null && field.isSupportedBy(this);
+ }
+
+ @Override
+ public ValueRange range(TemporalField field) {
+ if (field instanceof ChronoField) {
+ ChronoField f = (ChronoField) field;
+ return (f.isTimeBased() ? time.range(field) : date.range(field));
+ }
+ return field.rangeRefinedBy(this);
+ }
+
+ @Override
+ public int get(TemporalField field) {
+ if (field instanceof ChronoField) {
+ ChronoField f = (ChronoField) field;
+ return (f.isTimeBased() ? time.get(field) : date.get(field));
+ }
+ return range(field).checkValidIntValue(getLong(field), field);
+ }
+
+ @Override
+ public long getLong(TemporalField field) {
+ if (field instanceof ChronoField) {
+ ChronoField f = (ChronoField) field;
+ return (f.isTimeBased() ? time.getLong(field) : date.getLong(field));
+ }
+ return field.getFrom(this);
+ }
+
+ //-----------------------------------------------------------------------
+ @SuppressWarnings("unchecked")
+ @Override
+ public ChronoLocalDateTimeImpl<D> with(TemporalAdjuster adjuster) {
+ if (adjuster instanceof ChronoLocalDate) {
+ // The Chronology is checked in with(date,time)
+ return with((ChronoLocalDate) adjuster, time);
+ } else if (adjuster instanceof LocalTime) {
+ return with(date, (LocalTime) adjuster);
+ } else if (adjuster instanceof ChronoLocalDateTimeImpl) {
+ return ChronoLocalDateTimeImpl.ensureValid(date.getChronology(), (ChronoLocalDateTimeImpl<?>) adjuster);
+ }
+ return ChronoLocalDateTimeImpl.ensureValid(date.getChronology(), (ChronoLocalDateTimeImpl<?>) adjuster.adjustInto(this));
+ }
+
+ @Override
+ public ChronoLocalDateTimeImpl<D> with(TemporalField field, long newValue) {
+ if (field instanceof ChronoField) {
+ ChronoField f = (ChronoField) field;
+ if (f.isTimeBased()) {
+ return with(date, time.with(field, newValue));
+ } else {
+ return with(date.with(field, newValue), time);
+ }
+ }
+ return ChronoLocalDateTimeImpl.ensureValid(date.getChronology(), field.adjustInto(this, newValue));
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ public ChronoLocalDateTimeImpl<D> plus(long amountToAdd, TemporalUnit unit) {
+ if (unit instanceof ChronoUnit) {
+ ChronoUnit f = (ChronoUnit) unit;
+ switch (f) {
+ case NANOS: return plusNanos(amountToAdd);
+ case MICROS: return plusDays(amountToAdd / MICROS_PER_DAY).plusNanos((amountToAdd % MICROS_PER_DAY) * 1000);
+ case MILLIS: return plusDays(amountToAdd / MILLIS_PER_DAY).plusNanos((amountToAdd % MILLIS_PER_DAY) * 1000000);
+ case SECONDS: return plusSeconds(amountToAdd);
+ case MINUTES: return plusMinutes(amountToAdd);
+ case HOURS: return plusHours(amountToAdd);
+ case HALF_DAYS: return plusDays(amountToAdd / 256).plusHours((amountToAdd % 256) * 12); // no overflow (256 is multiple of 2)
+ }
+ return with(date.plus(amountToAdd, unit), time);
+ }
+ return ChronoLocalDateTimeImpl.ensureValid(date.getChronology(), unit.addTo(this, amountToAdd));
+ }
+
+ private ChronoLocalDateTimeImpl<D> plusDays(long days) {
+ return with(date.plus(days, ChronoUnit.DAYS), time);
+ }
+
+ private ChronoLocalDateTimeImpl<D> plusHours(long hours) {
+ return plusWithOverflow(date, hours, 0, 0, 0);
+ }
+
+ private ChronoLocalDateTimeImpl<D> plusMinutes(long minutes) {
+ return plusWithOverflow(date, 0, minutes, 0, 0);
+ }
+
+ ChronoLocalDateTimeImpl<D> plusSeconds(long seconds) {
+ return plusWithOverflow(date, 0, 0, seconds, 0);
+ }
+
+ private ChronoLocalDateTimeImpl<D> plusNanos(long nanos) {
+ return plusWithOverflow(date, 0, 0, 0, nanos);
+ }
+
+ //-----------------------------------------------------------------------
+ private ChronoLocalDateTimeImpl<D> plusWithOverflow(D newDate, long hours, long minutes, long seconds, long nanos) {
+ // 9223372036854775808 long, 2147483648 int
+ if ((hours | minutes | seconds | nanos) == 0) {
+ return with(newDate, time);
+ }
+ long totDays = nanos / NANOS_PER_DAY + // max/24*60*60*1B
+ seconds / SECONDS_PER_DAY + // max/24*60*60
+ minutes / MINUTES_PER_DAY + // max/24*60
+ hours / HOURS_PER_DAY; // max/24
+ long totNanos = nanos % NANOS_PER_DAY + // max 86400000000000
+ (seconds % SECONDS_PER_DAY) * NANOS_PER_SECOND + // max 86400000000000
+ (minutes % MINUTES_PER_DAY) * NANOS_PER_MINUTE + // max 86400000000000
+ (hours % HOURS_PER_DAY) * NANOS_PER_HOUR; // max 86400000000000
+ long curNoD = time.toNanoOfDay(); // max 86400000000000
+ totNanos = totNanos + curNoD; // total 432000000000000
+ totDays += Math.floorDiv(totNanos, NANOS_PER_DAY);
+ long newNoD = Math.floorMod(totNanos, NANOS_PER_DAY);
+ LocalTime newTime = (newNoD == curNoD ? time : LocalTime.ofNanoOfDay(newNoD));
+ return with(newDate.plus(totDays, ChronoUnit.DAYS), newTime);
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ public ChronoZonedDateTime<D> atZone(ZoneId zone) {
+ return ChronoZonedDateTimeImpl.ofBest(this, zone, null);
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ public long until(Temporal endExclusive, TemporalUnit unit) {
+ Objects.requireNonNull(endExclusive, "endExclusive");
+ @SuppressWarnings("unchecked")
+ ChronoLocalDateTime<D> end = (ChronoLocalDateTime<D>) getChronology().localDateTime(endExclusive);
+ if (unit instanceof ChronoUnit) {
+ if (unit.isTimeBased()) {
+ long amount = end.getLong(EPOCH_DAY) - date.getLong(EPOCH_DAY);
+ switch ((ChronoUnit) unit) {
+ case NANOS: amount = Math.multiplyExact(amount, NANOS_PER_DAY); break;
+ case MICROS: amount = Math.multiplyExact(amount, MICROS_PER_DAY); break;
+ case MILLIS: amount = Math.multiplyExact(amount, MILLIS_PER_DAY); break;
+ case SECONDS: amount = Math.multiplyExact(amount, SECONDS_PER_DAY); break;
+ case MINUTES: amount = Math.multiplyExact(amount, MINUTES_PER_DAY); break;
+ case HOURS: amount = Math.multiplyExact(amount, HOURS_PER_DAY); break;
+ case HALF_DAYS: amount = Math.multiplyExact(amount, 2); break;
+ }
+ return Math.addExact(amount, time.until(end.toLocalTime(), unit));
+ }
+ ChronoLocalDate endDate = end.toLocalDate();
+ if (end.toLocalTime().isBefore(time)) {
+ endDate = endDate.minus(1, ChronoUnit.DAYS);
+ }
+ return date.until(endDate, unit);
+ }
+ Objects.requireNonNull(unit, "unit");
+ return unit.between(this, end);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Writes the ChronoLocalDateTime using a
+ * <a href="../../../serialized-form.html#java.time.chrono.Ser">dedicated serialized form</a>.
+ * @serialData
+ * <pre>
+ * out.writeByte(2); // identifies a ChronoLocalDateTime
+ * out.writeObject(toLocalDate());
+ * out.witeObject(toLocalTime());
+ * </pre>
+ *
+ * @return the instance of {@code Ser}, not null
+ */
+ private Object writeReplace() {
+ return new Ser(Ser.CHRONO_LOCAL_DATE_TIME_TYPE, this);
+ }
+
+ /**
+ * Defend against malicious streams.
+ *
+ * @param s the stream to read
+ * @throws InvalidObjectException always
+ */
+ private void readObject(ObjectInputStream s) throws InvalidObjectException {
+ throw new InvalidObjectException("Deserialization via serialization delegate");
+ }
+
+ void writeExternal(ObjectOutput out) throws IOException {
+ out.writeObject(date);
+ out.writeObject(time);
+ }
+
+ static ChronoLocalDateTime<?> readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+ ChronoLocalDate date = (ChronoLocalDate) in.readObject();
+ LocalTime time = (LocalTime) in.readObject();
+ return date.atTime(time);
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof ChronoLocalDateTime) {
+ return compareTo((ChronoLocalDateTime<?>) obj) == 0;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return toLocalDate().hashCode() ^ toLocalTime().hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return toLocalDate().toString() + 'T' + toLocalTime().toString();
+ }
+
+}
diff --git a/java/time/chrono/ChronoPeriod.java b/java/time/chrono/ChronoPeriod.java
new file mode 100644
index 0000000..0ac1d39
--- /dev/null
+++ b/java/time/chrono/ChronoPeriod.java
@@ -0,0 +1,365 @@
+/*
+ * 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.TemporalAmount;
+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 ChronoPeriod} 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 ChronoPeriod} 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 ChronoPeriod} 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 ChronoPeriod} 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 ChronoPeriod} 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/java/time/chrono/ChronoPeriodImpl.java b/java/time/chrono/ChronoPeriodImpl.java
new file mode 100644
index 0000000..911144d
--- /dev/null
+++ b/java/time/chrono/ChronoPeriodImpl.java
@@ -0,0 +1,401 @@
+/*
+ * 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.ObjectInputStream;
+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.TemporalQueries;
+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 static final 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(TemporalQueries.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.
+ *
+ * @param s the stream to read
+ * @throws InvalidObjectException always
+ */
+ private void readObject(ObjectInputStream s) 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/java/time/chrono/ChronoZonedDateTime.java b/java/time/chrono/ChronoZonedDateTime.java
new file mode 100644
index 0000000..cae9adc
--- /dev/null
+++ b/java/time/chrono/ChronoZonedDateTime.java
@@ -0,0 +1,677 @@
+/*
+ * Copyright (c) 2012, 2015, 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) 2007-2012, 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.INSTANT_SECONDS;
+import static java.time.temporal.ChronoField.OFFSET_SECONDS;
+import static java.time.temporal.ChronoUnit.FOREVER;
+import static java.time.temporal.ChronoUnit.NANOS;
+
+import java.time.DateTimeException;
+import java.time.Instant;
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoField;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.Temporal;
+import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalAdjuster;
+import java.time.temporal.TemporalAmount;
+import java.time.temporal.TemporalField;
+import java.time.temporal.TemporalQueries;
+import java.time.temporal.TemporalQuery;
+import java.time.temporal.TemporalUnit;
+import java.time.temporal.UnsupportedTemporalTypeException;
+import java.time.temporal.ValueRange;
+import java.util.Comparator;
+import java.util.Objects;
+
+/**
+ * A date-time with a time-zone in an arbitrary chronology,
+ * intended for advanced globalization use cases.
+ * <p>
+ * <b>Most applications should declare method signatures, fields and variables
+ * as {@link ZonedDateTime}, not this interface.</b>
+ * <p>
+ * A {@code ChronoZonedDateTime} is the abstract representation of an offset date-time
+ * where the {@code Chronology chronology}, or calendar system, is pluggable.
+ * The date-time is defined in terms of fields expressed by {@link TemporalField},
+ * where most common implementations are defined in {@link ChronoField}.
+ * The chronology defines how the calendar system operates and the meaning of
+ * the standard fields.
+ *
+ * <h3>When to use this interface</h3>
+ * The design of the API encourages the use of {@code ZonedDateTime} rather than this
+ * interface, even in the case where the application needs to deal with multiple
+ * calendar systems. The rationale for this is explored in detail in {@link ChronoLocalDate}.
+ * <p>
+ * Ensure that the discussion in {@code ChronoLocalDate} has been read and understood
+ * before using this interface.
+ *
+ * @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.
+ *
+ * @param <D> the concrete type for the date of this date-time
+ * @since 1.8
+ */
+public interface ChronoZonedDateTime<D extends ChronoLocalDate>
+ extends Temporal, Comparable<ChronoZonedDateTime<?>> {
+
+ /**
+ * Gets a comparator that compares {@code ChronoZonedDateTime} in
+ * time-line order ignoring the chronology.
+ * <p>
+ * This comparator differs from the comparison in {@link #compareTo} in that it
+ * only compares the underlying instant and not the chronology.
+ * This allows dates in different calendar systems to be compared based
+ * on the position of the date-time on the instant time-line.
+ * The underlying comparison is equivalent to comparing the epoch-second and nano-of-second.
+ *
+ * @return a comparator that compares in time-line order ignoring the chronology
+ * @see #isAfter
+ * @see #isBefore
+ * @see #isEqual
+ */
+ static Comparator<ChronoZonedDateTime<?>> timeLineOrder() {
+ return AbstractChronology.INSTANT_ORDER;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code ChronoZonedDateTime} from a temporal object.
+ * <p>
+ * This creates a zoned date-time based on the specified temporal.
+ * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
+ * which this factory converts to an instance of {@code ChronoZonedDateTime}.
+ * <p>
+ * The conversion extracts and combines the chronology, date, time and zone
+ * from the temporal object. The behavior is equivalent to using
+ * {@link Chronology#zonedDateTime(TemporalAccessor)} with the extracted chronology.
+ * Implementations are permitted to perform optimizations such as accessing
+ * those fields that are equivalent to the relevant objects.
+ * <p>
+ * This method matches the signature of the functional interface {@link TemporalQuery}
+ * allowing it to be used as a query via method reference, {@code ChronoZonedDateTime::from}.
+ *
+ * @param temporal the temporal object to convert, not null
+ * @return the date-time, not null
+ * @throws DateTimeException if unable to convert to a {@code ChronoZonedDateTime}
+ * @see Chronology#zonedDateTime(TemporalAccessor)
+ */
+ static ChronoZonedDateTime<?> from(TemporalAccessor temporal) {
+ if (temporal instanceof ChronoZonedDateTime) {
+ return (ChronoZonedDateTime<?>) temporal;
+ }
+ Objects.requireNonNull(temporal, "temporal");
+ Chronology chrono = temporal.query(TemporalQueries.chronology());
+ if (chrono == null) {
+ throw new DateTimeException("Unable to obtain ChronoZonedDateTime from TemporalAccessor: " + temporal.getClass());
+ }
+ return chrono.zonedDateTime(temporal);
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ default ValueRange range(TemporalField field) {
+ if (field instanceof ChronoField) {
+ if (field == INSTANT_SECONDS || field == OFFSET_SECONDS) {
+ return field.range();
+ }
+ return toLocalDateTime().range(field);
+ }
+ return field.rangeRefinedBy(this);
+ }
+
+ @Override
+ default int get(TemporalField field) {
+ if (field instanceof ChronoField) {
+ switch ((ChronoField) field) {
+ case INSTANT_SECONDS:
+ throw new UnsupportedTemporalTypeException("Invalid field 'InstantSeconds' for get() method, use getLong() instead");
+ case OFFSET_SECONDS:
+ return getOffset().getTotalSeconds();
+ }
+ return toLocalDateTime().get(field);
+ }
+ return Temporal.super.get(field);
+ }
+
+ @Override
+ default long getLong(TemporalField field) {
+ if (field instanceof ChronoField) {
+ switch ((ChronoField) field) {
+ case INSTANT_SECONDS: return toEpochSecond();
+ case OFFSET_SECONDS: return getOffset().getTotalSeconds();
+ }
+ return toLocalDateTime().getLong(field);
+ }
+ return field.getFrom(this);
+ }
+
+ /**
+ * Gets the local date part of this date-time.
+ * <p>
+ * This returns a local date with the same year, month and day
+ * as this date-time.
+ *
+ * @return the date part of this date-time, not null
+ */
+ default D toLocalDate() {
+ return toLocalDateTime().toLocalDate();
+ }
+
+ /**
+ * Gets the local time part of this date-time.
+ * <p>
+ * This returns a local time with the same hour, minute, second and
+ * nanosecond as this date-time.
+ *
+ * @return the time part of this date-time, not null
+ */
+ default LocalTime toLocalTime() {
+ return toLocalDateTime().toLocalTime();
+ }
+
+ /**
+ * Gets the local date-time part of this date-time.
+ * <p>
+ * This returns a local date with the same year, month and day
+ * as this date-time.
+ *
+ * @return the local date-time part of this date-time, not null
+ */
+ ChronoLocalDateTime<D> toLocalDateTime();
+
+ /**
+ * Gets the chronology of this date-time.
+ * <p>
+ * The {@code Chronology} represents the calendar system in use.
+ * The era and other fields in {@link ChronoField} are defined by the chronology.
+ *
+ * @return the chronology, not null
+ */
+ default Chronology getChronology() {
+ return toLocalDate().getChronology();
+ }
+
+ /**
+ * Gets the zone offset, such as '+01:00'.
+ * <p>
+ * This is the offset of the local date-time from UTC/Greenwich.
+ *
+ * @return the zone offset, not null
+ */
+ ZoneOffset getOffset();
+
+ /**
+ * Gets the zone ID, such as 'Europe/Paris'.
+ * <p>
+ * This returns the stored time-zone id used to determine the time-zone rules.
+ *
+ * @return the zone ID, not null
+ */
+ ZoneId getZone();
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this date-time changing the zone offset to the
+ * earlier of the two valid offsets at a local time-line overlap.
+ * <p>
+ * This method only has any effect when the local time-line overlaps, such as
+ * at an autumn daylight savings cutover. In this scenario, there are two
+ * valid offsets for the local date-time. Calling this method will return
+ * a zoned date-time with the earlier of the two selected.
+ * <p>
+ * If this method is called when it is not an overlap, {@code this}
+ * is returned.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @return a {@code ChronoZonedDateTime} based on this date-time with the earlier offset, not null
+ * @throws DateTimeException if no rules can be found for the zone
+ * @throws DateTimeException if no rules are valid for this date-time
+ */
+ ChronoZonedDateTime<D> withEarlierOffsetAtOverlap();
+
+ /**
+ * Returns a copy of this date-time changing the zone offset to the
+ * later of the two valid offsets at a local time-line overlap.
+ * <p>
+ * This method only has any effect when the local time-line overlaps, such as
+ * at an autumn daylight savings cutover. In this scenario, there are two
+ * valid offsets for the local date-time. Calling this method will return
+ * a zoned date-time with the later of the two selected.
+ * <p>
+ * If this method is called when it is not an overlap, {@code this}
+ * is returned.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @return a {@code ChronoZonedDateTime} based on this date-time with the later offset, not null
+ * @throws DateTimeException if no rules can be found for the zone
+ * @throws DateTimeException if no rules are valid for this date-time
+ */
+ ChronoZonedDateTime<D> withLaterOffsetAtOverlap();
+
+ /**
+ * Returns a copy of this date-time with a different time-zone,
+ * retaining the local date-time if possible.
+ * <p>
+ * This method changes the time-zone and retains the local date-time.
+ * The local date-time is only changed if it is invalid for the new zone.
+ * <p>
+ * To change the zone and adjust the local date-time,
+ * use {@link #withZoneSameInstant(ZoneId)}.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param zone the time-zone to change to, not null
+ * @return a {@code ChronoZonedDateTime} based on this date-time with the requested zone, not null
+ */
+ ChronoZonedDateTime<D> withZoneSameLocal(ZoneId zone);
+
+ /**
+ * Returns a copy of this date-time with a different time-zone,
+ * retaining the instant.
+ * <p>
+ * This method changes the time-zone and retains the instant.
+ * This normally results in a change to the local date-time.
+ * <p>
+ * This method is based on retaining the same instant, thus gaps and overlaps
+ * in the local time-line have no effect on the result.
+ * <p>
+ * To change the offset while keeping the local time,
+ * use {@link #withZoneSameLocal(ZoneId)}.
+ *
+ * @param zone the time-zone to change to, not null
+ * @return a {@code ChronoZonedDateTime} based on this date-time with the requested zone, not null
+ * @throws DateTimeException if the result exceeds the supported date range
+ */
+ ChronoZonedDateTime<D> withZoneSameInstant(ZoneId zone);
+
+ /**
+ * Checks if the specified field is supported.
+ * <p>
+ * This checks if the specified field can be queried on this date-time.
+ * If false, then calling the {@link #range(TemporalField) range},
+ * {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}
+ * methods will throw an exception.
+ * <p>
+ * The set of supported fields is defined by the chronology and normally includes
+ * all {@code ChronoField} fields.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}
+ * passing {@code this} as the argument.
+ * Whether the field is supported is determined by the field.
+ *
+ * @param field the field to check, null returns false
+ * @return true if the field can be queried, false if not
+ */
+ @Override
+ boolean isSupported(TemporalField field);
+
+ /**
+ * Checks if the specified unit is supported.
+ * <p>
+ * This checks if the specified unit can be added to or subtracted from this date-time.
+ * If false, then calling the {@link #plus(long, TemporalUnit)} and
+ * {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
+ * <p>
+ * The set of supported units is defined by the chronology and normally includes
+ * all {@code ChronoUnit} units except {@code FOREVER}.
+ * <p>
+ * If the unit is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
+ * passing {@code this} as the argument.
+ * Whether the unit is supported is determined by the unit.
+ *
+ * @param unit the unit to check, null returns false
+ * @return true if the unit can be added/subtracted, false if not
+ */
+ @Override
+ default boolean isSupported(TemporalUnit unit) {
+ if (unit instanceof ChronoUnit) {
+ return unit != FOREVER;
+ }
+ return unit != null && unit.isSupportedBy(this);
+ }
+
+ //-----------------------------------------------------------------------
+ // override for covariant return type
+ /**
+ * {@inheritDoc}
+ * @throws DateTimeException {@inheritDoc}
+ * @throws ArithmeticException {@inheritDoc}
+ */
+ @Override
+ default ChronoZonedDateTime<D> with(TemporalAdjuster adjuster) {
+ return ChronoZonedDateTimeImpl.ensureValid(getChronology(), Temporal.super.with(adjuster));
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws DateTimeException {@inheritDoc}
+ * @throws ArithmeticException {@inheritDoc}
+ */
+ @Override
+ ChronoZonedDateTime<D> with(TemporalField field, long newValue);
+
+ /**
+ * {@inheritDoc}
+ * @throws DateTimeException {@inheritDoc}
+ * @throws ArithmeticException {@inheritDoc}
+ */
+ @Override
+ default ChronoZonedDateTime<D> plus(TemporalAmount amount) {
+ return ChronoZonedDateTimeImpl.ensureValid(getChronology(), Temporal.super.plus(amount));
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws DateTimeException {@inheritDoc}
+ * @throws ArithmeticException {@inheritDoc}
+ */
+ @Override
+ ChronoZonedDateTime<D> plus(long amountToAdd, TemporalUnit unit);
+
+ /**
+ * {@inheritDoc}
+ * @throws DateTimeException {@inheritDoc}
+ * @throws ArithmeticException {@inheritDoc}
+ */
+ @Override
+ default ChronoZonedDateTime<D> minus(TemporalAmount amount) {
+ return ChronoZonedDateTimeImpl.ensureValid(getChronology(), Temporal.super.minus(amount));
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws DateTimeException {@inheritDoc}
+ * @throws ArithmeticException {@inheritDoc}
+ */
+ @Override
+ default ChronoZonedDateTime<D> minus(long amountToSubtract, TemporalUnit unit) {
+ return ChronoZonedDateTimeImpl.ensureValid(getChronology(), Temporal.super.minus(amountToSubtract, unit));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Queries this date-time using the specified query.
+ * <p>
+ * This queries this date-time using the specified query strategy object.
+ * The {@code TemporalQuery} object defines the logic to be used to
+ * obtain the result. Read the documentation of the query to understand
+ * what the result of this method will be.
+ * <p>
+ * The result of this method is obtained by invoking the
+ * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the
+ * specified query passing {@code this} as the argument.
+ *
+ * @param <R> the type of the result
+ * @param query the query to invoke, not null
+ * @return the query result, null may be returned (defined by the query)
+ * @throws DateTimeException if unable to query (defined by the query)
+ * @throws ArithmeticException if numeric overflow occurs (defined by the query)
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ default <R> R query(TemporalQuery<R> query) {
+ if (query == TemporalQueries.zone() || query == TemporalQueries.zoneId()) {
+ return (R) getZone();
+ } else if (query == TemporalQueries.offset()) {
+ return (R) getOffset();
+ } else if (query == TemporalQueries.localTime()) {
+ return (R) toLocalTime();
+ } else if (query == TemporalQueries.chronology()) {
+ return (R) getChronology();
+ } else if (query == TemporalQueries.precision()) {
+ return (R) NANOS;
+ }
+ // inline TemporalAccessor.super.query(query) as an optimization
+ // non-JDK classes are not permitted to make this optimization
+ return query.queryFrom(this);
+ }
+
+ /**
+ * Formats this date-time using the specified formatter.
+ * <p>
+ * This date-time will be passed to the formatter to produce a string.
+ * <p>
+ * The default implementation must behave as follows:
+ * <pre>
+ * return formatter.format(this);
+ * </pre>
+ *
+ * @param formatter the formatter to use, not null
+ * @return the formatted date-time string, not null
+ * @throws DateTimeException if an error occurs during printing
+ */
+ default String format(DateTimeFormatter formatter) {
+ Objects.requireNonNull(formatter, "formatter");
+ return formatter.format(this);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Converts this date-time to an {@code Instant}.
+ * <p>
+ * This returns an {@code Instant} representing the same point on the
+ * time-line as this date-time. The calculation combines the
+ * {@linkplain #toLocalDateTime() local date-time} and
+ * {@linkplain #getOffset() offset}.
+ *
+ * @return an {@code Instant} representing the same instant, not null
+ */
+ default Instant toInstant() {
+ return Instant.ofEpochSecond(toEpochSecond(), toLocalTime().getNano());
+ }
+
+ /**
+ * Converts this date-time to the number of seconds from the epoch
+ * of 1970-01-01T00:00:00Z.
+ * <p>
+ * This uses the {@linkplain #toLocalDateTime() local date-time} and
+ * {@linkplain #getOffset() offset} to calculate the epoch-second value,
+ * which is the number of elapsed seconds from 1970-01-01T00:00:00Z.
+ * Instants on the time-line after the epoch are positive, earlier are negative.
+ *
+ * @return the number of seconds from the epoch of 1970-01-01T00:00:00Z
+ */
+ default long toEpochSecond() {
+ long epochDay = toLocalDate().toEpochDay();
+ long secs = epochDay * 86400 + toLocalTime().toSecondOfDay();
+ secs -= getOffset().getTotalSeconds();
+ return secs;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Compares this date-time to another date-time, including the chronology.
+ * <p>
+ * The comparison is based first on the instant, then on the local date-time,
+ * then on the zone ID, then on the chronology.
+ * It is "consistent with equals", as defined by {@link Comparable}.
+ * <p>
+ * If all the date-time objects being compared are in the same chronology, then the
+ * additional chronology stage is not required.
+ * <p>
+ * This default implementation performs the comparison defined above.
+ *
+ * @param other the other date-time to compare to, not null
+ * @return the comparator value, negative if less, positive if greater
+ */
+ @Override
+ default int compareTo(ChronoZonedDateTime<?> other) {
+ int cmp = Long.compare(toEpochSecond(), other.toEpochSecond());
+ if (cmp == 0) {
+ cmp = toLocalTime().getNano() - other.toLocalTime().getNano();
+ if (cmp == 0) {
+ cmp = toLocalDateTime().compareTo(other.toLocalDateTime());
+ if (cmp == 0) {
+ cmp = getZone().getId().compareTo(other.getZone().getId());
+ if (cmp == 0) {
+ cmp = getChronology().compareTo(other.getChronology());
+ }
+ }
+ }
+ }
+ return cmp;
+ }
+
+ /**
+ * Checks if the instant of this date-time is before that of the specified date-time.
+ * <p>
+ * This method differs from the comparison in {@link #compareTo} in that it
+ * only compares the instant of the date-time. This is equivalent to using
+ * {@code dateTime1.toInstant().isBefore(dateTime2.toInstant());}.
+ * <p>
+ * This default implementation performs the comparison based on the epoch-second
+ * and nano-of-second.
+ *
+ * @param other the other date-time to compare to, not null
+ * @return true if this point is before the specified date-time
+ */
+ default boolean isBefore(ChronoZonedDateTime<?> other) {
+ long thisEpochSec = toEpochSecond();
+ long otherEpochSec = other.toEpochSecond();
+ return thisEpochSec < otherEpochSec ||
+ (thisEpochSec == otherEpochSec && toLocalTime().getNano() < other.toLocalTime().getNano());
+ }
+
+ /**
+ * Checks if the instant of this date-time is after that of the specified date-time.
+ * <p>
+ * This method differs from the comparison in {@link #compareTo} in that it
+ * only compares the instant of the date-time. This is equivalent to using
+ * {@code dateTime1.toInstant().isAfter(dateTime2.toInstant());}.
+ * <p>
+ * This default implementation performs the comparison based on the epoch-second
+ * and nano-of-second.
+ *
+ * @param other the other date-time to compare to, not null
+ * @return true if this is after the specified date-time
+ */
+ default boolean isAfter(ChronoZonedDateTime<?> other) {
+ long thisEpochSec = toEpochSecond();
+ long otherEpochSec = other.toEpochSecond();
+ return thisEpochSec > otherEpochSec ||
+ (thisEpochSec == otherEpochSec && toLocalTime().getNano() > other.toLocalTime().getNano());
+ }
+
+ /**
+ * Checks if the instant of this date-time is equal to that of the specified date-time.
+ * <p>
+ * This method differs from the comparison in {@link #compareTo} and {@link #equals}
+ * in that it only compares the instant of the date-time. This is equivalent to using
+ * {@code dateTime1.toInstant().equals(dateTime2.toInstant());}.
+ * <p>
+ * This default implementation performs the comparison based on the epoch-second
+ * and nano-of-second.
+ *
+ * @param other the other date-time to compare to, not null
+ * @return true if the instant equals the instant of the specified date-time
+ */
+ default boolean isEqual(ChronoZonedDateTime<?> other) {
+ return toEpochSecond() == other.toEpochSecond() &&
+ toLocalTime().getNano() == other.toLocalTime().getNano();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if this date-time is equal to another date-time.
+ * <p>
+ * The comparison is based on the offset date-time and the zone.
+ * To compare for the same instant on the time-line, use {@link #compareTo}.
+ * Only objects of type {@code ChronoZonedDateTime} are compared, other types return false.
+ *
+ * @param obj the object to check, null returns false
+ * @return true if this is equal to the other date-time
+ */
+ @Override
+ boolean equals(Object obj);
+
+ /**
+ * A hash code for this date-time.
+ *
+ * @return a suitable hash code
+ */
+ @Override
+ int hashCode();
+
+ //-----------------------------------------------------------------------
+ /**
+ * Outputs this date-time as a {@code String}.
+ * <p>
+ * The output will include the full zoned date-time.
+ *
+ * @return a string representation of this date-time, not null
+ */
+ @Override
+ String toString();
+
+}
diff --git a/java/time/chrono/ChronoZonedDateTimeImpl.java b/java/time/chrono/ChronoZonedDateTimeImpl.java
new file mode 100644
index 0000000..abd21ee
--- /dev/null
+++ b/java/time/chrono/ChronoZonedDateTimeImpl.java
@@ -0,0 +1,391 @@
+/*
+ * Copyright (c) 2012, 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) 2007-2012, 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.ChronoUnit.SECONDS;
+
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInput;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutput;
+import java.io.Serializable;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.temporal.ChronoField;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.Temporal;
+import java.time.temporal.TemporalField;
+import java.time.temporal.TemporalUnit;
+import java.time.zone.ZoneOffsetTransition;
+import java.time.zone.ZoneRules;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A date-time with a time-zone in the calendar neutral API.
+ * <p>
+ * {@code ZoneChronoDateTime} is an immutable representation of a date-time with a time-zone.
+ * This class stores all date and time fields, to a precision of nanoseconds,
+ * as well as a time-zone and zone offset.
+ * <p>
+ * The purpose of storing the time-zone is to distinguish the ambiguous case where
+ * the local time-line overlaps, typically as a result of the end of daylight time.
+ * Information about the local-time can be obtained using methods on the time-zone.
+ *
+ * @implSpec
+ * This class is immutable and thread-safe.
+ *
+ * @serial Document the delegation of this class in the serialized-form specification.
+ * @param <D> the concrete type for the date of this date-time
+ * @since 1.8
+ */
+final class ChronoZonedDateTimeImpl<D extends ChronoLocalDate>
+ implements ChronoZonedDateTime<D>, Serializable {
+
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = -5261813987200935591L;
+
+ /**
+ * The local date-time.
+ */
+ private final transient ChronoLocalDateTimeImpl<D> dateTime;
+ /**
+ * The zone offset.
+ */
+ private final transient ZoneOffset offset;
+ /**
+ * The zone ID.
+ */
+ private final transient ZoneId zone;
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance from a local date-time using the preferred offset if possible.
+ *
+ * @param localDateTime the local date-time, not null
+ * @param zone the zone identifier, not null
+ * @param preferredOffset the zone offset, null if no preference
+ * @return the zoned date-time, not null
+ */
+ static <R extends ChronoLocalDate> ChronoZonedDateTime<R> ofBest(
+ ChronoLocalDateTimeImpl<R> localDateTime, ZoneId zone, ZoneOffset preferredOffset) {
+ Objects.requireNonNull(localDateTime, "localDateTime");
+ Objects.requireNonNull(zone, "zone");
+ if (zone instanceof ZoneOffset) {
+ return new ChronoZonedDateTimeImpl<>(localDateTime, (ZoneOffset) zone, zone);
+ }
+ ZoneRules rules = zone.getRules();
+ LocalDateTime isoLDT = LocalDateTime.from(localDateTime);
+ List<ZoneOffset> validOffsets = rules.getValidOffsets(isoLDT);
+ ZoneOffset offset;
+ if (validOffsets.size() == 1) {
+ offset = validOffsets.get(0);
+ } else if (validOffsets.size() == 0) {
+ ZoneOffsetTransition trans = rules.getTransition(isoLDT);
+ localDateTime = localDateTime.plusSeconds(trans.getDuration().getSeconds());
+ offset = trans.getOffsetAfter();
+ } else {
+ if (preferredOffset != null && validOffsets.contains(preferredOffset)) {
+ offset = preferredOffset;
+ } else {
+ offset = validOffsets.get(0);
+ }
+ }
+ Objects.requireNonNull(offset, "offset"); // protect against bad ZoneRules
+ return new ChronoZonedDateTimeImpl<>(localDateTime, offset, zone);
+ }
+
+ /**
+ * Obtains an instance from an instant using the specified time-zone.
+ *
+ * @param chrono the chronology, not null
+ * @param instant the instant, not null
+ * @param zone the zone identifier, not null
+ * @return the zoned date-time, not null
+ */
+ static ChronoZonedDateTimeImpl<?> ofInstant(Chronology chrono, Instant instant, ZoneId zone) {
+ ZoneRules rules = zone.getRules();
+ ZoneOffset offset = rules.getOffset(instant);
+ Objects.requireNonNull(offset, "offset"); // protect against bad ZoneRules
+ LocalDateTime ldt = LocalDateTime.ofEpochSecond(instant.getEpochSecond(), instant.getNano(), offset);
+ ChronoLocalDateTimeImpl<?> cldt = (ChronoLocalDateTimeImpl<?>)chrono.localDateTime(ldt);
+ return new ChronoZonedDateTimeImpl<>(cldt, offset, zone);
+ }
+
+ /**
+ * Obtains an instance from an {@code Instant}.
+ *
+ * @param instant the instant to create the date-time from, not null
+ * @param zone the time-zone to use, validated not null
+ * @return the zoned date-time, validated not null
+ */
+ @SuppressWarnings("unchecked")
+ private ChronoZonedDateTimeImpl<D> create(Instant instant, ZoneId zone) {
+ return (ChronoZonedDateTimeImpl<D>)ofInstant(getChronology(), instant, zone);
+ }
+
+ /**
+ * Casts the {@code Temporal} to {@code ChronoZonedDateTimeImpl} ensuring it bas the specified chronology.
+ *
+ * @param chrono the chronology to check for, not null
+ * @param temporal a date-time to cast, not null
+ * @return the date-time checked and cast to {@code ChronoZonedDateTimeImpl}, not null
+ * @throws ClassCastException if the date-time cannot be cast to ChronoZonedDateTimeImpl
+ * or the chronology is not equal this Chronology
+ */
+ static <R extends ChronoLocalDate> ChronoZonedDateTimeImpl<R> ensureValid(Chronology chrono, Temporal temporal) {
+ @SuppressWarnings("unchecked")
+ ChronoZonedDateTimeImpl<R> other = (ChronoZonedDateTimeImpl<R>) temporal;
+ if (chrono.equals(other.getChronology()) == false) {
+ throw new ClassCastException("Chronology mismatch, required: " + chrono.getId()
+ + ", actual: " + other.getChronology().getId());
+ }
+ return other;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Constructor.
+ *
+ * @param dateTime the date-time, not null
+ * @param offset the zone offset, not null
+ * @param zone the zone ID, not null
+ */
+ private ChronoZonedDateTimeImpl(ChronoLocalDateTimeImpl<D> dateTime, ZoneOffset offset, ZoneId zone) {
+ this.dateTime = Objects.requireNonNull(dateTime, "dateTime");
+ this.offset = Objects.requireNonNull(offset, "offset");
+ this.zone = Objects.requireNonNull(zone, "zone");
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ public ZoneOffset getOffset() {
+ return offset;
+ }
+
+ @Override
+ public ChronoZonedDateTime<D> withEarlierOffsetAtOverlap() {
+ ZoneOffsetTransition trans = getZone().getRules().getTransition(LocalDateTime.from(this));
+ if (trans != null && trans.isOverlap()) {
+ ZoneOffset earlierOffset = trans.getOffsetBefore();
+ if (earlierOffset.equals(offset) == false) {
+ return new ChronoZonedDateTimeImpl<>(dateTime, earlierOffset, zone);
+ }
+ }
+ return this;
+ }
+
+ @Override
+ public ChronoZonedDateTime<D> withLaterOffsetAtOverlap() {
+ ZoneOffsetTransition trans = getZone().getRules().getTransition(LocalDateTime.from(this));
+ if (trans != null) {
+ ZoneOffset offset = trans.getOffsetAfter();
+ if (offset.equals(getOffset()) == false) {
+ return new ChronoZonedDateTimeImpl<>(dateTime, offset, zone);
+ }
+ }
+ return this;
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ public ChronoLocalDateTime<D> toLocalDateTime() {
+ return dateTime;
+ }
+
+ @Override
+ public ZoneId getZone() {
+ return zone;
+ }
+
+ @Override
+ public ChronoZonedDateTime<D> withZoneSameLocal(ZoneId zone) {
+ return ofBest(dateTime, zone, offset);
+ }
+
+ @Override
+ public ChronoZonedDateTime<D> withZoneSameInstant(ZoneId zone) {
+ Objects.requireNonNull(zone, "zone");
+ return this.zone.equals(zone) ? this : create(dateTime.toInstant(offset), zone);
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ public boolean isSupported(TemporalField field) {
+ return field instanceof ChronoField || (field != null && field.isSupportedBy(this));
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ public ChronoZonedDateTime<D> with(TemporalField field, long newValue) {
+ if (field instanceof ChronoField) {
+ ChronoField f = (ChronoField) field;
+ switch (f) {
+ case INSTANT_SECONDS: return plus(newValue - toEpochSecond(), SECONDS);
+ case OFFSET_SECONDS: {
+ ZoneOffset offset = ZoneOffset.ofTotalSeconds(f.checkValidIntValue(newValue));
+ return create(dateTime.toInstant(offset), zone);
+ }
+ }
+ return ofBest(dateTime.with(field, newValue), zone, offset);
+ }
+ return ChronoZonedDateTimeImpl.ensureValid(getChronology(), field.adjustInto(this, newValue));
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ public ChronoZonedDateTime<D> plus(long amountToAdd, TemporalUnit unit) {
+ if (unit instanceof ChronoUnit) {
+ return with(dateTime.plus(amountToAdd, unit));
+ }
+ return ChronoZonedDateTimeImpl.ensureValid(getChronology(), unit.addTo(this, amountToAdd)); /// TODO: Generics replacement Risk!
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ public long until(Temporal endExclusive, TemporalUnit unit) {
+ Objects.requireNonNull(endExclusive, "endExclusive");
+ @SuppressWarnings("unchecked")
+ ChronoZonedDateTime<D> end = (ChronoZonedDateTime<D>) getChronology().zonedDateTime(endExclusive);
+ if (unit instanceof ChronoUnit) {
+ end = end.withZoneSameInstant(offset);
+ return dateTime.until(end.toLocalDateTime(), unit);
+ }
+ Objects.requireNonNull(unit, "unit");
+ return unit.between(this, end);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Writes the ChronoZonedDateTime using a
+ * <a href="../../../serialized-form.html#java.time.chrono.Ser">dedicated serialized form</a>.
+ * @serialData
+ * <pre>
+ * out.writeByte(3); // identifies a ChronoZonedDateTime
+ * out.writeObject(toLocalDateTime());
+ * out.writeObject(getOffset());
+ * out.writeObject(getZone());
+ * </pre>
+ *
+ * @return the instance of {@code Ser}, not null
+ */
+ private Object writeReplace() {
+ return new Ser(Ser.CHRONO_ZONE_DATE_TIME_TYPE, this);
+ }
+
+ /**
+ * Defend against malicious streams.
+ *
+ * @param s the stream to read
+ * @throws InvalidObjectException always
+ */
+ private void readObject(ObjectInputStream s) throws InvalidObjectException {
+ throw new InvalidObjectException("Deserialization via serialization delegate");
+ }
+
+ void writeExternal(ObjectOutput out) throws IOException {
+ out.writeObject(dateTime);
+ out.writeObject(offset);
+ out.writeObject(zone);
+ }
+
+ static ChronoZonedDateTime<?> readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+ ChronoLocalDateTime<?> dateTime = (ChronoLocalDateTime<?>) in.readObject();
+ ZoneOffset offset = (ZoneOffset) in.readObject();
+ ZoneId zone = (ZoneId) in.readObject();
+ return dateTime.atZone(offset).withZoneSameLocal(zone);
+ // TODO: ZDT uses ofLenient()
+ }
+
+ //-------------------------------------------------------------------------
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof ChronoZonedDateTime) {
+ return compareTo((ChronoZonedDateTime<?>) obj) == 0;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return toLocalDateTime().hashCode() ^ getOffset().hashCode() ^ Integer.rotateLeft(getZone().hashCode(), 3);
+ }
+
+ @Override
+ public String toString() {
+ String str = toLocalDateTime().toString() + getOffset().toString();
+ if (getOffset() != getZone()) {
+ str += '[' + getZone().toString() + ']';
+ }
+ return str;
+ }
+
+
+}
diff --git a/java/time/chrono/Chronology.java b/java/time/chrono/Chronology.java
new file mode 100644
index 0000000..0b2d2e1
--- /dev/null
+++ b/java/time/chrono/Chronology.java
@@ -0,0 +1,754 @@
+/*
+ * Copyright (c) 2012, 2015, 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) 2012, 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.Clock;
+import java.time.DateTimeException;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalTime;
+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.TemporalAccessor;
+import java.time.temporal.TemporalField;
+import java.time.temporal.TemporalQueries;
+import java.time.temporal.TemporalQuery;
+import java.time.temporal.UnsupportedTemporalTypeException;
+import java.time.temporal.ValueRange;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * A calendar system, used to organize and identify dates.
+ * <p>
+ * The main date and time API is built on the ISO calendar system.
+ * The chronology operates behind the scenes to represent the general concept of a calendar system.
+ * For example, the Japanese, Minguo, Thai Buddhist and others.
+ * <p>
+ * Most other calendar systems also operate on the shared concepts of year, month and day,
+ * linked to the cycles of the Earth around the Sun, and the Moon around the Earth.
+ * These shared concepts are defined by {@link ChronoField} and are available
+ * for use by any {@code Chronology} implementation:
+ * <pre>
+ * LocalDate isoDate = ...
+ * ThaiBuddhistDate thaiDate = ...
+ * int isoYear = isoDate.get(ChronoField.YEAR);
+ * int thaiYear = thaiDate.get(ChronoField.YEAR);
+ * </pre>
+ * As shown, although the date objects are in different calendar systems, represented by different
+ * {@code Chronology} instances, both can be queried using the same constant on {@code ChronoField}.
+ * For a full discussion of the implications of this, see {@link ChronoLocalDate}.
+ * In general, the advice is to use the known ISO-based {@code LocalDate}, rather than
+ * {@code ChronoLocalDate}.
+ * <p>
+ * While a {@code Chronology} object typically uses {@code ChronoField} and is based on
+ * an era, year-of-era, month-of-year, day-of-month model of a date, this is not required.
+ * A {@code Chronology} instance may represent a totally different kind of calendar system,
+ * such as the Mayan.
+ * <p>
+ * In practical terms, the {@code Chronology} instance also acts as a factory.
+ * The {@link #of(String)} method allows an instance to be looked up by identifier,
+ * while the {@link #ofLocale(Locale)} method allows lookup by locale.
+ * <p>
+ * The {@code Chronology} instance provides a set of methods to create {@code ChronoLocalDate} instances.
+ * The date classes are used to manipulate specific dates.
+ * <ul>
+ * <li> {@link #dateNow() dateNow()}
+ * <li> {@link #dateNow(Clock) dateNow(clock)}
+ * <li> {@link #dateNow(ZoneId) dateNow(zone)}
+ * <li> {@link #date(int, int, int) date(yearProleptic, month, day)}
+ * <li> {@link #date(Era, int, int, int) date(era, yearOfEra, month, day)}
+ * <li> {@link #dateYearDay(int, int) dateYearDay(yearProleptic, dayOfYear)}
+ * <li> {@link #dateYearDay(Era, int, int) dateYearDay(era, yearOfEra, dayOfYear)}
+ * <li> {@link #date(TemporalAccessor) date(TemporalAccessor)}
+ * </ul>
+ *
+ * <h3 id="addcalendars">Adding New Calendars</h3>
+ * The set of available chronologies can be extended by applications.
+ * Adding a new calendar system requires the writing of an implementation of
+ * {@code Chronology}, {@code ChronoLocalDate} and {@code Era}.
+ * The majority of the logic specific to the calendar system will be in the
+ * {@code ChronoLocalDate} implementation.
+ * The {@code Chronology} implementation acts as a factory.
+ * <p>
+ * To permit the discovery of additional chronologies, the {@link java.util.ServiceLoader ServiceLoader}
+ * is used. A file must be added to the {@code META-INF/services} directory with the
+ * name 'java.time.chrono.Chronology' listing the implementation classes.
+ * See the ServiceLoader for more details on service loading.
+ * For lookup by id or calendarType, the system provided calendars are found
+ * first followed by application provided calendars.
+ * <p>
+ * Each chronology must define a chronology ID that is unique within the system.
+ * If the chronology represents a calendar system defined by the
+ * CLDR specification then the calendar type is the concatenation of the
+ * CLDR type and, if applicable, the CLDR variant,
+ *
+ * @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 Chronology extends Comparable<Chronology> {
+
+ /**
+ * Obtains an instance of {@code Chronology} from a temporal object.
+ * <p>
+ * This obtains a chronology based on the specified temporal.
+ * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
+ * which this factory converts to an instance of {@code Chronology}.
+ * <p>
+ * The conversion will obtain the chronology using {@link TemporalQueries#chronology()}.
+ * If the specified temporal object does not have a chronology, {@link IsoChronology} is returned.
+ * <p>
+ * This method matches the signature of the functional interface {@link TemporalQuery}
+ * allowing it to be used as a query via method reference, {@code Chronology::from}.
+ *
+ * @param temporal the temporal to convert, not null
+ * @return the chronology, not null
+ * @throws DateTimeException if unable to convert to an {@code Chronology}
+ */
+ static Chronology from(TemporalAccessor temporal) {
+ Objects.requireNonNull(temporal, "temporal");
+ Chronology obj = temporal.query(TemporalQueries.chronology());
+ return (obj != null ? obj : IsoChronology.INSTANCE);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code Chronology} from a locale.
+ * <p>
+ * This returns a {@code Chronology} based on the specified locale,
+ * typically returning {@code IsoChronology}. Other calendar systems
+ * are only returned if they are explicitly selected within the locale.
+ * <p>
+ * The {@link Locale} class provide access to a range of information useful
+ * for localizing an application. This includes the language and region,
+ * such as "en-GB" for English as used in Great Britain.
+ * <p>
+ * The {@code Locale} class also supports an extension mechanism that
+ * can be used to identify a calendar system. The mechanism is a form
+ * of key-value pairs, where the calendar system has the key "ca".
+ * For example, the locale "en-JP-u-ca-japanese" represents the English
+ * language as used in Japan with the Japanese calendar system.
+ * <p>
+ * This method finds the desired calendar system by in a manner equivalent
+ * to passing "ca" to {@link Locale#getUnicodeLocaleType(String)}.
+ * If the "ca" key is not present, then {@code IsoChronology} is returned.
+ * <p>
+ * Note that the behavior of this method differs from the older
+ * {@link java.util.Calendar#getInstance(Locale)} method.
+ * If that method receives a locale of "th_TH" it will return {@code BuddhistCalendar}.
+ * By contrast, this method will return {@code IsoChronology}.
+ * Passing the locale "th-TH-u-ca-buddhist" into either method will
+ * result in the Thai Buddhist calendar system and is therefore the
+ * recommended approach going forward for Thai calendar system localization.
+ * <p>
+ * A similar, but simpler, situation occurs for the Japanese calendar system.
+ * The locale "jp_JP_JP" has previously been used to access the calendar.
+ * However, unlike the Thai locale, "ja_JP_JP" is automatically converted by
+ * {@code Locale} to the modern and recommended form of "ja-JP-u-ca-japanese".
+ * Thus, there is no difference in behavior between this method and
+ * {@code Calendar#getInstance(Locale)}.
+ *
+ * @param locale the locale to use to obtain the calendar system, not null
+ * @return the calendar system associated with the locale, not null
+ * @throws DateTimeException if the locale-specified calendar cannot be found
+ */
+ static Chronology ofLocale(Locale locale) {
+ return AbstractChronology.ofLocale(locale);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code Chronology} from a chronology ID or
+ * calendar system type.
+ * <p>
+ * This returns a chronology based on either the ID or the type.
+ * The {@link #getId() chronology ID} uniquely identifies the chronology.
+ * The {@link #getCalendarType() calendar system type} is defined by the
+ * CLDR specification.
+ * <p>
+ * The chronology may be a system chronology or a chronology
+ * provided by the application via ServiceLoader configuration.
+ * <p>
+ * Since some calendars can be customized, the ID or type typically refers
+ * to the default customization. For example, the Gregorian calendar can have multiple
+ * cutover dates from the Julian, but the lookup only provides the default cutover date.
+ *
+ * @param id the chronology ID or calendar system type, not null
+ * @return the chronology with the identifier requested, not null
+ * @throws DateTimeException if the chronology cannot be found
+ */
+ static Chronology of(String id) {
+ return AbstractChronology.of(id);
+ }
+
+ /**
+ * Returns the available chronologies.
+ * <p>
+ * Each returned {@code Chronology} is available for use in the system.
+ * The set of chronologies includes the system chronologies and
+ * any chronologies provided by the application via ServiceLoader
+ * configuration.
+ *
+ * @return the independent, modifiable set of the available chronology IDs, not null
+ */
+ static Set<Chronology> getAvailableChronologies() {
+ return AbstractChronology.getAvailableChronologies();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the ID of the chronology.
+ * <p>
+ * The ID uniquely identifies the {@code Chronology}.
+ * It can be used to lookup the {@code Chronology} using {@link #of(String)}.
+ *
+ * @return the chronology ID, not null
+ * @see #getCalendarType()
+ */
+ String getId();
+
+ /**
+ * Gets the calendar type of the calendar system.
+ * <p>
+ * The calendar type is an identifier defined by the CLDR and
+ * <em>Unicode Locale Data Markup Language (LDML)</em> specifications
+ * to uniquely identification a calendar.
+ * The {@code getCalendarType} is the concatenation of the CLDR calendar type
+ * and the variant, if applicable, is appended separated by "-".
+ * The calendar type is used to lookup the {@code Chronology} using {@link #of(String)}.
+ *
+ * @return the calendar system type, null if the calendar is not defined by CLDR/LDML
+ * @see #getId()
+ */
+ String getCalendarType();
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains a local date in this chronology from the era, year-of-era,
+ * month-of-year and day-of-month fields.
+ *
+ * @implSpec
+ * The default implementation combines the era and year-of-era into a proleptic
+ * year before calling {@link #date(int, int, int)}.
+ *
+ * @param era the era of the correct type for the chronology, not null
+ * @param yearOfEra the chronology year-of-era
+ * @param month the chronology month-of-year
+ * @param dayOfMonth the chronology day-of-month
+ * @return the local date in this chronology, not null
+ * @throws DateTimeException if unable to create the date
+ * @throws ClassCastException if the {@code era} is not of the correct type for the chronology
+ */
+ default ChronoLocalDate date(Era era, int yearOfEra, int month, int dayOfMonth) {
+ return date(prolepticYear(era, yearOfEra), month, dayOfMonth);
+ }
+
+ /**
+ * Obtains a local date in this chronology from the proleptic-year,
+ * month-of-year and day-of-month fields.
+ *
+ * @param prolepticYear the chronology proleptic-year
+ * @param month the chronology month-of-year
+ * @param dayOfMonth the chronology day-of-month
+ * @return the local date in this chronology, not null
+ * @throws DateTimeException if unable to create the date
+ */
+ ChronoLocalDate date(int prolepticYear, int month, int dayOfMonth);
+
+ /**
+ * Obtains a local date in this chronology from the era, year-of-era and
+ * day-of-year fields.
+ *
+ * @implSpec
+ * The default implementation combines the era and year-of-era into a proleptic
+ * year before calling {@link #dateYearDay(int, int)}.
+ *
+ * @param era the era of the correct type for the chronology, not null
+ * @param yearOfEra the chronology year-of-era
+ * @param dayOfYear the chronology day-of-year
+ * @return the local date in this chronology, not null
+ * @throws DateTimeException if unable to create the date
+ * @throws ClassCastException if the {@code era} is not of the correct type for the chronology
+ */
+ default ChronoLocalDate dateYearDay(Era era, int yearOfEra, int dayOfYear) {
+ return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear);
+ }
+
+ /**
+ * Obtains a local date in this chronology from the proleptic-year and
+ * day-of-year fields.
+ *
+ * @param prolepticYear the chronology proleptic-year
+ * @param dayOfYear the chronology day-of-year
+ * @return the local date in this chronology, not null
+ * @throws DateTimeException if unable to create the date
+ */
+ ChronoLocalDate dateYearDay(int prolepticYear, int dayOfYear);
+
+ /**
+ * Obtains a local date in this chronology from the epoch-day.
+ * <p>
+ * The definition of {@link ChronoField#EPOCH_DAY EPOCH_DAY} is the same
+ * for all calendar systems, thus it can be used for conversion.
+ *
+ * @param epochDay the epoch day
+ * @return the local date in this chronology, not null
+ * @throws DateTimeException if unable to create the date
+ */
+ ChronoLocalDate dateEpochDay(long epochDay);
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains the current local date in this chronology from the system clock in the default time-zone.
+ * <p>
+ * This will query the {@link Clock#systemDefaultZone() system clock} in the default
+ * time-zone to obtain the current date.
+ * <p>
+ * Using this method will prevent the ability to use an alternate clock for testing
+ * because the clock is hard-coded.
+ *
+ * @implSpec
+ * The default implementation invokes {@link #dateNow(Clock)}.
+ *
+ * @return the current local date using the system clock and default time-zone, not null
+ * @throws DateTimeException if unable to create the date
+ */
+ default ChronoLocalDate dateNow() {
+ return dateNow(Clock.systemDefaultZone());
+ }
+
+ /**
+ * Obtains the current local date in this chronology from the system clock in the specified time-zone.
+ * <p>
+ * This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date.
+ * Specifying the time-zone avoids dependence on the default time-zone.
+ * <p>
+ * Using this method will prevent the ability to use an alternate clock for testing
+ * because the clock is hard-coded.
+ *
+ * @implSpec
+ * The default implementation invokes {@link #dateNow(Clock)}.
+ *
+ * @param zone the zone ID to use, not null
+ * @return the current local date using the system clock, not null
+ * @throws DateTimeException if unable to create the date
+ */
+ default ChronoLocalDate dateNow(ZoneId zone) {
+ return dateNow(Clock.system(zone));
+ }
+
+ /**
+ * Obtains the current local date in this chronology from the specified clock.
+ * <p>
+ * This will query the specified clock to obtain the current date - today.
+ * Using this method allows the use of an alternate clock for testing.
+ * The alternate clock may be introduced using {@link Clock dependency injection}.
+ *
+ * @implSpec
+ * The default implementation invokes {@link #date(TemporalAccessor)}.
+ *
+ * @param clock the clock to use, not null
+ * @return the current local date, not null
+ * @throws DateTimeException if unable to create the date
+ */
+ default ChronoLocalDate dateNow(Clock clock) {
+ Objects.requireNonNull(clock, "clock");
+ return date(LocalDate.now(clock));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains a local date in this chronology from another temporal object.
+ * <p>
+ * This obtains a date in this chronology based on the specified temporal.
+ * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
+ * which this factory converts to an instance of {@code ChronoLocalDate}.
+ * <p>
+ * The conversion typically uses the {@link ChronoField#EPOCH_DAY EPOCH_DAY}
+ * field, which is standardized across calendar systems.
+ * <p>
+ * This method matches the signature of the functional interface {@link TemporalQuery}
+ * allowing it to be used as a query via method reference, {@code aChronology::date}.
+ *
+ * @param temporal the temporal object to convert, not null
+ * @return the local date in this chronology, not null
+ * @throws DateTimeException if unable to create the date
+ * @see ChronoLocalDate#from(TemporalAccessor)
+ */
+ ChronoLocalDate date(TemporalAccessor temporal);
+
+ /**
+ * Obtains a local date-time in this chronology from another temporal object.
+ * <p>
+ * This obtains a date-time in this chronology based on the specified temporal.
+ * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
+ * which this factory converts to an instance of {@code ChronoLocalDateTime}.
+ * <p>
+ * The conversion extracts and combines the {@code ChronoLocalDate} and the
+ * {@code LocalTime} from the temporal object.
+ * Implementations are permitted to perform optimizations such as accessing
+ * those fields that are equivalent to the relevant objects.
+ * The result uses this chronology.
+ * <p>
+ * This method matches the signature of the functional interface {@link TemporalQuery}
+ * allowing it to be used as a query via method reference, {@code aChronology::localDateTime}.
+ *
+ * @param temporal the temporal object to convert, not null
+ * @return the local date-time in this chronology, not null
+ * @throws DateTimeException if unable to create the date-time
+ * @see ChronoLocalDateTime#from(TemporalAccessor)
+ */
+ default ChronoLocalDateTime<? extends ChronoLocalDate> localDateTime(TemporalAccessor temporal) {
+ try {
+ return date(temporal).atTime(LocalTime.from(temporal));
+ } catch (DateTimeException ex) {
+ throw new DateTimeException("Unable to obtain ChronoLocalDateTime from TemporalAccessor: " + temporal.getClass(), ex);
+ }
+ }
+
+ /**
+ * Obtains a {@code ChronoZonedDateTime} in this chronology from another temporal object.
+ * <p>
+ * This obtains a zoned date-time in this chronology based on the specified temporal.
+ * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
+ * which this factory converts to an instance of {@code ChronoZonedDateTime}.
+ * <p>
+ * The conversion will first obtain a {@code ZoneId} from the temporal object,
+ * falling back to a {@code ZoneOffset} if necessary. It will then try to obtain
+ * an {@code Instant}, falling back to a {@code ChronoLocalDateTime} if necessary.
+ * The result will be either the combination of {@code ZoneId} or {@code ZoneOffset}
+ * with {@code Instant} or {@code ChronoLocalDateTime}.
+ * Implementations are permitted to perform optimizations such as accessing
+ * those fields that are equivalent to the relevant objects.
+ * The result uses this chronology.
+ * <p>
+ * This method matches the signature of the functional interface {@link TemporalQuery}
+ * allowing it to be used as a query via method reference, {@code aChronology::zonedDateTime}.
+ *
+ * @param temporal the temporal object to convert, not null
+ * @return the zoned date-time in this chronology, not null
+ * @throws DateTimeException if unable to create the date-time
+ * @see ChronoZonedDateTime#from(TemporalAccessor)
+ */
+ default ChronoZonedDateTime<? extends ChronoLocalDate> zonedDateTime(TemporalAccessor temporal) {
+ try {
+ ZoneId zone = ZoneId.from(temporal);
+ try {
+ Instant instant = Instant.from(temporal);
+ return zonedDateTime(instant, zone);
+
+ } catch (DateTimeException ex1) {
+ ChronoLocalDateTimeImpl<?> cldt = ChronoLocalDateTimeImpl.ensureValid(this, localDateTime(temporal));
+ return ChronoZonedDateTimeImpl.ofBest(cldt, zone, null);
+ }
+ } catch (DateTimeException ex) {
+ throw new DateTimeException("Unable to obtain ChronoZonedDateTime from TemporalAccessor: " + temporal.getClass(), ex);
+ }
+ }
+
+ /**
+ * Obtains a {@code ChronoZonedDateTime} in this chronology from an {@code Instant}.
+ * <p>
+ * This obtains a zoned date-time with the same instant as that specified.
+ *
+ * @param instant the instant to create the date-time from, not null
+ * @param zone the time-zone, not null
+ * @return the zoned date-time, not null
+ * @throws DateTimeException if the result exceeds the supported range
+ */
+ default ChronoZonedDateTime<? extends ChronoLocalDate> zonedDateTime(Instant instant, ZoneId zone) {
+ return ChronoZonedDateTimeImpl.ofInstant(this, instant, zone);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if the specified year is a leap year.
+ * <p>
+ * A leap-year is a year of a longer length than normal.
+ * The exact meaning is determined by the chronology according to the following constraints.
+ * <ul>
+ * <li>a leap-year must imply a year-length longer than a non leap-year.
+ * <li>a chronology that does not support the concept of a year must return false.
+ * </ul>
+ *
+ * @param prolepticYear the proleptic-year to check, not validated for range
+ * @return true if the year is a leap year
+ */
+ boolean isLeapYear(long prolepticYear);
+
+ /**
+ * Calculates the proleptic-year given the era and year-of-era.
+ * <p>
+ * This combines the era and year-of-era into the single proleptic-year field.
+ * <p>
+ * If the chronology makes active use of eras, such as {@code JapaneseChronology}
+ * then the year-of-era will be validated against the era.
+ * For other chronologies, validation is optional.
+ *
+ * @param era the era of the correct type for the chronology, not null
+ * @param yearOfEra the chronology year-of-era
+ * @return the proleptic-year
+ * @throws DateTimeException if unable to convert to a proleptic-year,
+ * such as if the year is invalid for the era
+ * @throws ClassCastException if the {@code era} is not of the correct type for the chronology
+ */
+ int prolepticYear(Era era, int yearOfEra);
+
+ /**
+ * Creates the chronology era object from the numeric value.
+ * <p>
+ * The era is, conceptually, the largest division of the time-line.
+ * Most calendar systems have a single epoch dividing the time-line into two eras.
+ * However, some have multiple eras, such as one for the reign of each leader.
+ * The exact meaning is determined by the chronology according to the following constraints.
+ * <p>
+ * The era in use at 1970-01-01 must have the value 1.
+ * Later eras must have sequentially higher values.
+ * Earlier eras must have sequentially lower values.
+ * Each chronology must refer to an enum or similar singleton to provide the era values.
+ * <p>
+ * This method returns the singleton era of the correct type for the specified era value.
+ *
+ * @param eraValue the era value
+ * @return the calendar system era, not null
+ * @throws DateTimeException if unable to create the era
+ */
+ Era eraOf(int eraValue);
+
+ /**
+ * Gets the list of eras for the chronology.
+ * <p>
+ * Most calendar systems have an era, within which the year has meaning.
+ * If the calendar system does not support the concept of eras, an empty
+ * list must be returned.
+ *
+ * @return the list of eras for the chronology, may be immutable, not null
+ */
+ List<Era> eras();
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the range of valid values for the specified field.
+ * <p>
+ * All fields can be expressed as a {@code long} integer.
+ * This method returns an object that describes the valid range for that value.
+ * <p>
+ * Note that the result only describes the minimum and maximum valid values
+ * and it is important not to read too much into them. For example, there
+ * could be values within the range that are invalid for the field.
+ * <p>
+ * This method will return a result whether or not the chronology supports the field.
+ *
+ * @param field the field to get the range for, not null
+ * @return the range of valid values for the field, not null
+ * @throws DateTimeException if the range for the field cannot be obtained
+ */
+ ValueRange range(ChronoField field);
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the textual representation of this chronology.
+ * <p>
+ * This returns the textual name used to identify the chronology,
+ * suitable for presentation to the user.
+ * The parameters control the style of the returned text and the locale.
+ *
+ * @implSpec
+ * The default implementation behaves as though the formatter was used to
+ * format the chronology textual name.
+ *
+ * @param style the style of the text required, not null
+ * @param locale the locale to use, not null
+ * @return the text value of the chronology, not null
+ */
+ default String getDisplayName(TextStyle style, Locale locale) {
+ TemporalAccessor temporal = new TemporalAccessor() {
+ @Override
+ public boolean isSupported(TemporalField field) {
+ return false;
+ }
+ @Override
+ public long getLong(TemporalField field) {
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
+ }
+ @SuppressWarnings("unchecked")
+ @Override
+ public <R> R query(TemporalQuery<R> query) {
+ if (query == TemporalQueries.chronology()) {
+ return (R) Chronology.this;
+ }
+ return TemporalAccessor.super.query(query);
+ }
+ };
+ return new DateTimeFormatterBuilder().appendChronologyText(style).toFormatter(locale).format(temporal);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Resolves parsed {@code ChronoField} values into a date during parsing.
+ * <p>
+ * Most {@code TemporalField} implementations are resolved using the
+ * resolve method on the field. By contrast, the {@code ChronoField} class
+ * defines fields that only have meaning relative to the chronology.
+ * As such, {@code ChronoField} date fields are resolved here in the
+ * context of a specific chronology.
+ * <p>
+ * The default implementation, which explains typical resolve behaviour,
+ * is provided in {@link AbstractChronology}.
+ *
+ * @param fieldValues the map of fields to values, which can be updated, not null
+ * @param resolverStyle the requested type of resolve, not null
+ * @return the resolved date, null if insufficient information to create a date
+ * @throws DateTimeException if the date cannot be resolved, typically
+ * because of a conflict in the input data
+ */
+ ChronoLocalDate resolveDate(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle);
+
+ //-----------------------------------------------------------------------
+ /**
+ * 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 {@code ChronoPeriod} API
+ * allows the period to be represented using other units.
+ *
+ * @implSpec
+ * 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
+ */
+ default 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
+ * additional information specific to the subclass.
+ * It is "consistent with equals", as defined by {@link Comparable}.
+ *
+ * @param other the other chronology to compare to, not null
+ * @return the comparator value, negative if less, positive if greater
+ */
+ @Override
+ int compareTo(Chronology other);
+
+ /**
+ * Checks if this chronology is equal to another chronology.
+ * <p>
+ * The comparison is based on the entire state of the object.
+ *
+ * @param obj the object to check, null returns false
+ * @return true if this is equal to the other chronology
+ */
+ @Override
+ boolean equals(Object obj);
+
+ /**
+ * A hash code for this chronology.
+ * <p>
+ * The hash code should be based on the entire state of the object.
+ *
+ * @return a suitable hash code
+ */
+ @Override
+ int hashCode();
+
+ //-----------------------------------------------------------------------
+ /**
+ * Outputs this chronology as a {@code String}.
+ * <p>
+ * The format should include the entire state of the object.
+ *
+ * @return a string representation of this chronology, not null
+ */
+ @Override
+ String toString();
+
+}
diff --git a/java/time/chrono/Era.java b/java/time/chrono/Era.java
new file mode 100644
index 0000000..0e3c533
--- /dev/null
+++ b/java/time/chrono/Era.java
@@ -0,0 +1,325 @@
+/*
+ * Copyright (c) 2012, 2015, 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) 2012, 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.ERA;
+import static java.time.temporal.ChronoUnit.ERAS;
+
+import java.time.DateTimeException;
+import java.time.temporal.UnsupportedTemporalTypeException;
+import java.time.format.DateTimeFormatterBuilder;
+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;
+import java.time.temporal.TemporalQueries;
+import java.time.temporal.TemporalQuery;
+import java.time.temporal.ValueRange;
+import java.util.Locale;
+
+/**
+ * An era of the time-line.
+ * <p>
+ * Most calendar systems have a single epoch dividing the time-line into two eras.
+ * However, some calendar systems, have multiple eras, such as one for the reign
+ * of each leader.
+ * In all cases, the era is conceptually the largest division of the time-line.
+ * Each chronology defines the Era's that are known Eras and a
+ * {@link Chronology#eras Chronology.eras} to get the valid eras.
+ * <p>
+ * For example, the Thai Buddhist calendar system divides time into two eras,
+ * before and after a single date. By contrast, the Japanese calendar system
+ * has one era for the reign of each Emperor.
+ * <p>
+ * Instances of {@code Era} may be compared using the {@code ==} operator.
+ *
+ * @implSpec
+ * This interface must be implemented with care to ensure other classes operate correctly.
+ * All implementations must be singletons - final, immutable and thread-safe.
+ * It is recommended to use an enum whenever possible.
+ *
+ * @since 1.8
+ */
+public interface Era extends TemporalAccessor, TemporalAdjuster {
+
+ /**
+ * Gets the numeric value associated with the era as defined by the chronology.
+ * Each chronology defines the predefined Eras and methods to list the Eras
+ * of the chronology.
+ * <p>
+ * All fields, including eras, have an associated numeric value.
+ * The meaning of the numeric value for era is determined by the chronology
+ * according to these principles:
+ * <ul>
+ * <li>The era in use at the epoch 1970-01-01 (ISO) has the value 1.
+ * <li>Later eras have sequentially higher values.
+ * <li>Earlier eras have sequentially lower values, which may be negative.
+ * </ul>
+ *
+ * @return the numeric era value
+ */
+ int getValue();
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if the specified field is supported.
+ * <p>
+ * This checks if this era can be queried for the specified field.
+ * If false, then calling the {@link #range(TemporalField) range} and
+ * {@link #get(TemporalField) get} methods will throw an exception.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The {@code ERA} field returns true.
+ * All other {@code ChronoField} instances will return false.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}
+ * passing {@code this} as the argument.
+ * Whether the field is supported is determined by the field.
+ *
+ * @param field the field to check, null returns false
+ * @return true if the field is supported on this era, false if not
+ */
+ @Override
+ default boolean isSupported(TemporalField field) {
+ if (field instanceof ChronoField) {
+ return field == ERA;
+ }
+ return field != null && field.isSupportedBy(this);
+ }
+
+ /**
+ * Gets the range of valid values for the specified field.
+ * <p>
+ * The range object expresses the minimum and maximum valid values for a field.
+ * This era is used to enhance the accuracy of the returned range.
+ * If it is not possible to return the range, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The {@code ERA} field returns the range.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.rangeRefinedBy(TemporalAccessor)}
+ * passing {@code this} as the argument.
+ * Whether the range can be obtained is determined by the field.
+ * <p>
+ * The default implementation must return a range for {@code ERA} from
+ * zero to one, suitable for two era calendar systems such as ISO.
+ *
+ * @param field the field to query the range for, not null
+ * @return the range of valid values for the field, not null
+ * @throws DateTimeException if the range for the field cannot be obtained
+ * @throws UnsupportedTemporalTypeException if the unit is not supported
+ */
+ @Override // override for Javadoc
+ default ValueRange range(TemporalField field) {
+ return TemporalAccessor.super.range(field);
+ }
+
+ /**
+ * Gets the value of the specified field from this era as an {@code int}.
+ * <p>
+ * This queries this era for the value of the specified field.
+ * The returned value will always be within the valid range of values for the field.
+ * If it is not possible to return the value, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The {@code ERA} field returns the value of the era.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
+ * passing {@code this} as the argument. Whether the value can be obtained,
+ * and what the value represents, is determined by the field.
+ *
+ * @param field the field to get, not null
+ * @return the value for the field
+ * @throws DateTimeException if a value for the field cannot be obtained or
+ * the value is outside the range of valid values for the field
+ * @throws UnsupportedTemporalTypeException if the field is not supported or
+ * the range of values exceeds an {@code int}
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override // override for Javadoc and performance
+ default int get(TemporalField field) {
+ if (field == ERA) {
+ return getValue();
+ }
+ return TemporalAccessor.super.get(field);
+ }
+
+ /**
+ * Gets the value of the specified field from this era as a {@code long}.
+ * <p>
+ * This queries this era for the value of the specified field.
+ * If it is not possible to return the value, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The {@code ERA} field returns the value of the era.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
+ * passing {@code this} as the argument. Whether the value can be obtained,
+ * and what the value represents, is determined by the field.
+ *
+ * @param field the field to get, not null
+ * @return the value for the field
+ * @throws DateTimeException if a value for the field cannot be obtained
+ * @throws UnsupportedTemporalTypeException if the field is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ default long getLong(TemporalField field) {
+ if (field == ERA) {
+ return getValue();
+ } else if (field instanceof ChronoField) {
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
+ }
+ return field.getFrom(this);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Queries this era using the specified query.
+ * <p>
+ * This queries this era using the specified query strategy object.
+ * The {@code TemporalQuery} object defines the logic to be used to
+ * obtain the result. Read the documentation of the query to understand
+ * what the result of this method will be.
+ * <p>
+ * The result of this method is obtained by invoking the
+ * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the
+ * specified query passing {@code this} as the argument.
+ *
+ * @param <R> the type of the result
+ * @param query the query to invoke, not null
+ * @return the query result, null may be returned (defined by the query)
+ * @throws DateTimeException if unable to query (defined by the query)
+ * @throws ArithmeticException if numeric overflow occurs (defined by the query)
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ default <R> R query(TemporalQuery<R> query) {
+ if (query == TemporalQueries.precision()) {
+ return (R) ERAS;
+ }
+ return TemporalAccessor.super.query(query);
+ }
+
+ /**
+ * Adjusts the specified temporal object to have the same era as this object.
+ * <p>
+ * This returns a temporal object of the same observable type as the input
+ * with the era changed to be the same as this.
+ * <p>
+ * The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)}
+ * passing {@link ChronoField#ERA} as the field.
+ * <p>
+ * In most cases, it is clearer to reverse the calling pattern by using
+ * {@link Temporal#with(TemporalAdjuster)}:
+ * <pre>
+ * // these two lines are equivalent, but the second approach is recommended
+ * temporal = thisEra.adjustInto(temporal);
+ * temporal = temporal.with(thisEra);
+ * </pre>
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param temporal the target object to be adjusted, not null
+ * @return the adjusted object, not null
+ * @throws DateTimeException if unable to make the adjustment
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ @Override
+ default Temporal adjustInto(Temporal temporal) {
+ return temporal.with(ERA, getValue());
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the textual representation of this era.
+ * <p>
+ * This returns the textual name used to identify the era,
+ * suitable for presentation to the user.
+ * The parameters control the style of the returned text and the locale.
+ * <p>
+ * If no textual mapping is found then the {@link #getValue() numeric value} is returned.
+ * <p>
+ * This default implementation is suitable for all implementations.
+ *
+ * @param style the style of the text required, not null
+ * @param locale the locale to use, not null
+ * @return the text value of the era, not null
+ */
+ default String getDisplayName(TextStyle style, Locale locale) {
+ return new DateTimeFormatterBuilder().appendText(ERA, style).toFormatter(locale).format(this);
+ }
+
+ // NOTE: methods to convert year-of-era/proleptic-year cannot be here as they may depend on month/day (Japanese)
+}
diff --git a/java/time/chrono/HijrahChronology.java b/java/time/chrono/HijrahChronology.java
new file mode 100644
index 0000000..e7b3554
--- /dev/null
+++ b/java/time/chrono/HijrahChronology.java
@@ -0,0 +1,1095 @@
+/*
+ * Copyright (c) 2012, 2015, 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) 2012, 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.EPOCH_DAY;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.time.Clock;
+import java.time.DateTimeException;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.time.format.ResolverStyle;
+import java.time.temporal.ChronoField;
+import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalField;
+import java.time.temporal.ValueRange;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Properties;
+
+import sun.util.logging.PlatformLogger;
+
+/**
+ * The Hijrah calendar is a lunar calendar supporting Islamic calendars.
+ * <p>
+ * The HijrahChronology follows the rules of the Hijrah calendar system. The Hijrah
+ * calendar has several variants based on differences in when the new moon is
+ * determined to have occurred and where the observation is made.
+ * In some variants the length of each month is
+ * computed algorithmically from the astronomical data for the moon and earth and
+ * in others the length of the month is determined by an authorized sighting
+ * of the new moon. For the algorithmically based calendars the calendar
+ * can project into the future.
+ * For sighting based calendars only historical data from past
+ * sightings is available.
+ * <p>
+ * The length of each month is 29 or 30 days.
+ * Ordinary years have 354 days; leap years have 355 days.
+ *
+ * <p>
+ * CLDR and LDML identify variants:
+ * <table cellpadding="2" summary="Variants of Hijrah Calendars">
+ * <thead>
+ * <tr class="tableSubHeadingColor">
+ * <th class="colFirst" align="left" >Chronology ID</th>
+ * <th class="colFirst" align="left" >Calendar Type</th>
+ * <th class="colFirst" align="left" >Locale extension, see {@link java.util.Locale}</th>
+ * <th class="colLast" align="left" >Description</th>
+ * </tr>
+ * </thead>
+ * <tbody>
+ * <tr class="altColor">
+ * <td>Hijrah-umalqura</td>
+ * <td>islamic-umalqura</td>
+ * <td>ca-islamic-umalqura</td>
+ * <td>Islamic - Umm Al-Qura calendar of Saudi Arabia</td>
+ * </tr>
+ * </tbody>
+ * </table>
+ * <p>Additional variants may be available through {@link Chronology#getAvailableChronologies()}.
+ *
+ * <p>Example</p>
+ * <p>
+ * Selecting the chronology from the locale uses {@link Chronology#ofLocale}
+ * to find the Chronology based on Locale supported BCP 47 extension mechanism
+ * to request a specific calendar ("ca"). For example,
+ * </p>
+ * <pre>
+ * Locale locale = Locale.forLanguageTag("en-US-u-ca-islamic-umalqura");
+ * Chronology chrono = Chronology.ofLocale(locale);
+ * </pre>
+ *
+ * @implSpec
+ * This class is immutable and thread-safe.
+ *
+ * @implNote
+ * Each Hijrah variant is configured individually. Each variant is defined by a
+ * property resource that defines the {@code ID}, the {@code calendar type},
+ * the start of the calendar, the alignment with the
+ * ISO calendar, and the length of each month for a range of years.
+ * The variants are identified in the {@code calendars.properties} file.
+ * The new properties are prefixed with {@code "calendars.hijrah."}:
+ * <table cellpadding="2" border="0" summary="Configuration of Hijrah Calendar Variants">
+ * <thead>
+ * <tr class="tableSubHeadingColor">
+ * <th class="colFirst" align="left">Property Name</th>
+ * <th class="colFirst" align="left">Property value</th>
+ * <th class="colLast" align="left">Description </th>
+ * </tr>
+ * </thead>
+ * <tbody>
+ * <tr class="altColor">
+ * <td>calendars.hijrah.{ID}</td>
+ * <td>The property resource defining the {@code {ID}} variant</td>
+ * <td>The property resource is located with the {@code calendars.properties} file</td>
+ * </tr>
+ * <tr class="rowColor">
+ * <td>calendars.hijrah.{ID}.type</td>
+ * <td>The calendar type</td>
+ * <td>LDML defines the calendar type names</td>
+ * </tr>
+ * </tbody>
+ * </table>
+ * <p>
+ * The Hijrah property resource is a set of properties that describe the calendar.
+ * The syntax is defined by {@code java.util.Properties#load(Reader)}.
+ * <table cellpadding="2" summary="Configuration of Hijrah Calendar">
+ * <thead>
+ * <tr class="tableSubHeadingColor">
+ * <th class="colFirst" align="left" > Property Name</th>
+ * <th class="colFirst" align="left" > Property value</th>
+ * <th class="colLast" align="left" > Description </th>
+ * </tr>
+ * </thead>
+ * <tbody>
+ * <tr class="altColor">
+ * <td>id</td>
+ * <td>Chronology Id, for example, "Hijrah-umalqura"</td>
+ * <td>The Id of the calendar in common usage</td>
+ * </tr>
+ * <tr class="rowColor">
+ * <td>type</td>
+ * <td>Calendar type, for example, "islamic-umalqura"</td>
+ * <td>LDML defines the calendar types</td>
+ * </tr>
+ * <tr class="altColor">
+ * <td>version</td>
+ * <td>Version, for example: "1.8.0_1"</td>
+ * <td>The version of the Hijrah variant data</td>
+ * </tr>
+ * <tr class="rowColor">
+ * <td>iso-start</td>
+ * <td>ISO start date, formatted as {@code yyyy-MM-dd}, for example: "1900-04-30"</td>
+ * <td>The ISO date of the first day of the minimum Hijrah year.</td>
+ * </tr>
+ * <tr class="altColor">
+ * <td>yyyy - a numeric 4 digit year, for example "1434"</td>
+ * <td>The value is a sequence of 12 month lengths,
+ * for example: "29 30 29 30 29 30 30 30 29 30 29 29"</td>
+ * <td>The lengths of the 12 months of the year separated by whitespace.
+ * A numeric year property must be present for every year without any gaps.
+ * The month lengths must be between 29-32 inclusive.
+ * </td>
+ * </tr>
+ * </tbody>
+ * </table>
+ *
+ * @since 1.8
+ */
+public final class HijrahChronology extends AbstractChronology implements Serializable {
+
+ /**
+ * The Hijrah Calendar id.
+ */
+ private final transient String typeId;
+ /**
+ * The Hijrah calendarType.
+ */
+ private final transient String calendarType;
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = 3127340209035924785L;
+ /**
+ * Singleton instance of the Islamic Umm Al-Qura calendar of Saudi Arabia.
+ * Other Hijrah chronology variants may be available from
+ * {@link Chronology#getAvailableChronologies}.
+ */
+ public static final HijrahChronology INSTANCE;
+ /**
+ * Flag to indicate the initialization of configuration data is complete.
+ * @see #checkCalendarInit()
+ */
+ private transient volatile boolean initComplete;
+ /**
+ * Array of epoch days indexed by Hijrah Epoch month.
+ * Computed by {@link #loadCalendarData}.
+ */
+ private transient int[] hijrahEpochMonthStartDays;
+ /**
+ * The minimum epoch day of this Hijrah calendar.
+ * Computed by {@link #loadCalendarData}.
+ */
+ private transient int minEpochDay;
+ /**
+ * The maximum epoch day for which calendar data is available.
+ * Computed by {@link #loadCalendarData}.
+ */
+ private transient int maxEpochDay;
+ /**
+ * The minimum epoch month.
+ * Computed by {@link #loadCalendarData}.
+ */
+ private transient int hijrahStartEpochMonth;
+ /**
+ * The minimum length of a month.
+ * Computed by {@link #createEpochMonths}.
+ */
+ private transient int minMonthLength;
+ /**
+ * The maximum length of a month.
+ * Computed by {@link #createEpochMonths}.
+ */
+ private transient int maxMonthLength;
+ /**
+ * The minimum length of a year in days.
+ * Computed by {@link #createEpochMonths}.
+ */
+ private transient int minYearLength;
+ /**
+ * The maximum length of a year in days.
+ * Computed by {@link #createEpochMonths}.
+ */
+ private transient int maxYearLength;
+ /**
+ * A reference to the properties stored in
+ * ${java.home}/lib/calendars.properties
+ */
+ private final transient static Properties calendarProperties;
+
+ /**
+ * Prefix of property names for Hijrah calendar variants.
+ */
+ private static final String PROP_PREFIX = "calendar.hijrah.";
+ /**
+ * Suffix of property names containing the calendar type of a variant.
+ */
+ private static final String PROP_TYPE_SUFFIX = ".type";
+
+ /**
+ * Static initialization of the predefined calendars found in the
+ * lib/calendars.properties file.
+ */
+ static {
+ try {
+ calendarProperties = sun.util.calendar.BaseCalendar.getCalendarProperties();
+ } catch (IOException ioe) {
+ throw new InternalError("Can't initialize lib/calendars.properties", ioe);
+ }
+
+ try {
+ INSTANCE = new HijrahChronology("Hijrah-umalqura");
+ // Register it by its aliases
+ AbstractChronology.registerChrono(INSTANCE, "Hijrah");
+ AbstractChronology.registerChrono(INSTANCE, "islamic");
+ } catch (DateTimeException ex) {
+ // Absence of Hijrah calendar is fatal to initializing this class.
+ PlatformLogger logger = PlatformLogger.getLogger("java.time.chrono");
+ logger.severe("Unable to initialize Hijrah calendar: Hijrah-umalqura", ex);
+ throw new RuntimeException("Unable to initialize Hijrah-umalqura calendar", ex.getCause());
+ }
+ registerVariants();
+ }
+
+ /**
+ * For each Hijrah variant listed, create the HijrahChronology and register it.
+ * Exceptions during initialization are logged but otherwise ignored.
+ */
+ private static void registerVariants() {
+ for (String name : calendarProperties.stringPropertyNames()) {
+ if (name.startsWith(PROP_PREFIX)) {
+ String id = name.substring(PROP_PREFIX.length());
+ if (id.indexOf('.') >= 0) {
+ continue; // no name or not a simple name of a calendar
+ }
+ if (id.equals(INSTANCE.getId())) {
+ continue; // do not duplicate the default
+ }
+ try {
+ // Create and register the variant
+ HijrahChronology chrono = new HijrahChronology(id);
+ AbstractChronology.registerChrono(chrono);
+ } catch (DateTimeException ex) {
+ // Log error and continue
+ PlatformLogger logger = PlatformLogger.getLogger("java.time.chrono");
+ logger.severe("Unable to initialize Hijrah calendar: " + id, ex);
+ }
+ }
+ }
+ }
+
+ /**
+ * Create a HijrahChronology for the named variant.
+ * The resource and calendar type are retrieved from properties
+ * in the {@code calendars.properties}.
+ * The property names are {@code "calendar.hijrah." + id}
+ * and {@code "calendar.hijrah." + id + ".type"}
+ * @param id the id of the calendar
+ * @throws DateTimeException if the calendar type is missing from the properties file.
+ * @throws IllegalArgumentException if the id is empty
+ */
+ private HijrahChronology(String id) throws DateTimeException {
+ if (id.isEmpty()) {
+ throw new IllegalArgumentException("calendar id is empty");
+ }
+ String propName = PROP_PREFIX + id + PROP_TYPE_SUFFIX;
+ String calType = calendarProperties.getProperty(propName);
+ if (calType == null || calType.isEmpty()) {
+ throw new DateTimeException("calendarType is missing or empty for: " + propName);
+ }
+ this.typeId = id;
+ this.calendarType = calType;
+ }
+
+ /**
+ * Check and ensure that the calendar data has been initialized.
+ * The initialization check is performed at the boundary between
+ * public and package methods. If a public calls another public method
+ * a check is not necessary in the caller.
+ * The constructors of HijrahDate call {@link #getEpochDay} or
+ * {@link #getHijrahDateInfo} so every call from HijrahDate to a
+ * HijrahChronology via package private methods has been checked.
+ *
+ * @throws DateTimeException if the calendar data configuration is
+ * malformed or IOExceptions occur loading the data
+ */
+ private void checkCalendarInit() {
+ // Keep this short so it can be inlined for performance
+ if (initComplete == false) {
+ loadCalendarData();
+ initComplete = true;
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the ID of the chronology.
+ * <p>
+ * The ID uniquely identifies the {@code Chronology}. It can be used to
+ * lookup the {@code Chronology} using {@link Chronology#of(String)}.
+ *
+ * @return the chronology ID, non-null
+ * @see #getCalendarType()
+ */
+ @Override
+ public String getId() {
+ return typeId;
+ }
+
+ /**
+ * Gets the calendar type of the Islamic calendar.
+ * <p>
+ * The calendar type is an identifier defined by the
+ * <em>Unicode Locale Data Markup Language (LDML)</em> specification.
+ * It can be used to lookup the {@code Chronology} using {@link Chronology#of(String)}.
+ *
+ * @return the calendar system type; non-null if the calendar has
+ * a standard type, otherwise null
+ * @see #getId()
+ */
+ @Override
+ public String getCalendarType() {
+ return calendarType;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains a local date in Hijrah calendar system from the
+ * era, year-of-era, month-of-year and day-of-month fields.
+ *
+ * @param era the Hijrah era, not null
+ * @param yearOfEra the year-of-era
+ * @param month the month-of-year
+ * @param dayOfMonth the day-of-month
+ * @return the Hijrah local date, not null
+ * @throws DateTimeException if unable to create the date
+ * @throws ClassCastException if the {@code era} is not a {@code HijrahEra}
+ */
+ @Override
+ public HijrahDate date(Era era, int yearOfEra, int month, int dayOfMonth) {
+ return date(prolepticYear(era, yearOfEra), month, dayOfMonth);
+ }
+
+ /**
+ * Obtains a local date in Hijrah calendar system from the
+ * proleptic-year, month-of-year and day-of-month fields.
+ *
+ * @param prolepticYear the proleptic-year
+ * @param month the month-of-year
+ * @param dayOfMonth the day-of-month
+ * @return the Hijrah local date, not null
+ * @throws DateTimeException if unable to create the date
+ */
+ @Override
+ public HijrahDate date(int prolepticYear, int month, int dayOfMonth) {
+ return HijrahDate.of(this, prolepticYear, month, dayOfMonth);
+ }
+
+ /**
+ * Obtains a local date in Hijrah calendar system from the
+ * era, year-of-era and day-of-year fields.
+ *
+ * @param era the Hijrah era, not null
+ * @param yearOfEra the year-of-era
+ * @param dayOfYear the day-of-year
+ * @return the Hijrah local date, not null
+ * @throws DateTimeException if unable to create the date
+ * @throws ClassCastException if the {@code era} is not a {@code HijrahEra}
+ */
+ @Override
+ public HijrahDate dateYearDay(Era era, int yearOfEra, int dayOfYear) {
+ return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear);
+ }
+
+ /**
+ * Obtains a local date in Hijrah calendar system from the
+ * proleptic-year and day-of-year fields.
+ *
+ * @param prolepticYear the proleptic-year
+ * @param dayOfYear the day-of-year
+ * @return the Hijrah local date, not null
+ * @throws DateTimeException if the value of the year is out of range,
+ * or if the day-of-year is invalid for the year
+ */
+ @Override
+ public HijrahDate dateYearDay(int prolepticYear, int dayOfYear) {
+ HijrahDate date = HijrahDate.of(this, prolepticYear, 1, 1);
+ if (dayOfYear > date.lengthOfYear()) {
+ throw new DateTimeException("Invalid dayOfYear: " + dayOfYear);
+ }
+ return date.plusDays(dayOfYear - 1);
+ }
+
+ /**
+ * Obtains a local date in the Hijrah calendar system from the epoch-day.
+ *
+ * @param epochDay the epoch day
+ * @return the Hijrah local date, not null
+ * @throws DateTimeException if unable to create the date
+ */
+ @Override // override with covariant return type
+ public HijrahDate dateEpochDay(long epochDay) {
+ return HijrahDate.ofEpochDay(this, epochDay);
+ }
+
+ @Override
+ public HijrahDate dateNow() {
+ return dateNow(Clock.systemDefaultZone());
+ }
+
+ @Override
+ public HijrahDate dateNow(ZoneId zone) {
+ return dateNow(Clock.system(zone));
+ }
+
+ @Override
+ public HijrahDate dateNow(Clock clock) {
+ return date(LocalDate.now(clock));
+ }
+
+ @Override
+ public HijrahDate date(TemporalAccessor temporal) {
+ if (temporal instanceof HijrahDate) {
+ return (HijrahDate) temporal;
+ }
+ return HijrahDate.ofEpochDay(this, temporal.getLong(EPOCH_DAY));
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public ChronoLocalDateTime<HijrahDate> localDateTime(TemporalAccessor temporal) {
+ return (ChronoLocalDateTime<HijrahDate>) super.localDateTime(temporal);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public ChronoZonedDateTime<HijrahDate> zonedDateTime(TemporalAccessor temporal) {
+ return (ChronoZonedDateTime<HijrahDate>) super.zonedDateTime(temporal);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public ChronoZonedDateTime<HijrahDate> zonedDateTime(Instant instant, ZoneId zone) {
+ return (ChronoZonedDateTime<HijrahDate>) super.zonedDateTime(instant, zone);
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ public boolean isLeapYear(long prolepticYear) {
+ checkCalendarInit();
+ if (prolepticYear < getMinimumYear() || prolepticYear > getMaximumYear()) {
+ return false;
+ }
+ int len = getYearLength((int) prolepticYear);
+ return (len > 354);
+ }
+
+ @Override
+ public int prolepticYear(Era era, int yearOfEra) {
+ if (era instanceof HijrahEra == false) {
+ throw new ClassCastException("Era must be HijrahEra");
+ }
+ return yearOfEra;
+ }
+
+ @Override
+ public HijrahEra eraOf(int eraValue) {
+ switch (eraValue) {
+ case 1:
+ return HijrahEra.AH;
+ default:
+ throw new DateTimeException("invalid Hijrah era");
+ }
+ }
+
+ @Override
+ public List<Era> eras() {
+ return Arrays.<Era>asList(HijrahEra.values());
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ public ValueRange range(ChronoField field) {
+ checkCalendarInit();
+ if (field instanceof ChronoField) {
+ ChronoField f = field;
+ switch (f) {
+ case DAY_OF_MONTH:
+ return ValueRange.of(1, 1, getMinimumMonthLength(), getMaximumMonthLength());
+ case DAY_OF_YEAR:
+ return ValueRange.of(1, getMaximumDayOfYear());
+ case ALIGNED_WEEK_OF_MONTH:
+ return ValueRange.of(1, 5);
+ case YEAR:
+ case YEAR_OF_ERA:
+ return ValueRange.of(getMinimumYear(), getMaximumYear());
+ case ERA:
+ return ValueRange.of(1, 1);
+ default:
+ return field.range();
+ }
+ }
+ return field.range();
+ }
+
+ //-----------------------------------------------------------------------
+ @Override // override for return type
+ public HijrahDate resolveDate(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
+ return (HijrahDate) super.resolveDate(fieldValues, resolverStyle);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Check the validity of a year.
+ *
+ * @param prolepticYear the year to check
+ */
+ int checkValidYear(long prolepticYear) {
+ if (prolepticYear < getMinimumYear() || prolepticYear > getMaximumYear()) {
+ throw new DateTimeException("Invalid Hijrah year: " + prolepticYear);
+ }
+ return (int) prolepticYear;
+ }
+
+ void checkValidDayOfYear(int dayOfYear) {
+ if (dayOfYear < 1 || dayOfYear > getMaximumDayOfYear()) {
+ throw new DateTimeException("Invalid Hijrah day of year: " + dayOfYear);
+ }
+ }
+
+ void checkValidMonth(int month) {
+ if (month < 1 || month > 12) {
+ throw new DateTimeException("Invalid Hijrah month: " + month);
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns an array containing the Hijrah year, month and day
+ * computed from the epoch day.
+ *
+ * @param epochDay the EpochDay
+ * @return int[0] = YEAR, int[1] = MONTH, int[2] = DATE
+ */
+ int[] getHijrahDateInfo(int epochDay) {
+ checkCalendarInit(); // ensure that the chronology is initialized
+ if (epochDay < minEpochDay || epochDay >= maxEpochDay) {
+ throw new DateTimeException("Hijrah date out of range");
+ }
+
+ int epochMonth = epochDayToEpochMonth(epochDay);
+ int year = epochMonthToYear(epochMonth);
+ int month = epochMonthToMonth(epochMonth);
+ int day1 = epochMonthToEpochDay(epochMonth);
+ int date = epochDay - day1; // epochDay - dayOfEpoch(year, month);
+
+ int dateInfo[] = new int[3];
+ dateInfo[0] = year;
+ dateInfo[1] = month + 1; // change to 1-based.
+ dateInfo[2] = date + 1; // change to 1-based.
+ return dateInfo;
+ }
+
+ /**
+ * Return the epoch day computed from Hijrah year, month, and day.
+ *
+ * @param prolepticYear the year to represent, 0-origin
+ * @param monthOfYear the month-of-year to represent, 1-origin
+ * @param dayOfMonth the day-of-month to represent, 1-origin
+ * @return the epoch day
+ */
+ long getEpochDay(int prolepticYear, int monthOfYear, int dayOfMonth) {
+ checkCalendarInit(); // ensure that the chronology is initialized
+ checkValidMonth(monthOfYear);
+ int epochMonth = yearToEpochMonth(prolepticYear) + (monthOfYear - 1);
+ if (epochMonth < 0 || epochMonth >= hijrahEpochMonthStartDays.length) {
+ throw new DateTimeException("Invalid Hijrah date, year: " +
+ prolepticYear + ", month: " + monthOfYear);
+ }
+ if (dayOfMonth < 1 || dayOfMonth > getMonthLength(prolepticYear, monthOfYear)) {
+ throw new DateTimeException("Invalid Hijrah day of month: " + dayOfMonth);
+ }
+ return epochMonthToEpochDay(epochMonth) + (dayOfMonth - 1);
+ }
+
+ /**
+ * Returns day of year for the year and month.
+ *
+ * @param prolepticYear a proleptic year
+ * @param month a month, 1-origin
+ * @return the day of year, 1-origin
+ */
+ int getDayOfYear(int prolepticYear, int month) {
+ return yearMonthToDayOfYear(prolepticYear, (month - 1));
+ }
+
+ /**
+ * Returns month length for the year and month.
+ *
+ * @param prolepticYear a proleptic year
+ * @param monthOfYear a month, 1-origin.
+ * @return the length of the month
+ */
+ int getMonthLength(int prolepticYear, int monthOfYear) {
+ int epochMonth = yearToEpochMonth(prolepticYear) + (monthOfYear - 1);
+ if (epochMonth < 0 || epochMonth >= hijrahEpochMonthStartDays.length) {
+ throw new DateTimeException("Invalid Hijrah date, year: " +
+ prolepticYear + ", month: " + monthOfYear);
+ }
+ return epochMonthLength(epochMonth);
+ }
+
+ /**
+ * Returns year length.
+ * Note: The 12th month must exist in the data.
+ *
+ * @param prolepticYear a proleptic year
+ * @return year length in days
+ */
+ int getYearLength(int prolepticYear) {
+ return yearMonthToDayOfYear(prolepticYear, 12);
+ }
+
+ /**
+ * Return the minimum supported Hijrah year.
+ *
+ * @return the minimum
+ */
+ int getMinimumYear() {
+ return epochMonthToYear(0);
+ }
+
+ /**
+ * Return the maximum supported Hijrah ear.
+ *
+ * @return the minimum
+ */
+ int getMaximumYear() {
+ return epochMonthToYear(hijrahEpochMonthStartDays.length - 1) - 1;
+ }
+
+ /**
+ * Returns maximum day-of-month.
+ *
+ * @return maximum day-of-month
+ */
+ int getMaximumMonthLength() {
+ return maxMonthLength;
+ }
+
+ /**
+ * Returns smallest maximum day-of-month.
+ *
+ * @return smallest maximum day-of-month
+ */
+ int getMinimumMonthLength() {
+ return minMonthLength;
+ }
+
+ /**
+ * Returns maximum day-of-year.
+ *
+ * @return maximum day-of-year
+ */
+ int getMaximumDayOfYear() {
+ return maxYearLength;
+ }
+
+ /**
+ * Returns smallest maximum day-of-year.
+ *
+ * @return smallest maximum day-of-year
+ */
+ int getSmallestMaximumDayOfYear() {
+ return minYearLength;
+ }
+
+ /**
+ * Returns the epochMonth found by locating the epochDay in the table. The
+ * epochMonth is the index in the table
+ *
+ * @param epochDay
+ * @return The index of the element of the start of the month containing the
+ * epochDay.
+ */
+ private int epochDayToEpochMonth(int epochDay) {
+ // binary search
+ int ndx = Arrays.binarySearch(hijrahEpochMonthStartDays, epochDay);
+ if (ndx < 0) {
+ ndx = -ndx - 2;
+ }
+ return ndx;
+ }
+
+ /**
+ * Returns the year computed from the epochMonth
+ *
+ * @param epochMonth the epochMonth
+ * @return the Hijrah Year
+ */
+ private int epochMonthToYear(int epochMonth) {
+ return (epochMonth + hijrahStartEpochMonth) / 12;
+ }
+
+ /**
+ * Returns the epochMonth for the Hijrah Year.
+ *
+ * @param year the HijrahYear
+ * @return the epochMonth for the beginning of the year.
+ */
+ private int yearToEpochMonth(int year) {
+ return (year * 12) - hijrahStartEpochMonth;
+ }
+
+ /**
+ * Returns the Hijrah month from the epochMonth.
+ *
+ * @param epochMonth the epochMonth
+ * @return the month of the Hijrah Year
+ */
+ private int epochMonthToMonth(int epochMonth) {
+ return (epochMonth + hijrahStartEpochMonth) % 12;
+ }
+
+ /**
+ * Returns the epochDay for the start of the epochMonth.
+ *
+ * @param epochMonth the epochMonth
+ * @return the epochDay for the start of the epochMonth.
+ */
+ private int epochMonthToEpochDay(int epochMonth) {
+ return hijrahEpochMonthStartDays[epochMonth];
+
+ }
+
+ /**
+ * Returns the day of year for the requested HijrahYear and month.
+ *
+ * @param prolepticYear the Hijrah year
+ * @param month the Hijrah month
+ * @return the day of year for the start of the month of the year
+ */
+ private int yearMonthToDayOfYear(int prolepticYear, int month) {
+ int epochMonthFirst = yearToEpochMonth(prolepticYear);
+ return epochMonthToEpochDay(epochMonthFirst + month)
+ - epochMonthToEpochDay(epochMonthFirst);
+ }
+
+ /**
+ * Returns the length of the epochMonth. It is computed from the start of
+ * the following month minus the start of the requested month.
+ *
+ * @param epochMonth the epochMonth; assumed to be within range
+ * @return the length in days of the epochMonth
+ */
+ private int epochMonthLength(int epochMonth) {
+ // The very last entry in the epochMonth table is not the start of a month
+ return hijrahEpochMonthStartDays[epochMonth + 1]
+ - hijrahEpochMonthStartDays[epochMonth];
+ }
+
+ //-----------------------------------------------------------------------
+ private static final String KEY_ID = "id";
+ private static final String KEY_TYPE = "type";
+ private static final String KEY_VERSION = "version";
+ private static final String KEY_ISO_START = "iso-start";
+
+ /**
+ * Return the configuration properties from the resource.
+ * <p>
+ * The default location of the variant configuration resource is:
+ * <pre>
+ * "$java.home/lib/" + resource-name
+ * </pre>
+ *
+ * @param resource the name of the calendar property resource
+ * @return a Properties containing the properties read from the resource.
+ * @throws Exception if access to the property resource fails
+ */
+ private static Properties readConfigProperties(final String resource) throws Exception {
+ // Android-changed: Load system resources.
+ Properties props = new Properties();
+ try (InputStream is = ClassLoader.getSystemResourceAsStream(resource)) {
+ props.load(is);
+ }
+ return props;
+ }
+
+ /**
+ * Loads and processes the Hijrah calendar properties file for this calendarType.
+ * The starting Hijrah date and the corresponding ISO date are
+ * extracted and used to calculate the epochDate offset.
+ * The version number is identified and ignored.
+ * Everything else is the data for a year with containing the length of each
+ * of 12 months.
+ *
+ * @throws DateTimeException if initialization of the calendar data from the
+ * resource fails
+ */
+ private void loadCalendarData() {
+ try {
+ String resourceName = calendarProperties.getProperty(PROP_PREFIX + typeId);
+ Objects.requireNonNull(resourceName, "Resource missing for calendar: " + PROP_PREFIX + typeId);
+ Properties props = readConfigProperties(resourceName);
+
+ Map<Integer, int[]> years = new HashMap<>();
+ int minYear = Integer.MAX_VALUE;
+ int maxYear = Integer.MIN_VALUE;
+ String id = null;
+ String type = null;
+ String version = null;
+ int isoStart = 0;
+ for (Map.Entry<Object, Object> entry : props.entrySet()) {
+ String key = (String) entry.getKey();
+ switch (key) {
+ case KEY_ID:
+ id = (String)entry.getValue();
+ break;
+ case KEY_TYPE:
+ type = (String)entry.getValue();
+ break;
+ case KEY_VERSION:
+ version = (String)entry.getValue();
+ break;
+ case KEY_ISO_START: {
+ int[] ymd = parseYMD((String) entry.getValue());
+ isoStart = (int) LocalDate.of(ymd[0], ymd[1], ymd[2]).toEpochDay();
+ break;
+ }
+ default:
+ try {
+ // Everything else is either a year or invalid
+ int year = Integer.valueOf(key);
+ int[] months = parseMonths((String) entry.getValue());
+ years.put(year, months);
+ maxYear = Math.max(maxYear, year);
+ minYear = Math.min(minYear, year);
+ } catch (NumberFormatException nfe) {
+ throw new IllegalArgumentException("bad key: " + key);
+ }
+ }
+ }
+
+ if (!getId().equals(id)) {
+ throw new IllegalArgumentException("Configuration is for a different calendar: " + id);
+ }
+ if (!getCalendarType().equals(type)) {
+ throw new IllegalArgumentException("Configuration is for a different calendar type: " + type);
+ }
+ if (version == null || version.isEmpty()) {
+ throw new IllegalArgumentException("Configuration does not contain a version");
+ }
+ if (isoStart == 0) {
+ throw new IllegalArgumentException("Configuration does not contain a ISO start date");
+ }
+
+ // Now create and validate the array of epochDays indexed by epochMonth
+ hijrahStartEpochMonth = minYear * 12;
+ minEpochDay = isoStart;
+ hijrahEpochMonthStartDays = createEpochMonths(minEpochDay, minYear, maxYear, years);
+ maxEpochDay = hijrahEpochMonthStartDays[hijrahEpochMonthStartDays.length - 1];
+
+ // Compute the min and max year length in days.
+ for (int year = minYear; year < maxYear; year++) {
+ int length = getYearLength(year);
+ minYearLength = Math.min(minYearLength, length);
+ maxYearLength = Math.max(maxYearLength, length);
+ }
+ } catch (Exception ex) {
+ // Log error and throw a DateTimeException
+ PlatformLogger logger = PlatformLogger.getLogger("java.time.chrono");
+ logger.severe("Unable to initialize Hijrah calendar proxy: " + typeId, ex);
+ throw new DateTimeException("Unable to initialize HijrahCalendar: " + typeId, ex);
+ }
+ }
+
+ /**
+ * Converts the map of year to month lengths ranging from minYear to maxYear
+ * into a linear contiguous array of epochDays. The index is the hijrahMonth
+ * computed from year and month and offset by minYear. The value of each
+ * entry is the epochDay corresponding to the first day of the month.
+ *
+ * @param minYear The minimum year for which data is provided
+ * @param maxYear The maximum year for which data is provided
+ * @param years a Map of year to the array of 12 month lengths
+ * @return array of epochDays for each month from min to max
+ */
+ private int[] createEpochMonths(int epochDay, int minYear, int maxYear, Map<Integer, int[]> years) {
+ // Compute the size for the array of dates
+ int numMonths = (maxYear - minYear + 1) * 12 + 1;
+
+ // Initialize the running epochDay as the corresponding ISO Epoch day
+ int epochMonth = 0; // index into array of epochMonths
+ int[] epochMonths = new int[numMonths];
+ minMonthLength = Integer.MAX_VALUE;
+ maxMonthLength = Integer.MIN_VALUE;
+
+ // Only whole years are valid, any zero's in the array are illegal
+ for (int year = minYear; year <= maxYear; year++) {
+ int[] months = years.get(year);// must not be gaps
+ for (int month = 0; month < 12; month++) {
+ int length = months[month];
+ epochMonths[epochMonth++] = epochDay;
+
+ if (length < 29 || length > 32) {
+ throw new IllegalArgumentException("Invalid month length in year: " + minYear);
+ }
+ epochDay += length;
+ minMonthLength = Math.min(minMonthLength, length);
+ maxMonthLength = Math.max(maxMonthLength, length);
+ }
+ }
+
+ // Insert the final epochDay
+ epochMonths[epochMonth++] = epochDay;
+
+ if (epochMonth != epochMonths.length) {
+ throw new IllegalStateException("Did not fill epochMonths exactly: ndx = " + epochMonth
+ + " should be " + epochMonths.length);
+ }
+
+ return epochMonths;
+ }
+
+ /**
+ * Parses the 12 months lengths from a property value for a specific year.
+ *
+ * @param line the value of a year property
+ * @return an array of int[12] containing the 12 month lengths
+ * @throws IllegalArgumentException if the number of months is not 12
+ * @throws NumberFormatException if the 12 tokens are not numbers
+ */
+ private int[] parseMonths(String line) {
+ int[] months = new int[12];
+ String[] numbers = line.split("\\s");
+ if (numbers.length != 12) {
+ throw new IllegalArgumentException("wrong number of months on line: " + Arrays.toString(numbers) + "; count: " + numbers.length);
+ }
+ for (int i = 0; i < 12; i++) {
+ try {
+ months[i] = Integer.valueOf(numbers[i]);
+ } catch (NumberFormatException nfe) {
+ throw new IllegalArgumentException("bad key: " + numbers[i]);
+ }
+ }
+ return months;
+ }
+
+ /**
+ * Parse yyyy-MM-dd into a 3 element array [yyyy, mm, dd].
+ *
+ * @param string the input string
+ * @return the 3 element array with year, month, day
+ */
+ private int[] parseYMD(String string) {
+ // yyyy-MM-dd
+ string = string.trim();
+ try {
+ if (string.charAt(4) != '-' || string.charAt(7) != '-') {
+ throw new IllegalArgumentException("date must be yyyy-MM-dd");
+ }
+ int[] ymd = new int[3];
+ ymd[0] = Integer.valueOf(string.substring(0, 4));
+ ymd[1] = Integer.valueOf(string.substring(5, 7));
+ ymd[2] = Integer.valueOf(string.substring(8, 10));
+ return ymd;
+ } catch (NumberFormatException ex) {
+ throw new IllegalArgumentException("date must be yyyy-MM-dd", ex);
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Writes the Chronology using a
+ * <a href="../../../serialized-form.html#java.time.chrono.Ser">dedicated serialized form</a>.
+ * @serialData
+ * <pre>
+ * out.writeByte(1); // identifies a Chronology
+ * out.writeUTF(getId());
+ * </pre>
+ *
+ * @return the instance of {@code Ser}, not null
+ */
+ @Override
+ Object writeReplace() {
+ return super.writeReplace();
+ }
+
+ /**
+ * Defend against malicious streams.
+ *
+ * @param s the stream to read
+ * @throws InvalidObjectException always
+ */
+ private void readObject(ObjectInputStream s) throws InvalidObjectException {
+ throw new InvalidObjectException("Deserialization via serialization delegate");
+ }
+}
diff --git a/java/time/chrono/HijrahDate.java b/java/time/chrono/HijrahDate.java
new file mode 100644
index 0000000..de2cb13
--- /dev/null
+++ b/java/time/chrono/HijrahDate.java
@@ -0,0 +1,692 @@
+/*
+ * Copyright (c) 2012, 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) 2012, 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.ALIGNED_DAY_OF_WEEK_IN_MONTH;
+import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR;
+import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH;
+import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR;
+import static java.time.temporal.ChronoField.DAY_OF_MONTH;
+import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
+import static java.time.temporal.ChronoField.YEAR;
+
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInput;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutput;
+import java.io.Serializable;
+import java.time.Clock;
+import java.time.DateTimeException;
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.time.temporal.ChronoField;
+import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalAdjuster;
+import java.time.temporal.TemporalAmount;
+import java.time.temporal.TemporalField;
+import java.time.temporal.TemporalQuery;
+import java.time.temporal.TemporalUnit;
+import java.time.temporal.UnsupportedTemporalTypeException;
+import java.time.temporal.ValueRange;
+
+// Android-changed: removed ValueBased paragraph.
+/**
+ * A date in the Hijrah calendar system.
+ * <p>
+ * This date operates using one of several variants of the
+ * {@linkplain HijrahChronology Hijrah calendar}.
+ * <p>
+ * The Hijrah calendar has a different total of days in a year than
+ * Gregorian calendar, and the length of each month is based on the period
+ * of a complete revolution of the moon around the earth
+ * (as between successive new moons).
+ * Refer to the {@link HijrahChronology} for details of supported variants.
+ * <p>
+ * Each HijrahDate is created bound to a particular HijrahChronology,
+ * The same chronology is propagated to each HijrahDate computed from the date.
+ * To use a different Hijrah variant, its HijrahChronology can be used
+ * to create new HijrahDate instances.
+ * Alternatively, the {@link #withVariant} method can be used to convert
+ * to a new HijrahChronology.
+ *
+ * @implSpec
+ * This class is immutable and thread-safe.
+ *
+ * @since 1.8
+ */
+public final class HijrahDate
+ extends ChronoLocalDateImpl<HijrahDate>
+ implements ChronoLocalDate, Serializable {
+
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = -5207853542612002020L;
+ /**
+ * The Chronology of this HijrahDate.
+ */
+ private final transient HijrahChronology chrono;
+ /**
+ * The proleptic year.
+ */
+ private final transient int prolepticYear;
+ /**
+ * The month-of-year.
+ */
+ private final transient int monthOfYear;
+ /**
+ * The day-of-month.
+ */
+ private final transient int dayOfMonth;
+
+ //-------------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code HijrahDate} from the Hijrah proleptic year,
+ * month-of-year and day-of-month.
+ *
+ * @param prolepticYear the proleptic year to represent in the Hijrah calendar
+ * @param monthOfYear the month-of-year to represent, from 1 to 12
+ * @param dayOfMonth the day-of-month to represent, from 1 to 30
+ * @return the Hijrah date, never null
+ * @throws DateTimeException if the value of any field is out of range
+ */
+ static HijrahDate of(HijrahChronology chrono, int prolepticYear, int monthOfYear, int dayOfMonth) {
+ return new HijrahDate(chrono, prolepticYear, monthOfYear, dayOfMonth);
+ }
+
+ /**
+ * Returns a HijrahDate for the chronology and epochDay.
+ * @param chrono The Hijrah chronology
+ * @param epochDay the epoch day
+ * @return a HijrahDate for the epoch day; non-null
+ */
+ static HijrahDate ofEpochDay(HijrahChronology chrono, long epochDay) {
+ return new HijrahDate(chrono, epochDay);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains the current {@code HijrahDate} of the Islamic Umm Al-Qura calendar
+ * in the default time-zone.
+ * <p>
+ * This will query the {@link Clock#systemDefaultZone() system clock} in the default
+ * time-zone to obtain the current date.
+ * <p>
+ * Using this method will prevent the ability to use an alternate clock for testing
+ * because the clock is hard-coded.
+ *
+ * @return the current date using the system clock and default time-zone, not null
+ */
+ public static HijrahDate now() {
+ return now(Clock.systemDefaultZone());
+ }
+
+ /**
+ * Obtains the current {@code HijrahDate} of the Islamic Umm Al-Qura calendar
+ * in the specified time-zone.
+ * <p>
+ * This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date.
+ * Specifying the time-zone avoids dependence on the default time-zone.
+ * <p>
+ * Using this method will prevent the ability to use an alternate clock for testing
+ * because the clock is hard-coded.
+ *
+ * @param zone the zone ID to use, not null
+ * @return the current date using the system clock, not null
+ */
+ public static HijrahDate now(ZoneId zone) {
+ return now(Clock.system(zone));
+ }
+
+ /**
+ * Obtains the current {@code HijrahDate} of the Islamic Umm Al-Qura calendar
+ * from the specified clock.
+ * <p>
+ * This will query the specified clock to obtain the current date - today.
+ * Using this method allows the use of an alternate clock for testing.
+ * The alternate clock may be introduced using {@linkplain Clock dependency injection}.
+ *
+ * @param clock the clock to use, not null
+ * @return the current date, not null
+ * @throws DateTimeException if the current date cannot be obtained
+ */
+ public static HijrahDate now(Clock clock) {
+ return HijrahDate.ofEpochDay(HijrahChronology.INSTANCE, LocalDate.now(clock).toEpochDay());
+ }
+
+ /**
+ * Obtains a {@code HijrahDate} of the Islamic Umm Al-Qura calendar
+ * from the proleptic-year, month-of-year and day-of-month fields.
+ * <p>
+ * This returns a {@code HijrahDate} with the specified fields.
+ * The day must be valid for the year and month, otherwise an exception will be thrown.
+ *
+ * @param prolepticYear the Hijrah proleptic-year
+ * @param month the Hijrah month-of-year, from 1 to 12
+ * @param dayOfMonth the Hijrah day-of-month, from 1 to 30
+ * @return the date in Hijrah calendar system, not null
+ * @throws DateTimeException if the value of any field is out of range,
+ * or if the day-of-month is invalid for the month-year
+ */
+ public static HijrahDate of(int prolepticYear, int month, int dayOfMonth) {
+ return HijrahChronology.INSTANCE.date(prolepticYear, month, dayOfMonth);
+ }
+
+ /**
+ * Obtains a {@code HijrahDate} of the Islamic Umm Al-Qura calendar from a temporal object.
+ * <p>
+ * This obtains a date in the Hijrah calendar system based on the specified temporal.
+ * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
+ * which this factory converts to an instance of {@code HijrahDate}.
+ * <p>
+ * The conversion typically uses the {@link ChronoField#EPOCH_DAY EPOCH_DAY}
+ * field, which is standardized across calendar systems.
+ * <p>
+ * This method matches the signature of the functional interface {@link TemporalQuery}
+ * allowing it to be used as a query via method reference, {@code HijrahDate::from}.
+ *
+ * @param temporal the temporal object to convert, not null
+ * @return the date in Hijrah calendar system, not null
+ * @throws DateTimeException if unable to convert to a {@code HijrahDate}
+ */
+ public static HijrahDate from(TemporalAccessor temporal) {
+ return HijrahChronology.INSTANCE.date(temporal);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Constructs an {@code HijrahDate} with the proleptic-year, month-of-year and
+ * day-of-month fields.
+ *
+ * @param chrono The chronology to create the date with
+ * @param prolepticYear the proleptic year
+ * @param monthOfYear the month of year
+ * @param dayOfMonth the day of month
+ */
+ private HijrahDate(HijrahChronology chrono, int prolepticYear, int monthOfYear, int dayOfMonth) {
+ // Computing the Gregorian day checks the valid ranges
+ chrono.getEpochDay(prolepticYear, monthOfYear, dayOfMonth);
+
+ this.chrono = chrono;
+ this.prolepticYear = prolepticYear;
+ this.monthOfYear = monthOfYear;
+ this.dayOfMonth = dayOfMonth;
+ }
+
+ /**
+ * Constructs an instance with the Epoch Day.
+ *
+ * @param epochDay the epochDay
+ */
+ private HijrahDate(HijrahChronology chrono, long epochDay) {
+ int[] dateInfo = chrono.getHijrahDateInfo((int)epochDay);
+
+ this.chrono = chrono;
+ this.prolepticYear = dateInfo[0];
+ this.monthOfYear = dateInfo[1];
+ this.dayOfMonth = dateInfo[2];
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the chronology of this date, which is the Hijrah calendar system.
+ * <p>
+ * The {@code Chronology} represents the calendar system in use.
+ * The era and other fields in {@link ChronoField} are defined by the chronology.
+ *
+ * @return the Hijrah chronology, not null
+ */
+ @Override
+ public HijrahChronology getChronology() {
+ return chrono;
+ }
+
+ /**
+ * Gets the era applicable at this date.
+ * <p>
+ * The Hijrah calendar system has one era, 'AH',
+ * defined by {@link HijrahEra}.
+ *
+ * @return the era applicable at this date, not null
+ */
+ @Override
+ public HijrahEra getEra() {
+ return HijrahEra.AH;
+ }
+
+ /**
+ * Returns the length of the month represented by this date.
+ * <p>
+ * This returns the length of the month in days.
+ * Month lengths in the Hijrah calendar system vary between 29 and 30 days.
+ *
+ * @return the length of the month in days
+ */
+ @Override
+ public int lengthOfMonth() {
+ return chrono.getMonthLength(prolepticYear, monthOfYear);
+ }
+
+ /**
+ * Returns the length of the year represented by this date.
+ * <p>
+ * This returns the length of the year in days.
+ * A Hijrah calendar system year is typically shorter than
+ * that of the ISO calendar system.
+ *
+ * @return the length of the year in days
+ */
+ @Override
+ public int lengthOfYear() {
+ return chrono.getYearLength(prolepticYear);
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ public ValueRange range(TemporalField field) {
+ if (field instanceof ChronoField) {
+ if (isSupported(field)) {
+ ChronoField f = (ChronoField) field;
+ switch (f) {
+ case DAY_OF_MONTH: return ValueRange.of(1, lengthOfMonth());
+ case DAY_OF_YEAR: return ValueRange.of(1, lengthOfYear());
+ case ALIGNED_WEEK_OF_MONTH: return ValueRange.of(1, 5); // TODO
+ // TODO does the limited range of valid years cause years to
+ // start/end part way through? that would affect range
+ }
+ return getChronology().range(f);
+ }
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
+ }
+ return field.rangeRefinedBy(this);
+ }
+
+ @Override
+ public long getLong(TemporalField field) {
+ if (field instanceof ChronoField) {
+ switch ((ChronoField) field) {
+ case DAY_OF_WEEK: return getDayOfWeek();
+ case ALIGNED_DAY_OF_WEEK_IN_MONTH: return ((getDayOfWeek() - 1) % 7) + 1;
+ case ALIGNED_DAY_OF_WEEK_IN_YEAR: return ((getDayOfYear() - 1) % 7) + 1;
+ case DAY_OF_MONTH: return this.dayOfMonth;
+ case DAY_OF_YEAR: return this.getDayOfYear();
+ case EPOCH_DAY: return toEpochDay();
+ case ALIGNED_WEEK_OF_MONTH: return ((dayOfMonth - 1) / 7) + 1;
+ case ALIGNED_WEEK_OF_YEAR: return ((getDayOfYear() - 1) / 7) + 1;
+ case MONTH_OF_YEAR: return monthOfYear;
+ case PROLEPTIC_MONTH: return getProlepticMonth();
+ case YEAR_OF_ERA: return prolepticYear;
+ case YEAR: return prolepticYear;
+ case ERA: return getEraValue();
+ }
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
+ }
+ return field.getFrom(this);
+ }
+
+ private long getProlepticMonth() {
+ return prolepticYear * 12L + monthOfYear - 1;
+ }
+
+ @Override
+ public HijrahDate with(TemporalField field, long newValue) {
+ if (field instanceof ChronoField) {
+ ChronoField f = (ChronoField) field;
+ // not using checkValidIntValue so EPOCH_DAY and PROLEPTIC_MONTH work
+ chrono.range(f).checkValidValue(newValue, f); // TODO: validate value
+ int nvalue = (int) newValue;
+ switch (f) {
+ case DAY_OF_WEEK: return plusDays(newValue - getDayOfWeek());
+ case ALIGNED_DAY_OF_WEEK_IN_MONTH: return plusDays(newValue - getLong(ALIGNED_DAY_OF_WEEK_IN_MONTH));
+ case ALIGNED_DAY_OF_WEEK_IN_YEAR: return plusDays(newValue - getLong(ALIGNED_DAY_OF_WEEK_IN_YEAR));
+ case DAY_OF_MONTH: return resolvePreviousValid(prolepticYear, monthOfYear, nvalue);
+ case DAY_OF_YEAR: return plusDays(Math.min(nvalue, lengthOfYear()) - getDayOfYear());
+ case EPOCH_DAY: return new HijrahDate(chrono, newValue);
+ case ALIGNED_WEEK_OF_MONTH: return plusDays((newValue - getLong(ALIGNED_WEEK_OF_MONTH)) * 7);
+ case ALIGNED_WEEK_OF_YEAR: return plusDays((newValue - getLong(ALIGNED_WEEK_OF_YEAR)) * 7);
+ case MONTH_OF_YEAR: return resolvePreviousValid(prolepticYear, nvalue, dayOfMonth);
+ case PROLEPTIC_MONTH: return plusMonths(newValue - getProlepticMonth());
+ case YEAR_OF_ERA: return resolvePreviousValid(prolepticYear >= 1 ? nvalue : 1 - nvalue, monthOfYear, dayOfMonth);
+ case YEAR: return resolvePreviousValid(nvalue, monthOfYear, dayOfMonth);
+ case ERA: return resolvePreviousValid(1 - prolepticYear, monthOfYear, dayOfMonth);
+ }
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
+ }
+ return super.with(field, newValue);
+ }
+
+ private HijrahDate resolvePreviousValid(int prolepticYear, int month, int day) {
+ int monthDays = chrono.getMonthLength(prolepticYear, month);
+ if (day > monthDays) {
+ day = monthDays;
+ }
+ return HijrahDate.of(chrono, prolepticYear, month, day);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws DateTimeException if unable to make the adjustment.
+ * For example, if the adjuster requires an ISO chronology
+ * @throws ArithmeticException {@inheritDoc}
+ */
+ @Override
+ public HijrahDate with(TemporalAdjuster adjuster) {
+ return super.with(adjuster);
+ }
+
+ /**
+ * Returns a {@code HijrahDate} with the Chronology requested.
+ * <p>
+ * The year, month, and day are checked against the new requested
+ * HijrahChronology. If the chronology has a shorter month length
+ * for the month, the day is reduced to be the last day of the month.
+ *
+ * @param chronology the new HijrahChonology, non-null
+ * @return a HijrahDate with the requested HijrahChronology, non-null
+ */
+ public HijrahDate withVariant(HijrahChronology chronology) {
+ if (chrono == chronology) {
+ return this;
+ }
+ // Like resolvePreviousValid the day is constrained to stay in the same month
+ int monthDays = chronology.getDayOfYear(prolepticYear, monthOfYear);
+ return HijrahDate.of(chronology, prolepticYear, monthOfYear,(dayOfMonth > monthDays) ? monthDays : dayOfMonth );
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws DateTimeException {@inheritDoc}
+ * @throws ArithmeticException {@inheritDoc}
+ */
+ @Override
+ public HijrahDate plus(TemporalAmount amount) {
+ return super.plus(amount);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws DateTimeException {@inheritDoc}
+ * @throws ArithmeticException {@inheritDoc}
+ */
+ @Override
+ public HijrahDate minus(TemporalAmount amount) {
+ return super.minus(amount);
+ }
+
+ @Override
+ public long toEpochDay() {
+ return chrono.getEpochDay(prolepticYear, monthOfYear, dayOfMonth);
+ }
+
+ /**
+ * Gets the day-of-year field.
+ * <p>
+ * This method returns the primitive {@code int} value for the day-of-year.
+ *
+ * @return the day-of-year
+ */
+ private int getDayOfYear() {
+ return chrono.getDayOfYear(prolepticYear, monthOfYear) + dayOfMonth;
+ }
+
+ /**
+ * Gets the day-of-week value.
+ *
+ * @return the day-of-week; computed from the epochday
+ */
+ private int getDayOfWeek() {
+ int dow0 = (int)Math.floorMod(toEpochDay() + 3, 7);
+ return dow0 + 1;
+ }
+
+ /**
+ * Gets the Era of this date.
+ *
+ * @return the Era of this date; computed from epochDay
+ */
+ private int getEraValue() {
+ return (prolepticYear > 1 ? 1 : 0);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if the year is a leap year, according to the Hijrah calendar system rules.
+ *
+ * @return true if this date is in a leap year
+ */
+ @Override
+ public boolean isLeapYear() {
+ return chrono.isLeapYear(prolepticYear);
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ HijrahDate plusYears(long years) {
+ if (years == 0) {
+ return this;
+ }
+ int newYear = Math.addExact(this.prolepticYear, (int)years);
+ return resolvePreviousValid(newYear, monthOfYear, dayOfMonth);
+ }
+
+ @Override
+ HijrahDate plusMonths(long monthsToAdd) {
+ if (monthsToAdd == 0) {
+ return this;
+ }
+ long monthCount = prolepticYear * 12L + (monthOfYear - 1);
+ long calcMonths = monthCount + monthsToAdd; // safe overflow
+ int newYear = chrono.checkValidYear(Math.floorDiv(calcMonths, 12L));
+ int newMonth = (int)Math.floorMod(calcMonths, 12L) + 1;
+ return resolvePreviousValid(newYear, newMonth, dayOfMonth);
+ }
+
+ @Override
+ HijrahDate plusWeeks(long weeksToAdd) {
+ return super.plusWeeks(weeksToAdd);
+ }
+
+ @Override
+ HijrahDate plusDays(long days) {
+ return new HijrahDate(chrono, toEpochDay() + days);
+ }
+
+ @Override
+ public HijrahDate plus(long amountToAdd, TemporalUnit unit) {
+ return super.plus(amountToAdd, unit);
+ }
+
+ @Override
+ public HijrahDate minus(long amountToSubtract, TemporalUnit unit) {
+ return super.minus(amountToSubtract, unit);
+ }
+
+ @Override
+ HijrahDate minusYears(long yearsToSubtract) {
+ return super.minusYears(yearsToSubtract);
+ }
+
+ @Override
+ HijrahDate minusMonths(long monthsToSubtract) {
+ return super.minusMonths(monthsToSubtract);
+ }
+
+ @Override
+ HijrahDate minusWeeks(long weeksToSubtract) {
+ return super.minusWeeks(weeksToSubtract);
+ }
+
+ @Override
+ HijrahDate minusDays(long daysToSubtract) {
+ return super.minusDays(daysToSubtract);
+ }
+
+ @Override // for javadoc and covariant return type
+ @SuppressWarnings("unchecked")
+ public final ChronoLocalDateTime<HijrahDate> atTime(LocalTime localTime) {
+ return (ChronoLocalDateTime<HijrahDate>)super.atTime(localTime);
+ }
+
+ @Override
+ public ChronoPeriod until(ChronoLocalDate endDate) {
+ // TODO: untested
+ HijrahDate end = getChronology().date(endDate);
+ long totalMonths = (end.prolepticYear - this.prolepticYear) * 12 + (end.monthOfYear - this.monthOfYear); // safe
+ int days = end.dayOfMonth - this.dayOfMonth;
+ if (totalMonths > 0 && days < 0) {
+ totalMonths--;
+ HijrahDate calcDate = this.plusMonths(totalMonths);
+ days = (int) (end.toEpochDay() - calcDate.toEpochDay()); // safe
+ } else if (totalMonths < 0 && days > 0) {
+ totalMonths++;
+ days -= end.lengthOfMonth();
+ }
+ long years = totalMonths / 12; // safe
+ int months = (int) (totalMonths % 12); // safe
+ return getChronology().period(Math.toIntExact(years), months, days);
+ }
+
+ //-------------------------------------------------------------------------
+ /**
+ * Compares this date to another date, including the chronology.
+ * <p>
+ * Compares this {@code HijrahDate} with another ensuring that the date is the same.
+ * <p>
+ * Only objects of type {@code HijrahDate} are compared, other types return false.
+ * To compare the dates of two {@code TemporalAccessor} instances, including dates
+ * in two different chronologies, use {@link ChronoField#EPOCH_DAY} as a comparator.
+ *
+ * @param obj the object to check, null returns false
+ * @return true if this is equal to the other date and the Chronologies are equal
+ */
+ @Override // override for performance
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof HijrahDate) {
+ HijrahDate otherDate = (HijrahDate) obj;
+ return prolepticYear == otherDate.prolepticYear
+ && this.monthOfYear == otherDate.monthOfYear
+ && this.dayOfMonth == otherDate.dayOfMonth
+ && getChronology().equals(otherDate.getChronology());
+ }
+ return false;
+ }
+
+ /**
+ * A hash code for this date.
+ *
+ * @return a suitable hash code based only on the Chronology and the date
+ */
+ @Override // override for performance
+ public int hashCode() {
+ int yearValue = prolepticYear;
+ int monthValue = monthOfYear;
+ int dayValue = dayOfMonth;
+ return getChronology().getId().hashCode() ^ (yearValue & 0xFFFFF800)
+ ^ ((yearValue << 11) + (monthValue << 6) + (dayValue));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Defend against malicious streams.
+ *
+ * @param s the stream to read
+ * @throws InvalidObjectException always
+ */
+ private void readObject(ObjectInputStream s) throws InvalidObjectException {
+ throw new InvalidObjectException("Deserialization via serialization delegate");
+ }
+
+ /**
+ * Writes the object using a
+ * <a href="../../../serialized-form.html#java.time.chrono.Ser">dedicated serialized form</a>.
+ * @serialData
+ * <pre>
+ * out.writeByte(6); // identifies a HijrahDate
+ * out.writeObject(chrono); // the HijrahChronology variant
+ * out.writeInt(get(YEAR));
+ * out.writeByte(get(MONTH_OF_YEAR));
+ * out.writeByte(get(DAY_OF_MONTH));
+ * </pre>
+ *
+ * @return the instance of {@code Ser}, not null
+ */
+ private Object writeReplace() {
+ return new Ser(Ser.HIJRAH_DATE_TYPE, this);
+ }
+
+ void writeExternal(ObjectOutput out) throws IOException {
+ // HijrahChronology is implicit in the Hijrah_DATE_TYPE
+ out.writeObject(getChronology());
+ out.writeInt(get(YEAR));
+ out.writeByte(get(MONTH_OF_YEAR));
+ out.writeByte(get(DAY_OF_MONTH));
+ }
+
+ static HijrahDate readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+ HijrahChronology chrono = (HijrahChronology) in.readObject();
+ int year = in.readInt();
+ int month = in.readByte();
+ int dayOfMonth = in.readByte();
+ return chrono.date(year, month, dayOfMonth);
+ }
+
+}
diff --git a/java/time/chrono/HijrahEra.java b/java/time/chrono/HijrahEra.java
new file mode 100644
index 0000000..4390f69
--- /dev/null
+++ b/java/time/chrono/HijrahEra.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2012, 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) 2012, 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.ERA;
+
+import java.time.DateTimeException;
+import java.time.temporal.ChronoField;
+import java.time.temporal.TemporalField;
+import java.time.temporal.UnsupportedTemporalTypeException;
+import java.time.temporal.ValueRange;
+
+/**
+ * An era in the Hijrah calendar system.
+ * <p>
+ * The Hijrah calendar system has only one era covering the
+ * proleptic years greater than zero.
+ * <p>
+ * <b>Do not use {@code ordinal()} to obtain the numeric representation of {@code HijrahEra}.
+ * Use {@code getValue()} instead.</b>
+ *
+ * @implSpec
+ * This is an immutable and thread-safe enum.
+ *
+ * @since 1.8
+ */
+public enum HijrahEra implements Era {
+
+ /**
+ * The singleton instance for the current era, 'Anno Hegirae',
+ * which has the numeric value 1.
+ */
+ AH;
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code HijrahEra} from an {@code int} value.
+ * <p>
+ * The current era, which is the only accepted value, has the value 1
+ *
+ * @param hijrahEra the era to represent, only 1 supported
+ * @return the HijrahEra.AH singleton, not null
+ * @throws DateTimeException if the value is invalid
+ */
+ public static HijrahEra of(int hijrahEra) {
+ if (hijrahEra == 1 ) {
+ return AH;
+ } else {
+ throw new DateTimeException("Invalid era: " + hijrahEra);
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the numeric era {@code int} value.
+ * <p>
+ * The era AH has the value 1.
+ *
+ * @return the era value, 1 (AH)
+ */
+ @Override
+ public int getValue() {
+ return 1;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the range of valid values for the specified field.
+ * <p>
+ * The range object expresses the minimum and maximum valid values for a field.
+ * This era is used to enhance the accuracy of the returned range.
+ * If it is not possible to return the range, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The {@code ERA} field returns the range.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.rangeRefinedBy(TemporalAccessor)}
+ * passing {@code this} as the argument.
+ * Whether the range can be obtained is determined by the field.
+ * <p>
+ * The {@code ERA} field returns a range for the one valid Hijrah era.
+ *
+ * @param field the field to query the range for, not null
+ * @return the range of valid values for the field, not null
+ * @throws DateTimeException if the range for the field cannot be obtained
+ * @throws UnsupportedTemporalTypeException if the unit is not supported
+ */
+ @Override // override as super would return range from 0 to 1
+ public ValueRange range(TemporalField field) {
+ if (field == ERA) {
+ return ValueRange.of(1, 1);
+ }
+ return Era.super.range(field);
+ }
+
+}
diff --git a/java/time/chrono/IsoChronology.java b/java/time/chrono/IsoChronology.java
new file mode 100644
index 0000000..a9c79ff
--- /dev/null
+++ b/java/time/chrono/IsoChronology.java
@@ -0,0 +1,613 @@
+/*
+ * Copyright (c) 2012, 2015, 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) 2012, 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.io.InvalidObjectException;
+import static java.time.temporal.ChronoField.DAY_OF_MONTH;
+import static java.time.temporal.ChronoField.ERA;
+import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
+import static java.time.temporal.ChronoField.PROLEPTIC_MONTH;
+import static java.time.temporal.ChronoField.YEAR;
+import static java.time.temporal.ChronoField.YEAR_OF_ERA;
+
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.time.Clock;
+import java.time.DateTimeException;
+import java.time.Instant;
+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;
+import java.time.format.ResolverStyle;
+import java.time.temporal.ChronoField;
+import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalField;
+import java.time.temporal.ValueRange;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * The ISO calendar system.
+ * <p>
+ * This chronology defines the rules of the ISO calendar system.
+ * This calendar system is based on the ISO-8601 standard, which is the
+ * <i>de facto</i> world calendar.
+ * <p>
+ * The fields are defined as follows:
+ * <ul>
+ * <li>era - There are two eras, 'Current Era' (CE) and 'Before Current Era' (BCE).
+ * <li>year-of-era - The year-of-era is the same as the proleptic-year for the current CE era.
+ * For the BCE era before the ISO epoch the year increases from 1 upwards as time goes backwards.
+ * <li>proleptic-year - The proleptic year is the same as the year-of-era for the
+ * current era. For the previous era, years have zero, then negative values.
+ * <li>month-of-year - There are 12 months in an ISO year, numbered from 1 to 12.
+ * <li>day-of-month - There are between 28 and 31 days in each of the ISO month, numbered from 1 to 31.
+ * Months 4, 6, 9 and 11 have 30 days, Months 1, 3, 5, 7, 8, 10 and 12 have 31 days.
+ * Month 2 has 28 days, or 29 in a leap year.
+ * <li>day-of-year - There are 365 days in a standard ISO year and 366 in a leap year.
+ * The days are numbered from 1 to 365 or 1 to 366.
+ * <li>leap-year - Leap years occur every 4 years, except where the year is divisble by 100 and not divisble by 400.
+ * </ul>
+ *
+ * @implSpec
+ * This class is immutable and thread-safe.
+ *
+ * @since 1.8
+ */
+public final class IsoChronology extends AbstractChronology implements Serializable {
+
+ /**
+ * Singleton instance of the ISO chronology.
+ */
+ public static final IsoChronology INSTANCE = new IsoChronology();
+
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = -1440403870442975015L;
+
+ /**
+ * Restricted constructor.
+ */
+ private IsoChronology() {
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the ID of the chronology - 'ISO'.
+ * <p>
+ * The ID uniquely identifies the {@code Chronology}.
+ * It can be used to lookup the {@code Chronology} using {@link Chronology#of(String)}.
+ *
+ * @return the chronology ID - 'ISO'
+ * @see #getCalendarType()
+ */
+ @Override
+ public String getId() {
+ return "ISO";
+ }
+
+ /**
+ * Gets the calendar type of the underlying calendar system - 'iso8601'.
+ * <p>
+ * The calendar type is an identifier defined by the
+ * <em>Unicode Locale Data Markup Language (LDML)</em> specification.
+ * It can be used to lookup the {@code Chronology} using {@link Chronology#of(String)}.
+ * It can also be used as part of a locale, accessible via
+ * {@link Locale#getUnicodeLocaleType(String)} with the key 'ca'.
+ *
+ * @return the calendar system type - 'iso8601'
+ * @see #getId()
+ */
+ @Override
+ public String getCalendarType() {
+ return "iso8601";
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an ISO local date from the era, year-of-era, month-of-year
+ * and day-of-month fields.
+ *
+ * @param era the ISO era, not null
+ * @param yearOfEra the ISO year-of-era
+ * @param month the ISO month-of-year
+ * @param dayOfMonth the ISO day-of-month
+ * @return the ISO local date, not null
+ * @throws DateTimeException if unable to create the date
+ * @throws ClassCastException if the type of {@code era} is not {@code IsoEra}
+ */
+ @Override // override with covariant return type
+ public LocalDate date(Era era, int yearOfEra, int month, int dayOfMonth) {
+ return date(prolepticYear(era, yearOfEra), month, dayOfMonth);
+ }
+
+ /**
+ * Obtains an ISO local date from the proleptic-year, month-of-year
+ * and day-of-month fields.
+ * <p>
+ * This is equivalent to {@link LocalDate#of(int, int, int)}.
+ *
+ * @param prolepticYear the ISO proleptic-year
+ * @param month the ISO month-of-year
+ * @param dayOfMonth the ISO day-of-month
+ * @return the ISO local date, not null
+ * @throws DateTimeException if unable to create the date
+ */
+ @Override // override with covariant return type
+ public LocalDate date(int prolepticYear, int month, int dayOfMonth) {
+ return LocalDate.of(prolepticYear, month, dayOfMonth);
+ }
+
+ /**
+ * Obtains an ISO local date from the era, year-of-era and day-of-year fields.
+ *
+ * @param era the ISO era, not null
+ * @param yearOfEra the ISO year-of-era
+ * @param dayOfYear the ISO day-of-year
+ * @return the ISO local date, not null
+ * @throws DateTimeException if unable to create the date
+ */
+ @Override // override with covariant return type
+ public LocalDate dateYearDay(Era era, int yearOfEra, int dayOfYear) {
+ return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear);
+ }
+
+ /**
+ * Obtains an ISO local date from the proleptic-year and day-of-year fields.
+ * <p>
+ * This is equivalent to {@link LocalDate#ofYearDay(int, int)}.
+ *
+ * @param prolepticYear the ISO proleptic-year
+ * @param dayOfYear the ISO day-of-year
+ * @return the ISO local date, not null
+ * @throws DateTimeException if unable to create the date
+ */
+ @Override // override with covariant return type
+ public LocalDate dateYearDay(int prolepticYear, int dayOfYear) {
+ return LocalDate.ofYearDay(prolepticYear, dayOfYear);
+ }
+
+ /**
+ * Obtains an ISO local date from the epoch-day.
+ * <p>
+ * This is equivalent to {@link LocalDate#ofEpochDay(long)}.
+ *
+ * @param epochDay the epoch day
+ * @return the ISO local date, not null
+ * @throws DateTimeException if unable to create the date
+ */
+ @Override // override with covariant return type
+ public LocalDate dateEpochDay(long epochDay) {
+ return LocalDate.ofEpochDay(epochDay);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an ISO local date from another date-time object.
+ * <p>
+ * This is equivalent to {@link LocalDate#from(TemporalAccessor)}.
+ *
+ * @param temporal the date-time object to convert, not null
+ * @return the ISO local date, not null
+ * @throws DateTimeException if unable to create the date
+ */
+ @Override // override with covariant return type
+ public LocalDate date(TemporalAccessor temporal) {
+ return LocalDate.from(temporal);
+ }
+
+ /**
+ * Obtains an ISO local date-time from another date-time object.
+ * <p>
+ * This is equivalent to {@link LocalDateTime#from(TemporalAccessor)}.
+ *
+ * @param temporal the date-time object to convert, not null
+ * @return the ISO local date-time, not null
+ * @throws DateTimeException if unable to create the date-time
+ */
+ @Override // override with covariant return type
+ public LocalDateTime localDateTime(TemporalAccessor temporal) {
+ return LocalDateTime.from(temporal);
+ }
+
+ /**
+ * Obtains an ISO zoned date-time from another date-time object.
+ * <p>
+ * This is equivalent to {@link ZonedDateTime#from(TemporalAccessor)}.
+ *
+ * @param temporal the date-time object to convert, not null
+ * @return the ISO zoned date-time, not null
+ * @throws DateTimeException if unable to create the date-time
+ */
+ @Override // override with covariant return type
+ public ZonedDateTime zonedDateTime(TemporalAccessor temporal) {
+ return ZonedDateTime.from(temporal);
+ }
+
+ /**
+ * Obtains an ISO zoned date-time in this chronology from an {@code Instant}.
+ * <p>
+ * This is equivalent to {@link ZonedDateTime#ofInstant(Instant, ZoneId)}.
+ *
+ * @param instant the instant to create the date-time from, not null
+ * @param zone the time-zone, not null
+ * @return the zoned date-time, not null
+ * @throws DateTimeException if the result exceeds the supported range
+ */
+ @Override
+ public ZonedDateTime zonedDateTime(Instant instant, ZoneId zone) {
+ return ZonedDateTime.ofInstant(instant, zone);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains the current ISO local date from the system clock in the default time-zone.
+ * <p>
+ * This will query the {@link Clock#systemDefaultZone() system clock} in the default
+ * time-zone to obtain the current date.
+ * <p>
+ * Using this method will prevent the ability to use an alternate clock for testing
+ * because the clock is hard-coded.
+ *
+ * @return the current ISO local date using the system clock and default time-zone, not null
+ * @throws DateTimeException if unable to create the date
+ */
+ @Override // override with covariant return type
+ public LocalDate dateNow() {
+ return dateNow(Clock.systemDefaultZone());
+ }
+
+ /**
+ * Obtains the current ISO local date from the system clock in the specified time-zone.
+ * <p>
+ * This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date.
+ * Specifying the time-zone avoids dependence on the default time-zone.
+ * <p>
+ * Using this method will prevent the ability to use an alternate clock for testing
+ * because the clock is hard-coded.
+ *
+ * @return the current ISO local date using the system clock, not null
+ * @throws DateTimeException if unable to create the date
+ */
+ @Override // override with covariant return type
+ public LocalDate dateNow(ZoneId zone) {
+ return dateNow(Clock.system(zone));
+ }
+
+ /**
+ * Obtains the current ISO local date from the specified clock.
+ * <p>
+ * This will query the specified clock to obtain the current date - today.
+ * Using this method allows the use of an alternate clock for testing.
+ * The alternate clock may be introduced using {@link Clock dependency injection}.
+ *
+ * @param clock the clock to use, not null
+ * @return the current ISO local date, not null
+ * @throws DateTimeException if unable to create the date
+ */
+ @Override // override with covariant return type
+ public LocalDate dateNow(Clock clock) {
+ Objects.requireNonNull(clock, "clock");
+ return date(LocalDate.now(clock));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if the year is a leap year, according to the ISO proleptic
+ * calendar system rules.
+ * <p>
+ * This method applies the current rules for leap years across the whole time-line.
+ * In general, a year is a leap year if it is divisible by four without
+ * remainder. However, years divisible by 100, are not leap years, with
+ * the exception of years divisible by 400 which are.
+ * <p>
+ * For example, 1904 is a leap year it is divisible by 4.
+ * 1900 was not a leap year as it is divisible by 100, however 2000 was a
+ * leap year as it is divisible by 400.
+ * <p>
+ * The calculation is proleptic - applying the same rules into the far future and far past.
+ * This is historically inaccurate, but is correct for the ISO-8601 standard.
+ *
+ * @param prolepticYear the ISO proleptic year to check
+ * @return true if the year is leap, false otherwise
+ */
+ @Override
+ public boolean isLeapYear(long prolepticYear) {
+ return ((prolepticYear & 3) == 0) && ((prolepticYear % 100) != 0 || (prolepticYear % 400) == 0);
+ }
+
+ @Override
+ public int prolepticYear(Era era, int yearOfEra) {
+ if (era instanceof IsoEra == false) {
+ throw new ClassCastException("Era must be IsoEra");
+ }
+ return (era == IsoEra.CE ? yearOfEra : 1 - yearOfEra);
+ }
+
+ @Override
+ public IsoEra eraOf(int eraValue) {
+ return IsoEra.of(eraValue);
+ }
+
+ @Override
+ public List<Era> eras() {
+ return Arrays.<Era>asList(IsoEra.values());
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Resolves parsed {@code ChronoField} values into a date during parsing.
+ * <p>
+ * Most {@code TemporalField} implementations are resolved using the
+ * resolve method on the field. By contrast, the {@code ChronoField} class
+ * defines fields that only have meaning relative to the chronology.
+ * As such, {@code ChronoField} date fields are resolved here in the
+ * context of a specific chronology.
+ * <p>
+ * {@code ChronoField} instances on the ISO calendar system are resolved
+ * as follows.
+ * <ul>
+ * <li>{@code EPOCH_DAY} - If present, this is converted to a {@code LocalDate}
+ * and all other date fields are then cross-checked against the date.
+ * <li>{@code PROLEPTIC_MONTH} - If present, then it is split into the
+ * {@code YEAR} and {@code MONTH_OF_YEAR}. If the mode is strict or smart
+ * then the field is validated.
+ * <li>{@code YEAR_OF_ERA} and {@code ERA} - If both are present, then they
+ * are combined to form a {@code YEAR}. In lenient mode, the {@code YEAR_OF_ERA}
+ * range is not validated, in smart and strict mode it is. The {@code ERA} is
+ * validated for range in all three modes. If only the {@code YEAR_OF_ERA} is
+ * present, and the mode is smart or lenient, then the current era (CE/AD)
+ * is assumed. In strict mode, no era is assumed and the {@code YEAR_OF_ERA} is
+ * left untouched. If only the {@code ERA} is present, then it is left untouched.
+ * <li>{@code YEAR}, {@code MONTH_OF_YEAR} and {@code DAY_OF_MONTH} -
+ * If all three are present, then they are combined to form a {@code LocalDate}.
+ * In all three modes, the {@code YEAR} is validated. If the mode is smart or strict,
+ * then the month and day are validated, with the day validated from 1 to 31.
+ * If the mode is lenient, then the date is combined in a manner equivalent to
+ * creating a date on the first of January in the requested year, then adding
+ * the difference in months, then the difference in days.
+ * If the mode is smart, and the day-of-month is greater than the maximum for
+ * the year-month, then the day-of-month is adjusted to the last day-of-month.
+ * If the mode is strict, then the three fields must form a valid date.
+ * <li>{@code YEAR} and {@code DAY_OF_YEAR} -
+ * If both are present, then they are combined to form a {@code LocalDate}.
+ * In all three modes, the {@code YEAR} is validated.
+ * If the mode is lenient, then the date is combined in a manner equivalent to
+ * creating a date on the first of January in the requested year, then adding
+ * the difference in days.
+ * If the mode is smart or strict, then the two fields must form a valid date.
+ * <li>{@code YEAR}, {@code MONTH_OF_YEAR}, {@code ALIGNED_WEEK_OF_MONTH} and
+ * {@code ALIGNED_DAY_OF_WEEK_IN_MONTH} -
+ * If all four are present, then they are combined to form a {@code LocalDate}.
+ * In all three modes, the {@code YEAR} is validated.
+ * If the mode is lenient, then the date is combined in a manner equivalent to
+ * creating a date on the first of January in the requested year, then adding
+ * the difference in months, then the difference in weeks, then in days.
+ * If the mode is smart or strict, then the all four fields are validated to
+ * their outer ranges. The date is then combined in a manner equivalent to
+ * creating a date on the first day of the requested year and month, then adding
+ * the amount in weeks and days to reach their values. If the mode is strict,
+ * the date is additionally validated to check that the day and week adjustment
+ * did not change the month.
+ * <li>{@code YEAR}, {@code MONTH_OF_YEAR}, {@code ALIGNED_WEEK_OF_MONTH} and
+ * {@code DAY_OF_WEEK} - If all four are present, then they are combined to
+ * form a {@code LocalDate}. The approach is the same as described above for
+ * years, months and weeks in {@code ALIGNED_DAY_OF_WEEK_IN_MONTH}.
+ * The day-of-week is adjusted as the next or same matching day-of-week once
+ * the years, months and weeks have been handled.
+ * <li>{@code YEAR}, {@code ALIGNED_WEEK_OF_YEAR} and {@code ALIGNED_DAY_OF_WEEK_IN_YEAR} -
+ * If all three are present, then they are combined to form a {@code LocalDate}.
+ * In all three modes, the {@code YEAR} is validated.
+ * If the mode is lenient, then the date is combined in a manner equivalent to
+ * creating a date on the first of January in the requested year, then adding
+ * the difference in weeks, then in days.
+ * If the mode is smart or strict, then the all three fields are validated to
+ * their outer ranges. The date is then combined in a manner equivalent to
+ * creating a date on the first day of the requested year, then adding
+ * the amount in weeks and days to reach their values. If the mode is strict,
+ * the date is additionally validated to check that the day and week adjustment
+ * did not change the year.
+ * <li>{@code YEAR}, {@code ALIGNED_WEEK_OF_YEAR} and {@code DAY_OF_WEEK} -
+ * If all three are present, then they are combined to form a {@code LocalDate}.
+ * The approach is the same as described above for years and weeks in
+ * {@code ALIGNED_DAY_OF_WEEK_IN_YEAR}. The day-of-week is adjusted as the
+ * next or same matching day-of-week once the years and weeks have been handled.
+ * </ul>
+ *
+ * @param fieldValues the map of fields to values, which can be updated, not null
+ * @param resolverStyle the requested type of resolve, not null
+ * @return the resolved date, null if insufficient information to create a date
+ * @throws DateTimeException if the date cannot be resolved, typically
+ * because of a conflict in the input data
+ */
+ @Override // override for performance
+ public LocalDate resolveDate(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
+ return (LocalDate) super.resolveDate(fieldValues, resolverStyle);
+ }
+
+ @Override // override for better proleptic algorithm
+ void resolveProlepticMonth(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
+ Long pMonth = fieldValues.remove(PROLEPTIC_MONTH);
+ if (pMonth != null) {
+ if (resolverStyle != ResolverStyle.LENIENT) {
+ PROLEPTIC_MONTH.checkValidValue(pMonth);
+ }
+ addFieldValue(fieldValues, MONTH_OF_YEAR, Math.floorMod(pMonth, 12) + 1);
+ addFieldValue(fieldValues, YEAR, Math.floorDiv(pMonth, 12));
+ }
+ }
+
+ @Override // override for enhanced behaviour
+ LocalDate resolveYearOfEra(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
+ Long yoeLong = fieldValues.remove(YEAR_OF_ERA);
+ if (yoeLong != null) {
+ if (resolverStyle != ResolverStyle.LENIENT) {
+ YEAR_OF_ERA.checkValidValue(yoeLong);
+ }
+ Long era = fieldValues.remove(ERA);
+ if (era == null) {
+ Long year = fieldValues.get(YEAR);
+ if (resolverStyle == ResolverStyle.STRICT) {
+ // do not invent era if strict, but do cross-check with year
+ if (year != null) {
+ addFieldValue(fieldValues, YEAR, (year > 0 ? yoeLong: Math.subtractExact(1, yoeLong)));
+ } else {
+ // reinstate the field removed earlier, no cross-check issues
+ fieldValues.put(YEAR_OF_ERA, yoeLong);
+ }
+ } else {
+ // invent era
+ addFieldValue(fieldValues, YEAR, (year == null || year > 0 ? yoeLong: Math.subtractExact(1, yoeLong)));
+ }
+ } else if (era.longValue() == 1L) {
+ addFieldValue(fieldValues, YEAR, yoeLong);
+ } else if (era.longValue() == 0L) {
+ addFieldValue(fieldValues, YEAR, Math.subtractExact(1, yoeLong));
+ } else {
+ throw new DateTimeException("Invalid value for era: " + era);
+ }
+ } else if (fieldValues.containsKey(ERA)) {
+ ERA.checkValidValue(fieldValues.get(ERA)); // always validated
+ }
+ return null;
+ }
+
+ @Override // override for performance
+ LocalDate resolveYMD(Map <TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
+ int y = YEAR.checkValidIntValue(fieldValues.remove(YEAR));
+ if (resolverStyle == ResolverStyle.LENIENT) {
+ long months = Math.subtractExact(fieldValues.remove(MONTH_OF_YEAR), 1);
+ long days = Math.subtractExact(fieldValues.remove(DAY_OF_MONTH), 1);
+ return LocalDate.of(y, 1, 1).plusMonths(months).plusDays(days);
+ }
+ int moy = MONTH_OF_YEAR.checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR));
+ int dom = DAY_OF_MONTH.checkValidIntValue(fieldValues.remove(DAY_OF_MONTH));
+ if (resolverStyle == ResolverStyle.SMART) { // previous valid
+ if (moy == 4 || moy == 6 || moy == 9 || moy == 11) {
+ dom = Math.min(dom, 30);
+ } else if (moy == 2) {
+ dom = Math.min(dom, Month.FEBRUARY.length(Year.isLeap(y)));
+
+ }
+ }
+ return LocalDate.of(y, moy, dom);
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ public ValueRange range(ChronoField field) {
+ 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);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Writes the Chronology using a
+ * <a href="../../../serialized-form.html#java.time.chrono.Ser">dedicated serialized form</a>.
+ * @serialData
+ * <pre>
+ * out.writeByte(1); // identifies a Chronology
+ * out.writeUTF(getId());
+ * </pre>
+ *
+ * @return the instance of {@code Ser}, not null
+ */
+ @Override
+ Object writeReplace() {
+ return super.writeReplace();
+ }
+
+ /**
+ * Defend against malicious streams.
+ *
+ * @param s the stream to read
+ * @throws InvalidObjectException always
+ */
+ private void readObject(ObjectInputStream s) throws InvalidObjectException {
+ throw new InvalidObjectException("Deserialization via serialization delegate");
+ }
+}
diff --git a/java/time/chrono/IsoEra.java b/java/time/chrono/IsoEra.java
new file mode 100644
index 0000000..bd29969
--- /dev/null
+++ b/java/time/chrono/IsoEra.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2012, 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) 2012, 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;
+
+/**
+ * An era in the ISO calendar system.
+ * <p>
+ * The ISO-8601 standard does not define eras.
+ * A definition has therefore been created with two eras - 'Current era' (CE) for
+ * years on or after 0001-01-01 (ISO), and 'Before current era' (BCE) for years before that.
+ *
+ * <table summary="ISO years and eras" cellpadding="2" cellspacing="3" border="0" >
+ * <thead>
+ * <tr class="tableSubHeadingColor">
+ * <th class="colFirst" align="left">year-of-era</th>
+ * <th class="colFirst" align="left">era</th>
+ * <th class="colLast" align="left">proleptic-year</th>
+ * </tr>
+ * </thead>
+ * <tbody>
+ * <tr class="rowColor">
+ * <td>2</td><td>CE</td><td>2</td>
+ * </tr>
+ * <tr class="altColor">
+ * <td>1</td><td>CE</td><td>1</td>
+ * </tr>
+ * <tr class="rowColor">
+ * <td>1</td><td>BCE</td><td>0</td>
+ * </tr>
+ * <tr class="altColor">
+ * <td>2</td><td>BCE</td><td>-1</td>
+ * </tr>
+ * </tbody>
+ * </table>
+ * <p>
+ * <b>Do not use {@code ordinal()} to obtain the numeric representation of {@code IsoEra}.
+ * Use {@code getValue()} instead.</b>
+ *
+ * @implSpec
+ * This is an immutable and thread-safe enum.
+ *
+ * @since 1.8
+ */
+public enum IsoEra implements Era {
+
+ /**
+ * The singleton instance for the era before the current one, 'Before Current Era',
+ * which has the numeric value 0.
+ */
+ BCE,
+ /**
+ * The singleton instance for the current era, 'Current Era',
+ * which has the numeric value 1.
+ */
+ CE;
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code IsoEra} from an {@code int} value.
+ * <p>
+ * {@code IsoEra} is an enum representing the ISO eras of BCE/CE.
+ * This factory allows the enum to be obtained from the {@code int} value.
+ *
+ * @param isoEra the BCE/CE value to represent, from 0 (BCE) to 1 (CE)
+ * @return the era singleton, not null
+ * @throws DateTimeException if the value is invalid
+ */
+ public static IsoEra of(int isoEra) {
+ switch (isoEra) {
+ case 0:
+ return BCE;
+ case 1:
+ return CE;
+ default:
+ throw new DateTimeException("Invalid era: " + isoEra);
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the numeric era {@code int} value.
+ * <p>
+ * The era BCE has the value 0, while the era CE has the value 1.
+ *
+ * @return the era value, from 0 (BCE) to 1 (CE)
+ */
+ @Override
+ public int getValue() {
+ return ordinal();
+ }
+
+}
diff --git a/java/time/chrono/JapaneseChronology.java b/java/time/chrono/JapaneseChronology.java
new file mode 100644
index 0000000..034a3bc
--- /dev/null
+++ b/java/time/chrono/JapaneseChronology.java
@@ -0,0 +1,543 @@
+/*
+ * Copyright (c) 2012, 2015, 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) 2012, 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.DAY_OF_MONTH;
+import static java.time.temporal.ChronoField.DAY_OF_YEAR;
+import static java.time.temporal.ChronoField.ERA;
+import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
+import static java.time.temporal.ChronoField.YEAR;
+import static java.time.temporal.ChronoField.YEAR_OF_ERA;
+import static java.time.temporal.ChronoUnit.DAYS;
+import static java.time.temporal.ChronoUnit.MONTHS;
+
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.time.Clock;
+import java.time.DateTimeException;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.Year;
+import java.time.ZoneId;
+import java.time.format.ResolverStyle;
+import java.time.temporal.ChronoField;
+import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalAdjusters;
+import java.time.temporal.TemporalField;
+import java.time.temporal.UnsupportedTemporalTypeException;
+import java.time.temporal.ValueRange;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+
+import sun.util.calendar.CalendarSystem;
+import sun.util.calendar.LocalGregorianCalendar;
+
+/**
+ * The Japanese Imperial calendar system.
+ * <p>
+ * This chronology defines the rules of the Japanese Imperial calendar system.
+ * This calendar system is primarily used in Japan.
+ * The Japanese Imperial calendar system is the same as the ISO calendar system
+ * apart from the era-based year numbering.
+ * <p>
+ * Japan introduced the Gregorian calendar starting with Meiji 6.
+ * Only Meiji and later eras are supported;
+ * dates before Meiji 6, January 1 are not supported.
+ * <p>
+ * The supported {@code ChronoField} instances are:
+ * <ul>
+ * <li>{@code DAY_OF_WEEK}
+ * <li>{@code DAY_OF_MONTH}
+ * <li>{@code DAY_OF_YEAR}
+ * <li>{@code EPOCH_DAY}
+ * <li>{@code MONTH_OF_YEAR}
+ * <li>{@code PROLEPTIC_MONTH}
+ * <li>{@code YEAR_OF_ERA}
+ * <li>{@code YEAR}
+ * <li>{@code ERA}
+ * </ul>
+ *
+ * @implSpec
+ * This class is immutable and thread-safe.
+ *
+ * @since 1.8
+ */
+public final class JapaneseChronology extends AbstractChronology implements Serializable {
+
+ static final LocalGregorianCalendar JCAL =
+ (LocalGregorianCalendar) CalendarSystem.forName("japanese");
+
+ // Android-changed: don't use locale to create japanese imperial calendar, as it's not generally
+ // supported on Android. Use Calendar.getJapaneseImperialInstance() instead. See .createCalendar
+ // Locale for creating a JapaneseImpericalCalendar.
+ private static final Locale LOCALE = Locale.forLanguageTag("ja-JP-u-ca-japanese");
+
+ static Calendar createCalendar() {
+ return Calendar.getJapaneseImperialInstance(TimeZone.getDefault(), LOCALE);
+ }
+
+ /**
+ * Singleton instance for Japanese chronology.
+ */
+ public static final JapaneseChronology INSTANCE = new JapaneseChronology();
+
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = 459996390165777884L;
+
+ //-----------------------------------------------------------------------
+ /**
+ * Restricted constructor.
+ */
+ private JapaneseChronology() {
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the ID of the chronology - 'Japanese'.
+ * <p>
+ * The ID uniquely identifies the {@code Chronology}.
+ * It can be used to lookup the {@code Chronology} using {@link Chronology#of(String)}.
+ *
+ * @return the chronology ID - 'Japanese'
+ * @see #getCalendarType()
+ */
+ @Override
+ public String getId() {
+ return "Japanese";
+ }
+
+ /**
+ * Gets the calendar type of the underlying calendar system - 'japanese'.
+ * <p>
+ * The calendar type is an identifier defined by the
+ * <em>Unicode Locale Data Markup Language (LDML)</em> specification.
+ * It can be used to lookup the {@code Chronology} using {@link Chronology#of(String)}.
+ * It can also be used as part of a locale, accessible via
+ * {@link Locale#getUnicodeLocaleType(String)} with the key 'ca'.
+ *
+ * @return the calendar system type - 'japanese'
+ * @see #getId()
+ */
+ @Override
+ public String getCalendarType() {
+ return "japanese";
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains a local date in Japanese calendar system from the
+ * era, year-of-era, month-of-year and day-of-month fields.
+ * <p>
+ * The Japanese month and day-of-month are the same as those in the
+ * ISO calendar system. They are not reset when the era changes.
+ * For example:
+ * <pre>
+ * 6th Jan Showa 64 = ISO 1989-01-06
+ * 7th Jan Showa 64 = ISO 1989-01-07
+ * 8th Jan Heisei 1 = ISO 1989-01-08
+ * 9th Jan Heisei 1 = ISO 1989-01-09
+ * </pre>
+ *
+ * @param era the Japanese era, not null
+ * @param yearOfEra the year-of-era
+ * @param month the month-of-year
+ * @param dayOfMonth the day-of-month
+ * @return the Japanese local date, not null
+ * @throws DateTimeException if unable to create the date
+ * @throws ClassCastException if the {@code era} is not a {@code JapaneseEra}
+ */
+ @Override
+ public JapaneseDate date(Era era, int yearOfEra, int month, int dayOfMonth) {
+ if (era instanceof JapaneseEra == false) {
+ throw new ClassCastException("Era must be JapaneseEra");
+ }
+ return JapaneseDate.of((JapaneseEra) era, yearOfEra, month, dayOfMonth);
+ }
+
+ /**
+ * Obtains a local date in Japanese calendar system from the
+ * proleptic-year, month-of-year and day-of-month fields.
+ * <p>
+ * The Japanese proleptic year, month and day-of-month are the same as those
+ * in the ISO calendar system. They are not reset when the era changes.
+ *
+ * @param prolepticYear the proleptic-year
+ * @param month the month-of-year
+ * @param dayOfMonth the day-of-month
+ * @return the Japanese local date, not null
+ * @throws DateTimeException if unable to create the date
+ */
+ @Override
+ public JapaneseDate date(int prolepticYear, int month, int dayOfMonth) {
+ return new JapaneseDate(LocalDate.of(prolepticYear, month, dayOfMonth));
+ }
+
+ /**
+ * Obtains a local date in Japanese calendar system from the
+ * era, year-of-era and day-of-year fields.
+ * <p>
+ * The day-of-year in this factory is expressed relative to the start of the year-of-era.
+ * This definition changes the normal meaning of day-of-year only in those years
+ * where the year-of-era is reset to one due to a change in the era.
+ * For example:
+ * <pre>
+ * 6th Jan Showa 64 = day-of-year 6
+ * 7th Jan Showa 64 = day-of-year 7
+ * 8th Jan Heisei 1 = day-of-year 1
+ * 9th Jan Heisei 1 = day-of-year 2
+ * </pre>
+ *
+ * @param era the Japanese era, not null
+ * @param yearOfEra the year-of-era
+ * @param dayOfYear the day-of-year
+ * @return the Japanese local date, not null
+ * @throws DateTimeException if unable to create the date
+ * @throws ClassCastException if the {@code era} is not a {@code JapaneseEra}
+ */
+ @Override
+ public JapaneseDate dateYearDay(Era era, int yearOfEra, int dayOfYear) {
+ return JapaneseDate.ofYearDay((JapaneseEra) era, yearOfEra, dayOfYear);
+ }
+
+ /**
+ * Obtains a local date in Japanese calendar system from the
+ * proleptic-year and day-of-year fields.
+ * <p>
+ * The day-of-year in this factory is expressed relative to the start of the proleptic year.
+ * The Japanese proleptic year and day-of-year are the same as those in the ISO calendar system.
+ * They are not reset when the era changes.
+ *
+ * @param prolepticYear the proleptic-year
+ * @param dayOfYear the day-of-year
+ * @return the Japanese local date, not null
+ * @throws DateTimeException if unable to create the date
+ */
+ @Override
+ public JapaneseDate dateYearDay(int prolepticYear, int dayOfYear) {
+ return new JapaneseDate(LocalDate.ofYearDay(prolepticYear, dayOfYear));
+ }
+
+ /**
+ * Obtains a local date in the Japanese calendar system from the epoch-day.
+ *
+ * @param epochDay the epoch day
+ * @return the Japanese local date, not null
+ * @throws DateTimeException if unable to create the date
+ */
+ @Override // override with covariant return type
+ public JapaneseDate dateEpochDay(long epochDay) {
+ return new JapaneseDate(LocalDate.ofEpochDay(epochDay));
+ }
+
+ @Override
+ public JapaneseDate dateNow() {
+ return dateNow(Clock.systemDefaultZone());
+ }
+
+ @Override
+ public JapaneseDate dateNow(ZoneId zone) {
+ return dateNow(Clock.system(zone));
+ }
+
+ @Override
+ public JapaneseDate dateNow(Clock clock) {
+ return date(LocalDate.now(clock));
+ }
+
+ @Override
+ public JapaneseDate date(TemporalAccessor temporal) {
+ if (temporal instanceof JapaneseDate) {
+ return (JapaneseDate) temporal;
+ }
+ return new JapaneseDate(LocalDate.from(temporal));
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public ChronoLocalDateTime<JapaneseDate> localDateTime(TemporalAccessor temporal) {
+ return (ChronoLocalDateTime<JapaneseDate>)super.localDateTime(temporal);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public ChronoZonedDateTime<JapaneseDate> zonedDateTime(TemporalAccessor temporal) {
+ return (ChronoZonedDateTime<JapaneseDate>)super.zonedDateTime(temporal);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public ChronoZonedDateTime<JapaneseDate> zonedDateTime(Instant instant, ZoneId zone) {
+ return (ChronoZonedDateTime<JapaneseDate>)super.zonedDateTime(instant, zone);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if the specified year is a leap year.
+ * <p>
+ * Japanese calendar leap years occur exactly in line with ISO leap years.
+ * This method does not validate the year passed in, and only has a
+ * well-defined result for years in the supported range.
+ *
+ * @param prolepticYear the proleptic-year to check, not validated for range
+ * @return true if the year is a leap year
+ */
+ @Override
+ public boolean isLeapYear(long prolepticYear) {
+ return IsoChronology.INSTANCE.isLeapYear(prolepticYear);
+ }
+
+ @Override
+ public int prolepticYear(Era era, int yearOfEra) {
+ if (era instanceof JapaneseEra == false) {
+ throw new ClassCastException("Era must be JapaneseEra");
+ }
+
+ JapaneseEra jera = (JapaneseEra) era;
+ int gregorianYear = jera.getPrivateEra().getSinceDate().getYear() + yearOfEra - 1;
+ if (yearOfEra == 1) {
+ return gregorianYear;
+ }
+ if (gregorianYear >= Year.MIN_VALUE && gregorianYear <= Year.MAX_VALUE) {
+ LocalGregorianCalendar.Date jdate = JCAL.newCalendarDate(null);
+ jdate.setEra(jera.getPrivateEra()).setDate(yearOfEra, 1, 1);
+ if (JapaneseChronology.JCAL.validate(jdate)) {
+ return gregorianYear;
+ }
+ }
+ throw new DateTimeException("Invalid yearOfEra value");
+ }
+
+ // Android-changed: Integrate OpenJDK support for Japanese Era Reiwa.
+ /**
+ * Returns the calendar system era object from the given numeric value.
+ *
+ * The numeric values supported by this method are the same as the
+ * numeric values supported by {@link JapaneseEra#of(int)}.
+ *
+ * @param eraValue the era value
+ * @return the Japanese {@code Era} for the given numeric era value
+ * @throws DateTimeException if {@code eraValue} is invalid
+ */
+ @Override
+ public JapaneseEra eraOf(int eraValue) {
+ return JapaneseEra.of(eraValue);
+ }
+
+ @Override
+ public List<Era> eras() {
+ return Arrays.<Era>asList(JapaneseEra.values());
+ }
+
+ JapaneseEra getCurrentEra() {
+ // Assume that the last JapaneseEra is the current one.
+ JapaneseEra[] eras = JapaneseEra.values();
+ return eras[eras.length - 1];
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ public ValueRange range(ChronoField field) {
+ switch (field) {
+ case ALIGNED_DAY_OF_WEEK_IN_MONTH:
+ case ALIGNED_DAY_OF_WEEK_IN_YEAR:
+ case ALIGNED_WEEK_OF_MONTH:
+ case ALIGNED_WEEK_OF_YEAR:
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
+ case YEAR_OF_ERA: {
+ // Android-changed: use #createCalendar() to create calendar.
+ Calendar jcal = createCalendar();
+ int startYear = getCurrentEra().getPrivateEra().getSinceDate().getYear();
+ return ValueRange.of(1, jcal.getGreatestMinimum(Calendar.YEAR),
+ jcal.getLeastMaximum(Calendar.YEAR) + 1, // +1 due to the different definitions
+ Year.MAX_VALUE - startYear);
+ }
+ case DAY_OF_YEAR: {
+ // Android-changed: use #createCalendar() to create calendar.
+ Calendar jcal = createCalendar();
+ int fieldIndex = Calendar.DAY_OF_YEAR;
+ return ValueRange.of(jcal.getMinimum(fieldIndex), jcal.getGreatestMinimum(fieldIndex),
+ jcal.getLeastMaximum(fieldIndex), jcal.getMaximum(fieldIndex));
+ }
+ case YEAR:
+ return ValueRange.of(JapaneseDate.MEIJI_6_ISODATE.getYear(), Year.MAX_VALUE);
+ case ERA:
+ return ValueRange.of(JapaneseEra.MEIJI.getValue(), getCurrentEra().getValue());
+ default:
+ return field.range();
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ @Override // override for return type
+ public JapaneseDate resolveDate(Map <TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
+ return (JapaneseDate) super.resolveDate(fieldValues, resolverStyle);
+ }
+
+ @Override // override for special Japanese behavior
+ ChronoLocalDate resolveYearOfEra(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
+ // validate era and year-of-era
+ Long eraLong = fieldValues.get(ERA);
+ JapaneseEra era = null;
+ if (eraLong != null) {
+ era = eraOf(range(ERA).checkValidIntValue(eraLong, ERA)); // always validated
+ }
+ Long yoeLong = fieldValues.get(YEAR_OF_ERA);
+ int yoe = 0;
+ if (yoeLong != null) {
+ yoe = range(YEAR_OF_ERA).checkValidIntValue(yoeLong, YEAR_OF_ERA); // always validated
+ }
+ // if only year-of-era and no year then invent era unless strict
+ if (era == null && yoeLong != null && fieldValues.containsKey(YEAR) == false && resolverStyle != ResolverStyle.STRICT) {
+ era = JapaneseEra.values()[JapaneseEra.values().length - 1];
+ }
+ // if both present, then try to create date
+ if (yoeLong != null && era != null) {
+ if (fieldValues.containsKey(MONTH_OF_YEAR)) {
+ if (fieldValues.containsKey(DAY_OF_MONTH)) {
+ return resolveYMD(era, yoe, fieldValues, resolverStyle);
+ }
+ }
+ if (fieldValues.containsKey(DAY_OF_YEAR)) {
+ return resolveYD(era, yoe, fieldValues, resolverStyle);
+ }
+ }
+ return null;
+ }
+
+ private int prolepticYearLenient(JapaneseEra era, int yearOfEra) {
+ return era.getPrivateEra().getSinceDate().getYear() + yearOfEra - 1;
+ }
+
+ private ChronoLocalDate resolveYMD(JapaneseEra era, int yoe, Map<TemporalField,Long> fieldValues, ResolverStyle resolverStyle) {
+ fieldValues.remove(ERA);
+ fieldValues.remove(YEAR_OF_ERA);
+ if (resolverStyle == ResolverStyle.LENIENT) {
+ int y = prolepticYearLenient(era, yoe);
+ long months = Math.subtractExact(fieldValues.remove(MONTH_OF_YEAR), 1);
+ long days = Math.subtractExact(fieldValues.remove(DAY_OF_MONTH), 1);
+ return date(y, 1, 1).plus(months, MONTHS).plus(days, DAYS);
+ }
+ int moy = range(MONTH_OF_YEAR).checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR), MONTH_OF_YEAR);
+ int dom = range(DAY_OF_MONTH).checkValidIntValue(fieldValues.remove(DAY_OF_MONTH), DAY_OF_MONTH);
+ if (resolverStyle == ResolverStyle.SMART) { // previous valid
+ if (yoe < 1) {
+ throw new DateTimeException("Invalid YearOfEra: " + yoe);
+ }
+ int y = prolepticYearLenient(era, yoe);
+ JapaneseDate result;
+ try {
+ result = date(y, moy, dom);
+ } catch (DateTimeException ex) {
+ result = date(y, moy, 1).with(TemporalAdjusters.lastDayOfMonth());
+ }
+ // handle the era being changed
+ // only allow if the new date is in the same Jan-Dec as the era change
+ // determine by ensuring either original yoe or result yoe is 1
+ if (result.getEra() != era && result.get(YEAR_OF_ERA) > 1 && yoe > 1) {
+ throw new DateTimeException("Invalid YearOfEra for Era: " + era + " " + yoe);
+ }
+ return result;
+ }
+ return date(era, yoe, moy, dom);
+ }
+
+ private ChronoLocalDate resolveYD(JapaneseEra era, int yoe, Map <TemporalField,Long> fieldValues, ResolverStyle resolverStyle) {
+ fieldValues.remove(ERA);
+ fieldValues.remove(YEAR_OF_ERA);
+ if (resolverStyle == ResolverStyle.LENIENT) {
+ int y = prolepticYearLenient(era, yoe);
+ long days = Math.subtractExact(fieldValues.remove(DAY_OF_YEAR), 1);
+ return dateYearDay(y, 1).plus(days, DAYS);
+ }
+ int doy = range(DAY_OF_YEAR).checkValidIntValue(fieldValues.remove(DAY_OF_YEAR), DAY_OF_YEAR);
+ return dateYearDay(era, yoe, doy); // smart is same as strict
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Writes the Chronology using a
+ * <a href="../../../serialized-form.html#java.time.chrono.Ser">dedicated serialized form</a>.
+ * @serialData
+ * <pre>
+ * out.writeByte(1); // identifies a Chronology
+ * out.writeUTF(getId());
+ * </pre>
+ *
+ * @return the instance of {@code Ser}, not null
+ */
+ @Override
+ Object writeReplace() {
+ return super.writeReplace();
+ }
+
+ /**
+ * Defend against malicious streams.
+ *
+ * @param s the stream to read
+ * @throws InvalidObjectException always
+ */
+ private void readObject(ObjectInputStream s) throws InvalidObjectException {
+ throw new InvalidObjectException("Deserialization via serialization delegate");
+ }
+}
diff --git a/java/time/chrono/JapaneseDate.java b/java/time/chrono/JapaneseDate.java
new file mode 100644
index 0000000..143c4a4
--- /dev/null
+++ b/java/time/chrono/JapaneseDate.java
@@ -0,0 +1,758 @@
+/*
+ * Copyright (c) 2012, 2016, 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) 2012, 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.ALIGNED_DAY_OF_WEEK_IN_MONTH;
+import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR;
+import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH;
+import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR;
+import static java.time.temporal.ChronoField.DAY_OF_MONTH;
+import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
+import static java.time.temporal.ChronoField.YEAR;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.time.Clock;
+import java.time.DateTimeException;
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.Period;
+import java.time.ZoneId;
+import java.time.temporal.ChronoField;
+import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalAdjuster;
+import java.time.temporal.TemporalAmount;
+import java.time.temporal.TemporalField;
+import java.time.temporal.TemporalQuery;
+import java.time.temporal.TemporalUnit;
+import java.time.temporal.UnsupportedTemporalTypeException;
+import java.time.temporal.ValueRange;
+import java.util.Calendar;
+import java.util.Objects;
+
+import sun.util.calendar.CalendarDate;
+import sun.util.calendar.LocalGregorianCalendar;
+
+// Android-changed: removed ValueBased paragraph.
+/**
+ * A date in the Japanese Imperial calendar system.
+ * <p>
+ * This date operates using the {@linkplain JapaneseChronology Japanese Imperial calendar}.
+ * This calendar system is primarily used in Japan.
+ * <p>
+ * The Japanese Imperial calendar system is the same as the ISO calendar system
+ * apart from the era-based year numbering. The proleptic-year is defined to be
+ * equal to the ISO proleptic-year.
+ * <p>
+ * Japan introduced the Gregorian calendar starting with Meiji 6.
+ * Only Meiji and later eras are supported;
+ * dates before Meiji 6, January 1 are not supported.
+ * <p>
+ * For example, the Japanese year "Heisei 24" corresponds to ISO year "2012".<br>
+ * Calling {@code japaneseDate.get(YEAR_OF_ERA)} will return 24.<br>
+ * Calling {@code japaneseDate.get(YEAR)} will return 2012.<br>
+ * Calling {@code japaneseDate.get(ERA)} will return 2, corresponding to
+ * {@code JapaneseChronology.ERA_HEISEI}.<br>
+ *
+ * @implSpec
+ * This class is immutable and thread-safe.
+ *
+ * @since 1.8
+ */
+public final class JapaneseDate
+ extends ChronoLocalDateImpl<JapaneseDate>
+ implements ChronoLocalDate, Serializable {
+
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = -305327627230580483L;
+
+ /**
+ * The underlying ISO local date.
+ */
+ private final transient LocalDate isoDate;
+ /**
+ * The JapaneseEra of this date.
+ */
+ private transient JapaneseEra era;
+ /**
+ * The Japanese imperial calendar year of this date.
+ */
+ private transient int yearOfEra;
+
+ /**
+ * The first day supported by the JapaneseChronology is Meiji 6, January 1st.
+ */
+ static final LocalDate MEIJI_6_ISODATE = LocalDate.of(1873, 1, 1);
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains the current {@code JapaneseDate} from the system clock in the default time-zone.
+ * <p>
+ * This will query the {@link Clock#systemDefaultZone() system clock} in the default
+ * time-zone to obtain the current date.
+ * <p>
+ * Using this method will prevent the ability to use an alternate clock for testing
+ * because the clock is hard-coded.
+ *
+ * @return the current date using the system clock and default time-zone, not null
+ */
+ public static JapaneseDate now() {
+ return now(Clock.systemDefaultZone());
+ }
+
+ /**
+ * Obtains the current {@code JapaneseDate} from the system clock in the specified time-zone.
+ * <p>
+ * This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date.
+ * Specifying the time-zone avoids dependence on the default time-zone.
+ * <p>
+ * Using this method will prevent the ability to use an alternate clock for testing
+ * because the clock is hard-coded.
+ *
+ * @param zone the zone ID to use, not null
+ * @return the current date using the system clock, not null
+ */
+ public static JapaneseDate now(ZoneId zone) {
+ return now(Clock.system(zone));
+ }
+
+ /**
+ * Obtains the current {@code JapaneseDate} from the specified clock.
+ * <p>
+ * This will query the specified clock to obtain the current date - today.
+ * Using this method allows the use of an alternate clock for testing.
+ * The alternate clock may be introduced using {@linkplain Clock dependency injection}.
+ *
+ * @param clock the clock to use, not null
+ * @return the current date, not null
+ * @throws DateTimeException if the current date cannot be obtained
+ */
+ public static JapaneseDate now(Clock clock) {
+ return new JapaneseDate(LocalDate.now(clock));
+ }
+
+ /**
+ * Obtains a {@code JapaneseDate} representing a date in the Japanese calendar
+ * system from the era, year-of-era, month-of-year and day-of-month fields.
+ * <p>
+ * This returns a {@code JapaneseDate} with the specified fields.
+ * The day must be valid for the year and month, otherwise an exception will be thrown.
+ * <p>
+ * The Japanese month and day-of-month are the same as those in the
+ * ISO calendar system. They are not reset when the era changes.
+ * For example:
+ * <pre>
+ * 6th Jan Showa 64 = ISO 1989-01-06
+ * 7th Jan Showa 64 = ISO 1989-01-07
+ * 8th Jan Heisei 1 = ISO 1989-01-08
+ * 9th Jan Heisei 1 = ISO 1989-01-09
+ * </pre>
+ *
+ * @param era the Japanese era, not null
+ * @param yearOfEra the Japanese year-of-era
+ * @param month the Japanese month-of-year, from 1 to 12
+ * @param dayOfMonth the Japanese day-of-month, from 1 to 31
+ * @return the date in Japanese calendar system, not null
+ * @throws DateTimeException if the value of any field is out of range,
+ * or if the day-of-month is invalid for the month-year,
+ * or if the date is not a Japanese era
+ */
+ public static JapaneseDate of(JapaneseEra era, int yearOfEra, int month, int dayOfMonth) {
+ Objects.requireNonNull(era, "era");
+ LocalGregorianCalendar.Date jdate = JapaneseChronology.JCAL.newCalendarDate(null);
+ jdate.setEra(era.getPrivateEra()).setDate(yearOfEra, month, dayOfMonth);
+ if (!JapaneseChronology.JCAL.validate(jdate)) {
+ throw new DateTimeException("year, month, and day not valid for Era");
+ }
+ LocalDate date = LocalDate.of(jdate.getNormalizedYear(), month, dayOfMonth);
+ return new JapaneseDate(era, yearOfEra, date);
+ }
+
+ /**
+ * Obtains a {@code JapaneseDate} representing a date in the Japanese calendar
+ * system from the proleptic-year, month-of-year and day-of-month fields.
+ * <p>
+ * This returns a {@code JapaneseDate} with the specified fields.
+ * The day must be valid for the year and month, otherwise an exception will be thrown.
+ * <p>
+ * The Japanese proleptic year, month and day-of-month are the same as those
+ * in the ISO calendar system. They are not reset when the era changes.
+ *
+ * @param prolepticYear the Japanese proleptic-year
+ * @param month the Japanese month-of-year, from 1 to 12
+ * @param dayOfMonth the Japanese day-of-month, from 1 to 31
+ * @return the date in Japanese calendar system, not null
+ * @throws DateTimeException if the value of any field is out of range,
+ * or if the day-of-month is invalid for the month-year
+ */
+ public static JapaneseDate of(int prolepticYear, int month, int dayOfMonth) {
+ return new JapaneseDate(LocalDate.of(prolepticYear, month, dayOfMonth));
+ }
+
+ /**
+ * Obtains a {@code JapaneseDate} representing a date in the Japanese calendar
+ * system from the era, year-of-era and day-of-year fields.
+ * <p>
+ * This returns a {@code JapaneseDate} with the specified fields.
+ * The day must be valid for the year, otherwise an exception will be thrown.
+ * <p>
+ * The day-of-year in this factory is expressed relative to the start of the year-of-era.
+ * This definition changes the normal meaning of day-of-year only in those years
+ * where the year-of-era is reset to one due to a change in the era.
+ * For example:
+ * <pre>
+ * 6th Jan Showa 64 = day-of-year 6
+ * 7th Jan Showa 64 = day-of-year 7
+ * 8th Jan Heisei 1 = day-of-year 1
+ * 9th Jan Heisei 1 = day-of-year 2
+ * </pre>
+ *
+ * @param era the Japanese era, not null
+ * @param yearOfEra the Japanese year-of-era
+ * @param dayOfYear the chronology day-of-year, from 1 to 366
+ * @return the date in Japanese calendar system, not null
+ * @throws DateTimeException if the value of any field is out of range,
+ * or if the day-of-year is invalid for the year
+ */
+ static JapaneseDate ofYearDay(JapaneseEra era, int yearOfEra, int dayOfYear) {
+ Objects.requireNonNull(era, "era");
+ CalendarDate firstDay = era.getPrivateEra().getSinceDate();
+ LocalGregorianCalendar.Date jdate = JapaneseChronology.JCAL.newCalendarDate(null);
+ jdate.setEra(era.getPrivateEra());
+ if (yearOfEra == 1) {
+ jdate.setDate(yearOfEra, firstDay.getMonth(), firstDay.getDayOfMonth() + dayOfYear - 1);
+ } else {
+ jdate.setDate(yearOfEra, 1, dayOfYear);
+ }
+ JapaneseChronology.JCAL.normalize(jdate);
+ if (era.getPrivateEra() != jdate.getEra() || yearOfEra != jdate.getYear()) {
+ throw new DateTimeException("Invalid parameters");
+ }
+ LocalDate localdate = LocalDate.of(jdate.getNormalizedYear(),
+ jdate.getMonth(), jdate.getDayOfMonth());
+ return new JapaneseDate(era, yearOfEra, localdate);
+ }
+
+ /**
+ * Obtains a {@code JapaneseDate} from a temporal object.
+ * <p>
+ * This obtains a date in the Japanese calendar system based on the specified temporal.
+ * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
+ * which this factory converts to an instance of {@code JapaneseDate}.
+ * <p>
+ * The conversion typically uses the {@link ChronoField#EPOCH_DAY EPOCH_DAY}
+ * field, which is standardized across calendar systems.
+ * <p>
+ * This method matches the signature of the functional interface {@link TemporalQuery}
+ * allowing it to be used as a query via method reference, {@code JapaneseDate::from}.
+ *
+ * @param temporal the temporal object to convert, not null
+ * @return the date in Japanese calendar system, not null
+ * @throws DateTimeException if unable to convert to a {@code JapaneseDate}
+ */
+ public static JapaneseDate from(TemporalAccessor temporal) {
+ return JapaneseChronology.INSTANCE.date(temporal);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Creates an instance from an ISO date.
+ *
+ * @param isoDate the standard local date, validated not null
+ */
+ JapaneseDate(LocalDate isoDate) {
+ if (isoDate.isBefore(MEIJI_6_ISODATE)) {
+ throw new DateTimeException("JapaneseDate before Meiji 6 is not supported");
+ }
+ LocalGregorianCalendar.Date jdate = toPrivateJapaneseDate(isoDate);
+ this.era = JapaneseEra.toJapaneseEra(jdate.getEra());
+ this.yearOfEra = jdate.getYear();
+ this.isoDate = isoDate;
+ }
+
+ /**
+ * Constructs a {@code JapaneseDate}. This constructor does NOT validate the given parameters,
+ * and {@code era} and {@code year} must agree with {@code isoDate}.
+ *
+ * @param era the era, validated not null
+ * @param year the year-of-era, validated
+ * @param isoDate the standard local date, validated not null
+ */
+ JapaneseDate(JapaneseEra era, int year, LocalDate isoDate) {
+ if (isoDate.isBefore(MEIJI_6_ISODATE)) {
+ throw new DateTimeException("JapaneseDate before Meiji 6 is not supported");
+ }
+ this.era = era;
+ this.yearOfEra = year;
+ this.isoDate = isoDate;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the chronology of this date, which is the Japanese calendar system.
+ * <p>
+ * The {@code Chronology} represents the calendar system in use.
+ * The era and other fields in {@link ChronoField} are defined by the chronology.
+ *
+ * @return the Japanese chronology, not null
+ */
+ @Override
+ public JapaneseChronology getChronology() {
+ return JapaneseChronology.INSTANCE;
+ }
+
+ /**
+ * Gets the era applicable at this date.
+ * <p>
+ * The Japanese calendar system has multiple eras defined by {@link JapaneseEra}.
+ *
+ * @return the era applicable at this date, not null
+ */
+ @Override
+ public JapaneseEra getEra() {
+ return era;
+ }
+
+ /**
+ * Returns the length of the month represented by this date.
+ * <p>
+ * This returns the length of the month in days.
+ * Month lengths match those of the ISO calendar system.
+ *
+ * @return the length of the month in days
+ */
+ @Override
+ public int lengthOfMonth() {
+ return isoDate.lengthOfMonth();
+ }
+
+ @Override
+ public int lengthOfYear() {
+ // Android-changed: use #createCalendar() to create calendar.
+ Calendar jcal = JapaneseChronology.createCalendar();
+ jcal.set(Calendar.ERA, era.getValue() + JapaneseEra.ERA_OFFSET);
+ jcal.set(yearOfEra, isoDate.getMonthValue() - 1, isoDate.getDayOfMonth());
+ return jcal.getActualMaximum(Calendar.DAY_OF_YEAR);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if the specified field is supported.
+ * <p>
+ * This checks if this date can be queried for the specified field.
+ * If false, then calling the {@link #range(TemporalField) range} and
+ * {@link #get(TemporalField) get} methods will throw an exception.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The supported fields are:
+ * <ul>
+ * <li>{@code DAY_OF_WEEK}
+ * <li>{@code DAY_OF_MONTH}
+ * <li>{@code DAY_OF_YEAR}
+ * <li>{@code EPOCH_DAY}
+ * <li>{@code MONTH_OF_YEAR}
+ * <li>{@code PROLEPTIC_MONTH}
+ * <li>{@code YEAR_OF_ERA}
+ * <li>{@code YEAR}
+ * <li>{@code ERA}
+ * </ul>
+ * All other {@code ChronoField} instances will return false.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}
+ * passing {@code this} as the argument.
+ * Whether the field is supported is determined by the field.
+ *
+ * @param field the field to check, null returns false
+ * @return true if the field is supported on this date, false if not
+ */
+ @Override
+ public boolean isSupported(TemporalField field) {
+ if (field == ALIGNED_DAY_OF_WEEK_IN_MONTH || field == ALIGNED_DAY_OF_WEEK_IN_YEAR ||
+ field == ALIGNED_WEEK_OF_MONTH || field == ALIGNED_WEEK_OF_YEAR) {
+ return false;
+ }
+ // Android-changed: Apply upstream OpenJDK 9 compilation fix.
+ // Applied OpenJDK 9 change from http://hg.openjdk.java.net/jdk9/dev/jdk/rev/2b7b09c81bf1
+ // On OpenJDK 8, either version is supported and has the same behavior.
+ // return ChronoLocalDate.super.isSupported(field);
+ return super.isSupported(field);
+ }
+
+ @Override
+ public ValueRange range(TemporalField field) {
+ if (field instanceof ChronoField) {
+ if (isSupported(field)) {
+ ChronoField f = (ChronoField) field;
+ switch (f) {
+ case DAY_OF_MONTH: return ValueRange.of(1, lengthOfMonth());
+ case DAY_OF_YEAR: return ValueRange.of(1, lengthOfYear());
+ case YEAR_OF_ERA: {
+ // Android-changed: use #createCalendar() to create calendar.
+ Calendar jcal = JapaneseChronology.createCalendar();
+ jcal.set(Calendar.ERA, era.getValue() + JapaneseEra.ERA_OFFSET);
+ jcal.set(yearOfEra, isoDate.getMonthValue() - 1, isoDate.getDayOfMonth());
+ return ValueRange.of(1, jcal.getActualMaximum(Calendar.YEAR));
+ }
+ }
+ return getChronology().range(f);
+ }
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
+ }
+ return field.rangeRefinedBy(this);
+ }
+
+ @Override
+ public long getLong(TemporalField field) {
+ if (field instanceof ChronoField) {
+ // same as ISO:
+ // DAY_OF_WEEK, DAY_OF_MONTH, EPOCH_DAY, MONTH_OF_YEAR, PROLEPTIC_MONTH, YEAR
+ //
+ // calendar specific fields
+ // DAY_OF_YEAR, YEAR_OF_ERA, ERA
+ switch ((ChronoField) field) {
+ case ALIGNED_DAY_OF_WEEK_IN_MONTH:
+ case ALIGNED_DAY_OF_WEEK_IN_YEAR:
+ case ALIGNED_WEEK_OF_MONTH:
+ case ALIGNED_WEEK_OF_YEAR:
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
+ case YEAR_OF_ERA:
+ return yearOfEra;
+ case ERA:
+ return era.getValue();
+ case DAY_OF_YEAR:
+ // Android-changed: use #createCalendar() to create calendar.
+ Calendar jcal = JapaneseChronology.createCalendar();
+ jcal.set(Calendar.ERA, era.getValue() + JapaneseEra.ERA_OFFSET);
+ jcal.set(yearOfEra, isoDate.getMonthValue() - 1, isoDate.getDayOfMonth());
+ return jcal.get(Calendar.DAY_OF_YEAR);
+ }
+ return isoDate.getLong(field);
+ }
+ return field.getFrom(this);
+ }
+
+ /**
+ * Returns a {@code LocalGregorianCalendar.Date} converted from the given {@code isoDate}.
+ *
+ * @param isoDate the local date, not null
+ * @return a {@code LocalGregorianCalendar.Date}, not null
+ */
+ private static LocalGregorianCalendar.Date toPrivateJapaneseDate(LocalDate isoDate) {
+ LocalGregorianCalendar.Date jdate = JapaneseChronology.JCAL.newCalendarDate(null);
+ sun.util.calendar.Era sunEra = JapaneseEra.privateEraFrom(isoDate);
+ int year = isoDate.getYear();
+ if (sunEra != null) {
+ year -= sunEra.getSinceDate().getYear() - 1;
+ }
+ jdate.setEra(sunEra).setYear(year).setMonth(isoDate.getMonthValue()).setDayOfMonth(isoDate.getDayOfMonth());
+ JapaneseChronology.JCAL.normalize(jdate);
+ return jdate;
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ public JapaneseDate with(TemporalField field, long newValue) {
+ if (field instanceof ChronoField) {
+ ChronoField f = (ChronoField) field;
+ if (getLong(f) == newValue) { // getLong() validates for supported fields
+ return this;
+ }
+ switch (f) {
+ case YEAR_OF_ERA:
+ case YEAR:
+ case ERA: {
+ int nvalue = getChronology().range(f).checkValidIntValue(newValue, f);
+ switch (f) {
+ case YEAR_OF_ERA:
+ return this.withYear(nvalue);
+ case YEAR:
+ return with(isoDate.withYear(nvalue));
+ case ERA: {
+ return this.withYear(JapaneseEra.of(nvalue), yearOfEra);
+ }
+ }
+ }
+ }
+ // YEAR, PROLEPTIC_MONTH and others are same as ISO
+ return with(isoDate.with(field, newValue));
+ }
+ return super.with(field, newValue);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws DateTimeException {@inheritDoc}
+ * @throws ArithmeticException {@inheritDoc}
+ */
+ @Override
+ public JapaneseDate with(TemporalAdjuster adjuster) {
+ return super.with(adjuster);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws DateTimeException {@inheritDoc}
+ * @throws ArithmeticException {@inheritDoc}
+ */
+ @Override
+ public JapaneseDate plus(TemporalAmount amount) {
+ return super.plus(amount);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws DateTimeException {@inheritDoc}
+ * @throws ArithmeticException {@inheritDoc}
+ */
+ @Override
+ public JapaneseDate minus(TemporalAmount amount) {
+ return super.minus(amount);
+ }
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a copy of this date with the year altered.
+ * <p>
+ * This method changes the year of the date.
+ * If the month-day is invalid for the year, then the previous valid day
+ * will be selected instead.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param era the era to set in the result, not null
+ * @param yearOfEra the year-of-era to set in the returned date
+ * @return a {@code JapaneseDate} based on this date with the requested year, never null
+ * @throws DateTimeException if {@code year} is invalid
+ */
+ private JapaneseDate withYear(JapaneseEra era, int yearOfEra) {
+ int year = JapaneseChronology.INSTANCE.prolepticYear(era, yearOfEra);
+ return with(isoDate.withYear(year));
+ }
+
+ /**
+ * Returns a copy of this date with the year-of-era altered.
+ * <p>
+ * This method changes the year-of-era of the date.
+ * If the month-day is invalid for the year, then the previous valid day
+ * will be selected instead.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param year the year to set in the returned date
+ * @return a {@code JapaneseDate} based on this date with the requested year-of-era, never null
+ * @throws DateTimeException if {@code year} is invalid
+ */
+ private JapaneseDate withYear(int year) {
+ return withYear(getEra(), year);
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ JapaneseDate plusYears(long years) {
+ return with(isoDate.plusYears(years));
+ }
+
+ @Override
+ JapaneseDate plusMonths(long months) {
+ return with(isoDate.plusMonths(months));
+ }
+
+ @Override
+ JapaneseDate plusWeeks(long weeksToAdd) {
+ return with(isoDate.plusWeeks(weeksToAdd));
+ }
+
+ @Override
+ JapaneseDate plusDays(long days) {
+ return with(isoDate.plusDays(days));
+ }
+
+ @Override
+ public JapaneseDate plus(long amountToAdd, TemporalUnit unit) {
+ return super.plus(amountToAdd, unit);
+ }
+
+ @Override
+ public JapaneseDate minus(long amountToAdd, TemporalUnit unit) {
+ return super.minus(amountToAdd, unit);
+ }
+
+ @Override
+ JapaneseDate minusYears(long yearsToSubtract) {
+ return super.minusYears(yearsToSubtract);
+ }
+
+ @Override
+ JapaneseDate minusMonths(long monthsToSubtract) {
+ return super.minusMonths(monthsToSubtract);
+ }
+
+ @Override
+ JapaneseDate minusWeeks(long weeksToSubtract) {
+ return super.minusWeeks(weeksToSubtract);
+ }
+
+ @Override
+ JapaneseDate minusDays(long daysToSubtract) {
+ return super.minusDays(daysToSubtract);
+ }
+
+ private JapaneseDate with(LocalDate newDate) {
+ return (newDate.equals(isoDate) ? this : new JapaneseDate(newDate));
+ }
+
+ @Override // for javadoc and covariant return type
+ @SuppressWarnings("unchecked")
+ public final ChronoLocalDateTime<JapaneseDate> atTime(LocalTime localTime) {
+ return (ChronoLocalDateTime<JapaneseDate>)super.atTime(localTime);
+ }
+
+ @Override
+ public ChronoPeriod until(ChronoLocalDate endDate) {
+ Period period = isoDate.until(endDate);
+ return getChronology().period(period.getYears(), period.getMonths(), period.getDays());
+ }
+
+ @Override // override for performance
+ public long toEpochDay() {
+ return isoDate.toEpochDay();
+ }
+
+ //-------------------------------------------------------------------------
+ /**
+ * Compares this date to another date, including the chronology.
+ * <p>
+ * Compares this {@code JapaneseDate} with another ensuring that the date is the same.
+ * <p>
+ * Only objects of type {@code JapaneseDate} are compared, other types return false.
+ * To compare the dates of two {@code TemporalAccessor} instances, including dates
+ * in two different chronologies, use {@link ChronoField#EPOCH_DAY} as a comparator.
+ *
+ * @param obj the object to check, null returns false
+ * @return true if this is equal to the other date
+ */
+ @Override // override for performance
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof JapaneseDate) {
+ JapaneseDate otherDate = (JapaneseDate) obj;
+ return this.isoDate.equals(otherDate.isoDate);
+ }
+ return false;
+ }
+
+ /**
+ * A hash code for this date.
+ *
+ * @return a suitable hash code based only on the Chronology and the date
+ */
+ @Override // override for performance
+ public int hashCode() {
+ return getChronology().getId().hashCode() ^ isoDate.hashCode();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Defend against malicious streams.
+ *
+ * @param s the stream to read
+ * @throws InvalidObjectException always
+ */
+ private void readObject(ObjectInputStream s) throws InvalidObjectException {
+ throw new InvalidObjectException("Deserialization via serialization delegate");
+ }
+
+ /**
+ * Writes the object using a
+ * <a href="../../../serialized-form.html#java.time.chrono.Ser">dedicated serialized form</a>.
+ * @serialData
+ * <pre>
+ * out.writeByte(4); // identifies a JapaneseDate
+ * out.writeInt(get(YEAR));
+ * out.writeByte(get(MONTH_OF_YEAR));
+ * out.writeByte(get(DAY_OF_MONTH));
+ * </pre>
+ *
+ * @return the instance of {@code Ser}, not null
+ */
+ private Object writeReplace() {
+ return new Ser(Ser.JAPANESE_DATE_TYPE, this);
+ }
+
+ void writeExternal(DataOutput out) throws IOException {
+ // JapaneseChronology is implicit in the JAPANESE_DATE_TYPE
+ out.writeInt(get(YEAR));
+ out.writeByte(get(MONTH_OF_YEAR));
+ out.writeByte(get(DAY_OF_MONTH));
+ }
+
+ static JapaneseDate readExternal(DataInput in) throws IOException {
+ int year = in.readInt();
+ int month = in.readByte();
+ int dayOfMonth = in.readByte();
+ return JapaneseChronology.INSTANCE.date(year, month, dayOfMonth);
+ }
+
+}
diff --git a/java/time/chrono/JapaneseEra.java b/java/time/chrono/JapaneseEra.java
new file mode 100644
index 0000000..81805d0
--- /dev/null
+++ b/java/time/chrono/JapaneseEra.java
@@ -0,0 +1,452 @@
+/*
+ * Copyright (c) 2012, 2019, 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) 2012, 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.chrono.JapaneseDate.MEIJI_6_ISODATE;
+import static java.time.temporal.ChronoField.ERA;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.time.DateTimeException;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatterBuilder;
+import java.time.format.TextStyle;
+import java.time.temporal.ChronoField;
+import java.time.temporal.TemporalField;
+import java.time.temporal.UnsupportedTemporalTypeException;
+import java.time.temporal.ValueRange;
+import java.util.Arrays;
+import java.util.Locale;
+import java.util.Objects;
+
+import sun.util.calendar.CalendarDate;
+
+/**
+ * An era in the Japanese Imperial calendar system.
+ * <p>
+ * The Japanese government defines the official name and start date of
+ * each era. Eras are consecutive and their date ranges do not overlap,
+ * so the end date of one era is always the day before the start date
+ * of the next era.
+ * <p>
+ * The Java SE Platform supports all eras defined by the Japanese government,
+ * beginning with the Meiji era. Each era is identified in the Platform by an
+ * integer value and a name. The {@link #of(int)} and {@link #valueOf(String)}
+ * methods may be used to obtain a singleton instance of JapaneseEra for each
+ * era. The {@link #values()} method returns the singleton instances of all
+ * supported eras.
+ * <p>
+ * For convenience, this class declares a number of public static final fields
+ * that refer to singleton instances returned by the values() method.
+ *
+ * @apiNote
+ * The fields declared in this class may evolve over time, in line with the
+ * results of the {@link #values()} method. However, there is not necessarily
+ * a 1:1 correspondence between the fields and the singleton instances.
+ *
+ * @apiNote
+ * The Japanese government may announce a new era and define its start
+ * date but not its official name. In this scenario, the singleton instance
+ * that represents the new era may return a name that is not stable until
+ * the official name is defined. Developers should exercise caution when
+ * relying on the name returned by any singleton instance that does not
+ * correspond to a public static final field.
+ *
+ * @implSpec
+ * This class is immutable and thread-safe.
+ *
+ * @since 1.8
+ */
+public final class JapaneseEra
+ implements Era, Serializable {
+
+ // The offset value to 0-based index from the era value.
+ // i.e., getValue() + ERA_OFFSET == 0-based index
+ static final int ERA_OFFSET = 2;
+
+ static final sun.util.calendar.Era[] ERA_CONFIG;
+
+ /**
+ * The singleton instance for the 'Meiji' era (1868-01-01 - 1912-07-29)
+ * which has the value -1.
+ */
+ public static final JapaneseEra MEIJI = new JapaneseEra(-1, LocalDate.of(1868, 1, 1));
+ /**
+ * The singleton instance for the 'Taisho' era (1912-07-30 - 1926-12-24)
+ * which has the value 0.
+ */
+ public static final JapaneseEra TAISHO = new JapaneseEra(0, LocalDate.of(1912, 7, 30));
+ /**
+ * The singleton instance for the 'Showa' era (1926-12-25 - 1989-01-07)
+ * which has the value 1.
+ */
+ public static final JapaneseEra SHOWA = new JapaneseEra(1, LocalDate.of(1926, 12, 25));
+ /**
+ * The singleton instance for the 'Heisei' era (1989-01-08 - 2019-04-30)
+ * which has the value 2.
+ */
+ public static final JapaneseEra HEISEI = new JapaneseEra(2, LocalDate.of(1989, 1, 8));
+ // Android-changed: Integrate OpenJDK support for Japanese Era Reiwa.
+ /**
+ * The singleton instance for the 'Reiwa' era (2019-05-01 - current)
+ * which has the value 3. The end date of this era is not specified, unless
+ * the Japanese Government defines it.
+ */
+ public static final JapaneseEra REIWA = new JapaneseEra(3, LocalDate.of(2019, 5, 1));
+
+ // The number of predefined JapaneseEra constants.
+ // There may be a supplemental era defined by the property.
+ private static final int N_ERA_CONSTANTS = REIWA.getValue() + ERA_OFFSET;
+
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = 1466499369062886794L;
+
+ // array for the singleton JapaneseEra instances
+ private static final JapaneseEra[] KNOWN_ERAS;
+
+ static {
+ ERA_CONFIG = JapaneseChronology.JCAL.getEras();
+
+ KNOWN_ERAS = new JapaneseEra[ERA_CONFIG.length];
+ KNOWN_ERAS[0] = MEIJI;
+ KNOWN_ERAS[1] = TAISHO;
+ KNOWN_ERAS[2] = SHOWA;
+ KNOWN_ERAS[3] = HEISEI;
+ KNOWN_ERAS[4] = REIWA;
+ for (int i = N_ERA_CONSTANTS; i < ERA_CONFIG.length; i++) {
+ CalendarDate date = ERA_CONFIG[i].getSinceDate();
+ LocalDate isoDate = LocalDate.of(date.getYear(), date.getMonth(), date.getDayOfMonth());
+ KNOWN_ERAS[i] = new JapaneseEra(i - ERA_OFFSET + 1, isoDate);
+ }
+ };
+
+ /**
+ * The era value.
+ * @serial
+ */
+ private final transient int eraValue;
+
+ // the first day of the era
+ private final transient LocalDate since;
+
+ /**
+ * Creates an instance.
+ *
+ * @param eraValue the era value, validated
+ * @param since the date representing the first date of the era, validated not null
+ */
+ private JapaneseEra(int eraValue, LocalDate since) {
+ this.eraValue = eraValue;
+ this.since = since;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns the Sun private Era instance corresponding to this {@code JapaneseEra}.
+ *
+ * @return the Sun private Era instance for this {@code JapaneseEra}.
+ */
+ sun.util.calendar.Era getPrivateEra() {
+ return ERA_CONFIG[ordinal(eraValue)];
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code JapaneseEra} from an {@code int} value.
+ * <ul>
+ * <li>The value {@code 1} is associated with the 'Showa' era, because
+ * it contains 1970-01-01 (ISO calendar system).</li>
+ * <li>The values {@code -1} and {@code 0} are associated with two earlier
+ * eras, Meiji and Taisho, respectively.</li>
+ * <li>A value greater than {@code 1} is associated with a later era,
+ * beginning with Heisei ({@code 2}).</li>
+ * </ul>
+ * <p>
+ * Every instance of {@code JapaneseEra} that is returned from the {@link #values()}
+ * method has an int value (available via {@link Era#getValue()} which is
+ * accepted by this method.
+ *
+ * @param japaneseEra the era to represent
+ * @return the {@code JapaneseEra} singleton, not null
+ * @throws DateTimeException if the value is invalid
+ */
+ public static JapaneseEra of(int japaneseEra) {
+ if (japaneseEra < MEIJI.eraValue || japaneseEra + ERA_OFFSET > KNOWN_ERAS.length) {
+ throw new DateTimeException("Invalid era: " + japaneseEra);
+ }
+ return KNOWN_ERAS[ordinal(japaneseEra)];
+ }
+
+ /**
+ * Returns the {@code JapaneseEra} with the name.
+ * <p>
+ * The string must match exactly the name of the era.
+ * (Extraneous whitespace characters are not permitted.)
+ *
+ * @param japaneseEra the japaneseEra name; non-null
+ * @return the {@code JapaneseEra} singleton, never null
+ * @throws IllegalArgumentException if there is not JapaneseEra with the specified name
+ */
+ public static JapaneseEra valueOf(String japaneseEra) {
+ Objects.requireNonNull(japaneseEra, "japaneseEra");
+ for (JapaneseEra era : KNOWN_ERAS) {
+ if (era.getName().equals(japaneseEra)) {
+ return era;
+ }
+ }
+ throw new IllegalArgumentException("japaneseEra is invalid");
+ }
+
+ /**
+ * Returns an array of JapaneseEras.
+ * <p>
+ * This method may be used to iterate over the JapaneseEras as follows:
+ * <pre>
+ * for (JapaneseEra c : JapaneseEra.values())
+ * System.out.println(c);
+ * </pre>
+ *
+ * @return an array of JapaneseEras
+ */
+ public static JapaneseEra[] values() {
+ return Arrays.copyOf(KNOWN_ERAS, KNOWN_ERAS.length);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @param style {@inheritDoc}
+ * @param locale {@inheritDoc}
+ */
+ @Override
+ public String getDisplayName(TextStyle style, Locale locale) {
+ // If this JapaneseEra is a supplemental one, obtain the name from
+ // the era definition.
+ if (getValue() > N_ERA_CONSTANTS - ERA_OFFSET) {
+ Objects.requireNonNull(locale, "locale");
+ return style.asNormal() == TextStyle.NARROW ? getAbbreviation() : getName();
+ }
+
+ return new DateTimeFormatterBuilder()
+ .appendText(ERA, style)
+ .toFormatter(locale)
+ .withChronology(JapaneseChronology.INSTANCE)
+ .format(this == MEIJI ? MEIJI_6_ISODATE : since);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code JapaneseEra} from a date.
+ *
+ * @param date the date, not null
+ * @return the Era singleton, never null
+ */
+ static JapaneseEra from(LocalDate date) {
+ if (date.isBefore(MEIJI_6_ISODATE)) {
+ throw new DateTimeException("JapaneseDate before Meiji 6 are not supported");
+ }
+ for (int i = KNOWN_ERAS.length - 1; i > 0; i--) {
+ JapaneseEra era = KNOWN_ERAS[i];
+ if (date.compareTo(era.since) >= 0) {
+ return era;
+ }
+ }
+ return null;
+ }
+
+ static JapaneseEra toJapaneseEra(sun.util.calendar.Era privateEra) {
+ for (int i = ERA_CONFIG.length - 1; i >= 0; i--) {
+ if (ERA_CONFIG[i].equals(privateEra)) {
+ return KNOWN_ERAS[i];
+ }
+ }
+ return null;
+ }
+
+ static sun.util.calendar.Era privateEraFrom(LocalDate isoDate) {
+ for (int i = KNOWN_ERAS.length - 1; i > 0; i--) {
+ JapaneseEra era = KNOWN_ERAS[i];
+ if (isoDate.compareTo(era.since) >= 0) {
+ return ERA_CONFIG[i];
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the index into the arrays from the Era value.
+ * the eraValue is a valid Era number, -1..2.
+ *
+ * @param eraValue the era value to convert to the index
+ * @return the index of the current Era
+ */
+ private static int ordinal(int eraValue) {
+ return eraValue + ERA_OFFSET - 1;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the numeric era {@code int} value.
+ * <p>
+ * The {@link #SHOWA} era that contains 1970-01-01 (ISO calendar system) has the value 1.
+ * Later eras are numbered from 2 ({@link #HEISEI}).
+ * Earlier eras are numbered 0 ({@link #TAISHO}), -1 ({@link #MEIJI})).
+ *
+ * @return the era value
+ */
+ @Override
+ public int getValue() {
+ return eraValue;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the range of valid values for the specified field.
+ * <p>
+ * The range object expresses the minimum and maximum valid values for a field.
+ * This era is used to enhance the accuracy of the returned range.
+ * If it is not possible to return the range, because the field is not supported
+ * or for some other reason, an exception is thrown.
+ * <p>
+ * If the field is a {@link ChronoField} then the query is implemented here.
+ * The {@code ERA} field returns the range.
+ * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.rangeRefinedBy(TemporalAccessor)}
+ * passing {@code this} as the argument.
+ * Whether the range can be obtained is determined by the field.
+ * <p>
+ * The range of valid Japanese eras can change over time due to the nature
+ * of the Japanese calendar system.
+ *
+ * @param field the field to query the range for, not null
+ * @return the range of valid values for the field, not null
+ * @throws DateTimeException if the range for the field cannot be obtained
+ * @throws UnsupportedTemporalTypeException if the unit is not supported
+ */
+ @Override // override as super would return range from 0 to 1
+ public ValueRange range(TemporalField field) {
+ if (field == ERA) {
+ return JapaneseChronology.INSTANCE.range(ERA);
+ }
+ return Era.super.range(field);
+ }
+
+ //-----------------------------------------------------------------------
+ String getAbbreviation() {
+ return ERA_CONFIG[ordinal(getValue())].getAbbreviation();
+ }
+
+ String getName() {
+ return ERA_CONFIG[ordinal(getValue())].getName();
+ }
+
+ @Override
+ public String toString() {
+ return getName();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Defend against malicious streams.
+ *
+ * @param s the stream to read
+ * @throws InvalidObjectException always
+ */
+ private void readObject(ObjectInputStream s) throws InvalidObjectException {
+ throw new InvalidObjectException("Deserialization via serialization delegate");
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Writes the object using a
+ * <a href="../../../serialized-form.html#java.time.chrono.Ser">dedicated serialized form</a>.
+ * @serialData
+ * <pre>
+ * out.writeByte(5); // identifies a JapaneseEra
+ * out.writeInt(getValue());
+ * </pre>
+ *
+ * @return the instance of {@code Ser}, not null
+ */
+ private Object writeReplace() {
+ return new Ser(Ser.JAPANESE_ERA_TYPE, this);
+ }
+
+ void writeExternal(DataOutput out) throws IOException {
+ out.writeByte(this.getValue());
+ }
+
+ static JapaneseEra readExternal(DataInput in) throws IOException {
+ byte eraValue = in.readByte();
+ return JapaneseEra.of(eraValue);
+ }
+
+}
diff --git a/java/time/chrono/MinguoChronology.java b/java/time/chrono/MinguoChronology.java
new file mode 100644
index 0000000..841884c
--- /dev/null
+++ b/java/time/chrono/MinguoChronology.java
@@ -0,0 +1,364 @@
+/*
+ * Copyright (c) 2012, 2015, 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) 2012, 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.io.InvalidObjectException;
+import static java.time.temporal.ChronoField.PROLEPTIC_MONTH;
+import static java.time.temporal.ChronoField.YEAR;
+
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.time.Clock;
+import java.time.DateTimeException;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.time.format.ResolverStyle;
+import java.time.temporal.ChronoField;
+import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalField;
+import java.time.temporal.ValueRange;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * The Minguo calendar system.
+ * <p>
+ * This chronology defines the rules of the Minguo calendar system.
+ * This calendar system is primarily used in the Republic of China, often known as Taiwan.
+ * Dates are aligned such that {@code 0001-01-01 (Minguo)} is {@code 1912-01-01 (ISO)}.
+ * <p>
+ * The fields are defined as follows:
+ * <ul>
+ * <li>era - There are two eras, the current 'Republic' (ERA_ROC) and the previous era (ERA_BEFORE_ROC).
+ * <li>year-of-era - The year-of-era for the current era increases uniformly from the epoch at year one.
+ * For the previous era the year increases from one as time goes backwards.
+ * The value for the current era is equal to the ISO proleptic-year minus 1911.
+ * <li>proleptic-year - The proleptic year is the same as the year-of-era for the
+ * current era. For the previous era, years have zero, then negative values.
+ * The value is equal to the ISO proleptic-year minus 1911.
+ * <li>month-of-year - The Minguo month-of-year exactly matches ISO.
+ * <li>day-of-month - The Minguo day-of-month exactly matches ISO.
+ * <li>day-of-year - The Minguo day-of-year exactly matches ISO.
+ * <li>leap-year - The Minguo leap-year pattern exactly matches ISO, such that the two calendars
+ * are never out of step.
+ * </ul>
+ *
+ * @implSpec
+ * This class is immutable and thread-safe.
+ *
+ * @since 1.8
+ */
+public final class MinguoChronology extends AbstractChronology implements Serializable {
+
+ /**
+ * Singleton instance for the Minguo chronology.
+ */
+ public static final MinguoChronology INSTANCE = new MinguoChronology();
+
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = 1039765215346859963L;
+ /**
+ * The difference in years between ISO and Minguo.
+ */
+ static final int YEARS_DIFFERENCE = 1911;
+
+ /**
+ * Restricted constructor.
+ */
+ private MinguoChronology() {
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the ID of the chronology - 'Minguo'.
+ * <p>
+ * The ID uniquely identifies the {@code Chronology}.
+ * It can be used to lookup the {@code Chronology} using {@link Chronology#of(String)}.
+ *
+ * @return the chronology ID - 'Minguo'
+ * @see #getCalendarType()
+ */
+ @Override
+ public String getId() {
+ return "Minguo";
+ }
+
+ /**
+ * Gets the calendar type of the underlying calendar system - 'roc'.
+ * <p>
+ * The calendar type is an identifier defined by the
+ * <em>Unicode Locale Data Markup Language (LDML)</em> specification.
+ * It can be used to lookup the {@code Chronology} using {@link Chronology#of(String)}.
+ * It can also be used as part of a locale, accessible via
+ * {@link Locale#getUnicodeLocaleType(String)} with the key 'ca'.
+ *
+ * @return the calendar system type - 'roc'
+ * @see #getId()
+ */
+ @Override
+ public String getCalendarType() {
+ return "roc";
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains a local date in Minguo calendar system from the
+ * era, year-of-era, month-of-year and day-of-month fields.
+ *
+ * @param era the Minguo era, not null
+ * @param yearOfEra the year-of-era
+ * @param month the month-of-year
+ * @param dayOfMonth the day-of-month
+ * @return the Minguo local date, not null
+ * @throws DateTimeException if unable to create the date
+ * @throws ClassCastException if the {@code era} is not a {@code MinguoEra}
+ */
+ @Override
+ public MinguoDate date(Era era, int yearOfEra, int month, int dayOfMonth) {
+ return date(prolepticYear(era, yearOfEra), month, dayOfMonth);
+ }
+
+ /**
+ * Obtains a local date in Minguo calendar system from the
+ * proleptic-year, month-of-year and day-of-month fields.
+ *
+ * @param prolepticYear the proleptic-year
+ * @param month the month-of-year
+ * @param dayOfMonth the day-of-month
+ * @return the Minguo local date, not null
+ * @throws DateTimeException if unable to create the date
+ */
+ @Override
+ public MinguoDate date(int prolepticYear, int month, int dayOfMonth) {
+ return new MinguoDate(LocalDate.of(prolepticYear + YEARS_DIFFERENCE, month, dayOfMonth));
+ }
+
+ /**
+ * Obtains a local date in Minguo calendar system from the
+ * era, year-of-era and day-of-year fields.
+ *
+ * @param era the Minguo era, not null
+ * @param yearOfEra the year-of-era
+ * @param dayOfYear the day-of-year
+ * @return the Minguo local date, not null
+ * @throws DateTimeException if unable to create the date
+ * @throws ClassCastException if the {@code era} is not a {@code MinguoEra}
+ */
+ @Override
+ public MinguoDate dateYearDay(Era era, int yearOfEra, int dayOfYear) {
+ return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear);
+ }
+
+ /**
+ * Obtains a local date in Minguo calendar system from the
+ * proleptic-year and day-of-year fields.
+ *
+ * @param prolepticYear the proleptic-year
+ * @param dayOfYear the day-of-year
+ * @return the Minguo local date, not null
+ * @throws DateTimeException if unable to create the date
+ */
+ @Override
+ public MinguoDate dateYearDay(int prolepticYear, int dayOfYear) {
+ return new MinguoDate(LocalDate.ofYearDay(prolepticYear + YEARS_DIFFERENCE, dayOfYear));
+ }
+
+ /**
+ * Obtains a local date in the Minguo calendar system from the epoch-day.
+ *
+ * @param epochDay the epoch day
+ * @return the Minguo local date, not null
+ * @throws DateTimeException if unable to create the date
+ */
+ @Override // override with covariant return type
+ public MinguoDate dateEpochDay(long epochDay) {
+ return new MinguoDate(LocalDate.ofEpochDay(epochDay));
+ }
+
+ @Override
+ public MinguoDate dateNow() {
+ return dateNow(Clock.systemDefaultZone());
+ }
+
+ @Override
+ public MinguoDate dateNow(ZoneId zone) {
+ return dateNow(Clock.system(zone));
+ }
+
+ @Override
+ public MinguoDate dateNow(Clock clock) {
+ return date(LocalDate.now(clock));
+ }
+
+ @Override
+ public MinguoDate date(TemporalAccessor temporal) {
+ if (temporal instanceof MinguoDate) {
+ return (MinguoDate) temporal;
+ }
+ return new MinguoDate(LocalDate.from(temporal));
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public ChronoLocalDateTime<MinguoDate> localDateTime(TemporalAccessor temporal) {
+ return (ChronoLocalDateTime<MinguoDate>)super.localDateTime(temporal);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public ChronoZonedDateTime<MinguoDate> zonedDateTime(TemporalAccessor temporal) {
+ return (ChronoZonedDateTime<MinguoDate>)super.zonedDateTime(temporal);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public ChronoZonedDateTime<MinguoDate> zonedDateTime(Instant instant, ZoneId zone) {
+ return (ChronoZonedDateTime<MinguoDate>)super.zonedDateTime(instant, zone);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if the specified year is a leap year.
+ * <p>
+ * Minguo leap years occur exactly in line with ISO leap years.
+ * This method does not validate the year passed in, and only has a
+ * well-defined result for years in the supported range.
+ *
+ * @param prolepticYear the proleptic-year to check, not validated for range
+ * @return true if the year is a leap year
+ */
+ @Override
+ public boolean isLeapYear(long prolepticYear) {
+ return IsoChronology.INSTANCE.isLeapYear(prolepticYear + YEARS_DIFFERENCE);
+ }
+
+ @Override
+ public int prolepticYear(Era era, int yearOfEra) {
+ if (era instanceof MinguoEra == false) {
+ throw new ClassCastException("Era must be MinguoEra");
+ }
+ return (era == MinguoEra.ROC ? yearOfEra : 1 - yearOfEra);
+ }
+
+ @Override
+ public MinguoEra eraOf(int eraValue) {
+ return MinguoEra.of(eraValue);
+ }
+
+ @Override
+ public List<Era> eras() {
+ return Arrays.<Era>asList(MinguoEra.values());
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ public ValueRange range(ChronoField field) {
+ switch (field) {
+ case PROLEPTIC_MONTH: {
+ ValueRange range = PROLEPTIC_MONTH.range();
+ return ValueRange.of(range.getMinimum() - YEARS_DIFFERENCE * 12L, range.getMaximum() - YEARS_DIFFERENCE * 12L);
+ }
+ case YEAR_OF_ERA: {
+ ValueRange range = YEAR.range();
+ return ValueRange.of(1, range.getMaximum() - YEARS_DIFFERENCE, -range.getMinimum() + 1 + YEARS_DIFFERENCE);
+ }
+ case YEAR: {
+ ValueRange range = YEAR.range();
+ return ValueRange.of(range.getMinimum() - YEARS_DIFFERENCE, range.getMaximum() - YEARS_DIFFERENCE);
+ }
+ }
+ return field.range();
+ }
+
+ //-----------------------------------------------------------------------
+ @Override // override for return type
+ public MinguoDate resolveDate(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
+ return (MinguoDate) super.resolveDate(fieldValues, resolverStyle);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Writes the Chronology using a
+ * <a href="../../../serialized-form.html#java.time.chrono.Ser">dedicated serialized form</a>.
+ * @serialData
+ * <pre>
+ * out.writeByte(1); // identifies a Chronology
+ * out.writeUTF(getId());
+ * </pre>
+ *
+ * @return the instance of {@code Ser}, not null
+ */
+ @Override
+ Object writeReplace() {
+ return super.writeReplace();
+ }
+
+ /**
+ * Defend against malicious streams.
+ *
+ * @param s the stream to read
+ * @throws InvalidObjectException always
+ */
+ private void readObject(ObjectInputStream s) throws InvalidObjectException {
+ throw new InvalidObjectException("Deserialization via serialization delegate");
+ }
+}
diff --git a/java/time/chrono/MinguoDate.java b/java/time/chrono/MinguoDate.java
new file mode 100644
index 0000000..71e0a12
--- /dev/null
+++ b/java/time/chrono/MinguoDate.java
@@ -0,0 +1,513 @@
+/*
+ * Copyright (c) 2012, 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) 2012, 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.chrono.MinguoChronology.YEARS_DIFFERENCE;
+import static java.time.temporal.ChronoField.DAY_OF_MONTH;
+import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
+import static java.time.temporal.ChronoField.YEAR;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.time.Clock;
+import java.time.DateTimeException;
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.Period;
+import java.time.ZoneId;
+import java.time.temporal.ChronoField;
+import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalAdjuster;
+import java.time.temporal.TemporalAmount;
+import java.time.temporal.TemporalField;
+import java.time.temporal.TemporalQuery;
+import java.time.temporal.TemporalUnit;
+import java.time.temporal.UnsupportedTemporalTypeException;
+import java.time.temporal.ValueRange;
+import java.util.Objects;
+
+// Android-changed: removed ValueBased paragraph.
+/**
+ * A date in the Minguo calendar system.
+ * <p>
+ * This date operates using the {@linkplain MinguoChronology Minguo calendar}.
+ * This calendar system is primarily used in the Republic of China, often known as Taiwan.
+ * Dates are aligned such that {@code 0001-01-01 (Minguo)} is {@code 1912-01-01 (ISO)}.
+ *
+ * @implSpec
+ * This class is immutable and thread-safe.
+ *
+ * @since 1.8
+ */
+public final class MinguoDate
+ extends ChronoLocalDateImpl<MinguoDate>
+ implements ChronoLocalDate, Serializable {
+
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = 1300372329181994526L;
+
+ /**
+ * The underlying date.
+ */
+ private final transient LocalDate isoDate;
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains the current {@code MinguoDate} from the system clock in the default time-zone.
+ * <p>
+ * This will query the {@link Clock#systemDefaultZone() system clock} in the default
+ * time-zone to obtain the current date.
+ * <p>
+ * Using this method will prevent the ability to use an alternate clock for testing
+ * because the clock is hard-coded.
+ *
+ * @return the current date using the system clock and default time-zone, not null
+ */
+ public static MinguoDate now() {
+ return now(Clock.systemDefaultZone());
+ }
+
+ /**
+ * Obtains the current {@code MinguoDate} from the system clock in the specified time-zone.
+ * <p>
+ * This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date.
+ * Specifying the time-zone avoids dependence on the default time-zone.
+ * <p>
+ * Using this method will prevent the ability to use an alternate clock for testing
+ * because the clock is hard-coded.
+ *
+ * @param zone the zone ID to use, not null
+ * @return the current date using the system clock, not null
+ */
+ public static MinguoDate now(ZoneId zone) {
+ return now(Clock.system(zone));
+ }
+
+ /**
+ * Obtains the current {@code MinguoDate} from the specified clock.
+ * <p>
+ * This will query the specified clock to obtain the current date - today.
+ * Using this method allows the use of an alternate clock for testing.
+ * The alternate clock may be introduced using {@linkplain Clock dependency injection}.
+ *
+ * @param clock the clock to use, not null
+ * @return the current date, not null
+ * @throws DateTimeException if the current date cannot be obtained
+ */
+ public static MinguoDate now(Clock clock) {
+ return new MinguoDate(LocalDate.now(clock));
+ }
+
+ /**
+ * Obtains a {@code MinguoDate} representing a date in the Minguo calendar
+ * system from the proleptic-year, month-of-year and day-of-month fields.
+ * <p>
+ * This returns a {@code MinguoDate} with the specified fields.
+ * The day must be valid for the year and month, otherwise an exception will be thrown.
+ *
+ * @param prolepticYear the Minguo proleptic-year
+ * @param month the Minguo month-of-year, from 1 to 12
+ * @param dayOfMonth the Minguo day-of-month, from 1 to 31
+ * @return the date in Minguo calendar system, not null
+ * @throws DateTimeException if the value of any field is out of range,
+ * or if the day-of-month is invalid for the month-year
+ */
+ public static MinguoDate of(int prolepticYear, int month, int dayOfMonth) {
+ return new MinguoDate(LocalDate.of(prolepticYear + YEARS_DIFFERENCE, month, dayOfMonth));
+ }
+
+ /**
+ * Obtains a {@code MinguoDate} from a temporal object.
+ * <p>
+ * This obtains a date in the Minguo calendar system based on the specified temporal.
+ * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
+ * which this factory converts to an instance of {@code MinguoDate}.
+ * <p>
+ * The conversion typically uses the {@link ChronoField#EPOCH_DAY EPOCH_DAY}
+ * field, which is standardized across calendar systems.
+ * <p>
+ * This method matches the signature of the functional interface {@link TemporalQuery}
+ * allowing it to be used as a query via method reference, {@code MinguoDate::from}.
+ *
+ * @param temporal the temporal object to convert, not null
+ * @return the date in Minguo calendar system, not null
+ * @throws DateTimeException if unable to convert to a {@code MinguoDate}
+ */
+ public static MinguoDate from(TemporalAccessor temporal) {
+ return MinguoChronology.INSTANCE.date(temporal);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Creates an instance from an ISO date.
+ *
+ * @param isoDate the standard local date, validated not null
+ */
+ MinguoDate(LocalDate isoDate) {
+ Objects.requireNonNull(isoDate, "isoDate");
+ this.isoDate = isoDate;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the chronology of this date, which is the Minguo calendar system.
+ * <p>
+ * The {@code Chronology} represents the calendar system in use.
+ * The era and other fields in {@link ChronoField} are defined by the chronology.
+ *
+ * @return the Minguo chronology, not null
+ */
+ @Override
+ public MinguoChronology getChronology() {
+ return MinguoChronology.INSTANCE;
+ }
+
+ /**
+ * Gets the era applicable at this date.
+ * <p>
+ * The Minguo calendar system has two eras, 'ROC' and 'BEFORE_ROC',
+ * defined by {@link MinguoEra}.
+ *
+ * @return the era applicable at this date, not null
+ */
+ @Override
+ public MinguoEra getEra() {
+ return (getProlepticYear() >= 1 ? MinguoEra.ROC : MinguoEra.BEFORE_ROC);
+ }
+
+ /**
+ * Returns the length of the month represented by this date.
+ * <p>
+ * This returns the length of the month in days.
+ * Month lengths match those of the ISO calendar system.
+ *
+ * @return the length of the month in days
+ */
+ @Override
+ public int lengthOfMonth() {
+ return isoDate.lengthOfMonth();
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ public ValueRange range(TemporalField field) {
+ if (field instanceof ChronoField) {
+ if (isSupported(field)) {
+ ChronoField f = (ChronoField) field;
+ switch (f) {
+ case DAY_OF_MONTH:
+ case DAY_OF_YEAR:
+ case ALIGNED_WEEK_OF_MONTH:
+ return isoDate.range(field);
+ case YEAR_OF_ERA: {
+ ValueRange range = YEAR.range();
+ long max = (getProlepticYear() <= 0 ? -range.getMinimum() + 1 + YEARS_DIFFERENCE : range.getMaximum() - YEARS_DIFFERENCE);
+ return ValueRange.of(1, max);
+ }
+ }
+ return getChronology().range(f);
+ }
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
+ }
+ return field.rangeRefinedBy(this);
+ }
+
+ @Override
+ public long getLong(TemporalField field) {
+ if (field instanceof ChronoField) {
+ switch ((ChronoField) field) {
+ case PROLEPTIC_MONTH:
+ return getProlepticMonth();
+ case YEAR_OF_ERA: {
+ int prolepticYear = getProlepticYear();
+ return (prolepticYear >= 1 ? prolepticYear : 1 - prolepticYear);
+ }
+ case YEAR:
+ return getProlepticYear();
+ case ERA:
+ return (getProlepticYear() >= 1 ? 1 : 0);
+ }
+ return isoDate.getLong(field);
+ }
+ return field.getFrom(this);
+ }
+
+ private long getProlepticMonth() {
+ return getProlepticYear() * 12L + isoDate.getMonthValue() - 1;
+ }
+
+ private int getProlepticYear() {
+ return isoDate.getYear() - YEARS_DIFFERENCE;
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ public MinguoDate with(TemporalField field, long newValue) {
+ if (field instanceof ChronoField) {
+ ChronoField f = (ChronoField) field;
+ if (getLong(f) == newValue) {
+ return this;
+ }
+ switch (f) {
+ case PROLEPTIC_MONTH:
+ getChronology().range(f).checkValidValue(newValue, f);
+ return plusMonths(newValue - getProlepticMonth());
+ case YEAR_OF_ERA:
+ case YEAR:
+ case ERA: {
+ int nvalue = getChronology().range(f).checkValidIntValue(newValue, f);
+ switch (f) {
+ case YEAR_OF_ERA:
+ return with(isoDate.withYear(getProlepticYear() >= 1 ? nvalue + YEARS_DIFFERENCE : (1 - nvalue) + YEARS_DIFFERENCE));
+ case YEAR:
+ return with(isoDate.withYear(nvalue + YEARS_DIFFERENCE));
+ case ERA:
+ return with(isoDate.withYear((1 - getProlepticYear()) + YEARS_DIFFERENCE));
+ }
+ }
+ }
+ return with(isoDate.with(field, newValue));
+ }
+ return super.with(field, newValue);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws DateTimeException {@inheritDoc}
+ * @throws ArithmeticException {@inheritDoc}
+ */
+ @Override
+ public MinguoDate with(TemporalAdjuster adjuster) {
+ return super.with(adjuster);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws DateTimeException {@inheritDoc}
+ * @throws ArithmeticException {@inheritDoc}
+ */
+ @Override
+ public MinguoDate plus(TemporalAmount amount) {
+ return super.plus(amount);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws DateTimeException {@inheritDoc}
+ * @throws ArithmeticException {@inheritDoc}
+ */
+ @Override
+ public MinguoDate minus(TemporalAmount amount) {
+ return super.minus(amount);
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ MinguoDate plusYears(long years) {
+ return with(isoDate.plusYears(years));
+ }
+
+ @Override
+ MinguoDate plusMonths(long months) {
+ return with(isoDate.plusMonths(months));
+ }
+
+ @Override
+ MinguoDate plusWeeks(long weeksToAdd) {
+ return super.plusWeeks(weeksToAdd);
+ }
+
+ @Override
+ MinguoDate plusDays(long days) {
+ return with(isoDate.plusDays(days));
+ }
+
+ @Override
+ public MinguoDate plus(long amountToAdd, TemporalUnit unit) {
+ return super.plus(amountToAdd, unit);
+ }
+
+ @Override
+ public MinguoDate minus(long amountToAdd, TemporalUnit unit) {
+ return super.minus(amountToAdd, unit);
+ }
+
+ @Override
+ MinguoDate minusYears(long yearsToSubtract) {
+ return super.minusYears(yearsToSubtract);
+ }
+
+ @Override
+ MinguoDate minusMonths(long monthsToSubtract) {
+ return super.minusMonths(monthsToSubtract);
+ }
+
+ @Override
+ MinguoDate minusWeeks(long weeksToSubtract) {
+ return super.minusWeeks(weeksToSubtract);
+ }
+
+ @Override
+ MinguoDate minusDays(long daysToSubtract) {
+ return super.minusDays(daysToSubtract);
+ }
+
+ private MinguoDate with(LocalDate newDate) {
+ return (newDate.equals(isoDate) ? this : new MinguoDate(newDate));
+ }
+
+ @Override // for javadoc and covariant return type
+ @SuppressWarnings("unchecked")
+ public final ChronoLocalDateTime<MinguoDate> atTime(LocalTime localTime) {
+ return (ChronoLocalDateTime<MinguoDate>)super.atTime(localTime);
+ }
+
+ @Override
+ public ChronoPeriod until(ChronoLocalDate endDate) {
+ Period period = isoDate.until(endDate);
+ return getChronology().period(period.getYears(), period.getMonths(), period.getDays());
+ }
+
+ @Override // override for performance
+ public long toEpochDay() {
+ return isoDate.toEpochDay();
+ }
+
+ //-------------------------------------------------------------------------
+ /**
+ * Compares this date to another date, including the chronology.
+ * <p>
+ * Compares this {@code MinguoDate} with another ensuring that the date is the same.
+ * <p>
+ * Only objects of type {@code MinguoDate} are compared, other types return false.
+ * To compare the dates of two {@code TemporalAccessor} instances, including dates
+ * in two different chronologies, use {@link ChronoField#EPOCH_DAY} as a comparator.
+ *
+ * @param obj the object to check, null returns false
+ * @return true if this is equal to the other date
+ */
+ @Override // override for performance
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof MinguoDate) {
+ MinguoDate otherDate = (MinguoDate) obj;
+ return this.isoDate.equals(otherDate.isoDate);
+ }
+ return false;
+ }
+
+ /**
+ * A hash code for this date.
+ *
+ * @return a suitable hash code based only on the Chronology and the date
+ */
+ @Override // override for performance
+ public int hashCode() {
+ return getChronology().getId().hashCode() ^ isoDate.hashCode();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Defend against malicious streams.
+ *
+ * @param s the stream to read
+ * @throws InvalidObjectException always
+ */
+ private void readObject(ObjectInputStream s) throws InvalidObjectException {
+ throw new InvalidObjectException("Deserialization via serialization delegate");
+ }
+
+ /**
+ * Writes the object using a
+ * <a href="../../../serialized-form.html#java.time.chrono.Ser">dedicated serialized form</a>.
+ * @serialData
+ * <pre>
+ * out.writeByte(8); // identifies a MinguoDate
+ * out.writeInt(get(YEAR));
+ * out.writeByte(get(MONTH_OF_YEAR));
+ * out.writeByte(get(DAY_OF_MONTH));
+ * </pre>
+ *
+ * @return the instance of {@code Ser}, not null
+ */
+ private Object writeReplace() {
+ return new Ser(Ser.MINGUO_DATE_TYPE, this);
+ }
+
+ void writeExternal(DataOutput out) throws IOException {
+ // MinguoChronology is implicit in the MINGUO_DATE_TYPE
+ out.writeInt(get(YEAR));
+ out.writeByte(get(MONTH_OF_YEAR));
+ out.writeByte(get(DAY_OF_MONTH));
+ }
+
+ static MinguoDate readExternal(DataInput in) throws IOException {
+ int year = in.readInt();
+ int month = in.readByte();
+ int dayOfMonth = in.readByte();
+ return MinguoChronology.INSTANCE.date(year, month, dayOfMonth);
+ }
+
+}
diff --git a/java/time/chrono/MinguoEra.java b/java/time/chrono/MinguoEra.java
new file mode 100644
index 0000000..fd96de1
--- /dev/null
+++ b/java/time/chrono/MinguoEra.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2012, 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) 2012, 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;
+
+/**
+ * An era in the Minguo calendar system.
+ * <p>
+ * The Minguo calendar system has two eras.
+ * The current era, for years from 1 onwards, is known as the 'Republic of China' era.
+ * All previous years, zero or earlier in the proleptic count or one and greater
+ * in the year-of-era count, are part of the 'Before Republic of China' era.
+ *
+ * <table summary="Minguo years and eras" cellpadding="2" cellspacing="3" border="0" >
+ * <thead>
+ * <tr class="tableSubHeadingColor">
+ * <th class="colFirst" align="left">year-of-era</th>
+ * <th class="colFirst" align="left">era</th>
+ * <th class="colFirst" align="left">proleptic-year</th>
+ * <th class="colLast" align="left">ISO proleptic-year</th>
+ * </tr>
+ * </thead>
+ * <tbody>
+ * <tr class="rowColor">
+ * <td>2</td><td>ROC</td><td>2</td><td>1913</td>
+ * </tr>
+ * <tr class="altColor">
+ * <td>1</td><td>ROC</td><td>1</td><td>1912</td>
+ * </tr>
+ * <tr class="rowColor">
+ * <td>1</td><td>BEFORE_ROC</td><td>0</td><td>1911</td>
+ * </tr>
+ * <tr class="altColor">
+ * <td>2</td><td>BEFORE_ROC</td><td>-1</td><td>1910</td>
+ * </tr>
+ * </tbody>
+ * </table>
+ * <p>
+ * <b>Do not use {@code ordinal()} to obtain the numeric representation of {@code MinguoEra}.
+ * Use {@code getValue()} instead.</b>
+ *
+ * @implSpec
+ * This is an immutable and thread-safe enum.
+ *
+ * @since 1.8
+ */
+public enum MinguoEra implements Era {
+
+ /**
+ * The singleton instance for the era before the current one, 'Before Republic of China Era',
+ * which has the numeric value 0.
+ */
+ BEFORE_ROC,
+ /**
+ * The singleton instance for the current era, 'Republic of China Era',
+ * which has the numeric value 1.
+ */
+ ROC;
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code MinguoEra} from an {@code int} value.
+ * <p>
+ * {@code MinguoEra} is an enum representing the Minguo eras of BEFORE_ROC/ROC.
+ * This factory allows the enum to be obtained from the {@code int} value.
+ *
+ * @param minguoEra the BEFORE_ROC/ROC value to represent, from 0 (BEFORE_ROC) to 1 (ROC)
+ * @return the era singleton, not null
+ * @throws DateTimeException if the value is invalid
+ */
+ public static MinguoEra of(int minguoEra) {
+ switch (minguoEra) {
+ case 0:
+ return BEFORE_ROC;
+ case 1:
+ return ROC;
+ default:
+ throw new DateTimeException("Invalid era: " + minguoEra);
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the numeric era {@code int} value.
+ * <p>
+ * The era BEFORE_ROC has the value 0, while the era ROC has the value 1.
+ *
+ * @return the era value, from 0 (BEFORE_ROC) to 1 (ROC)
+ */
+ @Override
+ public int getValue() {
+ return ordinal();
+ }
+
+}
diff --git a/java/time/chrono/Ser.java b/java/time/chrono/Ser.java
new file mode 100644
index 0000000..317a769
--- /dev/null
+++ b/java/time/chrono/Ser.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (c) 2012, 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) 2011-2012, 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.io.Externalizable;
+import java.io.IOException;
+import java.io.InvalidClassException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.io.StreamCorruptedException;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+/**
+ * The shared serialization delegate for this package.
+ *
+ * @implNote
+ * This class wraps the object being serialized, and takes a byte representing the type of the class to
+ * be serialized. This byte can also be used for versioning the serialization format. In this case another
+ * byte flag would be used in order to specify an alternative version of the type format.
+ * For example {@code CHRONO_TYPE_VERSION_2 = 21}
+ * <p>
+ * In order to serialize the object it writes its byte and then calls back to the appropriate class where
+ * the serialization is performed. In order to deserialize the object it read in the type byte, switching
+ * in order to select which class to call back into.
+ * <p>
+ * The serialization format is determined on a per class basis. In the case of field based classes each
+ * of the fields is written out with an appropriate size format in descending order of the field's size. For
+ * example in the case of {@link LocalDate} year is written before month. Composite classes, such as
+ * {@link LocalDateTime} are serialized as one object. Enum classes are serialized using the index of their
+ * element.
+ * <p>
+ * This class is mutable and should be created once per serialization.
+ *
+ * @serial include
+ * @since 1.8
+ */
+final class Ser implements Externalizable {
+
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = -6103370247208168577L;
+
+ static final byte CHRONO_TYPE = 1;
+ static final byte CHRONO_LOCAL_DATE_TIME_TYPE = 2;
+ static final byte CHRONO_ZONE_DATE_TIME_TYPE = 3;
+ static final byte JAPANESE_DATE_TYPE = 4;
+ static final byte JAPANESE_ERA_TYPE = 5;
+ static final byte HIJRAH_DATE_TYPE = 6;
+ static final byte MINGUO_DATE_TYPE = 7;
+ static final byte THAIBUDDHIST_DATE_TYPE = 8;
+ static final byte CHRONO_PERIOD_TYPE = 9;
+
+ /** The type being serialized. */
+ private byte type;
+ /** The object being serialized. */
+ private Object object;
+
+ /**
+ * Constructor for deserialization.
+ */
+ public Ser() {
+ }
+
+ /**
+ * Creates an instance for serialization.
+ *
+ * @param type the type
+ * @param object the object
+ */
+ Ser(byte type, Object object) {
+ this.type = type;
+ this.object = object;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Implements the {@code Externalizable} interface to write the object.
+ * @serialData
+ * Each serializable class is mapped to a type that is the first byte
+ * in the stream. Refer to each class {@code writeReplace}
+ * serialized form for the value of the type and sequence of values for the type.
+ * <ul>
+ * <li><a href="../../../serialized-form.html#java.time.chrono.HijrahChronology">HijrahChronology.writeReplace</a>
+ * <li><a href="../../../serialized-form.html#java.time.chrono.IsoChronology">IsoChronology.writeReplace</a>
+ * <li><a href="../../../serialized-form.html#java.time.chrono.JapaneseChronology">JapaneseChronology.writeReplace</a>
+ * <li><a href="../../../serialized-form.html#java.time.chrono.MinguoChronology">MinguoChronology.writeReplace</a>
+ * <li><a href="../../../serialized-form.html#java.time.chrono.ThaiBuddhistChronology">ThaiBuddhistChronology.writeReplace</a>
+ * <li><a href="../../../serialized-form.html#java.time.chrono.ChronoLocalDateTimeImpl">ChronoLocalDateTime.writeReplace</a>
+ * <li><a href="../../../serialized-form.html#java.time.chrono.ChronoZonedDateTimeImpl">ChronoZonedDateTime.writeReplace</a>
+ * <li><a href="../../../serialized-form.html#java.time.chrono.JapaneseDate">JapaneseDate.writeReplace</a>
+ * <li><a href="../../../serialized-form.html#java.time.chrono.JapaneseEra">JapaneseEra.writeReplace</a>
+ * <li><a href="../../../serialized-form.html#java.time.chrono.HijrahDate">HijrahDate.writeReplace</a>
+ * <li><a href="../../../serialized-form.html#java.time.chrono.MinguoDate">MinguoDate.writeReplace</a>
+ * <li><a href="../../../serialized-form.html#java.time.chrono.ThaiBuddhistDate">ThaiBuddhistDate.writeReplace</a>
+ * </ul>
+ *
+ * @param out the data stream to write to, not null
+ */
+ @Override
+ public void writeExternal(ObjectOutput out) throws IOException {
+ writeInternal(type, object, out);
+ }
+
+ private static void writeInternal(byte type, Object object, ObjectOutput out) throws IOException {
+ out.writeByte(type);
+ switch (type) {
+ case CHRONO_TYPE:
+ ((AbstractChronology) object).writeExternal(out);
+ break;
+ case CHRONO_LOCAL_DATE_TIME_TYPE:
+ ((ChronoLocalDateTimeImpl<?>) object).writeExternal(out);
+ break;
+ case CHRONO_ZONE_DATE_TIME_TYPE:
+ ((ChronoZonedDateTimeImpl<?>) object).writeExternal(out);
+ break;
+ case JAPANESE_DATE_TYPE:
+ ((JapaneseDate) object).writeExternal(out);
+ break;
+ case JAPANESE_ERA_TYPE:
+ ((JapaneseEra) object).writeExternal(out);
+ break;
+ case HIJRAH_DATE_TYPE:
+ ((HijrahDate) object).writeExternal(out);
+ break;
+ case MINGUO_DATE_TYPE:
+ ((MinguoDate) object).writeExternal(out);
+ break;
+ case THAIBUDDHIST_DATE_TYPE:
+ ((ThaiBuddhistDate) object).writeExternal(out);
+ break;
+ case CHRONO_PERIOD_TYPE:
+ ((ChronoPeriodImpl) object).writeExternal(out);
+ break;
+ default:
+ throw new InvalidClassException("Unknown serialized type");
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Implements the {@code Externalizable} interface to read the object.
+ * @serialData
+ * The streamed type and parameters defined by the type's {@code writeReplace}
+ * method are read and passed to the corresponding static factory for the type
+ * to create a new instance. That instance is returned as the de-serialized
+ * {@code Ser} object.
+ *
+ * <ul>
+ * <li><a href="../../../serialized-form.html#java.time.chrono.HijrahChronology">HijrahChronology</a> - Chronology.of(id)
+ * <li><a href="../../../serialized-form.html#java.time.chrono.IsoChronology">IsoChronology</a> - Chronology.of(id)
+ * <li><a href="../../../serialized-form.html#java.time.chrono.JapaneseChronology">JapaneseChronology</a> - Chronology.of(id)
+ * <li><a href="../../../serialized-form.html#java.time.chrono.MinguoChronology">MinguoChronology</a> - Chronology.of(id)
+ * <li><a href="../../../serialized-form.html#java.time.chrono.ThaiBuddhistChronology">ThaiBuddhistChronology</a> - Chronology.of(id)
+ * <li><a href="../../../serialized-form.html#java.time.chrono.ChronoLocalDateTimeImpl">ChronoLocalDateTime</a> - date.atTime(time)
+ * <li><a href="../../../serialized-form.html#java.time.chrono.ChronoZonedDateTimeImpl">ChronoZonedDateTime</a> - dateTime.atZone(offset).withZoneSameLocal(zone)
+ * <li><a href="../../../serialized-form.html#java.time.chrono.JapaneseDate">JapaneseDate</a> - JapaneseChronology.INSTANCE.date(year, month, dayOfMonth)
+ * <li><a href="../../../serialized-form.html#java.time.chrono.JapaneseEra">JapaneseEra</a> - JapaneseEra.of(eraValue)
+ * <li><a href="../../../serialized-form.html#java.time.chrono.HijrahDate">HijrahDate</a> - HijrahChronology chrono.date(year, month, dayOfMonth)
+ * <li><a href="../../../serialized-form.html#java.time.chrono.MinguoDate">MinguoDate</a> - MinguoChronology.INSTANCE.date(year, month, dayOfMonth)
+ * <li><a href="../../../serialized-form.html#java.time.chrono.ThaiBuddhistDate">ThaiBuddhistDate</a> - ThaiBuddhistChronology.INSTANCE.date(year, month, dayOfMonth)
+ * </ul>
+ *
+ * @param in the data stream to read from, not null
+ */
+ @Override
+ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+ type = in.readByte();
+ object = readInternal(type, in);
+ }
+
+ static Object read(ObjectInput in) throws IOException, ClassNotFoundException {
+ byte type = in.readByte();
+ return readInternal(type, in);
+ }
+
+ private static Object readInternal(byte type, ObjectInput in) throws IOException, ClassNotFoundException {
+ switch (type) {
+ case CHRONO_TYPE: return AbstractChronology.readExternal(in);
+ case CHRONO_LOCAL_DATE_TIME_TYPE: return ChronoLocalDateTimeImpl.readExternal(in);
+ case CHRONO_ZONE_DATE_TIME_TYPE: return ChronoZonedDateTimeImpl.readExternal(in);
+ case JAPANESE_DATE_TYPE: return JapaneseDate.readExternal(in);
+ case JAPANESE_ERA_TYPE: return JapaneseEra.readExternal(in);
+ case HIJRAH_DATE_TYPE: return HijrahDate.readExternal(in);
+ case MINGUO_DATE_TYPE: return MinguoDate.readExternal(in);
+ case THAIBUDDHIST_DATE_TYPE: return ThaiBuddhistDate.readExternal(in);
+ case CHRONO_PERIOD_TYPE: return ChronoPeriodImpl.readExternal(in);
+ default: throw new StreamCorruptedException("Unknown serialized type");
+ }
+ }
+
+ /**
+ * Returns the object that will replace this one.
+ *
+ * @return the read object, should never be null
+ */
+ private Object readResolve() {
+ return object;
+ }
+
+}
diff --git a/java/time/chrono/ThaiBuddhistChronology.java b/java/time/chrono/ThaiBuddhistChronology.java
new file mode 100644
index 0000000..5b96c85
--- /dev/null
+++ b/java/time/chrono/ThaiBuddhistChronology.java
@@ -0,0 +1,400 @@
+/*
+ * Copyright (c) 2012, 2015, 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) 2012, 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.io.InvalidObjectException;
+import static java.time.temporal.ChronoField.PROLEPTIC_MONTH;
+import static java.time.temporal.ChronoField.YEAR;
+
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.time.Clock;
+import java.time.DateTimeException;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.time.format.ResolverStyle;
+import java.time.temporal.ChronoField;
+import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalField;
+import java.time.temporal.ValueRange;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * The Thai Buddhist calendar system.
+ * <p>
+ * This chronology defines the rules of the Thai Buddhist calendar system.
+ * This calendar system is primarily used in Thailand.
+ * Dates are aligned such that {@code 2484-01-01 (Buddhist)} is {@code 1941-01-01 (ISO)}.
+ * <p>
+ * The fields are defined as follows:
+ * <ul>
+ * <li>era - There are two eras, the current 'Buddhist' (ERA_BE) and the previous era (ERA_BEFORE_BE).
+ * <li>year-of-era - The year-of-era for the current era increases uniformly from the epoch at year one.
+ * For the previous era the year increases from one as time goes backwards.
+ * The value for the current era is equal to the ISO proleptic-year plus 543.
+ * <li>proleptic-year - The proleptic year is the same as the year-of-era for the
+ * current era. For the previous era, years have zero, then negative values.
+ * The value is equal to the ISO proleptic-year plus 543.
+ * <li>month-of-year - The ThaiBuddhist month-of-year exactly matches ISO.
+ * <li>day-of-month - The ThaiBuddhist day-of-month exactly matches ISO.
+ * <li>day-of-year - The ThaiBuddhist day-of-year exactly matches ISO.
+ * <li>leap-year - The ThaiBuddhist leap-year pattern exactly matches ISO, such that the two calendars
+ * are never out of step.
+ * </ul>
+ *
+ * @implSpec
+ * This class is immutable and thread-safe.
+ *
+ * @since 1.8
+ */
+public final class ThaiBuddhistChronology extends AbstractChronology implements Serializable {
+
+ /**
+ * Singleton instance of the Buddhist chronology.
+ */
+ public static final ThaiBuddhistChronology INSTANCE = new ThaiBuddhistChronology();
+
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = 2775954514031616474L;
+ /**
+ * Containing the offset to add to the ISO year.
+ */
+ static final int YEARS_DIFFERENCE = 543;
+ /**
+ * Narrow names for eras.
+ */
+ private static final HashMap<String, String[]> ERA_NARROW_NAMES = new HashMap<>();
+ /**
+ * Short names for eras.
+ */
+ private static final HashMap<String, String[]> ERA_SHORT_NAMES = new HashMap<>();
+ /**
+ * Full names for eras.
+ */
+ private static final HashMap<String, String[]> ERA_FULL_NAMES = new HashMap<>();
+ /**
+ * Fallback language for the era names.
+ */
+ private static final String FALLBACK_LANGUAGE = "en";
+ /**
+ * Language that has the era names.
+ */
+ private static final String TARGET_LANGUAGE = "th";
+ /**
+ * Name data.
+ */
+ static {
+ ERA_NARROW_NAMES.put(FALLBACK_LANGUAGE, new String[]{"BB", "BE"});
+ ERA_NARROW_NAMES.put(TARGET_LANGUAGE, new String[]{"BB", "BE"});
+ ERA_SHORT_NAMES.put(FALLBACK_LANGUAGE, new String[]{"B.B.", "B.E."});
+ ERA_SHORT_NAMES.put(TARGET_LANGUAGE,
+ new String[]{"\u0e1e.\u0e28.",
+ "\u0e1b\u0e35\u0e01\u0e48\u0e2d\u0e19\u0e04\u0e23\u0e34\u0e2a\u0e15\u0e4c\u0e01\u0e32\u0e25\u0e17\u0e35\u0e48"});
+ ERA_FULL_NAMES.put(FALLBACK_LANGUAGE, new String[]{"Before Buddhist", "Budhhist Era"});
+ ERA_FULL_NAMES.put(TARGET_LANGUAGE,
+ new String[]{"\u0e1e\u0e38\u0e17\u0e18\u0e28\u0e31\u0e01\u0e23\u0e32\u0e0a",
+ "\u0e1b\u0e35\u0e01\u0e48\u0e2d\u0e19\u0e04\u0e23\u0e34\u0e2a\u0e15\u0e4c\u0e01\u0e32\u0e25\u0e17\u0e35\u0e48"});
+ }
+
+ /**
+ * Restricted constructor.
+ */
+ private ThaiBuddhistChronology() {
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the ID of the chronology - 'ThaiBuddhist'.
+ * <p>
+ * The ID uniquely identifies the {@code Chronology}.
+ * It can be used to lookup the {@code Chronology} using {@link Chronology#of(String)}.
+ *
+ * @return the chronology ID - 'ThaiBuddhist'
+ * @see #getCalendarType()
+ */
+ @Override
+ public String getId() {
+ return "ThaiBuddhist";
+ }
+
+ /**
+ * Gets the calendar type of the underlying calendar system - 'buddhist'.
+ * <p>
+ * The calendar type is an identifier defined by the
+ * <em>Unicode Locale Data Markup Language (LDML)</em> specification.
+ * It can be used to lookup the {@code Chronology} using {@link Chronology#of(String)}.
+ * It can also be used as part of a locale, accessible via
+ * {@link Locale#getUnicodeLocaleType(String)} with the key 'ca'.
+ *
+ * @return the calendar system type - 'buddhist'
+ * @see #getId()
+ */
+ @Override
+ public String getCalendarType() {
+ return "buddhist";
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains a local date in Thai Buddhist calendar system from the
+ * era, year-of-era, month-of-year and day-of-month fields.
+ *
+ * @param era the Thai Buddhist era, not null
+ * @param yearOfEra the year-of-era
+ * @param month the month-of-year
+ * @param dayOfMonth the day-of-month
+ * @return the Thai Buddhist local date, not null
+ * @throws DateTimeException if unable to create the date
+ * @throws ClassCastException if the {@code era} is not a {@code ThaiBuddhistEra}
+ */
+ @Override
+ public ThaiBuddhistDate date(Era era, int yearOfEra, int month, int dayOfMonth) {
+ return date(prolepticYear(era, yearOfEra), month, dayOfMonth);
+ }
+
+ /**
+ * Obtains a local date in Thai Buddhist calendar system from the
+ * proleptic-year, month-of-year and day-of-month fields.
+ *
+ * @param prolepticYear the proleptic-year
+ * @param month the month-of-year
+ * @param dayOfMonth the day-of-month
+ * @return the Thai Buddhist local date, not null
+ * @throws DateTimeException if unable to create the date
+ */
+ @Override
+ public ThaiBuddhistDate date(int prolepticYear, int month, int dayOfMonth) {
+ return new ThaiBuddhistDate(LocalDate.of(prolepticYear - YEARS_DIFFERENCE, month, dayOfMonth));
+ }
+
+ /**
+ * Obtains a local date in Thai Buddhist calendar system from the
+ * era, year-of-era and day-of-year fields.
+ *
+ * @param era the Thai Buddhist era, not null
+ * @param yearOfEra the year-of-era
+ * @param dayOfYear the day-of-year
+ * @return the Thai Buddhist local date, not null
+ * @throws DateTimeException if unable to create the date
+ * @throws ClassCastException if the {@code era} is not a {@code ThaiBuddhistEra}
+ */
+ @Override
+ public ThaiBuddhistDate dateYearDay(Era era, int yearOfEra, int dayOfYear) {
+ return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear);
+ }
+
+ /**
+ * Obtains a local date in Thai Buddhist calendar system from the
+ * proleptic-year and day-of-year fields.
+ *
+ * @param prolepticYear the proleptic-year
+ * @param dayOfYear the day-of-year
+ * @return the Thai Buddhist local date, not null
+ * @throws DateTimeException if unable to create the date
+ */
+ @Override
+ public ThaiBuddhistDate dateYearDay(int prolepticYear, int dayOfYear) {
+ return new ThaiBuddhistDate(LocalDate.ofYearDay(prolepticYear - YEARS_DIFFERENCE, dayOfYear));
+ }
+
+ /**
+ * Obtains a local date in the Thai Buddhist calendar system from the epoch-day.
+ *
+ * @param epochDay the epoch day
+ * @return the Thai Buddhist local date, not null
+ * @throws DateTimeException if unable to create the date
+ */
+ @Override // override with covariant return type
+ public ThaiBuddhistDate dateEpochDay(long epochDay) {
+ return new ThaiBuddhistDate(LocalDate.ofEpochDay(epochDay));
+ }
+
+ @Override
+ public ThaiBuddhistDate dateNow() {
+ return dateNow(Clock.systemDefaultZone());
+ }
+
+ @Override
+ public ThaiBuddhistDate dateNow(ZoneId zone) {
+ return dateNow(Clock.system(zone));
+ }
+
+ @Override
+ public ThaiBuddhistDate dateNow(Clock clock) {
+ return date(LocalDate.now(clock));
+ }
+
+ @Override
+ public ThaiBuddhistDate date(TemporalAccessor temporal) {
+ if (temporal instanceof ThaiBuddhistDate) {
+ return (ThaiBuddhistDate) temporal;
+ }
+ return new ThaiBuddhistDate(LocalDate.from(temporal));
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public ChronoLocalDateTime<ThaiBuddhistDate> localDateTime(TemporalAccessor temporal) {
+ return (ChronoLocalDateTime<ThaiBuddhistDate>)super.localDateTime(temporal);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public ChronoZonedDateTime<ThaiBuddhistDate> zonedDateTime(TemporalAccessor temporal) {
+ return (ChronoZonedDateTime<ThaiBuddhistDate>)super.zonedDateTime(temporal);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public ChronoZonedDateTime<ThaiBuddhistDate> zonedDateTime(Instant instant, ZoneId zone) {
+ return (ChronoZonedDateTime<ThaiBuddhistDate>)super.zonedDateTime(instant, zone);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if the specified year is a leap year.
+ * <p>
+ * Thai Buddhist leap years occur exactly in line with ISO leap years.
+ * This method does not validate the year passed in, and only has a
+ * well-defined result for years in the supported range.
+ *
+ * @param prolepticYear the proleptic-year to check, not validated for range
+ * @return true if the year is a leap year
+ */
+ @Override
+ public boolean isLeapYear(long prolepticYear) {
+ return IsoChronology.INSTANCE.isLeapYear(prolepticYear - YEARS_DIFFERENCE);
+ }
+
+ @Override
+ public int prolepticYear(Era era, int yearOfEra) {
+ if (era instanceof ThaiBuddhistEra == false) {
+ throw new ClassCastException("Era must be BuddhistEra");
+ }
+ return (era == ThaiBuddhistEra.BE ? yearOfEra : 1 - yearOfEra);
+ }
+
+ @Override
+ public ThaiBuddhistEra eraOf(int eraValue) {
+ return ThaiBuddhistEra.of(eraValue);
+ }
+
+ @Override
+ public List<Era> eras() {
+ return Arrays.<Era>asList(ThaiBuddhistEra.values());
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ public ValueRange range(ChronoField field) {
+ switch (field) {
+ case PROLEPTIC_MONTH: {
+ ValueRange range = PROLEPTIC_MONTH.range();
+ return ValueRange.of(range.getMinimum() + YEARS_DIFFERENCE * 12L, range.getMaximum() + YEARS_DIFFERENCE * 12L);
+ }
+ case YEAR_OF_ERA: {
+ ValueRange range = YEAR.range();
+ return ValueRange.of(1, -(range.getMinimum() + YEARS_DIFFERENCE) + 1, range.getMaximum() + YEARS_DIFFERENCE);
+ }
+ case YEAR: {
+ ValueRange range = YEAR.range();
+ return ValueRange.of(range.getMinimum() + YEARS_DIFFERENCE, range.getMaximum() + YEARS_DIFFERENCE);
+ }
+ }
+ return field.range();
+ }
+
+ //-----------------------------------------------------------------------
+ @Override // override for return type
+ public ThaiBuddhistDate resolveDate(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
+ return (ThaiBuddhistDate) super.resolveDate(fieldValues, resolverStyle);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Writes the Chronology using a
+ * <a href="../../../serialized-form.html#java.time.chrono.Ser">dedicated serialized form</a>.
+ * @serialData
+ * <pre>
+ * out.writeByte(1); // identifies a Chronology
+ * out.writeUTF(getId());
+ * </pre>
+ *
+ * @return the instance of {@code Ser}, not null
+ */
+ @Override
+ Object writeReplace() {
+ return super.writeReplace();
+ }
+
+ /**
+ * Defend against malicious streams.
+ *
+ * @param s the stream to read
+ * @throws InvalidObjectException always
+ */
+ private void readObject(ObjectInputStream s) throws InvalidObjectException {
+ throw new InvalidObjectException("Deserialization via serialization delegate");
+ }
+}
diff --git a/java/time/chrono/ThaiBuddhistDate.java b/java/time/chrono/ThaiBuddhistDate.java
new file mode 100644
index 0000000..cc58cea
--- /dev/null
+++ b/java/time/chrono/ThaiBuddhistDate.java
@@ -0,0 +1,513 @@
+/*
+ * Copyright (c) 2012, 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) 2012, 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.chrono.ThaiBuddhistChronology.YEARS_DIFFERENCE;
+import static java.time.temporal.ChronoField.DAY_OF_MONTH;
+import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
+import static java.time.temporal.ChronoField.YEAR;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.time.Clock;
+import java.time.DateTimeException;
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.Period;
+import java.time.ZoneId;
+import java.time.temporal.ChronoField;
+import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalAdjuster;
+import java.time.temporal.TemporalAmount;
+import java.time.temporal.TemporalField;
+import java.time.temporal.TemporalQuery;
+import java.time.temporal.TemporalUnit;
+import java.time.temporal.UnsupportedTemporalTypeException;
+import java.time.temporal.ValueRange;
+import java.util.Objects;
+
+// Android-changed: removed ValueBased paragraph.
+/**
+ * A date in the Thai Buddhist calendar system.
+ * <p>
+ * This date operates using the {@linkplain ThaiBuddhistChronology Thai Buddhist calendar}.
+ * This calendar system is primarily used in Thailand.
+ * Dates are aligned such that {@code 2484-01-01 (Buddhist)} is {@code 1941-01-01 (ISO)}.
+ *
+ * @implSpec
+ * This class is immutable and thread-safe.
+ *
+ * @since 1.8
+ */
+public final class ThaiBuddhistDate
+ extends ChronoLocalDateImpl<ThaiBuddhistDate>
+ implements ChronoLocalDate, Serializable {
+
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = -8722293800195731463L;
+
+ /**
+ * The underlying date.
+ */
+ private final transient LocalDate isoDate;
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains the current {@code ThaiBuddhistDate} from the system clock in the default time-zone.
+ * <p>
+ * This will query the {@link Clock#systemDefaultZone() system clock} in the default
+ * time-zone to obtain the current date.
+ * <p>
+ * Using this method will prevent the ability to use an alternate clock for testing
+ * because the clock is hard-coded.
+ *
+ * @return the current date using the system clock and default time-zone, not null
+ */
+ public static ThaiBuddhistDate now() {
+ return now(Clock.systemDefaultZone());
+ }
+
+ /**
+ * Obtains the current {@code ThaiBuddhistDate} from the system clock in the specified time-zone.
+ * <p>
+ * This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date.
+ * Specifying the time-zone avoids dependence on the default time-zone.
+ * <p>
+ * Using this method will prevent the ability to use an alternate clock for testing
+ * because the clock is hard-coded.
+ *
+ * @param zone the zone ID to use, not null
+ * @return the current date using the system clock, not null
+ */
+ public static ThaiBuddhistDate now(ZoneId zone) {
+ return now(Clock.system(zone));
+ }
+
+ /**
+ * Obtains the current {@code ThaiBuddhistDate} from the specified clock.
+ * <p>
+ * This will query the specified clock to obtain the current date - today.
+ * Using this method allows the use of an alternate clock for testing.
+ * The alternate clock may be introduced using {@linkplain Clock dependency injection}.
+ *
+ * @param clock the clock to use, not null
+ * @return the current date, not null
+ * @throws DateTimeException if the current date cannot be obtained
+ */
+ public static ThaiBuddhistDate now(Clock clock) {
+ return new ThaiBuddhistDate(LocalDate.now(clock));
+ }
+
+ /**
+ * Obtains a {@code ThaiBuddhistDate} representing a date in the Thai Buddhist calendar
+ * system from the proleptic-year, month-of-year and day-of-month fields.
+ * <p>
+ * This returns a {@code ThaiBuddhistDate} with the specified fields.
+ * The day must be valid for the year and month, otherwise an exception will be thrown.
+ *
+ * @param prolepticYear the Thai Buddhist proleptic-year
+ * @param month the Thai Buddhist month-of-year, from 1 to 12
+ * @param dayOfMonth the Thai Buddhist day-of-month, from 1 to 31
+ * @return the date in Thai Buddhist calendar system, not null
+ * @throws DateTimeException if the value of any field is out of range,
+ * or if the day-of-month is invalid for the month-year
+ */
+ public static ThaiBuddhistDate of(int prolepticYear, int month, int dayOfMonth) {
+ return new ThaiBuddhistDate(LocalDate.of(prolepticYear - YEARS_DIFFERENCE, month, dayOfMonth));
+ }
+
+ /**
+ * Obtains a {@code ThaiBuddhistDate} from a temporal object.
+ * <p>
+ * This obtains a date in the Thai Buddhist calendar system based on the specified temporal.
+ * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
+ * which this factory converts to an instance of {@code ThaiBuddhistDate}.
+ * <p>
+ * The conversion typically uses the {@link ChronoField#EPOCH_DAY EPOCH_DAY}
+ * field, which is standardized across calendar systems.
+ * <p>
+ * This method matches the signature of the functional interface {@link TemporalQuery}
+ * allowing it to be used as a query via method reference, {@code ThaiBuddhistDate::from}.
+ *
+ * @param temporal the temporal object to convert, not null
+ * @return the date in Thai Buddhist calendar system, not null
+ * @throws DateTimeException if unable to convert to a {@code ThaiBuddhistDate}
+ */
+ public static ThaiBuddhistDate from(TemporalAccessor temporal) {
+ return ThaiBuddhistChronology.INSTANCE.date(temporal);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Creates an instance from an ISO date.
+ *
+ * @param isoDate the standard local date, validated not null
+ */
+ ThaiBuddhistDate(LocalDate isoDate) {
+ Objects.requireNonNull(isoDate, "isoDate");
+ this.isoDate = isoDate;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the chronology of this date, which is the Thai Buddhist calendar system.
+ * <p>
+ * The {@code Chronology} represents the calendar system in use.
+ * The era and other fields in {@link ChronoField} are defined by the chronology.
+ *
+ * @return the Thai Buddhist chronology, not null
+ */
+ @Override
+ public ThaiBuddhistChronology getChronology() {
+ return ThaiBuddhistChronology.INSTANCE;
+ }
+
+ /**
+ * Gets the era applicable at this date.
+ * <p>
+ * The Thai Buddhist calendar system has two eras, 'BE' and 'BEFORE_BE',
+ * defined by {@link ThaiBuddhistEra}.
+ *
+ * @return the era applicable at this date, not null
+ */
+ @Override
+ public ThaiBuddhistEra getEra() {
+ return (getProlepticYear() >= 1 ? ThaiBuddhistEra.BE : ThaiBuddhistEra.BEFORE_BE);
+ }
+
+ /**
+ * Returns the length of the month represented by this date.
+ * <p>
+ * This returns the length of the month in days.
+ * Month lengths match those of the ISO calendar system.
+ *
+ * @return the length of the month in days
+ */
+ @Override
+ public int lengthOfMonth() {
+ return isoDate.lengthOfMonth();
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ public ValueRange range(TemporalField field) {
+ if (field instanceof ChronoField) {
+ if (isSupported(field)) {
+ ChronoField f = (ChronoField) field;
+ switch (f) {
+ case DAY_OF_MONTH:
+ case DAY_OF_YEAR:
+ case ALIGNED_WEEK_OF_MONTH:
+ return isoDate.range(field);
+ case YEAR_OF_ERA: {
+ ValueRange range = YEAR.range();
+ long max = (getProlepticYear() <= 0 ? -(range.getMinimum() + YEARS_DIFFERENCE) + 1 : range.getMaximum() + YEARS_DIFFERENCE);
+ return ValueRange.of(1, max);
+ }
+ }
+ return getChronology().range(f);
+ }
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
+ }
+ return field.rangeRefinedBy(this);
+ }
+
+ @Override
+ public long getLong(TemporalField field) {
+ if (field instanceof ChronoField) {
+ switch ((ChronoField) field) {
+ case PROLEPTIC_MONTH:
+ return getProlepticMonth();
+ case YEAR_OF_ERA: {
+ int prolepticYear = getProlepticYear();
+ return (prolepticYear >= 1 ? prolepticYear : 1 - prolepticYear);
+ }
+ case YEAR:
+ return getProlepticYear();
+ case ERA:
+ return (getProlepticYear() >= 1 ? 1 : 0);
+ }
+ return isoDate.getLong(field);
+ }
+ return field.getFrom(this);
+ }
+
+ private long getProlepticMonth() {
+ return getProlepticYear() * 12L + isoDate.getMonthValue() - 1;
+ }
+
+ private int getProlepticYear() {
+ return isoDate.getYear() + YEARS_DIFFERENCE;
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ public ThaiBuddhistDate with(TemporalField field, long newValue) {
+ if (field instanceof ChronoField) {
+ ChronoField f = (ChronoField) field;
+ if (getLong(f) == newValue) {
+ return this;
+ }
+ switch (f) {
+ case PROLEPTIC_MONTH:
+ getChronology().range(f).checkValidValue(newValue, f);
+ return plusMonths(newValue - getProlepticMonth());
+ case YEAR_OF_ERA:
+ case YEAR:
+ case ERA: {
+ int nvalue = getChronology().range(f).checkValidIntValue(newValue, f);
+ switch (f) {
+ case YEAR_OF_ERA:
+ return with(isoDate.withYear((getProlepticYear() >= 1 ? nvalue : 1 - nvalue) - YEARS_DIFFERENCE));
+ case YEAR:
+ return with(isoDate.withYear(nvalue - YEARS_DIFFERENCE));
+ case ERA:
+ return with(isoDate.withYear((1 - getProlepticYear()) - YEARS_DIFFERENCE));
+ }
+ }
+ }
+ return with(isoDate.with(field, newValue));
+ }
+ return super.with(field, newValue);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws DateTimeException {@inheritDoc}
+ * @throws ArithmeticException {@inheritDoc}
+ */
+ @Override
+ public ThaiBuddhistDate with(TemporalAdjuster adjuster) {
+ return super.with(adjuster);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws DateTimeException {@inheritDoc}
+ * @throws ArithmeticException {@inheritDoc}
+ */
+ @Override
+ public ThaiBuddhistDate plus(TemporalAmount amount) {
+ return super.plus(amount);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws DateTimeException {@inheritDoc}
+ * @throws ArithmeticException {@inheritDoc}
+ */
+ @Override
+ public ThaiBuddhistDate minus(TemporalAmount amount) {
+ return super.minus(amount);
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ ThaiBuddhistDate plusYears(long years) {
+ return with(isoDate.plusYears(years));
+ }
+
+ @Override
+ ThaiBuddhistDate plusMonths(long months) {
+ return with(isoDate.plusMonths(months));
+ }
+
+ @Override
+ ThaiBuddhistDate plusWeeks(long weeksToAdd) {
+ return super.plusWeeks(weeksToAdd);
+ }
+
+ @Override
+ ThaiBuddhistDate plusDays(long days) {
+ return with(isoDate.plusDays(days));
+ }
+
+ @Override
+ public ThaiBuddhistDate plus(long amountToAdd, TemporalUnit unit) {
+ return super.plus(amountToAdd, unit);
+ }
+
+ @Override
+ public ThaiBuddhistDate minus(long amountToAdd, TemporalUnit unit) {
+ return super.minus(amountToAdd, unit);
+ }
+
+ @Override
+ ThaiBuddhistDate minusYears(long yearsToSubtract) {
+ return super.minusYears(yearsToSubtract);
+ }
+
+ @Override
+ ThaiBuddhistDate minusMonths(long monthsToSubtract) {
+ return super.minusMonths(monthsToSubtract);
+ }
+
+ @Override
+ ThaiBuddhistDate minusWeeks(long weeksToSubtract) {
+ return super.minusWeeks(weeksToSubtract);
+ }
+
+ @Override
+ ThaiBuddhistDate minusDays(long daysToSubtract) {
+ return super.minusDays(daysToSubtract);
+ }
+
+ private ThaiBuddhistDate with(LocalDate newDate) {
+ return (newDate.equals(isoDate) ? this : new ThaiBuddhistDate(newDate));
+ }
+
+ @Override // for javadoc and covariant return type
+ @SuppressWarnings("unchecked")
+ public final ChronoLocalDateTime<ThaiBuddhistDate> atTime(LocalTime localTime) {
+ return (ChronoLocalDateTime<ThaiBuddhistDate>) super.atTime(localTime);
+ }
+
+ @Override
+ public ChronoPeriod until(ChronoLocalDate endDate) {
+ Period period = isoDate.until(endDate);
+ return getChronology().period(period.getYears(), period.getMonths(), period.getDays());
+ }
+
+ @Override // override for performance
+ public long toEpochDay() {
+ return isoDate.toEpochDay();
+ }
+
+ //-------------------------------------------------------------------------
+ /**
+ * Compares this date to another date, including the chronology.
+ * <p>
+ * Compares this {@code ThaiBuddhistDate} with another ensuring that the date is the same.
+ * <p>
+ * Only objects of type {@code ThaiBuddhistDate} are compared, other types return false.
+ * To compare the dates of two {@code TemporalAccessor} instances, including dates
+ * in two different chronologies, use {@link ChronoField#EPOCH_DAY} as a comparator.
+ *
+ * @param obj the object to check, null returns false
+ * @return true if this is equal to the other date
+ */
+ @Override // override for performance
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof ThaiBuddhistDate) {
+ ThaiBuddhistDate otherDate = (ThaiBuddhistDate) obj;
+ return this.isoDate.equals(otherDate.isoDate);
+ }
+ return false;
+ }
+
+ /**
+ * A hash code for this date.
+ *
+ * @return a suitable hash code based only on the Chronology and the date
+ */
+ @Override // override for performance
+ public int hashCode() {
+ return getChronology().getId().hashCode() ^ isoDate.hashCode();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Defend against malicious streams.
+ *
+ * @param s the stream to read
+ * @throws InvalidObjectException always
+ */
+ private void readObject(ObjectInputStream s) throws InvalidObjectException {
+ throw new InvalidObjectException("Deserialization via serialization delegate");
+ }
+
+ /**
+ * Writes the object using a
+ * <a href="../../../serialized-form.html#java.time.chrono.Ser">dedicated serialized form</a>.
+ * @serialData
+ * <pre>
+ * out.writeByte(10); // identifies a ThaiBuddhistDate
+ * out.writeInt(get(YEAR));
+ * out.writeByte(get(MONTH_OF_YEAR));
+ * out.writeByte(get(DAY_OF_MONTH));
+ * </pre>
+ *
+ * @return the instance of {@code Ser}, not null
+ */
+ private Object writeReplace() {
+ return new Ser(Ser.THAIBUDDHIST_DATE_TYPE, this);
+ }
+
+ void writeExternal(DataOutput out) throws IOException {
+ // ThaiBuddhistChronology is implicit in the THAIBUDDHIST_DATE_TYPE
+ out.writeInt(this.get(YEAR));
+ out.writeByte(this.get(MONTH_OF_YEAR));
+ out.writeByte(this.get(DAY_OF_MONTH));
+ }
+
+ static ThaiBuddhistDate readExternal(DataInput in) throws IOException {
+ int year = in.readInt();
+ int month = in.readByte();
+ int dayOfMonth = in.readByte();
+ return ThaiBuddhistChronology.INSTANCE.date(year, month, dayOfMonth);
+ }
+
+}
diff --git a/java/time/chrono/ThaiBuddhistEra.java b/java/time/chrono/ThaiBuddhistEra.java
new file mode 100644
index 0000000..e3e6e86
--- /dev/null
+++ b/java/time/chrono/ThaiBuddhistEra.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2012, 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) 2012, 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;
+
+/**
+ * An era in the Thai Buddhist calendar system.
+ * <p>
+ * The Thai Buddhist calendar system has two eras.
+ * The current era, for years from 1 onwards, is known as the 'Buddhist' era.
+ * All previous years, zero or earlier in the proleptic count or one and greater
+ * in the year-of-era count, are part of the 'Before Buddhist' era.
+ *
+ * <table summary="Buddhist years and eras" cellpadding="2" cellspacing="3" border="0" >
+ * <thead>
+ * <tr class="tableSubHeadingColor">
+ * <th class="colFirst" align="left">year-of-era</th>
+ * <th class="colFirst" align="left">era</th>
+ * <th class="colFirst" align="left">proleptic-year</th>
+ * <th class="colLast" align="left">ISO proleptic-year</th>
+ * </tr>
+ * </thead>
+ * <tbody>
+ * <tr class="rowColor">
+ * <td>2</td><td>BE</td><td>2</td><td>-542</td>
+ * </tr>
+ * <tr class="altColor">
+ * <td>1</td><td>BE</td><td>1</td><td>-543</td>
+ * </tr>
+ * <tr class="rowColor">
+ * <td>1</td><td>BEFORE_BE</td><td>0</td><td>-544</td>
+ * </tr>
+ * <tr class="altColor">
+ * <td>2</td><td>BEFORE_BE</td><td>-1</td><td>-545</td>
+ * </tr>
+ * </tbody>
+ * </table>
+ * <p>
+ * <b>Do not use {@code ordinal()} to obtain the numeric representation of {@code ThaiBuddhistEra}.
+ * Use {@code getValue()} instead.</b>
+ *
+ * @implSpec
+ * This is an immutable and thread-safe enum.
+ *
+ * @since 1.8
+ */
+public enum ThaiBuddhistEra implements Era {
+
+ /**
+ * The singleton instance for the era before the current one, 'Before Buddhist Era',
+ * which has the numeric value 0.
+ */
+ BEFORE_BE,
+ /**
+ * The singleton instance for the current era, 'Buddhist Era',
+ * which has the numeric value 1.
+ */
+ BE;
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code ThaiBuddhistEra} from an {@code int} value.
+ * <p>
+ * {@code ThaiBuddhistEra} is an enum representing the Thai Buddhist eras of BEFORE_BE/BE.
+ * This factory allows the enum to be obtained from the {@code int} value.
+ *
+ * @param thaiBuddhistEra the era to represent, from 0 to 1
+ * @return the BuddhistEra singleton, never null
+ * @throws DateTimeException if the era is invalid
+ */
+ public static ThaiBuddhistEra of(int thaiBuddhistEra) {
+ switch (thaiBuddhistEra) {
+ case 0:
+ return BEFORE_BE;
+ case 1:
+ return BE;
+ default:
+ throw new DateTimeException("Invalid era: " + thaiBuddhistEra);
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the numeric era {@code int} value.
+ * <p>
+ * The era BEFORE_BE has the value 0, while the era BE has the value 1.
+ *
+ * @return the era value, from 0 (BEFORE_BE) to 1 (BE)
+ */
+ @Override
+ public int getValue() {
+ return ordinal();
+ }
+
+}
diff --git a/java/time/chrono/package-info.java b/java/time/chrono/package-info.java
new file mode 100644
index 0000000..9273a77
--- /dev/null
+++ b/java/time/chrono/package-info.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2012, 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) 2012, 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.
+ */
+
+/**
+ * <p>
+ * Generic API for calendar systems other than the default ISO.
+ * </p>
+ * <p>
+ * The main API is based around the calendar system defined in ISO-8601.
+ * However, there are other calendar systems, and this package provides basic support for them.
+ * The alternate calendars are provided in the {@link java.time.chrono} package.
+ * </p>
+ * <p>
+ * A calendar system is defined by the {@link java.time.chrono.Chronology} interface,
+ * while a date in a calendar system is defined by the {@link java.time.chrono.ChronoLocalDate} interface.
+ * </p>
+ * <p>
+ * It is intended that applications use the main API whenever possible, including code to read and write
+ * from a persistent data store, such as a database, and to send dates and times across a network.
+ * The "chrono" classes are then used at the user interface level to deal with localized input/output.
+ * See {@link java.time.chrono.ChronoLocalDate ChronoLocalDate}
+ * for a full discussion of the issues.
+ * </p>
+ * <p>
+ * Using non-ISO calendar systems in an application introduces significant extra complexity.
+ * Ensure that the warnings and recommendations in {@code ChronoLocalDate} have been read before
+ * working with the "chrono" interfaces.
+ * </p>
+ * <p>
+ * The supported calendar systems includes:
+ * </p>
+ * <ul>
+ * <li>{@link java.time.chrono.HijrahChronology Hijrah calendar}</li>
+ * <li>{@link java.time.chrono.JapaneseChronology Japanese calendar}</li>
+ * <li>{@link java.time.chrono.MinguoChronology Minguo calendar}</li>
+ * <li>{@link java.time.chrono.ThaiBuddhistChronology Thai Buddhist calendar}</li>
+ * </ul>
+ *
+ * <h3>Example</h3>
+ * <p>
+ * This example lists todays date for all of the available calendars.
+ * </p>
+ * <pre>
+ * // Enumerate the list of available calendars and print todays date for each.
+ * Set<Chronology> chronos = Chronology.getAvailableChronologies();
+ * for (Chronology chrono : chronos) {
+ * ChronoLocalDate date = chrono.dateNow();
+ * System.out.printf(" %20s: %s%n", chrono.getId(), date.toString());
+ * }
+ * </pre>
+ *
+ * <p>
+ * This example creates and uses a date in a named non-ISO calendar system.
+ * </p>
+ * <pre>
+ * // Print the Thai Buddhist date
+ * ChronoLocalDate now1 = Chronology.of("ThaiBuddhist").dateNow();
+ * int day = now1.get(ChronoField.DAY_OF_MONTH);
+ * int dow = now1.get(ChronoField.DAY_OF_WEEK);
+ * int month = now1.get(ChronoField.MONTH_OF_YEAR);
+ * int year = now1.get(ChronoField.YEAR);
+ * System.out.printf(" Today is %s %s %d-%s-%d%n", now1.getChronology().getId(),
+ * dow, day, month, year);
+ * // Print today's date and the last day of the year for the Thai Buddhist Calendar.
+ * ChronoLocalDate first = now1
+ * .with(ChronoField.DAY_OF_MONTH, 1)
+ * .with(ChronoField.MONTH_OF_YEAR, 1);
+ * ChronoLocalDate last = first
+ * .plus(1, ChronoUnit.YEARS)
+ * .minus(1, ChronoUnit.DAYS);
+ * System.out.printf(" %s: 1st of year: %s; end of year: %s%n", last.getChronology().getId(),
+ * first, last);
+ * </pre>
+ *
+ * <p>
+ * This example creates and uses a date in a specific ThaiBuddhist calendar system.
+ * </p>
+ * <pre>
+ * // Print the Thai Buddhist date
+ * ThaiBuddhistDate now1 = ThaiBuddhistDate.now();
+ * int day = now1.get(ChronoField.DAY_OF_MONTH);
+ * int dow = now1.get(ChronoField.DAY_OF_WEEK);
+ * int month = now1.get(ChronoField.MONTH_OF_YEAR);
+ * int year = now1.get(ChronoField.YEAR);
+ * System.out.printf(" Today is %s %s %d-%s-%d%n", now1.getChronology().getId(),
+ * dow, day, month, year);
+ *
+ * // Print today's date and the last day of the year for the Thai Buddhist Calendar.
+ * ThaiBuddhistDate first = now1
+ * .with(ChronoField.DAY_OF_MONTH, 1)
+ * .with(ChronoField.MONTH_OF_YEAR, 1);
+ * ThaiBuddhistDate last = first
+ * .plus(1, ChronoUnit.YEARS)
+ * .minus(1, ChronoUnit.DAYS);
+ * System.out.printf(" %s: 1st of year: %s; end of year: %s%n", last.getChronology().getId(),
+ * first, last);
+ * </pre>
+ *
+ * <h3>Package specification</h3>
+ * <p>
+ * Unless otherwise noted, passing a null argument to a constructor or method in any class or interface
+ * in this package will cause a {@link java.lang.NullPointerException NullPointerException} to be thrown.
+ * The Javadoc "@param" definition is used to summarise the null-behavior.
+ * The "@throws {@link java.lang.NullPointerException}" is not explicitly documented in each method.
+ * </p>
+ * <p>
+ * All calculations should check for numeric overflow and throw either an {@link java.lang.ArithmeticException}
+ * or a {@link java.time.DateTimeException}.
+ * </p>
+ * @since JDK1.8
+ */
+package java.time.chrono;
diff --git a/java/time/format/DateTimeFormatter.java b/java/time/format/DateTimeFormatter.java
new file mode 100644
index 0000000..57a0bf8
--- /dev/null
+++ b/java/time/format/DateTimeFormatter.java
@@ -0,0 +1,2165 @@
+/*
+ * Copyright (c) 2012, 2015, 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) 2008-2012, 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.format;
+
+import static java.time.temporal.ChronoField.DAY_OF_MONTH;
+import static java.time.temporal.ChronoField.DAY_OF_WEEK;
+import static java.time.temporal.ChronoField.DAY_OF_YEAR;
+import static java.time.temporal.ChronoField.HOUR_OF_DAY;
+import static java.time.temporal.ChronoField.MINUTE_OF_HOUR;
+import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
+import static java.time.temporal.ChronoField.NANO_OF_SECOND;
+import static java.time.temporal.ChronoField.SECOND_OF_MINUTE;
+import static java.time.temporal.ChronoField.YEAR;
+
+import java.io.IOException;
+import java.text.FieldPosition;
+import java.text.Format;
+import java.text.ParseException;
+import java.text.ParsePosition;
+import java.time.DateTimeException;
+import java.time.Period;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.chrono.Chronology;
+import java.time.chrono.IsoChronology;
+import java.time.format.DateTimeFormatterBuilder.CompositePrinterParser;
+import java.time.temporal.ChronoField;
+import java.time.temporal.IsoFields;
+import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalField;
+import java.time.temporal.TemporalQuery;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Formatter for printing and parsing date-time objects.
+ * <p>
+ * This class provides the main application entry point for printing and parsing
+ * and provides common implementations of {@code DateTimeFormatter}:
+ * <ul>
+ * <li>Using predefined constants, such as {@link #ISO_LOCAL_DATE}</li>
+ * <li>Using pattern letters, such as {@code uuuu-MMM-dd}</li>
+ * <li>Using localized styles, such as {@code long} or {@code medium}</li>
+ * </ul>
+ * <p>
+ * More complex formatters are provided by
+ * {@link DateTimeFormatterBuilder DateTimeFormatterBuilder}.
+ *
+ * <p>
+ * The main date-time classes provide two methods - one for formatting,
+ * {@code format(DateTimeFormatter formatter)}, and one for parsing,
+ * {@code parse(CharSequence text, DateTimeFormatter formatter)}.
+ * <p>For example:
+ * <blockquote><pre>
+ * LocalDate date = LocalDate.now();
+ * String text = date.format(formatter);
+ * LocalDate parsedDate = LocalDate.parse(text, formatter);
+ * </pre></blockquote>
+ * <p>
+ * In addition to the format, formatters can be created with desired Locale,
+ * Chronology, ZoneId, and DecimalStyle.
+ * <p>
+ * The {@link #withLocale withLocale} method returns a new formatter that
+ * overrides the locale. The locale affects some aspects of formatting and
+ * parsing. For example, the {@link #ofLocalizedDate ofLocalizedDate} provides a
+ * formatter that uses the locale specific date format.
+ * <p>
+ * The {@link #withChronology withChronology} method returns a new formatter
+ * that overrides the chronology. If overridden, the date-time value is
+ * converted to the chronology before formatting. During parsing the date-time
+ * value is converted to the chronology before it is returned.
+ * <p>
+ * The {@link #withZone withZone} method returns a new formatter that overrides
+ * the zone. If overridden, the date-time value is converted to a ZonedDateTime
+ * with the requested ZoneId before formatting. During parsing the ZoneId is
+ * applied before the value is returned.
+ * <p>
+ * The {@link #withDecimalStyle withDecimalStyle} method returns a new formatter that
+ * overrides the {@link DecimalStyle}. The DecimalStyle symbols are used for
+ * formatting and parsing.
+ * <p>
+ * Some applications may need to use the older {@link Format java.text.Format}
+ * class for formatting. The {@link #toFormat()} method returns an
+ * implementation of {@code java.text.Format}.
+ *
+ * <h3 id="predefined">Predefined Formatters</h3>
+ * <table summary="Predefined Formatters" cellpadding="2" cellspacing="3" border="0" >
+ * <thead>
+ * <tr class="tableSubHeadingColor">
+ * <th class="colFirst" align="left">Formatter</th>
+ * <th class="colFirst" align="left">Description</th>
+ * <th class="colLast" align="left">Example</th>
+ * </tr>
+ * </thead>
+ * <tbody>
+ * <tr class="rowColor">
+ * <td>{@link #ofLocalizedDate ofLocalizedDate(dateStyle)} </td>
+ * <td> Formatter with date style from the locale </td>
+ * <td> '2011-12-03'</td>
+ * </tr>
+ * <tr class="altColor">
+ * <td> {@link #ofLocalizedTime ofLocalizedTime(timeStyle)} </td>
+ * <td> Formatter with time style from the locale </td>
+ * <td> '10:15:30'</td>
+ * </tr>
+ * <tr class="rowColor">
+ * <td> {@link #ofLocalizedDateTime ofLocalizedDateTime(dateTimeStyle)} </td>
+ * <td> Formatter with a style for date and time from the locale</td>
+ * <td> '3 Jun 2008 11:05:30'</td>
+ * </tr>
+ * <tr class="altColor">
+ * <td> {@link #ofLocalizedDateTime ofLocalizedDateTime(dateStyle,timeStyle)}
+ * </td>
+ * <td> Formatter with date and time styles from the locale </td>
+ * <td> '3 Jun 2008 11:05'</td>
+ * </tr>
+ * <tr class="rowColor">
+ * <td> {@link #BASIC_ISO_DATE}</td>
+ * <td>Basic ISO date </td> <td>'20111203'</td>
+ * </tr>
+ * <tr class="altColor">
+ * <td> {@link #ISO_LOCAL_DATE}</td>
+ * <td> ISO Local Date </td>
+ * <td>'2011-12-03'</td>
+ * </tr>
+ * <tr class="rowColor">
+ * <td> {@link #ISO_OFFSET_DATE}</td>
+ * <td> ISO Date with offset </td>
+ * <td>'2011-12-03+01:00'</td>
+ * </tr>
+ * <tr class="altColor">
+ * <td> {@link #ISO_DATE}</td>
+ * <td> ISO Date with or without offset </td>
+ * <td> '2011-12-03+01:00'; '2011-12-03'</td>
+ * </tr>
+ * <tr class="rowColor">
+ * <td> {@link #ISO_LOCAL_TIME}</td>
+ * <td> Time without offset </td>
+ * <td>'10:15:30'</td>
+ * </tr>
+ * <tr class="altColor">
+ * <td> {@link #ISO_OFFSET_TIME}</td>
+ * <td> Time with offset </td>
+ * <td>'10:15:30+01:00'</td>
+ * </tr>
+ * <tr class="rowColor">
+ * <td> {@link #ISO_TIME}</td>
+ * <td> Time with or without offset </td>
+ * <td>'10:15:30+01:00'; '10:15:30'</td>
+ * </tr>
+ * <tr class="altColor">
+ * <td> {@link #ISO_LOCAL_DATE_TIME}</td>
+ * <td> ISO Local Date and Time </td>
+ * <td>'2011-12-03T10:15:30'</td>
+ * </tr>
+ * <tr class="rowColor">
+ * <td> {@link #ISO_OFFSET_DATE_TIME}</td>
+ * <td> Date Time with Offset
+ * </td><td>2011-12-03T10:15:30+01:00'</td>
+ * </tr>
+ * <tr class="altColor">
+ * <td> {@link #ISO_ZONED_DATE_TIME}</td>
+ * <td> Zoned Date Time </td>
+ * <td>'2011-12-03T10:15:30+01:00[Europe/Paris]'</td>
+ * </tr>
+ * <tr class="rowColor">
+ * <td> {@link #ISO_DATE_TIME}</td>
+ * <td> Date and time with ZoneId </td>
+ * <td>'2011-12-03T10:15:30+01:00[Europe/Paris]'</td>
+ * </tr>
+ * <tr class="altColor">
+ * <td> {@link #ISO_ORDINAL_DATE}</td>
+ * <td> Year and day of year </td>
+ * <td>'2012-337'</td>
+ * </tr>
+ * <tr class="rowColor">
+ * <td> {@link #ISO_WEEK_DATE}</td>
+ * <td> Year and Week </td>
+ * <td>2012-W48-6'</td></tr>
+ * <tr class="altColor">
+ * <td> {@link #ISO_INSTANT}</td>
+ * <td> Date and Time of an Instant </td>
+ * <td>'2011-12-03T10:15:30Z' </td>
+ * </tr>
+ * <tr class="rowColor">
+ * <td> {@link #RFC_1123_DATE_TIME}</td>
+ * <td> RFC 1123 / RFC 822 </td>
+ * <td>'Tue, 3 Jun 2008 11:05:30 GMT'</td>
+ * </tr>
+ * </tbody>
+ * </table>
+ *
+ * <h3 id="patterns">Patterns for Formatting and Parsing</h3>
+ * Patterns are based on a simple sequence of letters and symbols.
+ * A pattern is used to create a Formatter using the
+ * {@link #ofPattern(String)} and {@link #ofPattern(String, Locale)} methods.
+ * For example,
+ * {@code "d MMM uuuu"} will format 2011-12-03 as '3 Dec 2011'.
+ * A formatter created from a pattern can be used as many times as necessary,
+ * it is immutable and is thread-safe.
+ * <p>
+ * For example:
+ * <blockquote><pre>
+ * LocalDate date = LocalDate.now();
+ * DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy MM dd");
+ * String text = date.format(formatter);
+ * LocalDate parsedDate = LocalDate.parse(text, formatter);
+ * </pre></blockquote>
+ * <p>
+ * All letters 'A' to 'Z' and 'a' to 'z' are reserved as pattern letters. The
+ * following pattern letters are defined:
+ * <pre>
+ * Symbol Meaning Presentation Examples
+ * ------ ------- ------------ -------
+ * G era text AD; Anno Domini; A
+ * u year year 2004; 04
+ * y year-of-era year 2004; 04
+ * D day-of-year number 189
+ * M/L month-of-year number/text 7; 07; Jul; July; J
+ * d day-of-month number 10
+ *
+ * Q/q quarter-of-year number/text 3; 03; Q3; 3rd quarter
+ * Y week-based-year year 1996; 96
+ * w week-of-week-based-year number 27
+ * W week-of-month number 4
+ * E day-of-week text Tue; Tuesday; T
+ * e/c localized day-of-week number/text 2; 02; Tue; Tuesday; T
+ * F week-of-month number 3
+ *
+ * a am-pm-of-day text PM
+ * h clock-hour-of-am-pm (1-12) number 12
+ * K hour-of-am-pm (0-11) number 0
+ * k clock-hour-of-am-pm (1-24) number 0
+ *
+ * H hour-of-day (0-23) number 0
+ * m minute-of-hour number 30
+ * s second-of-minute number 55
+ * S fraction-of-second fraction 978
+ * A milli-of-day number 1234
+ * n nano-of-second number 987654321
+ * N nano-of-day number 1234000000
+ *
+ * V time-zone ID zone-id America/Los_Angeles; Z; -08:30
+ * z time-zone name zone-name Pacific Standard Time; PST
+ * O localized zone-offset offset-O GMT+8; GMT+08:00; UTC-08:00;
+ * X zone-offset 'Z' for zero offset-X Z; -08; -0830; -08:30; -083015; -08:30:15;
+ * x zone-offset offset-x +0000; -08; -0830; -08:30; -083015; -08:30:15;
+ * Z zone-offset offset-Z +0000; -0800; -08:00;
+ *
+ * p pad next pad modifier 1
+ *
+ * ' escape for text delimiter
+ * '' single quote literal '
+ * [ optional section start
+ * ] optional section end
+ * # reserved for future use
+ * { reserved for future use
+ * } reserved for future use
+ * </pre>
+ * <p>
+ * The count of pattern letters determines the format.
+ * <p>
+ * <b>Text</b>: The text style is determined based on the number of pattern
+ * letters used. Less than 4 pattern letters will use the
+ * {@link TextStyle#SHORT short form}. Exactly 4 pattern letters will use the
+ * {@link TextStyle#FULL full form}. Exactly 5 pattern letters will use the
+ * {@link TextStyle#NARROW narrow form}.
+ * Pattern letters 'L', 'c', and 'q' specify the stand-alone form of the text styles.
+ * <p>
+ * <b>Number</b>: If the count of letters is one, then the value is output using
+ * the minimum number of digits and without padding. Otherwise, the count of digits
+ * is used as the width of the output field, with the value zero-padded as necessary.
+ * The following pattern letters have constraints on the count of letters.
+ * Only one letter of 'c' and 'F' can be specified.
+ * Up to two letters of 'd', 'H', 'h', 'K', 'k', 'm', and 's' can be specified.
+ * Up to three letters of 'D' can be specified.
+ * <p>
+ * <b>Number/Text</b>: If the count of pattern letters is 3 or greater, use the
+ * Text rules above. Otherwise use the Number rules above.
+ * <p>
+ * <b>Fraction</b>: Outputs the nano-of-second field as a fraction-of-second.
+ * The nano-of-second value has nine digits, thus the count of pattern letters
+ * is from 1 to 9. If it is less than 9, then the nano-of-second value is
+ * truncated, with only the most significant digits being output.
+ * <p>
+ * <b>Year</b>: The count of letters determines the minimum field width below
+ * which padding is used. If the count of letters is two, then a
+ * {@link DateTimeFormatterBuilder#appendValueReduced reduced} two digit form is
+ * used. For printing, this outputs the rightmost two digits. For parsing, this
+ * will parse using the base value of 2000, resulting in a year within the range
+ * 2000 to 2099 inclusive. If the count of letters is less than four (but not
+ * two), then the sign is only output for negative years as per
+ * {@link SignStyle#NORMAL}. Otherwise, the sign is output if the pad width is
+ * exceeded, as per {@link SignStyle#EXCEEDS_PAD}.
+ * <p>
+ * <b>ZoneId</b>: This outputs the time-zone ID, such as 'Europe/Paris'. If the
+ * count of letters is two, then the time-zone ID is output. Any other count of
+ * letters throws {@code IllegalArgumentException}.
+ * <p>
+ * <b>Zone names</b>: This outputs the display name of the time-zone ID. If the
+ * count of letters is one, two or three, then the short name is output. If the
+ * count of letters is four, then the full name is output. Five or more letters
+ * throws {@code IllegalArgumentException}.
+ * <p>
+ * <b>Offset X and x</b>: This formats the offset based on the number of pattern
+ * letters. One letter outputs just the hour, such as '+01', unless the minute
+ * is non-zero in which case the minute is also output, such as '+0130'. Two
+ * letters outputs the hour and minute, without a colon, such as '+0130'. Three
+ * letters outputs the hour and minute, with a colon, such as '+01:30'. Four
+ * letters outputs the hour and minute and optional second, without a colon,
+ * such as '+013015'. Five letters outputs the hour and minute and optional
+ * second, with a colon, such as '+01:30:15'. Six or more letters throws
+ * {@code IllegalArgumentException}. Pattern letter 'X' (upper case) will output
+ * 'Z' when the offset to be output would be zero, whereas pattern letter 'x'
+ * (lower case) will output '+00', '+0000', or '+00:00'.
+ * <p>
+ * <b>Offset O</b>: This formats the localized offset based on the number of
+ * pattern letters. One letter outputs the {@linkplain TextStyle#SHORT short}
+ * form of the localized offset, which is localized offset text, such as 'GMT',
+ * with hour without leading zero, optional 2-digit minute and second if
+ * non-zero, and colon, for example 'GMT+8'. Four letters outputs the
+ * {@linkplain TextStyle#FULL full} form, which is localized offset text,
+ * such as 'GMT, with 2-digit hour and minute field, optional second field
+ * if non-zero, and colon, for example 'GMT+08:00'. Any other count of letters
+ * throws {@code IllegalArgumentException}.
+ * <p>
+ * <b>Offset Z</b>: This formats the offset based on the number of pattern
+ * letters. One, two or three letters outputs the hour and minute, without a
+ * colon, such as '+0130'. The output will be '+0000' when the offset is zero.
+ * Four letters outputs the {@linkplain TextStyle#FULL full} form of localized
+ * offset, equivalent to four letters of Offset-O. The output will be the
+ * corresponding localized offset text if the offset is zero. Five
+ * letters outputs the hour, minute, with optional second if non-zero, with
+ * colon. It outputs 'Z' if the offset is zero.
+ * Six or more letters throws {@code IllegalArgumentException}.
+ * <p>
+ * <b>Optional section</b>: The optional section markers work exactly like
+ * calling {@link DateTimeFormatterBuilder#optionalStart()} and
+ * {@link DateTimeFormatterBuilder#optionalEnd()}.
+ * <p>
+ * <b>Pad modifier</b>: Modifies the pattern that immediately follows to be
+ * padded with spaces. The pad width is determined by the number of pattern
+ * letters. This is the same as calling
+ * {@link DateTimeFormatterBuilder#padNext(int)}.
+ * <p>
+ * For example, 'ppH' outputs the hour-of-day padded on the left with spaces to
+ * a width of 2.
+ * <p>
+ * Any unrecognized letter is an error. Any non-letter character, other than
+ * '[', ']', '{', '}', '#' and the single quote will be output directly.
+ * Despite this, it is recommended to use single quotes around all characters
+ * that you want to output directly to ensure that future changes do not break
+ * your application.
+ *
+ * <h3 id="resolving">Resolving</h3>
+ * Parsing is implemented as a two-phase operation.
+ * First, the text is parsed using the layout defined by the formatter, producing
+ * a {@code Map} of field to value, a {@code ZoneId} and a {@code Chronology}.
+ * Second, the parsed data is <em>resolved</em>, by validating, combining and
+ * simplifying the various fields into more useful ones.
+ * <p>
+ * Five parsing methods are supplied by this class.
+ * Four of these perform both the parse and resolve phases.
+ * The fifth method, {@link #parseUnresolved(CharSequence, ParsePosition)},
+ * only performs the first phase, leaving the result unresolved.
+ * As such, it is essentially a low-level operation.
+ * <p>
+ * The resolve phase is controlled by two parameters, set on this class.
+ * <p>
+ * The {@link ResolverStyle} is an enum that offers three different approaches,
+ * strict, smart and lenient. The smart option is the default.
+ * It can be set using {@link #withResolverStyle(ResolverStyle)}.
+ * <p>
+ * The {@link #withResolverFields(TemporalField...)} parameter allows the
+ * set of fields that will be resolved to be filtered before resolving starts.
+ * For example, if the formatter has parsed a year, month, day-of-month
+ * and day-of-year, then there are two approaches to resolve a date:
+ * (year + month + day-of-month) and (year + day-of-year).
+ * The resolver fields allows one of the two approaches to be selected.
+ * If no resolver fields are set then both approaches must result in the same date.
+ * <p>
+ * Resolving separate fields to form a complete date and time is a complex
+ * process with behaviour distributed across a number of classes.
+ * It follows these steps:
+ * <ol>
+ * <li>The chronology is determined.
+ * The chronology of the result is either the chronology that was parsed,
+ * or if no chronology was parsed, it is the chronology set on this class,
+ * or if that is null, it is {@code IsoChronology}.
+ * <li>The {@code ChronoField} date fields are resolved.
+ * This is achieved using {@link Chronology#resolveDate(Map, ResolverStyle)}.
+ * Documentation about field resolution is located in the implementation
+ * of {@code Chronology}.
+ * <li>The {@code ChronoField} time fields are resolved.
+ * This is documented on {@link ChronoField} and is the same for all chronologies.
+ * <li>Any fields that are not {@code ChronoField} are processed.
+ * This is achieved using {@link TemporalField#resolve(Map, TemporalAccessor, ResolverStyle)}.
+ * Documentation about field resolution is located in the implementation
+ * of {@code TemporalField}.
+ * <li>The {@code ChronoField} date and time fields are re-resolved.
+ * This allows fields in step four to produce {@code ChronoField} values
+ * and have them be processed into dates and times.
+ * <li>A {@code LocalTime} is formed if there is at least an hour-of-day available.
+ * This involves providing default values for minute, second and fraction of second.
+ * <li>Any remaining unresolved fields are cross-checked against any
+ * date and/or time that was resolved. Thus, an earlier stage would resolve
+ * (year + month + day-of-month) to a date, and this stage would check that
+ * day-of-week was valid for the date.
+ * <li>If an {@linkplain #parsedExcessDays() excess number of days}
+ * was parsed then it is added to the date if a date is available.
+ * </ol>
+ *
+ * @implSpec
+ * This class is immutable and thread-safe.
+ *
+ * @since 1.8
+ */
+public final class DateTimeFormatter {
+
+ /**
+ * The printer and/or parser to use, not null.
+ */
+ private final CompositePrinterParser printerParser;
+ /**
+ * The locale to use for formatting, not null.
+ */
+ private final Locale locale;
+ /**
+ * The symbols to use for formatting, not null.
+ */
+ private final DecimalStyle decimalStyle;
+ /**
+ * The resolver style to use, not null.
+ */
+ private final ResolverStyle resolverStyle;
+ /**
+ * The fields to use in resolving, null for all fields.
+ */
+ private final Set<TemporalField> resolverFields;
+ /**
+ * The chronology to use for formatting, null for no override.
+ */
+ private final Chronology chrono;
+ /**
+ * The zone to use for formatting, null for no override.
+ */
+ private final ZoneId zone;
+
+ //-----------------------------------------------------------------------
+ /**
+ * Creates a formatter using the specified pattern.
+ * <p>
+ * This method will create a formatter based on a simple
+ * <a href="#patterns">pattern of letters and symbols</a>
+ * as described in the class documentation.
+ * For example, {@code d MMM uuuu} will format 2011-12-03 as '3 Dec 2011'.
+ * <p>
+ * The formatter will use the {@link Locale#getDefault(Locale.Category) default FORMAT locale}.
+ * This can be changed using {@link DateTimeFormatter#withLocale(Locale)} on the returned formatter
+ * Alternatively use the {@link #ofPattern(String, Locale)} variant of this method.
+ * <p>
+ * The returned formatter has no override chronology or zone.
+ * It uses {@link ResolverStyle#SMART SMART} resolver style.
+ *
+ * @param pattern the pattern to use, not null
+ * @return the formatter based on the pattern, not null
+ * @throws IllegalArgumentException if the pattern is invalid
+ * @see DateTimeFormatterBuilder#appendPattern(String)
+ */
+ public static DateTimeFormatter ofPattern(String pattern) {
+ return new DateTimeFormatterBuilder().appendPattern(pattern).toFormatter();
+ }
+
+ /**
+ * Creates a formatter using the specified pattern and locale.
+ * <p>
+ * This method will create a formatter based on a simple
+ * <a href="#patterns">pattern of letters and symbols</a>
+ * as described in the class documentation.
+ * For example, {@code d MMM uuuu} will format 2011-12-03 as '3 Dec 2011'.
+ * <p>
+ * The formatter will use the specified locale.
+ * This can be changed using {@link DateTimeFormatter#withLocale(Locale)} on the returned formatter
+ * <p>
+ * The returned formatter has no override chronology or zone.
+ * It uses {@link ResolverStyle#SMART SMART} resolver style.
+ *
+ * @param pattern the pattern to use, not null
+ * @param locale the locale to use, not null
+ * @return the formatter based on the pattern, not null
+ * @throws IllegalArgumentException if the pattern is invalid
+ * @see DateTimeFormatterBuilder#appendPattern(String)
+ */
+ public static DateTimeFormatter ofPattern(String pattern, Locale locale) {
+ return new DateTimeFormatterBuilder().appendPattern(pattern).toFormatter(locale);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a locale specific date format for the ISO chronology.
+ * <p>
+ * This returns a formatter that will format or parse a date.
+ * The exact format pattern used varies by locale.
+ * <p>
+ * The locale is determined from the formatter. The formatter returned directly by
+ * this method will use the {@link Locale#getDefault(Locale.Category) default FORMAT locale}.
+ * The locale can be controlled using {@link DateTimeFormatter#withLocale(Locale) withLocale(Locale)}
+ * on the result of this method.
+ * <p>
+ * Note that the localized pattern is looked up lazily.
+ * This {@code DateTimeFormatter} holds the style required and the locale,
+ * looking up the pattern required on demand.
+ * <p>
+ * The returned formatter has a chronology of ISO set to ensure dates in
+ * other calendar systems are correctly converted.
+ * It has no override zone and uses the {@link ResolverStyle#SMART SMART} resolver style.
+ *
+ * @param dateStyle the formatter style to obtain, not null
+ * @return the date formatter, not null
+ */
+ public static DateTimeFormatter ofLocalizedDate(FormatStyle dateStyle) {
+ Objects.requireNonNull(dateStyle, "dateStyle");
+ return new DateTimeFormatterBuilder().appendLocalized(dateStyle, null)
+ .toFormatter(ResolverStyle.SMART, IsoChronology.INSTANCE);
+ }
+
+ /**
+ * Returns a locale specific time format for the ISO chronology.
+ * <p>
+ * This returns a formatter that will format or parse a time.
+ * The exact format pattern used varies by locale.
+ * <p>
+ * The locale is determined from the formatter. The formatter returned directly by
+ * this method will use the {@link Locale#getDefault(Locale.Category) default FORMAT locale}.
+ * The locale can be controlled using {@link DateTimeFormatter#withLocale(Locale) withLocale(Locale)}
+ * on the result of this method.
+ * <p>
+ * Note that the localized pattern is looked up lazily.
+ * This {@code DateTimeFormatter} holds the style required and the locale,
+ * looking up the pattern required on demand.
+ * <p>
+ * The returned formatter has a chronology of ISO set to ensure dates in
+ * other calendar systems are correctly converted.
+ * It has no override zone and uses the {@link ResolverStyle#SMART SMART} resolver style.
+ *
+ * @param timeStyle the formatter style to obtain, not null
+ * @return the time formatter, not null
+ */
+ public static DateTimeFormatter ofLocalizedTime(FormatStyle timeStyle) {
+ Objects.requireNonNull(timeStyle, "timeStyle");
+ return new DateTimeFormatterBuilder().appendLocalized(null, timeStyle)
+ .toFormatter(ResolverStyle.SMART, IsoChronology.INSTANCE);
+ }
+
+ /**
+ * Returns a locale specific date-time formatter for the ISO chronology.
+ * <p>
+ * This returns a formatter that will format or parse a date-time.
+ * The exact format pattern used varies by locale.
+ * <p>
+ * The locale is determined from the formatter. The formatter returned directly by
+ * this method will use the {@link Locale#getDefault(Locale.Category) default FORMAT locale}.
+ * The locale can be controlled using {@link DateTimeFormatter#withLocale(Locale) withLocale(Locale)}
+ * on the result of this method.
+ * <p>
+ * Note that the localized pattern is looked up lazily.
+ * This {@code DateTimeFormatter} holds the style required and the locale,
+ * looking up the pattern required on demand.
+ * <p>
+ * The returned formatter has a chronology of ISO set to ensure dates in
+ * other calendar systems are correctly converted.
+ * It has no override zone and uses the {@link ResolverStyle#SMART SMART} resolver style.
+ *
+ * @param dateTimeStyle the formatter style to obtain, not null
+ * @return the date-time formatter, not null
+ */
+ public static DateTimeFormatter ofLocalizedDateTime(FormatStyle dateTimeStyle) {
+ Objects.requireNonNull(dateTimeStyle, "dateTimeStyle");
+ return new DateTimeFormatterBuilder().appendLocalized(dateTimeStyle, dateTimeStyle)
+ .toFormatter(ResolverStyle.SMART, IsoChronology.INSTANCE);
+ }
+
+ /**
+ * Returns a locale specific date and time format for the ISO chronology.
+ * <p>
+ * This returns a formatter that will format or parse a date-time.
+ * The exact format pattern used varies by locale.
+ * <p>
+ * The locale is determined from the formatter. The formatter returned directly by
+ * this method will use the {@link Locale#getDefault() default FORMAT locale}.
+ * The locale can be controlled using {@link DateTimeFormatter#withLocale(Locale) withLocale(Locale)}
+ * on the result of this method.
+ * <p>
+ * Note that the localized pattern is looked up lazily.
+ * This {@code DateTimeFormatter} holds the style required and the locale,
+ * looking up the pattern required on demand.
+ * <p>
+ * The returned formatter has a chronology of ISO set to ensure dates in
+ * other calendar systems are correctly converted.
+ * It has no override zone and uses the {@link ResolverStyle#SMART SMART} resolver style.
+ *
+ * @param dateStyle the date formatter style to obtain, not null
+ * @param timeStyle the time formatter style to obtain, not null
+ * @return the date, time or date-time formatter, not null
+ */
+ public static DateTimeFormatter ofLocalizedDateTime(FormatStyle dateStyle, FormatStyle timeStyle) {
+ Objects.requireNonNull(dateStyle, "dateStyle");
+ Objects.requireNonNull(timeStyle, "timeStyle");
+ return new DateTimeFormatterBuilder().appendLocalized(dateStyle, timeStyle)
+ .toFormatter(ResolverStyle.SMART, IsoChronology.INSTANCE);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * The ISO date formatter that formats or parses a date without an
+ * offset, such as '2011-12-03'.
+ * <p>
+ * This returns an immutable formatter capable of formatting and parsing
+ * the ISO-8601 extended local date format.
+ * The format consists of:
+ * <ul>
+ * <li>Four digits or more for the {@link ChronoField#YEAR year}.
+ * Years in the range 0000 to 9999 will be pre-padded by zero to ensure four digits.
+ * Years outside that range will have a prefixed positive or negative symbol.
+ * <li>A dash
+ * <li>Two digits for the {@link ChronoField#MONTH_OF_YEAR month-of-year}.
+ * This is pre-padded by zero to ensure two digits.
+ * <li>A dash
+ * <li>Two digits for the {@link ChronoField#DAY_OF_MONTH day-of-month}.
+ * This is pre-padded by zero to ensure two digits.
+ * </ul>
+ * <p>
+ * The returned formatter has a chronology of ISO set to ensure dates in
+ * other calendar systems are correctly converted.
+ * It has no override zone and uses the {@link ResolverStyle#STRICT STRICT} resolver style.
+ */
+ public static final DateTimeFormatter ISO_LOCAL_DATE;
+ static {
+ ISO_LOCAL_DATE = new DateTimeFormatterBuilder()
+ .appendValue(YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
+ .appendLiteral('-')
+ .appendValue(MONTH_OF_YEAR, 2)
+ .appendLiteral('-')
+ .appendValue(DAY_OF_MONTH, 2)
+ .toFormatter(ResolverStyle.STRICT, IsoChronology.INSTANCE);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * The ISO date formatter that formats or parses a date with an
+ * offset, such as '2011-12-03+01:00'.
+ * <p>
+ * This returns an immutable formatter capable of formatting and parsing
+ * the ISO-8601 extended offset date format.
+ * The format consists of:
+ * <ul>
+ * <li>The {@link #ISO_LOCAL_DATE}
+ * <li>The {@link ZoneOffset#getId() offset ID}. If the offset has seconds then
+ * they will be handled even though this is not part of the ISO-8601 standard.
+ * Parsing is case insensitive.
+ * </ul>
+ * <p>
+ * The returned formatter has a chronology of ISO set to ensure dates in
+ * other calendar systems are correctly converted.
+ * It has no override zone and uses the {@link ResolverStyle#STRICT STRICT} resolver style.
+ */
+ public static final DateTimeFormatter ISO_OFFSET_DATE;
+ static {
+ ISO_OFFSET_DATE = new DateTimeFormatterBuilder()
+ .parseCaseInsensitive()
+ .append(ISO_LOCAL_DATE)
+ .appendOffsetId()
+ .toFormatter(ResolverStyle.STRICT, IsoChronology.INSTANCE);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * The ISO date formatter that formats or parses a date with the
+ * offset if available, such as '2011-12-03' or '2011-12-03+01:00'.
+ * <p>
+ * This returns an immutable formatter capable of formatting and parsing
+ * the ISO-8601 extended date format.
+ * The format consists of:
+ * <ul>
+ * <li>The {@link #ISO_LOCAL_DATE}
+ * <li>If the offset is not available then the format is complete.
+ * <li>The {@link ZoneOffset#getId() offset ID}. If the offset has seconds then
+ * they will be handled even though this is not part of the ISO-8601 standard.
+ * Parsing is case insensitive.
+ * </ul>
+ * <p>
+ * As this formatter has an optional element, it may be necessary to parse using
+ * {@link DateTimeFormatter#parseBest}.
+ * <p>
+ * The returned formatter has a chronology of ISO set to ensure dates in
+ * other calendar systems are correctly converted.
+ * It has no override zone and uses the {@link ResolverStyle#STRICT STRICT} resolver style.
+ */
+ public static final DateTimeFormatter ISO_DATE;
+ static {
+ ISO_DATE = new DateTimeFormatterBuilder()
+ .parseCaseInsensitive()
+ .append(ISO_LOCAL_DATE)
+ .optionalStart()
+ .appendOffsetId()
+ .toFormatter(ResolverStyle.STRICT, IsoChronology.INSTANCE);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * The ISO time formatter that formats or parses a time without an
+ * offset, such as '10:15' or '10:15:30'.
+ * <p>
+ * This returns an immutable formatter capable of formatting and parsing
+ * the ISO-8601 extended local time format.
+ * The format consists of:
+ * <ul>
+ * <li>Two digits for the {@link ChronoField#HOUR_OF_DAY hour-of-day}.
+ * This is pre-padded by zero to ensure two digits.
+ * <li>A colon
+ * <li>Two digits for the {@link ChronoField#MINUTE_OF_HOUR minute-of-hour}.
+ * This is pre-padded by zero to ensure two digits.
+ * <li>If the second-of-minute is not available then the format is complete.
+ * <li>A colon
+ * <li>Two digits for the {@link ChronoField#SECOND_OF_MINUTE second-of-minute}.
+ * This is pre-padded by zero to ensure two digits.
+ * <li>If the nano-of-second is zero or not available then the format is complete.
+ * <li>A decimal point
+ * <li>One to nine digits for the {@link ChronoField#NANO_OF_SECOND nano-of-second}.
+ * As many digits will be output as required.
+ * </ul>
+ * <p>
+ * The returned formatter has no override chronology or zone.
+ * It uses the {@link ResolverStyle#STRICT STRICT} resolver style.
+ */
+ public static final DateTimeFormatter ISO_LOCAL_TIME;
+ static {
+ ISO_LOCAL_TIME = new DateTimeFormatterBuilder()
+ .appendValue(HOUR_OF_DAY, 2)
+ .appendLiteral(':')
+ .appendValue(MINUTE_OF_HOUR, 2)
+ .optionalStart()
+ .appendLiteral(':')
+ .appendValue(SECOND_OF_MINUTE, 2)
+ .optionalStart()
+ .appendFraction(NANO_OF_SECOND, 0, 9, true)
+ .toFormatter(ResolverStyle.STRICT, null);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * The ISO time formatter that formats or parses a time with an
+ * offset, such as '10:15+01:00' or '10:15:30+01:00'.
+ * <p>
+ * This returns an immutable formatter capable of formatting and parsing
+ * the ISO-8601 extended offset time format.
+ * The format consists of:
+ * <ul>
+ * <li>The {@link #ISO_LOCAL_TIME}
+ * <li>The {@link ZoneOffset#getId() offset ID}. If the offset has seconds then
+ * they will be handled even though this is not part of the ISO-8601 standard.
+ * Parsing is case insensitive.
+ * </ul>
+ * <p>
+ * The returned formatter has no override chronology or zone.
+ * It uses the {@link ResolverStyle#STRICT STRICT} resolver style.
+ */
+ public static final DateTimeFormatter ISO_OFFSET_TIME;
+ static {
+ ISO_OFFSET_TIME = new DateTimeFormatterBuilder()
+ .parseCaseInsensitive()
+ .append(ISO_LOCAL_TIME)
+ .appendOffsetId()
+ .toFormatter(ResolverStyle.STRICT, null);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * The ISO time formatter that formats or parses a time, with the
+ * offset if available, such as '10:15', '10:15:30' or '10:15:30+01:00'.
+ * <p>
+ * This returns an immutable formatter capable of formatting and parsing
+ * the ISO-8601 extended offset time format.
+ * The format consists of:
+ * <ul>
+ * <li>The {@link #ISO_LOCAL_TIME}
+ * <li>If the offset is not available then the format is complete.
+ * <li>The {@link ZoneOffset#getId() offset ID}. If the offset has seconds then
+ * they will be handled even though this is not part of the ISO-8601 standard.
+ * Parsing is case insensitive.
+ * </ul>
+ * <p>
+ * As this formatter has an optional element, it may be necessary to parse using
+ * {@link DateTimeFormatter#parseBest}.
+ * <p>
+ * The returned formatter has no override chronology or zone.
+ * It uses the {@link ResolverStyle#STRICT STRICT} resolver style.
+ */
+ public static final DateTimeFormatter ISO_TIME;
+ static {
+ ISO_TIME = new DateTimeFormatterBuilder()
+ .parseCaseInsensitive()
+ .append(ISO_LOCAL_TIME)
+ .optionalStart()
+ .appendOffsetId()
+ .toFormatter(ResolverStyle.STRICT, null);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * The ISO date-time formatter that formats or parses a date-time without
+ * an offset, such as '2011-12-03T10:15:30'.
+ * <p>
+ * This returns an immutable formatter capable of formatting and parsing
+ * the ISO-8601 extended offset date-time format.
+ * The format consists of:
+ * <ul>
+ * <li>The {@link #ISO_LOCAL_DATE}
+ * <li>The letter 'T'. Parsing is case insensitive.
+ * <li>The {@link #ISO_LOCAL_TIME}
+ * </ul>
+ * <p>
+ * The returned formatter has a chronology of ISO set to ensure dates in
+ * other calendar systems are correctly converted.
+ * It has no override zone and uses the {@link ResolverStyle#STRICT STRICT} resolver style.
+ */
+ public static final DateTimeFormatter ISO_LOCAL_DATE_TIME;
+ static {
+ ISO_LOCAL_DATE_TIME = new DateTimeFormatterBuilder()
+ .parseCaseInsensitive()
+ .append(ISO_LOCAL_DATE)
+ .appendLiteral('T')
+ .append(ISO_LOCAL_TIME)
+ .toFormatter(ResolverStyle.STRICT, IsoChronology.INSTANCE);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * The ISO date-time formatter that formats or parses a date-time with an
+ * offset, such as '2011-12-03T10:15:30+01:00'.
+ * <p>
+ * This returns an immutable formatter capable of formatting and parsing
+ * the ISO-8601 extended offset date-time format.
+ * The format consists of:
+ * <ul>
+ * <li>The {@link #ISO_LOCAL_DATE_TIME}
+ * <li>The {@link ZoneOffset#getId() offset ID}. If the offset has seconds then
+ * they will be handled even though this is not part of the ISO-8601 standard.
+ * Parsing is case insensitive.
+ * </ul>
+ * <p>
+ * The returned formatter has a chronology of ISO set to ensure dates in
+ * other calendar systems are correctly converted.
+ * It has no override zone and uses the {@link ResolverStyle#STRICT STRICT} resolver style.
+ */
+ public static final DateTimeFormatter ISO_OFFSET_DATE_TIME;
+ static {
+ ISO_OFFSET_DATE_TIME = new DateTimeFormatterBuilder()
+ .parseCaseInsensitive()
+ .append(ISO_LOCAL_DATE_TIME)
+ .appendOffsetId()
+ .toFormatter(ResolverStyle.STRICT, IsoChronology.INSTANCE);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * The ISO-like date-time formatter that formats or parses a date-time with
+ * offset and zone, such as '2011-12-03T10:15:30+01:00[Europe/Paris]'.
+ * <p>
+ * This returns an immutable formatter capable of formatting and parsing
+ * a format that extends the ISO-8601 extended offset date-time format
+ * to add the time-zone.
+ * The section in square brackets is not part of the ISO-8601 standard.
+ * The format consists of:
+ * <ul>
+ * <li>The {@link #ISO_OFFSET_DATE_TIME}
+ * <li>If the zone ID is not available or is a {@code ZoneOffset} then the format is complete.
+ * <li>An open square bracket '['.
+ * <li>The {@link ZoneId#getId() zone ID}. This is not part of the ISO-8601 standard.
+ * Parsing is case sensitive.
+ * <li>A close square bracket ']'.
+ * </ul>
+ * <p>
+ * The returned formatter has a chronology of ISO set to ensure dates in
+ * other calendar systems are correctly converted.
+ * It has no override zone and uses the {@link ResolverStyle#STRICT STRICT} resolver style.
+ */
+ public static final DateTimeFormatter ISO_ZONED_DATE_TIME;
+ static {
+ ISO_ZONED_DATE_TIME = new DateTimeFormatterBuilder()
+ .append(ISO_OFFSET_DATE_TIME)
+ .optionalStart()
+ .appendLiteral('[')
+ .parseCaseSensitive()
+ .appendZoneRegionId()
+ .appendLiteral(']')
+ .toFormatter(ResolverStyle.STRICT, IsoChronology.INSTANCE);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * The ISO-like date-time formatter that formats or parses a date-time with
+ * the offset and zone if available, such as '2011-12-03T10:15:30',
+ * '2011-12-03T10:15:30+01:00' or '2011-12-03T10:15:30+01:00[Europe/Paris]'.
+ * <p>
+ * This returns an immutable formatter capable of formatting and parsing
+ * the ISO-8601 extended local or offset date-time format, as well as the
+ * extended non-ISO form specifying the time-zone.
+ * The format consists of:
+ * <ul>
+ * <li>The {@link #ISO_LOCAL_DATE_TIME}
+ * <li>If the offset is not available to format or parse then the format is complete.
+ * <li>The {@link ZoneOffset#getId() offset ID}. If the offset has seconds then
+ * they will be handled even though this is not part of the ISO-8601 standard.
+ * <li>If the zone ID is not available or is a {@code ZoneOffset} then the format is complete.
+ * <li>An open square bracket '['.
+ * <li>The {@link ZoneId#getId() zone ID}. This is not part of the ISO-8601 standard.
+ * Parsing is case sensitive.
+ * <li>A close square bracket ']'.
+ * </ul>
+ * <p>
+ * As this formatter has an optional element, it may be necessary to parse using
+ * {@link DateTimeFormatter#parseBest}.
+ * <p>
+ * The returned formatter has a chronology of ISO set to ensure dates in
+ * other calendar systems are correctly converted.
+ * It has no override zone and uses the {@link ResolverStyle#STRICT STRICT} resolver style.
+ */
+ public static final DateTimeFormatter ISO_DATE_TIME;
+ static {
+ ISO_DATE_TIME = new DateTimeFormatterBuilder()
+ .append(ISO_LOCAL_DATE_TIME)
+ .optionalStart()
+ .appendOffsetId()
+ .optionalStart()
+ .appendLiteral('[')
+ .parseCaseSensitive()
+ .appendZoneRegionId()
+ .appendLiteral(']')
+ .toFormatter(ResolverStyle.STRICT, IsoChronology.INSTANCE);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * The ISO date formatter that formats or parses the ordinal date
+ * without an offset, such as '2012-337'.
+ * <p>
+ * This returns an immutable formatter capable of formatting and parsing
+ * the ISO-8601 extended ordinal date format.
+ * The format consists of:
+ * <ul>
+ * <li>Four digits or more for the {@link ChronoField#YEAR year}.
+ * Years in the range 0000 to 9999 will be pre-padded by zero to ensure four digits.
+ * Years outside that range will have a prefixed positive or negative symbol.
+ * <li>A dash
+ * <li>Three digits for the {@link ChronoField#DAY_OF_YEAR day-of-year}.
+ * This is pre-padded by zero to ensure three digits.
+ * <li>If the offset is not available to format or parse then the format is complete.
+ * <li>The {@link ZoneOffset#getId() offset ID}. If the offset has seconds then
+ * they will be handled even though this is not part of the ISO-8601 standard.
+ * Parsing is case insensitive.
+ * </ul>
+ * <p>
+ * As this formatter has an optional element, it may be necessary to parse using
+ * {@link DateTimeFormatter#parseBest}.
+ * <p>
+ * The returned formatter has a chronology of ISO set to ensure dates in
+ * other calendar systems are correctly converted.
+ * It has no override zone and uses the {@link ResolverStyle#STRICT STRICT} resolver style.
+ */
+ public static final DateTimeFormatter ISO_ORDINAL_DATE;
+ static {
+ ISO_ORDINAL_DATE = new DateTimeFormatterBuilder()
+ .parseCaseInsensitive()
+ .appendValue(YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
+ .appendLiteral('-')
+ .appendValue(DAY_OF_YEAR, 3)
+ .optionalStart()
+ .appendOffsetId()
+ .toFormatter(ResolverStyle.STRICT, IsoChronology.INSTANCE);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * The ISO date formatter that formats or parses the week-based date
+ * without an offset, such as '2012-W48-6'.
+ * <p>
+ * This returns an immutable formatter capable of formatting and parsing
+ * the ISO-8601 extended week-based date format.
+ * The format consists of:
+ * <ul>
+ * <li>Four digits or more for the {@link IsoFields#WEEK_BASED_YEAR week-based-year}.
+ * Years in the range 0000 to 9999 will be pre-padded by zero to ensure four digits.
+ * Years outside that range will have a prefixed positive or negative symbol.
+ * <li>A dash
+ * <li>The letter 'W'. Parsing is case insensitive.
+ * <li>Two digits for the {@link IsoFields#WEEK_OF_WEEK_BASED_YEAR week-of-week-based-year}.
+ * This is pre-padded by zero to ensure three digits.
+ * <li>A dash
+ * <li>One digit for the {@link ChronoField#DAY_OF_WEEK day-of-week}.
+ * The value run from Monday (1) to Sunday (7).
+ * <li>If the offset is not available to format or parse then the format is complete.
+ * <li>The {@link ZoneOffset#getId() offset ID}. If the offset has seconds then
+ * they will be handled even though this is not part of the ISO-8601 standard.
+ * Parsing is case insensitive.
+ * </ul>
+ * <p>
+ * As this formatter has an optional element, it may be necessary to parse using
+ * {@link DateTimeFormatter#parseBest}.
+ * <p>
+ * The returned formatter has a chronology of ISO set to ensure dates in
+ * other calendar systems are correctly converted.
+ * It has no override zone and uses the {@link ResolverStyle#STRICT STRICT} resolver style.
+ */
+ public static final DateTimeFormatter ISO_WEEK_DATE;
+ static {
+ ISO_WEEK_DATE = new DateTimeFormatterBuilder()
+ .parseCaseInsensitive()
+ .appendValue(IsoFields.WEEK_BASED_YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
+ .appendLiteral("-W")
+ .appendValue(IsoFields.WEEK_OF_WEEK_BASED_YEAR, 2)
+ .appendLiteral('-')
+ .appendValue(DAY_OF_WEEK, 1)
+ .optionalStart()
+ .appendOffsetId()
+ .toFormatter(ResolverStyle.STRICT, IsoChronology.INSTANCE);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * The ISO instant formatter that formats or parses an instant in UTC,
+ * such as '2011-12-03T10:15:30Z'.
+ * <p>
+ * This returns an immutable formatter capable of formatting and parsing
+ * the ISO-8601 instant format.
+ * When formatting, the second-of-minute is always output.
+ * The nano-of-second outputs zero, three, six or nine digits digits as necessary.
+ * When parsing, time to at least the seconds field is required.
+ * Fractional seconds from zero to nine are parsed.
+ * The localized decimal style is not used.
+ * <p>
+ * This is a special case formatter intended to allow a human readable form
+ * of an {@link java.time.Instant}. The {@code Instant} class is designed to
+ * only represent a point in time and internally stores a value in nanoseconds
+ * from a fixed epoch of 1970-01-01Z. As such, an {@code Instant} cannot be
+ * formatted as a date or time without providing some form of time-zone.
+ * This formatter allows the {@code Instant} to be formatted, by providing
+ * a suitable conversion using {@code ZoneOffset.UTC}.
+ * <p>
+ * The format consists of:
+ * <ul>
+ * <li>The {@link #ISO_OFFSET_DATE_TIME} where the instant is converted from
+ * {@link ChronoField#INSTANT_SECONDS} and {@link ChronoField#NANO_OF_SECOND}
+ * using the {@code UTC} offset. Parsing is case insensitive.
+ * </ul>
+ * <p>
+ * The returned formatter has no override chronology or zone.
+ * It uses the {@link ResolverStyle#STRICT STRICT} resolver style.
+ */
+ public static final DateTimeFormatter ISO_INSTANT;
+ static {
+ ISO_INSTANT = new DateTimeFormatterBuilder()
+ .parseCaseInsensitive()
+ .appendInstant()
+ .toFormatter(ResolverStyle.STRICT, null);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * The ISO date formatter that formats or parses a date without an
+ * offset, such as '20111203'.
+ * <p>
+ * This returns an immutable formatter capable of formatting and parsing
+ * the ISO-8601 basic local date format.
+ * The format consists of:
+ * <ul>
+ * <li>Four digits for the {@link ChronoField#YEAR year}.
+ * Only years in the range 0000 to 9999 are supported.
+ * <li>Two digits for the {@link ChronoField#MONTH_OF_YEAR month-of-year}.
+ * This is pre-padded by zero to ensure two digits.
+ * <li>Two digits for the {@link ChronoField#DAY_OF_MONTH day-of-month}.
+ * This is pre-padded by zero to ensure two digits.
+ * <li>If the offset is not available to format or parse then the format is complete.
+ * <li>The {@link ZoneOffset#getId() offset ID} without colons. If the offset has
+ * seconds then they will be handled even though this is not part of the ISO-8601 standard.
+ * Parsing is case insensitive.
+ * </ul>
+ * <p>
+ * As this formatter has an optional element, it may be necessary to parse using
+ * {@link DateTimeFormatter#parseBest}.
+ * <p>
+ * The returned formatter has a chronology of ISO set to ensure dates in
+ * other calendar systems are correctly converted.
+ * It has no override zone and uses the {@link ResolverStyle#STRICT STRICT} resolver style.
+ */
+ public static final DateTimeFormatter BASIC_ISO_DATE;
+ static {
+ BASIC_ISO_DATE = new DateTimeFormatterBuilder()
+ .parseCaseInsensitive()
+ .appendValue(YEAR, 4)
+ .appendValue(MONTH_OF_YEAR, 2)
+ .appendValue(DAY_OF_MONTH, 2)
+ .optionalStart()
+ .appendOffset("+HHMMss", "Z")
+ .toFormatter(ResolverStyle.STRICT, IsoChronology.INSTANCE);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * The RFC-1123 date-time formatter, such as 'Tue, 3 Jun 2008 11:05:30 GMT'.
+ * <p>
+ * This returns an immutable formatter capable of formatting and parsing
+ * most of the RFC-1123 format.
+ * RFC-1123 updates RFC-822 changing the year from two digits to four.
+ * This implementation requires a four digit year.
+ * This implementation also does not handle North American or military zone
+ * names, only 'GMT' and offset amounts.
+ * <p>
+ * The format consists of:
+ * <ul>
+ * <li>If the day-of-week is not available to format or parse then jump to day-of-month.
+ * <li>Three letter {@link ChronoField#DAY_OF_WEEK day-of-week} in English.
+ * <li>A comma
+ * <li>A space
+ * <li>One or two digits for the {@link ChronoField#DAY_OF_MONTH day-of-month}.
+ * <li>A space
+ * <li>Three letter {@link ChronoField#MONTH_OF_YEAR month-of-year} in English.
+ * <li>A space
+ * <li>Four digits for the {@link ChronoField#YEAR year}.
+ * Only years in the range 0000 to 9999 are supported.
+ * <li>A space
+ * <li>Two digits for the {@link ChronoField#HOUR_OF_DAY hour-of-day}.
+ * This is pre-padded by zero to ensure two digits.
+ * <li>A colon
+ * <li>Two digits for the {@link ChronoField#MINUTE_OF_HOUR minute-of-hour}.
+ * This is pre-padded by zero to ensure two digits.
+ * <li>If the second-of-minute is not available then jump to the next space.
+ * <li>A colon
+ * <li>Two digits for the {@link ChronoField#SECOND_OF_MINUTE second-of-minute}.
+ * This is pre-padded by zero to ensure two digits.
+ * <li>A space
+ * <li>The {@link ZoneOffset#getId() offset ID} without colons or seconds.
+ * An offset of zero uses "GMT". North American zone names and military zone names are not handled.
+ * </ul>
+ * <p>
+ * Parsing is case insensitive.
+ * <p>
+ * The returned formatter has a chronology of ISO set to ensure dates in
+ * other calendar systems are correctly converted.
+ * It has no override zone and uses the {@link ResolverStyle#SMART SMART} resolver style.
+ */
+ public static final DateTimeFormatter RFC_1123_DATE_TIME;
+ static {
+ // manually code maps to ensure correct data always used
+ // (locale data can be changed by application code)
+ Map<Long, String> dow = new HashMap<>();
+ dow.put(1L, "Mon");
+ dow.put(2L, "Tue");
+ dow.put(3L, "Wed");
+ dow.put(4L, "Thu");
+ dow.put(5L, "Fri");
+ dow.put(6L, "Sat");
+ dow.put(7L, "Sun");
+ Map<Long, String> moy = new HashMap<>();
+ moy.put(1L, "Jan");
+ moy.put(2L, "Feb");
+ moy.put(3L, "Mar");
+ moy.put(4L, "Apr");
+ moy.put(5L, "May");
+ moy.put(6L, "Jun");
+ moy.put(7L, "Jul");
+ moy.put(8L, "Aug");
+ moy.put(9L, "Sep");
+ moy.put(10L, "Oct");
+ moy.put(11L, "Nov");
+ moy.put(12L, "Dec");
+ RFC_1123_DATE_TIME = new DateTimeFormatterBuilder()
+ .parseCaseInsensitive()
+ .parseLenient()
+ .optionalStart()
+ .appendText(DAY_OF_WEEK, dow)
+ .appendLiteral(", ")
+ .optionalEnd()
+ .appendValue(DAY_OF_MONTH, 1, 2, SignStyle.NOT_NEGATIVE)
+ .appendLiteral(' ')
+ .appendText(MONTH_OF_YEAR, moy)
+ .appendLiteral(' ')
+ .appendValue(YEAR, 4) // 2 digit year not handled
+ .appendLiteral(' ')
+ .appendValue(HOUR_OF_DAY, 2)
+ .appendLiteral(':')
+ .appendValue(MINUTE_OF_HOUR, 2)
+ .optionalStart()
+ .appendLiteral(':')
+ .appendValue(SECOND_OF_MINUTE, 2)
+ .optionalEnd()
+ .appendLiteral(' ')
+ .appendOffset("+HHMM", "GMT") // should handle UT/Z/EST/EDT/CST/CDT/MST/MDT/PST/MDT
+ .toFormatter(ResolverStyle.SMART, IsoChronology.INSTANCE);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * A query that provides access to the excess days that were parsed.
+ * <p>
+ * This returns a singleton {@linkplain TemporalQuery query} that provides
+ * access to additional information from the parse. The query always returns
+ * a non-null period, with a zero period returned instead of null.
+ * <p>
+ * There are two situations where this query may return a non-zero period.
+ * <ul>
+ * <li>If the {@code ResolverStyle} is {@code LENIENT} and a time is parsed
+ * without a date, then the complete result of the parse consists of a
+ * {@code LocalTime} and an excess {@code Period} in days.
+ *
+ * <li>If the {@code ResolverStyle} is {@code SMART} and a time is parsed
+ * without a date where the time is 24:00:00, then the complete result of
+ * the parse consists of a {@code LocalTime} of 00:00:00 and an excess
+ * {@code Period} of one day.
+ * </ul>
+ * <p>
+ * In both cases, if a complete {@code ChronoLocalDateTime} or {@code Instant}
+ * is parsed, then the excess days are added to the date part.
+ * As a result, this query will return a zero period.
+ * <p>
+ * The {@code SMART} behaviour handles the common "end of day" 24:00 value.
+ * Processing in {@code LENIENT} mode also produces the same result:
+ * <pre>
+ * Text to parse Parsed object Excess days
+ * "2012-12-03T00:00" LocalDateTime.of(2012, 12, 3, 0, 0) ZERO
+ * "2012-12-03T24:00" LocalDateTime.of(2012, 12, 4, 0, 0) ZERO
+ * "00:00" LocalTime.of(0, 0) ZERO
+ * "24:00" LocalTime.of(0, 0) Period.ofDays(1)
+ * </pre>
+ * The query can be used as follows:
+ * <pre>
+ * TemporalAccessor parsed = formatter.parse(str);
+ * LocalTime time = parsed.query(LocalTime::from);
+ * Period extraDays = parsed.query(DateTimeFormatter.parsedExcessDays());
+ * </pre>
+ * @return a query that provides access to the excess days that were parsed
+ */
+ public static final TemporalQuery<Period> parsedExcessDays() {
+ return PARSED_EXCESS_DAYS;
+ }
+ private static final TemporalQuery<Period> PARSED_EXCESS_DAYS = t -> {
+ if (t instanceof Parsed) {
+ return ((Parsed) t).excessDays;
+ } else {
+ return Period.ZERO;
+ }
+ };
+
+ /**
+ * A query that provides access to whether a leap-second was parsed.
+ * <p>
+ * This returns a singleton {@linkplain TemporalQuery query} that provides
+ * access to additional information from the parse. The query always returns
+ * a non-null boolean, true if parsing saw a leap-second, false if not.
+ * <p>
+ * Instant parsing handles the special "leap second" time of '23:59:60'.
+ * Leap seconds occur at '23:59:60' in the UTC time-zone, but at other
+ * local times in different time-zones. To avoid this potential ambiguity,
+ * the handling of leap-seconds is limited to
+ * {@link DateTimeFormatterBuilder#appendInstant()}, as that method
+ * always parses the instant with the UTC zone offset.
+ * <p>
+ * If the time '23:59:60' is received, then a simple conversion is applied,
+ * replacing the second-of-minute of 60 with 59. This query can be used
+ * on the parse result to determine if the leap-second adjustment was made.
+ * The query will return {@code true} if it did adjust to remove the
+ * leap-second, and {@code false} if not. Note that applying a leap-second
+ * smoothing mechanism, such as UTC-SLS, is the responsibility of the
+ * application, as follows:
+ * <pre>
+ * TemporalAccessor parsed = formatter.parse(str);
+ * Instant instant = parsed.query(Instant::from);
+ * if (parsed.query(DateTimeFormatter.parsedLeapSecond())) {
+ * // validate leap-second is correct and apply correct smoothing
+ * }
+ * </pre>
+ * @return a query that provides access to whether a leap-second was parsed
+ */
+ public static final TemporalQuery<Boolean> parsedLeapSecond() {
+ return PARSED_LEAP_SECOND;
+ }
+ private static final TemporalQuery<Boolean> PARSED_LEAP_SECOND = t -> {
+ if (t instanceof Parsed) {
+ return ((Parsed) t).leapSecond;
+ } else {
+ return Boolean.FALSE;
+ }
+ };
+
+ //-----------------------------------------------------------------------
+ /**
+ * Constructor.
+ *
+ * @param printerParser the printer/parser to use, not null
+ * @param locale the locale to use, not null
+ * @param decimalStyle the DecimalStyle to use, not null
+ * @param resolverStyle the resolver style to use, not null
+ * @param resolverFields the fields to use during resolving, null for all fields
+ * @param chrono the chronology to use, null for no override
+ * @param zone the zone to use, null for no override
+ */
+ DateTimeFormatter(CompositePrinterParser printerParser,
+ Locale locale, DecimalStyle decimalStyle,
+ ResolverStyle resolverStyle, Set<TemporalField> resolverFields,
+ Chronology chrono, ZoneId zone) {
+ this.printerParser = Objects.requireNonNull(printerParser, "printerParser");
+ this.resolverFields = resolverFields;
+ this.locale = Objects.requireNonNull(locale, "locale");
+ this.decimalStyle = Objects.requireNonNull(decimalStyle, "decimalStyle");
+ this.resolverStyle = Objects.requireNonNull(resolverStyle, "resolverStyle");
+ this.chrono = chrono;
+ this.zone = zone;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the locale to be used during formatting.
+ * <p>
+ * This is used to lookup any part of the formatter needing specific
+ * localization, such as the text or localized pattern.
+ *
+ * @return the locale of this formatter, not null
+ */
+ public Locale getLocale() {
+ return locale;
+ }
+
+ /**
+ * Returns a copy of this formatter with a new locale.
+ * <p>
+ * This is used to lookup any part of the formatter needing specific
+ * localization, such as the text or localized pattern.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param locale the new locale, not null
+ * @return a formatter based on this formatter with the requested locale, not null
+ */
+ public DateTimeFormatter withLocale(Locale locale) {
+ if (this.locale.equals(locale)) {
+ return this;
+ }
+ return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, resolverFields, chrono, zone);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the DecimalStyle to be used during formatting.
+ *
+ * @return the locale of this formatter, not null
+ */
+ public DecimalStyle getDecimalStyle() {
+ return decimalStyle;
+ }
+
+ /**
+ * Returns a copy of this formatter with a new DecimalStyle.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param decimalStyle the new DecimalStyle, not null
+ * @return a formatter based on this formatter with the requested DecimalStyle, not null
+ */
+ public DateTimeFormatter withDecimalStyle(DecimalStyle decimalStyle) {
+ if (this.decimalStyle.equals(decimalStyle)) {
+ return this;
+ }
+ return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, resolverFields, chrono, zone);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the overriding chronology to be used during formatting.
+ * <p>
+ * This returns the override chronology, used to convert dates.
+ * By default, a formatter has no override chronology, returning null.
+ * See {@link #withChronology(Chronology)} for more details on overriding.
+ *
+ * @return the override chronology of this formatter, null if no override
+ */
+ public Chronology getChronology() {
+ return chrono;
+ }
+
+ /**
+ * Returns a copy of this formatter with a new override chronology.
+ * <p>
+ * This returns a formatter with similar state to this formatter but
+ * with the override chronology set.
+ * By default, a formatter has no override chronology, returning null.
+ * <p>
+ * If an override is added, then any date that is formatted or parsed will be affected.
+ * <p>
+ * When formatting, if the temporal object contains a date, then it will
+ * be converted to a date in the override chronology.
+ * Whether the temporal contains a date is determined by querying the
+ * {@link ChronoField#EPOCH_DAY EPOCH_DAY} field.
+ * Any time or zone will be retained unaltered unless overridden.
+ * <p>
+ * If the temporal object does not contain a date, but does contain one
+ * or more {@code ChronoField} date fields, then a {@code DateTimeException}
+ * is thrown. In all other cases, the override chronology is added to the temporal,
+ * replacing any previous chronology, but without changing the date/time.
+ * <p>
+ * When parsing, there are two distinct cases to consider.
+ * If a chronology has been parsed directly from the text, perhaps because
+ * {@link DateTimeFormatterBuilder#appendChronologyId()} was used, then
+ * this override chronology has no effect.
+ * If no zone has been parsed, then this override chronology will be used
+ * to interpret the {@code ChronoField} values into a date according to the
+ * date resolving rules of the chronology.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param chrono the new chronology, null if no override
+ * @return a formatter based on this formatter with the requested override chronology, not null
+ */
+ public DateTimeFormatter withChronology(Chronology chrono) {
+ if (Objects.equals(this.chrono, chrono)) {
+ return this;
+ }
+ return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, resolverFields, chrono, zone);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the overriding zone to be used during formatting.
+ * <p>
+ * This returns the override zone, used to convert instants.
+ * By default, a formatter has no override zone, returning null.
+ * See {@link #withZone(ZoneId)} for more details on overriding.
+ *
+ * @return the override zone of this formatter, null if no override
+ */
+ public ZoneId getZone() {
+ return zone;
+ }
+
+ /**
+ * Returns a copy of this formatter with a new override zone.
+ * <p>
+ * This returns a formatter with similar state to this formatter but
+ * with the override zone set.
+ * By default, a formatter has no override zone, returning null.
+ * <p>
+ * If an override is added, then any instant that is formatted or parsed will be affected.
+ * <p>
+ * When formatting, if the temporal object contains an instant, then it will
+ * be converted to a zoned date-time using the override zone.
+ * Whether the temporal is an instant is determined by querying the
+ * {@link ChronoField#INSTANT_SECONDS INSTANT_SECONDS} field.
+ * If the input has a chronology then it will be retained unless overridden.
+ * If the input does not have a chronology, such as {@code Instant}, then
+ * the ISO chronology will be used.
+ * <p>
+ * If the temporal object does not contain an instant, but does contain
+ * an offset then an additional check is made. If the normalized override
+ * zone is an offset that differs from the offset of the temporal, then
+ * a {@code DateTimeException} is thrown. In all other cases, the override
+ * zone is added to the temporal, replacing any previous zone, but without
+ * changing the date/time.
+ * <p>
+ * When parsing, there are two distinct cases to consider.
+ * If a zone has been parsed directly from the text, perhaps because
+ * {@link DateTimeFormatterBuilder#appendZoneId()} was used, then
+ * this override zone has no effect.
+ * If no zone has been parsed, then this override zone will be included in
+ * the result of the parse where it can be used to build instants and date-times.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param zone the new override zone, null if no override
+ * @return a formatter based on this formatter with the requested override zone, not null
+ */
+ public DateTimeFormatter withZone(ZoneId zone) {
+ if (Objects.equals(this.zone, zone)) {
+ return this;
+ }
+ return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, resolverFields, chrono, zone);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the resolver style to use during parsing.
+ * <p>
+ * This returns the resolver style, used during the second phase of parsing
+ * when fields are resolved into dates and times.
+ * By default, a formatter has the {@link ResolverStyle#SMART SMART} resolver style.
+ * See {@link #withResolverStyle(ResolverStyle)} for more details.
+ *
+ * @return the resolver style of this formatter, not null
+ */
+ public ResolverStyle getResolverStyle() {
+ return resolverStyle;
+ }
+
+ /**
+ * Returns a copy of this formatter with a new resolver style.
+ * <p>
+ * This returns a formatter with similar state to this formatter but
+ * with the resolver style set. By default, a formatter has the
+ * {@link ResolverStyle#SMART SMART} resolver style.
+ * <p>
+ * Changing the resolver style only has an effect during parsing.
+ * Parsing a text string occurs in two phases.
+ * Phase 1 is a basic text parse according to the fields added to the builder.
+ * Phase 2 resolves the parsed field-value pairs into date and/or time objects.
+ * The resolver style is used to control how phase 2, resolving, happens.
+ * See {@code ResolverStyle} for more information on the options available.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param resolverStyle the new resolver style, not null
+ * @return a formatter based on this formatter with the requested resolver style, not null
+ */
+ public DateTimeFormatter withResolverStyle(ResolverStyle resolverStyle) {
+ Objects.requireNonNull(resolverStyle, "resolverStyle");
+ if (Objects.equals(this.resolverStyle, resolverStyle)) {
+ return this;
+ }
+ return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, resolverFields, chrono, zone);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the resolver fields to use during parsing.
+ * <p>
+ * This returns the resolver fields, used during the second phase of parsing
+ * when fields are resolved into dates and times.
+ * By default, a formatter has no resolver fields, and thus returns null.
+ * See {@link #withResolverFields(Set)} for more details.
+ *
+ * @return the immutable set of resolver fields of this formatter, null if no fields
+ */
+ public Set<TemporalField> getResolverFields() {
+ return resolverFields;
+ }
+
+ /**
+ * Returns a copy of this formatter with a new set of resolver fields.
+ * <p>
+ * This returns a formatter with similar state to this formatter but with
+ * the resolver fields set. By default, a formatter has no resolver fields.
+ * <p>
+ * Changing the resolver fields only has an effect during parsing.
+ * Parsing a text string occurs in two phases.
+ * Phase 1 is a basic text parse according to the fields added to the builder.
+ * Phase 2 resolves the parsed field-value pairs into date and/or time objects.
+ * The resolver fields are used to filter the field-value pairs between phase 1 and 2.
+ * <p>
+ * This can be used to select between two or more ways that a date or time might
+ * be resolved. For example, if the formatter consists of year, month, day-of-month
+ * and day-of-year, then there are two ways to resolve a date.
+ * Calling this method with the arguments {@link ChronoField#YEAR YEAR} and
+ * {@link ChronoField#DAY_OF_YEAR DAY_OF_YEAR} will ensure that the date is
+ * resolved using the year and day-of-year, effectively meaning that the month
+ * and day-of-month are ignored during the resolving phase.
+ * <p>
+ * In a similar manner, this method can be used to ignore secondary fields that
+ * would otherwise be cross-checked. For example, if the formatter consists of year,
+ * month, day-of-month and day-of-week, then there is only one way to resolve a
+ * date, but the parsed value for day-of-week will be cross-checked against the
+ * resolved date. Calling this method with the arguments {@link ChronoField#YEAR YEAR},
+ * {@link ChronoField#MONTH_OF_YEAR MONTH_OF_YEAR} and
+ * {@link ChronoField#DAY_OF_MONTH DAY_OF_MONTH} will ensure that the date is
+ * resolved correctly, but without any cross-check for the day-of-week.
+ * <p>
+ * In implementation terms, this method behaves as follows. The result of the
+ * parsing phase can be considered to be a map of field to value. The behavior
+ * of this method is to cause that map to be filtered between phase 1 and 2,
+ * removing all fields other than those specified as arguments to this method.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param resolverFields the new set of resolver fields, null if no fields
+ * @return a formatter based on this formatter with the requested resolver style, not null
+ */
+ public DateTimeFormatter withResolverFields(TemporalField... resolverFields) {
+ Set<TemporalField> fields = null;
+ if (resolverFields != null) {
+ fields = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(resolverFields)));
+ }
+ if (Objects.equals(this.resolverFields, fields)) {
+ return this;
+ }
+ return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, fields, chrono, zone);
+ }
+
+ /**
+ * Returns a copy of this formatter with a new set of resolver fields.
+ * <p>
+ * This returns a formatter with similar state to this formatter but with
+ * the resolver fields set. By default, a formatter has no resolver fields.
+ * <p>
+ * Changing the resolver fields only has an effect during parsing.
+ * Parsing a text string occurs in two phases.
+ * Phase 1 is a basic text parse according to the fields added to the builder.
+ * Phase 2 resolves the parsed field-value pairs into date and/or time objects.
+ * The resolver fields are used to filter the field-value pairs between phase 1 and 2.
+ * <p>
+ * This can be used to select between two or more ways that a date or time might
+ * be resolved. For example, if the formatter consists of year, month, day-of-month
+ * and day-of-year, then there are two ways to resolve a date.
+ * Calling this method with the arguments {@link ChronoField#YEAR YEAR} and
+ * {@link ChronoField#DAY_OF_YEAR DAY_OF_YEAR} will ensure that the date is
+ * resolved using the year and day-of-year, effectively meaning that the month
+ * and day-of-month are ignored during the resolving phase.
+ * <p>
+ * In a similar manner, this method can be used to ignore secondary fields that
+ * would otherwise be cross-checked. For example, if the formatter consists of year,
+ * month, day-of-month and day-of-week, then there is only one way to resolve a
+ * date, but the parsed value for day-of-week will be cross-checked against the
+ * resolved date. Calling this method with the arguments {@link ChronoField#YEAR YEAR},
+ * {@link ChronoField#MONTH_OF_YEAR MONTH_OF_YEAR} and
+ * {@link ChronoField#DAY_OF_MONTH DAY_OF_MONTH} will ensure that the date is
+ * resolved correctly, but without any cross-check for the day-of-week.
+ * <p>
+ * In implementation terms, this method behaves as follows. The result of the
+ * parsing phase can be considered to be a map of field to value. The behavior
+ * of this method is to cause that map to be filtered between phase 1 and 2,
+ * removing all fields other than those specified as arguments to this method.
+ * <p>
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param resolverFields the new set of resolver fields, null if no fields
+ * @return a formatter based on this formatter with the requested resolver style, not null
+ */
+ public DateTimeFormatter withResolverFields(Set<TemporalField> resolverFields) {
+ if (Objects.equals(this.resolverFields, resolverFields)) {
+ return this;
+ }
+ if (resolverFields != null) {
+ resolverFields = Collections.unmodifiableSet(new HashSet<>(resolverFields));
+ }
+ return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, resolverFields, chrono, zone);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Formats a date-time object using this formatter.
+ * <p>
+ * This formats the date-time to a String using the rules of the formatter.
+ *
+ * @param temporal the temporal object to format, not null
+ * @return the formatted string, not null
+ * @throws DateTimeException if an error occurs during formatting
+ */
+ public String format(TemporalAccessor temporal) {
+ StringBuilder buf = new StringBuilder(32);
+ formatTo(temporal, buf);
+ return buf.toString();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Formats a date-time object to an {@code Appendable} using this formatter.
+ * <p>
+ * This outputs the formatted date-time to the specified destination.
+ * {@link Appendable} is a general purpose interface that is implemented by all
+ * key character output classes including {@code StringBuffer}, {@code StringBuilder},
+ * {@code PrintStream} and {@code Writer}.
+ * <p>
+ * Although {@code Appendable} methods throw an {@code IOException}, this method does not.
+ * Instead, any {@code IOException} is wrapped in a runtime exception.
+ *
+ * @param temporal the temporal object to format, not null
+ * @param appendable the appendable to format to, not null
+ * @throws DateTimeException if an error occurs during formatting
+ */
+ public void formatTo(TemporalAccessor temporal, Appendable appendable) {
+ Objects.requireNonNull(temporal, "temporal");
+ Objects.requireNonNull(appendable, "appendable");
+ try {
+ DateTimePrintContext context = new DateTimePrintContext(temporal, this);
+ if (appendable instanceof StringBuilder) {
+ printerParser.format(context, (StringBuilder) appendable);
+ } else {
+ // buffer output to avoid writing to appendable in case of error
+ StringBuilder buf = new StringBuilder(32);
+ printerParser.format(context, buf);
+ appendable.append(buf);
+ }
+ } catch (IOException ex) {
+ throw new DateTimeException(ex.getMessage(), ex);
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Fully parses the text producing a temporal object.
+ * <p>
+ * This parses the entire text producing a temporal object.
+ * It is typically more useful to use {@link #parse(CharSequence, TemporalQuery)}.
+ * The result of this method is {@code TemporalAccessor} which has been resolved,
+ * applying basic validation checks to help ensure a valid date-time.
+ * <p>
+ * If the parse completes without reading the entire length of the text,
+ * or a problem occurs during parsing or merging, then an exception is thrown.
+ *
+ * @param text the text to parse, not null
+ * @return the parsed temporal object, not null
+ * @throws DateTimeParseException if unable to parse the requested result
+ */
+ public TemporalAccessor parse(CharSequence text) {
+ Objects.requireNonNull(text, "text");
+ try {
+ return parseResolved0(text, null);
+ } catch (DateTimeParseException ex) {
+ throw ex;
+ } catch (RuntimeException ex) {
+ throw createError(text, ex);
+ }
+ }
+
+ /**
+ * Parses the text using this formatter, providing control over the text position.
+ * <p>
+ * This parses the text without requiring the parse to start from the beginning
+ * of the string or finish at the end.
+ * The result of this method is {@code TemporalAccessor} which has been resolved,
+ * applying basic validation checks to help ensure a valid date-time.
+ * <p>
+ * The text will be parsed from the specified start {@code ParsePosition}.
+ * The entire length of the text does not have to be parsed, the {@code ParsePosition}
+ * will be updated with the index at the end of parsing.
+ * <p>
+ * The operation of this method is slightly different to similar methods using
+ * {@code ParsePosition} on {@code java.text.Format}. That class will return
+ * errors using the error index on the {@code ParsePosition}. By contrast, this
+ * method will throw a {@link DateTimeParseException} if an error occurs, with
+ * the exception containing the error index.
+ * This change in behavior is necessary due to the increased complexity of
+ * parsing and resolving dates/times in this API.
+ * <p>
+ * If the formatter parses the same field more than once with different values,
+ * the result will be an error.
+ *
+ * @param text the text to parse, not null
+ * @param position the position to parse from, updated with length parsed
+ * and the index of any error, not null
+ * @return the parsed temporal object, not null
+ * @throws DateTimeParseException if unable to parse the requested result
+ * @throws IndexOutOfBoundsException if the position is invalid
+ */
+ public TemporalAccessor parse(CharSequence text, ParsePosition position) {
+ Objects.requireNonNull(text, "text");
+ Objects.requireNonNull(position, "position");
+ try {
+ return parseResolved0(text, position);
+ } catch (DateTimeParseException | IndexOutOfBoundsException ex) {
+ throw ex;
+ } catch (RuntimeException ex) {
+ throw createError(text, ex);
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Fully parses the text producing an object of the specified type.
+ * <p>
+ * Most applications should use this method for parsing.
+ * It parses the entire text to produce the required date-time.
+ * The query is typically a method reference to a {@code from(TemporalAccessor)} method.
+ * For example:
+ * <pre>
+ * LocalDateTime dt = parser.parse(str, LocalDateTime::from);
+ * </pre>
+ * If the parse completes without reading the entire length of the text,
+ * or a problem occurs during parsing or merging, then an exception is thrown.
+ *
+ * @param <T> the type of the parsed date-time
+ * @param text the text to parse, not null
+ * @param query the query defining the type to parse to, not null
+ * @return the parsed date-time, not null
+ * @throws DateTimeParseException if unable to parse the requested result
+ */
+ public <T> T parse(CharSequence text, TemporalQuery<T> query) {
+ Objects.requireNonNull(text, "text");
+ Objects.requireNonNull(query, "query");
+ try {
+ return parseResolved0(text, null).query(query);
+ } catch (DateTimeParseException ex) {
+ throw ex;
+ } catch (RuntimeException ex) {
+ throw createError(text, ex);
+ }
+ }
+
+ /**
+ * Fully parses the text producing an object of one of the specified types.
+ * <p>
+ * This parse method is convenient for use when the parser can handle optional elements.
+ * For example, a pattern of 'uuuu-MM-dd HH.mm[ VV]' can be fully parsed to a {@code ZonedDateTime},
+ * or partially parsed to a {@code LocalDateTime}.
+ * The queries must be specified in order, starting from the best matching full-parse option
+ * and ending with the worst matching minimal parse option.
+ * The query is typically a method reference to a {@code from(TemporalAccessor)} method.
+ * <p>
+ * The result is associated with the first type that successfully parses.
+ * Normally, applications will use {@code instanceof} to check the result.
+ * For example:
+ * <pre>
+ * TemporalAccessor dt = parser.parseBest(str, ZonedDateTime::from, LocalDateTime::from);
+ * if (dt instanceof ZonedDateTime) {
+ * ...
+ * } else {
+ * ...
+ * }
+ * </pre>
+ * If the parse completes without reading the entire length of the text,
+ * or a problem occurs during parsing or merging, then an exception is thrown.
+ *
+ * @param text the text to parse, not null
+ * @param queries the queries defining the types to attempt to parse to,
+ * must implement {@code TemporalAccessor}, not null
+ * @return the parsed date-time, not null
+ * @throws IllegalArgumentException if less than 2 types are specified
+ * @throws DateTimeParseException if unable to parse the requested result
+ */
+ public TemporalAccessor parseBest(CharSequence text, TemporalQuery<?>... queries) {
+ Objects.requireNonNull(text, "text");
+ Objects.requireNonNull(queries, "queries");
+ if (queries.length < 2) {
+ throw new IllegalArgumentException("At least two queries must be specified");
+ }
+ try {
+ TemporalAccessor resolved = parseResolved0(text, null);
+ for (TemporalQuery<?> query : queries) {
+ try {
+ return (TemporalAccessor) resolved.query(query);
+ } catch (RuntimeException ex) {
+ // continue
+ }
+ }
+ throw new DateTimeException("Unable to convert parsed text using any of the specified queries");
+ } catch (DateTimeParseException ex) {
+ throw ex;
+ } catch (RuntimeException ex) {
+ throw createError(text, ex);
+ }
+ }
+
+ private DateTimeParseException createError(CharSequence text, RuntimeException ex) {
+ String abbr;
+ if (text.length() > 64) {
+ abbr = text.subSequence(0, 64).toString() + "...";
+ } else {
+ abbr = text.toString();
+ }
+ return new DateTimeParseException("Text '" + abbr + "' could not be parsed: " + ex.getMessage(), text, 0, ex);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Parses and resolves the specified text.
+ * <p>
+ * This parses to a {@code TemporalAccessor} ensuring that the text is fully parsed.
+ *
+ * @param text the text to parse, not null
+ * @param position the position to parse from, updated with length parsed
+ * and the index of any error, null if parsing whole string
+ * @return the resolved result of the parse, not null
+ * @throws DateTimeParseException if the parse fails
+ * @throws DateTimeException if an error occurs while resolving the date or time
+ * @throws IndexOutOfBoundsException if the position is invalid
+ */
+ private TemporalAccessor parseResolved0(final CharSequence text, final ParsePosition position) {
+ ParsePosition pos = (position != null ? position : new ParsePosition(0));
+ DateTimeParseContext context = parseUnresolved0(text, pos);
+ if (context == null || pos.getErrorIndex() >= 0 || (position == null && pos.getIndex() < text.length())) {
+ String abbr;
+ if (text.length() > 64) {
+ abbr = text.subSequence(0, 64).toString() + "...";
+ } else {
+ abbr = text.toString();
+ }
+ if (pos.getErrorIndex() >= 0) {
+ throw new DateTimeParseException("Text '" + abbr + "' could not be parsed at index " +
+ pos.getErrorIndex(), text, pos.getErrorIndex());
+ } else {
+ throw new DateTimeParseException("Text '" + abbr + "' could not be parsed, unparsed text found at index " +
+ pos.getIndex(), text, pos.getIndex());
+ }
+ }
+ return context.toResolved(resolverStyle, resolverFields);
+ }
+
+ /**
+ * Parses the text using this formatter, without resolving the result, intended
+ * for advanced use cases.
+ * <p>
+ * Parsing is implemented as a two-phase operation.
+ * First, the text is parsed using the layout defined by the formatter, producing
+ * a {@code Map} of field to value, a {@code ZoneId} and a {@code Chronology}.
+ * Second, the parsed data is <em>resolved</em>, by validating, combining and
+ * simplifying the various fields into more useful ones.
+ * This method performs the parsing stage but not the resolving stage.
+ * <p>
+ * The result of this method is {@code TemporalAccessor} which represents the
+ * data as seen in the input. Values are not validated, thus parsing a date string
+ * of '2012-00-65' would result in a temporal with three fields - year of '2012',
+ * month of '0' and day-of-month of '65'.
+ * <p>
+ * The text will be parsed from the specified start {@code ParsePosition}.
+ * The entire length of the text does not have to be parsed, the {@code ParsePosition}
+ * will be updated with the index at the end of parsing.
+ * <p>
+ * Errors are returned using the error index field of the {@code ParsePosition}
+ * instead of {@code DateTimeParseException}.
+ * The returned error index will be set to an index indicative of the error.
+ * Callers must check for errors before using the result.
+ * <p>
+ * If the formatter parses the same field more than once with different values,
+ * the result will be an error.
+ * <p>
+ * This method is intended for advanced use cases that need access to the
+ * internal state during parsing. Typical application code should use
+ * {@link #parse(CharSequence, TemporalQuery)} or the parse method on the target type.
+ *
+ * @param text the text to parse, not null
+ * @param position the position to parse from, updated with length parsed
+ * and the index of any error, not null
+ * @return the parsed text, null if the parse results in an error
+ * @throws DateTimeException if some problem occurs during parsing
+ * @throws IndexOutOfBoundsException if the position is invalid
+ */
+ public TemporalAccessor parseUnresolved(CharSequence text, ParsePosition position) {
+ DateTimeParseContext context = parseUnresolved0(text, position);
+ if (context == null) {
+ return null;
+ }
+ return context.toUnresolved();
+ }
+
+ private DateTimeParseContext parseUnresolved0(CharSequence text, ParsePosition position) {
+ Objects.requireNonNull(text, "text");
+ Objects.requireNonNull(position, "position");
+ DateTimeParseContext context = new DateTimeParseContext(this);
+ int pos = position.getIndex();
+ pos = printerParser.parse(context, text, pos);
+ if (pos < 0) {
+ position.setErrorIndex(~pos); // index not updated from input
+ return null;
+ }
+ position.setIndex(pos); // errorIndex not updated from input
+ return context;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns the formatter as a composite printer parser.
+ *
+ * @param optional whether the printer/parser should be optional
+ * @return the printer/parser, not null
+ */
+ CompositePrinterParser toPrinterParser(boolean optional) {
+ return printerParser.withOptional(optional);
+ }
+
+ /**
+ * Returns this formatter as a {@code java.text.Format} instance.
+ * <p>
+ * The returned {@link Format} instance will format any {@link TemporalAccessor}
+ * and parses to a resolved {@link TemporalAccessor}.
+ * <p>
+ * Exceptions will follow the definitions of {@code Format}, see those methods
+ * for details about {@code IllegalArgumentException} during formatting and
+ * {@code ParseException} or null during parsing.
+ * The format does not support attributing of the returned format string.
+ *
+ * @return this formatter as a classic format instance, not null
+ */
+ public Format toFormat() {
+ return new ClassicFormat(this, null);
+ }
+
+ /**
+ * Returns this formatter as a {@code java.text.Format} instance that will
+ * parse using the specified query.
+ * <p>
+ * The returned {@link Format} instance will format any {@link TemporalAccessor}
+ * and parses to the type specified.
+ * The type must be one that is supported by {@link #parse}.
+ * <p>
+ * Exceptions will follow the definitions of {@code Format}, see those methods
+ * for details about {@code IllegalArgumentException} during formatting and
+ * {@code ParseException} or null during parsing.
+ * The format does not support attributing of the returned format string.
+ *
+ * @param parseQuery the query defining the type to parse to, not null
+ * @return this formatter as a classic format instance, not null
+ */
+ public Format toFormat(TemporalQuery<?> parseQuery) {
+ Objects.requireNonNull(parseQuery, "parseQuery");
+ return new ClassicFormat(this, parseQuery);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a description of the underlying formatters.
+ *
+ * @return a description of this formatter, not null
+ */
+ @Override
+ public String toString() {
+ String pattern = printerParser.toString();
+ pattern = pattern.startsWith("[") ? pattern : pattern.substring(1, pattern.length() - 1);
+ return pattern;
+ // TODO: Fix tests to not depend on toString()
+// return "DateTimeFormatter[" + locale +
+// (chrono != null ? "," + chrono : "") +
+// (zone != null ? "," + zone : "") +
+// pattern + "]";
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Implements the classic Java Format API.
+ * @serial exclude
+ */
+ @SuppressWarnings("serial") // not actually serializable
+ static class ClassicFormat extends Format {
+ /** The formatter. */
+ private final DateTimeFormatter formatter;
+ /** The type to be parsed. */
+ private final TemporalQuery<?> parseType;
+ /** Constructor. */
+ public ClassicFormat(DateTimeFormatter formatter, TemporalQuery<?> parseType) {
+ this.formatter = formatter;
+ this.parseType = parseType;
+ }
+
+ @Override
+ public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
+ Objects.requireNonNull(obj, "obj");
+ Objects.requireNonNull(toAppendTo, "toAppendTo");
+ Objects.requireNonNull(pos, "pos");
+ if (obj instanceof TemporalAccessor == false) {
+ throw new IllegalArgumentException("Format target must implement TemporalAccessor");
+ }
+ pos.setBeginIndex(0);
+ pos.setEndIndex(0);
+ try {
+ formatter.formatTo((TemporalAccessor) obj, toAppendTo);
+ } catch (RuntimeException ex) {
+ throw new IllegalArgumentException(ex.getMessage(), ex);
+ }
+ return toAppendTo;
+ }
+ @Override
+ public Object parseObject(String text) throws ParseException {
+ Objects.requireNonNull(text, "text");
+ try {
+ if (parseType == null) {
+ return formatter.parseResolved0(text, null);
+ }
+ return formatter.parse(text, parseType);
+ } catch (DateTimeParseException ex) {
+ throw new ParseException(ex.getMessage(), ex.getErrorIndex());
+ } catch (RuntimeException ex) {
+ throw (ParseException) new ParseException(ex.getMessage(), 0).initCause(ex);
+ }
+ }
+ @Override
+ public Object parseObject(String text, ParsePosition pos) {
+ Objects.requireNonNull(text, "text");
+ DateTimeParseContext context;
+ try {
+ context = formatter.parseUnresolved0(text, pos);
+ } catch (IndexOutOfBoundsException ex) {
+ if (pos.getErrorIndex() < 0) {
+ pos.setErrorIndex(0);
+ }
+ return null;
+ }
+ if (context == null) {
+ if (pos.getErrorIndex() < 0) {
+ pos.setErrorIndex(0);
+ }
+ return null;
+ }
+ try {
+ TemporalAccessor resolved = context.toResolved(formatter.resolverStyle, formatter.resolverFields);
+ if (parseType == null) {
+ return resolved;
+ }
+ return resolved.query(parseType);
+ } catch (RuntimeException ex) {
+ pos.setErrorIndex(0);
+ return null;
+ }
+ }
+ }
+
+}
diff --git a/java/time/format/DateTimeFormatterBuilder.java b/java/time/format/DateTimeFormatterBuilder.java
new file mode 100644
index 0000000..736a8fc
--- /dev/null
+++ b/java/time/format/DateTimeFormatterBuilder.java
@@ -0,0 +1,4610 @@
+/*
+ * Copyright (c) 2012, 2015, 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) 2008-2012, Stephen Colebourne & Michael Nascimento Santos
+ *
+ * All rights hg qreserved.
+ *
+ * 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.format;
+
+import android.icu.impl.ZoneMeta;
+import android.icu.text.LocaleDisplayNames;
+import android.icu.text.TimeZoneFormat;
+import android.icu.text.TimeZoneNames;
+import android.icu.util.Calendar;
+import android.icu.util.ULocale;
+
+import static java.time.temporal.ChronoField.DAY_OF_MONTH;
+import static java.time.temporal.ChronoField.HOUR_OF_DAY;
+import static java.time.temporal.ChronoField.INSTANT_SECONDS;
+import static java.time.temporal.ChronoField.MINUTE_OF_HOUR;
+import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
+import static java.time.temporal.ChronoField.NANO_OF_SECOND;
+import static java.time.temporal.ChronoField.OFFSET_SECONDS;
+import static java.time.temporal.ChronoField.SECOND_OF_MINUTE;
+import static java.time.temporal.ChronoField.YEAR;
+
+import java.lang.ref.SoftReference;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.math.RoundingMode;
+import java.text.ParsePosition;
+import java.time.DateTimeException;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.chrono.ChronoLocalDate;
+import java.time.chrono.Chronology;
+import java.time.chrono.IsoChronology;
+import java.time.format.DateTimeTextProvider.LocaleStore;
+import java.time.temporal.ChronoField;
+import java.time.temporal.IsoFields;
+import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalField;
+import java.time.temporal.TemporalQueries;
+import java.time.temporal.TemporalQuery;
+import java.time.temporal.ValueRange;
+import java.time.temporal.WeekFields;
+import java.time.zone.ZoneRulesProvider;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Set;
+import java.util.TimeZone;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * Builder to create date-time formatters.
+ * <p>
+ * This allows a {@code DateTimeFormatter} to be created.
+ * All date-time formatters are created ultimately using this builder.
+ * <p>
+ * The basic elements of date-time can all be added:
+ * <ul>
+ * <li>Value - a numeric value</li>
+ * <li>Fraction - a fractional value including the decimal place. Always use this when
+ * outputting fractions to ensure that the fraction is parsed correctly</li>
+ * <li>Text - the textual equivalent for the value</li>
+ * <li>OffsetId/Offset - the {@linkplain ZoneOffset zone offset}</li>
+ * <li>ZoneId - the {@linkplain ZoneId time-zone} id</li>
+ * <li>ZoneText - the name of the time-zone</li>
+ * <li>ChronologyId - the {@linkplain Chronology chronology} id</li>
+ * <li>ChronologyText - the name of the chronology</li>
+ * <li>Literal - a text literal</li>
+ * <li>Nested and Optional - formats can be nested or made optional</li>
+ * </ul>
+ * In addition, any of the elements may be decorated by padding, either with spaces or any other character.
+ * <p>
+ * Finally, a shorthand pattern, mostly compatible with {@code java.text.SimpleDateFormat SimpleDateFormat}
+ * can be used, see {@link #appendPattern(String)}.
+ * In practice, this simply parses the pattern and calls other methods on the builder.
+ *
+ * @implSpec
+ * This class is a mutable builder intended for use from a single thread.
+ *
+ * @since 1.8
+ */
+public final class DateTimeFormatterBuilder {
+
+ /**
+ * Query for a time-zone that is region-only.
+ */
+ private static final TemporalQuery<ZoneId> QUERY_REGION_ONLY = (temporal) -> {
+ ZoneId zone = temporal.query(TemporalQueries.zoneId());
+ return (zone != null && zone instanceof ZoneOffset == false ? zone : null);
+ };
+
+ /**
+ * The currently active builder, used by the outermost builder.
+ */
+ private DateTimeFormatterBuilder active = this;
+ /**
+ * The parent builder, null for the outermost builder.
+ */
+ private final DateTimeFormatterBuilder parent;
+ /**
+ * The list of printers that will be used.
+ */
+ private final List<DateTimePrinterParser> printerParsers = new ArrayList<>();
+ /**
+ * Whether this builder produces an optional formatter.
+ */
+ private final boolean optional;
+ /**
+ * The width to pad the next field to.
+ */
+ private int padNextWidth;
+ /**
+ * The character to pad the next field with.
+ */
+ private char padNextChar;
+ /**
+ * The index of the last variable width value parser.
+ */
+ private int valueParserIndex = -1;
+
+ /**
+ * Gets the formatting pattern for date and time styles for a locale and chronology.
+ * The locale and chronology are used to lookup the locale specific format
+ * for the requested dateStyle and/or timeStyle.
+ *
+ * @param dateStyle the FormatStyle for the date, null for time-only pattern
+ * @param timeStyle the FormatStyle for the time, null for date-only pattern
+ * @param chrono the Chronology, non-null
+ * @param locale the locale, non-null
+ * @return the locale and Chronology specific formatting pattern
+ * @throws IllegalArgumentException if both dateStyle and timeStyle are null
+ */
+ public static String getLocalizedDateTimePattern(FormatStyle dateStyle, FormatStyle timeStyle,
+ Chronology chrono, Locale locale) {
+ Objects.requireNonNull(locale, "locale");
+ Objects.requireNonNull(chrono, "chrono");
+ if (dateStyle == null && timeStyle == null) {
+ throw new IllegalArgumentException("Either dateStyle or timeStyle must be non-null");
+ }
+
+ // Android-changed: get format string from ICU.
+ // LocaleResources lr = LocaleProviderAdapter.getResourceBundleBased()
+ // .getLocaleResources(locale);
+ // String pattern = lr.getJavaTimeDateTimePattern(
+ // convertStyle(timeStyle), convertStyle(dateStyle), chrono.getCalendarType());
+ String pattern = Calendar.getDateTimeFormatString(
+ ULocale.forLocale(locale), chrono.getCalendarType(),
+ convertStyle(dateStyle), convertStyle(timeStyle));
+ return pattern;
+ }
+
+ /**
+ * Converts the given FormatStyle to the java.text.DateFormat style.
+ *
+ * @param style the FormatStyle style
+ * @return the int style, or -1 if style is null, indicating un-required
+ */
+ private static int convertStyle(FormatStyle style) {
+ if (style == null) {
+ return -1;
+ }
+ return style.ordinal(); // indices happen to align
+ }
+
+ /**
+ * Constructs a new instance of the builder.
+ */
+ public DateTimeFormatterBuilder() {
+ super();
+ parent = null;
+ optional = false;
+ }
+
+ /**
+ * Constructs a new instance of the builder.
+ *
+ * @param parent the parent builder, not null
+ * @param optional whether the formatter is optional, not null
+ */
+ private DateTimeFormatterBuilder(DateTimeFormatterBuilder parent, boolean optional) {
+ super();
+ this.parent = parent;
+ this.optional = optional;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Changes the parse style to be case sensitive for the remainder of the formatter.
+ * <p>
+ * Parsing can be case sensitive or insensitive - by default it is case sensitive.
+ * This method allows the case sensitivity setting of parsing to be changed.
+ * <p>
+ * Calling this method changes the state of the builder such that all
+ * subsequent builder method calls will parse text in case sensitive mode.
+ * See {@link #parseCaseInsensitive} for the opposite setting.
+ * The parse case sensitive/insensitive methods may be called at any point
+ * in the builder, thus the parser can swap between case parsing modes
+ * multiple times during the parse.
+ * <p>
+ * Since the default is case sensitive, this method should only be used after
+ * a previous call to {@code #parseCaseInsensitive}.
+ *
+ * @return this, for chaining, not null
+ */
+ public DateTimeFormatterBuilder parseCaseSensitive() {
+ appendInternal(SettingsParser.SENSITIVE);
+ return this;
+ }
+
+ /**
+ * Changes the parse style to be case insensitive for the remainder of the formatter.
+ * <p>
+ * Parsing can be case sensitive or insensitive - by default it is case sensitive.
+ * This method allows the case sensitivity setting of parsing to be changed.
+ * <p>
+ * Calling this method changes the state of the builder such that all
+ * subsequent builder method calls will parse text in case insensitive mode.
+ * See {@link #parseCaseSensitive()} for the opposite setting.
+ * The parse case sensitive/insensitive methods may be called at any point
+ * in the builder, thus the parser can swap between case parsing modes
+ * multiple times during the parse.
+ *
+ * @return this, for chaining, not null
+ */
+ public DateTimeFormatterBuilder parseCaseInsensitive() {
+ appendInternal(SettingsParser.INSENSITIVE);
+ return this;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Changes the parse style to be strict for the remainder of the formatter.
+ * <p>
+ * Parsing can be strict or lenient - by default its strict.
+ * This controls the degree of flexibility in matching the text and sign styles.
+ * <p>
+ * When used, this method changes the parsing to be strict from this point onwards.
+ * As strict is the default, this is normally only needed after calling {@link #parseLenient()}.
+ * The change will remain in force until the end of the formatter that is eventually
+ * constructed or until {@code parseLenient} is called.
+ *
+ * @return this, for chaining, not null
+ */
+ public DateTimeFormatterBuilder parseStrict() {
+ appendInternal(SettingsParser.STRICT);
+ return this;
+ }
+
+ /**
+ * Changes the parse style to be lenient for the remainder of the formatter.
+ * Note that case sensitivity is set separately to this method.
+ * <p>
+ * Parsing can be strict or lenient - by default its strict.
+ * This controls the degree of flexibility in matching the text and sign styles.
+ * Applications calling this method should typically also call {@link #parseCaseInsensitive()}.
+ * <p>
+ * When used, this method changes the parsing to be lenient from this point onwards.
+ * The change will remain in force until the end of the formatter that is eventually
+ * constructed or until {@code parseStrict} is called.
+ *
+ * @return this, for chaining, not null
+ */
+ public DateTimeFormatterBuilder parseLenient() {
+ appendInternal(SettingsParser.LENIENT);
+ return this;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Appends a default value for a field to the formatter for use in parsing.
+ * <p>
+ * This appends an instruction to the builder to inject a default value
+ * into the parsed result. This is especially useful in conjunction with
+ * optional parts of the formatter.
+ * <p>
+ * For example, consider a formatter that parses the year, followed by
+ * an optional month, with a further optional day-of-month. Using such a
+ * formatter would require the calling code to check whether a full date,
+ * year-month or just a year had been parsed. This method can be used to
+ * default the month and day-of-month to a sensible value, such as the
+ * first of the month, allowing the calling code to always get a date.
+ * <p>
+ * During formatting, this method has no effect.
+ * <p>
+ * During parsing, the current state of the parse is inspected.
+ * If the specified field has no associated value, because it has not been
+ * parsed successfully at that point, then the specified value is injected
+ * into the parse result. Injection is immediate, thus the field-value pair
+ * will be visible to any subsequent elements in the formatter.
+ * As such, this method is normally called at the end of the builder.
+ *
+ * @param field the field to default the value of, not null
+ * @param value the value to default the field to
+ * @return this, for chaining, not null
+ */
+ public DateTimeFormatterBuilder parseDefaulting(TemporalField field, long value) {
+ Objects.requireNonNull(field, "field");
+ appendInternal(new DefaultValueParser(field, value));
+ return this;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Appends the value of a date-time field to the formatter using a normal
+ * output style.
+ * <p>
+ * The value of the field will be output during a format.
+ * If the value cannot be obtained then an exception will be thrown.
+ * <p>
+ * The value will be printed as per the normal format of an integer value.
+ * Only negative numbers will be signed. No padding will be added.
+ * <p>
+ * The parser for a variable width value such as this normally behaves greedily,
+ * requiring one digit, but accepting as many digits as possible.
+ * This behavior can be affected by 'adjacent value parsing'.
+ * See {@link #appendValue(java.time.temporal.TemporalField, int)} for full details.
+ *
+ * @param field the field to append, not null
+ * @return this, for chaining, not null
+ */
+ public DateTimeFormatterBuilder appendValue(TemporalField field) {
+ Objects.requireNonNull(field, "field");
+ appendValue(new NumberPrinterParser(field, 1, 19, SignStyle.NORMAL));
+ return this;
+ }
+
+ /**
+ * Appends the value of a date-time field to the formatter using a fixed
+ * width, zero-padded approach.
+ * <p>
+ * The value of the field will be output during a format.
+ * If the value cannot be obtained then an exception will be thrown.
+ * <p>
+ * The value will be zero-padded on the left. If the size of the value
+ * means that it cannot be printed within the width then an exception is thrown.
+ * If the value of the field is negative then an exception is thrown during formatting.
+ * <p>
+ * This method supports a special technique of parsing known as 'adjacent value parsing'.
+ * This technique solves the problem where a value, variable or fixed width, is followed by one or more
+ * fixed length values. The standard parser is greedy, and thus it would normally
+ * steal the digits that are needed by the fixed width value parsers that follow the
+ * variable width one.
+ * <p>
+ * No action is required to initiate 'adjacent value parsing'.
+ * When a call to {@code appendValue} is made, the builder
+ * enters adjacent value parsing setup mode. If the immediately subsequent method
+ * call or calls on the same builder are for a fixed width value, then the parser will reserve
+ * space so that the fixed width values can be parsed.
+ * <p>
+ * For example, consider {@code builder.appendValue(YEAR).appendValue(MONTH_OF_YEAR, 2);}
+ * The year is a variable width parse of between 1 and 19 digits.
+ * The month is a fixed width parse of 2 digits.
+ * Because these were appended to the same builder immediately after one another,
+ * the year parser will reserve two digits for the month to parse.
+ * Thus, the text '201106' will correctly parse to a year of 2011 and a month of 6.
+ * Without adjacent value parsing, the year would greedily parse all six digits and leave
+ * nothing for the month.
+ * <p>
+ * Adjacent value parsing applies to each set of fixed width not-negative values in the parser
+ * that immediately follow any kind of value, variable or fixed width.
+ * Calling any other append method will end the setup of adjacent value parsing.
+ * Thus, in the unlikely event that you need to avoid adjacent value parsing behavior,
+ * simply add the {@code appendValue} to another {@code DateTimeFormatterBuilder}
+ * and add that to this builder.
+ * <p>
+ * If adjacent parsing is active, then parsing must match exactly the specified
+ * number of digits in both strict and lenient modes.
+ * In addition, no positive or negative sign is permitted.
+ *
+ * @param field the field to append, not null
+ * @param width the width of the printed field, from 1 to 19
+ * @return this, for chaining, not null
+ * @throws IllegalArgumentException if the width is invalid
+ */
+ public DateTimeFormatterBuilder appendValue(TemporalField field, int width) {
+ Objects.requireNonNull(field, "field");
+ if (width < 1 || width > 19) {
+ throw new IllegalArgumentException("The width must be from 1 to 19 inclusive but was " + width);
+ }
+ NumberPrinterParser pp = new NumberPrinterParser(field, width, width, SignStyle.NOT_NEGATIVE);
+ appendValue(pp);
+ return this;
+ }
+
+ /**
+ * Appends the value of a date-time field to the formatter providing full
+ * control over formatting.
+ * <p>
+ * The value of the field will be output during a format.
+ * If the value cannot be obtained then an exception will be thrown.
+ * <p>
+ * This method provides full control of the numeric formatting, including
+ * zero-padding and the positive/negative sign.
+ * <p>
+ * The parser for a variable width value such as this normally behaves greedily,
+ * accepting as many digits as possible.
+ * This behavior can be affected by 'adjacent value parsing'.
+ * See {@link #appendValue(java.time.temporal.TemporalField, int)} for full details.
+ * <p>
+ * In strict parsing mode, the minimum number of parsed digits is {@code minWidth}
+ * and the maximum is {@code maxWidth}.
+ * In lenient parsing mode, the minimum number of parsed digits is one
+ * and the maximum is 19 (except as limited by adjacent value parsing).
+ * <p>
+ * If this method is invoked with equal minimum and maximum widths and a sign style of
+ * {@code NOT_NEGATIVE} then it delegates to {@code appendValue(TemporalField,int)}.
+ * In this scenario, the formatting and parsing behavior described there occur.
+ *
+ * @param field the field to append, not null
+ * @param minWidth the minimum field width of the printed field, from 1 to 19
+ * @param maxWidth the maximum field width of the printed field, from 1 to 19
+ * @param signStyle the positive/negative output style, not null
+ * @return this, for chaining, not null
+ * @throws IllegalArgumentException if the widths are invalid
+ */
+ public DateTimeFormatterBuilder appendValue(
+ TemporalField field, int minWidth, int maxWidth, SignStyle signStyle) {
+ if (minWidth == maxWidth && signStyle == SignStyle.NOT_NEGATIVE) {
+ return appendValue(field, maxWidth);
+ }
+ Objects.requireNonNull(field, "field");
+ Objects.requireNonNull(signStyle, "signStyle");
+ if (minWidth < 1 || minWidth > 19) {
+ throw new IllegalArgumentException("The minimum width must be from 1 to 19 inclusive but was " + minWidth);
+ }
+ if (maxWidth < 1 || maxWidth > 19) {
+ throw new IllegalArgumentException("The maximum width must be from 1 to 19 inclusive but was " + maxWidth);
+ }
+ if (maxWidth < minWidth) {
+ throw new IllegalArgumentException("The maximum width must exceed or equal the minimum width but " +
+ maxWidth + " < " + minWidth);
+ }
+ NumberPrinterParser pp = new NumberPrinterParser(field, minWidth, maxWidth, signStyle);
+ appendValue(pp);
+ return this;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Appends the reduced value of a date-time field to the formatter.
+ * <p>
+ * Since fields such as year vary by chronology, it is recommended to use the
+ * {@link #appendValueReduced(TemporalField, int, int, ChronoLocalDate)} date}
+ * variant of this method in most cases. This variant is suitable for
+ * simple fields or working with only the ISO chronology.
+ * <p>
+ * For formatting, the {@code width} and {@code maxWidth} are used to
+ * determine the number of characters to format.
+ * If they are equal then the format is fixed width.
+ * If the value of the field is within the range of the {@code baseValue} using
+ * {@code width} characters then the reduced value is formatted otherwise the value is
+ * truncated to fit {@code maxWidth}.
+ * The rightmost characters are output to match the width, left padding with zero.
+ * <p>
+ * For strict parsing, the number of characters allowed by {@code width} to {@code maxWidth} are parsed.
+ * For lenient parsing, the number of characters must be at least 1 and less than 10.
+ * If the number of digits parsed is equal to {@code width} and the value is positive,
+ * the value of the field is computed to be the first number greater than
+ * or equal to the {@code baseValue} with the same least significant characters,
+ * otherwise the value parsed is the field value.
+ * This allows a reduced value to be entered for values in range of the baseValue
+ * and width and absolute values can be entered for values outside the range.
+ * <p>
+ * For example, a base value of {@code 1980} and a width of {@code 2} will have
+ * valid values from {@code 1980} to {@code 2079}.
+ * During parsing, the text {@code "12"} will result in the value {@code 2012} as that
+ * is the value within the range where the last two characters are "12".
+ * By contrast, parsing the text {@code "1915"} will result in the value {@code 1915}.
+ *
+ * @param field the field to append, not null
+ * @param width the field width of the printed and parsed field, from 1 to 10
+ * @param maxWidth the maximum field width of the printed field, from 1 to 10
+ * @param baseValue the base value of the range of valid values
+ * @return this, for chaining, not null
+ * @throws IllegalArgumentException if the width or base value is invalid
+ */
+ public DateTimeFormatterBuilder appendValueReduced(TemporalField field,
+ int width, int maxWidth, int baseValue) {
+ Objects.requireNonNull(field, "field");
+ ReducedPrinterParser pp = new ReducedPrinterParser(field, width, maxWidth, baseValue, null);
+ appendValue(pp);
+ return this;
+ }
+
+ /**
+ * Appends the reduced value of a date-time field to the formatter.
+ * <p>
+ * This is typically used for formatting and parsing a two digit year.
+ * <p>
+ * The base date is used to calculate the full value during parsing.
+ * For example, if the base date is 1950-01-01 then parsed values for
+ * a two digit year parse will be in the range 1950-01-01 to 2049-12-31.
+ * Only the year would be extracted from the date, thus a base date of
+ * 1950-08-25 would also parse to the range 1950-01-01 to 2049-12-31.
+ * This behavior is necessary to support fields such as week-based-year
+ * or other calendar systems where the parsed value does not align with
+ * standard ISO years.
+ * <p>
+ * The exact behavior is as follows. Parse the full set of fields and
+ * determine the effective chronology using the last chronology if
+ * it appears more than once. Then convert the base date to the
+ * effective chronology. Then extract the specified field from the
+ * chronology-specific base date and use it to determine the
+ * {@code baseValue} used below.
+ * <p>
+ * For formatting, the {@code width} and {@code maxWidth} are used to
+ * determine the number of characters to format.
+ * If they are equal then the format is fixed width.
+ * If the value of the field is within the range of the {@code baseValue} using
+ * {@code width} characters then the reduced value is formatted otherwise the value is
+ * truncated to fit {@code maxWidth}.
+ * The rightmost characters are output to match the width, left padding with zero.
+ * <p>
+ * For strict parsing, the number of characters allowed by {@code width} to {@code maxWidth} are parsed.
+ * For lenient parsing, the number of characters must be at least 1 and less than 10.
+ * If the number of digits parsed is equal to {@code width} and the value is positive,
+ * the value of the field is computed to be the first number greater than
+ * or equal to the {@code baseValue} with the same least significant characters,
+ * otherwise the value parsed is the field value.
+ * This allows a reduced value to be entered for values in range of the baseValue
+ * and width and absolute values can be entered for values outside the range.
+ * <p>
+ * For example, a base value of {@code 1980} and a width of {@code 2} will have
+ * valid values from {@code 1980} to {@code 2079}.
+ * During parsing, the text {@code "12"} will result in the value {@code 2012} as that
+ * is the value within the range where the last two characters are "12".
+ * By contrast, parsing the text {@code "1915"} will result in the value {@code 1915}.
+ *
+ * @param field the field to append, not null
+ * @param width the field width of the printed and parsed field, from 1 to 10
+ * @param maxWidth the maximum field width of the printed field, from 1 to 10
+ * @param baseDate the base date used to calculate the base value for the range
+ * of valid values in the parsed chronology, not null
+ * @return this, for chaining, not null
+ * @throws IllegalArgumentException if the width or base value is invalid
+ */
+ public DateTimeFormatterBuilder appendValueReduced(
+ TemporalField field, int width, int maxWidth, ChronoLocalDate baseDate) {
+ Objects.requireNonNull(field, "field");
+ Objects.requireNonNull(baseDate, "baseDate");
+ ReducedPrinterParser pp = new ReducedPrinterParser(field, width, maxWidth, 0, baseDate);
+ appendValue(pp);
+ return this;
+ }
+
+ /**
+ * Appends a fixed or variable width printer-parser handling adjacent value mode.
+ * If a PrinterParser is not active then the new PrinterParser becomes
+ * the active PrinterParser.
+ * Otherwise, the active PrinterParser is modified depending on the new PrinterParser.
+ * If the new PrinterParser is fixed width and has sign style {@code NOT_NEGATIVE}
+ * then its width is added to the active PP and
+ * the new PrinterParser is forced to be fixed width.
+ * If the new PrinterParser is variable width, the active PrinterParser is changed
+ * to be fixed width and the new PrinterParser becomes the active PP.
+ *
+ * @param pp the printer-parser, not null
+ * @return this, for chaining, not null
+ */
+ private DateTimeFormatterBuilder appendValue(NumberPrinterParser pp) {
+ if (active.valueParserIndex >= 0) {
+ final int activeValueParser = active.valueParserIndex;
+
+ // adjacent parsing mode, update setting in previous parsers
+ NumberPrinterParser basePP = (NumberPrinterParser) active.printerParsers.get(activeValueParser);
+ if (pp.minWidth == pp.maxWidth && pp.signStyle == SignStyle.NOT_NEGATIVE) {
+ // Append the width to the subsequentWidth of the active parser
+ basePP = basePP.withSubsequentWidth(pp.maxWidth);
+ // Append the new parser as a fixed width
+ appendInternal(pp.withFixedWidth());
+ // Retain the previous active parser
+ active.valueParserIndex = activeValueParser;
+ } else {
+ // Modify the active parser to be fixed width
+ basePP = basePP.withFixedWidth();
+ // The new parser becomes the mew active parser
+ active.valueParserIndex = appendInternal(pp);
+ }
+ // Replace the modified parser with the updated one
+ active.printerParsers.set(activeValueParser, basePP);
+ } else {
+ // The new Parser becomes the active parser
+ active.valueParserIndex = appendInternal(pp);
+ }
+ return this;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Appends the fractional value of a date-time field to the formatter.
+ * <p>
+ * The fractional value of the field will be output including the
+ * preceding decimal point. The preceding value is not output.
+ * For example, the second-of-minute value of 15 would be output as {@code .25}.
+ * <p>
+ * The width of the printed fraction can be controlled. Setting the
+ * minimum width to zero will cause no output to be generated.
+ * The printed fraction will have the minimum width necessary between
+ * the minimum and maximum widths - trailing zeroes are omitted.
+ * No rounding occurs due to the maximum width - digits are simply dropped.
+ * <p>
+ * When parsing in strict mode, the number of parsed digits must be between
+ * the minimum and maximum width. When parsing in lenient mode, the minimum
+ * width is considered to be zero and the maximum is nine.
+ * <p>
+ * If the value cannot be obtained then an exception will be thrown.
+ * If the value is negative an exception will be thrown.
+ * If the field does not have a fixed set of valid values then an
+ * exception will be thrown.
+ * If the field value in the date-time to be printed is invalid it
+ * cannot be printed and an exception will be thrown.
+ *
+ * @param field the field to append, not null
+ * @param minWidth the minimum width of the field excluding the decimal point, from 0 to 9
+ * @param maxWidth the maximum width of the field excluding the decimal point, from 1 to 9
+ * @param decimalPoint whether to output the localized decimal point symbol
+ * @return this, for chaining, not null
+ * @throws IllegalArgumentException if the field has a variable set of valid values or
+ * either width is invalid
+ */
+ public DateTimeFormatterBuilder appendFraction(
+ TemporalField field, int minWidth, int maxWidth, boolean decimalPoint) {
+ appendInternal(new FractionPrinterParser(field, minWidth, maxWidth, decimalPoint));
+ return this;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Appends the text of a date-time field to the formatter using the full
+ * text style.
+ * <p>
+ * The text of the field will be output during a format.
+ * The value must be within the valid range of the field.
+ * If the value cannot be obtained then an exception will be thrown.
+ * If the field has no textual representation, then the numeric value will be used.
+ * <p>
+ * The value will be printed as per the normal format of an integer value.
+ * Only negative numbers will be signed. No padding will be added.
+ *
+ * @param field the field to append, not null
+ * @return this, for chaining, not null
+ */
+ public DateTimeFormatterBuilder appendText(TemporalField field) {
+ return appendText(field, TextStyle.FULL);
+ }
+
+ /**
+ * Appends the text of a date-time field to the formatter.
+ * <p>
+ * The text of the field will be output during a format.
+ * The value must be within the valid range of the field.
+ * If the value cannot be obtained then an exception will be thrown.
+ * If the field has no textual representation, then the numeric value will be used.
+ * <p>
+ * The value will be printed as per the normal format of an integer value.
+ * Only negative numbers will be signed. No padding will be added.
+ *
+ * @param field the field to append, not null
+ * @param textStyle the text style to use, not null
+ * @return this, for chaining, not null
+ */
+ public DateTimeFormatterBuilder appendText(TemporalField field, TextStyle textStyle) {
+ Objects.requireNonNull(field, "field");
+ Objects.requireNonNull(textStyle, "textStyle");
+ appendInternal(new TextPrinterParser(field, textStyle, DateTimeTextProvider.getInstance()));
+ return this;
+ }
+
+ /**
+ * Appends the text of a date-time field to the formatter using the specified
+ * map to supply the text.
+ * <p>
+ * The standard text outputting methods use the localized text in the JDK.
+ * This method allows that text to be specified directly.
+ * The supplied map is not validated by the builder to ensure that formatting or
+ * parsing is possible, thus an invalid map may throw an error during later use.
+ * <p>
+ * Supplying the map of text provides considerable flexibility in formatting and parsing.
+ * For example, a legacy application might require or supply the months of the
+ * year as "JNY", "FBY", "MCH" etc. These do not match the standard set of text
+ * for localized month names. Using this method, a map can be created which
+ * defines the connection between each value and the text:
+ * <pre>
+ * Map<Long, String> map = new HashMap<>();
+ * map.put(1L, "JNY");
+ * map.put(2L, "FBY");
+ * map.put(3L, "MCH");
+ * ...
+ * builder.appendText(MONTH_OF_YEAR, map);
+ * </pre>
+ * <p>
+ * Other uses might be to output the value with a suffix, such as "1st", "2nd", "3rd",
+ * or as Roman numerals "I", "II", "III", "IV".
+ * <p>
+ * During formatting, the value is obtained and checked that it is in the valid range.
+ * If text is not available for the value then it is output as a number.
+ * During parsing, the parser will match against the map of text and numeric values.
+ *
+ * @param field the field to append, not null
+ * @param textLookup the map from the value to the text
+ * @return this, for chaining, not null
+ */
+ public DateTimeFormatterBuilder appendText(TemporalField field, Map<Long, String> textLookup) {
+ Objects.requireNonNull(field, "field");
+ Objects.requireNonNull(textLookup, "textLookup");
+ Map<Long, String> copy = new LinkedHashMap<>(textLookup);
+ Map<TextStyle, Map<Long, String>> map = Collections.singletonMap(TextStyle.FULL, copy);
+ final LocaleStore store = new LocaleStore(map);
+ DateTimeTextProvider provider = new DateTimeTextProvider() {
+ @Override
+ public String getText(TemporalField field, long value, TextStyle style, Locale locale) {
+ return store.getText(value, style);
+ }
+ @Override
+ public Iterator<Entry<String, Long>> getTextIterator(TemporalField field, TextStyle style, Locale locale) {
+ return store.getTextIterator(style);
+ }
+ };
+ appendInternal(new TextPrinterParser(field, TextStyle.FULL, provider));
+ return this;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Appends an instant using ISO-8601 to the formatter, formatting fractional
+ * digits in groups of three.
+ * <p>
+ * Instants have a fixed output format.
+ * They are converted to a date-time with a zone-offset of UTC and formatted
+ * using the standard ISO-8601 format.
+ * With this method, formatting nano-of-second outputs zero, three, six
+ * or nine digits digits as necessary.
+ * The localized decimal style is not used.
+ * <p>
+ * The instant is obtained using {@link ChronoField#INSTANT_SECONDS INSTANT_SECONDS}
+ * and optionally (@code NANO_OF_SECOND). The value of {@code INSTANT_SECONDS}
+ * may be outside the maximum range of {@code LocalDateTime}.
+ * <p>
+ * The {@linkplain ResolverStyle resolver style} has no effect on instant parsing.
+ * The end-of-day time of '24:00' is handled as midnight at the start of the following day.
+ * The leap-second time of '23:59:59' is handled to some degree, see
+ * {@link DateTimeFormatter#parsedLeapSecond()} for full details.
+ * <p>
+ * An alternative to this method is to format/parse the instant as a single
+ * epoch-seconds value. That is achieved using {@code appendValue(INSTANT_SECONDS)}.
+ *
+ * @return this, for chaining, not null
+ */
+ public DateTimeFormatterBuilder appendInstant() {
+ appendInternal(new InstantPrinterParser(-2));
+ return this;
+ }
+
+ /**
+ * Appends an instant using ISO-8601 to the formatter with control over
+ * the number of fractional digits.
+ * <p>
+ * Instants have a fixed output format, although this method provides some
+ * control over the fractional digits. They are converted to a date-time
+ * with a zone-offset of UTC and printed using the standard ISO-8601 format.
+ * The localized decimal style is not used.
+ * <p>
+ * The {@code fractionalDigits} parameter allows the output of the fractional
+ * second to be controlled. Specifying zero will cause no fractional digits
+ * to be output. From 1 to 9 will output an increasing number of digits, using
+ * zero right-padding if necessary. The special value -1 is used to output as
+ * many digits as necessary to avoid any trailing zeroes.
+ * <p>
+ * When parsing in strict mode, the number of parsed digits must match the
+ * fractional digits. When parsing in lenient mode, any number of fractional
+ * digits from zero to nine are accepted.
+ * <p>
+ * The instant is obtained using {@link ChronoField#INSTANT_SECONDS INSTANT_SECONDS}
+ * and optionally (@code NANO_OF_SECOND). The value of {@code INSTANT_SECONDS}
+ * may be outside the maximum range of {@code LocalDateTime}.
+ * <p>
+ * The {@linkplain ResolverStyle resolver style} has no effect on instant parsing.
+ * The end-of-day time of '24:00' is handled as midnight at the start of the following day.
+ * The leap-second time of '23:59:60' is handled to some degree, see
+ * {@link DateTimeFormatter#parsedLeapSecond()} for full details.
+ * <p>
+ * An alternative to this method is to format/parse the instant as a single
+ * epoch-seconds value. That is achieved using {@code appendValue(INSTANT_SECONDS)}.
+ *
+ * @param fractionalDigits the number of fractional second digits to format with,
+ * from 0 to 9, or -1 to use as many digits as necessary
+ * @return this, for chaining, not null
+ */
+ public DateTimeFormatterBuilder appendInstant(int fractionalDigits) {
+ if (fractionalDigits < -1 || fractionalDigits > 9) {
+ throw new IllegalArgumentException("The fractional digits must be from -1 to 9 inclusive but was " + fractionalDigits);
+ }
+ appendInternal(new InstantPrinterParser(fractionalDigits));
+ return this;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Appends the zone offset, such as '+01:00', to the formatter.
+ * <p>
+ * This appends an instruction to format/parse the offset ID to the builder.
+ * This is equivalent to calling {@code appendOffset("+HH:MM:ss", "Z")}.
+ *
+ * @return this, for chaining, not null
+ */
+ public DateTimeFormatterBuilder appendOffsetId() {
+ appendInternal(OffsetIdPrinterParser.INSTANCE_ID_Z);
+ return this;
+ }
+
+ /**
+ * Appends the zone offset, such as '+01:00', to the formatter.
+ * <p>
+ * This appends an instruction to format/parse the offset ID to the builder.
+ * <p>
+ * During formatting, the offset is obtained using a mechanism equivalent
+ * to querying the temporal with {@link TemporalQueries#offset()}.
+ * It will be printed using the format defined below.
+ * If the offset cannot be obtained then an exception is thrown unless the
+ * section of the formatter is optional.
+ * <p>
+ * During parsing, the offset is parsed using the format defined below.
+ * If the offset cannot be parsed then an exception is thrown unless the
+ * section of the formatter is optional.
+ * <p>
+ * The format of the offset is controlled by a pattern which must be one
+ * of the following:
+ * <ul>
+ * <li>{@code +HH} - hour only, ignoring minute and second
+ * <li>{@code +HHmm} - hour, with minute if non-zero, ignoring second, no colon
+ * <li>{@code +HH:mm} - hour, with minute if non-zero, ignoring second, with colon
+ * <li>{@code +HHMM} - hour and minute, ignoring second, no colon
+ * <li>{@code +HH:MM} - hour and minute, ignoring second, with colon
+ * <li>{@code +HHMMss} - hour and minute, with second if non-zero, no colon
+ * <li>{@code +HH:MM:ss} - hour and minute, with second if non-zero, with colon
+ * <li>{@code +HHMMSS} - hour, minute and second, no colon
+ * <li>{@code +HH:MM:SS} - hour, minute and second, with colon
+ * </ul>
+ * The "no offset" text controls what text is printed when the total amount of
+ * the offset fields to be output is zero.
+ * Example values would be 'Z', '+00:00', 'UTC' or 'GMT'.
+ * Three formats are accepted for parsing UTC - the "no offset" text, and the
+ * plus and minus versions of zero defined by the pattern.
+ *
+ * @param pattern the pattern to use, not null
+ * @param noOffsetText the text to use when the offset is zero, not null
+ * @return this, for chaining, not null
+ */
+ public DateTimeFormatterBuilder appendOffset(String pattern, String noOffsetText) {
+ appendInternal(new OffsetIdPrinterParser(pattern, noOffsetText));
+ return this;
+ }
+
+ /**
+ * Appends the localized zone offset, such as 'GMT+01:00', to the formatter.
+ * <p>
+ * This appends a localized zone offset to the builder, the format of the
+ * localized offset is controlled by the specified {@link FormatStyle style}
+ * to this method:
+ * <ul>
+ * <li>{@link TextStyle#FULL full} - formats with localized offset text, such
+ * as 'GMT, 2-digit hour and minute field, optional second field if non-zero,
+ * and colon.
+ * <li>{@link TextStyle#SHORT short} - formats with localized offset text,
+ * such as 'GMT, hour without leading zero, optional 2-digit minute and
+ * second if non-zero, and colon.
+ * </ul>
+ * <p>
+ * During formatting, the offset is obtained using a mechanism equivalent
+ * to querying the temporal with {@link TemporalQueries#offset()}.
+ * If the offset cannot be obtained then an exception is thrown unless the
+ * section of the formatter is optional.
+ * <p>
+ * During parsing, the offset is parsed using the format defined above.
+ * If the offset cannot be parsed then an exception is thrown unless the
+ * section of the formatter is optional.
+ * <p>
+ * @param style the format style to use, not null
+ * @return this, for chaining, not null
+ * @throws IllegalArgumentException if style is neither {@link TextStyle#FULL
+ * full} nor {@link TextStyle#SHORT short}
+ */
+ public DateTimeFormatterBuilder appendLocalizedOffset(TextStyle style) {
+ Objects.requireNonNull(style, "style");
+ if (style != TextStyle.FULL && style != TextStyle.SHORT) {
+ throw new IllegalArgumentException("Style must be either full or short");
+ }
+ appendInternal(new LocalizedOffsetIdPrinterParser(style));
+ return this;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Appends the time-zone ID, such as 'Europe/Paris' or '+02:00', to the formatter.
+ * <p>
+ * This appends an instruction to format/parse the zone ID to the builder.
+ * The zone ID is obtained in a strict manner suitable for {@code ZonedDateTime}.
+ * By contrast, {@code OffsetDateTime} does not have a zone ID suitable
+ * for use with this method, see {@link #appendZoneOrOffsetId()}.
+ * <p>
+ * During formatting, the zone is obtained using a mechanism equivalent
+ * to querying the temporal with {@link TemporalQueries#zoneId()}.
+ * It will be printed using the result of {@link ZoneId#getId()}.
+ * If the zone cannot be obtained then an exception is thrown unless the
+ * section of the formatter is optional.
+ * <p>
+ * During parsing, the text must match a known zone or offset.
+ * There are two types of zone ID, offset-based, such as '+01:30' and
+ * region-based, such as 'Europe/London'. These are parsed differently.
+ * If the parse starts with '+', '-', 'UT', 'UTC' or 'GMT', then the parser
+ * expects an offset-based zone and will not match region-based zones.
+ * The offset ID, such as '+02:30', may be at the start of the parse,
+ * or prefixed by 'UT', 'UTC' or 'GMT'. The offset ID parsing is
+ * equivalent to using {@link #appendOffset(String, String)} using the
+ * arguments 'HH:MM:ss' and the no offset string '0'.
+ * If the parse starts with 'UT', 'UTC' or 'GMT', and the parser cannot
+ * match a following offset ID, then {@link ZoneOffset#UTC} is selected.
+ * In all other cases, the list of known region-based zones is used to
+ * find the longest available match. If no match is found, and the parse
+ * starts with 'Z', then {@code ZoneOffset.UTC} is selected.
+ * The parser uses the {@linkplain #parseCaseInsensitive() case sensitive} setting.
+ * <p>
+ * For example, the following will parse:
+ * <pre>
+ * "Europe/London" -- ZoneId.of("Europe/London")
+ * "Z" -- ZoneOffset.UTC
+ * "UT" -- ZoneId.of("UT")
+ * "UTC" -- ZoneId.of("UTC")
+ * "GMT" -- ZoneId.of("GMT")
+ * "+01:30" -- ZoneOffset.of("+01:30")
+ * "UT+01:30" -- ZoneOffset.of("+01:30")
+ * "UTC+01:30" -- ZoneOffset.of("+01:30")
+ * "GMT+01:30" -- ZoneOffset.of("+01:30")
+ * </pre>
+ *
+ * @return this, for chaining, not null
+ * @see #appendZoneRegionId()
+ */
+ public DateTimeFormatterBuilder appendZoneId() {
+ appendInternal(new ZoneIdPrinterParser(TemporalQueries.zoneId(), "ZoneId()"));
+ return this;
+ }
+
+ /**
+ * Appends the time-zone region ID, such as 'Europe/Paris', to the formatter,
+ * rejecting the zone ID if it is a {@code ZoneOffset}.
+ * <p>
+ * This appends an instruction to format/parse the zone ID to the builder
+ * only if it is a region-based ID.
+ * <p>
+ * During formatting, the zone is obtained using a mechanism equivalent
+ * to querying the temporal with {@link TemporalQueries#zoneId()}.
+ * If the zone is a {@code ZoneOffset} or it cannot be obtained then
+ * an exception is thrown unless the section of the formatter is optional.
+ * If the zone is not an offset, then the zone will be printed using
+ * the zone ID from {@link ZoneId#getId()}.
+ * <p>
+ * During parsing, the text must match a known zone or offset.
+ * There are two types of zone ID, offset-based, such as '+01:30' and
+ * region-based, such as 'Europe/London'. These are parsed differently.
+ * If the parse starts with '+', '-', 'UT', 'UTC' or 'GMT', then the parser
+ * expects an offset-based zone and will not match region-based zones.
+ * The offset ID, such as '+02:30', may be at the start of the parse,
+ * or prefixed by 'UT', 'UTC' or 'GMT'. The offset ID parsing is
+ * equivalent to using {@link #appendOffset(String, String)} using the
+ * arguments 'HH:MM:ss' and the no offset string '0'.
+ * If the parse starts with 'UT', 'UTC' or 'GMT', and the parser cannot
+ * match a following offset ID, then {@link ZoneOffset#UTC} is selected.
+ * In all other cases, the list of known region-based zones is used to
+ * find the longest available match. If no match is found, and the parse
+ * starts with 'Z', then {@code ZoneOffset.UTC} is selected.
+ * The parser uses the {@linkplain #parseCaseInsensitive() case sensitive} setting.
+ * <p>
+ * For example, the following will parse:
+ * <pre>
+ * "Europe/London" -- ZoneId.of("Europe/London")
+ * "Z" -- ZoneOffset.UTC
+ * "UT" -- ZoneId.of("UT")
+ * "UTC" -- ZoneId.of("UTC")
+ * "GMT" -- ZoneId.of("GMT")
+ * "+01:30" -- ZoneOffset.of("+01:30")
+ * "UT+01:30" -- ZoneOffset.of("+01:30")
+ * "UTC+01:30" -- ZoneOffset.of("+01:30")
+ * "GMT+01:30" -- ZoneOffset.of("+01:30")
+ * </pre>
+ * <p>
+ * Note that this method is identical to {@code appendZoneId()} except
+ * in the mechanism used to obtain the zone.
+ * Note also that parsing accepts offsets, whereas formatting will never
+ * produce one.
+ *
+ * @return this, for chaining, not null
+ * @see #appendZoneId()
+ */
+ public DateTimeFormatterBuilder appendZoneRegionId() {
+ appendInternal(new ZoneIdPrinterParser(QUERY_REGION_ONLY, "ZoneRegionId()"));
+ return this;
+ }
+
+ /**
+ * Appends the time-zone ID, such as 'Europe/Paris' or '+02:00', to
+ * the formatter, using the best available zone ID.
+ * <p>
+ * This appends an instruction to format/parse the best available
+ * zone or offset ID to the builder.
+ * The zone ID is obtained in a lenient manner that first attempts to
+ * find a true zone ID, such as that on {@code ZonedDateTime}, and
+ * then attempts to find an offset, such as that on {@code OffsetDateTime}.
+ * <p>
+ * During formatting, the zone is obtained using a mechanism equivalent
+ * to querying the temporal with {@link TemporalQueries#zone()}.
+ * It will be printed using the result of {@link ZoneId#getId()}.
+ * If the zone cannot be obtained then an exception is thrown unless the
+ * section of the formatter is optional.
+ * <p>
+ * During parsing, the text must match a known zone or offset.
+ * There are two types of zone ID, offset-based, such as '+01:30' and
+ * region-based, such as 'Europe/London'. These are parsed differently.
+ * If the parse starts with '+', '-', 'UT', 'UTC' or 'GMT', then the parser
+ * expects an offset-based zone and will not match region-based zones.
+ * The offset ID, such as '+02:30', may be at the start of the parse,
+ * or prefixed by 'UT', 'UTC' or 'GMT'. The offset ID parsing is
+ * equivalent to using {@link #appendOffset(String, String)} using the
+ * arguments 'HH:MM:ss' and the no offset string '0'.
+ * If the parse starts with 'UT', 'UTC' or 'GMT', and the parser cannot
+ * match a following offset ID, then {@link ZoneOffset#UTC} is selected.
+ * In all other cases, the list of known region-based zones is used to
+ * find the longest available match. If no match is found, and the parse
+ * starts with 'Z', then {@code ZoneOffset.UTC} is selected.
+ * The parser uses the {@linkplain #parseCaseInsensitive() case sensitive} setting.
+ * <p>
+ * For example, the following will parse:
+ * <pre>
+ * "Europe/London" -- ZoneId.of("Europe/London")
+ * "Z" -- ZoneOffset.UTC
+ * "UT" -- ZoneId.of("UT")
+ * "UTC" -- ZoneId.of("UTC")
+ * "GMT" -- ZoneId.of("GMT")
+ * "+01:30" -- ZoneOffset.of("+01:30")
+ * "UT+01:30" -- ZoneOffset.of("UT+01:30")
+ * "UTC+01:30" -- ZoneOffset.of("UTC+01:30")
+ * "GMT+01:30" -- ZoneOffset.of("GMT+01:30")
+ * </pre>
+ * <p>
+ * Note that this method is identical to {@code appendZoneId()} except
+ * in the mechanism used to obtain the zone.
+ *
+ * @return this, for chaining, not null
+ * @see #appendZoneId()
+ */
+ public DateTimeFormatterBuilder appendZoneOrOffsetId() {
+ appendInternal(new ZoneIdPrinterParser(TemporalQueries.zone(), "ZoneOrOffsetId()"));
+ return this;
+ }
+
+ /**
+ * Appends the time-zone name, such as 'British Summer Time', to the formatter.
+ * <p>
+ * This appends an instruction to format/parse the textual name of the zone to
+ * the builder.
+ * <p>
+ * During formatting, the zone is obtained using a mechanism equivalent
+ * to querying the temporal with {@link TemporalQueries#zoneId()}.
+ * If the zone is a {@code ZoneOffset} it will be printed using the
+ * result of {@link ZoneOffset#getId()}.
+ * If the zone is not an offset, the textual name will be looked up
+ * for the locale set in the {@link DateTimeFormatter}.
+ * If the temporal object being printed represents an instant, then the text
+ * will be the summer or winter time text as appropriate.
+ * If the lookup for text does not find any suitable result, then the
+ * {@link ZoneId#getId() ID} will be printed instead.
+ * If the zone cannot be obtained then an exception is thrown unless the
+ * section of the formatter is optional.
+ * <p>
+ * During parsing, either the textual zone name, the zone ID or the offset
+ * is accepted. Many textual zone names are not unique, such as CST can be
+ * for both "Central Standard Time" and "China Standard Time". In this
+ * situation, the zone id will be determined by the region information from
+ * formatter's {@link DateTimeFormatter#getLocale() locale} and the standard
+ * zone id for that area, for example, America/New_York for the America Eastern
+ * zone. The {@link #appendZoneText(TextStyle, Set)} may be used
+ * to specify a set of preferred {@link ZoneId} in this situation.
+ *
+ * @param textStyle the text style to use, not null
+ * @return this, for chaining, not null
+ */
+ public DateTimeFormatterBuilder appendZoneText(TextStyle textStyle) {
+ appendInternal(new ZoneTextPrinterParser(textStyle, null));
+ return this;
+ }
+
+ /**
+ * Appends the time-zone name, such as 'British Summer Time', to the formatter.
+ * <p>
+ * This appends an instruction to format/parse the textual name of the zone to
+ * the builder.
+ * <p>
+ * During formatting, the zone is obtained using a mechanism equivalent
+ * to querying the temporal with {@link TemporalQueries#zoneId()}.
+ * If the zone is a {@code ZoneOffset} it will be printed using the
+ * result of {@link ZoneOffset#getId()}.
+ * If the zone is not an offset, the textual name will be looked up
+ * for the locale set in the {@link DateTimeFormatter}.
+ * If the temporal object being printed represents an instant, then the text
+ * will be the summer or winter time text as appropriate.
+ * If the lookup for text does not find any suitable result, then the
+ * {@link ZoneId#getId() ID} will be printed instead.
+ * If the zone cannot be obtained then an exception is thrown unless the
+ * section of the formatter is optional.
+ * <p>
+ * During parsing, either the textual zone name, the zone ID or the offset
+ * is accepted. Many textual zone names are not unique, such as CST can be
+ * for both "Central Standard Time" and "China Standard Time". In this
+ * situation, the zone id will be determined by the region information from
+ * formatter's {@link DateTimeFormatter#getLocale() locale} and the standard
+ * zone id for that area, for example, America/New_York for the America Eastern
+ * zone. This method also allows a set of preferred {@link ZoneId} to be
+ * specified for parsing. The matched preferred zone id will be used if the
+ * textural zone name being parsed is not unique.
+ * <p>
+ * If the zone cannot be parsed then an exception is thrown unless the
+ * section of the formatter is optional.
+ *
+ * @param textStyle the text style to use, not null
+ * @param preferredZones the set of preferred zone ids, not null
+ * @return this, for chaining, not null
+ */
+ public DateTimeFormatterBuilder appendZoneText(TextStyle textStyle,
+ Set<ZoneId> preferredZones) {
+ Objects.requireNonNull(preferredZones, "preferredZones");
+ appendInternal(new ZoneTextPrinterParser(textStyle, preferredZones));
+ return this;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Appends the chronology ID, such as 'ISO' or 'ThaiBuddhist', to the formatter.
+ * <p>
+ * This appends an instruction to format/parse the chronology ID to the builder.
+ * <p>
+ * During formatting, the chronology is obtained using a mechanism equivalent
+ * to querying the temporal with {@link TemporalQueries#chronology()}.
+ * It will be printed using the result of {@link Chronology#getId()}.
+ * If the chronology cannot be obtained then an exception is thrown unless the
+ * section of the formatter is optional.
+ * <p>
+ * During parsing, the chronology is parsed and must match one of the chronologies
+ * in {@link Chronology#getAvailableChronologies()}.
+ * If the chronology cannot be parsed then an exception is thrown unless the
+ * section of the formatter is optional.
+ * The parser uses the {@linkplain #parseCaseInsensitive() case sensitive} setting.
+ *
+ * @return this, for chaining, not null
+ */
+ public DateTimeFormatterBuilder appendChronologyId() {
+ appendInternal(new ChronoPrinterParser(null));
+ return this;
+ }
+
+ /**
+ * Appends the chronology name to the formatter.
+ * <p>
+ * The calendar system name will be output during a format.
+ * If the chronology cannot be obtained then an exception will be thrown.
+ *
+ * @param textStyle the text style to use, not null
+ * @return this, for chaining, not null
+ */
+ public DateTimeFormatterBuilder appendChronologyText(TextStyle textStyle) {
+ Objects.requireNonNull(textStyle, "textStyle");
+ appendInternal(new ChronoPrinterParser(textStyle));
+ return this;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Appends a localized date-time pattern to the formatter.
+ * <p>
+ * This appends a localized section to the builder, suitable for outputting
+ * a date, time or date-time combination. The format of the localized
+ * section is lazily looked up based on four items:
+ * <ul>
+ * <li>the {@code dateStyle} specified to this method
+ * <li>the {@code timeStyle} specified to this method
+ * <li>the {@code Locale} of the {@code DateTimeFormatter}
+ * <li>the {@code Chronology}, selecting the best available
+ * </ul>
+ * During formatting, the chronology is obtained from the temporal object
+ * being formatted, which may have been overridden by
+ * {@link DateTimeFormatter#withChronology(Chronology)}.
+ * <p>
+ * During parsing, if a chronology has already been parsed, then it is used.
+ * Otherwise the default from {@code DateTimeFormatter.withChronology(Chronology)}
+ * is used, with {@code IsoChronology} as the fallback.
+ * <p>
+ * Note that this method provides similar functionality to methods on
+ * {@code DateFormat} such as {@link java.text.DateFormat#getDateTimeInstance(int, int)}.
+ *
+ * @param dateStyle the date style to use, null means no date required
+ * @param timeStyle the time style to use, null means no time required
+ * @return this, for chaining, not null
+ * @throws IllegalArgumentException if both the date and time styles are null
+ */
+ public DateTimeFormatterBuilder appendLocalized(FormatStyle dateStyle, FormatStyle timeStyle) {
+ if (dateStyle == null && timeStyle == null) {
+ throw new IllegalArgumentException("Either the date or time style must be non-null");
+ }
+ appendInternal(new LocalizedPrinterParser(dateStyle, timeStyle));
+ return this;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Appends a character literal to the formatter.
+ * <p>
+ * This character will be output during a format.
+ *
+ * @param literal the literal to append, not null
+ * @return this, for chaining, not null
+ */
+ public DateTimeFormatterBuilder appendLiteral(char literal) {
+ appendInternal(new CharLiteralPrinterParser(literal));
+ return this;
+ }
+
+ /**
+ * Appends a string literal to the formatter.
+ * <p>
+ * This string will be output during a format.
+ * <p>
+ * If the literal is empty, nothing is added to the formatter.
+ *
+ * @param literal the literal to append, not null
+ * @return this, for chaining, not null
+ */
+ public DateTimeFormatterBuilder appendLiteral(String literal) {
+ Objects.requireNonNull(literal, "literal");
+ if (literal.length() > 0) {
+ if (literal.length() == 1) {
+ appendInternal(new CharLiteralPrinterParser(literal.charAt(0)));
+ } else {
+ appendInternal(new StringLiteralPrinterParser(literal));
+ }
+ }
+ return this;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Appends all the elements of a formatter to the builder.
+ * <p>
+ * This method has the same effect as appending each of the constituent
+ * parts of the formatter directly to this builder.
+ *
+ * @param formatter the formatter to add, not null
+ * @return this, for chaining, not null
+ */
+ public DateTimeFormatterBuilder append(DateTimeFormatter formatter) {
+ Objects.requireNonNull(formatter, "formatter");
+ appendInternal(formatter.toPrinterParser(false));
+ return this;
+ }
+
+ /**
+ * Appends a formatter to the builder which will optionally format/parse.
+ * <p>
+ * This method has the same effect as appending each of the constituent
+ * parts directly to this builder surrounded by an {@link #optionalStart()} and
+ * {@link #optionalEnd()}.
+ * <p>
+ * The formatter will format if data is available for all the fields contained within it.
+ * The formatter will parse if the string matches, otherwise no error is returned.
+ *
+ * @param formatter the formatter to add, not null
+ * @return this, for chaining, not null
+ */
+ public DateTimeFormatterBuilder appendOptional(DateTimeFormatter formatter) {
+ Objects.requireNonNull(formatter, "formatter");
+ appendInternal(formatter.toPrinterParser(true));
+ return this;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Appends the elements defined by the specified pattern to the builder.
+ * <p>
+ * All letters 'A' to 'Z' and 'a' to 'z' are reserved as pattern letters.
+ * The characters '#', '{' and '}' are reserved for future use.
+ * The characters '[' and ']' indicate optional patterns.
+ * The following pattern letters are defined:
+ * <pre>
+ * Symbol Meaning Presentation Examples
+ * ------ ------- ------------ -------
+ * G era text AD; Anno Domini; A
+ * u year year 2004; 04
+ * y year-of-era year 2004; 04
+ * D day-of-year number 189
+ * M/L month-of-year number/text 7; 07; Jul; July; J
+ * d day-of-month number 10
+ *
+ * Q/q quarter-of-year number/text 3; 03; Q3; 3rd quarter
+ * Y week-based-year year 1996; 96
+ * w week-of-week-based-year number 27
+ * W week-of-month number 4
+ * E day-of-week text Tue; Tuesday; T
+ * e/c localized day-of-week number/text 2; 02; Tue; Tuesday; T
+ * F week-of-month number 3
+ *
+ * a am-pm-of-day text PM
+ * h clock-hour-of-am-pm (1-12) number 12
+ * K hour-of-am-pm (0-11) number 0
+ * k clock-hour-of-am-pm (1-24) number 0
+ *
+ * H hour-of-day (0-23) number 0
+ * m minute-of-hour number 30
+ * s second-of-minute number 55
+ * S fraction-of-second fraction 978
+ * A milli-of-day number 1234
+ * n nano-of-second number 987654321
+ * N nano-of-day number 1234000000
+ *
+ * V time-zone ID zone-id America/Los_Angeles; Z; -08:30
+ * z time-zone name zone-name Pacific Standard Time; PST
+ * O localized zone-offset offset-O GMT+8; GMT+08:00; UTC-08:00;
+ * X zone-offset 'Z' for zero offset-X Z; -08; -0830; -08:30; -083015; -08:30:15;
+ * x zone-offset offset-x +0000; -08; -0830; -08:30; -083015; -08:30:15;
+ * Z zone-offset offset-Z +0000; -0800; -08:00;
+ *
+ * p pad next pad modifier 1
+ *
+ * ' escape for text delimiter
+ * '' single quote literal '
+ * [ optional section start
+ * ] optional section end
+ * # reserved for future use
+ * { reserved for future use
+ * } reserved for future use
+ * </pre>
+ * <p>
+ * The count of pattern letters determine the format.
+ * See <a href="DateTimeFormatter.html#patterns">DateTimeFormatter</a> for a user-focused description of the patterns.
+ * The following tables define how the pattern letters map to the builder.
+ * <p>
+ * <b>Date fields</b>: Pattern letters to output a date.
+ * <pre>
+ * Pattern Count Equivalent builder methods
+ * ------- ----- --------------------------
+ * G 1 appendText(ChronoField.ERA, TextStyle.SHORT)
+ * GG 2 appendText(ChronoField.ERA, TextStyle.SHORT)
+ * GGG 3 appendText(ChronoField.ERA, TextStyle.SHORT)
+ * GGGG 4 appendText(ChronoField.ERA, TextStyle.FULL)
+ * GGGGG 5 appendText(ChronoField.ERA, TextStyle.NARROW)
+ *
+ * u 1 appendValue(ChronoField.YEAR, 1, 19, SignStyle.NORMAL);
+ * uu 2 appendValueReduced(ChronoField.YEAR, 2, 2000);
+ * uuu 3 appendValue(ChronoField.YEAR, 3, 19, SignStyle.NORMAL);
+ * u..u 4..n appendValue(ChronoField.YEAR, n, 19, SignStyle.EXCEEDS_PAD);
+ * y 1 appendValue(ChronoField.YEAR_OF_ERA, 1, 19, SignStyle.NORMAL);
+ * yy 2 appendValueReduced(ChronoField.YEAR_OF_ERA, 2, 2000);
+ * yyy 3 appendValue(ChronoField.YEAR_OF_ERA, 3, 19, SignStyle.NORMAL);
+ * y..y 4..n appendValue(ChronoField.YEAR_OF_ERA, n, 19, SignStyle.EXCEEDS_PAD);
+ * Y 1 append special localized WeekFields element for numeric week-based-year
+ * YY 2 append special localized WeekFields element for reduced numeric week-based-year 2 digits;
+ * YYY 3 append special localized WeekFields element for numeric week-based-year (3, 19, SignStyle.NORMAL);
+ * Y..Y 4..n append special localized WeekFields element for numeric week-based-year (n, 19, SignStyle.EXCEEDS_PAD);
+ *
+ * Q 1 appendValue(IsoFields.QUARTER_OF_YEAR);
+ * QQ 2 appendValue(IsoFields.QUARTER_OF_YEAR, 2);
+ * QQQ 3 appendText(IsoFields.QUARTER_OF_YEAR, TextStyle.SHORT)
+ * QQQQ 4 appendText(IsoFields.QUARTER_OF_YEAR, TextStyle.FULL)
+ * QQQQQ 5 appendText(IsoFields.QUARTER_OF_YEAR, TextStyle.NARROW)
+ * q 1 appendValue(IsoFields.QUARTER_OF_YEAR);
+ * qq 2 appendValue(IsoFields.QUARTER_OF_YEAR, 2);
+ * qqq 3 appendText(IsoFields.QUARTER_OF_YEAR, TextStyle.SHORT_STANDALONE)
+ * qqqq 4 appendText(IsoFields.QUARTER_OF_YEAR, TextStyle.FULL_STANDALONE)
+ * qqqqq 5 appendText(IsoFields.QUARTER_OF_YEAR, TextStyle.NARROW_STANDALONE)
+ *
+ * M 1 appendValue(ChronoField.MONTH_OF_YEAR);
+ * MM 2 appendValue(ChronoField.MONTH_OF_YEAR, 2);
+ * MMM 3 appendText(ChronoField.MONTH_OF_YEAR, TextStyle.SHORT)
+ * MMMM 4 appendText(ChronoField.MONTH_OF_YEAR, TextStyle.FULL)
+ * MMMMM 5 appendText(ChronoField.MONTH_OF_YEAR, TextStyle.NARROW)
+ * L 1 appendValue(ChronoField.MONTH_OF_YEAR);
+ * LL 2 appendValue(ChronoField.MONTH_OF_YEAR, 2);
+ * LLL 3 appendText(ChronoField.MONTH_OF_YEAR, TextStyle.SHORT_STANDALONE)
+ * LLLL 4 appendText(ChronoField.MONTH_OF_YEAR, TextStyle.FULL_STANDALONE)
+ * LLLLL 5 appendText(ChronoField.MONTH_OF_YEAR, TextStyle.NARROW_STANDALONE)
+ *
+ * w 1 append special localized WeekFields element for numeric week-of-year
+ * ww 2 append special localized WeekFields element for numeric week-of-year, zero-padded
+ * W 1 append special localized WeekFields element for numeric week-of-month
+ * d 1 appendValue(ChronoField.DAY_OF_MONTH)
+ * dd 2 appendValue(ChronoField.DAY_OF_MONTH, 2)
+ * D 1 appendValue(ChronoField.DAY_OF_YEAR)
+ * DD 2 appendValue(ChronoField.DAY_OF_YEAR, 2)
+ * DDD 3 appendValue(ChronoField.DAY_OF_YEAR, 3)
+ * F 1 appendValue(ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH)
+ * E 1 appendText(ChronoField.DAY_OF_WEEK, TextStyle.SHORT)
+ * EE 2 appendText(ChronoField.DAY_OF_WEEK, TextStyle.SHORT)
+ * EEE 3 appendText(ChronoField.DAY_OF_WEEK, TextStyle.SHORT)
+ * EEEE 4 appendText(ChronoField.DAY_OF_WEEK, TextStyle.FULL)
+ * EEEEE 5 appendText(ChronoField.DAY_OF_WEEK, TextStyle.NARROW)
+ * e 1 append special localized WeekFields element for numeric day-of-week
+ * ee 2 append special localized WeekFields element for numeric day-of-week, zero-padded
+ * eee 3 appendText(ChronoField.DAY_OF_WEEK, TextStyle.SHORT)
+ * eeee 4 appendText(ChronoField.DAY_OF_WEEK, TextStyle.FULL)
+ * eeeee 5 appendText(ChronoField.DAY_OF_WEEK, TextStyle.NARROW)
+ * c 1 append special localized WeekFields element for numeric day-of-week
+ * ccc 3 appendText(ChronoField.DAY_OF_WEEK, TextStyle.SHORT_STANDALONE)
+ * cccc 4 appendText(ChronoField.DAY_OF_WEEK, TextStyle.FULL_STANDALONE)
+ * ccccc 5 appendText(ChronoField.DAY_OF_WEEK, TextStyle.NARROW_STANDALONE)
+ * </pre>
+ * <p>
+ * <b>Time fields</b>: Pattern letters to output a time.
+ * <pre>
+ * Pattern Count Equivalent builder methods
+ * ------- ----- --------------------------
+ * a 1 appendText(ChronoField.AMPM_OF_DAY, TextStyle.SHORT)
+ * h 1 appendValue(ChronoField.CLOCK_HOUR_OF_AMPM)
+ * hh 2 appendValue(ChronoField.CLOCK_HOUR_OF_AMPM, 2)
+ * H 1 appendValue(ChronoField.HOUR_OF_DAY)
+ * HH 2 appendValue(ChronoField.HOUR_OF_DAY, 2)
+ * k 1 appendValue(ChronoField.CLOCK_HOUR_OF_DAY)
+ * kk 2 appendValue(ChronoField.CLOCK_HOUR_OF_DAY, 2)
+ * K 1 appendValue(ChronoField.HOUR_OF_AMPM)
+ * KK 2 appendValue(ChronoField.HOUR_OF_AMPM, 2)
+ * m 1 appendValue(ChronoField.MINUTE_OF_HOUR)
+ * mm 2 appendValue(ChronoField.MINUTE_OF_HOUR, 2)
+ * s 1 appendValue(ChronoField.SECOND_OF_MINUTE)
+ * ss 2 appendValue(ChronoField.SECOND_OF_MINUTE, 2)
+ *
+ * S..S 1..n appendFraction(ChronoField.NANO_OF_SECOND, n, n, false)
+ * A 1 appendValue(ChronoField.MILLI_OF_DAY)
+ * A..A 2..n appendValue(ChronoField.MILLI_OF_DAY, n)
+ * n 1 appendValue(ChronoField.NANO_OF_SECOND)
+ * n..n 2..n appendValue(ChronoField.NANO_OF_SECOND, n)
+ * N 1 appendValue(ChronoField.NANO_OF_DAY)
+ * N..N 2..n appendValue(ChronoField.NANO_OF_DAY, n)
+ * </pre>
+ * <p>
+ * <b>Zone ID</b>: Pattern letters to output {@code ZoneId}.
+ * <pre>
+ * Pattern Count Equivalent builder methods
+ * ------- ----- --------------------------
+ * VV 2 appendZoneId()
+ * z 1 appendZoneText(TextStyle.SHORT)
+ * zz 2 appendZoneText(TextStyle.SHORT)
+ * zzz 3 appendZoneText(TextStyle.SHORT)
+ * zzzz 4 appendZoneText(TextStyle.FULL)
+ * </pre>
+ * <p>
+ * <b>Zone offset</b>: Pattern letters to output {@code ZoneOffset}.
+ * <pre>
+ * Pattern Count Equivalent builder methods
+ * ------- ----- --------------------------
+ * O 1 appendLocalizedOffsetPrefixed(TextStyle.SHORT);
+ * OOOO 4 appendLocalizedOffsetPrefixed(TextStyle.FULL);
+ * X 1 appendOffset("+HHmm","Z")
+ * XX 2 appendOffset("+HHMM","Z")
+ * XXX 3 appendOffset("+HH:MM","Z")
+ * XXXX 4 appendOffset("+HHMMss","Z")
+ * XXXXX 5 appendOffset("+HH:MM:ss","Z")
+ * x 1 appendOffset("+HHmm","+00")
+ * xx 2 appendOffset("+HHMM","+0000")
+ * xxx 3 appendOffset("+HH:MM","+00:00")
+ * xxxx 4 appendOffset("+HHMMss","+0000")
+ * xxxxx 5 appendOffset("+HH:MM:ss","+00:00")
+ * Z 1 appendOffset("+HHMM","+0000")
+ * ZZ 2 appendOffset("+HHMM","+0000")
+ * ZZZ 3 appendOffset("+HHMM","+0000")
+ * ZZZZ 4 appendLocalizedOffset(TextStyle.FULL);
+ * ZZZZZ 5 appendOffset("+HH:MM:ss","Z")
+ * </pre>
+ * <p>
+ * <b>Modifiers</b>: Pattern letters that modify the rest of the pattern:
+ * <pre>
+ * Pattern Count Equivalent builder methods
+ * ------- ----- --------------------------
+ * [ 1 optionalStart()
+ * ] 1 optionalEnd()
+ * p..p 1..n padNext(n)
+ * </pre>
+ * <p>
+ * Any sequence of letters not specified above, unrecognized letter or
+ * reserved character will throw an exception.
+ * Future versions may add to the set of patterns.
+ * It is recommended to use single quotes around all characters that you want
+ * to output directly to ensure that future changes do not break your application.
+ * <p>
+ * Note that the pattern string is similar, but not identical, to
+ * {@link java.text.SimpleDateFormat SimpleDateFormat}.
+ * The pattern string is also similar, but not identical, to that defined by the
+ * Unicode Common Locale Data Repository (CLDR/LDML).
+ * Pattern letters 'X' and 'u' are aligned with Unicode CLDR/LDML.
+ * By contrast, {@code SimpleDateFormat} uses 'u' for the numeric day of week.
+ * Pattern letters 'y' and 'Y' parse years of two digits and more than 4 digits differently.
+ * Pattern letters 'n', 'A', 'N', and 'p' are added.
+ * Number types will reject large numbers.
+ *
+ * @param pattern the pattern to add, not null
+ * @return this, for chaining, not null
+ * @throws IllegalArgumentException if the pattern is invalid
+ */
+ public DateTimeFormatterBuilder appendPattern(String pattern) {
+ Objects.requireNonNull(pattern, "pattern");
+ parsePattern(pattern);
+ return this;
+ }
+
+ private void parsePattern(String pattern) {
+ for (int pos = 0; pos < pattern.length(); pos++) {
+ char cur = pattern.charAt(pos);
+ if ((cur >= 'A' && cur <= 'Z') || (cur >= 'a' && cur <= 'z')) {
+ int start = pos++;
+ for ( ; pos < pattern.length() && pattern.charAt(pos) == cur; pos++); // short loop
+ int count = pos - start;
+ // padding
+ if (cur == 'p') {
+ int pad = 0;
+ if (pos < pattern.length()) {
+ cur = pattern.charAt(pos);
+ if ((cur >= 'A' && cur <= 'Z') || (cur >= 'a' && cur <= 'z')) {
+ pad = count;
+ start = pos++;
+ for ( ; pos < pattern.length() && pattern.charAt(pos) == cur; pos++); // short loop
+ count = pos - start;
+ }
+ }
+ if (pad == 0) {
+ throw new IllegalArgumentException(
+ "Pad letter 'p' must be followed by valid pad pattern: " + pattern);
+ }
+ padNext(pad); // pad and continue parsing
+ }
+ // main rules
+ TemporalField field = FIELD_MAP.get(cur);
+ if (field != null) {
+ parseField(cur, count, field);
+ } else if (cur == 'z') {
+ if (count > 4) {
+ throw new IllegalArgumentException("Too many pattern letters: " + cur);
+ } else if (count == 4) {
+ appendZoneText(TextStyle.FULL);
+ } else {
+ appendZoneText(TextStyle.SHORT);
+ }
+ } else if (cur == 'V') {
+ if (count != 2) {
+ throw new IllegalArgumentException("Pattern letter count must be 2: " + cur);
+ }
+ appendZoneId();
+ } else if (cur == 'Z') {
+ if (count < 4) {
+ appendOffset("+HHMM", "+0000");
+ } else if (count == 4) {
+ appendLocalizedOffset(TextStyle.FULL);
+ } else if (count == 5) {
+ appendOffset("+HH:MM:ss","Z");
+ } else {
+ throw new IllegalArgumentException("Too many pattern letters: " + cur);
+ }
+ } else if (cur == 'O') {
+ if (count == 1) {
+ appendLocalizedOffset(TextStyle.SHORT);
+ } else if (count == 4) {
+ appendLocalizedOffset(TextStyle.FULL);
+ } else {
+ throw new IllegalArgumentException("Pattern letter count must be 1 or 4: " + cur);
+ }
+ } else if (cur == 'X') {
+ if (count > 5) {
+ throw new IllegalArgumentException("Too many pattern letters: " + cur);
+ }
+ appendOffset(OffsetIdPrinterParser.PATTERNS[count + (count == 1 ? 0 : 1)], "Z");
+ } else if (cur == 'x') {
+ if (count > 5) {
+ throw new IllegalArgumentException("Too many pattern letters: " + cur);
+ }
+ String zero = (count == 1 ? "+00" : (count % 2 == 0 ? "+0000" : "+00:00"));
+ appendOffset(OffsetIdPrinterParser.PATTERNS[count + (count == 1 ? 0 : 1)], zero);
+ } else if (cur == 'W') {
+ // Fields defined by Locale
+ if (count > 1) {
+ throw new IllegalArgumentException("Too many pattern letters: " + cur);
+ }
+ appendInternal(new WeekBasedFieldPrinterParser(cur, count));
+ } else if (cur == 'w') {
+ // Fields defined by Locale
+ if (count > 2) {
+ throw new IllegalArgumentException("Too many pattern letters: " + cur);
+ }
+ appendInternal(new WeekBasedFieldPrinterParser(cur, count));
+ } else if (cur == 'Y') {
+ // Fields defined by Locale
+ appendInternal(new WeekBasedFieldPrinterParser(cur, count));
+ } else {
+ throw new IllegalArgumentException("Unknown pattern letter: " + cur);
+ }
+ pos--;
+
+ } else if (cur == '\'') {
+ // parse literals
+ int start = pos++;
+ for ( ; pos < pattern.length(); pos++) {
+ if (pattern.charAt(pos) == '\'') {
+ if (pos + 1 < pattern.length() && pattern.charAt(pos + 1) == '\'') {
+ pos++;
+ } else {
+ break; // end of literal
+ }
+ }
+ }
+ if (pos >= pattern.length()) {
+ throw new IllegalArgumentException("Pattern ends with an incomplete string literal: " + pattern);
+ }
+ String str = pattern.substring(start + 1, pos);
+ if (str.length() == 0) {
+ appendLiteral('\'');
+ } else {
+ appendLiteral(str.replace("''", "'"));
+ }
+
+ } else if (cur == '[') {
+ optionalStart();
+
+ } else if (cur == ']') {
+ if (active.parent == null) {
+ throw new IllegalArgumentException("Pattern invalid as it contains ] without previous [");
+ }
+ optionalEnd();
+
+ } else if (cur == '{' || cur == '}' || cur == '#') {
+ throw new IllegalArgumentException("Pattern includes reserved character: '" + cur + "'");
+ } else {
+ appendLiteral(cur);
+ }
+ }
+ }
+
+ @SuppressWarnings("fallthrough")
+ private void parseField(char cur, int count, TemporalField field) {
+ boolean standalone = false;
+ switch (cur) {
+ case 'u':
+ case 'y':
+ if (count == 2) {
+ appendValueReduced(field, 2, 2, ReducedPrinterParser.BASE_DATE);
+ } else if (count < 4) {
+ appendValue(field, count, 19, SignStyle.NORMAL);
+ } else {
+ appendValue(field, count, 19, SignStyle.EXCEEDS_PAD);
+ }
+ break;
+ case 'c':
+ if (count == 2) {
+ throw new IllegalArgumentException("Invalid pattern \"cc\"");
+ }
+ /*fallthrough*/
+ case 'L':
+ case 'q':
+ standalone = true;
+ /*fallthrough*/
+ case 'M':
+ case 'Q':
+ case 'E':
+ case 'e':
+ switch (count) {
+ case 1:
+ case 2:
+ if (cur == 'c' || cur == 'e') {
+ appendInternal(new WeekBasedFieldPrinterParser(cur, count));
+ } else if (cur == 'E') {
+ appendText(field, TextStyle.SHORT);
+ } else {
+ if (count == 1) {
+ appendValue(field);
+ } else {
+ appendValue(field, 2);
+ }
+ }
+ break;
+ case 3:
+ appendText(field, standalone ? TextStyle.SHORT_STANDALONE : TextStyle.SHORT);
+ break;
+ case 4:
+ appendText(field, standalone ? TextStyle.FULL_STANDALONE : TextStyle.FULL);
+ break;
+ case 5:
+ appendText(field, standalone ? TextStyle.NARROW_STANDALONE : TextStyle.NARROW);
+ break;
+ default:
+ throw new IllegalArgumentException("Too many pattern letters: " + cur);
+ }
+ break;
+ case 'a':
+ if (count == 1) {
+ appendText(field, TextStyle.SHORT);
+ } else {
+ throw new IllegalArgumentException("Too many pattern letters: " + cur);
+ }
+ break;
+ case 'G':
+ switch (count) {
+ case 1:
+ case 2:
+ case 3:
+ appendText(field, TextStyle.SHORT);
+ break;
+ case 4:
+ appendText(field, TextStyle.FULL);
+ break;
+ case 5:
+ appendText(field, TextStyle.NARROW);
+ break;
+ default:
+ throw new IllegalArgumentException("Too many pattern letters: " + cur);
+ }
+ break;
+ case 'S':
+ appendFraction(NANO_OF_SECOND, count, count, false);
+ break;
+ case 'F':
+ if (count == 1) {
+ appendValue(field);
+ } else {
+ throw new IllegalArgumentException("Too many pattern letters: " + cur);
+ }
+ break;
+ case 'd':
+ case 'h':
+ case 'H':
+ case 'k':
+ case 'K':
+ case 'm':
+ case 's':
+ if (count == 1) {
+ appendValue(field);
+ } else if (count == 2) {
+ appendValue(field, count);
+ } else {
+ throw new IllegalArgumentException("Too many pattern letters: " + cur);
+ }
+ break;
+ case 'D':
+ if (count == 1) {
+ appendValue(field);
+ } else if (count <= 3) {
+ appendValue(field, count);
+ } else {
+ throw new IllegalArgumentException("Too many pattern letters: " + cur);
+ }
+ break;
+ default:
+ if (count == 1) {
+ appendValue(field);
+ } else {
+ appendValue(field, count);
+ }
+ break;
+ }
+ }
+
+ /** Map of letters to fields. */
+ private static final Map<Character, TemporalField> FIELD_MAP = new HashMap<>();
+ static {
+ // SDF = SimpleDateFormat
+ FIELD_MAP.put('G', ChronoField.ERA); // SDF, LDML (different to both for 1/2 chars)
+ FIELD_MAP.put('y', ChronoField.YEAR_OF_ERA); // SDF, LDML
+ FIELD_MAP.put('u', ChronoField.YEAR); // LDML (different in SDF)
+ FIELD_MAP.put('Q', IsoFields.QUARTER_OF_YEAR); // LDML (removed quarter from 310)
+ FIELD_MAP.put('q', IsoFields.QUARTER_OF_YEAR); // LDML (stand-alone)
+ FIELD_MAP.put('M', ChronoField.MONTH_OF_YEAR); // SDF, LDML
+ FIELD_MAP.put('L', ChronoField.MONTH_OF_YEAR); // SDF, LDML (stand-alone)
+ FIELD_MAP.put('D', ChronoField.DAY_OF_YEAR); // SDF, LDML
+ FIELD_MAP.put('d', ChronoField.DAY_OF_MONTH); // SDF, LDML
+ FIELD_MAP.put('F', ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH); // SDF, LDML
+ FIELD_MAP.put('E', ChronoField.DAY_OF_WEEK); // SDF, LDML (different to both for 1/2 chars)
+ FIELD_MAP.put('c', ChronoField.DAY_OF_WEEK); // LDML (stand-alone)
+ FIELD_MAP.put('e', ChronoField.DAY_OF_WEEK); // LDML (needs localized week number)
+ FIELD_MAP.put('a', ChronoField.AMPM_OF_DAY); // SDF, LDML
+ FIELD_MAP.put('H', ChronoField.HOUR_OF_DAY); // SDF, LDML
+ FIELD_MAP.put('k', ChronoField.CLOCK_HOUR_OF_DAY); // SDF, LDML
+ FIELD_MAP.put('K', ChronoField.HOUR_OF_AMPM); // SDF, LDML
+ FIELD_MAP.put('h', ChronoField.CLOCK_HOUR_OF_AMPM); // SDF, LDML
+ FIELD_MAP.put('m', ChronoField.MINUTE_OF_HOUR); // SDF, LDML
+ FIELD_MAP.put('s', ChronoField.SECOND_OF_MINUTE); // SDF, LDML
+ FIELD_MAP.put('S', ChronoField.NANO_OF_SECOND); // LDML (SDF uses milli-of-second number)
+ FIELD_MAP.put('A', ChronoField.MILLI_OF_DAY); // LDML
+ FIELD_MAP.put('n', ChronoField.NANO_OF_SECOND); // 310 (proposed for LDML)
+ FIELD_MAP.put('N', ChronoField.NANO_OF_DAY); // 310 (proposed for LDML)
+ // 310 - z - time-zone names, matches LDML and SimpleDateFormat 1 to 4
+ // 310 - Z - matches SimpleDateFormat and LDML
+ // 310 - V - time-zone id, matches LDML
+ // 310 - p - prefix for padding
+ // 310 - X - matches LDML, almost matches SDF for 1, exact match 2&3, extended 4&5
+ // 310 - x - matches LDML
+ // 310 - w, W, and Y are localized forms matching LDML
+ // LDML - U - cycle year name, not supported by 310 yet
+ // LDML - l - deprecated
+ // LDML - j - not relevant
+ // LDML - g - modified-julian-day
+ // LDML - v,V - extended time-zone names
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Causes the next added printer/parser to pad to a fixed width using a space.
+ * <p>
+ * This padding will pad to a fixed width using spaces.
+ * <p>
+ * During formatting, the decorated element will be output and then padded
+ * to the specified width. An exception will be thrown during formatting if
+ * the pad width is exceeded.
+ * <p>
+ * During parsing, the padding and decorated element are parsed.
+ * If parsing is lenient, then the pad width is treated as a maximum.
+ * The padding is parsed greedily. Thus, if the decorated element starts with
+ * the pad character, it will not be parsed.
+ *
+ * @param padWidth the pad width, 1 or greater
+ * @return this, for chaining, not null
+ * @throws IllegalArgumentException if pad width is too small
+ */
+ public DateTimeFormatterBuilder padNext(int padWidth) {
+ return padNext(padWidth, ' ');
+ }
+
+ /**
+ * Causes the next added printer/parser to pad to a fixed width.
+ * <p>
+ * This padding is intended for padding other than zero-padding.
+ * Zero-padding should be achieved using the appendValue methods.
+ * <p>
+ * During formatting, the decorated element will be output and then padded
+ * to the specified width. An exception will be thrown during formatting if
+ * the pad width is exceeded.
+ * <p>
+ * During parsing, the padding and decorated element are parsed.
+ * If parsing is lenient, then the pad width is treated as a maximum.
+ * If parsing is case insensitive, then the pad character is matched ignoring case.
+ * The padding is parsed greedily. Thus, if the decorated element starts with
+ * the pad character, it will not be parsed.
+ *
+ * @param padWidth the pad width, 1 or greater
+ * @param padChar the pad character
+ * @return this, for chaining, not null
+ * @throws IllegalArgumentException if pad width is too small
+ */
+ public DateTimeFormatterBuilder padNext(int padWidth, char padChar) {
+ if (padWidth < 1) {
+ throw new IllegalArgumentException("The pad width must be at least one but was " + padWidth);
+ }
+ active.padNextWidth = padWidth;
+ active.padNextChar = padChar;
+ active.valueParserIndex = -1;
+ return this;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Mark the start of an optional section.
+ * <p>
+ * The output of formatting can include optional sections, which may be nested.
+ * An optional section is started by calling this method and ended by calling
+ * {@link #optionalEnd()} or by ending the build process.
+ * <p>
+ * All elements in the optional section are treated as optional.
+ * During formatting, the section is only output if data is available in the
+ * {@code TemporalAccessor} for all the elements in the section.
+ * During parsing, the whole section may be missing from the parsed string.
+ * <p>
+ * For example, consider a builder setup as
+ * {@code builder.appendValue(HOUR_OF_DAY,2).optionalStart().appendValue(MINUTE_OF_HOUR,2)}.
+ * The optional section ends automatically at the end of the builder.
+ * During formatting, the minute will only be output if its value can be obtained from the date-time.
+ * During parsing, the input will be successfully parsed whether the minute is present or not.
+ *
+ * @return this, for chaining, not null
+ */
+ public DateTimeFormatterBuilder optionalStart() {
+ active.valueParserIndex = -1;
+ active = new DateTimeFormatterBuilder(active, true);
+ return this;
+ }
+
+ /**
+ * Ends an optional section.
+ * <p>
+ * The output of formatting can include optional sections, which may be nested.
+ * An optional section is started by calling {@link #optionalStart()} and ended
+ * using this method (or at the end of the builder).
+ * <p>
+ * Calling this method without having previously called {@code optionalStart}
+ * will throw an exception.
+ * Calling this method immediately after calling {@code optionalStart} has no effect
+ * on the formatter other than ending the (empty) optional section.
+ * <p>
+ * All elements in the optional section are treated as optional.
+ * During formatting, the section is only output if data is available in the
+ * {@code TemporalAccessor} for all the elements in the section.
+ * During parsing, the whole section may be missing from the parsed string.
+ * <p>
+ * For example, consider a builder setup as
+ * {@code builder.appendValue(HOUR_OF_DAY,2).optionalStart().appendValue(MINUTE_OF_HOUR,2).optionalEnd()}.
+ * During formatting, the minute will only be output if its value can be obtained from the date-time.
+ * During parsing, the input will be successfully parsed whether the minute is present or not.
+ *
+ * @return this, for chaining, not null
+ * @throws IllegalStateException if there was no previous call to {@code optionalStart}
+ */
+ public DateTimeFormatterBuilder optionalEnd() {
+ if (active.parent == null) {
+ throw new IllegalStateException("Cannot call optionalEnd() as there was no previous call to optionalStart()");
+ }
+ if (active.printerParsers.size() > 0) {
+ CompositePrinterParser cpp = new CompositePrinterParser(active.printerParsers, active.optional);
+ active = active.parent;
+ appendInternal(cpp);
+ } else {
+ active = active.parent;
+ }
+ return this;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Appends a printer and/or parser to the internal list handling padding.
+ *
+ * @param pp the printer-parser to add, not null
+ * @return the index into the active parsers list
+ */
+ private int appendInternal(DateTimePrinterParser pp) {
+ Objects.requireNonNull(pp, "pp");
+ if (active.padNextWidth > 0) {
+ if (pp != null) {
+ pp = new PadPrinterParserDecorator(pp, active.padNextWidth, active.padNextChar);
+ }
+ active.padNextWidth = 0;
+ active.padNextChar = 0;
+ }
+ active.printerParsers.add(pp);
+ active.valueParserIndex = -1;
+ return active.printerParsers.size() - 1;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Completes this builder by creating the {@code DateTimeFormatter}
+ * using the default locale.
+ * <p>
+ * This will create a formatter with the {@linkplain Locale#getDefault(Locale.Category) default FORMAT locale}.
+ * Numbers will be printed and parsed using the standard DecimalStyle.
+ * The resolver style will be {@link ResolverStyle#SMART SMART}.
+ * <p>
+ * Calling this method will end any open optional sections by repeatedly
+ * calling {@link #optionalEnd()} before creating the formatter.
+ * <p>
+ * This builder can still be used after creating the formatter if desired,
+ * although the state may have been changed by calls to {@code optionalEnd}.
+ *
+ * @return the created formatter, not null
+ */
+ public DateTimeFormatter toFormatter() {
+ return toFormatter(Locale.getDefault(Locale.Category.FORMAT));
+ }
+
+ /**
+ * Completes this builder by creating the {@code DateTimeFormatter}
+ * using the specified locale.
+ * <p>
+ * This will create a formatter with the specified locale.
+ * Numbers will be printed and parsed using the standard DecimalStyle.
+ * The resolver style will be {@link ResolverStyle#SMART SMART}.
+ * <p>
+ * Calling this method will end any open optional sections by repeatedly
+ * calling {@link #optionalEnd()} before creating the formatter.
+ * <p>
+ * This builder can still be used after creating the formatter if desired,
+ * although the state may have been changed by calls to {@code optionalEnd}.
+ *
+ * @param locale the locale to use for formatting, not null
+ * @return the created formatter, not null
+ */
+ public DateTimeFormatter toFormatter(Locale locale) {
+ return toFormatter(locale, ResolverStyle.SMART, null);
+ }
+
+ /**
+ * Completes this builder by creating the formatter.
+ * This uses the default locale.
+ *
+ * @param resolverStyle the resolver style to use, not null
+ * @return the created formatter, not null
+ */
+ DateTimeFormatter toFormatter(ResolverStyle resolverStyle, Chronology chrono) {
+ return toFormatter(Locale.getDefault(Locale.Category.FORMAT), resolverStyle, chrono);
+ }
+
+ /**
+ * Completes this builder by creating the formatter.
+ *
+ * @param locale the locale to use for formatting, not null
+ * @param chrono the chronology to use, may be null
+ * @return the created formatter, not null
+ */
+ private DateTimeFormatter toFormatter(Locale locale, ResolverStyle resolverStyle, Chronology chrono) {
+ Objects.requireNonNull(locale, "locale");
+ while (active.parent != null) {
+ optionalEnd();
+ }
+ CompositePrinterParser pp = new CompositePrinterParser(printerParsers, false);
+ return new DateTimeFormatter(pp, locale, DecimalStyle.STANDARD,
+ resolverStyle, null, chrono, null);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Strategy for formatting/parsing date-time information.
+ * <p>
+ * The printer may format any part, or the whole, of the input date-time object.
+ * Typically, a complete format is constructed from a number of smaller
+ * units, each outputting a single field.
+ * <p>
+ * The parser may parse any piece of text from the input, storing the result
+ * in the context. Typically, each individual parser will just parse one
+ * field, such as the day-of-month, storing the value in the context.
+ * Once the parse is complete, the caller will then resolve the parsed values
+ * to create the desired object, such as a {@code LocalDate}.
+ * <p>
+ * The parse position will be updated during the parse. Parsing will start at
+ * the specified index and the return value specifies the new parse position
+ * for the next parser. If an error occurs, the returned index will be negative
+ * and will have the error position encoded using the complement operator.
+ *
+ * @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.
+ * <p>
+ * The context is not a thread-safe object and a new instance will be created
+ * for each format that occurs. The context must not be stored in an instance
+ * variable or shared with any other threads.
+ */
+ interface DateTimePrinterParser {
+
+ /**
+ * Prints the date-time object to the buffer.
+ * <p>
+ * The context holds information to use during the format.
+ * It also contains the date-time information to be printed.
+ * <p>
+ * The buffer must not be mutated beyond the content controlled by the implementation.
+ *
+ * @param context the context to format using, not null
+ * @param buf the buffer to append to, not null
+ * @return false if unable to query the value from the date-time, true otherwise
+ * @throws DateTimeException if the date-time cannot be printed successfully
+ */
+ boolean format(DateTimePrintContext context, StringBuilder buf);
+
+ /**
+ * Parses text into date-time information.
+ * <p>
+ * The context holds information to use during the parse.
+ * It is also used to store the parsed date-time information.
+ *
+ * @param context the context to use and parse into, not null
+ * @param text the input text to parse, not null
+ * @param position the position to start parsing at, from 0 to the text length
+ * @return the new parse position, where negative means an error with the
+ * error position encoded using the complement ~ operator
+ * @throws NullPointerException if the context or text is null
+ * @throws IndexOutOfBoundsException if the position is invalid
+ */
+ int parse(DateTimeParseContext context, CharSequence text, int position);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Composite printer and parser.
+ */
+ static final class CompositePrinterParser implements DateTimePrinterParser {
+ private final DateTimePrinterParser[] printerParsers;
+ private final boolean optional;
+
+ CompositePrinterParser(List<DateTimePrinterParser> printerParsers, boolean optional) {
+ this(printerParsers.toArray(new DateTimePrinterParser[printerParsers.size()]), optional);
+ }
+
+ CompositePrinterParser(DateTimePrinterParser[] printerParsers, boolean optional) {
+ this.printerParsers = printerParsers;
+ this.optional = optional;
+ }
+
+ /**
+ * Returns a copy of this printer-parser with the optional flag changed.
+ *
+ * @param optional the optional flag to set in the copy
+ * @return the new printer-parser, not null
+ */
+ public CompositePrinterParser withOptional(boolean optional) {
+ if (optional == this.optional) {
+ return this;
+ }
+ return new CompositePrinterParser(printerParsers, optional);
+ }
+
+ @Override
+ public boolean format(DateTimePrintContext context, StringBuilder buf) {
+ int length = buf.length();
+ if (optional) {
+ context.startOptional();
+ }
+ try {
+ for (DateTimePrinterParser pp : printerParsers) {
+ if (pp.format(context, buf) == false) {
+ buf.setLength(length); // reset buffer
+ return true;
+ }
+ }
+ } finally {
+ if (optional) {
+ context.endOptional();
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public int parse(DateTimeParseContext context, CharSequence text, int position) {
+ if (optional) {
+ context.startOptional();
+ int pos = position;
+ for (DateTimePrinterParser pp : printerParsers) {
+ pos = pp.parse(context, text, pos);
+ if (pos < 0) {
+ context.endOptional(false);
+ return position; // return original position
+ }
+ }
+ context.endOptional(true);
+ return pos;
+ } else {
+ for (DateTimePrinterParser pp : printerParsers) {
+ position = pp.parse(context, text, position);
+ if (position < 0) {
+ break;
+ }
+ }
+ return position;
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder buf = new StringBuilder();
+ if (printerParsers != null) {
+ buf.append(optional ? "[" : "(");
+ for (DateTimePrinterParser pp : printerParsers) {
+ buf.append(pp);
+ }
+ buf.append(optional ? "]" : ")");
+ }
+ return buf.toString();
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Pads the output to a fixed width.
+ */
+ static final class PadPrinterParserDecorator implements DateTimePrinterParser {
+ private final DateTimePrinterParser printerParser;
+ private final int padWidth;
+ private final char padChar;
+
+ /**
+ * Constructor.
+ *
+ * @param printerParser the printer, not null
+ * @param padWidth the width to pad to, 1 or greater
+ * @param padChar the pad character
+ */
+ PadPrinterParserDecorator(DateTimePrinterParser printerParser, int padWidth, char padChar) {
+ // input checked by DateTimeFormatterBuilder
+ this.printerParser = printerParser;
+ this.padWidth = padWidth;
+ this.padChar = padChar;
+ }
+
+ @Override
+ public boolean format(DateTimePrintContext context, StringBuilder buf) {
+ int preLen = buf.length();
+ if (printerParser.format(context, buf) == false) {
+ return false;
+ }
+ int len = buf.length() - preLen;
+ if (len > padWidth) {
+ throw new DateTimeException(
+ "Cannot print as output of " + len + " characters exceeds pad width of " + padWidth);
+ }
+ for (int i = 0; i < padWidth - len; i++) {
+ buf.insert(preLen, padChar);
+ }
+ return true;
+ }
+
+ @Override
+ public int parse(DateTimeParseContext context, CharSequence text, int position) {
+ // cache context before changed by decorated parser
+ final boolean strict = context.isStrict();
+ // parse
+ if (position > text.length()) {
+ throw new IndexOutOfBoundsException();
+ }
+ if (position == text.length()) {
+ return ~position; // no more characters in the string
+ }
+ int endPos = position + padWidth;
+ if (endPos > text.length()) {
+ if (strict) {
+ return ~position; // not enough characters in the string to meet the parse width
+ }
+ endPos = text.length();
+ }
+ int pos = position;
+ while (pos < endPos && context.charEquals(text.charAt(pos), padChar)) {
+ pos++;
+ }
+ text = text.subSequence(0, endPos);
+ int resultPos = printerParser.parse(context, text, pos);
+ if (resultPos != endPos && strict) {
+ return ~(position + pos); // parse of decorated field didn't parse to the end
+ }
+ return resultPos;
+ }
+
+ @Override
+ public String toString() {
+ return "Pad(" + printerParser + "," + padWidth + (padChar == ' ' ? ")" : ",'" + padChar + "')");
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Enumeration to apply simple parse settings.
+ */
+ static enum SettingsParser implements DateTimePrinterParser {
+ SENSITIVE,
+ INSENSITIVE,
+ STRICT,
+ LENIENT;
+
+ @Override
+ public boolean format(DateTimePrintContext context, StringBuilder buf) {
+ return true; // nothing to do here
+ }
+
+ @Override
+ public int parse(DateTimeParseContext context, CharSequence text, int position) {
+ // using ordinals to avoid javac synthetic inner class
+ switch (ordinal()) {
+ case 0: context.setCaseSensitive(true); break;
+ case 1: context.setCaseSensitive(false); break;
+ case 2: context.setStrict(true); break;
+ case 3: context.setStrict(false); break;
+ }
+ return position;
+ }
+
+ @Override
+ public String toString() {
+ // using ordinals to avoid javac synthetic inner class
+ switch (ordinal()) {
+ case 0: return "ParseCaseSensitive(true)";
+ case 1: return "ParseCaseSensitive(false)";
+ case 2: return "ParseStrict(true)";
+ case 3: return "ParseStrict(false)";
+ }
+ throw new IllegalStateException("Unreachable");
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Defaults a value into the parse if not currently present.
+ */
+ static class DefaultValueParser implements DateTimePrinterParser {
+ private final TemporalField field;
+ private final long value;
+
+ DefaultValueParser(TemporalField field, long value) {
+ this.field = field;
+ this.value = value;
+ }
+
+ public boolean format(DateTimePrintContext context, StringBuilder buf) {
+ return true;
+ }
+
+ public int parse(DateTimeParseContext context, CharSequence text, int position) {
+ if (context.getParsed(field) == null) {
+ context.setParsedField(field, value, position, position);
+ }
+ return position;
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Prints or parses a character literal.
+ */
+ static final class CharLiteralPrinterParser implements DateTimePrinterParser {
+ private final char literal;
+
+ CharLiteralPrinterParser(char literal) {
+ this.literal = literal;
+ }
+
+ @Override
+ public boolean format(DateTimePrintContext context, StringBuilder buf) {
+ buf.append(literal);
+ return true;
+ }
+
+ @Override
+ public int parse(DateTimeParseContext context, CharSequence text, int position) {
+ int length = text.length();
+ if (position == length) {
+ return ~position;
+ }
+ char ch = text.charAt(position);
+ if (ch != literal) {
+ if (context.isCaseSensitive() ||
+ (Character.toUpperCase(ch) != Character.toUpperCase(literal) &&
+ Character.toLowerCase(ch) != Character.toLowerCase(literal))) {
+ return ~position;
+ }
+ }
+ return position + 1;
+ }
+
+ @Override
+ public String toString() {
+ if (literal == '\'') {
+ return "''";
+ }
+ return "'" + literal + "'";
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Prints or parses a string literal.
+ */
+ static final class StringLiteralPrinterParser implements DateTimePrinterParser {
+ private final String literal;
+
+ StringLiteralPrinterParser(String literal) {
+ this.literal = literal; // validated by caller
+ }
+
+ @Override
+ public boolean format(DateTimePrintContext context, StringBuilder buf) {
+ buf.append(literal);
+ return true;
+ }
+
+ @Override
+ public int parse(DateTimeParseContext context, CharSequence text, int position) {
+ int length = text.length();
+ if (position > length || position < 0) {
+ throw new IndexOutOfBoundsException();
+ }
+ if (context.subSequenceEquals(text, position, literal, 0, literal.length()) == false) {
+ return ~position;
+ }
+ return position + literal.length();
+ }
+
+ @Override
+ public String toString() {
+ String converted = literal.replace("'", "''");
+ return "'" + converted + "'";
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Prints and parses a numeric date-time field with optional padding.
+ */
+ static class NumberPrinterParser implements DateTimePrinterParser {
+
+ /**
+ * Array of 10 to the power of n.
+ */
+ static final long[] EXCEED_POINTS = new long[] {
+ 0L,
+ 10L,
+ 100L,
+ 1000L,
+ 10000L,
+ 100000L,
+ 1000000L,
+ 10000000L,
+ 100000000L,
+ 1000000000L,
+ 10000000000L,
+ };
+
+ final TemporalField field;
+ final int minWidth;
+ final int maxWidth;
+ private final SignStyle signStyle;
+ final int subsequentWidth;
+
+ /**
+ * Constructor.
+ *
+ * @param field the field to format, not null
+ * @param minWidth the minimum field width, from 1 to 19
+ * @param maxWidth the maximum field width, from minWidth to 19
+ * @param signStyle the positive/negative sign style, not null
+ */
+ NumberPrinterParser(TemporalField field, int minWidth, int maxWidth, SignStyle signStyle) {
+ // validated by caller
+ this.field = field;
+ this.minWidth = minWidth;
+ this.maxWidth = maxWidth;
+ this.signStyle = signStyle;
+ this.subsequentWidth = 0;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param field the field to format, not null
+ * @param minWidth the minimum field width, from 1 to 19
+ * @param maxWidth the maximum field width, from minWidth to 19
+ * @param signStyle the positive/negative sign style, not null
+ * @param subsequentWidth the width of subsequent non-negative numbers, 0 or greater,
+ * -1 if fixed width due to active adjacent parsing
+ */
+ protected NumberPrinterParser(TemporalField field, int minWidth, int maxWidth, SignStyle signStyle, int subsequentWidth) {
+ // validated by caller
+ this.field = field;
+ this.minWidth = minWidth;
+ this.maxWidth = maxWidth;
+ this.signStyle = signStyle;
+ this.subsequentWidth = subsequentWidth;
+ }
+
+ /**
+ * Returns a new instance with fixed width flag set.
+ *
+ * @return a new updated printer-parser, not null
+ */
+ NumberPrinterParser withFixedWidth() {
+ if (subsequentWidth == -1) {
+ return this;
+ }
+ return new NumberPrinterParser(field, minWidth, maxWidth, signStyle, -1);
+ }
+
+ /**
+ * Returns a new instance with an updated subsequent width.
+ *
+ * @param subsequentWidth the width of subsequent non-negative numbers, 0 or greater
+ * @return a new updated printer-parser, not null
+ */
+ NumberPrinterParser withSubsequentWidth(int subsequentWidth) {
+ return new NumberPrinterParser(field, minWidth, maxWidth, signStyle, this.subsequentWidth + subsequentWidth);
+ }
+
+ @Override
+ public boolean format(DateTimePrintContext context, StringBuilder buf) {
+ Long valueLong = context.getValue(field);
+ if (valueLong == null) {
+ return false;
+ }
+ long value = getValue(context, valueLong);
+ DecimalStyle decimalStyle = context.getDecimalStyle();
+ String str = (value == Long.MIN_VALUE ? "9223372036854775808" : Long.toString(Math.abs(value)));
+ if (str.length() > maxWidth) {
+ throw new DateTimeException("Field " + field +
+ " cannot be printed as the value " + value +
+ " exceeds the maximum print width of " + maxWidth);
+ }
+ str = decimalStyle.convertNumberToI18N(str);
+
+ if (value >= 0) {
+ switch (signStyle) {
+ case EXCEEDS_PAD:
+ if (minWidth < 19 && value >= EXCEED_POINTS[minWidth]) {
+ buf.append(decimalStyle.getPositiveSign());
+ }
+ break;
+ case ALWAYS:
+ buf.append(decimalStyle.getPositiveSign());
+ break;
+ }
+ } else {
+ switch (signStyle) {
+ case NORMAL:
+ case EXCEEDS_PAD:
+ case ALWAYS:
+ buf.append(decimalStyle.getNegativeSign());
+ break;
+ case NOT_NEGATIVE:
+ throw new DateTimeException("Field " + field +
+ " cannot be printed as the value " + value +
+ " cannot be negative according to the SignStyle");
+ }
+ }
+ for (int i = 0; i < minWidth - str.length(); i++) {
+ buf.append(decimalStyle.getZeroDigit());
+ }
+ buf.append(str);
+ return true;
+ }
+
+ /**
+ * Gets the value to output.
+ *
+ * @param context the context
+ * @param value the value of the field, not null
+ * @return the value
+ */
+ long getValue(DateTimePrintContext context, long value) {
+ return value;
+ }
+
+ /**
+ * For NumberPrinterParser, the width is fixed depending on the
+ * minWidth, maxWidth, signStyle and whether subsequent fields are fixed.
+ * @param context the context
+ * @return true if the field is fixed width
+ * @see DateTimeFormatterBuilder#appendValue(java.time.temporal.TemporalField, int)
+ */
+ boolean isFixedWidth(DateTimeParseContext context) {
+ return subsequentWidth == -1 ||
+ (subsequentWidth > 0 && minWidth == maxWidth && signStyle == SignStyle.NOT_NEGATIVE);
+ }
+
+ @Override
+ public int parse(DateTimeParseContext context, CharSequence text, int position) {
+ int length = text.length();
+ if (position == length) {
+ return ~position;
+ }
+ char sign = text.charAt(position); // IOOBE if invalid position
+ boolean negative = false;
+ boolean positive = false;
+ if (sign == context.getDecimalStyle().getPositiveSign()) {
+ if (signStyle.parse(true, context.isStrict(), minWidth == maxWidth) == false) {
+ return ~position;
+ }
+ positive = true;
+ position++;
+ } else if (sign == context.getDecimalStyle().getNegativeSign()) {
+ if (signStyle.parse(false, context.isStrict(), minWidth == maxWidth) == false) {
+ return ~position;
+ }
+ negative = true;
+ position++;
+ } else {
+ if (signStyle == SignStyle.ALWAYS && context.isStrict()) {
+ return ~position;
+ }
+ }
+ int effMinWidth = (context.isStrict() || isFixedWidth(context) ? minWidth : 1);
+ int minEndPos = position + effMinWidth;
+ if (minEndPos > length) {
+ return ~position;
+ }
+ int effMaxWidth = (context.isStrict() || isFixedWidth(context) ? maxWidth : 9) + Math.max(subsequentWidth, 0);
+ long total = 0;
+ BigInteger totalBig = null;
+ int pos = position;
+ for (int pass = 0; pass < 2; pass++) {
+ int maxEndPos = Math.min(pos + effMaxWidth, length);
+ while (pos < maxEndPos) {
+ char ch = text.charAt(pos++);
+ int digit = context.getDecimalStyle().convertToDigit(ch);
+ if (digit < 0) {
+ pos--;
+ if (pos < minEndPos) {
+ return ~position; // need at least min width digits
+ }
+ break;
+ }
+ if ((pos - position) > 18) {
+ if (totalBig == null) {
+ totalBig = BigInteger.valueOf(total);
+ }
+ totalBig = totalBig.multiply(BigInteger.TEN).add(BigInteger.valueOf(digit));
+ } else {
+ total = total * 10 + digit;
+ }
+ }
+ if (subsequentWidth > 0 && pass == 0) {
+ // re-parse now we know the correct width
+ int parseLen = pos - position;
+ effMaxWidth = Math.max(effMinWidth, parseLen - subsequentWidth);
+ pos = position;
+ total = 0;
+ totalBig = null;
+ } else {
+ break;
+ }
+ }
+ if (negative) {
+ if (totalBig != null) {
+ if (totalBig.equals(BigInteger.ZERO) && context.isStrict()) {
+ return ~(position - 1); // minus zero not allowed
+ }
+ totalBig = totalBig.negate();
+ } else {
+ if (total == 0 && context.isStrict()) {
+ return ~(position - 1); // minus zero not allowed
+ }
+ total = -total;
+ }
+ } else if (signStyle == SignStyle.EXCEEDS_PAD && context.isStrict()) {
+ int parseLen = pos - position;
+ if (positive) {
+ if (parseLen <= minWidth) {
+ return ~(position - 1); // '+' only parsed if minWidth exceeded
+ }
+ } else {
+ if (parseLen > minWidth) {
+ return ~position; // '+' must be parsed if minWidth exceeded
+ }
+ }
+ }
+ if (totalBig != null) {
+ if (totalBig.bitLength() > 63) {
+ // overflow, parse 1 less digit
+ totalBig = totalBig.divide(BigInteger.TEN);
+ pos--;
+ }
+ return setValue(context, totalBig.longValue(), position, pos);
+ }
+ return setValue(context, total, position, pos);
+ }
+
+ /**
+ * Stores the value.
+ *
+ * @param context the context to store into, not null
+ * @param value the value
+ * @param errorPos the position of the field being parsed
+ * @param successPos the position after the field being parsed
+ * @return the new position
+ */
+ int setValue(DateTimeParseContext context, long value, int errorPos, int successPos) {
+ return context.setParsedField(field, value, errorPos, successPos);
+ }
+
+ @Override
+ public String toString() {
+ if (minWidth == 1 && maxWidth == 19 && signStyle == SignStyle.NORMAL) {
+ return "Value(" + field + ")";
+ }
+ if (minWidth == maxWidth && signStyle == SignStyle.NOT_NEGATIVE) {
+ return "Value(" + field + "," + minWidth + ")";
+ }
+ return "Value(" + field + "," + minWidth + "," + maxWidth + "," + signStyle + ")";
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Prints and parses a reduced numeric date-time field.
+ */
+ static final class ReducedPrinterParser extends NumberPrinterParser {
+ /**
+ * The base date for reduced value parsing.
+ */
+ static final LocalDate BASE_DATE = LocalDate.of(2000, 1, 1);
+
+ private final int baseValue;
+ private final ChronoLocalDate baseDate;
+
+ /**
+ * Constructor.
+ *
+ * @param field the field to format, validated not null
+ * @param minWidth the minimum field width, from 1 to 10
+ * @param maxWidth the maximum field width, from 1 to 10
+ * @param baseValue the base value
+ * @param baseDate the base date
+ */
+ ReducedPrinterParser(TemporalField field, int minWidth, int maxWidth,
+ int baseValue, ChronoLocalDate baseDate) {
+ this(field, minWidth, maxWidth, baseValue, baseDate, 0);
+ if (minWidth < 1 || minWidth > 10) {
+ throw new IllegalArgumentException("The minWidth must be from 1 to 10 inclusive but was " + minWidth);
+ }
+ if (maxWidth < 1 || maxWidth > 10) {
+ throw new IllegalArgumentException("The maxWidth must be from 1 to 10 inclusive but was " + minWidth);
+ }
+ if (maxWidth < minWidth) {
+ throw new IllegalArgumentException("Maximum width must exceed or equal the minimum width but " +
+ maxWidth + " < " + minWidth);
+ }
+ if (baseDate == null) {
+ if (field.range().isValidValue(baseValue) == false) {
+ throw new IllegalArgumentException("The base value must be within the range of the field");
+ }
+ if ((((long) baseValue) + EXCEED_POINTS[maxWidth]) > Integer.MAX_VALUE) {
+ throw new DateTimeException("Unable to add printer-parser as the range exceeds the capacity of an int");
+ }
+ }
+ }
+
+ /**
+ * Constructor.
+ * The arguments have already been checked.
+ *
+ * @param field the field to format, validated not null
+ * @param minWidth the minimum field width, from 1 to 10
+ * @param maxWidth the maximum field width, from 1 to 10
+ * @param baseValue the base value
+ * @param baseDate the base date
+ * @param subsequentWidth the subsequentWidth for this instance
+ */
+ private ReducedPrinterParser(TemporalField field, int minWidth, int maxWidth,
+ int baseValue, ChronoLocalDate baseDate, int subsequentWidth) {
+ super(field, minWidth, maxWidth, SignStyle.NOT_NEGATIVE, subsequentWidth);
+ this.baseValue = baseValue;
+ this.baseDate = baseDate;
+ }
+
+ @Override
+ long getValue(DateTimePrintContext context, long value) {
+ long absValue = Math.abs(value);
+ int baseValue = this.baseValue;
+ if (baseDate != null) {
+ Chronology chrono = Chronology.from(context.getTemporal());
+ baseValue = chrono.date(baseDate).get(field);
+ }
+ if (value >= baseValue && value < baseValue + EXCEED_POINTS[minWidth]) {
+ // Use the reduced value if it fits in minWidth
+ return absValue % EXCEED_POINTS[minWidth];
+ }
+ // Otherwise truncate to fit in maxWidth
+ return absValue % EXCEED_POINTS[maxWidth];
+ }
+
+ @Override
+ int setValue(DateTimeParseContext context, long value, int errorPos, int successPos) {
+ int baseValue = this.baseValue;
+ if (baseDate != null) {
+ Chronology chrono = context.getEffectiveChronology();
+ baseValue = chrono.date(baseDate).get(field);
+
+ // In case the Chronology is changed later, add a callback when/if it changes
+ final long initialValue = value;
+ context.addChronoChangedListener(
+ (_unused) -> {
+ /* Repeat the set of the field using the current Chronology
+ * The success/error position is ignored because the value is
+ * intentionally being overwritten.
+ */
+ setValue(context, initialValue, errorPos, successPos);
+ });
+ }
+ int parseLen = successPos - errorPos;
+ if (parseLen == minWidth && value >= 0) {
+ long range = EXCEED_POINTS[minWidth];
+ long lastPart = baseValue % range;
+ long basePart = baseValue - lastPart;
+ if (baseValue > 0) {
+ value = basePart + value;
+ } else {
+ value = basePart - value;
+ }
+ if (value < baseValue) {
+ value += range;
+ }
+ }
+ return context.setParsedField(field, value, errorPos, successPos);
+ }
+
+ /**
+ * Returns a new instance with fixed width flag set.
+ *
+ * @return a new updated printer-parser, not null
+ */
+ @Override
+ ReducedPrinterParser withFixedWidth() {
+ if (subsequentWidth == -1) {
+ return this;
+ }
+ return new ReducedPrinterParser(field, minWidth, maxWidth, baseValue, baseDate, -1);
+ }
+
+ /**
+ * Returns a new instance with an updated subsequent width.
+ *
+ * @param subsequentWidth the width of subsequent non-negative numbers, 0 or greater
+ * @return a new updated printer-parser, not null
+ */
+ @Override
+ ReducedPrinterParser withSubsequentWidth(int subsequentWidth) {
+ return new ReducedPrinterParser(field, minWidth, maxWidth, baseValue, baseDate,
+ this.subsequentWidth + subsequentWidth);
+ }
+
+ /**
+ * For a ReducedPrinterParser, fixed width is false if the mode is strict,
+ * otherwise it is set as for NumberPrinterParser.
+ * @param context the context
+ * @return if the field is fixed width
+ * @see DateTimeFormatterBuilder#appendValueReduced(java.time.temporal.TemporalField, int, int, int)
+ */
+ @Override
+ boolean isFixedWidth(DateTimeParseContext context) {
+ if (context.isStrict() == false) {
+ return false;
+ }
+ return super.isFixedWidth(context);
+ }
+
+ @Override
+ public String toString() {
+ return "ReducedValue(" + field + "," + minWidth + "," + maxWidth + "," + (baseDate != null ? baseDate : baseValue) + ")";
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Prints and parses a numeric date-time field with optional padding.
+ */
+ static final class FractionPrinterParser implements DateTimePrinterParser {
+ private final TemporalField field;
+ private final int minWidth;
+ private final int maxWidth;
+ private final boolean decimalPoint;
+
+ /**
+ * Constructor.
+ *
+ * @param field the field to output, not null
+ * @param minWidth the minimum width to output, from 0 to 9
+ * @param maxWidth the maximum width to output, from 0 to 9
+ * @param decimalPoint whether to output the localized decimal point symbol
+ */
+ FractionPrinterParser(TemporalField field, int minWidth, int maxWidth, boolean decimalPoint) {
+ Objects.requireNonNull(field, "field");
+ if (field.range().isFixed() == false) {
+ throw new IllegalArgumentException("Field must have a fixed set of values: " + field);
+ }
+ if (minWidth < 0 || minWidth > 9) {
+ throw new IllegalArgumentException("Minimum width must be from 0 to 9 inclusive but was " + minWidth);
+ }
+ if (maxWidth < 1 || maxWidth > 9) {
+ throw new IllegalArgumentException("Maximum width must be from 1 to 9 inclusive but was " + maxWidth);
+ }
+ if (maxWidth < minWidth) {
+ throw new IllegalArgumentException("Maximum width must exceed or equal the minimum width but " +
+ maxWidth + " < " + minWidth);
+ }
+ this.field = field;
+ this.minWidth = minWidth;
+ this.maxWidth = maxWidth;
+ this.decimalPoint = decimalPoint;
+ }
+
+ @Override
+ public boolean format(DateTimePrintContext context, StringBuilder buf) {
+ Long value = context.getValue(field);
+ if (value == null) {
+ return false;
+ }
+ DecimalStyle decimalStyle = context.getDecimalStyle();
+ BigDecimal fraction = convertToFraction(value);
+ if (fraction.scale() == 0) { // scale is zero if value is zero
+ if (minWidth > 0) {
+ if (decimalPoint) {
+ buf.append(decimalStyle.getDecimalSeparator());
+ }
+ for (int i = 0; i < minWidth; i++) {
+ buf.append(decimalStyle.getZeroDigit());
+ }
+ }
+ } else {
+ int outputScale = Math.min(Math.max(fraction.scale(), minWidth), maxWidth);
+ fraction = fraction.setScale(outputScale, RoundingMode.FLOOR);
+ String str = fraction.toPlainString().substring(2);
+ str = decimalStyle.convertNumberToI18N(str);
+ if (decimalPoint) {
+ buf.append(decimalStyle.getDecimalSeparator());
+ }
+ buf.append(str);
+ }
+ return true;
+ }
+
+ @Override
+ public int parse(DateTimeParseContext context, CharSequence text, int position) {
+ int effectiveMin = (context.isStrict() ? minWidth : 0);
+ int effectiveMax = (context.isStrict() ? maxWidth : 9);
+ int length = text.length();
+ if (position == length) {
+ // valid if whole field is optional, invalid if minimum width
+ return (effectiveMin > 0 ? ~position : position);
+ }
+ if (decimalPoint) {
+ if (text.charAt(position) != context.getDecimalStyle().getDecimalSeparator()) {
+ // valid if whole field is optional, invalid if minimum width
+ return (effectiveMin > 0 ? ~position : position);
+ }
+ position++;
+ }
+ int minEndPos = position + effectiveMin;
+ if (minEndPos > length) {
+ return ~position; // need at least min width digits
+ }
+ int maxEndPos = Math.min(position + effectiveMax, length);
+ int total = 0; // can use int because we are only parsing up to 9 digits
+ int pos = position;
+ while (pos < maxEndPos) {
+ char ch = text.charAt(pos++);
+ int digit = context.getDecimalStyle().convertToDigit(ch);
+ if (digit < 0) {
+ if (pos < minEndPos) {
+ return ~position; // need at least min width digits
+ }
+ pos--;
+ break;
+ }
+ total = total * 10 + digit;
+ }
+ BigDecimal fraction = new BigDecimal(total).movePointLeft(pos - position);
+ long value = convertFromFraction(fraction);
+ return context.setParsedField(field, value, position, pos);
+ }
+
+ /**
+ * Converts a value for this field to a fraction between 0 and 1.
+ * <p>
+ * The fractional value is between 0 (inclusive) and 1 (exclusive).
+ * It can only be returned if the {@link java.time.temporal.TemporalField#range() value range} is fixed.
+ * The fraction is obtained by calculation from the field range using 9 decimal
+ * places and a rounding mode of {@link RoundingMode#FLOOR FLOOR}.
+ * The calculation is inaccurate if the values do not run continuously from smallest to largest.
+ * <p>
+ * For example, the second-of-minute value of 15 would be returned as 0.25,
+ * assuming the standard definition of 60 seconds in a minute.
+ *
+ * @param value the value to convert, must be valid for this rule
+ * @return the value as a fraction within the range, from 0 to 1, not null
+ * @throws DateTimeException if the value cannot be converted to a fraction
+ */
+ private BigDecimal convertToFraction(long value) {
+ ValueRange range = field.range();
+ range.checkValidValue(value, field);
+ BigDecimal minBD = BigDecimal.valueOf(range.getMinimum());
+ BigDecimal rangeBD = BigDecimal.valueOf(range.getMaximum()).subtract(minBD).add(BigDecimal.ONE);
+ BigDecimal valueBD = BigDecimal.valueOf(value).subtract(minBD);
+ BigDecimal fraction = valueBD.divide(rangeBD, 9, RoundingMode.FLOOR);
+ // stripTrailingZeros bug
+ return fraction.compareTo(BigDecimal.ZERO) == 0 ? BigDecimal.ZERO : fraction.stripTrailingZeros();
+ }
+
+ /**
+ * Converts a fraction from 0 to 1 for this field to a value.
+ * <p>
+ * The fractional value must be between 0 (inclusive) and 1 (exclusive).
+ * It can only be returned if the {@link java.time.temporal.TemporalField#range() value range} is fixed.
+ * The value is obtained by calculation from the field range and a rounding
+ * mode of {@link RoundingMode#FLOOR FLOOR}.
+ * The calculation is inaccurate if the values do not run continuously from smallest to largest.
+ * <p>
+ * For example, the fractional second-of-minute of 0.25 would be converted to 15,
+ * assuming the standard definition of 60 seconds in a minute.
+ *
+ * @param fraction the fraction to convert, not null
+ * @return the value of the field, valid for this rule
+ * @throws DateTimeException if the value cannot be converted
+ */
+ private long convertFromFraction(BigDecimal fraction) {
+ ValueRange range = field.range();
+ BigDecimal minBD = BigDecimal.valueOf(range.getMinimum());
+ BigDecimal rangeBD = BigDecimal.valueOf(range.getMaximum()).subtract(minBD).add(BigDecimal.ONE);
+ BigDecimal valueBD = fraction.multiply(rangeBD).setScale(0, RoundingMode.FLOOR).add(minBD);
+ return valueBD.longValueExact();
+ }
+
+ @Override
+ public String toString() {
+ String decimal = (decimalPoint ? ",DecimalPoint" : "");
+ return "Fraction(" + field + "," + minWidth + "," + maxWidth + decimal + ")";
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Prints or parses field text.
+ */
+ static final class TextPrinterParser implements DateTimePrinterParser {
+ private final TemporalField field;
+ private final TextStyle textStyle;
+ private final DateTimeTextProvider provider;
+ /**
+ * The cached number printer parser.
+ * Immutable and volatile, so no synchronization needed.
+ */
+ private volatile NumberPrinterParser numberPrinterParser;
+
+ /**
+ * Constructor.
+ *
+ * @param field the field to output, not null
+ * @param textStyle the text style, not null
+ * @param provider the text provider, not null
+ */
+ TextPrinterParser(TemporalField field, TextStyle textStyle, DateTimeTextProvider provider) {
+ // validated by caller
+ this.field = field;
+ this.textStyle = textStyle;
+ this.provider = provider;
+ }
+
+ @Override
+ public boolean format(DateTimePrintContext context, StringBuilder buf) {
+ Long value = context.getValue(field);
+ if (value == null) {
+ return false;
+ }
+ String text;
+ Chronology chrono = context.getTemporal().query(TemporalQueries.chronology());
+ if (chrono == null || chrono == IsoChronology.INSTANCE) {
+ text = provider.getText(field, value, textStyle, context.getLocale());
+ } else {
+ text = provider.getText(chrono, field, value, textStyle, context.getLocale());
+ }
+ if (text == null) {
+ return numberPrinterParser().format(context, buf);
+ }
+ buf.append(text);
+ return true;
+ }
+
+ @Override
+ public int parse(DateTimeParseContext context, CharSequence parseText, int position) {
+ int length = parseText.length();
+ if (position < 0 || position > length) {
+ throw new IndexOutOfBoundsException();
+ }
+ TextStyle style = (context.isStrict() ? textStyle : null);
+ Chronology chrono = context.getEffectiveChronology();
+ Iterator<Entry<String, Long>> it;
+ if (chrono == null || chrono == IsoChronology.INSTANCE) {
+ it = provider.getTextIterator(field, style, context.getLocale());
+ } else {
+ it = provider.getTextIterator(chrono, field, style, context.getLocale());
+ }
+ if (it != null) {
+ while (it.hasNext()) {
+ Entry<String, Long> entry = it.next();
+ String itText = entry.getKey();
+ if (context.subSequenceEquals(itText, 0, parseText, position, itText.length())) {
+ return context.setParsedField(field, entry.getValue(), position, position + itText.length());
+ }
+ }
+ if (context.isStrict()) {
+ return ~position;
+ }
+ }
+ return numberPrinterParser().parse(context, parseText, position);
+ }
+
+ /**
+ * Create and cache a number printer parser.
+ * @return the number printer parser for this field, not null
+ */
+ private NumberPrinterParser numberPrinterParser() {
+ if (numberPrinterParser == null) {
+ numberPrinterParser = new NumberPrinterParser(field, 1, 19, SignStyle.NORMAL);
+ }
+ return numberPrinterParser;
+ }
+
+ @Override
+ public String toString() {
+ if (textStyle == TextStyle.FULL) {
+ return "Text(" + field + ")";
+ }
+ return "Text(" + field + "," + textStyle + ")";
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Prints or parses an ISO-8601 instant.
+ */
+ static final class InstantPrinterParser implements DateTimePrinterParser {
+ // days in a 400 year cycle = 146097
+ // days in a 10,000 year cycle = 146097 * 25
+ // seconds per day = 86400
+ private static final long SECONDS_PER_10000_YEARS = 146097L * 25L * 86400L;
+ private static final long SECONDS_0000_TO_1970 = ((146097L * 5L) - (30L * 365L + 7L)) * 86400L;
+ private final int fractionalDigits;
+
+ InstantPrinterParser(int fractionalDigits) {
+ this.fractionalDigits = fractionalDigits;
+ }
+
+ @Override
+ public boolean format(DateTimePrintContext context, StringBuilder buf) {
+ // use INSTANT_SECONDS, thus this code is not bound by Instant.MAX
+ Long inSecs = context.getValue(INSTANT_SECONDS);
+ Long inNanos = null;
+ if (context.getTemporal().isSupported(NANO_OF_SECOND)) {
+ inNanos = context.getTemporal().getLong(NANO_OF_SECOND);
+ }
+ if (inSecs == null) {
+ return false;
+ }
+ long inSec = inSecs;
+ int inNano = NANO_OF_SECOND.checkValidIntValue(inNanos != null ? inNanos : 0);
+ // format mostly using LocalDateTime.toString
+ if (inSec >= -SECONDS_0000_TO_1970) {
+ // current era
+ long zeroSecs = inSec - SECONDS_PER_10000_YEARS + SECONDS_0000_TO_1970;
+ long hi = Math.floorDiv(zeroSecs, SECONDS_PER_10000_YEARS) + 1;
+ long lo = Math.floorMod(zeroSecs, SECONDS_PER_10000_YEARS);
+ LocalDateTime ldt = LocalDateTime.ofEpochSecond(lo - SECONDS_0000_TO_1970, 0, ZoneOffset.UTC);
+ if (hi > 0) {
+ buf.append('+').append(hi);
+ }
+ buf.append(ldt);
+ if (ldt.getSecond() == 0) {
+ buf.append(":00");
+ }
+ } else {
+ // before current era
+ long zeroSecs = inSec + SECONDS_0000_TO_1970;
+ long hi = zeroSecs / SECONDS_PER_10000_YEARS;
+ long lo = zeroSecs % SECONDS_PER_10000_YEARS;
+ LocalDateTime ldt = LocalDateTime.ofEpochSecond(lo - SECONDS_0000_TO_1970, 0, ZoneOffset.UTC);
+ int pos = buf.length();
+ buf.append(ldt);
+ if (ldt.getSecond() == 0) {
+ buf.append(":00");
+ }
+ if (hi < 0) {
+ if (ldt.getYear() == -10_000) {
+ buf.replace(pos, pos + 2, Long.toString(hi - 1));
+ } else if (lo == 0) {
+ buf.insert(pos, hi);
+ } else {
+ buf.insert(pos + 1, Math.abs(hi));
+ }
+ }
+ }
+ // add fraction
+ if ((fractionalDigits < 0 && inNano > 0) || fractionalDigits > 0) {
+ buf.append('.');
+ int div = 100_000_000;
+ for (int i = 0; ((fractionalDigits == -1 && inNano > 0) ||
+ (fractionalDigits == -2 && (inNano > 0 || (i % 3) != 0)) ||
+ i < fractionalDigits); i++) {
+ int digit = inNano / div;
+ buf.append((char) (digit + '0'));
+ inNano = inNano - (digit * div);
+ div = div / 10;
+ }
+ }
+ buf.append('Z');
+ return true;
+ }
+
+ @Override
+ public int parse(DateTimeParseContext context, CharSequence text, int position) {
+ // new context to avoid overwriting fields like year/month/day
+ int minDigits = (fractionalDigits < 0 ? 0 : fractionalDigits);
+ int maxDigits = (fractionalDigits < 0 ? 9 : fractionalDigits);
+ CompositePrinterParser parser = new DateTimeFormatterBuilder()
+ .append(DateTimeFormatter.ISO_LOCAL_DATE).appendLiteral('T')
+ .appendValue(HOUR_OF_DAY, 2).appendLiteral(':')
+ .appendValue(MINUTE_OF_HOUR, 2).appendLiteral(':')
+ .appendValue(SECOND_OF_MINUTE, 2)
+ .appendFraction(NANO_OF_SECOND, minDigits, maxDigits, true)
+ .appendLiteral('Z')
+ .toFormatter().toPrinterParser(false);
+ DateTimeParseContext newContext = context.copy();
+ int pos = parser.parse(newContext, text, position);
+ if (pos < 0) {
+ return pos;
+ }
+ // parser restricts most fields to 2 digits, so definitely int
+ // correctly parsed nano is also guaranteed to be valid
+ long yearParsed = newContext.getParsed(YEAR);
+ int month = newContext.getParsed(MONTH_OF_YEAR).intValue();
+ int day = newContext.getParsed(DAY_OF_MONTH).intValue();
+ int hour = newContext.getParsed(HOUR_OF_DAY).intValue();
+ int min = newContext.getParsed(MINUTE_OF_HOUR).intValue();
+ Long secVal = newContext.getParsed(SECOND_OF_MINUTE);
+ Long nanoVal = newContext.getParsed(NANO_OF_SECOND);
+ int sec = (secVal != null ? secVal.intValue() : 0);
+ int nano = (nanoVal != null ? nanoVal.intValue() : 0);
+ int days = 0;
+ if (hour == 24 && min == 0 && sec == 0 && nano == 0) {
+ hour = 0;
+ days = 1;
+ } else if (hour == 23 && min == 59 && sec == 60) {
+ context.setParsedLeapSecond();
+ sec = 59;
+ }
+ int year = (int) yearParsed % 10_000;
+ long instantSecs;
+ try {
+ LocalDateTime ldt = LocalDateTime.of(year, month, day, hour, min, sec, 0).plusDays(days);
+ instantSecs = ldt.toEpochSecond(ZoneOffset.UTC);
+ instantSecs += Math.multiplyExact(yearParsed / 10_000L, SECONDS_PER_10000_YEARS);
+ } catch (RuntimeException ex) {
+ return ~position;
+ }
+ int successPos = pos;
+ successPos = context.setParsedField(INSTANT_SECONDS, instantSecs, position, successPos);
+ return context.setParsedField(NANO_OF_SECOND, nano, position, successPos);
+ }
+
+ @Override
+ public String toString() {
+ return "Instant()";
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Prints or parses an offset ID.
+ */
+ static final class OffsetIdPrinterParser implements DateTimePrinterParser {
+ static final String[] PATTERNS = new String[] {
+ "+HH", "+HHmm", "+HH:mm", "+HHMM", "+HH:MM", "+HHMMss", "+HH:MM:ss", "+HHMMSS", "+HH:MM:SS",
+ }; // order used in pattern builder
+ static final OffsetIdPrinterParser INSTANCE_ID_Z = new OffsetIdPrinterParser("+HH:MM:ss", "Z");
+ static final OffsetIdPrinterParser INSTANCE_ID_ZERO = new OffsetIdPrinterParser("+HH:MM:ss", "0");
+
+ private final String noOffsetText;
+ private final int type;
+
+ /**
+ * Constructor.
+ *
+ * @param pattern the pattern
+ * @param noOffsetText the text to use for UTC, not null
+ */
+ OffsetIdPrinterParser(String pattern, String noOffsetText) {
+ Objects.requireNonNull(pattern, "pattern");
+ Objects.requireNonNull(noOffsetText, "noOffsetText");
+ this.type = checkPattern(pattern);
+ this.noOffsetText = noOffsetText;
+ }
+
+ private int checkPattern(String pattern) {
+ for (int i = 0; i < PATTERNS.length; i++) {
+ if (PATTERNS[i].equals(pattern)) {
+ return i;
+ }
+ }
+ throw new IllegalArgumentException("Invalid zone offset pattern: " + pattern);
+ }
+
+ @Override
+ public boolean format(DateTimePrintContext context, StringBuilder buf) {
+ Long offsetSecs = context.getValue(OFFSET_SECONDS);
+ if (offsetSecs == null) {
+ return false;
+ }
+ int totalSecs = Math.toIntExact(offsetSecs);
+ if (totalSecs == 0) {
+ buf.append(noOffsetText);
+ } else {
+ int absHours = Math.abs((totalSecs / 3600) % 100); // anything larger than 99 silently dropped
+ int absMinutes = Math.abs((totalSecs / 60) % 60);
+ int absSeconds = Math.abs(totalSecs % 60);
+ int bufPos = buf.length();
+ int output = absHours;
+ buf.append(totalSecs < 0 ? "-" : "+")
+ .append((char) (absHours / 10 + '0')).append((char) (absHours % 10 + '0'));
+ if (type >= 3 || (type >= 1 && absMinutes > 0)) {
+ buf.append((type % 2) == 0 ? ":" : "")
+ .append((char) (absMinutes / 10 + '0')).append((char) (absMinutes % 10 + '0'));
+ output += absMinutes;
+ if (type >= 7 || (type >= 5 && absSeconds > 0)) {
+ buf.append((type % 2) == 0 ? ":" : "")
+ .append((char) (absSeconds / 10 + '0')).append((char) (absSeconds % 10 + '0'));
+ output += absSeconds;
+ }
+ }
+ if (output == 0) {
+ buf.setLength(bufPos);
+ buf.append(noOffsetText);
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public int parse(DateTimeParseContext context, CharSequence text, int position) {
+ int length = text.length();
+ int noOffsetLen = noOffsetText.length();
+ if (noOffsetLen == 0) {
+ if (position == length) {
+ return context.setParsedField(OFFSET_SECONDS, 0, position, position);
+ }
+ } else {
+ if (position == length) {
+ return ~position;
+ }
+ if (context.subSequenceEquals(text, position, noOffsetText, 0, noOffsetLen)) {
+ return context.setParsedField(OFFSET_SECONDS, 0, position, position + noOffsetLen);
+ }
+ }
+
+ // parse normal plus/minus offset
+ char sign = text.charAt(position); // IOOBE if invalid position
+ if (sign == '+' || sign == '-') {
+ // starts
+ int negative = (sign == '-' ? -1 : 1);
+ int[] array = new int[4];
+ array[0] = position + 1;
+ if ((parseNumber(array, 1, text, true) ||
+ parseNumber(array, 2, text, type >=3) ||
+ parseNumber(array, 3, text, false)) == false) {
+ // success
+ long offsetSecs = negative * (array[1] * 3600L + array[2] * 60L + array[3]);
+ return context.setParsedField(OFFSET_SECONDS, offsetSecs, position, array[0]);
+ }
+ }
+ // handle special case of empty no offset text
+ if (noOffsetLen == 0) {
+ return context.setParsedField(OFFSET_SECONDS, 0, position, position + noOffsetLen);
+ }
+ return ~position;
+ }
+
+ /**
+ * Parse a two digit zero-prefixed number.
+ *
+ * @param array the array of parsed data, 0=pos,1=hours,2=mins,3=secs, not null
+ * @param arrayIndex the index to parse the value into
+ * @param parseText the offset ID, not null
+ * @param required whether this number is required
+ * @return true if an error occurred
+ */
+ private boolean parseNumber(int[] array, int arrayIndex, CharSequence parseText, boolean required) {
+ if ((type + 3) / 2 < arrayIndex) {
+ return false; // ignore seconds/minutes
+ }
+ int pos = array[0];
+ if ((type % 2) == 0 && arrayIndex > 1) {
+ if (pos + 1 > parseText.length() || parseText.charAt(pos) != ':') {
+ return required;
+ }
+ pos++;
+ }
+ if (pos + 2 > parseText.length()) {
+ return required;
+ }
+ char ch1 = parseText.charAt(pos++);
+ char ch2 = parseText.charAt(pos++);
+ if (ch1 < '0' || ch1 > '9' || ch2 < '0' || ch2 > '9') {
+ return required;
+ }
+ int value = (ch1 - 48) * 10 + (ch2 - 48);
+ if (value < 0 || value > 59) {
+ return required;
+ }
+ array[arrayIndex] = value;
+ array[0] = pos;
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ String converted = noOffsetText.replace("'", "''");
+ return "Offset(" + PATTERNS[type] + ",'" + converted + "')";
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Prints or parses an offset ID.
+ */
+ static final class LocalizedOffsetIdPrinterParser implements DateTimePrinterParser {
+ private final TextStyle style;
+
+ /**
+ * Constructor.
+ *
+ * @param style the style, not null
+ */
+ LocalizedOffsetIdPrinterParser(TextStyle style) {
+ this.style = style;
+ }
+
+ private static StringBuilder appendHMS(StringBuilder buf, int t) {
+ return buf.append((char)(t / 10 + '0'))
+ .append((char)(t % 10 + '0'));
+ }
+
+ @Override
+ public boolean format(DateTimePrintContext context, StringBuilder buf) {
+ Long offsetSecs = context.getValue(OFFSET_SECONDS);
+ if (offsetSecs == null) {
+ return false;
+ }
+ String gmtText = "GMT"; // TODO: get localized version of 'GMT'
+ if (gmtText != null) {
+ buf.append(gmtText);
+ }
+ int totalSecs = Math.toIntExact(offsetSecs);
+ if (totalSecs != 0) {
+ int absHours = Math.abs((totalSecs / 3600) % 100); // anything larger than 99 silently dropped
+ int absMinutes = Math.abs((totalSecs / 60) % 60);
+ int absSeconds = Math.abs(totalSecs % 60);
+ buf.append(totalSecs < 0 ? "-" : "+");
+ if (style == TextStyle.FULL) {
+ appendHMS(buf, absHours);
+ buf.append(':');
+ appendHMS(buf, absMinutes);
+ if (absSeconds != 0) {
+ buf.append(':');
+ appendHMS(buf, absSeconds);
+ }
+ } else {
+ if (absHours >= 10) {
+ buf.append((char)(absHours / 10 + '0'));
+ }
+ buf.append((char)(absHours % 10 + '0'));
+ if (absMinutes != 0 || absSeconds != 0) {
+ buf.append(':');
+ appendHMS(buf, absMinutes);
+ if (absSeconds != 0) {
+ buf.append(':');
+ appendHMS(buf, absSeconds);
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ int getDigit(CharSequence text, int position) {
+ char c = text.charAt(position);
+ if (c < '0' || c > '9') {
+ return -1;
+ }
+ return c - '0';
+ }
+
+ @Override
+ public int parse(DateTimeParseContext context, CharSequence text, int position) {
+ int pos = position;
+ int end = pos + text.length();
+ String gmtText = "GMT"; // TODO: get localized version of 'GMT'
+ if (gmtText != null) {
+ if (!context.subSequenceEquals(text, pos, gmtText, 0, gmtText.length())) {
+ return ~position;
+ }
+ pos += gmtText.length();
+ }
+ // parse normal plus/minus offset
+ int negative = 0;
+ if (pos == end) {
+ return context.setParsedField(OFFSET_SECONDS, 0, position, pos);
+ }
+ char sign = text.charAt(pos); // IOOBE if invalid position
+ if (sign == '+') {
+ negative = 1;
+ } else if (sign == '-') {
+ negative = -1;
+ } else {
+ return context.setParsedField(OFFSET_SECONDS, 0, position, pos);
+ }
+ pos++;
+ int h = 0;
+ int m = 0;
+ int s = 0;
+ if (style == TextStyle.FULL) {
+ int h1 = getDigit(text, pos++);
+ int h2 = getDigit(text, pos++);
+ if (h1 < 0 || h2 < 0 || text.charAt(pos++) != ':') {
+ return ~position;
+ }
+ h = h1 * 10 + h2;
+ int m1 = getDigit(text, pos++);
+ int m2 = getDigit(text, pos++);
+ if (m1 < 0 || m2 < 0) {
+ return ~position;
+ }
+ m = m1 * 10 + m2;
+ if (pos + 2 < end && text.charAt(pos) == ':') {
+ int s1 = getDigit(text, pos + 1);
+ int s2 = getDigit(text, pos + 2);
+ if (s1 >= 0 && s2 >= 0) {
+ s = s1 * 10 + s2;
+ pos += 3;
+ }
+ }
+ } else {
+ h = getDigit(text, pos++);
+ if (h < 0) {
+ return ~position;
+ }
+ if (pos < end) {
+ int h2 = getDigit(text, pos);
+ if (h2 >=0) {
+ h = h * 10 + h2;
+ pos++;
+ }
+ if (pos + 2 < end && text.charAt(pos) == ':') {
+ if (pos + 2 < end && text.charAt(pos) == ':') {
+ int m1 = getDigit(text, pos + 1);
+ int m2 = getDigit(text, pos + 2);
+ if (m1 >= 0 && m2 >= 0) {
+ m = m1 * 10 + m2;
+ pos += 3;
+ if (pos + 2 < end && text.charAt(pos) == ':') {
+ int s1 = getDigit(text, pos + 1);
+ int s2 = getDigit(text, pos + 2);
+ if (s1 >= 0 && s2 >= 0) {
+ s = s1 * 10 + s2;
+ pos += 3;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ long offsetSecs = negative * (h * 3600L + m * 60L + s);
+ return context.setParsedField(OFFSET_SECONDS, offsetSecs, position, pos);
+ }
+
+ @Override
+ public String toString() {
+ return "LocalizedOffset(" + style + ")";
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Prints or parses a zone ID.
+ */
+ static final class ZoneTextPrinterParser extends ZoneIdPrinterParser {
+
+ /** The text style to output. */
+ private final TextStyle textStyle;
+
+ /** The preferred zoneid map */
+ private Set<String> preferredZones;
+
+ ZoneTextPrinterParser(TextStyle textStyle, Set<ZoneId> preferredZones) {
+ super(TemporalQueries.zone(), "ZoneText(" + textStyle + ")");
+ this.textStyle = Objects.requireNonNull(textStyle, "textStyle");
+ if (preferredZones != null && preferredZones.size() != 0) {
+ this.preferredZones = new HashSet<>();
+ for (ZoneId id : preferredZones) {
+ this.preferredZones.add(id.getId());
+ }
+ }
+ }
+
+ private static final int STD = 0;
+ private static final int DST = 1;
+ private static final int GENERIC = 2;
+
+ // BEGIN Android-added: Lists of types used by getDisplayName().
+ private static final TimeZoneNames.NameType[] TYPES = new TimeZoneNames.NameType[] {
+ TimeZoneNames.NameType.LONG_STANDARD,
+ TimeZoneNames.NameType.SHORT_STANDARD,
+ TimeZoneNames.NameType.LONG_DAYLIGHT,
+ TimeZoneNames.NameType.SHORT_DAYLIGHT,
+ TimeZoneNames.NameType.LONG_GENERIC,
+ TimeZoneNames.NameType.SHORT_GENERIC,
+ };
+
+ private static final TimeZoneNames.NameType[] FULL_TYPES = new TimeZoneNames.NameType[] {
+ TimeZoneNames.NameType.LONG_STANDARD,
+ TimeZoneNames.NameType.LONG_DAYLIGHT,
+ TimeZoneNames.NameType.LONG_GENERIC,
+ };
+
+ private static final TimeZoneNames.NameType[] SHORT_TYPES = new TimeZoneNames.NameType[] {
+ TimeZoneNames.NameType.SHORT_STANDARD,
+ TimeZoneNames.NameType.SHORT_DAYLIGHT,
+ TimeZoneNames.NameType.SHORT_GENERIC,
+ };
+ // END Android-added: Lists of types used by getDisplayName().
+
+ private static final Map<String, SoftReference<Map<Locale, String[]>>> cache =
+ new ConcurrentHashMap<>();
+
+ private String getDisplayName(String id, int type, Locale locale) {
+ if (textStyle == TextStyle.NARROW) {
+ return null;
+ }
+ String[] names;
+ SoftReference<Map<Locale, String[]>> ref = cache.get(id);
+ Map<Locale, String[]> perLocale = null;
+ if (ref == null || (perLocale = ref.get()) == null ||
+ (names = perLocale.get(locale)) == null) {
+ // BEGIN Android-changed: use ICU TimeZoneNames instead of TimeZoneNameUtility.
+ /*
+ names = TimeZoneNameUtility.retrieveDisplayNames(id, locale);
+ if (names == null) {
+ return null;
+ }
+ names = Arrays.copyOfRange(names, 0, 7);
+ names[5] =
+ TimeZoneNameUtility.retrieveGenericDisplayName(id, TimeZone.LONG, locale);
+ if (names[5] == null) {
+ names[5] = names[0]; // use the id
+ }
+ names[6] =
+ TimeZoneNameUtility.retrieveGenericDisplayName(id, TimeZone.SHORT, locale);
+ */
+ TimeZoneNames timeZoneNames = TimeZoneNames.getInstance(locale);
+ names = new String[TYPES.length + 1];
+ // Zeroth index used for id, other indexes based on NameType constant + 1.
+ names[0] = id;
+ String canonicalId = ZoneMeta.getCanonicalCLDRID(id);
+ timeZoneNames.getDisplayNames(canonicalId, TYPES, System.currentTimeMillis(),
+ /* dest */ names, /* destoffset */ 1);
+ if (names == null) {
+ return null;
+ }
+ if (names[1] == null || names[2] == null || names[3] == null || names[4] == null) {
+ // Use "GMT+XX:XX" analogous to java.util.TimeZone.getDisplayName()
+ TimeZone tz = TimeZone.getTimeZone(id);
+ String stdString = TimeZone.createGmtOffsetString(
+ /* includeGmt */ true, /* includeMinuteSeparator */ true,
+ tz.getRawOffset());
+ String dstString = TimeZone.createGmtOffsetString(
+ /* includeGmt */ true, /* includeMinuteSeparator */ true,
+ tz.getRawOffset() + tz.getDSTSavings());
+ names[1] = names[1] != null ? names[1] : stdString;
+ names[2] = names[2] != null ? names[2] : stdString;
+ names[3] = names[3] != null ? names[3] : dstString;
+ names[4] = names[4] != null ? names[4] : dstString;
+ }
+ if (names[5] == null) {
+ names[5] = names[0]; // use the id
+ }
+ // END Android-changed: use ICU TimeZoneNames instead of TimeZoneNameUtility.
+ if (names[6] == null) {
+ names[6] = names[0];
+ }
+ if (perLocale == null) {
+ perLocale = new ConcurrentHashMap<>();
+ }
+ perLocale.put(locale, names);
+ cache.put(id, new SoftReference<>(perLocale));
+ }
+ switch (type) {
+ case STD:
+ return names[textStyle.zoneNameStyleIndex() + 1];
+ case DST:
+ return names[textStyle.zoneNameStyleIndex() + 3];
+ }
+ return names[textStyle.zoneNameStyleIndex() + 5];
+ }
+
+ @Override
+ public boolean format(DateTimePrintContext context, StringBuilder buf) {
+ ZoneId zone = context.getValue(TemporalQueries.zoneId());
+ if (zone == null) {
+ return false;
+ }
+ String zname = zone.getId();
+ if (!(zone instanceof ZoneOffset)) {
+ TemporalAccessor dt = context.getTemporal();
+ String name = getDisplayName(zname,
+ dt.isSupported(ChronoField.INSTANT_SECONDS)
+ ? (zone.getRules().isDaylightSavings(Instant.from(dt)) ? DST : STD)
+ : GENERIC,
+ context.getLocale());
+ if (name != null) {
+ zname = name;
+ }
+ }
+ buf.append(zname);
+ return true;
+ }
+
+ // cache per instance for now
+ private final Map<Locale, Entry<Integer, SoftReference<PrefixTree>>>
+ cachedTree = new HashMap<>();
+ private final Map<Locale, Entry<Integer, SoftReference<PrefixTree>>>
+ cachedTreeCI = new HashMap<>();
+
+ @Override
+ protected PrefixTree getTree(DateTimeParseContext context) {
+ if (textStyle == TextStyle.NARROW) {
+ return super.getTree(context);
+ }
+ Locale locale = context.getLocale();
+ boolean isCaseSensitive = context.isCaseSensitive();
+ Set<String> regionIds = ZoneRulesProvider.getAvailableZoneIds();
+ int regionIdsSize = regionIds.size();
+
+ Map<Locale, Entry<Integer, SoftReference<PrefixTree>>> cached =
+ isCaseSensitive ? cachedTree : cachedTreeCI;
+
+ Entry<Integer, SoftReference<PrefixTree>> entry = null;
+ PrefixTree tree = null;
+ String[][] zoneStrings = null;
+ if ((entry = cached.get(locale)) == null ||
+ (entry.getKey() != regionIdsSize ||
+ (tree = entry.getValue().get()) == null)) {
+ tree = PrefixTree.newTree(context);
+ // BEGIN Android-changed: use ICU TimeZoneNames to get Zone names.
+ /*
+ zoneStrings = TimeZoneNameUtility.getZoneStrings(locale);
+ for (String[] names : zoneStrings) {
+ String zid = names[0];
+ if (!regionIds.contains(zid)) {
+ continue;
+ }
+ tree.add(zid, zid); // don't convert zid -> metazone
+ zid = ZoneName.toZid(zid, locale);
+ int i = textStyle == TextStyle.FULL ? 1 : 2;
+ for (; i < names.length; i += 2) {
+ tree.add(names[i], zid);
+ }
+ }
+ // if we have a set of preferred zones, need a copy and
+ // add the preferred zones again to overwrite
+ if (preferredZones != null) {
+ for (String[] names : zoneStrings) {
+ String zid = names[0];
+ if (!preferredZones.contains(zid) || !regionIds.contains(zid)) {
+ continue;
+ }
+ int i = textStyle == TextStyle.FULL ? 1 : 2;
+ for (; i < names.length; i += 2) {
+ tree.add(names[i], zid);
+ }
+ }
+ }
+ */
+ TimeZoneNames timeZoneNames = TimeZoneNames.getInstance(locale);
+ long now = System.currentTimeMillis();
+ TimeZoneNames.NameType[] types =
+ textStyle == TextStyle.FULL ? FULL_TYPES : SHORT_TYPES;
+ String[] names = new String[types.length];
+ for (String zid : regionIds) {
+ tree.add(zid, zid); // don't convert zid -> metazone
+ zid = ZoneName.toZid(zid, locale);
+ timeZoneNames.getDisplayNames(zid, types, now, names, 0);
+ for (int i = 0; i < names.length; i++) {
+ if (names[i] != null) {
+ tree.add(names[i], zid);
+ }
+ }
+ }
+ // if we have a set of preferred zones, need a copy and
+ // add the preferred zones again to overwrite
+ if (preferredZones != null) {
+ for (String zid : regionIds) {
+ if (!preferredZones.contains(zid)) {
+ continue;
+ }
+ String canonicalId = ZoneName.toZid(zid, locale);
+ timeZoneNames.getDisplayNames(canonicalId, types, now, names, 0);
+ for (int i = 0; i < names.length; i++) {
+ if (names[i] != null) {
+ tree.add(names[i], zid);
+ }
+ }
+ }
+ }
+ // END Android-changed: use ICU TimeZoneNames to get Zone names.
+ cached.put(locale, new SimpleImmutableEntry<>(regionIdsSize, new SoftReference<>(tree)));
+ }
+ return tree;
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Prints or parses a zone ID.
+ */
+ static class ZoneIdPrinterParser implements DateTimePrinterParser {
+ private final TemporalQuery<ZoneId> query;
+ private final String description;
+
+ ZoneIdPrinterParser(TemporalQuery<ZoneId> query, String description) {
+ this.query = query;
+ this.description = description;
+ }
+
+ @Override
+ public boolean format(DateTimePrintContext context, StringBuilder buf) {
+ ZoneId zone = context.getValue(query);
+ if (zone == null) {
+ return false;
+ }
+ buf.append(zone.getId());
+ return true;
+ }
+
+ /**
+ * The cached tree to speed up parsing.
+ */
+ private static volatile Entry<Integer, PrefixTree> cachedPrefixTree;
+ private static volatile Entry<Integer, PrefixTree> cachedPrefixTreeCI;
+
+ protected PrefixTree getTree(DateTimeParseContext context) {
+ // prepare parse tree
+ Set<String> regionIds = ZoneRulesProvider.getAvailableZoneIds();
+ final int regionIdsSize = regionIds.size();
+ Entry<Integer, PrefixTree> cached = context.isCaseSensitive()
+ ? cachedPrefixTree : cachedPrefixTreeCI;
+ if (cached == null || cached.getKey() != regionIdsSize) {
+ synchronized (this) {
+ cached = context.isCaseSensitive() ? cachedPrefixTree : cachedPrefixTreeCI;
+ if (cached == null || cached.getKey() != regionIdsSize) {
+ cached = new SimpleImmutableEntry<>(regionIdsSize, PrefixTree.newTree(regionIds, context));
+ if (context.isCaseSensitive()) {
+ cachedPrefixTree = cached;
+ } else {
+ cachedPrefixTreeCI = cached;
+ }
+ }
+ }
+ }
+ return cached.getValue();
+ }
+
+ /**
+ * This implementation looks for the longest matching string.
+ * For example, parsing Etc/GMT-2 will return Etc/GMC-2 rather than just
+ * Etc/GMC although both are valid.
+ */
+ @Override
+ public int parse(DateTimeParseContext context, CharSequence text, int position) {
+ int length = text.length();
+ if (position > length) {
+ throw new IndexOutOfBoundsException();
+ }
+ if (position == length) {
+ return ~position;
+ }
+
+ // handle fixed time-zone IDs
+ char nextChar = text.charAt(position);
+ if (nextChar == '+' || nextChar == '-') {
+ return parseOffsetBased(context, text, position, position, OffsetIdPrinterParser.INSTANCE_ID_Z);
+ } else if (length >= position + 2) {
+ char nextNextChar = text.charAt(position + 1);
+ if (context.charEquals(nextChar, 'U') && context.charEquals(nextNextChar, 'T')) {
+ if (length >= position + 3 && context.charEquals(text.charAt(position + 2), 'C')) {
+ return parseOffsetBased(context, text, position, position + 3, OffsetIdPrinterParser.INSTANCE_ID_ZERO);
+ }
+ return parseOffsetBased(context, text, position, position + 2, OffsetIdPrinterParser.INSTANCE_ID_ZERO);
+ } else if (context.charEquals(nextChar, 'G') && length >= position + 3 &&
+ context.charEquals(nextNextChar, 'M') && context.charEquals(text.charAt(position + 2), 'T')) {
+ return parseOffsetBased(context, text, position, position + 3, OffsetIdPrinterParser.INSTANCE_ID_ZERO);
+ }
+ }
+
+ // parse
+ PrefixTree tree = getTree(context);
+ ParsePosition ppos = new ParsePosition(position);
+ String parsedZoneId = tree.match(text, ppos);
+ if (parsedZoneId == null) {
+ if (context.charEquals(nextChar, 'Z')) {
+ context.setParsed(ZoneOffset.UTC);
+ return position + 1;
+ }
+ return ~position;
+ }
+ context.setParsed(ZoneId.of(parsedZoneId));
+ return ppos.getIndex();
+ }
+
+ /**
+ * Parse an offset following a prefix and set the ZoneId if it is valid.
+ * To matching the parsing of ZoneId.of the values are not normalized
+ * to ZoneOffsets.
+ *
+ * @param context the parse context
+ * @param text the input text
+ * @param prefixPos start of the prefix
+ * @param position start of text after the prefix
+ * @param parser parser for the value after the prefix
+ * @return the position after the parse
+ */
+ private int parseOffsetBased(DateTimeParseContext context, CharSequence text, int prefixPos, int position, OffsetIdPrinterParser parser) {
+ String prefix = text.toString().substring(prefixPos, position).toUpperCase();
+ if (position >= text.length()) {
+ context.setParsed(ZoneId.of(prefix));
+ return position;
+ }
+
+ // Android-added: "GMT0" is considered a valid ZoneId.
+ if (text.charAt(position) == '0' && prefix.equals("GMT")) {
+ context.setParsed(ZoneId.of("GMT0"));
+ return position + 1;
+ }
+
+ // '0' or 'Z' after prefix is not part of a valid ZoneId; use bare prefix
+ if (text.charAt(position) == '0' ||
+ context.charEquals(text.charAt(position), 'Z')) {
+ context.setParsed(ZoneId.of(prefix));
+ return position;
+ }
+
+ DateTimeParseContext newContext = context.copy();
+ int endPos = parser.parse(newContext, text, position);
+ try {
+ if (endPos < 0) {
+ if (parser == OffsetIdPrinterParser.INSTANCE_ID_Z) {
+ return ~prefixPos;
+ }
+ context.setParsed(ZoneId.of(prefix));
+ return position;
+ }
+ int offset = (int) newContext.getParsed(OFFSET_SECONDS).longValue();
+ ZoneOffset zoneOffset = ZoneOffset.ofTotalSeconds(offset);
+ context.setParsed(ZoneId.ofOffset(prefix, zoneOffset));
+ return endPos;
+ } catch (DateTimeException dte) {
+ return ~prefixPos;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return description;
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * A String based prefix tree for parsing time-zone names.
+ */
+ static class PrefixTree {
+ protected String key;
+ protected String value;
+ protected char c0; // performance optimization to avoid the
+ // boundary check cost of key.charat(0)
+ protected PrefixTree child;
+ protected PrefixTree sibling;
+
+ private PrefixTree(String k, String v, PrefixTree child) {
+ this.key = k;
+ this.value = v;
+ this.child = child;
+ if (k.length() == 0){
+ c0 = 0xffff;
+ } else {
+ c0 = key.charAt(0);
+ }
+ }
+
+ /**
+ * Creates a new prefix parsing tree based on parse context.
+ *
+ * @param context the parse context
+ * @return the tree, not null
+ */
+ public static PrefixTree newTree(DateTimeParseContext context) {
+ //if (!context.isStrict()) {
+ // return new LENIENT("", null, null);
+ //}
+ if (context.isCaseSensitive()) {
+ return new PrefixTree("", null, null);
+ }
+ return new CI("", null, null);
+ }
+
+ /**
+ * Creates a new prefix parsing tree.
+ *
+ * @param keys a set of strings to build the prefix parsing tree, not null
+ * @param context the parse context
+ * @return the tree, not null
+ */
+ public static PrefixTree newTree(Set<String> keys, DateTimeParseContext context) {
+ PrefixTree tree = newTree(context);
+ for (String k : keys) {
+ tree.add0(k, k);
+ }
+ return tree;
+ }
+
+ /**
+ * Clone a copy of this tree
+ */
+ public PrefixTree copyTree() {
+ PrefixTree copy = new PrefixTree(key, value, null);
+ if (child != null) {
+ copy.child = child.copyTree();
+ }
+ if (sibling != null) {
+ copy.sibling = sibling.copyTree();
+ }
+ return copy;
+ }
+
+
+ /**
+ * Adds a pair of {key, value} into the prefix tree.
+ *
+ * @param k the key, not null
+ * @param v the value, not null
+ * @return true if the pair is added successfully
+ */
+ public boolean add(String k, String v) {
+ return add0(k, v);
+ }
+
+ private boolean add0(String k, String v) {
+ k = toKey(k);
+ int prefixLen = prefixLength(k);
+ if (prefixLen == key.length()) {
+ if (prefixLen < k.length()) { // down the tree
+ String subKey = k.substring(prefixLen);
+ PrefixTree c = child;
+ while (c != null) {
+ if (isEqual(c.c0, subKey.charAt(0))) {
+ return c.add0(subKey, v);
+ }
+ c = c.sibling;
+ }
+ // add the node as the child of the current node
+ c = newNode(subKey, v, null);
+ c.sibling = child;
+ child = c;
+ return true;
+ }
+ // have an existing <key, value> already, overwrite it
+ // if (value != null) {
+ // return false;
+ //}
+ value = v;
+ return true;
+ }
+ // split the existing node
+ PrefixTree n1 = newNode(key.substring(prefixLen), value, child);
+ key = k.substring(0, prefixLen);
+ child = n1;
+ if (prefixLen < k.length()) {
+ PrefixTree n2 = newNode(k.substring(prefixLen), v, null);
+ child.sibling = n2;
+ value = null;
+ } else {
+ value = v;
+ }
+ return true;
+ }
+
+ /**
+ * Match text with the prefix tree.
+ *
+ * @param text the input text to parse, not null
+ * @param off the offset position to start parsing at
+ * @param end the end position to stop parsing
+ * @return the resulting string, or null if no match found.
+ */
+ public String match(CharSequence text, int off, int end) {
+ if (!prefixOf(text, off, end)){
+ return null;
+ }
+ if (child != null && (off += key.length()) != end) {
+ PrefixTree c = child;
+ do {
+ if (isEqual(c.c0, text.charAt(off))) {
+ String found = c.match(text, off, end);
+ if (found != null) {
+ return found;
+ }
+ return value;
+ }
+ c = c.sibling;
+ } while (c != null);
+ }
+ return value;
+ }
+
+ /**
+ * Match text with the prefix tree.
+ *
+ * @param text the input text to parse, not null
+ * @param pos the position to start parsing at, from 0 to the text
+ * length. Upon return, position will be updated to the new parse
+ * position, or unchanged, if no match found.
+ * @return the resulting string, or null if no match found.
+ */
+ public String match(CharSequence text, ParsePosition pos) {
+ int off = pos.getIndex();
+ int end = text.length();
+ if (!prefixOf(text, off, end)){
+ return null;
+ }
+ off += key.length();
+ if (child != null && off != end) {
+ PrefixTree c = child;
+ do {
+ if (isEqual(c.c0, text.charAt(off))) {
+ pos.setIndex(off);
+ String found = c.match(text, pos);
+ if (found != null) {
+ return found;
+ }
+ break;
+ }
+ c = c.sibling;
+ } while (c != null);
+ }
+ pos.setIndex(off);
+ return value;
+ }
+
+ protected String toKey(String k) {
+ return k;
+ }
+
+ protected PrefixTree newNode(String k, String v, PrefixTree child) {
+ return new PrefixTree(k, v, child);
+ }
+
+ protected boolean isEqual(char c1, char c2) {
+ return c1 == c2;
+ }
+
+ protected boolean prefixOf(CharSequence text, int off, int end) {
+ if (text instanceof String) {
+ return ((String)text).startsWith(key, off);
+ }
+ int len = key.length();
+ if (len > end - off) {
+ return false;
+ }
+ int off0 = 0;
+ while (len-- > 0) {
+ if (!isEqual(key.charAt(off0++), text.charAt(off++))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private int prefixLength(String k) {
+ int off = 0;
+ while (off < k.length() && off < key.length()) {
+ if (!isEqual(k.charAt(off), key.charAt(off))) {
+ return off;
+ }
+ off++;
+ }
+ return off;
+ }
+
+ /**
+ * Case Insensitive prefix tree.
+ */
+ private static class CI extends PrefixTree {
+
+ private CI(String k, String v, PrefixTree child) {
+ super(k, v, child);
+ }
+
+ @Override
+ protected CI newNode(String k, String v, PrefixTree child) {
+ return new CI(k, v, child);
+ }
+
+ @Override
+ protected boolean isEqual(char c1, char c2) {
+ return DateTimeParseContext.charEqualsIgnoreCase(c1, c2);
+ }
+
+ @Override
+ protected boolean prefixOf(CharSequence text, int off, int end) {
+ int len = key.length();
+ if (len > end - off) {
+ return false;
+ }
+ int off0 = 0;
+ while (len-- > 0) {
+ if (!isEqual(key.charAt(off0++), text.charAt(off++))) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ /**
+ * Lenient prefix tree. Case insensitive and ignores characters
+ * like space, underscore and slash.
+ */
+ private static class LENIENT extends CI {
+
+ private LENIENT(String k, String v, PrefixTree child) {
+ super(k, v, child);
+ }
+
+ @Override
+ protected CI newNode(String k, String v, PrefixTree child) {
+ return new LENIENT(k, v, child);
+ }
+
+ private boolean isLenientChar(char c) {
+ return c == ' ' || c == '_' || c == '/';
+ }
+
+ protected String toKey(String k) {
+ for (int i = 0; i < k.length(); i++) {
+ if (isLenientChar(k.charAt(i))) {
+ StringBuilder sb = new StringBuilder(k.length());
+ sb.append(k, 0, i);
+ i++;
+ while (i < k.length()) {
+ if (!isLenientChar(k.charAt(i))) {
+ sb.append(k.charAt(i));
+ }
+ i++;
+ }
+ return sb.toString();
+ }
+ }
+ return k;
+ }
+
+ @Override
+ public String match(CharSequence text, ParsePosition pos) {
+ int off = pos.getIndex();
+ int end = text.length();
+ int len = key.length();
+ int koff = 0;
+ while (koff < len && off < end) {
+ if (isLenientChar(text.charAt(off))) {
+ off++;
+ continue;
+ }
+ if (!isEqual(key.charAt(koff++), text.charAt(off++))) {
+ return null;
+ }
+ }
+ if (koff != len) {
+ return null;
+ }
+ if (child != null && off != end) {
+ int off0 = off;
+ while (off0 < end && isLenientChar(text.charAt(off0))) {
+ off0++;
+ }
+ if (off0 < end) {
+ PrefixTree c = child;
+ do {
+ if (isEqual(c.c0, text.charAt(off0))) {
+ pos.setIndex(off0);
+ String found = c.match(text, pos);
+ if (found != null) {
+ return found;
+ }
+ break;
+ }
+ c = c.sibling;
+ } while (c != null);
+ }
+ }
+ pos.setIndex(off);
+ return value;
+ }
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Prints or parses a chronology.
+ */
+ static final class ChronoPrinterParser implements DateTimePrinterParser {
+ /** The text style to output, null means the ID. */
+ private final TextStyle textStyle;
+
+ ChronoPrinterParser(TextStyle textStyle) {
+ // validated by caller
+ this.textStyle = textStyle;
+ }
+
+ @Override
+ public boolean format(DateTimePrintContext context, StringBuilder buf) {
+ Chronology chrono = context.getValue(TemporalQueries.chronology());
+ if (chrono == null) {
+ return false;
+ }
+ if (textStyle == null) {
+ buf.append(chrono.getId());
+ } else {
+ buf.append(getChronologyName(chrono, context.getLocale()));
+ }
+ return true;
+ }
+
+ @Override
+ public int parse(DateTimeParseContext context, CharSequence text, int position) {
+ // simple looping parser to find the chronology
+ if (position < 0 || position > text.length()) {
+ throw new IndexOutOfBoundsException();
+ }
+ Set<Chronology> chronos = Chronology.getAvailableChronologies();
+ Chronology bestMatch = null;
+ int matchLen = -1;
+ for (Chronology chrono : chronos) {
+ String name;
+ if (textStyle == null) {
+ name = chrono.getId();
+ } else {
+ name = getChronologyName(chrono, context.getLocale());
+ }
+ int nameLen = name.length();
+ if (nameLen > matchLen && context.subSequenceEquals(text, position, name, 0, nameLen)) {
+ bestMatch = chrono;
+ matchLen = nameLen;
+ }
+ }
+ if (bestMatch == null) {
+ return ~position;
+ }
+ context.setParsed(bestMatch);
+ return position + matchLen;
+ }
+
+ /**
+ * Returns the chronology name of the given chrono in the given locale
+ * if available, or the chronology Id otherwise. The regular ResourceBundle
+ * search path is used for looking up the chronology name.
+ *
+ * @param chrono the chronology, not null
+ * @param locale the locale, not null
+ * @return the chronology name of chrono in locale, or the id if no name is available
+ * @throws NullPointerException if chrono or locale is null
+ */
+ private String getChronologyName(Chronology chrono, Locale locale) {
+ // Android-changed: Use ICU LocaleDisplayNames. http://b/28832222
+ // String key = "calendarname." + chrono.getCalendarType();
+ // String name = DateTimeTextProvider.getLocalizedResource(key, locale);
+ LocaleDisplayNames displayNames = LocaleDisplayNames.getInstance(ULocale.forLocale(locale));
+ String name = displayNames.keyValueDisplayName("calendar", chrono.getCalendarType());
+ return name != null ? name : chrono.getId();
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Prints or parses a localized pattern.
+ */
+ static final class LocalizedPrinterParser implements DateTimePrinterParser {
+ /** Cache of formatters. */
+ private static final ConcurrentMap<String, DateTimeFormatter> FORMATTER_CACHE = new ConcurrentHashMap<>(16, 0.75f, 2);
+
+ private final FormatStyle dateStyle;
+ private final FormatStyle timeStyle;
+
+ /**
+ * Constructor.
+ *
+ * @param dateStyle the date style to use, may be null
+ * @param timeStyle the time style to use, may be null
+ */
+ LocalizedPrinterParser(FormatStyle dateStyle, FormatStyle timeStyle) {
+ // validated by caller
+ this.dateStyle = dateStyle;
+ this.timeStyle = timeStyle;
+ }
+
+ @Override
+ public boolean format(DateTimePrintContext context, StringBuilder buf) {
+ Chronology chrono = Chronology.from(context.getTemporal());
+ return formatter(context.getLocale(), chrono).toPrinterParser(false).format(context, buf);
+ }
+
+ @Override
+ public int parse(DateTimeParseContext context, CharSequence text, int position) {
+ Chronology chrono = context.getEffectiveChronology();
+ return formatter(context.getLocale(), chrono).toPrinterParser(false).parse(context, text, position);
+ }
+
+ /**
+ * Gets the formatter to use.
+ * <p>
+ * The formatter will be the most appropriate to use for the date and time style in the locale.
+ * For example, some locales will use the month name while others will use the number.
+ *
+ * @param locale the locale to use, not null
+ * @param chrono the chronology to use, not null
+ * @return the formatter, not null
+ * @throws IllegalArgumentException if the formatter cannot be found
+ */
+ private DateTimeFormatter formatter(Locale locale, Chronology chrono) {
+ String key = chrono.getId() + '|' + locale.toString() + '|' + dateStyle + timeStyle;
+ DateTimeFormatter formatter = FORMATTER_CACHE.get(key);
+ if (formatter == null) {
+ String pattern = getLocalizedDateTimePattern(dateStyle, timeStyle, chrono, locale);
+ formatter = new DateTimeFormatterBuilder().appendPattern(pattern).toFormatter(locale);
+ DateTimeFormatter old = FORMATTER_CACHE.putIfAbsent(key, formatter);
+ if (old != null) {
+ formatter = old;
+ }
+ }
+ return formatter;
+ }
+
+ @Override
+ public String toString() {
+ return "Localized(" + (dateStyle != null ? dateStyle : "") + "," +
+ (timeStyle != null ? timeStyle : "") + ")";
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Prints or parses a localized pattern from a localized field.
+ * The specific formatter and parameters is not selected until the
+ * the field is to be printed or parsed.
+ * The locale is needed to select the proper WeekFields from which
+ * the field for day-of-week, week-of-month, or week-of-year is selected.
+ */
+ static final class WeekBasedFieldPrinterParser implements DateTimePrinterParser {
+ private char chr;
+ private int count;
+
+ /**
+ * Constructor.
+ *
+ * @param chr the pattern format letter that added this PrinterParser.
+ * @param count the repeat count of the format letter
+ */
+ WeekBasedFieldPrinterParser(char chr, int count) {
+ this.chr = chr;
+ this.count = count;
+ }
+
+ @Override
+ public boolean format(DateTimePrintContext context, StringBuilder buf) {
+ return printerParser(context.getLocale()).format(context, buf);
+ }
+
+ @Override
+ public int parse(DateTimeParseContext context, CharSequence text, int position) {
+ return printerParser(context.getLocale()).parse(context, text, position);
+ }
+
+ /**
+ * Gets the printerParser to use based on the field and the locale.
+ *
+ * @param locale the locale to use, not null
+ * @return the formatter, not null
+ * @throws IllegalArgumentException if the formatter cannot be found
+ */
+ private DateTimePrinterParser printerParser(Locale locale) {
+ WeekFields weekDef = WeekFields.of(locale);
+ TemporalField field = null;
+ switch (chr) {
+ case 'Y':
+ field = weekDef.weekBasedYear();
+ if (count == 2) {
+ return new ReducedPrinterParser(field, 2, 2, 0, ReducedPrinterParser.BASE_DATE, 0);
+ } else {
+ return new NumberPrinterParser(field, count, 19,
+ (count < 4) ? SignStyle.NORMAL : SignStyle.EXCEEDS_PAD, -1);
+ }
+ case 'e':
+ case 'c':
+ field = weekDef.dayOfWeek();
+ break;
+ case 'w':
+ field = weekDef.weekOfWeekBasedYear();
+ break;
+ case 'W':
+ field = weekDef.weekOfMonth();
+ break;
+ default:
+ throw new IllegalStateException("unreachable");
+ }
+ return new NumberPrinterParser(field, (count == 2 ? 2 : 1), 2, SignStyle.NOT_NEGATIVE);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(30);
+ sb.append("Localized(");
+ if (chr == 'Y') {
+ if (count == 1) {
+ sb.append("WeekBasedYear");
+ } else if (count == 2) {
+ sb.append("ReducedValue(WeekBasedYear,2,2,2000-01-01)");
+ } else {
+ sb.append("WeekBasedYear,").append(count).append(",")
+ .append(19).append(",")
+ .append((count < 4) ? SignStyle.NORMAL : SignStyle.EXCEEDS_PAD);
+ }
+ } else {
+ switch (chr) {
+ case 'c':
+ case 'e':
+ sb.append("DayOfWeek");
+ break;
+ case 'w':
+ sb.append("WeekOfWeekBasedYear");
+ break;
+ case 'W':
+ sb.append("WeekOfMonth");
+ break;
+ default:
+ break;
+ }
+ sb.append(",");
+ sb.append(count);
+ }
+ sb.append(")");
+ return sb.toString();
+ }
+ }
+
+ //-------------------------------------------------------------------------
+ /**
+ * Length comparator.
+ */
+ static final Comparator<String> LENGTH_SORT = new Comparator<String>() {
+ @Override
+ public int compare(String str1, String str2) {
+ return str1.length() == str2.length() ? str1.compareTo(str2) : str1.length() - str2.length();
+ }
+ };
+}
diff --git a/java/time/format/DateTimeParseContext.java b/java/time/format/DateTimeParseContext.java
new file mode 100644
index 0000000..e3ca5e9
--- /dev/null
+++ b/java/time/format/DateTimeParseContext.java
@@ -0,0 +1,438 @@
+/*
+ * Copyright (c) 2012, 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) 2008-2012, 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.format;
+
+import java.time.ZoneId;
+import java.time.chrono.Chronology;
+import java.time.chrono.IsoChronology;
+import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalField;
+import java.util.ArrayList;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Consumer;
+
+/**
+ * Context object used during date and time parsing.
+ * <p>
+ * This class represents the current state of the parse.
+ * It has the ability to store and retrieve the parsed values and manage optional segments.
+ * It also provides key information to the parsing methods.
+ * <p>
+ * Once parsing is complete, the {@link #toUnresolved()} is used to obtain the unresolved
+ * result data. The {@link #toResolved()} is used to obtain the resolved result.
+ *
+ * @implSpec
+ * This class is a mutable context intended for use from a single thread.
+ * Usage of the class is thread-safe within standard parsing as a new instance of this class
+ * is automatically created for each parse and parsing is single-threaded
+ *
+ * @since 1.8
+ */
+final class DateTimeParseContext {
+
+ /**
+ * The formatter, not null.
+ */
+ private DateTimeFormatter formatter;
+ /**
+ * Whether to parse using case sensitively.
+ */
+ private boolean caseSensitive = true;
+ /**
+ * Whether to parse using strict rules.
+ */
+ private boolean strict = true;
+ /**
+ * The list of parsed data.
+ */
+ private final ArrayList<Parsed> parsed = new ArrayList<>();
+ /**
+ * List of Consumers<Chronology> to be notified if the Chronology changes.
+ */
+ private ArrayList<Consumer<Chronology>> chronoListeners = null;
+
+ /**
+ * Creates a new instance of the context.
+ *
+ * @param formatter the formatter controlling the parse, not null
+ */
+ DateTimeParseContext(DateTimeFormatter formatter) {
+ super();
+ this.formatter = formatter;
+ parsed.add(new Parsed());
+ }
+
+ /**
+ * Creates a copy of this context.
+ * This retains the case sensitive and strict flags.
+ */
+ DateTimeParseContext copy() {
+ DateTimeParseContext newContext = new DateTimeParseContext(formatter);
+ newContext.caseSensitive = caseSensitive;
+ newContext.strict = strict;
+ return newContext;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the locale.
+ * <p>
+ * This locale is used to control localization in the parse except
+ * where localization is controlled by the DecimalStyle.
+ *
+ * @return the locale, not null
+ */
+ Locale getLocale() {
+ return formatter.getLocale();
+ }
+
+ /**
+ * Gets the DecimalStyle.
+ * <p>
+ * The DecimalStyle controls the numeric parsing.
+ *
+ * @return the DecimalStyle, not null
+ */
+ DecimalStyle getDecimalStyle() {
+ return formatter.getDecimalStyle();
+ }
+
+ /**
+ * Gets the effective chronology during parsing.
+ *
+ * @return the effective parsing chronology, not null
+ */
+ Chronology getEffectiveChronology() {
+ Chronology chrono = currentParsed().chrono;
+ if (chrono == null) {
+ chrono = formatter.getChronology();
+ if (chrono == null) {
+ chrono = IsoChronology.INSTANCE;
+ }
+ }
+ return chrono;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if parsing is case sensitive.
+ *
+ * @return true if parsing is case sensitive, false if case insensitive
+ */
+ boolean isCaseSensitive() {
+ return caseSensitive;
+ }
+
+ /**
+ * Sets whether the parsing is case sensitive or not.
+ *
+ * @param caseSensitive changes the parsing to be case sensitive or not from now on
+ */
+ void setCaseSensitive(boolean caseSensitive) {
+ this.caseSensitive = caseSensitive;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Helper to compare two {@code CharSequence} instances.
+ * This uses {@link #isCaseSensitive()}.
+ *
+ * @param cs1 the first character sequence, not null
+ * @param offset1 the offset into the first sequence, valid
+ * @param cs2 the second character sequence, not null
+ * @param offset2 the offset into the second sequence, valid
+ * @param length the length to check, valid
+ * @return true if equal
+ */
+ boolean subSequenceEquals(CharSequence cs1, int offset1, CharSequence cs2, int offset2, int length) {
+ if (offset1 + length > cs1.length() || offset2 + length > cs2.length()) {
+ return false;
+ }
+ if (isCaseSensitive()) {
+ for (int i = 0; i < length; i++) {
+ char ch1 = cs1.charAt(offset1 + i);
+ char ch2 = cs2.charAt(offset2 + i);
+ if (ch1 != ch2) {
+ return false;
+ }
+ }
+ } else {
+ for (int i = 0; i < length; i++) {
+ char ch1 = cs1.charAt(offset1 + i);
+ char ch2 = cs2.charAt(offset2 + i);
+ if (ch1 != ch2 && Character.toUpperCase(ch1) != Character.toUpperCase(ch2) &&
+ Character.toLowerCase(ch1) != Character.toLowerCase(ch2)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Helper to compare two {@code char}.
+ * This uses {@link #isCaseSensitive()}.
+ *
+ * @param ch1 the first character
+ * @param ch2 the second character
+ * @return true if equal
+ */
+ boolean charEquals(char ch1, char ch2) {
+ if (isCaseSensitive()) {
+ return ch1 == ch2;
+ }
+ return charEqualsIgnoreCase(ch1, ch2);
+ }
+
+ /**
+ * Compares two characters ignoring case.
+ *
+ * @param c1 the first
+ * @param c2 the second
+ * @return true if equal
+ */
+ static boolean charEqualsIgnoreCase(char c1, char c2) {
+ return c1 == c2 ||
+ Character.toUpperCase(c1) == Character.toUpperCase(c2) ||
+ Character.toLowerCase(c1) == Character.toLowerCase(c2);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if parsing is strict.
+ * <p>
+ * Strict parsing requires exact matching of the text and sign styles.
+ *
+ * @return true if parsing is strict, false if lenient
+ */
+ boolean isStrict() {
+ return strict;
+ }
+
+ /**
+ * Sets whether parsing is strict or lenient.
+ *
+ * @param strict changes the parsing to be strict or lenient from now on
+ */
+ void setStrict(boolean strict) {
+ this.strict = strict;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Starts the parsing of an optional segment of the input.
+ */
+ void startOptional() {
+ parsed.add(currentParsed().copy());
+ }
+
+ /**
+ * Ends the parsing of an optional segment of the input.
+ *
+ * @param successful whether the optional segment was successfully parsed
+ */
+ void endOptional(boolean successful) {
+ if (successful) {
+ parsed.remove(parsed.size() - 2);
+ } else {
+ parsed.remove(parsed.size() - 1);
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the currently active temporal objects.
+ *
+ * @return the current temporal objects, not null
+ */
+ private Parsed currentParsed() {
+ return parsed.get(parsed.size() - 1);
+ }
+
+ /**
+ * Gets the unresolved result of the parse.
+ *
+ * @return the result of the parse, not null
+ */
+ Parsed toUnresolved() {
+ return currentParsed();
+ }
+
+ /**
+ * Gets the resolved result of the parse.
+ *
+ * @return the result of the parse, not null
+ */
+ TemporalAccessor toResolved(ResolverStyle resolverStyle, Set<TemporalField> resolverFields) {
+ Parsed parsed = currentParsed();
+ parsed.chrono = getEffectiveChronology();
+ parsed.zone = (parsed.zone != null ? parsed.zone : formatter.getZone());
+ return parsed.resolve(resolverStyle, resolverFields);
+ }
+
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the first value that was parsed for the specified field.
+ * <p>
+ * This searches the results of the parse, returning the first value found
+ * for the specified field. No attempt is made to derive a value.
+ * The field may have an out of range value.
+ * For example, the day-of-month might be set to 50, or the hour to 1000.
+ *
+ * @param field the field to query from the map, null returns null
+ * @return the value mapped to the specified field, null if field was not parsed
+ */
+ Long getParsed(TemporalField field) {
+ return currentParsed().fieldValues.get(field);
+ }
+
+ /**
+ * Stores the parsed field.
+ * <p>
+ * This stores a field-value pair that has been parsed.
+ * The value stored may be out of range for the field - no checks are performed.
+ *
+ * @param field the field to set in the field-value map, not null
+ * @param value the value to set in the field-value map
+ * @param errorPos the position of the field being parsed
+ * @param successPos the position after the field being parsed
+ * @return the new position
+ */
+ int setParsedField(TemporalField field, long value, int errorPos, int successPos) {
+ Objects.requireNonNull(field, "field");
+ Long old = currentParsed().fieldValues.put(field, value);
+ return (old != null && old.longValue() != value) ? ~errorPos : successPos;
+ }
+
+ /**
+ * Stores the parsed chronology.
+ * <p>
+ * This stores the chronology that has been parsed.
+ * No validation is performed other than ensuring it is not null.
+ * <p>
+ * The list of listeners is copied and cleared so that each
+ * listener is called only once. A listener can add itself again
+ * if it needs to be notified of future changes.
+ *
+ * @param chrono the parsed chronology, not null
+ */
+ void setParsed(Chronology chrono) {
+ Objects.requireNonNull(chrono, "chrono");
+ currentParsed().chrono = chrono;
+ if (chronoListeners != null && !chronoListeners.isEmpty()) {
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ Consumer<Chronology>[] tmp = new Consumer[1];
+ Consumer<Chronology>[] listeners = chronoListeners.toArray(tmp);
+ chronoListeners.clear();
+ for (Consumer<Chronology> l : listeners) {
+ l.accept(chrono);
+ }
+ }
+ }
+
+ /**
+ * Adds a Consumer<Chronology> to the list of listeners to be notified
+ * if the Chronology changes.
+ * @param listener a Consumer<Chronology> to be called when Chronology changes
+ */
+ void addChronoChangedListener(Consumer<Chronology> listener) {
+ if (chronoListeners == null) {
+ chronoListeners = new ArrayList<Consumer<Chronology>>();
+ }
+ chronoListeners.add(listener);
+ }
+
+ /**
+ * Stores the parsed zone.
+ * <p>
+ * This stores the zone that has been parsed.
+ * No validation is performed other than ensuring it is not null.
+ *
+ * @param zone the parsed zone, not null
+ */
+ void setParsed(ZoneId zone) {
+ Objects.requireNonNull(zone, "zone");
+ currentParsed().zone = zone;
+ }
+
+ /**
+ * Stores the parsed leap second.
+ */
+ void setParsedLeapSecond() {
+ currentParsed().leapSecond = true;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a string version of the context for debugging.
+ *
+ * @return a string representation of the context data, not null
+ */
+ @Override
+ public String toString() {
+ return currentParsed().toString();
+ }
+
+}
diff --git a/java/time/format/DateTimeParseException.java b/java/time/format/DateTimeParseException.java
new file mode 100644
index 0000000..d02ed15
--- /dev/null
+++ b/java/time/format/DateTimeParseException.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2012, 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) 2008-2012, 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.format;
+
+import java.time.DateTimeException;
+
+/**
+ * An exception thrown when an error occurs during parsing.
+ * <p>
+ * This exception includes the text being parsed and the error index.
+ *
+ * @implSpec
+ * This class is intended for use in a single thread.
+ *
+ * @since 1.8
+ */
+public class DateTimeParseException extends DateTimeException {
+
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = 4304633501674722597L;
+
+ /**
+ * The text that was being parsed.
+ */
+ private final String parsedString;
+ /**
+ * The error index in the text.
+ */
+ private final int errorIndex;
+
+ /**
+ * Constructs a new exception with the specified message.
+ *
+ * @param message the message to use for this exception, may be null
+ * @param parsedData the parsed text, should not be null
+ * @param errorIndex the index in the parsed string that was invalid, should be a valid index
+ */
+ public DateTimeParseException(String message, CharSequence parsedData, int errorIndex) {
+ super(message);
+ this.parsedString = parsedData.toString();
+ this.errorIndex = errorIndex;
+ }
+
+ /**
+ * Constructs a new exception with the specified message and cause.
+ *
+ * @param message the message to use for this exception, may be null
+ * @param parsedData the parsed text, should not be null
+ * @param errorIndex the index in the parsed string that was invalid, should be a valid index
+ * @param cause the cause exception, may be null
+ */
+ public DateTimeParseException(String message, CharSequence parsedData, int errorIndex, Throwable cause) {
+ super(message, cause);
+ this.parsedString = parsedData.toString();
+ this.errorIndex = errorIndex;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns the string that was being parsed.
+ *
+ * @return the string that was being parsed, should not be null.
+ */
+ public String getParsedString() {
+ return parsedString;
+ }
+
+ /**
+ * Returns the index where the error was found.
+ *
+ * @return the index in the parsed string that was invalid, should be a valid index
+ */
+ public int getErrorIndex() {
+ return errorIndex;
+ }
+
+}
diff --git a/java/time/format/DateTimePrintContext.java b/java/time/format/DateTimePrintContext.java
new file mode 100644
index 0000000..543a317
--- /dev/null
+++ b/java/time/format/DateTimePrintContext.java
@@ -0,0 +1,318 @@
+/*
+ * Copyright (c) 2012, 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) 2011-2012, 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.format;
+
+import static java.time.temporal.ChronoField.EPOCH_DAY;
+import static java.time.temporal.ChronoField.INSTANT_SECONDS;
+import static java.time.temporal.ChronoField.OFFSET_SECONDS;
+
+import java.time.DateTimeException;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.chrono.ChronoLocalDate;
+import java.time.chrono.Chronology;
+import java.time.chrono.IsoChronology;
+import java.time.temporal.ChronoField;
+import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalField;
+import java.time.temporal.TemporalQueries;
+import java.time.temporal.TemporalQuery;
+import java.time.temporal.ValueRange;
+import java.util.Locale;
+import java.util.Objects;
+
+/**
+ * Context object used during date and time printing.
+ * <p>
+ * This class provides a single wrapper to items used in the format.
+ *
+ * @implSpec
+ * This class is a mutable context intended for use from a single thread.
+ * Usage of the class is thread-safe within standard printing as the framework creates
+ * a new instance of the class for each format and printing is single-threaded.
+ *
+ * @since 1.8
+ */
+final class DateTimePrintContext {
+
+ /**
+ * The temporal being output.
+ */
+ private TemporalAccessor temporal;
+ /**
+ * The formatter, not null.
+ */
+ private DateTimeFormatter formatter;
+ /**
+ * Whether the current formatter is optional.
+ */
+ private int optional;
+
+ /**
+ * Creates a new instance of the context.
+ *
+ * @param temporal the temporal object being output, not null
+ * @param formatter the formatter controlling the format, not null
+ */
+ DateTimePrintContext(TemporalAccessor temporal, DateTimeFormatter formatter) {
+ super();
+ this.temporal = adjust(temporal, formatter);
+ this.formatter = formatter;
+ }
+
+ private static TemporalAccessor adjust(final TemporalAccessor temporal, DateTimeFormatter formatter) {
+ // normal case first (early return is an optimization)
+ Chronology overrideChrono = formatter.getChronology();
+ ZoneId overrideZone = formatter.getZone();
+ if (overrideChrono == null && overrideZone == null) {
+ return temporal;
+ }
+
+ // ensure minimal change (early return is an optimization)
+ Chronology temporalChrono = temporal.query(TemporalQueries.chronology());
+ ZoneId temporalZone = temporal.query(TemporalQueries.zoneId());
+ if (Objects.equals(overrideChrono, temporalChrono)) {
+ overrideChrono = null;
+ }
+ if (Objects.equals(overrideZone, temporalZone)) {
+ overrideZone = null;
+ }
+ if (overrideChrono == null && overrideZone == null) {
+ return temporal;
+ }
+
+ // make adjustment
+ final Chronology effectiveChrono = (overrideChrono != null ? overrideChrono : temporalChrono);
+ if (overrideZone != null) {
+ // if have zone and instant, calculation is simple, defaulting chrono if necessary
+ if (temporal.isSupported(INSTANT_SECONDS)) {
+ Chronology chrono = (effectiveChrono != null ? effectiveChrono : IsoChronology.INSTANCE);
+ return chrono.zonedDateTime(Instant.from(temporal), overrideZone);
+ }
+ // block changing zone on OffsetTime, and similar problem cases
+ if (overrideZone.normalized() instanceof ZoneOffset && temporal.isSupported(OFFSET_SECONDS) &&
+ temporal.get(OFFSET_SECONDS) != overrideZone.getRules().getOffset(Instant.EPOCH).getTotalSeconds()) {
+ throw new DateTimeException("Unable to apply override zone '" + overrideZone +
+ "' because the temporal object being formatted has a different offset but" +
+ " does not represent an instant: " + temporal);
+ }
+ }
+ final ZoneId effectiveZone = (overrideZone != null ? overrideZone : temporalZone);
+ final ChronoLocalDate effectiveDate;
+ if (overrideChrono != null) {
+ if (temporal.isSupported(EPOCH_DAY)) {
+ effectiveDate = effectiveChrono.date(temporal);
+ } else {
+ // check for date fields other than epoch-day, ignoring case of converting null to ISO
+ if (!(overrideChrono == IsoChronology.INSTANCE && temporalChrono == null)) {
+ for (ChronoField f : ChronoField.values()) {
+ if (f.isDateBased() && temporal.isSupported(f)) {
+ throw new DateTimeException("Unable to apply override chronology '" + overrideChrono +
+ "' because the temporal object being formatted contains date fields but" +
+ " does not represent a whole date: " + temporal);
+ }
+ }
+ }
+ effectiveDate = null;
+ }
+ } else {
+ effectiveDate = null;
+ }
+
+ // combine available data
+ // this is a non-standard temporal that is almost a pure delegate
+ // this better handles map-like underlying temporal instances
+ return new TemporalAccessor() {
+ @Override
+ public boolean isSupported(TemporalField field) {
+ if (effectiveDate != null && field.isDateBased()) {
+ return effectiveDate.isSupported(field);
+ }
+ return temporal.isSupported(field);
+ }
+ @Override
+ public ValueRange range(TemporalField field) {
+ if (effectiveDate != null && field.isDateBased()) {
+ return effectiveDate.range(field);
+ }
+ return temporal.range(field);
+ }
+ @Override
+ public long getLong(TemporalField field) {
+ if (effectiveDate != null && field.isDateBased()) {
+ return effectiveDate.getLong(field);
+ }
+ return temporal.getLong(field);
+ }
+ @SuppressWarnings("unchecked")
+ @Override
+ public <R> R query(TemporalQuery<R> query) {
+ if (query == TemporalQueries.chronology()) {
+ return (R) effectiveChrono;
+ }
+ if (query == TemporalQueries.zoneId()) {
+ return (R) effectiveZone;
+ }
+ if (query == TemporalQueries.precision()) {
+ return temporal.query(query);
+ }
+ return query.queryFrom(this);
+ }
+ };
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the temporal object being output.
+ *
+ * @return the temporal object, not null
+ */
+ TemporalAccessor getTemporal() {
+ return temporal;
+ }
+
+ /**
+ * Gets the locale.
+ * <p>
+ * This locale is used to control localization in the format output except
+ * where localization is controlled by the DecimalStyle.
+ *
+ * @return the locale, not null
+ */
+ Locale getLocale() {
+ return formatter.getLocale();
+ }
+
+ /**
+ * Gets the DecimalStyle.
+ * <p>
+ * The DecimalStyle controls the localization of numeric output.
+ *
+ * @return the DecimalStyle, not null
+ */
+ DecimalStyle getDecimalStyle() {
+ return formatter.getDecimalStyle();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Starts the printing of an optional segment of the input.
+ */
+ void startOptional() {
+ this.optional++;
+ }
+
+ /**
+ * Ends the printing of an optional segment of the input.
+ */
+ void endOptional() {
+ this.optional--;
+ }
+
+ /**
+ * Gets a value using a query.
+ *
+ * @param query the query to use, not null
+ * @return the result, null if not found and optional is true
+ * @throws DateTimeException if the type is not available and the section is not optional
+ */
+ <R> R getValue(TemporalQuery<R> query) {
+ R result = temporal.query(query);
+ if (result == null && optional == 0) {
+ throw new DateTimeException("Unable to extract value: " + temporal.getClass());
+ }
+ return result;
+ }
+
+ /**
+ * Gets the value of the specified field.
+ * <p>
+ * This will return the value for the specified field.
+ *
+ * @param field the field to find, not null
+ * @return the value, null if not found and optional is true
+ * @throws DateTimeException if the field is not available and the section is not optional
+ */
+ Long getValue(TemporalField field) {
+ try {
+ return temporal.getLong(field);
+ } catch (DateTimeException ex) {
+ if (optional > 0) {
+ return null;
+ }
+ throw ex;
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a string version of the context for debugging.
+ *
+ * @return a string representation of the context, not null
+ */
+ @Override
+ public String toString() {
+ return temporal.toString();
+ }
+
+}
diff --git a/java/time/format/DateTimeTextProvider.java b/java/time/format/DateTimeTextProvider.java
new file mode 100644
index 0000000..260196a
--- /dev/null
+++ b/java/time/format/DateTimeTextProvider.java
@@ -0,0 +1,587 @@
+/*
+ * Copyright (c) 2012, 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) 2011-2012, 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.format;
+
+import android.icu.impl.ICUData;
+import android.icu.impl.ICUResourceBundle;
+import android.icu.util.UResourceBundle;
+
+import static java.time.temporal.ChronoField.AMPM_OF_DAY;
+import static java.time.temporal.ChronoField.DAY_OF_WEEK;
+import static java.time.temporal.ChronoField.ERA;
+import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
+
+import java.time.chrono.Chronology;
+import java.time.chrono.IsoChronology;
+import java.time.chrono.JapaneseChronology;
+import java.time.temporal.ChronoField;
+import java.time.temporal.IsoFields;
+import java.time.temporal.TemporalField;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import sun.util.locale.provider.CalendarDataUtility;
+
+/**
+ * A provider to obtain the textual form of a date-time field.
+ *
+ * @implSpec
+ * Implementations must be thread-safe.
+ * Implementations should cache the textual information.
+ *
+ * @since 1.8
+ */
+class DateTimeTextProvider {
+
+ /** Cache. */
+ private static final ConcurrentMap<Entry<TemporalField, Locale>, Object> CACHE = new ConcurrentHashMap<>(16, 0.75f, 2);
+ /** Comparator. */
+ private static final Comparator<Entry<String, Long>> COMPARATOR = new Comparator<Entry<String, Long>>() {
+ @Override
+ public int compare(Entry<String, Long> obj1, Entry<String, Long> obj2) {
+ return obj2.getKey().length() - obj1.getKey().length(); // longest to shortest
+ }
+ };
+
+ DateTimeTextProvider() {}
+
+ /**
+ * Gets the provider of text.
+ *
+ * @return the provider, not null
+ */
+ static DateTimeTextProvider getInstance() {
+ return new DateTimeTextProvider();
+ }
+
+ /**
+ * Gets the text for the specified field, locale and style
+ * for the purpose of formatting.
+ * <p>
+ * The text associated with the value is returned.
+ * The null return value should be used if there is no applicable text, or
+ * if the text would be a numeric representation of the value.
+ *
+ * @param field the field to get text for, not null
+ * @param value the field value to get text for, not null
+ * @param style the style to get text for, not null
+ * @param locale the locale to get text for, not null
+ * @return the text for the field value, null if no text found
+ */
+ public String getText(TemporalField field, long value, TextStyle style, Locale locale) {
+ Object store = findStore(field, locale);
+ if (store instanceof LocaleStore) {
+ return ((LocaleStore) store).getText(value, style);
+ }
+ return null;
+ }
+
+ /**
+ * Gets the text for the specified chrono, field, locale and style
+ * for the purpose of formatting.
+ * <p>
+ * The text associated with the value is returned.
+ * The null return value should be used if there is no applicable text, or
+ * if the text would be a numeric representation of the value.
+ *
+ * @param chrono the Chronology to get text for, not null
+ * @param field the field to get text for, not null
+ * @param value the field value to get text for, not null
+ * @param style the style to get text for, not null
+ * @param locale the locale to get text for, not null
+ * @return the text for the field value, null if no text found
+ */
+ public String getText(Chronology chrono, TemporalField field, long value,
+ TextStyle style, Locale locale) {
+ if (chrono == IsoChronology.INSTANCE
+ || !(field instanceof ChronoField)) {
+ return getText(field, value, style, locale);
+ }
+
+ int fieldIndex;
+ int fieldValue;
+ if (field == ERA) {
+ fieldIndex = Calendar.ERA;
+ if (chrono == JapaneseChronology.INSTANCE) {
+ if (value == -999) {
+ fieldValue = 0;
+ } else {
+ fieldValue = (int) value + 2;
+ }
+ } else {
+ fieldValue = (int) value;
+ }
+ } else if (field == MONTH_OF_YEAR) {
+ fieldIndex = Calendar.MONTH;
+ fieldValue = (int) value - 1;
+ } else if (field == DAY_OF_WEEK) {
+ fieldIndex = Calendar.DAY_OF_WEEK;
+ fieldValue = (int) value + 1;
+ if (fieldValue > 7) {
+ fieldValue = Calendar.SUNDAY;
+ }
+ } else if (field == AMPM_OF_DAY) {
+ fieldIndex = Calendar.AM_PM;
+ fieldValue = (int) value;
+ } else {
+ return null;
+ }
+ return CalendarDataUtility.retrieveJavaTimeFieldValueName(
+ chrono.getCalendarType(), fieldIndex, fieldValue, style.toCalendarStyle(), locale);
+ }
+
+ /**
+ * Gets an iterator of text to field for the specified field, locale and style
+ * for the purpose of parsing.
+ * <p>
+ * The iterator must be returned in order from the longest text to the shortest.
+ * <p>
+ * The null return value should be used if there is no applicable parsable text, or
+ * if the text would be a numeric representation of the value.
+ * Text can only be parsed if all the values for that field-style-locale combination are unique.
+ *
+ * @param field the field to get text for, not null
+ * @param style the style to get text for, null for all parsable text
+ * @param locale the locale to get text for, not null
+ * @return the iterator of text to field pairs, in order from longest text to shortest text,
+ * null if the field or style is not parsable
+ */
+ public Iterator<Entry<String, Long>> getTextIterator(TemporalField field, TextStyle style, Locale locale) {
+ Object store = findStore(field, locale);
+ if (store instanceof LocaleStore) {
+ return ((LocaleStore) store).getTextIterator(style);
+ }
+ return null;
+ }
+
+ /**
+ * Gets an iterator of text to field for the specified chrono, field, locale and style
+ * for the purpose of parsing.
+ * <p>
+ * The iterator must be returned in order from the longest text to the shortest.
+ * <p>
+ * The null return value should be used if there is no applicable parsable text, or
+ * if the text would be a numeric representation of the value.
+ * Text can only be parsed if all the values for that field-style-locale combination are unique.
+ *
+ * @param chrono the Chronology to get text for, not null
+ * @param field the field to get text for, not null
+ * @param style the style to get text for, null for all parsable text
+ * @param locale the locale to get text for, not null
+ * @return the iterator of text to field pairs, in order from longest text to shortest text,
+ * null if the field or style is not parsable
+ */
+ public Iterator<Entry<String, Long>> getTextIterator(Chronology chrono, TemporalField field,
+ TextStyle style, Locale locale) {
+ if (chrono == IsoChronology.INSTANCE
+ || !(field instanceof ChronoField)) {
+ return getTextIterator(field, style, locale);
+ }
+
+ int fieldIndex;
+ switch ((ChronoField)field) {
+ case ERA:
+ fieldIndex = Calendar.ERA;
+ break;
+ case MONTH_OF_YEAR:
+ fieldIndex = Calendar.MONTH;
+ break;
+ case DAY_OF_WEEK:
+ fieldIndex = Calendar.DAY_OF_WEEK;
+ break;
+ case AMPM_OF_DAY:
+ fieldIndex = Calendar.AM_PM;
+ break;
+ default:
+ return null;
+ }
+
+ int calendarStyle = (style == null) ? Calendar.ALL_STYLES : style.toCalendarStyle();
+ Map<String, Integer> map = CalendarDataUtility.retrieveJavaTimeFieldValueNames(
+ chrono.getCalendarType(), fieldIndex, calendarStyle, locale);
+ if (map == null) {
+ return null;
+ }
+ List<Entry<String, Long>> list = new ArrayList<>(map.size());
+ switch (fieldIndex) {
+ case Calendar.ERA:
+ for (Map.Entry<String, Integer> entry : map.entrySet()) {
+ int era = entry.getValue();
+ if (chrono == JapaneseChronology.INSTANCE) {
+ if (era == 0) {
+ era = -999;
+ } else {
+ era -= 2;
+ }
+ }
+ list.add(createEntry(entry.getKey(), (long)era));
+ }
+ break;
+ case Calendar.MONTH:
+ for (Map.Entry<String, Integer> entry : map.entrySet()) {
+ list.add(createEntry(entry.getKey(), (long)(entry.getValue() + 1)));
+ }
+ break;
+ case Calendar.DAY_OF_WEEK:
+ for (Map.Entry<String, Integer> entry : map.entrySet()) {
+ list.add(createEntry(entry.getKey(), (long)toWeekDay(entry.getValue())));
+ }
+ break;
+ default:
+ for (Map.Entry<String, Integer> entry : map.entrySet()) {
+ list.add(createEntry(entry.getKey(), (long)entry.getValue()));
+ }
+ break;
+ }
+ return list.iterator();
+ }
+
+ private Object findStore(TemporalField field, Locale locale) {
+ Entry<TemporalField, Locale> key = createEntry(field, locale);
+ Object store = CACHE.get(key);
+ if (store == null) {
+ store = createStore(field, locale);
+ CACHE.putIfAbsent(key, store);
+ store = CACHE.get(key);
+ }
+ return store;
+ }
+
+ private static int toWeekDay(int calWeekDay) {
+ if (calWeekDay == Calendar.SUNDAY) {
+ return 7;
+ } else {
+ return calWeekDay - 1;
+ }
+ }
+
+ private Object createStore(TemporalField field, Locale locale) {
+ Map<TextStyle, Map<Long, String>> styleMap = new HashMap<>();
+ if (field == ERA) {
+ for (TextStyle textStyle : TextStyle.values()) {
+ if (textStyle.isStandalone()) {
+ // Stand-alone isn't applicable to era names.
+ continue;
+ }
+ Map<String, Integer> displayNames = CalendarDataUtility.retrieveJavaTimeFieldValueNames(
+ "gregory", Calendar.ERA, textStyle.toCalendarStyle(), locale);
+ if (displayNames != null) {
+ Map<Long, String> map = new HashMap<>();
+ for (Entry<String, Integer> entry : displayNames.entrySet()) {
+ map.put((long) entry.getValue(), entry.getKey());
+ }
+ if (!map.isEmpty()) {
+ styleMap.put(textStyle, map);
+ }
+ }
+ }
+ return new LocaleStore(styleMap);
+ }
+
+ if (field == MONTH_OF_YEAR) {
+ for (TextStyle textStyle : TextStyle.values()) {
+ Map<String, Integer> displayNames = CalendarDataUtility.retrieveJavaTimeFieldValueNames(
+ "gregory", Calendar.MONTH, textStyle.toCalendarStyle(), locale);
+ Map<Long, String> map = new HashMap<>();
+ if (displayNames != null) {
+ for (Entry<String, Integer> entry : displayNames.entrySet()) {
+ map.put((long) (entry.getValue() + 1), entry.getKey());
+ }
+
+ } else {
+ // Narrow names may have duplicated names, such as "J" for January, Jun, July.
+ // Get names one by one in that case.
+ for (int month = Calendar.JANUARY; month <= Calendar.DECEMBER; month++) {
+ String name;
+ name = CalendarDataUtility.retrieveJavaTimeFieldValueName(
+ "gregory", Calendar.MONTH, month, textStyle.toCalendarStyle(), locale);
+ if (name == null) {
+ break;
+ }
+ map.put((long) (month + 1), name);
+ }
+ }
+ if (!map.isEmpty()) {
+ styleMap.put(textStyle, map);
+ }
+ }
+ return new LocaleStore(styleMap);
+ }
+
+ if (field == DAY_OF_WEEK) {
+ for (TextStyle textStyle : TextStyle.values()) {
+ Map<String, Integer> displayNames = CalendarDataUtility.retrieveJavaTimeFieldValueNames(
+ "gregory", Calendar.DAY_OF_WEEK, textStyle.toCalendarStyle(), locale);
+ Map<Long, String> map = new HashMap<>();
+ if (displayNames != null) {
+ for (Entry<String, Integer> entry : displayNames.entrySet()) {
+ map.put((long)toWeekDay(entry.getValue()), entry.getKey());
+ }
+
+ } else {
+ // Narrow names may have duplicated names, such as "S" for Sunday and Saturday.
+ // Get names one by one in that case.
+ for (int wday = Calendar.SUNDAY; wday <= Calendar.SATURDAY; wday++) {
+ String name;
+ name = CalendarDataUtility.retrieveJavaTimeFieldValueName(
+ "gregory", Calendar.DAY_OF_WEEK, wday, textStyle.toCalendarStyle(), locale);
+ if (name == null) {
+ break;
+ }
+ map.put((long)toWeekDay(wday), name);
+ }
+ }
+ if (!map.isEmpty()) {
+ styleMap.put(textStyle, map);
+ }
+ }
+ return new LocaleStore(styleMap);
+ }
+
+ if (field == AMPM_OF_DAY) {
+ for (TextStyle textStyle : TextStyle.values()) {
+ if (textStyle.isStandalone()) {
+ // Stand-alone isn't applicable to AM/PM.
+ continue;
+ }
+ Map<String, Integer> displayNames = CalendarDataUtility.retrieveJavaTimeFieldValueNames(
+ "gregory", Calendar.AM_PM, textStyle.toCalendarStyle(), locale);
+ if (displayNames != null) {
+ Map<Long, String> map = new HashMap<>();
+ for (Entry<String, Integer> entry : displayNames.entrySet()) {
+ map.put((long) entry.getValue(), entry.getKey());
+ }
+ if (!map.isEmpty()) {
+ styleMap.put(textStyle, map);
+ }
+ }
+ }
+ return new LocaleStore(styleMap);
+ }
+
+ if (field == IsoFields.QUARTER_OF_YEAR) {
+ // BEGIN Android-changed: Use ICU resources.
+ /*
+ // The order of keys must correspond to the TextStyle.values() order.
+ final String[] keys = {
+ "QuarterNames",
+ "standalone.QuarterNames",
+ "QuarterAbbreviations",
+ "standalone.QuarterAbbreviations",
+ "QuarterNarrows",
+ "standalone.QuarterNarrows",
+ };
+ for (int i = 0; i < keys.length; i++) {
+ String[] names = getLocalizedResource(keys[i], locale);
+ if (names != null) {
+ Map<Long, String> map = new HashMap<>();
+ for (int q = 0; q < names.length; q++) {
+ map.put((long) (q + 1), names[q]);
+ }
+ styleMap.put(TextStyle.values()[i], map);
+ }
+ }
+ */
+ ICUResourceBundle rb = (ICUResourceBundle) UResourceBundle
+ .getBundleInstance(ICUData.ICU_BASE_NAME, locale);
+ ICUResourceBundle quartersRb = rb.getWithFallback("calendar/gregorian/quarters");
+ ICUResourceBundle formatRb = quartersRb.getWithFallback("format");
+ ICUResourceBundle standaloneRb = quartersRb.getWithFallback("stand-alone");
+ styleMap.put(TextStyle.FULL, extractQuarters(formatRb, "wide"));
+ styleMap.put(TextStyle.FULL_STANDALONE, extractQuarters(standaloneRb, "wide"));
+ styleMap.put(TextStyle.SHORT, extractQuarters(formatRb, "abbreviated"));
+ styleMap.put(TextStyle.SHORT_STANDALONE, extractQuarters(standaloneRb, "abbreviated"));
+ styleMap.put(TextStyle.NARROW, extractQuarters(formatRb, "narrow"));
+ styleMap.put(TextStyle.NARROW_STANDALONE, extractQuarters(standaloneRb, "narrow"));
+ // END Android-changed: Use ICU resources.
+ return new LocaleStore(styleMap);
+ }
+
+ return ""; // null marker for map
+ }
+
+ // BEGIN Android-added: Extracts a Map of quarter names from ICU resource bundle.
+ private static Map<Long, String> extractQuarters(ICUResourceBundle rb, String key) {
+ String[] names = rb.getWithFallback(key).getStringArray();
+ Map<Long, String> map = new HashMap<>();
+ for (int q = 0; q < names.length; q++) {
+ map.put((long) (q + 1), names[q]);
+ }
+ return map;
+ }
+ // END Android-added: Extracts a Map of quarter names from ICU resource bundle.
+
+ /**
+ * Helper method to create an immutable entry.
+ *
+ * @param text the text, not null
+ * @param field the field, not null
+ * @return the entry, not null
+ */
+ private static <A, B> Entry<A, B> createEntry(A text, B field) {
+ return new SimpleImmutableEntry<>(text, field);
+ }
+
+ // BEGIN Android-removed: Android uses ICU resources and has no LocaleProviderAdapter.
+ /**
+ * Returns the localized resource of the given key and locale, or null
+ * if no localized resource is available.
+ *
+ * @param key the key of the localized resource, not null
+ * @param locale the locale, not null
+ * @return the localized resource, or null if not available
+ * @throws NullPointerException if key or locale is null
+ */
+ // @SuppressWarnings("unchecked")
+ // static <T> T getLocalizedResource(String key, Locale locale) {
+ // LocaleResources lr = LocaleProviderAdapter.getResourceBundleBased()
+ // .getLocaleResources(locale);
+ // ResourceBundle rb = lr.getJavaTimeFormatData();
+ // return rb.containsKey(key) ? (T) rb.getObject(key) : null;
+ // }
+ // END Android-removed: Android uses ICU resources and has no LocaleProviderAdapter.
+
+ /**
+ * Stores the text for a single locale.
+ * <p>
+ * Some fields have a textual representation, such as day-of-week or month-of-year.
+ * These textual representations can be captured in this class for printing
+ * and parsing.
+ * <p>
+ * This class is immutable and thread-safe.
+ */
+ static final class LocaleStore {
+ /**
+ * Map of value to text.
+ */
+ private final Map<TextStyle, Map<Long, String>> valueTextMap;
+ /**
+ * Parsable data.
+ */
+ private final Map<TextStyle, List<Entry<String, Long>>> parsable;
+
+ /**
+ * Constructor.
+ *
+ * @param valueTextMap the map of values to text to store, assigned and not altered, not null
+ */
+ LocaleStore(Map<TextStyle, Map<Long, String>> valueTextMap) {
+ this.valueTextMap = valueTextMap;
+ Map<TextStyle, List<Entry<String, Long>>> map = new HashMap<>();
+ List<Entry<String, Long>> allList = new ArrayList<>();
+ for (Map.Entry<TextStyle, Map<Long, String>> vtmEntry : valueTextMap.entrySet()) {
+ Map<String, Entry<String, Long>> reverse = new HashMap<>();
+ for (Map.Entry<Long, String> entry : vtmEntry.getValue().entrySet()) {
+ if (reverse.put(entry.getValue(), createEntry(entry.getValue(), entry.getKey())) != null) {
+ // TODO: BUG: this has no effect
+ continue; // not parsable, try next style
+ }
+ }
+ List<Entry<String, Long>> list = new ArrayList<>(reverse.values());
+ Collections.sort(list, COMPARATOR);
+ map.put(vtmEntry.getKey(), list);
+ allList.addAll(list);
+ map.put(null, allList);
+ }
+ Collections.sort(allList, COMPARATOR);
+ this.parsable = map;
+ }
+
+ /**
+ * Gets the text for the specified field value, locale and style
+ * for the purpose of printing.
+ *
+ * @param value the value to get text for, not null
+ * @param style the style to get text for, not null
+ * @return the text for the field value, null if no text found
+ */
+ String getText(long value, TextStyle style) {
+ Map<Long, String> map = valueTextMap.get(style);
+ return map != null ? map.get(value) : null;
+ }
+
+ /**
+ * Gets an iterator of text to field for the specified style for the purpose of parsing.
+ * <p>
+ * The iterator must be returned in order from the longest text to the shortest.
+ *
+ * @param style the style to get text for, null for all parsable text
+ * @return the iterator of text to field pairs, in order from longest text to shortest text,
+ * null if the style is not parsable
+ */
+ Iterator<Entry<String, Long>> getTextIterator(TextStyle style) {
+ List<Entry<String, Long>> list = parsable.get(style);
+ return list != null ? list.iterator() : null;
+ }
+ }
+}
diff --git a/java/time/format/DecimalStyle.java b/java/time/format/DecimalStyle.java
new file mode 100644
index 0000000..484f2f2
--- /dev/null
+++ b/java/time/format/DecimalStyle.java
@@ -0,0 +1,381 @@
+/*
+ * Copyright (c) 2012, 2015, 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) 2008-2012, 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.format;
+
+import java.text.DecimalFormatSymbols;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * Localized decimal style used in date and time formatting.
+ * <p>
+ * A significant part of dealing with dates and times is the localization.
+ * This class acts as a central point for accessing the information.
+ *
+ * @implSpec
+ * This class is immutable and thread-safe.
+ *
+ * @since 1.8
+ */
+public final class DecimalStyle {
+
+ /**
+ * The standard set of non-localized decimal style symbols.
+ * <p>
+ * This uses standard ASCII characters for zero, positive, negative and a dot for the decimal point.
+ */
+ public static final DecimalStyle STANDARD = new DecimalStyle('0', '+', '-', '.');
+ /**
+ * The cache of DecimalStyle instances.
+ */
+ private static final ConcurrentMap<Locale, DecimalStyle> CACHE = new ConcurrentHashMap<>(16, 0.75f, 2);
+
+ /**
+ * The zero digit.
+ */
+ private final char zeroDigit;
+ /**
+ * The positive sign.
+ */
+ private final char positiveSign;
+ /**
+ * The negative sign.
+ */
+ private final char negativeSign;
+ /**
+ * The decimal separator.
+ */
+ private final char decimalSeparator;
+
+ //-----------------------------------------------------------------------
+ /**
+ * Lists all the locales that are supported.
+ * <p>
+ * The locale 'en_US' will always be present.
+ *
+ * @return a Set of Locales for which localization is supported
+ */
+ public static Set<Locale> getAvailableLocales() {
+ Locale[] l = DecimalFormatSymbols.getAvailableLocales();
+ Set<Locale> locales = new HashSet<>(l.length);
+ Collections.addAll(locales, l);
+ return locales;
+ }
+
+ /**
+ * Obtains the DecimalStyle for the default
+ * {@link java.util.Locale.Category#FORMAT FORMAT} locale.
+ * <p>
+ * This method provides access to locale sensitive decimal style symbols.
+ * <p>
+ * This is equivalent to calling
+ * {@link #of(Locale)
+ * of(Locale.getDefault(Locale.Category.FORMAT))}.
+ *
+ * @see java.util.Locale.Category#FORMAT
+ * @return the decimal style, not null
+ */
+ public static DecimalStyle ofDefaultLocale() {
+ return of(Locale.getDefault(Locale.Category.FORMAT));
+ }
+
+ /**
+ * Obtains the DecimalStyle for the specified locale.
+ * <p>
+ * This method provides access to locale sensitive decimal style symbols.
+ *
+ * @param locale the locale, not null
+ * @return the decimal style, not null
+ */
+ public static DecimalStyle of(Locale locale) {
+ Objects.requireNonNull(locale, "locale");
+ DecimalStyle info = CACHE.get(locale);
+ if (info == null) {
+ info = create(locale);
+ CACHE.putIfAbsent(locale, info);
+ info = CACHE.get(locale);
+ }
+ return info;
+ }
+
+ private static DecimalStyle create(Locale locale) {
+ DecimalFormatSymbols oldSymbols = DecimalFormatSymbols.getInstance(locale);
+ char zeroDigit = oldSymbols.getZeroDigit();
+ char positiveSign = '+';
+ char negativeSign = oldSymbols.getMinusSign();
+ char decimalSeparator = oldSymbols.getDecimalSeparator();
+ if (zeroDigit == '0' && negativeSign == '-' && decimalSeparator == '.') {
+ return STANDARD;
+ }
+ return new DecimalStyle(zeroDigit, positiveSign, negativeSign, decimalSeparator);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Restricted constructor.
+ *
+ * @param zeroChar the character to use for the digit of zero
+ * @param positiveSignChar the character to use for the positive sign
+ * @param negativeSignChar the character to use for the negative sign
+ * @param decimalPointChar the character to use for the decimal point
+ */
+ private DecimalStyle(char zeroChar, char positiveSignChar, char negativeSignChar, char decimalPointChar) {
+ this.zeroDigit = zeroChar;
+ this.positiveSign = positiveSignChar;
+ this.negativeSign = negativeSignChar;
+ this.decimalSeparator = decimalPointChar;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the character that represents zero.
+ * <p>
+ * The character used to represent digits may vary by culture.
+ * This method specifies the zero character to use, which implies the characters for one to nine.
+ *
+ * @return the character for zero
+ */
+ public char getZeroDigit() {
+ return zeroDigit;
+ }
+
+ /**
+ * Returns a copy of the info with a new character that represents zero.
+ * <p>
+ * The character used to represent digits may vary by culture.
+ * This method specifies the zero character to use, which implies the characters for one to nine.
+ *
+ * @param zeroDigit the character for zero
+ * @return a copy with a new character that represents zero, not null
+
+ */
+ public DecimalStyle withZeroDigit(char zeroDigit) {
+ if (zeroDigit == this.zeroDigit) {
+ return this;
+ }
+ return new DecimalStyle(zeroDigit, positiveSign, negativeSign, decimalSeparator);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the character that represents the positive sign.
+ * <p>
+ * The character used to represent a positive number may vary by culture.
+ * This method specifies the character to use.
+ *
+ * @return the character for the positive sign
+ */
+ public char getPositiveSign() {
+ return positiveSign;
+ }
+
+ /**
+ * Returns a copy of the info with a new character that represents the positive sign.
+ * <p>
+ * The character used to represent a positive number may vary by culture.
+ * This method specifies the character to use.
+ *
+ * @param positiveSign the character for the positive sign
+ * @return a copy with a new character that represents the positive sign, not null
+ */
+ public DecimalStyle withPositiveSign(char positiveSign) {
+ if (positiveSign == this.positiveSign) {
+ return this;
+ }
+ return new DecimalStyle(zeroDigit, positiveSign, negativeSign, decimalSeparator);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the character that represents the negative sign.
+ * <p>
+ * The character used to represent a negative number may vary by culture.
+ * This method specifies the character to use.
+ *
+ * @return the character for the negative sign
+ */
+ public char getNegativeSign() {
+ return negativeSign;
+ }
+
+ /**
+ * Returns a copy of the info with a new character that represents the negative sign.
+ * <p>
+ * The character used to represent a negative number may vary by culture.
+ * This method specifies the character to use.
+ *
+ * @param negativeSign the character for the negative sign
+ * @return a copy with a new character that represents the negative sign, not null
+ */
+ public DecimalStyle withNegativeSign(char negativeSign) {
+ if (negativeSign == this.negativeSign) {
+ return this;
+ }
+ return new DecimalStyle(zeroDigit, positiveSign, negativeSign, decimalSeparator);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the character that represents the decimal point.
+ * <p>
+ * The character used to represent a decimal point may vary by culture.
+ * This method specifies the character to use.
+ *
+ * @return the character for the decimal point
+ */
+ public char getDecimalSeparator() {
+ return decimalSeparator;
+ }
+
+ /**
+ * Returns a copy of the info with a new character that represents the decimal point.
+ * <p>
+ * The character used to represent a decimal point may vary by culture.
+ * This method specifies the character to use.
+ *
+ * @param decimalSeparator the character for the decimal point
+ * @return a copy with a new character that represents the decimal point, not null
+ */
+ public DecimalStyle withDecimalSeparator(char decimalSeparator) {
+ if (decimalSeparator == this.decimalSeparator) {
+ return this;
+ }
+ return new DecimalStyle(zeroDigit, positiveSign, negativeSign, decimalSeparator);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks whether the character is a digit, based on the currently set zero character.
+ *
+ * @param ch the character to check
+ * @return the value, 0 to 9, of the character, or -1 if not a digit
+ */
+ int convertToDigit(char ch) {
+ int val = ch - zeroDigit;
+ return (val >= 0 && val <= 9) ? val : -1;
+ }
+
+ /**
+ * Converts the input numeric text to the internationalized form using the zero character.
+ *
+ * @param numericText the text, consisting of digits 0 to 9, to convert, not null
+ * @return the internationalized text, not null
+ */
+ String convertNumberToI18N(String numericText) {
+ if (zeroDigit == '0') {
+ return numericText;
+ }
+ int diff = zeroDigit - '0';
+ char[] array = numericText.toCharArray();
+ for (int i = 0; i < array.length; i++) {
+ array[i] = (char) (array[i] + diff);
+ }
+ return new String(array);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if this DecimalStyle is equal to another DecimalStyle.
+ *
+ * @param obj the object to check, null returns false
+ * @return true if this is equal to the other date
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof DecimalStyle) {
+ DecimalStyle other = (DecimalStyle) obj;
+ return (zeroDigit == other.zeroDigit && positiveSign == other.positiveSign &&
+ negativeSign == other.negativeSign && decimalSeparator == other.decimalSeparator);
+ }
+ return false;
+ }
+
+ /**
+ * A hash code for this DecimalStyle.
+ *
+ * @return a suitable hash code
+ */
+ @Override
+ public int hashCode() {
+ return zeroDigit + positiveSign + negativeSign + decimalSeparator;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a string describing this DecimalStyle.
+ *
+ * @return a string description, not null
+ */
+ @Override
+ public String toString() {
+ return "DecimalStyle[" + zeroDigit + positiveSign + negativeSign + decimalSeparator + "]";
+ }
+
+}
diff --git a/java/time/format/FormatStyle.java b/java/time/format/FormatStyle.java
new file mode 100644
index 0000000..a5c8229
--- /dev/null
+++ b/java/time/format/FormatStyle.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2012, 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) 2008-2012, 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.format;
+
+/**
+ * Enumeration of the style of a localized date, time or date-time formatter.
+ * <p>
+ * These styles are used when obtaining a date-time style from configuration.
+ * See {@link DateTimeFormatter} and {@link DateTimeFormatterBuilder} for usage.
+ *
+ * @implSpec
+ * This is an immutable and thread-safe enum.
+ *
+ * @since 1.8
+ */
+public enum FormatStyle {
+ // ordered from large to small
+
+ /**
+ * Full text style, with the most detail.
+ * For example, the format might be 'Tuesday, April 12, 1952 AD' or '3:30:42pm PST'.
+ */
+ FULL,
+ /**
+ * Long text style, with lots of detail.
+ * For example, the format might be 'January 12, 1952'.
+ */
+ LONG,
+ /**
+ * Medium text style, with some detail.
+ * For example, the format might be 'Jan 12, 1952'.
+ */
+ MEDIUM,
+ /**
+ * Short text style, typically numeric.
+ * For example, the format might be '12.13.52' or '3:30pm'.
+ */
+ SHORT;
+
+}
diff --git a/java/time/format/Parsed.java b/java/time/format/Parsed.java
new file mode 100644
index 0000000..1eb8845
--- /dev/null
+++ b/java/time/format/Parsed.java
@@ -0,0 +1,677 @@
+/*
+ * Copyright (c) 2012, 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) 2008-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.format;
+
+import static java.time.temporal.ChronoField.AMPM_OF_DAY;
+import static java.time.temporal.ChronoField.CLOCK_HOUR_OF_AMPM;
+import static java.time.temporal.ChronoField.CLOCK_HOUR_OF_DAY;
+import static java.time.temporal.ChronoField.HOUR_OF_AMPM;
+import static java.time.temporal.ChronoField.HOUR_OF_DAY;
+import static java.time.temporal.ChronoField.INSTANT_SECONDS;
+import static java.time.temporal.ChronoField.MICRO_OF_DAY;
+import static java.time.temporal.ChronoField.MICRO_OF_SECOND;
+import static java.time.temporal.ChronoField.MILLI_OF_DAY;
+import static java.time.temporal.ChronoField.MILLI_OF_SECOND;
+import static java.time.temporal.ChronoField.MINUTE_OF_DAY;
+import static java.time.temporal.ChronoField.MINUTE_OF_HOUR;
+import static java.time.temporal.ChronoField.NANO_OF_DAY;
+import static java.time.temporal.ChronoField.NANO_OF_SECOND;
+import static java.time.temporal.ChronoField.OFFSET_SECONDS;
+import static java.time.temporal.ChronoField.SECOND_OF_DAY;
+import static java.time.temporal.ChronoField.SECOND_OF_MINUTE;
+
+import java.time.DateTimeException;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.Period;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.chrono.ChronoLocalDate;
+import java.time.chrono.ChronoLocalDateTime;
+import java.time.chrono.ChronoZonedDateTime;
+import java.time.chrono.Chronology;
+import java.time.temporal.ChronoField;
+import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalField;
+import java.time.temporal.TemporalQueries;
+import java.time.temporal.TemporalQuery;
+import java.time.temporal.UnsupportedTemporalTypeException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * A store of parsed data.
+ * <p>
+ * This class is used during parsing to collect the data. Part of the parsing process
+ * involves handling optional blocks and multiple copies of the data get created to
+ * support the necessary backtracking.
+ * <p>
+ * Once parsing is completed, this class can be used as the resultant {@code TemporalAccessor}.
+ * In most cases, it is only exposed once the fields have been resolved.
+ *
+ * @implSpec
+ * This class is a mutable context intended for use from a single thread.
+ * Usage of the class is thread-safe within standard parsing as a new instance of this class
+ * is automatically created for each parse and parsing is single-threaded
+ *
+ * @since 1.8
+ */
+final class Parsed implements TemporalAccessor {
+ // some fields are accessed using package scope from DateTimeParseContext
+
+ /**
+ * The parsed fields.
+ */
+ final Map<TemporalField, Long> fieldValues = new HashMap<>();
+ /**
+ * The parsed zone.
+ */
+ ZoneId zone;
+ /**
+ * The parsed chronology.
+ */
+ Chronology chrono;
+ /**
+ * Whether a leap-second is parsed.
+ */
+ boolean leapSecond;
+ /**
+ * The resolver style to use.
+ */
+ private ResolverStyle resolverStyle;
+ /**
+ * The resolved date.
+ */
+ private ChronoLocalDate date;
+ /**
+ * The resolved time.
+ */
+ private LocalTime time;
+ /**
+ * The excess period from time-only parsing.
+ */
+ Period excessDays = Period.ZERO;
+
+ /**
+ * Creates an instance.
+ */
+ Parsed() {
+ }
+
+ /**
+ * Creates a copy.
+ */
+ Parsed copy() {
+ // only copy fields used in parsing stage
+ Parsed cloned = new Parsed();
+ cloned.fieldValues.putAll(this.fieldValues);
+ cloned.zone = this.zone;
+ cloned.chrono = this.chrono;
+ cloned.leapSecond = this.leapSecond;
+ return cloned;
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ public boolean isSupported(TemporalField field) {
+ if (fieldValues.containsKey(field) ||
+ (date != null && date.isSupported(field)) ||
+ (time != null && time.isSupported(field))) {
+ return true;
+ }
+ return field != null && (field instanceof ChronoField == false) && field.isSupportedBy(this);
+ }
+
+ @Override
+ public long getLong(TemporalField field) {
+ Objects.requireNonNull(field, "field");
+ Long value = fieldValues.get(field);
+ if (value != null) {
+ return value;
+ }
+ if (date != null && date.isSupported(field)) {
+ return date.getLong(field);
+ }
+ if (time != null && time.isSupported(field)) {
+ return time.getLong(field);
+ }
+ if (field instanceof ChronoField) {
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
+ }
+ return field.getFrom(this);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <R> R query(TemporalQuery<R> query) {
+ if (query == TemporalQueries.zoneId()) {
+ return (R) zone;
+ } else if (query == TemporalQueries.chronology()) {
+ return (R) chrono;
+ } else if (query == TemporalQueries.localDate()) {
+ return (R) (date != null ? LocalDate.from(date) : null);
+ } else if (query == TemporalQueries.localTime()) {
+ return (R) time;
+ } else if (query == TemporalQueries.zone() || query == TemporalQueries.offset()) {
+ return query.queryFrom(this);
+ } else if (query == TemporalQueries.precision()) {
+ return null; // not a complete date/time
+ }
+ // inline TemporalAccessor.super.query(query) as an optimization
+ // non-JDK classes are not permitted to make this optimization
+ return query.queryFrom(this);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Resolves the fields in this context.
+ *
+ * @param resolverStyle the resolver style, not null
+ * @param resolverFields the fields to use for resolving, null for all fields
+ * @return this, for method chaining
+ * @throws DateTimeException if resolving one field results in a value for
+ * another field that is in conflict
+ */
+ TemporalAccessor resolve(ResolverStyle resolverStyle, Set<TemporalField> resolverFields) {
+ if (resolverFields != null) {
+ fieldValues.keySet().retainAll(resolverFields);
+ }
+ this.resolverStyle = resolverStyle;
+ resolveFields();
+ resolveTimeLenient();
+ crossCheck();
+ resolvePeriod();
+ resolveFractional();
+ resolveInstant();
+ return this;
+ }
+
+ //-----------------------------------------------------------------------
+ private void resolveFields() {
+ // resolve ChronoField
+ resolveInstantFields();
+ resolveDateFields();
+ resolveTimeFields();
+
+ // if any other fields, handle them
+ // any lenient date resolution should return epoch-day
+ if (fieldValues.size() > 0) {
+ int changedCount = 0;
+ outer:
+ while (changedCount < 50) {
+ for (Map.Entry<TemporalField, Long> entry : fieldValues.entrySet()) {
+ TemporalField targetField = entry.getKey();
+ TemporalAccessor resolvedObject = targetField.resolve(fieldValues, this, resolverStyle);
+ if (resolvedObject != null) {
+ if (resolvedObject instanceof ChronoZonedDateTime) {
+ ChronoZonedDateTime<?> czdt = (ChronoZonedDateTime<?>) resolvedObject;
+ if (zone == null) {
+ zone = czdt.getZone();
+ } else if (zone.equals(czdt.getZone()) == false) {
+ throw new DateTimeException("ChronoZonedDateTime must use the effective parsed zone: " + zone);
+ }
+ resolvedObject = czdt.toLocalDateTime();
+ }
+ if (resolvedObject instanceof ChronoLocalDateTime) {
+ ChronoLocalDateTime<?> cldt = (ChronoLocalDateTime<?>) resolvedObject;
+ updateCheckConflict(cldt.toLocalTime(), Period.ZERO);
+ updateCheckConflict(cldt.toLocalDate());
+ changedCount++;
+ continue outer; // have to restart to avoid concurrent modification
+ }
+ if (resolvedObject instanceof ChronoLocalDate) {
+ updateCheckConflict((ChronoLocalDate) resolvedObject);
+ changedCount++;
+ continue outer; // have to restart to avoid concurrent modification
+ }
+ if (resolvedObject instanceof LocalTime) {
+ updateCheckConflict((LocalTime) resolvedObject, Period.ZERO);
+ changedCount++;
+ continue outer; // have to restart to avoid concurrent modification
+ }
+ throw new DateTimeException("Method resolve() can only return ChronoZonedDateTime, " +
+ "ChronoLocalDateTime, ChronoLocalDate or LocalTime");
+ } else if (fieldValues.containsKey(targetField) == false) {
+ changedCount++;
+ continue outer; // have to restart to avoid concurrent modification
+ }
+ }
+ break;
+ }
+ if (changedCount == 50) { // catch infinite loops
+ throw new DateTimeException("One of the parsed fields has an incorrectly implemented resolve method");
+ }
+ // if something changed then have to redo ChronoField resolve
+ if (changedCount > 0) {
+ resolveInstantFields();
+ resolveDateFields();
+ resolveTimeFields();
+ }
+ }
+ }
+
+ private void updateCheckConflict(TemporalField targetField, TemporalField changeField, Long changeValue) {
+ Long old = fieldValues.put(changeField, changeValue);
+ if (old != null && old.longValue() != changeValue.longValue()) {
+ throw new DateTimeException("Conflict found: " + changeField + " " + old +
+ " differs from " + changeField + " " + changeValue +
+ " while resolving " + targetField);
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ private void resolveInstantFields() {
+ // resolve parsed instant seconds to date and time if zone available
+ if (fieldValues.containsKey(INSTANT_SECONDS)) {
+ if (zone != null) {
+ resolveInstantFields0(zone);
+ } else {
+ Long offsetSecs = fieldValues.get(OFFSET_SECONDS);
+ if (offsetSecs != null) {
+ ZoneOffset offset = ZoneOffset.ofTotalSeconds(offsetSecs.intValue());
+ resolveInstantFields0(offset);
+ }
+ }
+ }
+ }
+
+ private void resolveInstantFields0(ZoneId selectedZone) {
+ Instant instant = Instant.ofEpochSecond(fieldValues.remove(INSTANT_SECONDS));
+ ChronoZonedDateTime<?> zdt = chrono.zonedDateTime(instant, selectedZone);
+ updateCheckConflict(zdt.toLocalDate());
+ updateCheckConflict(INSTANT_SECONDS, SECOND_OF_DAY, (long) zdt.toLocalTime().toSecondOfDay());
+ }
+
+ //-----------------------------------------------------------------------
+ private void resolveDateFields() {
+ updateCheckConflict(chrono.resolveDate(fieldValues, resolverStyle));
+ }
+
+ private void updateCheckConflict(ChronoLocalDate cld) {
+ if (date != null) {
+ if (cld != null && date.equals(cld) == false) {
+ throw new DateTimeException("Conflict found: Fields resolved to two different dates: " + date + " " + cld);
+ }
+ } else if (cld != null) {
+ if (chrono.equals(cld.getChronology()) == false) {
+ throw new DateTimeException("ChronoLocalDate must use the effective parsed chronology: " + chrono);
+ }
+ date = cld;
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ private void resolveTimeFields() {
+ // simplify fields
+ if (fieldValues.containsKey(CLOCK_HOUR_OF_DAY)) {
+ // lenient allows anything, smart allows 0-24, strict allows 1-24
+ long ch = fieldValues.remove(CLOCK_HOUR_OF_DAY);
+ if (resolverStyle == ResolverStyle.STRICT || (resolverStyle == ResolverStyle.SMART && ch != 0)) {
+ CLOCK_HOUR_OF_DAY.checkValidValue(ch);
+ }
+ updateCheckConflict(CLOCK_HOUR_OF_DAY, HOUR_OF_DAY, ch == 24 ? 0 : ch);
+ }
+ if (fieldValues.containsKey(CLOCK_HOUR_OF_AMPM)) {
+ // lenient allows anything, smart allows 0-12, strict allows 1-12
+ long ch = fieldValues.remove(CLOCK_HOUR_OF_AMPM);
+ if (resolverStyle == ResolverStyle.STRICT || (resolverStyle == ResolverStyle.SMART && ch != 0)) {
+ CLOCK_HOUR_OF_AMPM.checkValidValue(ch);
+ }
+ updateCheckConflict(CLOCK_HOUR_OF_AMPM, HOUR_OF_AMPM, ch == 12 ? 0 : ch);
+ }
+ if (fieldValues.containsKey(AMPM_OF_DAY) && fieldValues.containsKey(HOUR_OF_AMPM)) {
+ long ap = fieldValues.remove(AMPM_OF_DAY);
+ long hap = fieldValues.remove(HOUR_OF_AMPM);
+ if (resolverStyle == ResolverStyle.LENIENT) {
+ updateCheckConflict(AMPM_OF_DAY, HOUR_OF_DAY, Math.addExact(Math.multiplyExact(ap, 12), hap));
+ } else { // STRICT or SMART
+ AMPM_OF_DAY.checkValidValue(ap);
+ HOUR_OF_AMPM.checkValidValue(ap);
+ updateCheckConflict(AMPM_OF_DAY, HOUR_OF_DAY, ap * 12 + hap);
+ }
+ }
+ if (fieldValues.containsKey(NANO_OF_DAY)) {
+ long nod = fieldValues.remove(NANO_OF_DAY);
+ if (resolverStyle != ResolverStyle.LENIENT) {
+ NANO_OF_DAY.checkValidValue(nod);
+ }
+ updateCheckConflict(NANO_OF_DAY, HOUR_OF_DAY, nod / 3600_000_000_000L);
+ updateCheckConflict(NANO_OF_DAY, MINUTE_OF_HOUR, (nod / 60_000_000_000L) % 60);
+ updateCheckConflict(NANO_OF_DAY, SECOND_OF_MINUTE, (nod / 1_000_000_000L) % 60);
+ updateCheckConflict(NANO_OF_DAY, NANO_OF_SECOND, nod % 1_000_000_000L);
+ }
+ if (fieldValues.containsKey(MICRO_OF_DAY)) {
+ long cod = fieldValues.remove(MICRO_OF_DAY);
+ if (resolverStyle != ResolverStyle.LENIENT) {
+ MICRO_OF_DAY.checkValidValue(cod);
+ }
+ updateCheckConflict(MICRO_OF_DAY, SECOND_OF_DAY, cod / 1_000_000L);
+ updateCheckConflict(MICRO_OF_DAY, MICRO_OF_SECOND, cod % 1_000_000L);
+ }
+ if (fieldValues.containsKey(MILLI_OF_DAY)) {
+ long lod = fieldValues.remove(MILLI_OF_DAY);
+ if (resolverStyle != ResolverStyle.LENIENT) {
+ MILLI_OF_DAY.checkValidValue(lod);
+ }
+ updateCheckConflict(MILLI_OF_DAY, SECOND_OF_DAY, lod / 1_000);
+ updateCheckConflict(MILLI_OF_DAY, MILLI_OF_SECOND, lod % 1_000);
+ }
+ if (fieldValues.containsKey(SECOND_OF_DAY)) {
+ long sod = fieldValues.remove(SECOND_OF_DAY);
+ if (resolverStyle != ResolverStyle.LENIENT) {
+ SECOND_OF_DAY.checkValidValue(sod);
+ }
+ updateCheckConflict(SECOND_OF_DAY, HOUR_OF_DAY, sod / 3600);
+ updateCheckConflict(SECOND_OF_DAY, MINUTE_OF_HOUR, (sod / 60) % 60);
+ updateCheckConflict(SECOND_OF_DAY, SECOND_OF_MINUTE, sod % 60);
+ }
+ if (fieldValues.containsKey(MINUTE_OF_DAY)) {
+ long mod = fieldValues.remove(MINUTE_OF_DAY);
+ if (resolverStyle != ResolverStyle.LENIENT) {
+ MINUTE_OF_DAY.checkValidValue(mod);
+ }
+ updateCheckConflict(MINUTE_OF_DAY, HOUR_OF_DAY, mod / 60);
+ updateCheckConflict(MINUTE_OF_DAY, MINUTE_OF_HOUR, mod % 60);
+ }
+
+ // combine partial second fields strictly, leaving lenient expansion to later
+ if (fieldValues.containsKey(NANO_OF_SECOND)) {
+ long nos = fieldValues.get(NANO_OF_SECOND);
+ if (resolverStyle != ResolverStyle.LENIENT) {
+ NANO_OF_SECOND.checkValidValue(nos);
+ }
+ if (fieldValues.containsKey(MICRO_OF_SECOND)) {
+ long cos = fieldValues.remove(MICRO_OF_SECOND);
+ if (resolverStyle != ResolverStyle.LENIENT) {
+ MICRO_OF_SECOND.checkValidValue(cos);
+ }
+ nos = cos * 1000 + (nos % 1000);
+ updateCheckConflict(MICRO_OF_SECOND, NANO_OF_SECOND, nos);
+ }
+ if (fieldValues.containsKey(MILLI_OF_SECOND)) {
+ long los = fieldValues.remove(MILLI_OF_SECOND);
+ if (resolverStyle != ResolverStyle.LENIENT) {
+ MILLI_OF_SECOND.checkValidValue(los);
+ }
+ updateCheckConflict(MILLI_OF_SECOND, NANO_OF_SECOND, los * 1_000_000L + (nos % 1_000_000L));
+ }
+ }
+
+ // convert to time if all four fields available (optimization)
+ if (fieldValues.containsKey(HOUR_OF_DAY) && fieldValues.containsKey(MINUTE_OF_HOUR) &&
+ fieldValues.containsKey(SECOND_OF_MINUTE) && fieldValues.containsKey(NANO_OF_SECOND)) {
+ long hod = fieldValues.remove(HOUR_OF_DAY);
+ long moh = fieldValues.remove(MINUTE_OF_HOUR);
+ long som = fieldValues.remove(SECOND_OF_MINUTE);
+ long nos = fieldValues.remove(NANO_OF_SECOND);
+ resolveTime(hod, moh, som, nos);
+ }
+ }
+
+ private void resolveTimeLenient() {
+ // leniently create a time from incomplete information
+ // done after everything else as it creates information from nothing
+ // which would break updateCheckConflict(field)
+
+ if (time == null) {
+ // NANO_OF_SECOND merged with MILLI/MICRO above
+ if (fieldValues.containsKey(MILLI_OF_SECOND)) {
+ long los = fieldValues.remove(MILLI_OF_SECOND);
+ if (fieldValues.containsKey(MICRO_OF_SECOND)) {
+ // merge milli-of-second and micro-of-second for better error message
+ long cos = los * 1_000 + (fieldValues.get(MICRO_OF_SECOND) % 1_000);
+ updateCheckConflict(MILLI_OF_SECOND, MICRO_OF_SECOND, cos);
+ fieldValues.remove(MICRO_OF_SECOND);
+ fieldValues.put(NANO_OF_SECOND, cos * 1_000L);
+ } else {
+ // convert milli-of-second to nano-of-second
+ fieldValues.put(NANO_OF_SECOND, los * 1_000_000L);
+ }
+ } else if (fieldValues.containsKey(MICRO_OF_SECOND)) {
+ // convert micro-of-second to nano-of-second
+ long cos = fieldValues.remove(MICRO_OF_SECOND);
+ fieldValues.put(NANO_OF_SECOND, cos * 1_000L);
+ }
+
+ // merge hour/minute/second/nano leniently
+ Long hod = fieldValues.get(HOUR_OF_DAY);
+ if (hod != null) {
+ Long moh = fieldValues.get(MINUTE_OF_HOUR);
+ Long som = fieldValues.get(SECOND_OF_MINUTE);
+ Long nos = fieldValues.get(NANO_OF_SECOND);
+
+ // check for invalid combinations that cannot be defaulted
+ if ((moh == null && (som != null || nos != null)) ||
+ (moh != null && som == null && nos != null)) {
+ return;
+ }
+
+ // default as necessary and build time
+ long mohVal = (moh != null ? moh : 0);
+ long somVal = (som != null ? som : 0);
+ long nosVal = (nos != null ? nos : 0);
+ resolveTime(hod, mohVal, somVal, nosVal);
+ fieldValues.remove(HOUR_OF_DAY);
+ fieldValues.remove(MINUTE_OF_HOUR);
+ fieldValues.remove(SECOND_OF_MINUTE);
+ fieldValues.remove(NANO_OF_SECOND);
+ }
+ }
+
+ // validate remaining
+ if (resolverStyle != ResolverStyle.LENIENT && fieldValues.size() > 0) {
+ for (Entry<TemporalField, Long> entry : fieldValues.entrySet()) {
+ TemporalField field = entry.getKey();
+ if (field instanceof ChronoField && field.isTimeBased()) {
+ ((ChronoField) field).checkValidValue(entry.getValue());
+ }
+ }
+ }
+ }
+
+ private void resolveTime(long hod, long moh, long som, long nos) {
+ if (resolverStyle == ResolverStyle.LENIENT) {
+ long totalNanos = Math.multiplyExact(hod, 3600_000_000_000L);
+ totalNanos = Math.addExact(totalNanos, Math.multiplyExact(moh, 60_000_000_000L));
+ totalNanos = Math.addExact(totalNanos, Math.multiplyExact(som, 1_000_000_000L));
+ totalNanos = Math.addExact(totalNanos, nos);
+ int excessDays = (int) Math.floorDiv(totalNanos, 86400_000_000_000L); // safe int cast
+ long nod = Math.floorMod(totalNanos, 86400_000_000_000L);
+ updateCheckConflict(LocalTime.ofNanoOfDay(nod), Period.ofDays(excessDays));
+ } else { // STRICT or SMART
+ int mohVal = MINUTE_OF_HOUR.checkValidIntValue(moh);
+ int nosVal = NANO_OF_SECOND.checkValidIntValue(nos);
+ // handle 24:00 end of day
+ if (resolverStyle == ResolverStyle.SMART && hod == 24 && mohVal == 0 && som == 0 && nosVal == 0) {
+ updateCheckConflict(LocalTime.MIDNIGHT, Period.ofDays(1));
+ } else {
+ int hodVal = HOUR_OF_DAY.checkValidIntValue(hod);
+ int somVal = SECOND_OF_MINUTE.checkValidIntValue(som);
+ updateCheckConflict(LocalTime.of(hodVal, mohVal, somVal, nosVal), Period.ZERO);
+ }
+ }
+ }
+
+ private void resolvePeriod() {
+ // add whole days if we have both date and time
+ if (date != null && time != null && excessDays.isZero() == false) {
+ date = date.plus(excessDays);
+ excessDays = Period.ZERO;
+ }
+ }
+
+ private void resolveFractional() {
+ // ensure fractional seconds available as ChronoField requires
+ // resolveTimeLenient() will have merged MICRO_OF_SECOND/MILLI_OF_SECOND to NANO_OF_SECOND
+ if (time == null &&
+ (fieldValues.containsKey(INSTANT_SECONDS) ||
+ fieldValues.containsKey(SECOND_OF_DAY) ||
+ fieldValues.containsKey(SECOND_OF_MINUTE))) {
+ if (fieldValues.containsKey(NANO_OF_SECOND)) {
+ long nos = fieldValues.get(NANO_OF_SECOND);
+ fieldValues.put(MICRO_OF_SECOND, nos / 1000);
+ fieldValues.put(MILLI_OF_SECOND, nos / 1000000);
+ } else {
+ fieldValues.put(NANO_OF_SECOND, 0L);
+ fieldValues.put(MICRO_OF_SECOND, 0L);
+ fieldValues.put(MILLI_OF_SECOND, 0L);
+ }
+ }
+ }
+
+ private void resolveInstant() {
+ // add instant seconds if we have date, time and zone
+ if (date != null && time != null) {
+ if (zone != null) {
+ long instant = date.atTime(time).atZone(zone).getLong(ChronoField.INSTANT_SECONDS);
+ fieldValues.put(INSTANT_SECONDS, instant);
+ } else {
+ Long offsetSecs = fieldValues.get(OFFSET_SECONDS);
+ if (offsetSecs != null) {
+ ZoneOffset offset = ZoneOffset.ofTotalSeconds(offsetSecs.intValue());
+ long instant = date.atTime(time).atZone(offset).getLong(ChronoField.INSTANT_SECONDS);
+ fieldValues.put(INSTANT_SECONDS, instant);
+ }
+ }
+ }
+ }
+
+ private void updateCheckConflict(LocalTime timeToSet, Period periodToSet) {
+ if (time != null) {
+ if (time.equals(timeToSet) == false) {
+ throw new DateTimeException("Conflict found: Fields resolved to different times: " + time + " " + timeToSet);
+ }
+ if (excessDays.isZero() == false && periodToSet.isZero() == false && excessDays.equals(periodToSet) == false) {
+ throw new DateTimeException("Conflict found: Fields resolved to different excess periods: " + excessDays + " " + periodToSet);
+ } else {
+ excessDays = periodToSet;
+ }
+ } else {
+ time = timeToSet;
+ excessDays = periodToSet;
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ private void crossCheck() {
+ // only cross-check date, time and date-time
+ // avoid object creation if possible
+ if (date != null) {
+ crossCheck(date);
+ }
+ if (time != null) {
+ crossCheck(time);
+ if (date != null && fieldValues.size() > 0) {
+ crossCheck(date.atTime(time));
+ }
+ }
+ }
+
+ private void crossCheck(TemporalAccessor target) {
+ for (Iterator<Entry<TemporalField, Long>> it = fieldValues.entrySet().iterator(); it.hasNext(); ) {
+ Entry<TemporalField, Long> entry = it.next();
+ TemporalField field = entry.getKey();
+ if (target.isSupported(field)) {
+ long val1;
+ try {
+ val1 = target.getLong(field);
+ } catch (RuntimeException ex) {
+ continue;
+ }
+ long val2 = entry.getValue();
+ if (val1 != val2) {
+ throw new DateTimeException("Conflict found: Field " + field + " " + val1 +
+ " differs from " + field + " " + val2 + " derived from " + target);
+ }
+ it.remove();
+ }
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ public String toString() {
+ StringBuilder buf = new StringBuilder(64);
+ buf.append(fieldValues).append(',').append(chrono);
+ if (zone != null) {
+ buf.append(',').append(zone);
+ }
+ if (date != null || time != null) {
+ buf.append(" resolved to ");
+ if (date != null) {
+ buf.append(date);
+ if (time != null) {
+ buf.append('T').append(time);
+ }
+ } else {
+ buf.append(time);
+ }
+ }
+ return buf.toString();
+ }
+
+}
diff --git a/java/time/format/ResolverStyle.java b/java/time/format/ResolverStyle.java
new file mode 100644
index 0000000..313bd17
--- /dev/null
+++ b/java/time/format/ResolverStyle.java
@@ -0,0 +1,116 @@
+/*
+ * 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) 2008-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.format;
+
+/**
+ * Enumeration of different ways to resolve dates and times.
+ * <p>
+ * Parsing a text string occurs in two phases.
+ * Phase 1 is a basic text parse according to the fields added to the builder.
+ * Phase 2 resolves the parsed field-value pairs into date and/or time objects.
+ * This style is used to control how phase 2, resolving, happens.
+ *
+ * @implSpec
+ * This is an immutable and thread-safe enum.
+ *
+ * @since 1.8
+ */
+public enum ResolverStyle {
+
+ /**
+ * Style to resolve dates and times strictly.
+ * <p>
+ * Using strict resolution will ensure that all parsed values are within
+ * the outer range of valid values for the field. Individual fields may
+ * be further processed for strictness.
+ * <p>
+ * For example, resolving year-month and day-of-month in the ISO calendar
+ * system using strict mode will ensure that the day-of-month is valid
+ * for the year-month, rejecting invalid values.
+ */
+ STRICT,
+ /**
+ * Style to resolve dates and times in a smart, or intelligent, manner.
+ * <p>
+ * Using smart resolution will perform the sensible default for each
+ * field, which may be the same as strict, the same as lenient, or a third
+ * behavior. Individual fields will interpret this differently.
+ * <p>
+ * For example, resolving year-month and day-of-month in the ISO calendar
+ * system using smart mode will ensure that the day-of-month is from
+ * 1 to 31, converting any value beyond the last valid day-of-month to be
+ * the last valid day-of-month.
+ */
+ SMART,
+ /**
+ * Style to resolve dates and times leniently.
+ * <p>
+ * Using lenient resolution will resolve the values in an appropriate
+ * lenient manner. Individual fields will interpret this differently.
+ * <p>
+ * For example, lenient mode allows the month in the ISO calendar system
+ * to be outside the range 1 to 12.
+ * For example, month 15 is treated as being 3 months after month 12.
+ */
+ LENIENT;
+
+}
diff --git a/java/time/format/SignStyle.java b/java/time/format/SignStyle.java
new file mode 100644
index 0000000..56dfd03
--- /dev/null
+++ b/java/time/format/SignStyle.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2012, 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) 2008-2012, 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.format;
+
+/**
+ * Enumeration of ways to handle the positive/negative sign.
+ * <p>
+ * The formatting engine allows the positive and negative signs of numbers
+ * to be controlled using this enum.
+ * See {@link DateTimeFormatterBuilder} for usage.
+ *
+ * @implSpec
+ * This is an immutable and thread-safe enum.
+ *
+ * @since 1.8
+ */
+public enum SignStyle {
+
+ /**
+ * Style to output the sign only if the value is negative.
+ * <p>
+ * In strict parsing, the negative sign will be accepted and the positive sign rejected.
+ * In lenient parsing, any sign will be accepted.
+ */
+ NORMAL,
+ /**
+ * Style to always output the sign, where zero will output '+'.
+ * <p>
+ * In strict parsing, the absence of a sign will be rejected.
+ * In lenient parsing, any sign will be accepted, with the absence
+ * of a sign treated as a positive number.
+ */
+ ALWAYS,
+ /**
+ * Style to never output sign, only outputting the absolute value.
+ * <p>
+ * In strict parsing, any sign will be rejected.
+ * In lenient parsing, any sign will be accepted unless the width is fixed.
+ */
+ NEVER,
+ /**
+ * Style to block negative values, throwing an exception on printing.
+ * <p>
+ * In strict parsing, any sign will be rejected.
+ * In lenient parsing, any sign will be accepted unless the width is fixed.
+ */
+ NOT_NEGATIVE,
+ /**
+ * Style to always output the sign if the value exceeds the pad width.
+ * A negative value will always output the '-' sign.
+ * <p>
+ * In strict parsing, the sign will be rejected unless the pad width is exceeded.
+ * In lenient parsing, any sign will be accepted, with the absence
+ * of a sign treated as a positive number.
+ */
+ EXCEEDS_PAD;
+
+ /**
+ * Parse helper.
+ *
+ * @param positive true if positive sign parsed, false for negative sign
+ * @param strict true if strict, false if lenient
+ * @param fixedWidth true if fixed width, false if not
+ * @return
+ */
+ boolean parse(boolean positive, boolean strict, boolean fixedWidth) {
+ switch (ordinal()) {
+ case 0: // NORMAL
+ // valid if negative or (positive and lenient)
+ return !positive || !strict;
+ case 1: // ALWAYS
+ case 4: // EXCEEDS_PAD
+ return true;
+ default:
+ // valid if lenient and not fixed width
+ return !strict && !fixedWidth;
+ }
+ }
+
+}
diff --git a/java/time/format/TextStyle.java b/java/time/format/TextStyle.java
new file mode 100644
index 0000000..ed65d27
--- /dev/null
+++ b/java/time/format/TextStyle.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2012, 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) 2008-2012, 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.format;
+
+import java.util.Calendar;
+
+/**
+ * Enumeration of the style of text formatting and parsing.
+ * <p>
+ * Text styles define three sizes for the formatted text - 'full', 'short' and 'narrow'.
+ * Each of these three sizes is available in both 'standard' and 'stand-alone' variations.
+ * <p>
+ * The difference between the three sizes is obvious in most languages.
+ * For example, in English the 'full' month is 'January', the 'short' month is 'Jan'
+ * and the 'narrow' month is 'J'. Note that the narrow size is often not unique.
+ * For example, 'January', 'June' and 'July' all have the 'narrow' text 'J'.
+ * <p>
+ * The difference between the 'standard' and 'stand-alone' forms is trickier to describe
+ * as there is no difference in English. However, in other languages there is a difference
+ * in the word used when the text is used alone, as opposed to in a complete date.
+ * For example, the word used for a month when used alone in a date picker is different
+ * to the word used for month in association with a day and year in a date.
+ *
+ * @implSpec
+ * This is immutable and thread-safe enum.
+ */
+public enum TextStyle {
+ // ordered from large to small
+ // ordered so that bit 0 of the ordinal indicates stand-alone.
+
+ /**
+ * Full text, typically the full description.
+ * For example, day-of-week Monday might output "Monday".
+ */
+ FULL(Calendar.LONG_FORMAT, 0),
+ /**
+ * Full text for stand-alone use, typically the full description.
+ * For example, day-of-week Monday might output "Monday".
+ */
+ FULL_STANDALONE(Calendar.LONG_STANDALONE, 0),
+ /**
+ * Short text, typically an abbreviation.
+ * For example, day-of-week Monday might output "Mon".
+ */
+ SHORT(Calendar.SHORT_FORMAT, 1),
+ /**
+ * Short text for stand-alone use, typically an abbreviation.
+ * For example, day-of-week Monday might output "Mon".
+ */
+ SHORT_STANDALONE(Calendar.SHORT_STANDALONE, 1),
+ /**
+ * Narrow text, typically a single letter.
+ * For example, day-of-week Monday might output "M".
+ */
+ NARROW(Calendar.NARROW_FORMAT, 1),
+ /**
+ * Narrow text for stand-alone use, typically a single letter.
+ * For example, day-of-week Monday might output "M".
+ */
+ NARROW_STANDALONE(Calendar.NARROW_STANDALONE, 1);
+
+ private final int calendarStyle;
+ private final int zoneNameStyleIndex;
+
+ private TextStyle(int calendarStyle, int zoneNameStyleIndex) {
+ this.calendarStyle = calendarStyle;
+ this.zoneNameStyleIndex = zoneNameStyleIndex;
+ }
+
+ /**
+ * Returns true if the Style is a stand-alone style.
+ * @return true if the style is a stand-alone style.
+ */
+ public boolean isStandalone() {
+ return (ordinal() & 1) == 1;
+ }
+
+ /**
+ * Returns the stand-alone style with the same size.
+ * @return the stand-alone style with the same size
+ */
+ public TextStyle asStandalone() {
+ return TextStyle.values()[ordinal() | 1];
+ }
+
+ /**
+ * Returns the normal style with the same size.
+ *
+ * @return the normal style with the same size
+ */
+ public TextStyle asNormal() {
+ return TextStyle.values()[ordinal() & ~1];
+ }
+
+ /**
+ * Returns the {@code Calendar} style corresponding to this {@code TextStyle}.
+ *
+ * @return the corresponding {@code Calendar} style
+ */
+ int toCalendarStyle() {
+ return calendarStyle;
+ }
+
+ /**
+ * Returns the relative index value to an element of the {@link
+ * java.text.DateFormatSymbols#getZoneStrings() DateFormatSymbols.getZoneStrings()}
+ * value, 0 for long names and 1 for short names (abbreviations). Note that these values
+ * do <em>not</em> correspond to the {@link java.util.TimeZone#LONG} and {@link
+ * java.util.TimeZone#SHORT} values.
+ *
+ * @return the relative index value to time zone names array
+ */
+ int zoneNameStyleIndex() {
+ return zoneNameStyleIndex;
+ }
+}
diff --git a/java/time/format/ZoneName.java b/java/time/format/ZoneName.java
new file mode 100644
index 0000000..fe4a95a
--- /dev/null
+++ b/java/time/format/ZoneName.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2016, 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.
+ */
+package java.time.format;
+
+import android.icu.impl.ZoneMeta;
+import android.icu.text.TimeZoneNames;
+import android.icu.util.TimeZone;
+import android.icu.util.ULocale;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * A helper class to map a zone name to metazone and back to the
+ * appropriate zone id for the particular locale.
+ * <p>
+ * The zid<->metazone mappings are based on CLDR metaZones.xml.
+ * The alias mappings are based on Link entries in tzdb data files.
+ */
+class ZoneName {
+
+ public static String toZid(String zid, Locale locale) {
+ // Android-changed: delegate to ICU.
+ TimeZoneNames tzNames = TimeZoneNames.getInstance(locale);
+ if (tzNames.getAvailableMetaZoneIDs().contains(zid)) {
+ // Compare TimeZoneFormat#getTargetRegion.
+ ULocale uLocale = ULocale.forLocale(locale);
+ String region = uLocale.getCountry();
+ if (region.length() == 0) {
+ uLocale = ULocale.addLikelySubtags(uLocale);
+ region = uLocale.getCountry();
+ }
+ zid = tzNames.getReferenceZoneID(zid, region);
+ }
+ return toZid(zid);
+ }
+
+ public static String toZid(String zid) {
+ // Android-changed: Use ICU ZoneMeta.
+ String canonicalCldrId = ZoneMeta.getCanonicalCLDRID(zid);
+ if (canonicalCldrId != null) {
+ return canonicalCldrId;
+ }
+ return zid;
+ }
+
+ // Android-removed: zidMap and aliasMap containing zone id data.
+ // Android-removed: zidToMzone, mzoneToZid, mzoneToZidL, aliases and their initialization code.
+}
diff --git a/java/time/format/package-info.java b/java/time/format/package-info.java
new file mode 100644
index 0000000..140bb6d
--- /dev/null
+++ b/java/time/format/package-info.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2012, 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) 2012, 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.
+ */
+
+/**
+ * <p>
+ * Provides classes to print and parse dates and times.
+ * </p>
+ * <p>
+ * Printing and parsing is based around the
+ * {@link java.time.format.DateTimeFormatter DateTimeFormatter} class.
+ * Instances are generally obtained from
+ * {@link java.time.format.DateTimeFormatter DateTimeFormatter}, however
+ * {@link java.time.format.DateTimeFormatterBuilder DateTimeFormatterBuilder}
+ * can be used if more power is needed.
+ * </p>
+ * <p>
+ * Localization occurs by calling
+ * {@link java.time.format.DateTimeFormatter#withLocale(java.util.Locale) withLocale(Locale)}
+ * on the formatter. Further customization is possible using
+ * {@link java.time.format.DecimalStyle DecimalStyle}.
+ * </p>
+ *
+ * <h3>Package specification</h3>
+ * <p>
+ * Unless otherwise noted, passing a null argument to a constructor or method in any class or interface
+ * in this package will cause a {@link java.lang.NullPointerException NullPointerException} to be thrown.
+ * The Javadoc "@param" definition is used to summarise the null-behavior.
+ * The "@throws {@link java.lang.NullPointerException}" is not explicitly documented in each method.
+ * </p>
+ * <p>
+ * All calculations should check for numeric overflow and throw either an {@link java.lang.ArithmeticException}
+ * or a {@link java.time.DateTimeException}.
+ * </p>
+ * @since JDK1.8
+ */
+package java.time.format;
diff --git a/java/time/package-info.java b/java/time/package-info.java
new file mode 100644
index 0000000..ea160ff
--- /dev/null
+++ b/java/time/package-info.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright (c) 2012, 2015, 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) 2012, 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.
+ */
+
+/**
+ * <p>
+ * The main API for dates, times, instants, and durations.
+ * </p>
+ * <p>
+ * The classes defined here represent the principle date-time concepts,
+ * including instants, durations, dates, times, time-zones and periods.
+ * They are based on the ISO calendar system, which is the <i>de facto</i> world
+ * calendar following the proleptic Gregorian rules.
+ * All the classes are immutable and thread-safe.
+ * </p>
+ * <p>
+ * Each date time instance is composed of fields that are conveniently
+ * made available by the APIs. For lower level access to the fields refer
+ * to the {@code java.time.temporal} package.
+ * Each class includes support for printing and parsing all manner of dates and times.
+ * Refer to the {@code java.time.format} package for customization options.
+ * </p>
+ * <p>
+ * The {@code java.time.chrono} package contains the calendar neutral API
+ * {@link java.time.chrono.ChronoLocalDate ChronoLocalDate},
+ * {@link java.time.chrono.ChronoLocalDateTime ChronoLocalDateTime},
+ * {@link java.time.chrono.ChronoZonedDateTime ChronoZonedDateTime} and
+ * {@link java.time.chrono.Era Era}.
+ * This is intended for use by applications that need to use localized calendars.
+ * It is recommended that applications use the ISO-8601 date and time classes from
+ * this package across system boundaries, such as to the database or across the network.
+ * The calendar neutral API should be reserved for interactions with users.
+ * </p>
+ *
+ * <h3>Dates and Times</h3>
+ * <p>
+ * {@link java.time.Instant} is essentially a numeric timestamp.
+ * The current Instant can be retrieved from a {@link java.time.Clock}.
+ * This is useful for logging and persistence of a point in time
+ * and has in the past been associated with storing the result
+ * from {@link java.lang.System#currentTimeMillis()}.
+ * </p>
+ * <p>
+ * {@link java.time.LocalDate} stores a date without a time.
+ * This stores a date like '2010-12-03' and could be used to store a birthday.
+ * </p>
+ * <p>
+ * {@link java.time.LocalTime} stores a time without a date.
+ * This stores a time like '11:30' and could be used to store an opening or closing time.
+ * </p>
+ * <p>
+ * {@link java.time.LocalDateTime} stores a date and time.
+ * This stores a date-time like '2010-12-03T11:30'.
+ * </p>
+ * <p>
+ * {@link java.time.ZonedDateTime} stores a date and time with a time-zone.
+ * This is useful if you want to perform accurate calculations of
+ * dates and times taking into account the {@link java.time.ZoneId}, such as 'Europe/Paris'.
+ * Where possible, it is recommended to use a simpler class without a time-zone.
+ * The widespread use of time-zones tends to add considerable complexity to an application.
+ * </p>
+ *
+ * <h3>Duration and Period</h3>
+ * <p>
+ * Beyond dates and times, the API also allows the storage of periods and durations of time.
+ * A {@link java.time.Duration} is a simple measure of time along the time-line in nanoseconds.
+ * A {@link java.time.Period} expresses an amount of time in units meaningful
+ * to humans, such as years or days.
+ * </p>
+ *
+ * <h3>Additional value types</h3>
+ * <p>
+ * {@link java.time.Month} stores a month on its own.
+ * This stores a single month-of-year in isolation, such as 'DECEMBER'.
+ * </p>
+ * <p>
+ * {@link java.time.DayOfWeek} stores a day-of-week on its own.
+ * This stores a single day-of-week in isolation, such as 'TUESDAY'.
+ * </p>
+ * <p>
+ * {@link java.time.Year} stores a year on its own.
+ * This stores a single year in isolation, such as '2010'.
+ * </p>
+ * <p>
+ * {@link java.time.YearMonth} stores a year and month without a day or time.
+ * This stores a year and month, such as '2010-12' and could be used for a credit card expiry.
+ * </p>
+ * <p>
+ * {@link java.time.MonthDay} stores a month and day without a year or time.
+ * This stores a month and day-of-month, such as '--12-03' and
+ * could be used to store an annual event like a birthday without storing the year.
+ * </p>
+ * <p>
+ * {@link java.time.OffsetTime} stores a time and offset from UTC without a date.
+ * This stores a date like '11:30+01:00'.
+ * The {@link java.time.ZoneOffset ZoneOffset} is of the form '+01:00'.
+ * </p>
+ * <p>
+ * {@link java.time.OffsetDateTime} stores a date and time and offset from UTC.
+ * This stores a date-time like '2010-12-03T11:30+01:00'.
+ * This is sometimes found in XML messages and other forms of persistence,
+ * but contains less information than a full time-zone.
+ * </p>
+ *
+ * <h3>Package specification</h3>
+ * <p>
+ * Unless otherwise noted, passing a null argument to a constructor or method in any class or interface
+ * in this package will cause a {@link java.lang.NullPointerException NullPointerException} to be thrown.
+ * The Javadoc "@param" definition is used to summarise the null-behavior.
+ * The "@throws {@link java.lang.NullPointerException}" is not explicitly documented in each method.
+ * </p>
+ * <p>
+ * All calculations should check for numeric overflow and throw either an {@link java.lang.ArithmeticException}
+ * or a {@link java.time.DateTimeException}.
+ * </p>
+ *
+ * <h3>Design notes (non normative)</h3>
+ * <p>
+ * The API has been designed to reject null early and to be clear about this behavior.
+ * A key exception is any method that takes an object and returns a boolean, for the purpose
+ * of checking or validating, will generally return false for null.
+ * </p>
+ * <p>
+ * The API is designed to be type-safe where reasonable in the main high-level API.
+ * Thus, there are separate classes for the distinct concepts of date, time and date-time,
+ * plus variants for offset and time-zone.
+ * This can seem like a lot of classes, but most applications can begin with just five date/time types.
+ * <ul>
+ * <li>{@link java.time.Instant} - a timestamp</li>
+ * <li>{@link java.time.LocalDate} - a date without a time, or any reference to an offset or time-zone</li>
+ * <li>{@link java.time.LocalTime} - a time without a date, or any reference to an offset or time-zone</li>
+ * <li>{@link java.time.LocalDateTime} - combines date and time, but still without any offset or time-zone</li>
+ * <li>{@link java.time.ZonedDateTime} - a "full" date-time with time-zone and resolved offset from UTC/Greenwich</li>
+ * </ul>
+ * <p>
+ * {@code Instant} is the closest equivalent class to {@code java.util.Date}.
+ * {@code ZonedDateTime} is the closest equivalent class to {@code java.util.GregorianCalendar}.
+ * </p>
+ * <p>
+ * Where possible, applications should use {@code LocalDate}, {@code LocalTime} and {@code LocalDateTime}
+ * to better model the domain. For example, a birthday should be stored in a code {@code LocalDate}.
+ * Bear in mind that any use of a {@linkplain java.time.ZoneId time-zone}, such as 'Europe/Paris', adds
+ * considerable complexity to a calculation.
+ * Many applications can be written only using {@code LocalDate}, {@code LocalTime} and {@code Instant},
+ * with the time-zone added at the user interface (UI) layer.
+ * </p>
+ * <p>
+ * The offset-based date-time types {@code OffsetTime} and {@code OffsetDateTime},
+ * are intended primarily for use with network protocols and database access.
+ * For example, most databases cannot automatically store a time-zone like 'Europe/Paris', but
+ * they can store an offset like '+02:00'.
+ * </p>
+ * <p>
+ * Classes are also provided for the most important sub-parts of a date, including {@code Month},
+ * {@code DayOfWeek}, {@code Year}, {@code YearMonth} and {@code MonthDay}.
+ * These can be used to model more complex date-time concepts.
+ * For example, {@code YearMonth} is useful for representing a credit card expiry.
+ * </p>
+ * <p>
+ * Note that while there are a large number of classes representing different aspects of dates,
+ * there are relatively few dealing with different aspects of time.
+ * Following type-safety to its logical conclusion would have resulted in classes for
+ * hour-minute, hour-minute-second and hour-minute-second-nanosecond.
+ * While logically pure, this was not a practical option as it would have almost tripled the
+ * number of classes due to the combinations of date and time.
+ * Thus, {@code LocalTime} is used for all precisions of time, with zeroes used to imply lower precision.
+ * </p>
+ * <p>
+ * Following full type-safety to its ultimate conclusion might also argue for a separate class
+ * for each field in date-time, such as a class for HourOfDay and another for DayOfMonth.
+ * This approach was tried, but was excessively complicated in the Java language, lacking usability.
+ * A similar problem occurs with periods.
+ * There is a case for a separate class for each period unit, such as a type for Years and a type for Minutes.
+ * However, this yields a lot of classes and a problem of type conversion.
+ * Thus, the set of date-time types provided is a compromise between purity and practicality.
+ * </p>
+ * <p>
+ * The API has a relatively large surface area in terms of number of methods.
+ * This is made manageable through the use of consistent method prefixes.
+ * <ul>
+ * <li>{@code of} - static factory method</li>
+ * <li>{@code parse} - static factory method focussed on parsing</li>
+ * <li>{@code get} - gets the value of something</li>
+ * <li>{@code is} - checks if something is true</li>
+ * <li>{@code with} - the immutable equivalent of a setter</li>
+ * <li>{@code plus} - adds an amount to an object</li>
+ * <li>{@code minus} - subtracts an amount from an object</li>
+ * <li>{@code to} - converts this object to another type</li>
+ * <li>{@code at} - combines this object with another, such as {@code date.atTime(time)}</li>
+ * </ul>
+ * <p>
+ * Multiple calendar systems is an awkward addition to the design challenges.
+ * The first principle is that most users want the standard ISO calendar system.
+ * As such, the main classes are ISO-only. The second principle is that most of those that want a
+ * non-ISO calendar system want it for user interaction, thus it is a UI localization issue.
+ * As such, date and time objects should be held as ISO objects in the data model and persistent
+ * storage, only being converted to and from a local calendar for display.
+ * The calendar system would be stored separately in the user preferences.
+ * </p>
+ * <p>
+ * There are, however, some limited use cases where users believe they need to store and use
+ * dates in arbitrary calendar systems throughout the application.
+ * This is supported by {@link java.time.chrono.ChronoLocalDate}, however it is vital to read
+ * all the associated warnings in the Javadoc of that interface before using it.
+ * In summary, applications that require general interoperation between multiple calendar systems
+ * typically need to be written in a very different way to those only using the ISO calendar,
+ * thus most applications should just use ISO and avoid {@code ChronoLocalDate}.
+ * </p>
+ * <p>
+ * The API is also designed for user extensibility, as there are many ways of calculating time.
+ * The {@linkplain java.time.temporal.TemporalField field} and {@linkplain java.time.temporal.TemporalUnit unit}
+ * API, accessed via {@link java.time.temporal.TemporalAccessor TemporalAccessor} and
+ * {@link java.time.temporal.Temporal Temporal} provide considerable flexibility to applications.
+ * In addition, the {@link java.time.temporal.TemporalQuery TemporalQuery} and
+ * {@link java.time.temporal.TemporalAdjuster TemporalAdjuster} interfaces provide day-to-day
+ * power, allowing code to read close to business requirements:
+ * </p>
+ * <pre>
+ * LocalDate customerBirthday = customer.loadBirthdayFromDatabase();
+ * LocalDate today = LocalDate.now();
+ * if (customerBirthday.equals(today)) {
+ * LocalDate specialOfferExpiryDate = today.plusWeeks(2).with(next(FRIDAY));
+ * customer.sendBirthdaySpecialOffer(specialOfferExpiryDate);
+ * }
+ *
+ * </pre>
+ *
+ * @since JDK1.8
+ */
+package java.time;
diff --git a/java/time/temporal/ChronoField.java b/java/time/temporal/ChronoField.java
new file mode 100644
index 0000000..b8f7cee
--- /dev/null
+++ b/java/time/temporal/ChronoField.java
@@ -0,0 +1,780 @@
+/*
+ * Copyright (c) 2012, 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) 2012, 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.temporal;
+
+import android.icu.text.DateTimePatternGenerator;
+import android.icu.util.ULocale;
+import java.time.DayOfWeek;
+import java.time.Instant;
+import java.time.Year;
+import java.time.ZoneOffset;
+import java.time.chrono.ChronoLocalDate;
+import java.time.chrono.Chronology;
+import java.util.Locale;
+import java.util.Objects;
+
+import static java.time.temporal.ChronoUnit.DAYS;
+import static java.time.temporal.ChronoUnit.ERAS;
+import static java.time.temporal.ChronoUnit.FOREVER;
+import static java.time.temporal.ChronoUnit.HALF_DAYS;
+import static java.time.temporal.ChronoUnit.HOURS;
+import static java.time.temporal.ChronoUnit.MICROS;
+import static java.time.temporal.ChronoUnit.MILLIS;
+import static java.time.temporal.ChronoUnit.MINUTES;
+import static java.time.temporal.ChronoUnit.MONTHS;
+import static java.time.temporal.ChronoUnit.NANOS;
+import static java.time.temporal.ChronoUnit.SECONDS;
+import static java.time.temporal.ChronoUnit.WEEKS;
+import static java.time.temporal.ChronoUnit.YEARS;
+
+/**
+ * A standard set of fields.
+ * <p>
+ * This set of fields provide field-based access to manipulate a date, time or date-time.
+ * The standard set of fields can be extended by implementing {@link TemporalField}.
+ * <p>
+ * These fields are intended to be applicable in multiple calendar systems.
+ * For example, most non-ISO calendar systems define dates as a year, month and day,
+ * just with slightly different rules.
+ * The documentation of each field explains how it operates.
+ *
+ * @implSpec
+ * This is a final, immutable and thread-safe enum.
+ *
+ * @since 1.8
+ */
+public enum ChronoField implements TemporalField {
+
+ /**
+ * The nano-of-second.
+ * <p>
+ * This counts the nanosecond within the second, from 0 to 999,999,999.
+ * This field has the same meaning for all calendar systems.
+ * <p>
+ * This field is used to represent the nano-of-second handling any fraction of the second.
+ * Implementations of {@code TemporalAccessor} should provide a value for this field if
+ * they can return a value for {@link #SECOND_OF_MINUTE}, {@link #SECOND_OF_DAY} or
+ * {@link #INSTANT_SECONDS} filling unknown precision with zero.
+ * <p>
+ * When this field is used for setting a value, it should set as much precision as the
+ * object stores, using integer division to remove excess precision.
+ * For example, if the {@code TemporalAccessor} stores time to millisecond precision,
+ * then the nano-of-second must be divided by 1,000,000 before replacing the milli-of-second.
+ * <p>
+ * When parsing this field it behaves equivalent to the following:
+ * The value is validated in strict and smart mode but not in lenient mode.
+ * The field is resolved in combination with {@code MILLI_OF_SECOND} and {@code MICRO_OF_SECOND}.
+ */
+ NANO_OF_SECOND("NanoOfSecond", NANOS, SECONDS, ValueRange.of(0, 999_999_999)),
+ /**
+ * The nano-of-day.
+ * <p>
+ * This counts the nanosecond within the day, from 0 to (24 * 60 * 60 * 1,000,000,000) - 1.
+ * This field has the same meaning for all calendar systems.
+ * <p>
+ * This field is used to represent the nano-of-day handling any fraction of the second.
+ * Implementations of {@code TemporalAccessor} should provide a value for this field if
+ * they can return a value for {@link #SECOND_OF_DAY} filling unknown precision with zero.
+ * <p>
+ * When parsing this field it behaves equivalent to the following:
+ * The value is validated in strict and smart mode but not in lenient mode.
+ * The value is split to form {@code NANO_OF_SECOND}, {@code SECOND_OF_MINUTE},
+ * {@code MINUTE_OF_HOUR} and {@code HOUR_OF_DAY} fields.
+ */
+ NANO_OF_DAY("NanoOfDay", NANOS, DAYS, ValueRange.of(0, 86400L * 1000_000_000L - 1)),
+ /**
+ * The micro-of-second.
+ * <p>
+ * This counts the microsecond within the second, from 0 to 999,999.
+ * This field has the same meaning for all calendar systems.
+ * <p>
+ * This field is used to represent the micro-of-second handling any fraction of the second.
+ * Implementations of {@code TemporalAccessor} should provide a value for this field if
+ * they can return a value for {@link #SECOND_OF_MINUTE}, {@link #SECOND_OF_DAY} or
+ * {@link #INSTANT_SECONDS} filling unknown precision with zero.
+ * <p>
+ * When this field is used for setting a value, it should behave in the same way as
+ * setting {@link #NANO_OF_SECOND} with the value multiplied by 1,000.
+ * <p>
+ * When parsing this field it behaves equivalent to the following:
+ * The value is validated in strict and smart mode but not in lenient mode.
+ * The field is resolved in combination with {@code MILLI_OF_SECOND} to produce
+ * {@code NANO_OF_SECOND}.
+ */
+ MICRO_OF_SECOND("MicroOfSecond", MICROS, SECONDS, ValueRange.of(0, 999_999)),
+ /**
+ * The micro-of-day.
+ * <p>
+ * This counts the microsecond within the day, from 0 to (24 * 60 * 60 * 1,000,000) - 1.
+ * This field has the same meaning for all calendar systems.
+ * <p>
+ * This field is used to represent the micro-of-day handling any fraction of the second.
+ * Implementations of {@code TemporalAccessor} should provide a value for this field if
+ * they can return a value for {@link #SECOND_OF_DAY} filling unknown precision with zero.
+ * <p>
+ * When this field is used for setting a value, it should behave in the same way as
+ * setting {@link #NANO_OF_DAY} with the value multiplied by 1,000.
+ * <p>
+ * When parsing this field it behaves equivalent to the following:
+ * The value is validated in strict and smart mode but not in lenient mode.
+ * The value is split to form {@code MICRO_OF_SECOND}, {@code SECOND_OF_MINUTE},
+ * {@code MINUTE_OF_HOUR} and {@code HOUR_OF_DAY} fields.
+ */
+ MICRO_OF_DAY("MicroOfDay", MICROS, DAYS, ValueRange.of(0, 86400L * 1000_000L - 1)),
+ /**
+ * The milli-of-second.
+ * <p>
+ * This counts the millisecond within the second, from 0 to 999.
+ * This field has the same meaning for all calendar systems.
+ * <p>
+ * This field is used to represent the milli-of-second handling any fraction of the second.
+ * Implementations of {@code TemporalAccessor} should provide a value for this field if
+ * they can return a value for {@link #SECOND_OF_MINUTE}, {@link #SECOND_OF_DAY} or
+ * {@link #INSTANT_SECONDS} filling unknown precision with zero.
+ * <p>
+ * When this field is used for setting a value, it should behave in the same way as
+ * setting {@link #NANO_OF_SECOND} with the value multiplied by 1,000,000.
+ * <p>
+ * When parsing this field it behaves equivalent to the following:
+ * The value is validated in strict and smart mode but not in lenient mode.
+ * The field is resolved in combination with {@code MICRO_OF_SECOND} to produce
+ * {@code NANO_OF_SECOND}.
+ */
+ MILLI_OF_SECOND("MilliOfSecond", MILLIS, SECONDS, ValueRange.of(0, 999)),
+ /**
+ * The milli-of-day.
+ * <p>
+ * This counts the millisecond within the day, from 0 to (24 * 60 * 60 * 1,000) - 1.
+ * This field has the same meaning for all calendar systems.
+ * <p>
+ * This field is used to represent the milli-of-day handling any fraction of the second.
+ * Implementations of {@code TemporalAccessor} should provide a value for this field if
+ * they can return a value for {@link #SECOND_OF_DAY} filling unknown precision with zero.
+ * <p>
+ * When this field is used for setting a value, it should behave in the same way as
+ * setting {@link #NANO_OF_DAY} with the value multiplied by 1,000,000.
+ * <p>
+ * When parsing this field it behaves equivalent to the following:
+ * The value is validated in strict and smart mode but not in lenient mode.
+ * The value is split to form {@code MILLI_OF_SECOND}, {@code SECOND_OF_MINUTE},
+ * {@code MINUTE_OF_HOUR} and {@code HOUR_OF_DAY} fields.
+ */
+ MILLI_OF_DAY("MilliOfDay", MILLIS, DAYS, ValueRange.of(0, 86400L * 1000L - 1)),
+ /**
+ * The second-of-minute.
+ * <p>
+ * This counts the second within the minute, from 0 to 59.
+ * This field has the same meaning for all calendar systems.
+ * <p>
+ * When parsing this field it behaves equivalent to the following:
+ * The value is validated in strict and smart mode but not in lenient mode.
+ */
+ SECOND_OF_MINUTE("SecondOfMinute", SECONDS, MINUTES, ValueRange.of(0, 59), "second"),
+ /**
+ * The second-of-day.
+ * <p>
+ * This counts the second within the day, from 0 to (24 * 60 * 60) - 1.
+ * This field has the same meaning for all calendar systems.
+ * <p>
+ * When parsing this field it behaves equivalent to the following:
+ * The value is validated in strict and smart mode but not in lenient mode.
+ * The value is split to form {@code SECOND_OF_MINUTE}, {@code MINUTE_OF_HOUR}
+ * and {@code HOUR_OF_DAY} fields.
+ */
+ SECOND_OF_DAY("SecondOfDay", SECONDS, DAYS, ValueRange.of(0, 86400L - 1)),
+ /**
+ * The minute-of-hour.
+ * <p>
+ * This counts the minute within the hour, from 0 to 59.
+ * This field has the same meaning for all calendar systems.
+ * <p>
+ * When parsing this field it behaves equivalent to the following:
+ * The value is validated in strict and smart mode but not in lenient mode.
+ */
+ MINUTE_OF_HOUR("MinuteOfHour", MINUTES, HOURS, ValueRange.of(0, 59), "minute"),
+ /**
+ * The minute-of-day.
+ * <p>
+ * This counts the minute within the day, from 0 to (24 * 60) - 1.
+ * This field has the same meaning for all calendar systems.
+ * <p>
+ * When parsing this field it behaves equivalent to the following:
+ * The value is validated in strict and smart mode but not in lenient mode.
+ * The value is split to form {@code MINUTE_OF_HOUR} and {@code HOUR_OF_DAY} fields.
+ */
+ MINUTE_OF_DAY("MinuteOfDay", MINUTES, DAYS, ValueRange.of(0, (24 * 60) - 1)),
+ /**
+ * The hour-of-am-pm.
+ * <p>
+ * This counts the hour within the AM/PM, from 0 to 11.
+ * This is the hour that would be observed on a standard 12-hour digital clock.
+ * This field has the same meaning for all calendar systems.
+ * <p>
+ * When parsing this field it behaves equivalent to the following:
+ * The value is validated from 0 to 11 in strict and smart mode.
+ * In lenient mode the value is not validated. It is combined with
+ * {@code AMPM_OF_DAY} to form {@code HOUR_OF_DAY} by multiplying
+ * the {AMPM_OF_DAY} value by 12.
+ */
+ HOUR_OF_AMPM("HourOfAmPm", HOURS, HALF_DAYS, ValueRange.of(0, 11)),
+ /**
+ * The clock-hour-of-am-pm.
+ * <p>
+ * This counts the hour within the AM/PM, from 1 to 12.
+ * This is the hour that would be observed on a standard 12-hour analog wall clock.
+ * This field has the same meaning for all calendar systems.
+ * <p>
+ * When parsing this field it behaves equivalent to the following:
+ * The value is validated from 1 to 12 in strict mode and from
+ * 0 to 12 in smart mode. In lenient mode the value is not validated.
+ * The field is converted to an {@code HOUR_OF_AMPM} with the same value,
+ * unless the value is 12, in which case it is converted to 0.
+ */
+ CLOCK_HOUR_OF_AMPM("ClockHourOfAmPm", HOURS, HALF_DAYS, ValueRange.of(1, 12)),
+ /**
+ * The hour-of-day.
+ * <p>
+ * This counts the hour within the day, from 0 to 23.
+ * This is the hour that would be observed on a standard 24-hour digital clock.
+ * This field has the same meaning for all calendar systems.
+ * <p>
+ * When parsing this field it behaves equivalent to the following:
+ * The value is validated in strict and smart mode but not in lenient mode.
+ * The field is combined with {@code MINUTE_OF_HOUR}, {@code SECOND_OF_MINUTE} and
+ * {@code NANO_OF_SECOND} to produce a {@code LocalTime}.
+ * In lenient mode, any excess days are added to the parsed date, or
+ * made available via {@link java.time.format.DateTimeFormatter#parsedExcessDays()}.
+ */
+ HOUR_OF_DAY("HourOfDay", HOURS, DAYS, ValueRange.of(0, 23), "hour"),
+ /**
+ * The clock-hour-of-day.
+ * <p>
+ * This counts the hour within the AM/PM, from 1 to 24.
+ * This is the hour that would be observed on a 24-hour analog wall clock.
+ * This field has the same meaning for all calendar systems.
+ * <p>
+ * When parsing this field it behaves equivalent to the following:
+ * The value is validated from 1 to 24 in strict mode and from
+ * 0 to 24 in smart mode. In lenient mode the value is not validated.
+ * The field is converted to an {@code HOUR_OF_DAY} with the same value,
+ * unless the value is 24, in which case it is converted to 0.
+ */
+ CLOCK_HOUR_OF_DAY("ClockHourOfDay", HOURS, DAYS, ValueRange.of(1, 24)),
+ /**
+ * The am-pm-of-day.
+ * <p>
+ * This counts the AM/PM within the day, from 0 (AM) to 1 (PM).
+ * This field has the same meaning for all calendar systems.
+ * <p>
+ * When parsing this field it behaves equivalent to the following:
+ * The value is validated from 0 to 1 in strict and smart mode.
+ * In lenient mode the value is not validated. It is combined with
+ * {@code HOUR_OF_AMPM} to form {@code HOUR_OF_DAY} by multiplying
+ * the {AMPM_OF_DAY} value by 12.
+ */
+ AMPM_OF_DAY("AmPmOfDay", HALF_DAYS, DAYS, ValueRange.of(0, 1), "dayperiod"),
+ /**
+ * The day-of-week, such as Tuesday.
+ * <p>
+ * This represents the standard concept of the day of the week.
+ * In the default ISO calendar system, this has values from Monday (1) to Sunday (7).
+ * The {@link DayOfWeek} class can be used to interpret the result.
+ * <p>
+ * Most non-ISO calendar systems also define a seven day week that aligns with ISO.
+ * Those calendar systems must also use the same numbering system, from Monday (1) to
+ * Sunday (7), which allows {@code DayOfWeek} to be used.
+ * <p>
+ * Calendar systems that do not have a standard seven day week should implement this field
+ * if they have a similar concept of named or numbered days within a period similar
+ * to a week. It is recommended that the numbering starts from 1.
+ */
+ DAY_OF_WEEK("DayOfWeek", DAYS, WEEKS, ValueRange.of(1, 7), "weekday"),
+ /**
+ * The aligned day-of-week within a month.
+ * <p>
+ * This represents concept of the count of days within the period of a week
+ * where the weeks are aligned to the start of the month.
+ * This field is typically used with {@link #ALIGNED_WEEK_OF_MONTH}.
+ * <p>
+ * For example, in a calendar systems with a seven day week, the first aligned-week-of-month
+ * starts on day-of-month 1, the second aligned-week starts on day-of-month 8, and so on.
+ * Within each of these aligned-weeks, the days are numbered from 1 to 7 and returned
+ * as the value of this field.
+ * As such, day-of-month 1 to 7 will have aligned-day-of-week values from 1 to 7.
+ * And day-of-month 8 to 14 will repeat this with aligned-day-of-week values from 1 to 7.
+ * <p>
+ * Calendar systems that do not have a seven day week should typically implement this
+ * field in the same way, but using the alternate week length.
+ */
+ ALIGNED_DAY_OF_WEEK_IN_MONTH("AlignedDayOfWeekInMonth", DAYS, WEEKS, ValueRange.of(1, 7)),
+ /**
+ * The aligned day-of-week within a year.
+ * <p>
+ * This represents concept of the count of days within the period of a week
+ * where the weeks are aligned to the start of the year.
+ * This field is typically used with {@link #ALIGNED_WEEK_OF_YEAR}.
+ * <p>
+ * For example, in a calendar systems with a seven day week, the first aligned-week-of-year
+ * starts on day-of-year 1, the second aligned-week starts on day-of-year 8, and so on.
+ * Within each of these aligned-weeks, the days are numbered from 1 to 7 and returned
+ * as the value of this field.
+ * As such, day-of-year 1 to 7 will have aligned-day-of-week values from 1 to 7.
+ * And day-of-year 8 to 14 will repeat this with aligned-day-of-week values from 1 to 7.
+ * <p>
+ * Calendar systems that do not have a seven day week should typically implement this
+ * field in the same way, but using the alternate week length.
+ */
+ ALIGNED_DAY_OF_WEEK_IN_YEAR("AlignedDayOfWeekInYear", DAYS, WEEKS, ValueRange.of(1, 7)),
+ /**
+ * The day-of-month.
+ * <p>
+ * This represents the concept of the day within the month.
+ * In the default ISO calendar system, this has values from 1 to 31 in most months.
+ * April, June, September, November have days from 1 to 30, while February has days
+ * from 1 to 28, or 29 in a leap year.
+ * <p>
+ * Non-ISO calendar systems should implement this field using the most recognized
+ * day-of-month values for users of the calendar system.
+ * Normally, this is a count of days from 1 to the length of the month.
+ */
+ DAY_OF_MONTH("DayOfMonth", DAYS, MONTHS, ValueRange.of(1, 28, 31), "day"),
+ /**
+ * The day-of-year.
+ * <p>
+ * This represents the concept of the day within the year.
+ * In the default ISO calendar system, this has values from 1 to 365 in standard
+ * years and 1 to 366 in leap years.
+ * <p>
+ * Non-ISO calendar systems should implement this field using the most recognized
+ * day-of-year values for users of the calendar system.
+ * Normally, this is a count of days from 1 to the length of the year.
+ * <p>
+ * Note that a non-ISO calendar system may have year numbering system that changes
+ * at a different point to the natural reset in the month numbering. An example
+ * of this is the Japanese calendar system where a change of era, which resets
+ * the year number to 1, can happen on any date. The era and year reset also cause
+ * the day-of-year to be reset to 1, but not the month-of-year or day-of-month.
+ */
+ DAY_OF_YEAR("DayOfYear", DAYS, YEARS, ValueRange.of(1, 365, 366)),
+ /**
+ * The epoch-day, based on the Java epoch of 1970-01-01 (ISO).
+ * <p>
+ * This field is the sequential count of days where 1970-01-01 (ISO) is zero.
+ * Note that this uses the <i>local</i> time-line, ignoring offset and time-zone.
+ * <p>
+ * This field is strictly defined to have the same meaning in all calendar systems.
+ * This is necessary to ensure interoperation between calendars.
+ */
+ EPOCH_DAY("EpochDay", DAYS, FOREVER, ValueRange.of((long) (Year.MIN_VALUE * 365.25), (long) (Year.MAX_VALUE * 365.25))),
+ /**
+ * The aligned week within a month.
+ * <p>
+ * This represents concept of the count of weeks within the period of a month
+ * where the weeks are aligned to the start of the month.
+ * This field is typically used with {@link #ALIGNED_DAY_OF_WEEK_IN_MONTH}.
+ * <p>
+ * For example, in a calendar systems with a seven day week, the first aligned-week-of-month
+ * starts on day-of-month 1, the second aligned-week starts on day-of-month 8, and so on.
+ * Thus, day-of-month values 1 to 7 are in aligned-week 1, while day-of-month values
+ * 8 to 14 are in aligned-week 2, and so on.
+ * <p>
+ * Calendar systems that do not have a seven day week should typically implement this
+ * field in the same way, but using the alternate week length.
+ */
+ ALIGNED_WEEK_OF_MONTH("AlignedWeekOfMonth", WEEKS, MONTHS, ValueRange.of(1, 4, 5)),
+ /**
+ * The aligned week within a year.
+ * <p>
+ * This represents concept of the count of weeks within the period of a year
+ * where the weeks are aligned to the start of the year.
+ * This field is typically used with {@link #ALIGNED_DAY_OF_WEEK_IN_YEAR}.
+ * <p>
+ * For example, in a calendar systems with a seven day week, the first aligned-week-of-year
+ * starts on day-of-year 1, the second aligned-week starts on day-of-year 8, and so on.
+ * Thus, day-of-year values 1 to 7 are in aligned-week 1, while day-of-year values
+ * 8 to 14 are in aligned-week 2, and so on.
+ * <p>
+ * Calendar systems that do not have a seven day week should typically implement this
+ * field in the same way, but using the alternate week length.
+ */
+ ALIGNED_WEEK_OF_YEAR("AlignedWeekOfYear", WEEKS, YEARS, ValueRange.of(1, 53)),
+ /**
+ * The month-of-year, such as March.
+ * <p>
+ * This represents the concept of the month within the year.
+ * In the default ISO calendar system, this has values from January (1) to December (12).
+ * <p>
+ * Non-ISO calendar systems should implement this field using the most recognized
+ * month-of-year values for users of the calendar system.
+ * Normally, this is a count of months starting from 1.
+ */
+ MONTH_OF_YEAR("MonthOfYear", MONTHS, YEARS, ValueRange.of(1, 12), "month"),
+ /**
+ * The proleptic-month based, counting months sequentially from year 0.
+ * <p>
+ * This field is the sequential count of months where the first month
+ * in proleptic-year zero has the value zero.
+ * Later months have increasingly larger values.
+ * Earlier months have increasingly small values.
+ * There are no gaps or breaks in the sequence of months.
+ * Note that this uses the <i>local</i> time-line, ignoring offset and time-zone.
+ * <p>
+ * In the default ISO calendar system, June 2012 would have the value
+ * {@code (2012 * 12 + 6 - 1)}. This field is primarily for internal use.
+ * <p>
+ * Non-ISO calendar systems must implement this field as per the definition above.
+ * It is just a simple zero-based count of elapsed months from the start of proleptic-year 0.
+ * All calendar systems with a full proleptic-year definition will have a year zero.
+ * If the calendar system has a minimum year that excludes year zero, then one must
+ * be extrapolated in order for this method to be defined.
+ */
+ PROLEPTIC_MONTH("ProlepticMonth", MONTHS, FOREVER, ValueRange.of(Year.MIN_VALUE * 12L, Year.MAX_VALUE * 12L + 11)),
+ /**
+ * The year within the era.
+ * <p>
+ * This represents the concept of the year within the era.
+ * This field is typically used with {@link #ERA}.
+ * <p>
+ * The standard mental model for a date is based on three concepts - year, month and day.
+ * These map onto the {@code YEAR}, {@code MONTH_OF_YEAR} and {@code DAY_OF_MONTH} fields.
+ * Note that there is no reference to eras.
+ * The full model for a date requires four concepts - era, year, month and day. These map onto
+ * the {@code ERA}, {@code YEAR_OF_ERA}, {@code MONTH_OF_YEAR} and {@code DAY_OF_MONTH} fields.
+ * Whether this field or {@code YEAR} is used depends on which mental model is being used.
+ * See {@link ChronoLocalDate} for more discussion on this topic.
+ * <p>
+ * In the default ISO calendar system, there are two eras defined, 'BCE' and 'CE'.
+ * The era 'CE' is the one currently in use and year-of-era runs from 1 to the maximum value.
+ * The era 'BCE' is the previous era, and the year-of-era runs backwards.
+ * <p>
+ * For example, subtracting a year each time yield the following:<br>
+ * - year-proleptic 2 = 'CE' year-of-era 2<br>
+ * - year-proleptic 1 = 'CE' year-of-era 1<br>
+ * - year-proleptic 0 = 'BCE' year-of-era 1<br>
+ * - year-proleptic -1 = 'BCE' year-of-era 2<br>
+ * <p>
+ * Note that the ISO-8601 standard does not actually define eras.
+ * Note also that the ISO eras do not align with the well-known AD/BC eras due to the
+ * change between the Julian and Gregorian calendar systems.
+ * <p>
+ * Non-ISO calendar systems should implement this field using the most recognized
+ * year-of-era value for users of the calendar system.
+ * Since most calendar systems have only two eras, the year-of-era numbering approach
+ * will typically be the same as that used by the ISO calendar system.
+ * The year-of-era value should typically always be positive, however this is not required.
+ */
+ YEAR_OF_ERA("YearOfEra", YEARS, FOREVER, ValueRange.of(1, Year.MAX_VALUE, Year.MAX_VALUE + 1)),
+ /**
+ * The proleptic year, such as 2012.
+ * <p>
+ * This represents the concept of the year, counting sequentially and using negative numbers.
+ * The proleptic year is not interpreted in terms of the era.
+ * See {@link #YEAR_OF_ERA} for an example showing the mapping from proleptic year to year-of-era.
+ * <p>
+ * The standard mental model for a date is based on three concepts - year, month and day.
+ * These map onto the {@code YEAR}, {@code MONTH_OF_YEAR} and {@code DAY_OF_MONTH} fields.
+ * Note that there is no reference to eras.
+ * The full model for a date requires four concepts - era, year, month and day. These map onto
+ * the {@code ERA}, {@code YEAR_OF_ERA}, {@code MONTH_OF_YEAR} and {@code DAY_OF_MONTH} fields.
+ * Whether this field or {@code YEAR_OF_ERA} is used depends on which mental model is being used.
+ * See {@link ChronoLocalDate} for more discussion on this topic.
+ * <p>
+ * Non-ISO calendar systems should implement this field as follows.
+ * If the calendar system has only two eras, before and after a fixed date, then the
+ * proleptic-year value must be the same as the year-of-era value for the later era,
+ * and increasingly negative for the earlier era.
+ * If the calendar system has more than two eras, then the proleptic-year value may be
+ * defined with any appropriate value, although defining it to be the same as ISO may be
+ * the best option.
+ */
+ YEAR("Year", YEARS, FOREVER, ValueRange.of(Year.MIN_VALUE, Year.MAX_VALUE), "year"),
+ /**
+ * The era.
+ * <p>
+ * This represents the concept of the era, which is the largest division of the time-line.
+ * This field is typically used with {@link #YEAR_OF_ERA}.
+ * <p>
+ * In the default ISO calendar system, there are two eras defined, 'BCE' and 'CE'.
+ * The era 'CE' is the one currently in use and year-of-era runs from 1 to the maximum value.
+ * The era 'BCE' is the previous era, and the year-of-era runs backwards.
+ * See {@link #YEAR_OF_ERA} for a full example.
+ * <p>
+ * Non-ISO calendar systems should implement this field to define eras.
+ * The value of the era that was active on 1970-01-01 (ISO) must be assigned the value 1.
+ * Earlier eras must have sequentially smaller values.
+ * Later eras must have sequentially larger values,
+ */
+ ERA("Era", ERAS, FOREVER, ValueRange.of(0, 1), "era"),
+ /**
+ * The instant epoch-seconds.
+ * <p>
+ * This represents the concept of the sequential count of seconds where
+ * 1970-01-01T00:00Z (ISO) is zero.
+ * This field may be used with {@link #NANO_OF_SECOND} to represent the fraction of the second.
+ * <p>
+ * An {@link Instant} represents an instantaneous point on the time-line.
+ * On their own, an instant has insufficient information to allow a local date-time to be obtained.
+ * Only when paired with an offset or time-zone can the local date or time be calculated.
+ * <p>
+ * This field is strictly defined to have the same meaning in all calendar systems.
+ * This is necessary to ensure interoperation between calendars.
+ */
+ INSTANT_SECONDS("InstantSeconds", SECONDS, FOREVER, ValueRange.of(Long.MIN_VALUE, Long.MAX_VALUE)),
+ /**
+ * The offset from UTC/Greenwich.
+ * <p>
+ * This represents the concept of the offset in seconds of local time from UTC/Greenwich.
+ * <p>
+ * A {@link ZoneOffset} represents the period of time that local time differs from UTC/Greenwich.
+ * This is usually a fixed number of hours and minutes.
+ * It is equivalent to the {@link ZoneOffset#getTotalSeconds() total amount} of the offset in seconds.
+ * For example, during the winter Paris has an offset of {@code +01:00}, which is 3600 seconds.
+ * <p>
+ * This field is strictly defined to have the same meaning in all calendar systems.
+ * This is necessary to ensure interoperation between calendars.
+ */
+ OFFSET_SECONDS("OffsetSeconds", SECONDS, FOREVER, ValueRange.of(-18 * 3600, 18 * 3600));
+
+ private final String name;
+ private final TemporalUnit baseUnit;
+ private final TemporalUnit rangeUnit;
+ private final ValueRange range;
+ private final String displayNameKey;
+
+ private ChronoField(String name, TemporalUnit baseUnit, TemporalUnit rangeUnit, ValueRange range) {
+ this.name = name;
+ this.baseUnit = baseUnit;
+ this.rangeUnit = rangeUnit;
+ this.range = range;
+ this.displayNameKey = null;
+ }
+
+ private ChronoField(String name, TemporalUnit baseUnit, TemporalUnit rangeUnit,
+ ValueRange range, String displayNameKey) {
+ this.name = name;
+ this.baseUnit = baseUnit;
+ this.rangeUnit = rangeUnit;
+ this.range = range;
+ this.displayNameKey = displayNameKey;
+ }
+
+ @Override
+ public String getDisplayName(Locale locale) {
+ Objects.requireNonNull(locale, "locale");
+ if (displayNameKey == null) {
+ return name;
+ }
+
+ // Android-changed: use ICU names.
+ DateTimePatternGenerator generator = DateTimePatternGenerator
+ .getFrozenInstance(ULocale.forLocale(locale));
+ String icuName = generator.getAppendItemName(getIcuFieldNumber(this));
+ return icuName != null && !icuName.isEmpty() ? icuName : name;
+ }
+
+ /**
+ * @return the field id according to {@link DateTimePatternGenerator} for the field.
+ */
+ private static int getIcuFieldNumber(ChronoField field) {
+ switch (field) {
+ case SECOND_OF_MINUTE:
+ return DateTimePatternGenerator.SECOND;
+ case MINUTE_OF_HOUR:
+ return DateTimePatternGenerator.MINUTE;
+ case HOUR_OF_DAY:
+ return DateTimePatternGenerator.HOUR;
+ case AMPM_OF_DAY:
+ return DateTimePatternGenerator.DAYPERIOD;
+ case DAY_OF_WEEK:
+ return DateTimePatternGenerator.WEEKDAY;
+ case DAY_OF_MONTH:
+ return DateTimePatternGenerator.DAY;
+ case MONTH_OF_YEAR:
+ return DateTimePatternGenerator.MONTH;
+ case YEAR:
+ return DateTimePatternGenerator.YEAR;
+ case ERA:
+ return DateTimePatternGenerator.ERA;
+ default:
+ throw new IllegalArgumentException("Unexpected ChronoField " + field.name());
+ }
+ }
+
+ @Override
+ public TemporalUnit getBaseUnit() {
+ return baseUnit;
+ }
+
+ @Override
+ public TemporalUnit getRangeUnit() {
+ return rangeUnit;
+ }
+
+ /**
+ * Gets the range of valid values for the field.
+ * <p>
+ * All fields can be expressed as a {@code long} integer.
+ * This method returns an object that describes the valid range for that value.
+ * <p>
+ * This method returns the range of the field in the ISO-8601 calendar system.
+ * This range may be incorrect for other calendar systems.
+ * Use {@link Chronology#range(ChronoField)} to access the correct range
+ * for a different calendar system.
+ * <p>
+ * Note that the result only describes the minimum and maximum valid values
+ * and it is important not to read too much into them. For example, there
+ * could be values within the range that are invalid for the field.
+ *
+ * @return the range of valid values for the field, not null
+ */
+ @Override
+ public ValueRange range() {
+ return range;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if this field represents a component of a date.
+ * <p>
+ * Fields from day-of-week to era are date-based.
+ *
+ * @return true if it is a component of a date
+ */
+ @Override
+ public boolean isDateBased() {
+ return ordinal() >= DAY_OF_WEEK.ordinal() && ordinal() <= ERA.ordinal();
+ }
+
+ /**
+ * Checks if this field represents a component of a time.
+ * <p>
+ * Fields from nano-of-second to am-pm-of-day are time-based.
+ *
+ * @return true if it is a component of a time
+ */
+ @Override
+ public boolean isTimeBased() {
+ return ordinal() < DAY_OF_WEEK.ordinal();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks that the specified value is valid for this field.
+ * <p>
+ * This validates that the value is within the outer range of valid values
+ * returned by {@link #range()}.
+ * <p>
+ * This method checks against the range of the field in the ISO-8601 calendar system.
+ * This range may be incorrect for other calendar systems.
+ * Use {@link Chronology#range(ChronoField)} to access the correct range
+ * for a different calendar system.
+ *
+ * @param value the value to check
+ * @return the value that was passed in
+ */
+ public long checkValidValue(long value) {
+ return range().checkValidValue(value, this);
+ }
+
+ /**
+ * Checks that the specified value is valid and fits in an {@code int}.
+ * <p>
+ * This validates that the value is within the outer range of valid values
+ * returned by {@link #range()}.
+ * It also checks that all valid values are within the bounds of an {@code int}.
+ * <p>
+ * This method checks against the range of the field in the ISO-8601 calendar system.
+ * This range may be incorrect for other calendar systems.
+ * Use {@link Chronology#range(ChronoField)} to access the correct range
+ * for a different calendar system.
+ *
+ * @param value the value to check
+ * @return the value that was passed in
+ */
+ public int checkValidIntValue(long value) {
+ return range().checkValidIntValue(value, this);
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ public boolean isSupportedBy(TemporalAccessor temporal) {
+ return temporal.isSupported(this);
+ }
+
+ @Override
+ public ValueRange rangeRefinedBy(TemporalAccessor temporal) {
+ return temporal.range(this);
+ }
+
+ @Override
+ public long getFrom(TemporalAccessor temporal) {
+ return temporal.getLong(this);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <R extends Temporal> R adjustInto(R temporal, long newValue) {
+ return (R) temporal.with(this, newValue);
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ public String toString() {
+ return name;
+ }
+
+}
diff --git a/java/time/temporal/ChronoUnit.java b/java/time/temporal/ChronoUnit.java
new file mode 100644
index 0000000..c704327
--- /dev/null
+++ b/java/time/temporal/ChronoUnit.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 2012, 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) 2012, 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.temporal;
+
+import java.time.Duration;
+
+/**
+ * A standard set of date periods units.
+ * <p>
+ * This set of units provide unit-based access to manipulate a date, time or date-time.
+ * The standard set of units can be extended by implementing {@link TemporalUnit}.
+ * <p>
+ * These units are intended to be applicable in multiple calendar systems.
+ * For example, most non-ISO calendar systems define units of years, months and days,
+ * just with slightly different rules.
+ * The documentation of each unit explains how it operates.
+ *
+ * @implSpec
+ * This is a final, immutable and thread-safe enum.
+ *
+ * @since 1.8
+ */
+public enum ChronoUnit implements TemporalUnit {
+
+ /**
+ * Unit that represents the concept of a nanosecond, the smallest supported unit of time.
+ * For the ISO calendar system, it is equal to the 1,000,000,000th part of the second unit.
+ */
+ NANOS("Nanos", Duration.ofNanos(1)),
+ /**
+ * Unit that represents the concept of a microsecond.
+ * For the ISO calendar system, it is equal to the 1,000,000th part of the second unit.
+ */
+ MICROS("Micros", Duration.ofNanos(1000)),
+ /**
+ * Unit that represents the concept of a millisecond.
+ * For the ISO calendar system, it is equal to the 1000th part of the second unit.
+ */
+ MILLIS("Millis", Duration.ofNanos(1000_000)),
+ /**
+ * Unit that represents the concept of a second.
+ * For the ISO calendar system, it is equal to the second in the SI system
+ * of units, except around a leap-second.
+ */
+ SECONDS("Seconds", Duration.ofSeconds(1)),
+ /**
+ * Unit that represents the concept of a minute.
+ * For the ISO calendar system, it is equal to 60 seconds.
+ */
+ MINUTES("Minutes", Duration.ofSeconds(60)),
+ /**
+ * Unit that represents the concept of an hour.
+ * For the ISO calendar system, it is equal to 60 minutes.
+ */
+ HOURS("Hours", Duration.ofSeconds(3600)),
+ /**
+ * Unit that represents the concept of half a day, as used in AM/PM.
+ * For the ISO calendar system, it is equal to 12 hours.
+ */
+ HALF_DAYS("HalfDays", Duration.ofSeconds(43200)),
+ /**
+ * Unit that represents the concept of a day.
+ * For the ISO calendar system, it is the standard day from midnight to midnight.
+ * The estimated duration of a day is {@code 24 Hours}.
+ * <p>
+ * When used with other calendar systems it must correspond to the day defined by
+ * the rising and setting of the Sun on Earth. It is not required that days begin
+ * at midnight - when converting between calendar systems, the date should be
+ * equivalent at midday.
+ */
+ DAYS("Days", Duration.ofSeconds(86400)),
+ /**
+ * Unit that represents the concept of a week.
+ * For the ISO calendar system, it is equal to 7 days.
+ * <p>
+ * When used with other calendar systems it must correspond to an integral number of days.
+ */
+ WEEKS("Weeks", Duration.ofSeconds(7 * 86400L)),
+ /**
+ * Unit that represents the concept of a month.
+ * For the ISO calendar system, the length of the month varies by month-of-year.
+ * The estimated duration of a month is one twelfth of {@code 365.2425 Days}.
+ * <p>
+ * When used with other calendar systems it must correspond to an integral number of days.
+ */
+ MONTHS("Months", Duration.ofSeconds(31556952L / 12)),
+ /**
+ * Unit that represents the concept of a year.
+ * For the ISO calendar system, it is equal to 12 months.
+ * The estimated duration of a year is {@code 365.2425 Days}.
+ * <p>
+ * When used with other calendar systems it must correspond to an integral number of days
+ * or months roughly equal to a year defined by the passage of the Earth around the Sun.
+ */
+ YEARS("Years", Duration.ofSeconds(31556952L)),
+ /**
+ * Unit that represents the concept of a decade.
+ * For the ISO calendar system, it is equal to 10 years.
+ * <p>
+ * When used with other calendar systems it must correspond to an integral number of days
+ * and is normally an integral number of years.
+ */
+ DECADES("Decades", Duration.ofSeconds(31556952L * 10L)),
+ /**
+ * Unit that represents the concept of a century.
+ * For the ISO calendar system, it is equal to 100 years.
+ * <p>
+ * When used with other calendar systems it must correspond to an integral number of days
+ * and is normally an integral number of years.
+ */
+ CENTURIES("Centuries", Duration.ofSeconds(31556952L * 100L)),
+ /**
+ * Unit that represents the concept of a millennium.
+ * For the ISO calendar system, it is equal to 1000 years.
+ * <p>
+ * When used with other calendar systems it must correspond to an integral number of days
+ * and is normally an integral number of years.
+ */
+ MILLENNIA("Millennia", Duration.ofSeconds(31556952L * 1000L)),
+ /**
+ * Unit that represents the concept of an era.
+ * The ISO calendar system doesn't have eras thus it is impossible to add
+ * an era to a date or date-time.
+ * The estimated duration of the era is artificially defined as {@code 1,000,000,000 Years}.
+ * <p>
+ * When used with other calendar systems there are no restrictions on the unit.
+ */
+ ERAS("Eras", Duration.ofSeconds(31556952L * 1000_000_000L)),
+ /**
+ * Artificial unit that represents the concept of forever.
+ * This is primarily used with {@link TemporalField} to represent unbounded fields
+ * such as the year or era.
+ * The estimated duration of the era is artificially defined as the largest duration
+ * supported by {@code Duration}.
+ */
+ FOREVER("Forever", Duration.ofSeconds(Long.MAX_VALUE, 999_999_999));
+
+ private final String name;
+ private final Duration duration;
+
+ private ChronoUnit(String name, Duration estimatedDuration) {
+ this.name = name;
+ this.duration = estimatedDuration;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the estimated duration of this unit in the ISO calendar system.
+ * <p>
+ * All of the units in this class have an estimated duration.
+ * Days vary due to daylight saving time, while months have different lengths.
+ *
+ * @return the estimated duration of this unit, not null
+ */
+ @Override
+ public Duration getDuration() {
+ return duration;
+ }
+
+ /**
+ * Checks if the duration of the unit is an estimate.
+ * <p>
+ * All time units in this class are considered to be accurate, while all date
+ * units in this class are considered to be estimated.
+ * <p>
+ * This definition ignores leap seconds, but considers that Days vary due to
+ * daylight saving time and months have different lengths.
+ *
+ * @return true if the duration is estimated, false if accurate
+ */
+ @Override
+ public boolean isDurationEstimated() {
+ return this.compareTo(DAYS) >= 0;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if this unit is a date unit.
+ * <p>
+ * All units from days to eras inclusive are date-based.
+ * Time-based units and {@code FOREVER} return false.
+ *
+ * @return true if a date unit, false if a time unit
+ */
+ @Override
+ public boolean isDateBased() {
+ return this.compareTo(DAYS) >= 0 && this != FOREVER;
+ }
+
+ /**
+ * Checks if this unit is a time unit.
+ * <p>
+ * All units from nanos to half-days inclusive are time-based.
+ * Date-based units and {@code FOREVER} return false.
+ *
+ * @return true if a time unit, false if a date unit
+ */
+ @Override
+ public boolean isTimeBased() {
+ return this.compareTo(DAYS) < 0;
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ public boolean isSupportedBy(Temporal temporal) {
+ return temporal.isSupported(this);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <R extends Temporal> R addTo(R temporal, long amount) {
+ return (R) temporal.plus(amount, this);
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ public long between(Temporal temporal1Inclusive, Temporal temporal2Exclusive) {
+ return temporal1Inclusive.until(temporal2Exclusive, this);
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ public String toString() {
+ return name;
+ }
+
+}
diff --git a/java/time/temporal/IsoFields.java b/java/time/temporal/IsoFields.java
new file mode 100644
index 0000000..8b6f237
--- /dev/null
+++ b/java/time/temporal/IsoFields.java
@@ -0,0 +1,725 @@
+/*
+ * Copyright (c) 2012, 2015, 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) 2011-2012, 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.temporal;
+
+import android.icu.text.DateTimePatternGenerator;
+import android.icu.util.ULocale;
+
+import static java.time.DayOfWeek.THURSDAY;
+import static java.time.DayOfWeek.WEDNESDAY;
+import static java.time.temporal.ChronoField.DAY_OF_WEEK;
+import static java.time.temporal.ChronoField.DAY_OF_YEAR;
+import static java.time.temporal.ChronoField.EPOCH_DAY;
+import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
+import static java.time.temporal.ChronoField.YEAR;
+import static java.time.temporal.ChronoUnit.DAYS;
+import static java.time.temporal.ChronoUnit.FOREVER;
+import static java.time.temporal.ChronoUnit.MONTHS;
+import static java.time.temporal.ChronoUnit.WEEKS;
+import static java.time.temporal.ChronoUnit.YEARS;
+
+import java.time.DateTimeException;
+import java.time.Duration;
+import java.time.LocalDate;
+import java.time.chrono.ChronoLocalDate;
+import java.time.chrono.Chronology;
+import java.time.chrono.IsoChronology;
+import java.time.format.ResolverStyle;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Fields and units specific to the ISO-8601 calendar system,
+ * including quarter-of-year and week-based-year.
+ * <p>
+ * This class defines fields and units that are specific to the ISO calendar system.
+ *
+ * <h3>Quarter of year</h3>
+ * The ISO-8601 standard is based on the standard civic 12 month year.
+ * This is commonly divided into four quarters, often abbreviated as Q1, Q2, Q3 and Q4.
+ * <p>
+ * January, February and March are in Q1.
+ * April, May and June are in Q2.
+ * July, August and September are in Q3.
+ * October, November and December are in Q4.
+ * <p>
+ * The complete date is expressed using three fields:
+ * <ul>
+ * <li>{@link #DAY_OF_QUARTER DAY_OF_QUARTER} - the day within the quarter, from 1 to 90, 91 or 92
+ * <li>{@link #QUARTER_OF_YEAR QUARTER_OF_YEAR} - the week within the week-based-year
+ * <li>{@link ChronoField#YEAR YEAR} - the standard ISO year
+ * </ul>
+ *
+ * <h3>Week based years</h3>
+ * The ISO-8601 standard was originally intended as a data interchange format,
+ * defining a string format for dates and times. However, it also defines an
+ * alternate way of expressing the date, based on the concept of week-based-year.
+ * <p>
+ * The date is expressed using three fields:
+ * <ul>
+ * <li>{@link ChronoField#DAY_OF_WEEK DAY_OF_WEEK} - the standard field defining the
+ * day-of-week from Monday (1) to Sunday (7)
+ * <li>{@link #WEEK_OF_WEEK_BASED_YEAR} - the week within the week-based-year
+ * <li>{@link #WEEK_BASED_YEAR WEEK_BASED_YEAR} - the week-based-year
+ * </ul>
+ * The week-based-year itself is defined relative to the standard ISO proleptic year.
+ * It differs from the standard year in that it always starts on a Monday.
+ * <p>
+ * The first week of a week-based-year is the first Monday-based week of the standard
+ * ISO year that has at least 4 days in the new year.
+ * <ul>
+ * <li>If January 1st is Monday then week 1 starts on January 1st
+ * <li>If January 1st is Tuesday then week 1 starts on December 31st of the previous standard year
+ * <li>If January 1st is Wednesday then week 1 starts on December 30th of the previous standard year
+ * <li>If January 1st is Thursday then week 1 starts on December 29th of the previous standard year
+ * <li>If January 1st is Friday then week 1 starts on January 4th
+ * <li>If January 1st is Saturday then week 1 starts on January 3rd
+ * <li>If January 1st is Sunday then week 1 starts on January 2nd
+ * </ul>
+ * There are 52 weeks in most week-based years, however on occasion there are 53 weeks.
+ * <p>
+ * For example:
+ *
+ * <table cellpadding="0" cellspacing="3" border="0" style="text-align: left; width: 50%;">
+ * <caption>Examples of Week based Years</caption>
+ * <tr><th>Date</th><th>Day-of-week</th><th>Field values</th></tr>
+ * <tr><th>2008-12-28</th><td>Sunday</td><td>Week 52 of week-based-year 2008</td></tr>
+ * <tr><th>2008-12-29</th><td>Monday</td><td>Week 1 of week-based-year 2009</td></tr>
+ * <tr><th>2008-12-31</th><td>Wednesday</td><td>Week 1 of week-based-year 2009</td></tr>
+ * <tr><th>2009-01-01</th><td>Thursday</td><td>Week 1 of week-based-year 2009</td></tr>
+ * <tr><th>2009-01-04</th><td>Sunday</td><td>Week 1 of week-based-year 2009</td></tr>
+ * <tr><th>2009-01-05</th><td>Monday</td><td>Week 2 of week-based-year 2009</td></tr>
+ * </table>
+ *
+ * @implSpec
+ * <p>
+ * This class is immutable and thread-safe.
+ *
+ * @since 1.8
+ */
+public final class IsoFields {
+
+ /**
+ * The field that represents the day-of-quarter.
+ * <p>
+ * This field allows the day-of-quarter value to be queried and set.
+ * The day-of-quarter has values from 1 to 90 in Q1 of a standard year, from 1 to 91
+ * in Q1 of a leap year, from 1 to 91 in Q2 and from 1 to 92 in Q3 and Q4.
+ * <p>
+ * The day-of-quarter can only be calculated if the day-of-year, month-of-year and year
+ * are available.
+ * <p>
+ * When setting this field, the value is allowed to be partially lenient, taking any
+ * value from 1 to 92. If the quarter has less than 92 days, then day 92, and
+ * potentially day 91, is in the following quarter.
+ * <p>
+ * In the resolving phase of parsing, a date can be created from a year,
+ * quarter-of-year and day-of-quarter.
+ * <p>
+ * In {@linkplain ResolverStyle#STRICT strict mode}, all three fields are
+ * validated against their range of valid values. The day-of-quarter field
+ * is validated from 1 to 90, 91 or 92 depending on the year and quarter.
+ * <p>
+ * In {@linkplain ResolverStyle#SMART smart mode}, all three fields are
+ * validated against their range of valid values. The day-of-quarter field is
+ * validated between 1 and 92, ignoring the actual range based on the year and quarter.
+ * If the day-of-quarter exceeds the actual range by one day, then the resulting date
+ * is one day later. If the day-of-quarter exceeds the actual range by two days,
+ * then the resulting date is two days later.
+ * <p>
+ * In {@linkplain ResolverStyle#LENIENT lenient mode}, only the year is validated
+ * against the range of valid values. The resulting date is calculated equivalent to
+ * the following three stage approach. First, create a date on the first of January
+ * in the requested year. Then take the quarter-of-year, subtract one, and add the
+ * amount in quarters to the date. Finally, take the day-of-quarter, subtract one,
+ * and add the amount in days to the date.
+ * <p>
+ * This unit is an immutable and thread-safe singleton.
+ */
+ public static final TemporalField DAY_OF_QUARTER = Field.DAY_OF_QUARTER;
+ /**
+ * The field that represents the quarter-of-year.
+ * <p>
+ * This field allows the quarter-of-year value to be queried and set.
+ * The quarter-of-year has values from 1 to 4.
+ * <p>
+ * The quarter-of-year can only be calculated if the month-of-year is available.
+ * <p>
+ * In the resolving phase of parsing, a date can be created from a year,
+ * quarter-of-year and day-of-quarter.
+ * See {@link #DAY_OF_QUARTER} for details.
+ * <p>
+ * This unit is an immutable and thread-safe singleton.
+ */
+ public static final TemporalField QUARTER_OF_YEAR = Field.QUARTER_OF_YEAR;
+ /**
+ * The field that represents the week-of-week-based-year.
+ * <p>
+ * This field allows the week of the week-based-year value to be queried and set.
+ * The week-of-week-based-year has values from 1 to 52, or 53 if the
+ * week-based-year has 53 weeks.
+ * <p>
+ * In the resolving phase of parsing, a date can be created from a
+ * week-based-year, week-of-week-based-year and day-of-week.
+ * <p>
+ * In {@linkplain ResolverStyle#STRICT strict mode}, all three fields are
+ * validated against their range of valid values. The week-of-week-based-year
+ * field is validated from 1 to 52 or 53 depending on the week-based-year.
+ * <p>
+ * In {@linkplain ResolverStyle#SMART smart mode}, all three fields are
+ * validated against their range of valid values. The week-of-week-based-year
+ * field is validated between 1 and 53, ignoring the week-based-year.
+ * If the week-of-week-based-year is 53, but the week-based-year only has
+ * 52 weeks, then the resulting date is in week 1 of the following week-based-year.
+ * <p>
+ * In {@linkplain ResolverStyle#LENIENT lenient mode}, only the week-based-year
+ * is validated against the range of valid values. If the day-of-week is outside
+ * the range 1 to 7, then the resulting date is adjusted by a suitable number of
+ * weeks to reduce the day-of-week to the range 1 to 7. If the week-of-week-based-year
+ * value is outside the range 1 to 52, then any excess weeks are added or subtracted
+ * from the resulting date.
+ * <p>
+ * This unit is an immutable and thread-safe singleton.
+ */
+ public static final TemporalField WEEK_OF_WEEK_BASED_YEAR = Field.WEEK_OF_WEEK_BASED_YEAR;
+ /**
+ * The field that represents the week-based-year.
+ * <p>
+ * This field allows the week-based-year value to be queried and set.
+ * <p>
+ * The field has a range that matches {@link LocalDate#MAX} and {@link LocalDate#MIN}.
+ * <p>
+ * In the resolving phase of parsing, a date can be created from a
+ * week-based-year, week-of-week-based-year and day-of-week.
+ * See {@link #WEEK_OF_WEEK_BASED_YEAR} for details.
+ * <p>
+ * This unit is an immutable and thread-safe singleton.
+ */
+ public static final TemporalField WEEK_BASED_YEAR = Field.WEEK_BASED_YEAR;
+ /**
+ * The unit that represents week-based-years for the purpose of addition and subtraction.
+ * <p>
+ * This allows a number of week-based-years to be added to, or subtracted from, a date.
+ * The unit is equal to either 52 or 53 weeks.
+ * The estimated duration of a week-based-year is the same as that of a standard ISO
+ * year at {@code 365.2425 Days}.
+ * <p>
+ * The rules for addition add the number of week-based-years to the existing value
+ * for the week-based-year field. If the resulting week-based-year only has 52 weeks,
+ * then the date will be in week 1 of the following week-based-year.
+ * <p>
+ * This unit is an immutable and thread-safe singleton.
+ */
+ public static final TemporalUnit WEEK_BASED_YEARS = Unit.WEEK_BASED_YEARS;
+ /**
+ * Unit that represents the concept of a quarter-year.
+ * For the ISO calendar system, it is equal to 3 months.
+ * The estimated duration of a quarter-year is one quarter of {@code 365.2425 Days}.
+ * <p>
+ * This unit is an immutable and thread-safe singleton.
+ */
+ public static final TemporalUnit QUARTER_YEARS = Unit.QUARTER_YEARS;
+
+ /**
+ * Restricted constructor.
+ */
+ private IsoFields() {
+ throw new AssertionError("Not instantiable");
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Implementation of the field.
+ */
+ private static enum Field implements TemporalField {
+ DAY_OF_QUARTER {
+ @Override
+ public TemporalUnit getBaseUnit() {
+ return DAYS;
+ }
+ @Override
+ public TemporalUnit getRangeUnit() {
+ return QUARTER_YEARS;
+ }
+ @Override
+ public ValueRange range() {
+ return ValueRange.of(1, 90, 92);
+ }
+ @Override
+ public boolean isSupportedBy(TemporalAccessor temporal) {
+ return temporal.isSupported(DAY_OF_YEAR) && temporal.isSupported(MONTH_OF_YEAR) &&
+ temporal.isSupported(YEAR) && isIso(temporal);
+ }
+ @Override
+ public ValueRange rangeRefinedBy(TemporalAccessor temporal) {
+ if (isSupportedBy(temporal) == false) {
+ throw new UnsupportedTemporalTypeException("Unsupported field: DayOfQuarter");
+ }
+ long qoy = temporal.getLong(QUARTER_OF_YEAR);
+ if (qoy == 1) {
+ long year = temporal.getLong(YEAR);
+ return (IsoChronology.INSTANCE.isLeapYear(year) ? ValueRange.of(1, 91) : ValueRange.of(1, 90));
+ } else if (qoy == 2) {
+ return ValueRange.of(1, 91);
+ } else if (qoy == 3 || qoy == 4) {
+ return ValueRange.of(1, 92);
+ } // else value not from 1 to 4, so drop through
+ return range();
+ }
+ @Override
+ public long getFrom(TemporalAccessor temporal) {
+ if (isSupportedBy(temporal) == false) {
+ throw new UnsupportedTemporalTypeException("Unsupported field: DayOfQuarter");
+ }
+ int doy = temporal.get(DAY_OF_YEAR);
+ int moy = temporal.get(MONTH_OF_YEAR);
+ long year = temporal.getLong(YEAR);
+ return doy - QUARTER_DAYS[((moy - 1) / 3) + (IsoChronology.INSTANCE.isLeapYear(year) ? 4 : 0)];
+ }
+ @SuppressWarnings("unchecked")
+ @Override
+ public <R extends Temporal> R adjustInto(R temporal, long newValue) {
+ // calls getFrom() to check if supported
+ long curValue = getFrom(temporal);
+ range().checkValidValue(newValue, this); // leniently check from 1 to 92 TODO: check
+ return (R) temporal.with(DAY_OF_YEAR, temporal.getLong(DAY_OF_YEAR) + (newValue - curValue));
+ }
+ @Override
+ public ChronoLocalDate resolve(
+ Map<TemporalField, Long> fieldValues, TemporalAccessor partialTemporal, ResolverStyle resolverStyle) {
+ Long yearLong = fieldValues.get(YEAR);
+ Long qoyLong = fieldValues.get(QUARTER_OF_YEAR);
+ if (yearLong == null || qoyLong == null) {
+ return null;
+ }
+ int y = YEAR.checkValidIntValue(yearLong); // always validate
+ long doq = fieldValues.get(DAY_OF_QUARTER);
+ ensureIso(partialTemporal);
+ LocalDate date;
+ if (resolverStyle == ResolverStyle.LENIENT) {
+ date = LocalDate.of(y, 1, 1).plusMonths(Math.multiplyExact(Math.subtractExact(qoyLong, 1), 3));
+ doq = Math.subtractExact(doq, 1);
+ } else {
+ int qoy = QUARTER_OF_YEAR.range().checkValidIntValue(qoyLong, QUARTER_OF_YEAR); // validated
+ date = LocalDate.of(y, ((qoy - 1) * 3) + 1, 1);
+ if (doq < 1 || doq > 90) {
+ if (resolverStyle == ResolverStyle.STRICT) {
+ rangeRefinedBy(date).checkValidValue(doq, this); // only allow exact range
+ } else { // SMART
+ range().checkValidValue(doq, this); // allow 1-92 rolling into next quarter
+ }
+ }
+ doq--;
+ }
+ fieldValues.remove(this);
+ fieldValues.remove(YEAR);
+ fieldValues.remove(QUARTER_OF_YEAR);
+ return date.plusDays(doq);
+ }
+ @Override
+ public String toString() {
+ return "DayOfQuarter";
+ }
+ },
+ QUARTER_OF_YEAR {
+ @Override
+ public TemporalUnit getBaseUnit() {
+ return QUARTER_YEARS;
+ }
+ @Override
+ public TemporalUnit getRangeUnit() {
+ return YEARS;
+ }
+ @Override
+ public ValueRange range() {
+ return ValueRange.of(1, 4);
+ }
+ @Override
+ public boolean isSupportedBy(TemporalAccessor temporal) {
+ return temporal.isSupported(MONTH_OF_YEAR) && isIso(temporal);
+ }
+ @Override
+ public long getFrom(TemporalAccessor temporal) {
+ if (isSupportedBy(temporal) == false) {
+ throw new UnsupportedTemporalTypeException("Unsupported field: QuarterOfYear");
+ }
+ long moy = temporal.getLong(MONTH_OF_YEAR);
+ return ((moy + 2) / 3);
+ }
+ @SuppressWarnings("unchecked")
+ @Override
+ public <R extends Temporal> R adjustInto(R temporal, long newValue) {
+ // calls getFrom() to check if supported
+ long curValue = getFrom(temporal);
+ range().checkValidValue(newValue, this); // strictly check from 1 to 4
+ return (R) temporal.with(MONTH_OF_YEAR, temporal.getLong(MONTH_OF_YEAR) + (newValue - curValue) * 3);
+ }
+ @Override
+ public String toString() {
+ return "QuarterOfYear";
+ }
+ },
+ WEEK_OF_WEEK_BASED_YEAR {
+ @Override
+ public String getDisplayName(Locale locale) {
+ Objects.requireNonNull(locale, "locale");
+ // Android-changed: Use ICU name values.
+ DateTimePatternGenerator dateTimePatternGenerator = DateTimePatternGenerator
+ .getFrozenInstance(ULocale.forLocale(locale));
+ String icuName = dateTimePatternGenerator
+ .getAppendItemName(DateTimePatternGenerator.WEEK_OF_YEAR);
+ return icuName != null && !icuName.isEmpty() ? icuName : toString();
+ }
+
+ @Override
+ public TemporalUnit getBaseUnit() {
+ return WEEKS;
+ }
+ @Override
+ public TemporalUnit getRangeUnit() {
+ return WEEK_BASED_YEARS;
+ }
+ @Override
+ public ValueRange range() {
+ return ValueRange.of(1, 52, 53);
+ }
+ @Override
+ public boolean isSupportedBy(TemporalAccessor temporal) {
+ return temporal.isSupported(EPOCH_DAY) && isIso(temporal);
+ }
+ @Override
+ public ValueRange rangeRefinedBy(TemporalAccessor temporal) {
+ if (isSupportedBy(temporal) == false) {
+ throw new UnsupportedTemporalTypeException("Unsupported field: WeekOfWeekBasedYear");
+ }
+ return getWeekRange(LocalDate.from(temporal));
+ }
+ @Override
+ public long getFrom(TemporalAccessor temporal) {
+ if (isSupportedBy(temporal) == false) {
+ throw new UnsupportedTemporalTypeException("Unsupported field: WeekOfWeekBasedYear");
+ }
+ return getWeek(LocalDate.from(temporal));
+ }
+ @SuppressWarnings("unchecked")
+ @Override
+ public <R extends Temporal> R adjustInto(R temporal, long newValue) {
+ // calls getFrom() to check if supported
+ range().checkValidValue(newValue, this); // lenient range
+ return (R) temporal.plus(Math.subtractExact(newValue, getFrom(temporal)), WEEKS);
+ }
+ @Override
+ public ChronoLocalDate resolve(
+ Map<TemporalField, Long> fieldValues, TemporalAccessor partialTemporal, ResolverStyle resolverStyle) {
+ Long wbyLong = fieldValues.get(WEEK_BASED_YEAR);
+ Long dowLong = fieldValues.get(DAY_OF_WEEK);
+ if (wbyLong == null || dowLong == null) {
+ return null;
+ }
+ int wby = WEEK_BASED_YEAR.range().checkValidIntValue(wbyLong, WEEK_BASED_YEAR); // always validate
+ long wowby = fieldValues.get(WEEK_OF_WEEK_BASED_YEAR);
+ ensureIso(partialTemporal);
+ LocalDate date = LocalDate.of(wby, 1, 4);
+ if (resolverStyle == ResolverStyle.LENIENT) {
+ long dow = dowLong; // unvalidated
+ if (dow > 7) {
+ date = date.plusWeeks((dow - 1) / 7);
+ dow = ((dow - 1) % 7) + 1;
+ } else if (dow < 1) {
+ date = date.plusWeeks(Math.subtractExact(dow, 7) / 7);
+ dow = ((dow + 6) % 7) + 1;
+ }
+ date = date.plusWeeks(Math.subtractExact(wowby, 1)).with(DAY_OF_WEEK, dow);
+ } else {
+ int dow = DAY_OF_WEEK.checkValidIntValue(dowLong); // validated
+ if (wowby < 1 || wowby > 52) {
+ if (resolverStyle == ResolverStyle.STRICT) {
+ getWeekRange(date).checkValidValue(wowby, this); // only allow exact range
+ } else { // SMART
+ range().checkValidValue(wowby, this); // allow 1-53 rolling into next year
+ }
+ }
+ date = date.plusWeeks(wowby - 1).with(DAY_OF_WEEK, dow);
+ }
+ fieldValues.remove(this);
+ fieldValues.remove(WEEK_BASED_YEAR);
+ fieldValues.remove(DAY_OF_WEEK);
+ return date;
+ }
+ @Override
+ public String toString() {
+ return "WeekOfWeekBasedYear";
+ }
+ },
+ WEEK_BASED_YEAR {
+ @Override
+ public TemporalUnit getBaseUnit() {
+ return WEEK_BASED_YEARS;
+ }
+ @Override
+ public TemporalUnit getRangeUnit() {
+ return FOREVER;
+ }
+ @Override
+ public ValueRange range() {
+ return YEAR.range();
+ }
+ @Override
+ public boolean isSupportedBy(TemporalAccessor temporal) {
+ return temporal.isSupported(EPOCH_DAY) && isIso(temporal);
+ }
+ @Override
+ public long getFrom(TemporalAccessor temporal) {
+ if (isSupportedBy(temporal) == false) {
+ throw new UnsupportedTemporalTypeException("Unsupported field: WeekBasedYear");
+ }
+ return getWeekBasedYear(LocalDate.from(temporal));
+ }
+ @SuppressWarnings("unchecked")
+ @Override
+ public <R extends Temporal> R adjustInto(R temporal, long newValue) {
+ if (isSupportedBy(temporal) == false) {
+ throw new UnsupportedTemporalTypeException("Unsupported field: WeekBasedYear");
+ }
+ int newWby = range().checkValidIntValue(newValue, WEEK_BASED_YEAR); // strict check
+ LocalDate date = LocalDate.from(temporal);
+ int dow = date.get(DAY_OF_WEEK);
+ int week = getWeek(date);
+ if (week == 53 && getWeekRange(newWby) == 52) {
+ week = 52;
+ }
+ LocalDate resolved = LocalDate.of(newWby, 1, 4); // 4th is guaranteed to be in week one
+ int days = (dow - resolved.get(DAY_OF_WEEK)) + ((week - 1) * 7);
+ resolved = resolved.plusDays(days);
+ return (R) temporal.with(resolved);
+ }
+ @Override
+ public String toString() {
+ return "WeekBasedYear";
+ }
+ };
+
+ @Override
+ public boolean isDateBased() {
+ return true;
+ }
+
+ @Override
+ public boolean isTimeBased() {
+ return false;
+ }
+
+ @Override
+ public ValueRange rangeRefinedBy(TemporalAccessor temporal) {
+ return range();
+ }
+
+ //-------------------------------------------------------------------------
+ private static final int[] QUARTER_DAYS = {0, 90, 181, 273, 0, 91, 182, 274};
+
+ private static boolean isIso(TemporalAccessor temporal) {
+ return Chronology.from(temporal).equals(IsoChronology.INSTANCE);
+ }
+
+ private static void ensureIso(TemporalAccessor temporal) {
+ if (isIso(temporal) == false) {
+ throw new DateTimeException("Resolve requires IsoChronology");
+ }
+ }
+
+ private static ValueRange getWeekRange(LocalDate date) {
+ int wby = getWeekBasedYear(date);
+ return ValueRange.of(1, getWeekRange(wby));
+ }
+
+ private static int getWeekRange(int wby) {
+ LocalDate date = LocalDate.of(wby, 1, 1);
+ // 53 weeks if standard year starts on Thursday, or Wed in a leap year
+ if (date.getDayOfWeek() == THURSDAY || (date.getDayOfWeek() == WEDNESDAY && date.isLeapYear())) {
+ return 53;
+ }
+ return 52;
+ }
+
+ private static int getWeek(LocalDate date) {
+ int dow0 = date.getDayOfWeek().ordinal();
+ int doy0 = date.getDayOfYear() - 1;
+ int doyThu0 = doy0 + (3 - dow0); // adjust to mid-week Thursday (which is 3 indexed from zero)
+ int alignedWeek = doyThu0 / 7;
+ int firstThuDoy0 = doyThu0 - (alignedWeek * 7);
+ int firstMonDoy0 = firstThuDoy0 - 3;
+ if (firstMonDoy0 < -3) {
+ firstMonDoy0 += 7;
+ }
+ if (doy0 < firstMonDoy0) {
+ return (int) getWeekRange(date.withDayOfYear(180).minusYears(1)).getMaximum();
+ }
+ int week = ((doy0 - firstMonDoy0) / 7) + 1;
+ if (week == 53) {
+ if ((firstMonDoy0 == -3 || (firstMonDoy0 == -2 && date.isLeapYear())) == false) {
+ week = 1;
+ }
+ }
+ return week;
+ }
+
+ private static int getWeekBasedYear(LocalDate date) {
+ int year = date.getYear();
+ int doy = date.getDayOfYear();
+ if (doy <= 3) {
+ int dow = date.getDayOfWeek().ordinal();
+ if (doy - dow < -2) {
+ year--;
+ }
+ } else if (doy >= 363) {
+ int dow = date.getDayOfWeek().ordinal();
+ doy = doy - 363 - (date.isLeapYear() ? 1 : 0);
+ if (doy - dow >= 0) {
+ year++;
+ }
+ }
+ return year;
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Implementation of the unit.
+ */
+ private static enum Unit implements TemporalUnit {
+
+ /**
+ * Unit that represents the concept of a week-based-year.
+ */
+ WEEK_BASED_YEARS("WeekBasedYears", Duration.ofSeconds(31556952L)),
+ /**
+ * Unit that represents the concept of a quarter-year.
+ */
+ QUARTER_YEARS("QuarterYears", Duration.ofSeconds(31556952L / 4));
+
+ private final String name;
+ private final Duration duration;
+
+ private Unit(String name, Duration estimatedDuration) {
+ this.name = name;
+ this.duration = estimatedDuration;
+ }
+
+ @Override
+ public Duration getDuration() {
+ return duration;
+ }
+
+ @Override
+ public boolean isDurationEstimated() {
+ return true;
+ }
+
+ @Override
+ public boolean isDateBased() {
+ return true;
+ }
+
+ @Override
+ public boolean isTimeBased() {
+ return false;
+ }
+
+ @Override
+ public boolean isSupportedBy(Temporal temporal) {
+ return temporal.isSupported(EPOCH_DAY);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <R extends Temporal> R addTo(R temporal, long amount) {
+ switch (this) {
+ case WEEK_BASED_YEARS:
+ return (R) temporal.with(WEEK_BASED_YEAR,
+ Math.addExact(temporal.get(WEEK_BASED_YEAR), amount));
+ case QUARTER_YEARS:
+ // no overflow (256 is multiple of 4)
+ return (R) temporal.plus(amount / 256, YEARS)
+ .plus((amount % 256) * 3, MONTHS);
+ default:
+ throw new IllegalStateException("Unreachable");
+ }
+ }
+
+ @Override
+ public long between(Temporal temporal1Inclusive, Temporal temporal2Exclusive) {
+ if (temporal1Inclusive.getClass() != temporal2Exclusive.getClass()) {
+ return temporal1Inclusive.until(temporal2Exclusive, this);
+ }
+ switch(this) {
+ case WEEK_BASED_YEARS:
+ return Math.subtractExact(temporal2Exclusive.getLong(WEEK_BASED_YEAR),
+ temporal1Inclusive.getLong(WEEK_BASED_YEAR));
+ case QUARTER_YEARS:
+ return temporal1Inclusive.until(temporal2Exclusive, MONTHS) / 3;
+ default:
+ throw new IllegalStateException("Unreachable");
+ }
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+ }
+}
diff --git a/java/time/temporal/JulianFields.java b/java/time/temporal/JulianFields.java
new file mode 100644
index 0000000..3270379
--- /dev/null
+++ b/java/time/temporal/JulianFields.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright (c) 2012, 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) 2012, 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.temporal;
+
+import static java.time.temporal.ChronoField.EPOCH_DAY;
+import static java.time.temporal.ChronoUnit.DAYS;
+import static java.time.temporal.ChronoUnit.FOREVER;
+
+import java.time.DateTimeException;
+import java.time.chrono.ChronoLocalDate;
+import java.time.chrono.Chronology;
+import java.time.format.ResolverStyle;
+import java.util.Map;
+
+/**
+ * A set of date fields that provide access to Julian Days.
+ * <p>
+ * The Julian Day is a standard way of expressing date and time commonly used in the scientific community.
+ * It is expressed as a decimal number of whole days where days start at midday.
+ * This class represents variations on Julian Days that count whole days from midnight.
+ * <p>
+ * The fields are implemented relative to {@link ChronoField#EPOCH_DAY EPOCH_DAY}.
+ * The fields are supported, and can be queried and set if {@code EPOCH_DAY} is available.
+ * The fields work with all chronologies.
+ *
+ * @implSpec
+ * This is an immutable and thread-safe class.
+ *
+ * @since 1.8
+ */
+public final class JulianFields {
+
+ /**
+ * The offset from Julian to EPOCH DAY.
+ */
+ private static final long JULIAN_DAY_OFFSET = 2440588L;
+
+ /**
+ * Julian Day field.
+ * <p>
+ * This is an integer-based version of the Julian Day Number.
+ * Julian Day is a well-known system that represents the count of whole days since day 0,
+ * which is defined to be January 1, 4713 BCE in the Julian calendar, and -4713-11-24 Gregorian.
+ * The field has "JulianDay" as 'name', and 'DAYS' as 'baseUnit'.
+ * The field always refers to the local date-time, ignoring the offset or zone.
+ * <p>
+ * For date-times, 'JULIAN_DAY.getFrom()' assumes the same value from
+ * midnight until just before the next midnight.
+ * When 'JULIAN_DAY.adjustInto()' is applied to a date-time, the time of day portion remains unaltered.
+ * 'JULIAN_DAY.adjustInto()' and 'JULIAN_DAY.getFrom()' only apply to {@code Temporal} objects that
+ * can be converted into {@link ChronoField#EPOCH_DAY}.
+ * An {@link UnsupportedTemporalTypeException} is thrown for any other type of object.
+ * <p>
+ * In the resolving phase of parsing, a date can be created from a Julian Day field.
+ * In {@linkplain ResolverStyle#STRICT strict mode} and {@linkplain ResolverStyle#SMART smart mode}
+ * the Julian Day value is validated against the range of valid values.
+ * In {@linkplain ResolverStyle#LENIENT lenient mode} no validation occurs.
+ *
+ * <h3>Astronomical and Scientific Notes</h3>
+ * The standard astronomical definition uses a fraction to indicate the time-of-day,
+ * thus 3.25 would represent the time 18:00, since days start at midday.
+ * This implementation uses an integer and days starting at midnight.
+ * The integer value for the Julian Day Number is the astronomical Julian Day value at midday
+ * of the date in question.
+ * This amounts to the astronomical Julian Day, rounded to an integer {@code JDN = floor(JD + 0.5)}.
+ *
+ * <pre>
+ * | ISO date | Julian Day Number | Astronomical Julian Day |
+ * | 1970-01-01T00:00 | 2,440,588 | 2,440,587.5 |
+ * | 1970-01-01T06:00 | 2,440,588 | 2,440,587.75 |
+ * | 1970-01-01T12:00 | 2,440,588 | 2,440,588.0 |
+ * | 1970-01-01T18:00 | 2,440,588 | 2,440,588.25 |
+ * | 1970-01-02T00:00 | 2,440,589 | 2,440,588.5 |
+ * | 1970-01-02T06:00 | 2,440,589 | 2,440,588.75 |
+ * | 1970-01-02T12:00 | 2,440,589 | 2,440,589.0 |
+ * </pre>
+ * <p>
+ * Julian Days are sometimes taken to imply Universal Time or UTC, but this
+ * implementation always uses the Julian Day number for the local date,
+ * regardless of the offset or time-zone.
+ */
+ public static final TemporalField JULIAN_DAY = Field.JULIAN_DAY;
+
+ /**
+ * Modified Julian Day field.
+ * <p>
+ * This is an integer-based version of the Modified Julian Day Number.
+ * Modified Julian Day (MJD) is a well-known system that counts days continuously.
+ * It is defined relative to astronomical Julian Day as {@code MJD = JD - 2400000.5}.
+ * Each Modified Julian Day runs from midnight to midnight.
+ * The field always refers to the local date-time, ignoring the offset or zone.
+ * <p>
+ * For date-times, 'MODIFIED_JULIAN_DAY.getFrom()' assumes the same value from
+ * midnight until just before the next midnight.
+ * When 'MODIFIED_JULIAN_DAY.adjustInto()' is applied to a date-time, the time of day portion remains unaltered.
+ * 'MODIFIED_JULIAN_DAY.adjustInto()' and 'MODIFIED_JULIAN_DAY.getFrom()' only apply to {@code Temporal} objects
+ * that can be converted into {@link ChronoField#EPOCH_DAY}.
+ * An {@link UnsupportedTemporalTypeException} is thrown for any other type of object.
+ * <p>
+ * This implementation is an integer version of MJD with the decimal part rounded to floor.
+ * <p>
+ * In the resolving phase of parsing, a date can be created from a Modified Julian Day field.
+ * In {@linkplain ResolverStyle#STRICT strict mode} and {@linkplain ResolverStyle#SMART smart mode}
+ * the Modified Julian Day value is validated against the range of valid values.
+ * In {@linkplain ResolverStyle#LENIENT lenient mode} no validation occurs.
+ *
+ * <h3>Astronomical and Scientific Notes</h3>
+ * <pre>
+ * | ISO date | Modified Julian Day | Decimal MJD |
+ * | 1970-01-01T00:00 | 40,587 | 40,587.0 |
+ * | 1970-01-01T06:00 | 40,587 | 40,587.25 |
+ * | 1970-01-01T12:00 | 40,587 | 40,587.5 |
+ * | 1970-01-01T18:00 | 40,587 | 40,587.75 |
+ * | 1970-01-02T00:00 | 40,588 | 40,588.0 |
+ * | 1970-01-02T06:00 | 40,588 | 40,588.25 |
+ * | 1970-01-02T12:00 | 40,588 | 40,588.5 |
+ * </pre>
+ *
+ * Modified Julian Days are sometimes taken to imply Universal Time or UTC, but this
+ * implementation always uses the Modified Julian Day for the local date,
+ * regardless of the offset or time-zone.
+ */
+ public static final TemporalField MODIFIED_JULIAN_DAY = Field.MODIFIED_JULIAN_DAY;
+
+ /**
+ * Rata Die field.
+ * <p>
+ * Rata Die counts whole days continuously starting day 1 at midnight at the beginning of 0001-01-01 (ISO).
+ * The field always refers to the local date-time, ignoring the offset or zone.
+ * <p>
+ * For date-times, 'RATA_DIE.getFrom()' assumes the same value from
+ * midnight until just before the next midnight.
+ * When 'RATA_DIE.adjustInto()' is applied to a date-time, the time of day portion remains unaltered.
+ * 'RATA_DIE.adjustInto()' and 'RATA_DIE.getFrom()' only apply to {@code Temporal} objects
+ * that can be converted into {@link ChronoField#EPOCH_DAY}.
+ * An {@link UnsupportedTemporalTypeException} is thrown for any other type of object.
+ * <p>
+ * In the resolving phase of parsing, a date can be created from a Rata Die field.
+ * In {@linkplain ResolverStyle#STRICT strict mode} and {@linkplain ResolverStyle#SMART smart mode}
+ * the Rata Die value is validated against the range of valid values.
+ * In {@linkplain ResolverStyle#LENIENT lenient mode} no validation occurs.
+ */
+ public static final TemporalField RATA_DIE = Field.RATA_DIE;
+
+ /**
+ * Restricted constructor.
+ */
+ private JulianFields() {
+ throw new AssertionError("Not instantiable");
+ }
+
+ /**
+ * Implementation of JulianFields. Each instance is a singleton.
+ */
+ private static enum Field implements TemporalField {
+ JULIAN_DAY("JulianDay", DAYS, FOREVER, JULIAN_DAY_OFFSET),
+ MODIFIED_JULIAN_DAY("ModifiedJulianDay", DAYS, FOREVER, 40587L),
+ RATA_DIE("RataDie", DAYS, FOREVER, 719163L);
+
+ private static final long serialVersionUID = -7501623920830201812L;
+
+ private final transient String name;
+ private final transient TemporalUnit baseUnit;
+ private final transient TemporalUnit rangeUnit;
+ private final transient ValueRange range;
+ private final transient long offset;
+
+ private Field(String name, TemporalUnit baseUnit, TemporalUnit rangeUnit, long offset) {
+ this.name = name;
+ this.baseUnit = baseUnit;
+ this.rangeUnit = rangeUnit;
+ this.range = ValueRange.of(-365243219162L + offset, 365241780471L + offset);
+ this.offset = offset;
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ public TemporalUnit getBaseUnit() {
+ return baseUnit;
+ }
+
+ @Override
+ public TemporalUnit getRangeUnit() {
+ return rangeUnit;
+ }
+
+ @Override
+ public boolean isDateBased() {
+ return true;
+ }
+
+ @Override
+ public boolean isTimeBased() {
+ return false;
+ }
+
+ @Override
+ public ValueRange range() {
+ return range;
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ public boolean isSupportedBy(TemporalAccessor temporal) {
+ return temporal.isSupported(EPOCH_DAY);
+ }
+
+ @Override
+ public ValueRange rangeRefinedBy(TemporalAccessor temporal) {
+ if (isSupportedBy(temporal) == false) {
+ throw new DateTimeException("Unsupported field: " + this);
+ }
+ return range();
+ }
+
+ @Override
+ public long getFrom(TemporalAccessor temporal) {
+ return temporal.getLong(EPOCH_DAY) + offset;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <R extends Temporal> R adjustInto(R temporal, long newValue) {
+ if (range().isValidValue(newValue) == false) {
+ throw new DateTimeException("Invalid value: " + name + " " + newValue);
+ }
+ return (R) temporal.with(EPOCH_DAY, Math.subtractExact(newValue, offset));
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ public ChronoLocalDate resolve(
+ Map<TemporalField, Long> fieldValues, TemporalAccessor partialTemporal, ResolverStyle resolverStyle) {
+ long value = fieldValues.remove(this);
+ Chronology chrono = Chronology.from(partialTemporal);
+ if (resolverStyle == ResolverStyle.LENIENT) {
+ return chrono.dateEpochDay(Math.subtractExact(value, offset));
+ }
+ range().checkValidValue(value, this);
+ return chrono.dateEpochDay(value - offset);
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ public String toString() {
+ return name;
+ }
+ }
+}
diff --git a/java/time/temporal/Temporal.java b/java/time/temporal/Temporal.java
new file mode 100644
index 0000000..f7064aa
--- /dev/null
+++ b/java/time/temporal/Temporal.java
@@ -0,0 +1,449 @@
+/*
+ * Copyright (c) 2012, 2015, 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) 2012, 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.temporal;
+
+import java.time.DateTimeException;
+
+/**
+ * Framework-level interface defining read-write access to a temporal object,
+ * such as a date, time, offset or some combination of these.
+ * <p>
+ * This is the base interface type for date, time and offset objects that
+ * are complete enough to be manipulated using plus and minus.
+ * It is implemented by those classes that can provide and manipulate information
+ * as {@linkplain TemporalField fields} or {@linkplain TemporalQuery queries}.
+ * See {@link TemporalAccessor} for the read-only version of this interface.
+ * <p>
+ * Most date and time information can be represented as a number.
+ * These are modeled using {@code TemporalField} with the number held using
+ * a {@code long} to handle large values. Year, month and day-of-month are
+ * simple examples of fields, but they also include instant and offsets.
+ * See {@link ChronoField} for the standard set of fields.
+ * <p>
+ * Two pieces of date/time information cannot be represented by numbers,
+ * the {@linkplain java.time.chrono.Chronology chronology} and the
+ * {@linkplain java.time.ZoneId time-zone}.
+ * These can be accessed via {@link #query(TemporalQuery) queries} using
+ * the static methods defined on {@link TemporalQuery}.
+ * <p>
+ * This interface is a framework-level interface that should not be widely
+ * used in application code. Instead, applications should create and pass
+ * around instances of concrete types, such as {@code LocalDate}.
+ * There are many reasons for this, part of which is that implementations
+ * of this interface may be in calendar systems other than ISO.
+ * See {@link java.time.chrono.ChronoLocalDate} for a fuller discussion of the issues.
+ *
+ * <h3>When to implement</h3>
+ * <p>
+ * A class should implement this interface if it meets three criteria:
+ * <ul>
+ * <li>it provides access to date/time/offset information, as per {@code TemporalAccessor}
+ * <li>the set of fields are contiguous from the largest to the smallest
+ * <li>the set of fields are complete, such that no other field is needed to define the
+ * valid range of values for the fields that are represented
+ * </ul>
+ * <p>
+ * Four examples make this clear:
+ * <ul>
+ * <li>{@code LocalDate} implements this interface as it represents a set of fields
+ * that are contiguous from days to forever and require no external information to determine
+ * the validity of each date. It is therefore able to implement plus/minus correctly.
+ * <li>{@code LocalTime} implements this interface as it represents a set of fields
+ * that are contiguous from nanos to within days and require no external information to determine
+ * validity. It is able to implement plus/minus correctly, by wrapping around the day.
+ * <li>{@code MonthDay}, the combination of month-of-year and day-of-month, does not implement
+ * this interface. While the combination is contiguous, from days to months within years,
+ * the combination does not have sufficient information to define the valid range of values
+ * for day-of-month. As such, it is unable to implement plus/minus correctly.
+ * <li>The combination day-of-week and day-of-month ("Friday the 13th") should not implement
+ * this interface. It does not represent a contiguous set of fields, as days to weeks overlaps
+ * days to months.
+ * </ul>
+ *
+ * @implSpec
+ * This interface places no restrictions on the mutability of implementations,
+ * however immutability is strongly recommended.
+ * All implementations must be {@link Comparable}.
+ *
+ * @since 1.8
+ */
+public interface Temporal extends TemporalAccessor {
+
+ /**
+ * Checks if the specified unit is supported.
+ * <p>
+ * This checks if the specified unit can be added to, or subtracted from, this date-time.
+ * If false, then calling the {@link #plus(long, TemporalUnit)} and
+ * {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
+ *
+ * @implSpec
+ * Implementations must check and handle all units defined in {@link ChronoUnit}.
+ * If the unit is supported, then true must be returned, otherwise false must be returned.
+ * <p>
+ * If the field is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
+ * passing {@code this} as the argument.
+ * <p>
+ * Implementations must ensure that no observable state is altered when this
+ * read-only method is invoked.
+ *
+ * @param unit the unit to check, null returns false
+ * @return true if the unit can be added/subtracted, false if not
+ */
+ boolean isSupported(TemporalUnit unit);
+
+ /**
+ * Returns an adjusted object of the same type as this object with the adjustment made.
+ * <p>
+ * This adjusts this date-time according to the rules of the specified adjuster.
+ * A simple adjuster might simply set the one of the fields, such as the year field.
+ * A more complex adjuster might set the date to the last day of the month.
+ * A selection of common adjustments is provided in
+ * {@link java.time.temporal.TemporalAdjusters TemporalAdjusters}.
+ * These include finding the "last day of the month" and "next Wednesday".
+ * The adjuster is responsible for handling special cases, such as the varying
+ * lengths of month and leap years.
+ * <p>
+ * Some example code indicating how and why this method is used:
+ * <pre>
+ * date = date.with(Month.JULY); // most key classes implement TemporalAdjuster
+ * date = date.with(lastDayOfMonth()); // static import from Adjusters
+ * date = date.with(next(WEDNESDAY)); // static import from Adjusters and DayOfWeek
+ * </pre>
+ *
+ * @implSpec
+ * <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>
+ * The default implementation must behave equivalent to this code:
+ * <pre>
+ * return adjuster.adjustInto(this);
+ * </pre>
+ *
+ * @param adjuster the adjuster to use, not null
+ * @return an object of the same type with the specified adjustment made, not null
+ * @throws DateTimeException if unable to make the adjustment
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ default Temporal with(TemporalAdjuster adjuster) {
+ return adjuster.adjustInto(this);
+ }
+
+ /**
+ * Returns an object of the same type as this object with the specified field altered.
+ * <p>
+ * This returns a new object based on this one with the value for the specified field changed.
+ * For example, on a {@code LocalDate}, this could be used to set the year, month or day-of-month.
+ * The returned object will have the same observable type as this object.
+ * <p>
+ * In some cases, changing a field is not fully defined. For example, if the target object is
+ * a date representing the 31st January, then changing the month to February would be unclear.
+ * In cases like this, the field is responsible for resolving the result. Typically it will choose
+ * the previous valid date, which would be the last valid day of February in this example.
+ *
+ * @implSpec
+ * Implementations must check and handle all fields defined in {@link ChronoField}.
+ * If the field is supported, then the adjustment must be performed.
+ * If unsupported, then an {@code UnsupportedTemporalTypeException} must be thrown.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.adjustInto(Temporal, long)}
+ * passing {@code this} as the first argument.
+ * <p>
+ * 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.
+ *
+ * @param field the field to set in the result, not null
+ * @param newValue the new value of the field in the result
+ * @return an object of the same type with the specified field set, not null
+ * @throws DateTimeException if the field cannot be set
+ * @throws UnsupportedTemporalTypeException if the field is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ Temporal with(TemporalField field, long newValue);
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns an object of the same type as this object with an amount added.
+ * <p>
+ * This adjusts this temporal, adding according to the rules of the specified amount.
+ * The amount is typically a {@link java.time.Period} but may be any other type implementing
+ * the {@link TemporalAmount} interface, such as {@link java.time.Duration}.
+ * <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
+ * </pre>
+ * <p>
+ * Note that calling {@code plus} followed by {@code minus} is not guaranteed to
+ * return the same date-time.
+ *
+ * @implSpec
+ * <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>
+ * The default implementation must behave equivalent to this code:
+ * <pre>
+ * return amount.addTo(this);
+ * </pre>
+ *
+ * @param amount the amount to add, not null
+ * @return an object of the same type with the specified adjustment made, not null
+ * @throws DateTimeException if the addition cannot be made
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ default Temporal plus(TemporalAmount amount) {
+ return amount.addTo(this);
+ }
+
+ /**
+ * Returns an object of the same type as this object with the specified period added.
+ * <p>
+ * This method returns a new object based on this one with the specified period added.
+ * For example, on a {@code LocalDate}, this could be used to add a number of years, months or days.
+ * The returned object will have the same observable type as this object.
+ * <p>
+ * In some cases, changing a field is not fully defined. For example, if the target object is
+ * a date representing the 31st January, then adding one month would be unclear.
+ * In cases like this, the field is responsible for resolving the result. Typically it will choose
+ * the previous valid date, which would be the last valid day of February in this example.
+ *
+ * @implSpec
+ * Implementations must check and handle all units defined in {@link ChronoUnit}.
+ * If the unit is supported, then the addition must be performed.
+ * If unsupported, then an {@code UnsupportedTemporalTypeException} must be thrown.
+ * <p>
+ * If the unit is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.addTo(Temporal, long)}
+ * passing {@code this} as the first argument.
+ * <p>
+ * 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.
+ *
+ * @param amountToAdd the amount of the specified unit to add, may be negative
+ * @param unit the unit of the amount to add, not null
+ * @return an object of the same type with the specified period added, not null
+ * @throws DateTimeException if the unit cannot be added
+ * @throws UnsupportedTemporalTypeException if the unit is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ Temporal plus(long amountToAdd, TemporalUnit unit);
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns an object of the same type as this object with an amount subtracted.
+ * <p>
+ * This adjusts this temporal, subtracting according to the rules of the specified amount.
+ * The amount is typically a {@link java.time.Period} but may be any other type implementing
+ * the {@link TemporalAmount} interface, such as {@link java.time.Duration}.
+ * <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
+ * </pre>
+ * <p>
+ * Note that calling {@code plus} followed by {@code minus} is not guaranteed to
+ * return the same date-time.
+ *
+ * @implSpec
+ * <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>
+ * The default implementation must behave equivalent to this code:
+ * <pre>
+ * return amount.subtractFrom(this);
+ * </pre>
+ *
+ * @param amount the amount to subtract, not null
+ * @return an object of the same type with the specified adjustment made, not null
+ * @throws DateTimeException if the subtraction cannot be made
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ default Temporal minus(TemporalAmount amount) {
+ return amount.subtractFrom(this);
+ }
+
+ /**
+ * Returns an object of the same type as this object with the specified period subtracted.
+ * <p>
+ * This method returns a new object based on this one with the specified period subtracted.
+ * For example, on a {@code LocalDate}, this could be used to subtract a number of years, months or days.
+ * The returned object will have the same observable type as this object.
+ * <p>
+ * In some cases, changing a field is not fully defined. For example, if the target object is
+ * a date representing the 31st March, then subtracting one month would be unclear.
+ * In cases like this, the field is responsible for resolving the result. Typically it will choose
+ * the previous valid date, which would be the last valid day of February in this example.
+ *
+ * @implSpec
+ * Implementations must behave in a manor equivalent to the default method behavior.
+ * <p>
+ * 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>
+ * The default implementation must behave equivalent to this code:
+ * <pre>
+ * return (amountToSubtract == Long.MIN_VALUE ?
+ * plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit));
+ * </pre>
+ *
+ * @param amountToSubtract the amount of the specified unit to subtract, may be negative
+ * @param unit the unit of the amount to subtract, not null
+ * @return an object of the same type with the specified period subtracted, not null
+ * @throws DateTimeException if the unit cannot be subtracted
+ * @throws UnsupportedTemporalTypeException if the unit is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ default Temporal minus(long amountToSubtract, TemporalUnit unit) {
+ return (amountToSubtract == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Calculates the amount of time until another temporal in terms of the specified unit.
+ * <p>
+ * This calculates the amount of time between two temporal objects
+ * in terms of a single {@code TemporalUnit}.
+ * The start and end points are {@code this} and the specified temporal.
+ * The end point is converted to be of the same type as the start point if different.
+ * The result will be negative if the end is before the start.
+ * For example, the amount in hours between two temporal objects can be
+ * calculated using {@code startTime.until(endTime, HOURS)}.
+ * <p>
+ * The calculation returns a whole number, representing the number of
+ * complete units between the two temporals.
+ * For example, the amount in hours between the times 11:30 and 13:29
+ * will only be one hour as it is one minute short of two hours.
+ * <p>
+ * There are two equivalent ways of using this method.
+ * The first is to invoke this method directly.
+ * The second is to use {@link TemporalUnit#between(Temporal, Temporal)}:
+ * <pre>
+ * // these two lines are equivalent
+ * temporal = start.until(end, unit);
+ * temporal = unit.between(start, end);
+ * </pre>
+ * The choice should be made based on which makes the code more readable.
+ * <p>
+ * For example, this method allows the number of days between two dates to
+ * be calculated:
+ * <pre>
+ * long daysBetween = start.until(end, DAYS);
+ * // or alternatively
+ * long daysBetween = DAYS.between(start, end);
+ * </pre>
+ *
+ * @implSpec
+ * Implementations must begin by checking to ensure that the input temporal
+ * object is of the same observable type as the implementation.
+ * They must then perform the calculation for all instances of {@link ChronoUnit}.
+ * An {@code UnsupportedTemporalTypeException} must be thrown for {@code ChronoUnit}
+ * instances that are unsupported.
+ * <p>
+ * If the unit is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.between(Temporal, Temporal)}
+ * passing {@code this} as the first argument and the converted input temporal as
+ * the second argument.
+ * <p>
+ * In summary, implementations must behave in a manner equivalent to this pseudo-code:
+ * <pre>
+ * // convert the end temporal to the same type as this class
+ * if (unit instanceof ChronoUnit) {
+ * // if unit is supported, then calculate and return result
+ * // else throw UnsupportedTemporalTypeException for unsupported units
+ * }
+ * return unit.between(this, convertedEndTemporal);
+ * </pre>
+ * <p>
+ * Note that the unit's {@code between} method must only be invoked if the
+ * two temporal objects have exactly the same type evaluated by {@code getClass()}.
+ * <p>
+ * Implementations must ensure that no observable state is altered when this
+ * read-only method is invoked.
+ *
+ * @param endExclusive the end temporal, exclusive, converted to be of the
+ * same type as this object, not null
+ * @param unit the unit to measure the amount in, not null
+ * @return the amount of time between this temporal object and the specified one
+ * in terms of the unit; positive if the specified object is later than this one,
+ * negative if it is earlier than this one
+ * @throws DateTimeException if the amount cannot be calculated, or the end
+ * temporal cannot be converted to the same type as this temporal
+ * @throws UnsupportedTemporalTypeException if the unit is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ long until(Temporal endExclusive, TemporalUnit unit);
+
+}
diff --git a/java/time/temporal/TemporalAccessor.java b/java/time/temporal/TemporalAccessor.java
new file mode 100644
index 0000000..5e8b61e
--- /dev/null
+++ b/java/time/temporal/TemporalAccessor.java
@@ -0,0 +1,317 @@
+/*
+ * Copyright (c) 2012, 2015, 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) 2012, 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.temporal;
+
+import java.time.DateTimeException;
+import java.util.Objects;
+
+/**
+ * Framework-level interface defining read-only access to a temporal object,
+ * such as a date, time, offset or some combination of these.
+ * <p>
+ * This is the base interface type for date, time and offset objects.
+ * It is implemented by those classes that can provide information
+ * as {@linkplain TemporalField fields} or {@linkplain TemporalQuery queries}.
+ * <p>
+ * Most date and time information can be represented as a number.
+ * These are modeled using {@code TemporalField} with the number held using
+ * a {@code long} to handle large values. Year, month and day-of-month are
+ * simple examples of fields, but they also include instant and offsets.
+ * See {@link ChronoField} for the standard set of fields.
+ * <p>
+ * Two pieces of date/time information cannot be represented by numbers,
+ * the {@linkplain java.time.chrono.Chronology chronology} and the
+ * {@linkplain java.time.ZoneId time-zone}.
+ * These can be accessed via {@linkplain #query(TemporalQuery) queries} using
+ * the static methods defined on {@link TemporalQuery}.
+ * <p>
+ * A sub-interface, {@link Temporal}, extends this definition to one that also
+ * supports adjustment and manipulation on more complete temporal objects.
+ * <p>
+ * This interface is a framework-level interface that should not be widely
+ * used in application code. Instead, applications should create and pass
+ * around instances of concrete types, such as {@code LocalDate}.
+ * There are many reasons for this, part of which is that implementations
+ * of this interface may be in calendar systems other than ISO.
+ * See {@link java.time.chrono.ChronoLocalDate} for a fuller discussion of the issues.
+ *
+ * @implSpec
+ * This interface places no restrictions on the mutability of implementations,
+ * however immutability is strongly recommended.
+ *
+ * @since 1.8
+ */
+public interface TemporalAccessor {
+
+ /**
+ * Checks if the specified field is supported.
+ * <p>
+ * This checks if the date-time can be queried for the specified field.
+ * If false, then calling the {@link #range(TemporalField) range} and {@link #get(TemporalField) get}
+ * methods will throw an exception.
+ *
+ * @implSpec
+ * Implementations must check and handle all fields defined in {@link ChronoField}.
+ * If the field is supported, then true must be returned, otherwise false must be returned.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}
+ * passing {@code this} as the argument.
+ * <p>
+ * Implementations must ensure that no observable state is altered when this
+ * read-only method is invoked.
+ *
+ * @param field the field to check, null returns false
+ * @return true if this date-time can be queried for the field, false if not
+ */
+ boolean isSupported(TemporalField field);
+
+ /**
+ * Gets the range of valid values for the specified field.
+ * <p>
+ * All fields can be expressed as a {@code long} integer.
+ * This method returns an object that describes the valid range for that value.
+ * The value of this temporal object is used to enhance the accuracy of the returned range.
+ * If the date-time cannot return the range, because the field is unsupported or for
+ * some other reason, an exception will be thrown.
+ * <p>
+ * Note that the result only describes the minimum and maximum valid values
+ * and it is important not to read too much into them. For example, there
+ * could be values within the range that are invalid for the field.
+ *
+ * @implSpec
+ * Implementations must check and handle all fields defined in {@link ChronoField}.
+ * If the field is supported, then the range of the field must be returned.
+ * If unsupported, then an {@code UnsupportedTemporalTypeException} must be thrown.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.rangeRefinedBy(TemporalAccessorl)}
+ * passing {@code this} as the argument.
+ * <p>
+ * Implementations must ensure that no observable state is altered when this
+ * read-only method is invoked.
+ * <p>
+ * The default implementation must behave equivalent to this code:
+ * <pre>
+ * if (field instanceof ChronoField) {
+ * if (isSupported(field)) {
+ * return field.range();
+ * }
+ * throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
+ * }
+ * return field.rangeRefinedBy(this);
+ * </pre>
+ *
+ * @param field the field to query the range for, not null
+ * @return the range of valid values for the field, not null
+ * @throws DateTimeException if the range for the field cannot be obtained
+ * @throws UnsupportedTemporalTypeException if the field is not supported
+ */
+ default ValueRange range(TemporalField field) {
+ if (field instanceof ChronoField) {
+ if (isSupported(field)) {
+ return field.range();
+ }
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
+ }
+ Objects.requireNonNull(field, "field");
+ return field.rangeRefinedBy(this);
+ }
+
+ /**
+ * Gets the value of the specified field as an {@code int}.
+ * <p>
+ * This queries the date-time for the value of the specified field.
+ * The returned value will always be within the valid range of values for the field.
+ * If the date-time cannot return the value, because the field is unsupported or for
+ * some other reason, an exception will be thrown.
+ *
+ * @implSpec
+ * Implementations must check and handle all fields defined in {@link ChronoField}.
+ * If the field is supported and has an {@code int} range, then the value of
+ * the field must be returned.
+ * If unsupported, then an {@code UnsupportedTemporalTypeException} must be thrown.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
+ * passing {@code this} as the argument.
+ * <p>
+ * Implementations must ensure that no observable state is altered when this
+ * read-only method is invoked.
+ * <p>
+ * The default implementation must behave equivalent to this code:
+ * <pre>
+ * if (range(field).isIntValue()) {
+ * return range(field).checkValidIntValue(getLong(field), field);
+ * }
+ * throw new UnsupportedTemporalTypeException("Invalid field " + field + " + for get() method, use getLong() instead");
+ * </pre>
+ *
+ * @param field the field to get, not null
+ * @return the value for the field, within the valid range of values
+ * @throws DateTimeException if a value for the field cannot be obtained or
+ * the value is outside the range of valid values for the field
+ * @throws UnsupportedTemporalTypeException if the field is not supported or
+ * the range of values exceeds an {@code int}
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ default int get(TemporalField field) {
+ ValueRange range = range(field);
+ if (range.isIntValue() == false) {
+ throw new UnsupportedTemporalTypeException("Invalid field " + field + " for get() method, use getLong() instead");
+ }
+ long value = getLong(field);
+ if (range.isValidValue(value) == false) {
+ throw new DateTimeException("Invalid value for " + field + " (valid values " + range + "): " + value);
+ }
+ return (int) value;
+ }
+
+ /**
+ * Gets the value of the specified field as a {@code long}.
+ * <p>
+ * This queries the date-time for the value of the specified field.
+ * The returned value may be outside the valid range of values for the field.
+ * If the date-time cannot return the value, because the field is unsupported or for
+ * some other reason, an exception will be thrown.
+ *
+ * @implSpec
+ * Implementations must check and handle all fields defined in {@link ChronoField}.
+ * If the field is supported, then the value of the field must be returned.
+ * If unsupported, then an {@code UnsupportedTemporalTypeException} must be thrown.
+ * <p>
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
+ * passing {@code this} as the argument.
+ * <p>
+ * Implementations must ensure that no observable state is altered when this
+ * read-only method is invoked.
+ *
+ * @param field the field to get, not null
+ * @return the value for the field
+ * @throws DateTimeException if a value for the field cannot be obtained
+ * @throws UnsupportedTemporalTypeException if the field is not supported
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ long getLong(TemporalField field);
+
+ /**
+ * Queries this date-time.
+ * <p>
+ * This queries this date-time using the specified query strategy object.
+ * <p>
+ * Queries are a key tool for extracting information from date-times.
+ * They exists to externalize the process of querying, permitting different
+ * approaches, as per the strategy design pattern.
+ * Examples might be a query that checks if the date is the day before February 29th
+ * in a leap year, or calculates the number of days to your next birthday.
+ * <p>
+ * The most common query implementations are method references, such as
+ * {@code LocalDate::from} and {@code ZoneId::from}.
+ * Additional implementations are provided as static methods on {@link TemporalQuery}.
+ *
+ * @implSpec
+ * The default implementation must behave equivalent to this code:
+ * <pre>
+ * if (query == TemporalQueries.zoneId() ||
+ * query == TemporalQueries.chronology() || query == TemporalQueries.precision()) {
+ * return null;
+ * }
+ * return query.queryFrom(this);
+ * </pre>
+ * Future versions are permitted to add further queries to the if statement.
+ * <p>
+ * All classes implementing this interface and overriding this method must call
+ * {@code TemporalAccessor.super.query(query)}. JDK classes may avoid calling
+ * super if they provide behavior equivalent to the default behaviour, however
+ * non-JDK classes may not utilize this optimization and must call {@code super}.
+ * <p>
+ * If the implementation can supply a value for one of the queries listed in the
+ * if statement of the default implementation, then it must do so.
+ * For example, an application-defined {@code HourMin} class storing the hour
+ * and minute must override this method as follows:
+ * <pre>
+ * if (query == TemporalQueries.precision()) {
+ * return MINUTES;
+ * }
+ * return TemporalAccessor.super.query(query);
+ * </pre>
+ * <p>
+ * Implementations must ensure that no observable state is altered when this
+ * read-only method is invoked.
+ *
+ * @param <R> the type of the result
+ * @param query the query to invoke, not null
+ * @return the query result, null may be returned (defined by the query)
+ * @throws DateTimeException if unable to query
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ default <R> R query(TemporalQuery<R> query) {
+ if (query == TemporalQueries.zoneId()
+ || query == TemporalQueries.chronology()
+ || query == TemporalQueries.precision()) {
+ return null;
+ }
+ return query.queryFrom(this);
+ }
+
+}
diff --git a/java/time/temporal/TemporalAdjuster.java b/java/time/temporal/TemporalAdjuster.java
new file mode 100644
index 0000000..09b5fba
--- /dev/null
+++ b/java/time/temporal/TemporalAdjuster.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2012, 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) 2012, 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.temporal;
+
+import java.time.DateTimeException;
+
+/**
+ * Strategy for adjusting a temporal object.
+ * <p>
+ * Adjusters are a key tool for modifying temporal objects.
+ * They exist to externalize the process of adjustment, permitting different
+ * approaches, as per the strategy design pattern.
+ * Examples might be an adjuster that sets the date avoiding weekends, or one that
+ * sets the date to the last day of the month.
+ * <p>
+ * There are two equivalent ways of using a {@code TemporalAdjuster}.
+ * The first is to invoke the method on this interface directly.
+ * The second is to use {@link Temporal#with(TemporalAdjuster)}:
+ * <pre>
+ * // these two lines are equivalent, but the second approach is recommended
+ * temporal = thisAdjuster.adjustInto(temporal);
+ * temporal = temporal.with(thisAdjuster);
+ * </pre>
+ * It is recommended to use the second approach, {@code with(TemporalAdjuster)},
+ * as it is a lot clearer to read in code.
+ * <p>
+ * The {@link TemporalAdjusters} class contains a standard set of adjusters,
+ * available as static methods.
+ * These include:
+ * <ul>
+ * <li>finding the first or last day of the month
+ * <li>finding the first day of next month
+ * <li>finding the first or last day of the year
+ * <li>finding the first day of next year
+ * <li>finding the first or last day-of-week within a month, such as "first Wednesday in June"
+ * <li>finding the next or previous day-of-week, such as "next Thursday"
+ * </ul>
+ *
+ * @implSpec
+ * This interface places no restrictions on the mutability of implementations,
+ * however immutability is strongly recommended.
+ *
+ * @see TemporalAdjusters
+ * @since 1.8
+ */
+@FunctionalInterface
+public interface TemporalAdjuster {
+
+ /**
+ * Adjusts the specified temporal object.
+ * <p>
+ * This adjusts the specified temporal object using the logic
+ * encapsulated in the implementing class.
+ * Examples might be an adjuster that sets the date avoiding weekends, or one that
+ * sets the date to the last day of the month.
+ * <p>
+ * There are two equivalent ways of using this method.
+ * The first is to invoke this method directly.
+ * The second is to use {@link Temporal#with(TemporalAdjuster)}:
+ * <pre>
+ * // these two lines are equivalent, but the second approach is recommended
+ * temporal = thisAdjuster.adjustInto(temporal);
+ * temporal = temporal.with(thisAdjuster);
+ * </pre>
+ * It is recommended to use the second approach, {@code with(TemporalAdjuster)},
+ * as it is a lot clearer to read in code.
+ *
+ * @implSpec
+ * The implementation must take the input object and adjust it.
+ * The implementation defines the logic of the adjustment and is responsible for
+ * documenting that logic. It may use any method on {@code Temporal} to
+ * query the temporal object and perform the adjustment.
+ * The returned object must have the same observable type as the input object
+ * <p>
+ * The input object must not be altered.
+ * Instead, an adjusted copy of the original must be returned.
+ * This provides equivalent, safe behavior for immutable and mutable temporal objects.
+ * <p>
+ * The input temporal object may be in a calendar system other than ISO.
+ * Implementations may choose to document compatibility with other calendar systems,
+ * or reject non-ISO temporal objects by {@link TemporalQueries#chronology() querying the chronology}.
+ * <p>
+ * This method may be called from multiple threads in parallel.
+ * It must be thread-safe when invoked.
+ *
+ * @param temporal the temporal object to adjust, not null
+ * @return an object of the same observable type with the adjustment made, not null
+ * @throws DateTimeException if unable to make the adjustment
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ Temporal adjustInto(Temporal temporal);
+
+}
diff --git a/java/time/temporal/TemporalAdjusters.java b/java/time/temporal/TemporalAdjusters.java
new file mode 100644
index 0000000..0180c81
--- /dev/null
+++ b/java/time/temporal/TemporalAdjusters.java
@@ -0,0 +1,478 @@
+/*
+ * Copyright (c) 2012, 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) 2012-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.temporal;
+
+import static java.time.temporal.ChronoField.DAY_OF_MONTH;
+import static java.time.temporal.ChronoField.DAY_OF_WEEK;
+import static java.time.temporal.ChronoField.DAY_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.time.DayOfWeek;
+import java.time.LocalDate;
+import java.util.Objects;
+import java.util.function.UnaryOperator;
+
+/**
+ * Common and useful TemporalAdjusters.
+ * <p>
+ * Adjusters are a key tool for modifying temporal objects.
+ * They exist to externalize the process of adjustment, permitting different
+ * approaches, as per the strategy design pattern.
+ * Examples might be an adjuster that sets the date avoiding weekends, or one that
+ * sets the date to the last day of the month.
+ * <p>
+ * There are two equivalent ways of using a {@code TemporalAdjuster}.
+ * The first is to invoke the method on the interface directly.
+ * The second is to use {@link Temporal#with(TemporalAdjuster)}:
+ * <pre>
+ * // these two lines are equivalent, but the second approach is recommended
+ * temporal = thisAdjuster.adjustInto(temporal);
+ * temporal = temporal.with(thisAdjuster);
+ * </pre>
+ * It is recommended to use the second approach, {@code with(TemporalAdjuster)},
+ * as it is a lot clearer to read in code.
+ * <p>
+ * This class contains a standard set of adjusters, available as static methods.
+ * These include:
+ * <ul>
+ * <li>finding the first or last day of the month
+ * <li>finding the first day of next month
+ * <li>finding the first or last day of the year
+ * <li>finding the first day of next year
+ * <li>finding the first or last day-of-week within a month, such as "first Wednesday in June"
+ * <li>finding the next or previous day-of-week, such as "next Thursday"
+ * </ul>
+ *
+ * @implSpec
+ * All the implementations supplied by the static methods are immutable.
+ *
+ * @see TemporalAdjuster
+ * @since 1.8
+ */
+public final class TemporalAdjusters {
+
+ /**
+ * Private constructor since this is a utility class.
+ */
+ private TemporalAdjusters() {
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains a {@code TemporalAdjuster} that wraps a date adjuster.
+ * <p>
+ * The {@code TemporalAdjuster} is based on the low level {@code Temporal} interface.
+ * This method allows an adjustment from {@code LocalDate} to {@code LocalDate}
+ * to be wrapped to match the temporal-based interface.
+ * This is provided for convenience to make user-written adjusters simpler.
+ * <p>
+ * In general, user-written adjusters should be static constants:
+ * <pre>{@code
+ * static TemporalAdjuster TWO_DAYS_LATER =
+ * TemporalAdjusters.ofDateAdjuster(date -> date.plusDays(2));
+ * }</pre>
+ *
+ * @param dateBasedAdjuster the date-based adjuster, not null
+ * @return the temporal adjuster wrapping on the date adjuster, not null
+ */
+ public static TemporalAdjuster ofDateAdjuster(UnaryOperator<LocalDate> dateBasedAdjuster) {
+ Objects.requireNonNull(dateBasedAdjuster, "dateBasedAdjuster");
+ return (temporal) -> {
+ LocalDate input = LocalDate.from(temporal);
+ LocalDate output = dateBasedAdjuster.apply(input);
+ return temporal.with(output);
+ };
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns the "first day of month" adjuster, which returns a new date set to
+ * the first day of the current month.
+ * <p>
+ * The ISO calendar system behaves as follows:<br>
+ * The input 2011-01-15 will return 2011-01-01.<br>
+ * The input 2011-02-15 will return 2011-02-01.
+ * <p>
+ * The behavior is suitable for use with most calendar systems.
+ * It is equivalent to:
+ * <pre>
+ * temporal.with(DAY_OF_MONTH, 1);
+ * </pre>
+ *
+ * @return the first day-of-month adjuster, not null
+ */
+ public static TemporalAdjuster firstDayOfMonth() {
+ return (temporal) -> temporal.with(DAY_OF_MONTH, 1);
+ }
+
+ /**
+ * Returns the "last day of month" adjuster, which returns a new date set to
+ * the last day of the current month.
+ * <p>
+ * The ISO calendar system behaves as follows:<br>
+ * The input 2011-01-15 will return 2011-01-31.<br>
+ * The input 2011-02-15 will return 2011-02-28.<br>
+ * The input 2012-02-15 will return 2012-02-29 (leap year).<br>
+ * The input 2011-04-15 will return 2011-04-30.
+ * <p>
+ * The behavior is suitable for use with most calendar systems.
+ * It is equivalent to:
+ * <pre>
+ * long lastDay = temporal.range(DAY_OF_MONTH).getMaximum();
+ * temporal.with(DAY_OF_MONTH, lastDay);
+ * </pre>
+ *
+ * @return the last day-of-month adjuster, not null
+ */
+ public static TemporalAdjuster lastDayOfMonth() {
+ return (temporal) -> temporal.with(DAY_OF_MONTH, temporal.range(DAY_OF_MONTH).getMaximum());
+ }
+
+ /**
+ * Returns the "first day of next month" adjuster, which returns a new date set to
+ * the first day of the next month.
+ * <p>
+ * The ISO calendar system behaves as follows:<br>
+ * The input 2011-01-15 will return 2011-02-01.<br>
+ * The input 2011-02-15 will return 2011-03-01.
+ * <p>
+ * The behavior is suitable for use with most calendar systems.
+ * It is equivalent to:
+ * <pre>
+ * temporal.with(DAY_OF_MONTH, 1).plus(1, MONTHS);
+ * </pre>
+ *
+ * @return the first day of next month adjuster, not null
+ */
+ public static TemporalAdjuster firstDayOfNextMonth() {
+ return (temporal) -> temporal.with(DAY_OF_MONTH, 1).plus(1, MONTHS);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns the "first day of year" adjuster, which returns a new date set to
+ * the first day of the current year.
+ * <p>
+ * The ISO calendar system behaves as follows:<br>
+ * The input 2011-01-15 will return 2011-01-01.<br>
+ * The input 2011-02-15 will return 2011-01-01.<br>
+ * <p>
+ * The behavior is suitable for use with most calendar systems.
+ * It is equivalent to:
+ * <pre>
+ * temporal.with(DAY_OF_YEAR, 1);
+ * </pre>
+ *
+ * @return the first day-of-year adjuster, not null
+ */
+ public static TemporalAdjuster firstDayOfYear() {
+ return (temporal) -> temporal.with(DAY_OF_YEAR, 1);
+ }
+
+ /**
+ * Returns the "last day of year" adjuster, which returns a new date set to
+ * the last day of the current year.
+ * <p>
+ * The ISO calendar system behaves as follows:<br>
+ * The input 2011-01-15 will return 2011-12-31.<br>
+ * The input 2011-02-15 will return 2011-12-31.<br>
+ * <p>
+ * The behavior is suitable for use with most calendar systems.
+ * It is equivalent to:
+ * <pre>
+ * long lastDay = temporal.range(DAY_OF_YEAR).getMaximum();
+ * temporal.with(DAY_OF_YEAR, lastDay);
+ * </pre>
+ *
+ * @return the last day-of-year adjuster, not null
+ */
+ public static TemporalAdjuster lastDayOfYear() {
+ return (temporal) -> temporal.with(DAY_OF_YEAR, temporal.range(DAY_OF_YEAR).getMaximum());
+ }
+
+ /**
+ * Returns the "first day of next year" adjuster, which returns a new date set to
+ * the first day of the next year.
+ * <p>
+ * The ISO calendar system behaves as follows:<br>
+ * The input 2011-01-15 will return 2012-01-01.
+ * <p>
+ * The behavior is suitable for use with most calendar systems.
+ * It is equivalent to:
+ * <pre>
+ * temporal.with(DAY_OF_YEAR, 1).plus(1, YEARS);
+ * </pre>
+ *
+ * @return the first day of next month adjuster, not null
+ */
+ public static TemporalAdjuster firstDayOfNextYear() {
+ return (temporal) -> temporal.with(DAY_OF_YEAR, 1).plus(1, YEARS);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns the first in month adjuster, which returns a new date
+ * in the same month with the first matching day-of-week.
+ * This is used for expressions like 'first Tuesday in March'.
+ * <p>
+ * The ISO calendar system behaves as follows:<br>
+ * The input 2011-12-15 for (MONDAY) will return 2011-12-05.<br>
+ * The input 2011-12-15 for (FRIDAY) will return 2011-12-02.<br>
+ * <p>
+ * The behavior is suitable for use with most calendar systems.
+ * It uses the {@code DAY_OF_WEEK} and {@code DAY_OF_MONTH} fields
+ * and the {@code DAYS} unit, and assumes a seven day week.
+ *
+ * @param dayOfWeek the day-of-week, not null
+ * @return the first in month adjuster, not null
+ */
+ public static TemporalAdjuster firstInMonth(DayOfWeek dayOfWeek) {
+ return TemporalAdjusters.dayOfWeekInMonth(1, dayOfWeek);
+ }
+
+ /**
+ * Returns the last in month adjuster, which returns a new date
+ * in the same month with the last matching day-of-week.
+ * This is used for expressions like 'last Tuesday in March'.
+ * <p>
+ * The ISO calendar system behaves as follows:<br>
+ * The input 2011-12-15 for (MONDAY) will return 2011-12-26.<br>
+ * The input 2011-12-15 for (FRIDAY) will return 2011-12-30.<br>
+ * <p>
+ * The behavior is suitable for use with most calendar systems.
+ * It uses the {@code DAY_OF_WEEK} and {@code DAY_OF_MONTH} fields
+ * and the {@code DAYS} unit, and assumes a seven day week.
+ *
+ * @param dayOfWeek the day-of-week, not null
+ * @return the first in month adjuster, not null
+ */
+ public static TemporalAdjuster lastInMonth(DayOfWeek dayOfWeek) {
+ return TemporalAdjusters.dayOfWeekInMonth(-1, dayOfWeek);
+ }
+
+ /**
+ * Returns the day-of-week in month adjuster, which returns a new date
+ * in the same month with the ordinal day-of-week.
+ * This is used for expressions like the 'second Tuesday in March'.
+ * <p>
+ * The ISO calendar system behaves as follows:<br>
+ * The input 2011-12-15 for (1,TUESDAY) will return 2011-12-06.<br>
+ * The input 2011-12-15 for (2,TUESDAY) will return 2011-12-13.<br>
+ * The input 2011-12-15 for (3,TUESDAY) will return 2011-12-20.<br>
+ * The input 2011-12-15 for (4,TUESDAY) will return 2011-12-27.<br>
+ * The input 2011-12-15 for (5,TUESDAY) will return 2012-01-03.<br>
+ * The input 2011-12-15 for (-1,TUESDAY) will return 2011-12-27 (last in month).<br>
+ * The input 2011-12-15 for (-4,TUESDAY) will return 2011-12-06 (3 weeks before last in month).<br>
+ * The input 2011-12-15 for (-5,TUESDAY) will return 2011-11-29 (4 weeks before last in month).<br>
+ * The input 2011-12-15 for (0,TUESDAY) will return 2011-11-29 (last in previous month).<br>
+ * <p>
+ * For a positive or zero ordinal, the algorithm is equivalent to finding the first
+ * day-of-week that matches within the month and then adding a number of weeks to it.
+ * For a negative ordinal, the algorithm is equivalent to finding the last
+ * day-of-week that matches within the month and then subtracting a number of weeks to it.
+ * The ordinal number of weeks is not validated and is interpreted leniently
+ * according to this algorithm. This definition means that an ordinal of zero finds
+ * the last matching day-of-week in the previous month.
+ * <p>
+ * The behavior is suitable for use with most calendar systems.
+ * It uses the {@code DAY_OF_WEEK} and {@code DAY_OF_MONTH} fields
+ * and the {@code DAYS} unit, and assumes a seven day week.
+ *
+ * @param ordinal the week within the month, unbounded but typically from -5 to 5
+ * @param dayOfWeek the day-of-week, not null
+ * @return the day-of-week in month adjuster, not null
+ */
+ public static TemporalAdjuster dayOfWeekInMonth(int ordinal, DayOfWeek dayOfWeek) {
+ Objects.requireNonNull(dayOfWeek, "dayOfWeek");
+ int dowValue = dayOfWeek.getValue();
+ if (ordinal >= 0) {
+ return (temporal) -> {
+ Temporal temp = temporal.with(DAY_OF_MONTH, 1);
+ int curDow = temp.get(DAY_OF_WEEK);
+ int dowDiff = (dowValue - curDow + 7) % 7;
+ dowDiff += (ordinal - 1L) * 7L; // safe from overflow
+ return temp.plus(dowDiff, DAYS);
+ };
+ } else {
+ return (temporal) -> {
+ Temporal temp = temporal.with(DAY_OF_MONTH, temporal.range(DAY_OF_MONTH).getMaximum());
+ int curDow = temp.get(DAY_OF_WEEK);
+ int daysDiff = dowValue - curDow;
+ daysDiff = (daysDiff == 0 ? 0 : (daysDiff > 0 ? daysDiff - 7 : daysDiff));
+ daysDiff -= (-ordinal - 1L) * 7L; // safe from overflow
+ return temp.plus(daysDiff, DAYS);
+ };
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns the next day-of-week adjuster, which adjusts the date to the
+ * first occurrence of the specified day-of-week after the date being adjusted.
+ * <p>
+ * The ISO calendar system behaves as follows:<br>
+ * The input 2011-01-15 (a Saturday) for parameter (MONDAY) will return 2011-01-17 (two days later).<br>
+ * The input 2011-01-15 (a Saturday) for parameter (WEDNESDAY) will return 2011-01-19 (four days later).<br>
+ * The input 2011-01-15 (a Saturday) for parameter (SATURDAY) will return 2011-01-22 (seven days later).
+ * <p>
+ * The behavior is suitable for use with most calendar systems.
+ * It uses the {@code DAY_OF_WEEK} field and the {@code DAYS} unit,
+ * and assumes a seven day week.
+ *
+ * @param dayOfWeek the day-of-week to move the date to, not null
+ * @return the next day-of-week adjuster, not null
+ */
+ public static TemporalAdjuster next(DayOfWeek dayOfWeek) {
+ int dowValue = dayOfWeek.getValue();
+ return (temporal) -> {
+ int calDow = temporal.get(DAY_OF_WEEK);
+ int daysDiff = calDow - dowValue;
+ return temporal.plus(daysDiff >= 0 ? 7 - daysDiff : -daysDiff, DAYS);
+ };
+ }
+
+ /**
+ * Returns the next-or-same day-of-week adjuster, which adjusts the date to the
+ * first occurrence of the specified day-of-week after the date being adjusted
+ * unless it is already on that day in which case the same object is returned.
+ * <p>
+ * The ISO calendar system behaves as follows:<br>
+ * The input 2011-01-15 (a Saturday) for parameter (MONDAY) will return 2011-01-17 (two days later).<br>
+ * The input 2011-01-15 (a Saturday) for parameter (WEDNESDAY) will return 2011-01-19 (four days later).<br>
+ * The input 2011-01-15 (a Saturday) for parameter (SATURDAY) will return 2011-01-15 (same as input).
+ * <p>
+ * The behavior is suitable for use with most calendar systems.
+ * It uses the {@code DAY_OF_WEEK} field and the {@code DAYS} unit,
+ * and assumes a seven day week.
+ *
+ * @param dayOfWeek the day-of-week to check for or move the date to, not null
+ * @return the next-or-same day-of-week adjuster, not null
+ */
+ public static TemporalAdjuster nextOrSame(DayOfWeek dayOfWeek) {
+ int dowValue = dayOfWeek.getValue();
+ return (temporal) -> {
+ int calDow = temporal.get(DAY_OF_WEEK);
+ if (calDow == dowValue) {
+ return temporal;
+ }
+ int daysDiff = calDow - dowValue;
+ return temporal.plus(daysDiff >= 0 ? 7 - daysDiff : -daysDiff, DAYS);
+ };
+ }
+
+ /**
+ * Returns the previous day-of-week adjuster, which adjusts the date to the
+ * first occurrence of the specified day-of-week before the date being adjusted.
+ * <p>
+ * The ISO calendar system behaves as follows:<br>
+ * The input 2011-01-15 (a Saturday) for parameter (MONDAY) will return 2011-01-10 (five days earlier).<br>
+ * The input 2011-01-15 (a Saturday) for parameter (WEDNESDAY) will return 2011-01-12 (three days earlier).<br>
+ * The input 2011-01-15 (a Saturday) for parameter (SATURDAY) will return 2011-01-08 (seven days earlier).
+ * <p>
+ * The behavior is suitable for use with most calendar systems.
+ * It uses the {@code DAY_OF_WEEK} field and the {@code DAYS} unit,
+ * and assumes a seven day week.
+ *
+ * @param dayOfWeek the day-of-week to move the date to, not null
+ * @return the previous day-of-week adjuster, not null
+ */
+ public static TemporalAdjuster previous(DayOfWeek dayOfWeek) {
+ int dowValue = dayOfWeek.getValue();
+ return (temporal) -> {
+ int calDow = temporal.get(DAY_OF_WEEK);
+ int daysDiff = dowValue - calDow;
+ return temporal.minus(daysDiff >= 0 ? 7 - daysDiff : -daysDiff, DAYS);
+ };
+ }
+
+ /**
+ * Returns the previous-or-same day-of-week adjuster, which adjusts the date to the
+ * first occurrence of the specified day-of-week before the date being adjusted
+ * unless it is already on that day in which case the same object is returned.
+ * <p>
+ * The ISO calendar system behaves as follows:<br>
+ * The input 2011-01-15 (a Saturday) for parameter (MONDAY) will return 2011-01-10 (five days earlier).<br>
+ * The input 2011-01-15 (a Saturday) for parameter (WEDNESDAY) will return 2011-01-12 (three days earlier).<br>
+ * The input 2011-01-15 (a Saturday) for parameter (SATURDAY) will return 2011-01-15 (same as input).
+ * <p>
+ * The behavior is suitable for use with most calendar systems.
+ * It uses the {@code DAY_OF_WEEK} field and the {@code DAYS} unit,
+ * and assumes a seven day week.
+ *
+ * @param dayOfWeek the day-of-week to check for or move the date to, not null
+ * @return the previous-or-same day-of-week adjuster, not null
+ */
+ public static TemporalAdjuster previousOrSame(DayOfWeek dayOfWeek) {
+ int dowValue = dayOfWeek.getValue();
+ return (temporal) -> {
+ int calDow = temporal.get(DAY_OF_WEEK);
+ if (calDow == dowValue) {
+ return temporal;
+ }
+ int daysDiff = dowValue - calDow;
+ return temporal.minus(daysDiff >= 0 ? 7 - daysDiff : -daysDiff, DAYS);
+ };
+ }
+
+}
diff --git a/java/time/temporal/TemporalAmount.java b/java/time/temporal/TemporalAmount.java
new file mode 100644
index 0000000..0d8b8a9
--- /dev/null
+++ b/java/time/temporal/TemporalAmount.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (c) 2012, 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) 2012, 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.temporal;
+
+import java.time.DateTimeException;
+import java.time.Duration;
+import java.time.Period;
+import java.util.List;
+
+/**
+ * Framework-level interface defining an amount of time, such as
+ * "6 hours", "8 days" or "2 years and 3 months".
+ * <p>
+ * This is the base interface type for amounts of time.
+ * An amount is distinct from a date or time-of-day in that it is not tied
+ * to any specific point on the time-line.
+ * <p>
+ * The amount can be thought of as a {@code Map} of {@link TemporalUnit} to
+ * {@code long}, exposed via {@link #getUnits()} and {@link #get(TemporalUnit)}.
+ * A simple case might have a single unit-value pair, such as "6 hours".
+ * A more complex case may have multiple unit-value pairs, such as
+ * "7 years, 3 months and 5 days".
+ * <p>
+ * There are two common implementations.
+ * {@link Period} is a date-based implementation, storing years, months and days.
+ * {@link Duration} is a time-based implementation, storing seconds and nanoseconds,
+ * but providing some access using other duration based units such as minutes,
+ * hours and fixed 24-hour days.
+ * <p>
+ * This interface is a framework-level interface that should not be widely
+ * used in application code. Instead, applications should create and pass
+ * around instances of concrete types, such as {@code Period} and {@code Duration}.
+ *
+ * @implSpec
+ * This interface places no restrictions on the mutability of implementations,
+ * however immutability is strongly recommended.
+ *
+ * @since 1.8
+ */
+public interface TemporalAmount {
+
+ /**
+ * Returns the value of the requested unit.
+ * The units returned from {@link #getUnits()} uniquely define the
+ * value of the {@code TemporalAmount}. A value must be returned
+ * for each unit listed in {@code getUnits}.
+ *
+ * @implSpec
+ * Implementations may declare support for units not listed by {@link #getUnits()}.
+ * Typically, the implementation would define additional units
+ * as conversions for the convenience of developers.
+ *
+ * @param unit the {@code TemporalUnit} for which to return the value
+ * @return the long value of the unit
+ * @throws DateTimeException if a value for the unit cannot be obtained
+ * @throws UnsupportedTemporalTypeException if the {@code unit} is not supported
+ */
+ long get(TemporalUnit unit);
+
+ /**
+ * Returns the list of units uniquely defining the value of this TemporalAmount.
+ * The list of {@code TemporalUnits} is defined by the implementation class.
+ * The list is a snapshot of the units at the time {@code getUnits}
+ * is called and is not mutable.
+ * The units are ordered from longest duration to the shortest duration
+ * of the unit.
+ *
+ * @implSpec
+ * The list of units completely and uniquely represents the
+ * state of the object without omissions, overlaps or duplication.
+ * The units are in order from longest duration to shortest.
+ *
+ * @return the List of {@code TemporalUnits}; not null
+ */
+ List<TemporalUnit> getUnits();
+
+ /**
+ * Adds to the specified temporal object.
+ * <p>
+ * Adds the amount to the specified temporal object using the logic
+ * encapsulated in the implementing class.
+ * <p>
+ * There are two equivalent ways of using this method.
+ * The first is to invoke this method directly.
+ * The second is to use {@link Temporal#plus(TemporalAmount)}:
+ * <pre>
+ * // These two lines are equivalent, but the second approach is recommended
+ * dateTime = amount.addTo(dateTime);
+ * dateTime = dateTime.plus(adder);
+ * </pre>
+ * It is recommended to use the second approach, {@code plus(TemporalAmount)},
+ * as it is a lot clearer to read in code.
+ *
+ * @implSpec
+ * The implementation must take the input object and add to it.
+ * The implementation defines the logic of the addition and is responsible for
+ * documenting that logic. It may use any method on {@code Temporal} to
+ * query the temporal object and perform the addition.
+ * The returned object must have the same observable type as the input object
+ * <p>
+ * The input object must not be altered.
+ * Instead, an adjusted copy of the original must be returned.
+ * This provides equivalent, safe behavior for immutable and mutable temporal objects.
+ * <p>
+ * The input temporal object may be in a calendar system other than ISO.
+ * Implementations may choose to document compatibility with other calendar systems,
+ * or reject non-ISO temporal objects by {@link TemporalQueries#chronology() querying the chronology}.
+ * <p>
+ * This method may be called from multiple threads in parallel.
+ * It must be thread-safe when invoked.
+ *
+ * @param temporal the temporal object to add the amount to, not null
+ * @return an object of the same observable type with the addition made, not null
+ * @throws DateTimeException if unable to add
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ Temporal addTo(Temporal temporal);
+
+ /**
+ * Subtracts this object from the specified temporal object.
+ * <p>
+ * Subtracts the amount from the specified temporal object using the logic
+ * encapsulated in the implementing class.
+ * <p>
+ * There are two equivalent ways of using this method.
+ * The first is to invoke this method directly.
+ * The second is to use {@link Temporal#minus(TemporalAmount)}:
+ * <pre>
+ * // these two lines are equivalent, but the second approach is recommended
+ * dateTime = amount.subtractFrom(dateTime);
+ * dateTime = dateTime.minus(amount);
+ * </pre>
+ * It is recommended to use the second approach, {@code minus(TemporalAmount)},
+ * as it is a lot clearer to read in code.
+ *
+ * @implSpec
+ * The implementation must take the input object and subtract from it.
+ * The implementation defines the logic of the subtraction and is responsible for
+ * documenting that logic. It may use any method on {@code Temporal} to
+ * query the temporal object and perform the subtraction.
+ * The returned object must have the same observable type as the input object
+ * <p>
+ * The input object must not be altered.
+ * Instead, an adjusted copy of the original must be returned.
+ * This provides equivalent, safe behavior for immutable and mutable temporal objects.
+ * <p>
+ * The input temporal object may be in a calendar system other than ISO.
+ * Implementations may choose to document compatibility with other calendar systems,
+ * or reject non-ISO temporal objects by {@link TemporalQueries#chronology() querying the chronology}.
+ * <p>
+ * This method may be called from multiple threads in parallel.
+ * It must be thread-safe when invoked.
+ *
+ * @param temporal the temporal object to subtract the amount from, not null
+ * @return an object of the same observable type with the subtraction made, not null
+ * @throws DateTimeException if unable to subtract
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ Temporal subtractFrom(Temporal temporal);
+}
diff --git a/java/time/temporal/TemporalField.java b/java/time/temporal/TemporalField.java
new file mode 100644
index 0000000..a82255a
--- /dev/null
+++ b/java/time/temporal/TemporalField.java
@@ -0,0 +1,394 @@
+/*
+ * Copyright (c) 2012, 2015, 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) 2012, 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.temporal;
+
+import java.time.DateTimeException;
+import java.time.chrono.Chronology;
+import java.time.format.ResolverStyle;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * A field of date-time, such as month-of-year or hour-of-minute.
+ * <p>
+ * Date and time is expressed using fields which partition the time-line into something
+ * meaningful for humans. Implementations of this interface represent those fields.
+ * <p>
+ * The most commonly used units are defined in {@link ChronoField}.
+ * Further fields are supplied in {@link IsoFields}, {@link WeekFields} and {@link JulianFields}.
+ * Fields can also be written by application code by implementing this interface.
+ * <p>
+ * The field works using double dispatch. Client code calls methods on a date-time like
+ * {@code LocalDateTime} which check if the field is a {@code ChronoField}.
+ * If it is, then the date-time must handle it.
+ * Otherwise, the method call is re-dispatched to the matching method in this interface.
+ *
+ * @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.
+ * Implementations should be {@code Serializable} where possible.
+ * An enum is as effective implementation choice.
+ *
+ * @since 1.8
+ */
+public interface TemporalField {
+
+ /**
+ * Gets the display name for the field in the requested locale.
+ * <p>
+ * If there is no display name for the locale then a suitable default must be returned.
+ * <p>
+ * The default implementation must check the locale is not null
+ * and return {@code toString()}.
+ *
+ * @param locale the locale to use, not null
+ * @return the display name for the locale or a suitable default, not null
+ */
+ default String getDisplayName(Locale locale) {
+ Objects.requireNonNull(locale, "locale");
+ return toString();
+ }
+
+ /**
+ * Gets the unit that the field is measured in.
+ * <p>
+ * The unit of the field is the period that varies within the range.
+ * For example, in the field 'MonthOfYear', the unit is 'Months'.
+ * See also {@link #getRangeUnit()}.
+ *
+ * @return the unit defining the base unit of the field, not null
+ */
+ TemporalUnit getBaseUnit();
+
+ /**
+ * Gets the range that the field is bound by.
+ * <p>
+ * The range of the field is the period that the field varies within.
+ * For example, in the field 'MonthOfYear', the range is 'Years'.
+ * See also {@link #getBaseUnit()}.
+ * <p>
+ * The range is never null. For example, the 'Year' field is shorthand for
+ * 'YearOfForever'. It therefore has a unit of 'Years' and a range of 'Forever'.
+ *
+ * @return the unit defining the range of the field, not null
+ */
+ TemporalUnit getRangeUnit();
+
+ /**
+ * Gets the range of valid values for the field.
+ * <p>
+ * All fields can be expressed as a {@code long} integer.
+ * This method returns an object that describes the valid range for that value.
+ * This method is generally only applicable to the ISO-8601 calendar system.
+ * <p>
+ * Note that the result only describes the minimum and maximum valid values
+ * and it is important not to read too much into them. For example, there
+ * could be values within the range that are invalid for the field.
+ *
+ * @return the range of valid values for the field, not null
+ */
+ ValueRange range();
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if this field represents a component of a date.
+ * <p>
+ * A field is date-based if it can be derived from
+ * {@link ChronoField#EPOCH_DAY EPOCH_DAY}.
+ * Note that it is valid for both {@code isDateBased()} and {@code isTimeBased()}
+ * to return false, such as when representing a field like minute-of-week.
+ *
+ * @return true if this field is a component of a date
+ */
+ boolean isDateBased();
+
+ /**
+ * Checks if this field represents a component of a time.
+ * <p>
+ * A field is time-based if it can be derived from
+ * {@link ChronoField#NANO_OF_DAY NANO_OF_DAY}.
+ * Note that it is valid for both {@code isDateBased()} and {@code isTimeBased()}
+ * to return false, such as when representing a field like minute-of-week.
+ *
+ * @return true if this field is a component of a time
+ */
+ boolean isTimeBased();
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if this field is supported by the temporal object.
+ * <p>
+ * This determines whether the temporal accessor supports this field.
+ * If this returns false, then the temporal cannot be queried for this field.
+ * <p>
+ * There are two equivalent ways of using this method.
+ * The first is to invoke this method directly.
+ * The second is to use {@link TemporalAccessor#isSupported(TemporalField)}:
+ * <pre>
+ * // these two lines are equivalent, but the second approach is recommended
+ * temporal = thisField.isSupportedBy(temporal);
+ * temporal = temporal.isSupported(thisField);
+ * </pre>
+ * It is recommended to use the second approach, {@code isSupported(TemporalField)},
+ * as it is a lot clearer to read in code.
+ * <p>
+ * Implementations should determine whether they are supported using the fields
+ * available in {@link ChronoField}.
+ *
+ * @param temporal the temporal object to query, not null
+ * @return true if the date-time can be queried for this field, false if not
+ */
+ boolean isSupportedBy(TemporalAccessor temporal);
+
+ /**
+ * Get the range of valid values for this field using the temporal object to
+ * refine the result.
+ * <p>
+ * This uses the temporal object to find the range of valid values for the field.
+ * This is similar to {@link #range()}, however this method refines the result
+ * using the temporal. For example, if the field is {@code DAY_OF_MONTH} the
+ * {@code range} method is not accurate as there are four possible month lengths,
+ * 28, 29, 30 and 31 days. Using this method with a date allows the range to be
+ * accurate, returning just one of those four options.
+ * <p>
+ * There are two equivalent ways of using this method.
+ * The first is to invoke this method directly.
+ * The second is to use {@link TemporalAccessor#range(TemporalField)}:
+ * <pre>
+ * // these two lines are equivalent, but the second approach is recommended
+ * temporal = thisField.rangeRefinedBy(temporal);
+ * temporal = temporal.range(thisField);
+ * </pre>
+ * It is recommended to use the second approach, {@code range(TemporalField)},
+ * as it is a lot clearer to read in code.
+ * <p>
+ * Implementations should perform any queries or calculations using the fields
+ * available in {@link ChronoField}.
+ * If the field is not supported an {@code UnsupportedTemporalTypeException} must be thrown.
+ *
+ * @param temporal the temporal object used to refine the result, not null
+ * @return the range of valid values for this field, not null
+ * @throws DateTimeException if the range for the field cannot be obtained
+ * @throws UnsupportedTemporalTypeException if the field is not supported by the temporal
+ */
+ ValueRange rangeRefinedBy(TemporalAccessor temporal);
+
+ /**
+ * Gets the value of this field from the specified temporal object.
+ * <p>
+ * This queries the temporal object for the value of this field.
+ * <p>
+ * There are two equivalent ways of using this method.
+ * The first is to invoke this method directly.
+ * The second is to use {@link TemporalAccessor#getLong(TemporalField)}
+ * (or {@link TemporalAccessor#get(TemporalField)}):
+ * <pre>
+ * // these two lines are equivalent, but the second approach is recommended
+ * temporal = thisField.getFrom(temporal);
+ * temporal = temporal.getLong(thisField);
+ * </pre>
+ * It is recommended to use the second approach, {@code getLong(TemporalField)},
+ * as it is a lot clearer to read in code.
+ * <p>
+ * Implementations should perform any queries or calculations using the fields
+ * available in {@link ChronoField}.
+ * If the field is not supported an {@code UnsupportedTemporalTypeException} must be thrown.
+ *
+ * @param temporal the temporal object to query, not null
+ * @return the value of this field, not null
+ * @throws DateTimeException if a value for the field cannot be obtained
+ * @throws UnsupportedTemporalTypeException if the field is not supported by the temporal
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ long getFrom(TemporalAccessor temporal);
+
+ /**
+ * Returns a copy of the specified temporal object with the value of this field set.
+ * <p>
+ * This returns a new temporal object based on the specified one with the value for
+ * this field changed. For example, on a {@code LocalDate}, this could be used to
+ * set the year, month or day-of-month.
+ * The returned object has the same observable type as the specified object.
+ * <p>
+ * In some cases, changing a field is not fully defined. For example, if the target object is
+ * a date representing the 31st January, then changing the month to February would be unclear.
+ * In cases like this, the implementation is responsible for resolving the result.
+ * Typically it will choose the previous valid date, which would be the last valid
+ * day of February in this example.
+ * <p>
+ * There are two equivalent ways of using this method.
+ * The first is to invoke this method directly.
+ * The second is to use {@link Temporal#with(TemporalField, long)}:
+ * <pre>
+ * // these two lines are equivalent, but the second approach is recommended
+ * temporal = thisField.adjustInto(temporal);
+ * temporal = temporal.with(thisField);
+ * </pre>
+ * It is recommended to use the second approach, {@code with(TemporalField)},
+ * as it is a lot clearer to read in code.
+ * <p>
+ * Implementations should perform any queries or calculations using the fields
+ * available in {@link ChronoField}.
+ * If the field is not supported an {@code UnsupportedTemporalTypeException} must be thrown.
+ * <p>
+ * Implementations must not alter the specified temporal object.
+ * Instead, an adjusted copy of the original must be returned.
+ * This provides equivalent, safe behavior for immutable and mutable implementations.
+ *
+ * @param <R> the type of the Temporal object
+ * @param temporal the temporal object to adjust, not null
+ * @param newValue the new value of the field
+ * @return the adjusted temporal object, not null
+ * @throws DateTimeException if the field cannot be set
+ * @throws UnsupportedTemporalTypeException if the field is not supported by the temporal
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ <R extends Temporal> R adjustInto(R temporal, long newValue);
+
+ /**
+ * Resolves this field to provide a simpler alternative or a date.
+ * <p>
+ * This method is invoked during the resolve phase of parsing.
+ * It is designed to allow application defined fields to be simplified into
+ * more standard fields, such as those on {@code ChronoField}, or into a date.
+ * <p>
+ * Applications should not normally invoke this method directly.
+ *
+ * @implSpec
+ * If an implementation represents a field that can be simplified, or
+ * combined with others, then this method must be implemented.
+ * <p>
+ * The specified map contains the current state of the parse.
+ * The map is mutable and must be mutated to resolve the field and
+ * any related fields. This method will only be invoked during parsing
+ * if the map contains this field, and implementations should therefore
+ * assume this field is present.
+ * <p>
+ * Resolving a field will consist of looking at the value of this field,
+ * and potentially other fields, and either updating the map with a
+ * simpler value, such as a {@code ChronoField}, or returning a
+ * complete {@code ChronoLocalDate}. If a resolve is successful,
+ * the code must remove all the fields that were resolved from the map,
+ * including this field.
+ * <p>
+ * For example, the {@code IsoFields} class contains the quarter-of-year
+ * and day-of-quarter fields. The implementation of this method in that class
+ * resolves the two fields plus the {@link ChronoField#YEAR YEAR} into a
+ * complete {@code LocalDate}. The resolve method will remove all three
+ * fields from the map before returning the {@code LocalDate}.
+ * <p>
+ * A partially complete temporal is used to allow the chronology and zone
+ * to be queried. In general, only the chronology will be needed.
+ * Querying items other than the zone or chronology is undefined and
+ * must not be relied on.
+ * The behavior of other methods such as {@code get}, {@code getLong},
+ * {@code range} and {@code isSupported} is unpredictable and the results undefined.
+ * <p>
+ * If resolution should be possible, but the data is invalid, the resolver
+ * style should be used to determine an appropriate level of leniency, which
+ * may require throwing a {@code DateTimeException} or {@code ArithmeticException}.
+ * If no resolution is possible, the resolve method must return null.
+ * <p>
+ * When resolving time fields, the map will be altered and null returned.
+ * When resolving date fields, the date is normally returned from the method,
+ * with the map altered to remove the resolved fields. However, it would also
+ * be acceptable for the date fields to be resolved into other {@code ChronoField}
+ * instances that can produce a date, such as {@code EPOCH_DAY}.
+ * <p>
+ * Not all {@code TemporalAccessor} implementations are accepted as return values.
+ * Implementations that call this method must accept {@code ChronoLocalDate},
+ * {@code ChronoLocalDateTime}, {@code ChronoZonedDateTime} and {@code LocalTime}.
+ * <p>
+ * The default implementation must return null.
+ *
+ * @param fieldValues the map of fields to values, which can be updated, not null
+ * @param partialTemporal the partially complete temporal to query for zone and
+ * chronology; querying for other things is undefined and not recommended, not null
+ * @param resolverStyle the requested type of resolve, not null
+ * @return the resolved temporal object; null if resolving only
+ * changed the map, or no resolve occurred
+ * @throws ArithmeticException if numeric overflow occurs
+ * @throws DateTimeException if resolving results in an error. This must not be thrown
+ * by querying a field on the temporal without first checking if it is supported
+ */
+ default TemporalAccessor resolve(
+ Map<TemporalField, Long> fieldValues,
+ TemporalAccessor partialTemporal,
+ ResolverStyle resolverStyle) {
+ return null;
+ }
+
+ /**
+ * Gets a descriptive name for the field.
+ * <p>
+ * The should be of the format 'BaseOfRange', such as 'MonthOfYear',
+ * unless the field has a range of {@code FOREVER}, when only
+ * the base unit is mentioned, such as 'Year' or 'Era'.
+ *
+ * @return the name of the field, not null
+ */
+ @Override
+ String toString();
+
+
+}
diff --git a/java/time/temporal/TemporalQueries.java b/java/time/temporal/TemporalQueries.java
new file mode 100644
index 0000000..6b1777a
--- /dev/null
+++ b/java/time/temporal/TemporalQueries.java
@@ -0,0 +1,398 @@
+/*
+ * Copyright (c) 2012, 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) 2007-2012, 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.temporal;
+
+import static java.time.temporal.ChronoField.EPOCH_DAY;
+import static java.time.temporal.ChronoField.NANO_OF_DAY;
+import static java.time.temporal.ChronoField.OFFSET_SECONDS;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.chrono.Chronology;
+
+/**
+ * Common implementations of {@code TemporalQuery}.
+ * <p>
+ * This class provides common implementations of {@link TemporalQuery}.
+ * These are defined here as they must be constants, and the definition
+ * of lambdas does not guarantee that. By assigning them once here,
+ * they become 'normal' Java constants.
+ * <p>
+ * Queries are a key tool for extracting information from temporal objects.
+ * They exist to externalize the process of querying, permitting different
+ * approaches, as per the strategy design pattern.
+ * Examples might be a query that checks if the date is the day before February 29th
+ * in a leap year, or calculates the number of days to your next birthday.
+ * <p>
+ * The {@link TemporalField} interface provides another mechanism for querying
+ * temporal objects. That interface is limited to returning a {@code long}.
+ * By contrast, queries can return any type.
+ * <p>
+ * There are two equivalent ways of using a {@code TemporalQuery}.
+ * The first is to invoke the method on this interface directly.
+ * The second is to use {@link TemporalAccessor#query(TemporalQuery)}:
+ * <pre>
+ * // these two lines are equivalent, but the second approach is recommended
+ * temporal = thisQuery.queryFrom(temporal);
+ * temporal = temporal.query(thisQuery);
+ * </pre>
+ * It is recommended to use the second approach, {@code query(TemporalQuery)},
+ * as it is a lot clearer to read in code.
+ * <p>
+ * The most common implementations are method references, such as
+ * {@code LocalDate::from} and {@code ZoneId::from}.
+ * Additional common queries are provided to return:
+ * <ul>
+ * <li> a Chronology,
+ * <li> a LocalDate,
+ * <li> a LocalTime,
+ * <li> a ZoneOffset,
+ * <li> a precision,
+ * <li> a zone, or
+ * <li> a zoneId.
+ * </ul>
+ *
+ * @since 1.8
+ */
+public final class TemporalQueries {
+ // note that it is vital that each method supplies a constant, not a
+ // calculated value, as they will be checked for using ==
+ // it is also vital that each constant is different (due to the == checking)
+ // as such, alterations to this code must be done with care
+
+ /**
+ * Private constructor since this is a utility class.
+ */
+ private TemporalQueries() {
+ }
+
+ //-----------------------------------------------------------------------
+ // special constants should be used to extract information from a TemporalAccessor
+ // that cannot be derived in other ways
+ // Javadoc added here, so as to pretend they are more normal than they really are
+
+ /**
+ * A strict query for the {@code ZoneId}.
+ * <p>
+ * This queries a {@code TemporalAccessor} for the zone.
+ * The zone is only returned if the date-time conceptually contains a {@code ZoneId}.
+ * It will not be returned if the date-time only conceptually has an {@code ZoneOffset}.
+ * Thus a {@link java.time.ZonedDateTime} will return the result of {@code getZone()},
+ * but an {@link java.time.OffsetDateTime} will return null.
+ * <p>
+ * In most cases, applications should use {@link #zone()} as this query is too strict.
+ * <p>
+ * The result from JDK classes implementing {@code TemporalAccessor} is as follows:<br>
+ * {@code LocalDate} returns null<br>
+ * {@code LocalTime} returns null<br>
+ * {@code LocalDateTime} returns null<br>
+ * {@code ZonedDateTime} returns the associated zone<br>
+ * {@code OffsetTime} returns null<br>
+ * {@code OffsetDateTime} returns null<br>
+ * {@code ChronoLocalDate} returns null<br>
+ * {@code ChronoLocalDateTime} returns null<br>
+ * {@code ChronoZonedDateTime} returns the associated zone<br>
+ * {@code Era} returns null<br>
+ * {@code DayOfWeek} returns null<br>
+ * {@code Month} returns null<br>
+ * {@code Year} returns null<br>
+ * {@code YearMonth} returns null<br>
+ * {@code MonthDay} returns null<br>
+ * {@code ZoneOffset} returns null<br>
+ * {@code Instant} returns null<br>
+ *
+ * @return a query that can obtain the zone ID of a temporal, not null
+ */
+ public static TemporalQuery<ZoneId> zoneId() {
+ return TemporalQueries.ZONE_ID;
+ }
+
+ /**
+ * A query for the {@code Chronology}.
+ * <p>
+ * This queries a {@code TemporalAccessor} for the chronology.
+ * If the target {@code TemporalAccessor} represents a date, or part of a date,
+ * then it should return the chronology that the date is expressed in.
+ * As a result of this definition, objects only representing time, such as
+ * {@code LocalTime}, will return null.
+ * <p>
+ * The result from JDK classes implementing {@code TemporalAccessor} is as follows:<br>
+ * {@code LocalDate} returns {@code IsoChronology.INSTANCE}<br>
+ * {@code LocalTime} returns null (does not represent a date)<br>
+ * {@code LocalDateTime} returns {@code IsoChronology.INSTANCE}<br>
+ * {@code ZonedDateTime} returns {@code IsoChronology.INSTANCE}<br>
+ * {@code OffsetTime} returns null (does not represent a date)<br>
+ * {@code OffsetDateTime} returns {@code IsoChronology.INSTANCE}<br>
+ * {@code ChronoLocalDate} returns the associated chronology<br>
+ * {@code ChronoLocalDateTime} returns the associated chronology<br>
+ * {@code ChronoZonedDateTime} returns the associated chronology<br>
+ * {@code Era} returns the associated chronology<br>
+ * {@code DayOfWeek} returns null (shared across chronologies)<br>
+ * {@code Month} returns {@code IsoChronology.INSTANCE}<br>
+ * {@code Year} returns {@code IsoChronology.INSTANCE}<br>
+ * {@code YearMonth} returns {@code IsoChronology.INSTANCE}<br>
+ * {@code MonthDay} returns null {@code IsoChronology.INSTANCE}<br>
+ * {@code ZoneOffset} returns null (does not represent a date)<br>
+ * {@code Instant} returns null (does not represent a date)<br>
+ * <p>
+ * The method {@link java.time.chrono.Chronology#from(TemporalAccessor)} can be used as a
+ * {@code TemporalQuery} via a method reference, {@code Chronology::from}.
+ * That method is equivalent to this query, except that it throws an
+ * exception if a chronology cannot be obtained.
+ *
+ * @return a query that can obtain the chronology of a temporal, not null
+ */
+ public static TemporalQuery<Chronology> chronology() {
+ return TemporalQueries.CHRONO;
+ }
+
+ /**
+ * A query for the smallest supported unit.
+ * <p>
+ * This queries a {@code TemporalAccessor} for the time precision.
+ * If the target {@code TemporalAccessor} represents a consistent or complete date-time,
+ * date or time then this must return the smallest precision actually supported.
+ * Note that fields such as {@code NANO_OF_DAY} and {@code NANO_OF_SECOND}
+ * are defined to always return ignoring the precision, thus this is the only
+ * way to find the actual smallest supported unit.
+ * For example, were {@code GregorianCalendar} to implement {@code TemporalAccessor}
+ * it would return a precision of {@code MILLIS}.
+ * <p>
+ * The result from JDK classes implementing {@code TemporalAccessor} is as follows:<br>
+ * {@code LocalDate} returns {@code DAYS}<br>
+ * {@code LocalTime} returns {@code NANOS}<br>
+ * {@code LocalDateTime} returns {@code NANOS}<br>
+ * {@code ZonedDateTime} returns {@code NANOS}<br>
+ * {@code OffsetTime} returns {@code NANOS}<br>
+ * {@code OffsetDateTime} returns {@code NANOS}<br>
+ * {@code ChronoLocalDate} returns {@code DAYS}<br>
+ * {@code ChronoLocalDateTime} returns {@code NANOS}<br>
+ * {@code ChronoZonedDateTime} returns {@code NANOS}<br>
+ * {@code Era} returns {@code ERAS}<br>
+ * {@code DayOfWeek} returns {@code DAYS}<br>
+ * {@code Month} returns {@code MONTHS}<br>
+ * {@code Year} returns {@code YEARS}<br>
+ * {@code YearMonth} returns {@code MONTHS}<br>
+ * {@code MonthDay} returns null (does not represent a complete date or time)<br>
+ * {@code ZoneOffset} returns null (does not represent a date or time)<br>
+ * {@code Instant} returns {@code NANOS}<br>
+ *
+ * @return a query that can obtain the precision of a temporal, not null
+ */
+ public static TemporalQuery<TemporalUnit> precision() {
+ return TemporalQueries.PRECISION;
+ }
+
+ //-----------------------------------------------------------------------
+ // non-special constants are standard queries that derive information from other information
+ /**
+ * A lenient query for the {@code ZoneId}, falling back to the {@code ZoneOffset}.
+ * <p>
+ * This queries a {@code TemporalAccessor} for the zone.
+ * It first tries to obtain the zone, using {@link #zoneId()}.
+ * If that is not found it tries to obtain the {@link #offset()}.
+ * Thus a {@link java.time.ZonedDateTime} will return the result of {@code getZone()},
+ * while an {@link java.time.OffsetDateTime} will return the result of {@code getOffset()}.
+ * <p>
+ * In most cases, applications should use this query rather than {@code #zoneId()}.
+ * <p>
+ * The method {@link ZoneId#from(TemporalAccessor)} can be used as a
+ * {@code TemporalQuery} via a method reference, {@code ZoneId::from}.
+ * That method is equivalent to this query, except that it throws an
+ * exception if a zone cannot be obtained.
+ *
+ * @return a query that can obtain the zone ID or offset of a temporal, not null
+ */
+ public static TemporalQuery<ZoneId> zone() {
+ return TemporalQueries.ZONE;
+ }
+
+ /**
+ * A query for {@code ZoneOffset} returning null if not found.
+ * <p>
+ * This returns a {@code TemporalQuery} that can be used to query a temporal
+ * object for the offset. The query will return null if the temporal
+ * object cannot supply an offset.
+ * <p>
+ * The query implementation examines the {@link ChronoField#OFFSET_SECONDS OFFSET_SECONDS}
+ * field and uses it to create a {@code ZoneOffset}.
+ * <p>
+ * The method {@link java.time.ZoneOffset#from(TemporalAccessor)} can be used as a
+ * {@code TemporalQuery} via a method reference, {@code ZoneOffset::from}.
+ * This query and {@code ZoneOffset::from} will return the same result if the
+ * temporal object contains an offset. If the temporal object does not contain
+ * an offset, then the method reference will throw an exception, whereas this
+ * query will return null.
+ *
+ * @return a query that can obtain the offset of a temporal, not null
+ */
+ public static TemporalQuery<ZoneOffset> offset() {
+ return TemporalQueries.OFFSET;
+ }
+
+ /**
+ * A query for {@code LocalDate} returning null if not found.
+ * <p>
+ * This returns a {@code TemporalQuery} that can be used to query a temporal
+ * object for the local date. The query will return null if the temporal
+ * object cannot supply a local date.
+ * <p>
+ * The query implementation examines the {@link ChronoField#EPOCH_DAY EPOCH_DAY}
+ * field and uses it to create a {@code LocalDate}.
+ * <p>
+ * The method {@link ZoneOffset#from(TemporalAccessor)} can be used as a
+ * {@code TemporalQuery} via a method reference, {@code LocalDate::from}.
+ * This query and {@code LocalDate::from} will return the same result if the
+ * temporal object contains a date. If the temporal object does not contain
+ * a date, then the method reference will throw an exception, whereas this
+ * query will return null.
+ *
+ * @return a query that can obtain the date of a temporal, not null
+ */
+ public static TemporalQuery<LocalDate> localDate() {
+ return TemporalQueries.LOCAL_DATE;
+ }
+
+ /**
+ * A query for {@code LocalTime} returning null if not found.
+ * <p>
+ * This returns a {@code TemporalQuery} that can be used to query a temporal
+ * object for the local time. The query will return null if the temporal
+ * object cannot supply a local time.
+ * <p>
+ * The query implementation examines the {@link ChronoField#NANO_OF_DAY NANO_OF_DAY}
+ * field and uses it to create a {@code LocalTime}.
+ * <p>
+ * The method {@link ZoneOffset#from(TemporalAccessor)} can be used as a
+ * {@code TemporalQuery} via a method reference, {@code LocalTime::from}.
+ * This query and {@code LocalTime::from} will return the same result if the
+ * temporal object contains a time. If the temporal object does not contain
+ * a time, then the method reference will throw an exception, whereas this
+ * query will return null.
+ *
+ * @return a query that can obtain the time of a temporal, not null
+ */
+ public static TemporalQuery<LocalTime> localTime() {
+ return TemporalQueries.LOCAL_TIME;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * A strict query for the {@code ZoneId}.
+ */
+ static final TemporalQuery<ZoneId> ZONE_ID = (temporal) ->
+ temporal.query(TemporalQueries.ZONE_ID);
+
+ /**
+ * A query for the {@code Chronology}.
+ */
+ static final TemporalQuery<Chronology> CHRONO = (temporal) ->
+ temporal.query(TemporalQueries.CHRONO);
+
+ /**
+ * A query for the smallest supported unit.
+ */
+ static final TemporalQuery<TemporalUnit> PRECISION = (temporal) ->
+ temporal.query(TemporalQueries.PRECISION);
+
+ //-----------------------------------------------------------------------
+ /**
+ * A query for {@code ZoneOffset} returning null if not found.
+ */
+ static final TemporalQuery<ZoneOffset> OFFSET = (temporal) -> {
+ if (temporal.isSupported(OFFSET_SECONDS)) {
+ return ZoneOffset.ofTotalSeconds(temporal.get(OFFSET_SECONDS));
+ }
+ return null;
+ };
+
+ /**
+ * A lenient query for the {@code ZoneId}, falling back to the {@code ZoneOffset}.
+ */
+ static final TemporalQuery<ZoneId> ZONE = (temporal) -> {
+ ZoneId zone = temporal.query(ZONE_ID);
+ return (zone != null ? zone : temporal.query(OFFSET));
+ };
+
+ /**
+ * A query for {@code LocalDate} returning null if not found.
+ */
+ static final TemporalQuery<LocalDate> LOCAL_DATE = (temporal) -> {
+ if (temporal.isSupported(EPOCH_DAY)) {
+ return LocalDate.ofEpochDay(temporal.getLong(EPOCH_DAY));
+ }
+ return null;
+ };
+
+ /**
+ * A query for {@code LocalTime} returning null if not found.
+ */
+ static final TemporalQuery<LocalTime> LOCAL_TIME = (temporal) -> {
+ if (temporal.isSupported(NANO_OF_DAY)) {
+ return LocalTime.ofNanoOfDay(temporal.getLong(NANO_OF_DAY));
+ }
+ return null;
+ };
+
+}
diff --git a/java/time/temporal/TemporalQuery.java b/java/time/temporal/TemporalQuery.java
new file mode 100644
index 0000000..9614296
--- /dev/null
+++ b/java/time/temporal/TemporalQuery.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2012, 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) 2012, 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.temporal;
+
+import java.time.DateTimeException;
+
+/**
+ * Strategy for querying a temporal object.
+ * <p>
+ * Queries are a key tool for extracting information from temporal objects.
+ * They exist to externalize the process of querying, permitting different
+ * approaches, as per the strategy design pattern.
+ * Examples might be a query that checks if the date is the day before February 29th
+ * in a leap year, or calculates the number of days to your next birthday.
+ * <p>
+ * The {@link TemporalField} interface provides another mechanism for querying
+ * temporal objects. That interface is limited to returning a {@code long}.
+ * By contrast, queries can return any type.
+ * <p>
+ * There are two equivalent ways of using a {@code TemporalQuery}.
+ * The first is to invoke the method on this interface directly.
+ * The second is to use {@link TemporalAccessor#query(TemporalQuery)}:
+ * <pre>
+ * // these two lines are equivalent, but the second approach is recommended
+ * temporal = thisQuery.queryFrom(temporal);
+ * temporal = temporal.query(thisQuery);
+ * </pre>
+ * It is recommended to use the second approach, {@code query(TemporalQuery)},
+ * as it is a lot clearer to read in code.
+ * <p>
+ * The most common implementations are method references, such as
+ * {@code LocalDate::from} and {@code ZoneId::from}.
+ * Additional common queries are provided as static methods in {@link TemporalQueries}.
+ *
+ * @implSpec
+ * This interface places no restrictions on the mutability of implementations,
+ * however immutability is strongly recommended.
+ *
+ * @param <R> the type returned from the query
+ *
+ * @since 1.8
+ */
+@FunctionalInterface
+public interface TemporalQuery<R> {
+
+ /**
+ * Queries the specified temporal object.
+ * <p>
+ * This queries the specified temporal object to return an object using the logic
+ * encapsulated in the implementing class.
+ * Examples might be a query that checks if the date is the day before February 29th
+ * in a leap year, or calculates the number of days to your next birthday.
+ * <p>
+ * There are two equivalent ways of using this method.
+ * The first is to invoke this method directly.
+ * The second is to use {@link TemporalAccessor#query(TemporalQuery)}:
+ * <pre>
+ * // these two lines are equivalent, but the second approach is recommended
+ * temporal = thisQuery.queryFrom(temporal);
+ * temporal = temporal.query(thisQuery);
+ * </pre>
+ * It is recommended to use the second approach, {@code query(TemporalQuery)},
+ * as it is a lot clearer to read in code.
+ *
+ * @implSpec
+ * The implementation must take the input object and query it.
+ * The implementation defines the logic of the query and is responsible for
+ * documenting that logic.
+ * It may use any method on {@code TemporalAccessor} to determine the result.
+ * The input object must not be altered.
+ * <p>
+ * The input temporal object may be in a calendar system other than ISO.
+ * Implementations may choose to document compatibility with other calendar systems,
+ * or reject non-ISO temporal objects by {@link TemporalQueries#chronology() querying the chronology}.
+ * <p>
+ * This method may be called from multiple threads in parallel.
+ * It must be thread-safe when invoked.
+ *
+ * @param temporal the temporal object to query, not null
+ * @return the queried value, may return null to indicate not found
+ * @throws DateTimeException if unable to query
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ R queryFrom(TemporalAccessor temporal);
+
+}
diff --git a/java/time/temporal/TemporalUnit.java b/java/time/temporal/TemporalUnit.java
new file mode 100644
index 0000000..949104d
--- /dev/null
+++ b/java/time/temporal/TemporalUnit.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 2012, 2015, 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) 2012, 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.temporal;
+
+import java.time.DateTimeException;
+import java.time.Duration;
+import java.time.LocalTime;
+import java.time.Period;
+import java.time.chrono.ChronoLocalDate;
+import java.time.chrono.ChronoLocalDateTime;
+import java.time.chrono.ChronoZonedDateTime;
+
+/**
+ * A unit of date-time, such as Days or Hours.
+ * <p>
+ * Measurement of time is built on units, such as years, months, days, hours, minutes and seconds.
+ * Implementations of this interface represent those units.
+ * <p>
+ * An instance of this interface represents the unit itself, rather than an amount of the unit.
+ * See {@link Period} for a class that represents an amount in terms of the common units.
+ * <p>
+ * The most commonly used units are defined in {@link ChronoUnit}.
+ * Further units are supplied in {@link IsoFields}.
+ * Units can also be written by application code by implementing this interface.
+ * <p>
+ * The unit works using double dispatch. Client code calls methods on a date-time like
+ * {@code LocalDateTime} which check if the unit is a {@code ChronoUnit}.
+ * If it is, then the date-time must handle it.
+ * Otherwise, the method call is re-dispatched to the matching method in this interface.
+ *
+ * @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.
+ * It is recommended to use an enum where possible.
+ *
+ * @since 1.8
+ */
+public interface TemporalUnit {
+
+ /**
+ * Gets the duration of this unit, which may be an estimate.
+ * <p>
+ * All units return a duration measured in standard nanoseconds from this method.
+ * The duration will be positive and non-zero.
+ * For example, an hour has a duration of {@code 60 * 60 * 1,000,000,000ns}.
+ * <p>
+ * Some units may return an accurate duration while others return an estimate.
+ * For example, days have an estimated duration due to the possibility of
+ * daylight saving time changes.
+ * To determine if the duration is an estimate, use {@link #isDurationEstimated()}.
+ *
+ * @return the duration of this unit, which may be an estimate, not null
+ */
+ Duration getDuration();
+
+ /**
+ * Checks if the duration of the unit is an estimate.
+ * <p>
+ * All units have a duration, however the duration is not always accurate.
+ * For example, days have an estimated duration due to the possibility of
+ * daylight saving time changes.
+ * This method returns true if the duration is an estimate and false if it is
+ * accurate. Note that accurate/estimated ignores leap seconds.
+ *
+ * @return true if the duration is estimated, false if accurate
+ */
+ boolean isDurationEstimated();
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if this unit represents a component of a date.
+ * <p>
+ * A date is time-based if it can be used to imply meaning from a date.
+ * It must have a {@linkplain #getDuration() duration} that is an integral
+ * multiple of the length of a standard day.
+ * Note that it is valid for both {@code isDateBased()} and {@code isTimeBased()}
+ * to return false, such as when representing a unit like 36 hours.
+ *
+ * @return true if this unit is a component of a date
+ */
+ boolean isDateBased();
+
+ /**
+ * Checks if this unit represents a component of a time.
+ * <p>
+ * A unit is time-based if it can be used to imply meaning from a time.
+ * It must have a {@linkplain #getDuration() duration} that divides into
+ * the length of a standard day without remainder.
+ * Note that it is valid for both {@code isDateBased()} and {@code isTimeBased()}
+ * to return false, such as when representing a unit like 36 hours.
+ *
+ * @return true if this unit is a component of a time
+ */
+ boolean isTimeBased();
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if this unit is supported by the specified temporal object.
+ * <p>
+ * This checks that the implementing date-time can add/subtract this unit.
+ * This can be used to avoid throwing an exception.
+ * <p>
+ * This default implementation derives the value using
+ * {@link Temporal#plus(long, TemporalUnit)}.
+ *
+ * @param temporal the temporal object to check, not null
+ * @return true if the unit is supported
+ */
+ default boolean isSupportedBy(Temporal temporal) {
+ if (temporal instanceof LocalTime) {
+ return isTimeBased();
+ }
+ if (temporal instanceof ChronoLocalDate) {
+ return isDateBased();
+ }
+ if (temporal instanceof ChronoLocalDateTime || temporal instanceof ChronoZonedDateTime) {
+ return true;
+ }
+ try {
+ temporal.plus(1, this);
+ return true;
+ } catch (UnsupportedTemporalTypeException ex) {
+ return false;
+ } catch (RuntimeException ex) {
+ try {
+ temporal.plus(-1, this);
+ return true;
+ } catch (RuntimeException ex2) {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Returns a copy of the specified temporal object with the specified period added.
+ * <p>
+ * The period added is a multiple of this unit. For example, this method
+ * could be used to add "3 days" to a date by calling this method on the
+ * instance representing "days", passing the date and the period "3".
+ * The period to be added may be negative, which is equivalent to subtraction.
+ * <p>
+ * There are two equivalent ways of using this method.
+ * The first is to invoke this method directly.
+ * The second is to use {@link Temporal#plus(long, TemporalUnit)}:
+ * <pre>
+ * // these two lines are equivalent, but the second approach is recommended
+ * temporal = thisUnit.addTo(temporal);
+ * temporal = temporal.plus(thisUnit);
+ * </pre>
+ * It is recommended to use the second approach, {@code plus(TemporalUnit)},
+ * as it is a lot clearer to read in code.
+ * <p>
+ * Implementations should perform any queries or calculations using the units
+ * available in {@link ChronoUnit} or the fields available in {@link ChronoField}.
+ * If the unit is not supported an {@code UnsupportedTemporalTypeException} must be thrown.
+ * <p>
+ * Implementations must not alter the specified temporal object.
+ * Instead, an adjusted copy of the original must be returned.
+ * This provides equivalent, safe behavior for immutable and mutable implementations.
+ *
+ * @param <R> the type of the Temporal object
+ * @param temporal the temporal object to adjust, not null
+ * @param amount the amount of this unit to add, positive or negative
+ * @return the adjusted temporal object, not null
+ * @throws DateTimeException if the amount cannot be added
+ * @throws UnsupportedTemporalTypeException if the unit is not supported by the temporal
+ */
+ <R extends Temporal> R addTo(R temporal, long amount);
+
+ //-----------------------------------------------------------------------
+ /**
+ * Calculates the amount of time between two temporal objects.
+ * <p>
+ * This calculates the amount in terms of this unit. The start and end
+ * points are supplied as temporal objects and must be of compatible types.
+ * The implementation will convert the second type to be an instance of the
+ * first type before the calculating the amount.
+ * The result will be negative if the end is before the start.
+ * For example, the amount in hours between two temporal objects can be
+ * calculated using {@code HOURS.between(startTime, endTime)}.
+ * <p>
+ * The calculation returns a whole number, representing the number of
+ * complete units between the two temporals.
+ * For example, the amount in hours between the times 11:30 and 13:29
+ * will only be one hour as it is one minute short of two hours.
+ * <p>
+ * There are two equivalent ways of using this method.
+ * The first is to invoke this method directly.
+ * The second is to use {@link Temporal#until(Temporal, TemporalUnit)}:
+ * <pre>
+ * // these two lines are equivalent
+ * between = thisUnit.between(start, end);
+ * between = start.until(end, thisUnit);
+ * </pre>
+ * The choice should be made based on which makes the code more readable.
+ * <p>
+ * For example, this method allows the number of days between two dates to
+ * be calculated:
+ * <pre>
+ * long daysBetween = DAYS.between(start, end);
+ * // or alternatively
+ * long daysBetween = start.until(end, DAYS);
+ * </pre>
+ * <p>
+ * Implementations should perform any queries or calculations using the units
+ * available in {@link ChronoUnit} or the fields available in {@link ChronoField}.
+ * If the unit is not supported an {@code UnsupportedTemporalTypeException} must be thrown.
+ * Implementations must not alter the specified temporal objects.
+ *
+ * @implSpec
+ * Implementations must begin by checking to if the two temporals have the
+ * same type using {@code getClass()}. If they do not, then the result must be
+ * obtained by calling {@code temporal1Inclusive.until(temporal2Exclusive, this)}.
+ *
+ * @param temporal1Inclusive the base temporal object, not null
+ * @param temporal2Exclusive the other temporal object, exclusive, not null
+ * @return the amount of time between temporal1Inclusive and temporal2Exclusive
+ * in terms of this unit; positive if temporal2Exclusive is later than
+ * temporal1Inclusive, negative if earlier
+ * @throws DateTimeException if the amount cannot be calculated, or the end
+ * temporal cannot be converted to the same type as the start temporal
+ * @throws UnsupportedTemporalTypeException if the unit is not supported by the temporal
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ long between(Temporal temporal1Inclusive, Temporal temporal2Exclusive);
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets a descriptive name for the unit.
+ * <p>
+ * This should be in the plural and upper-first camel case, such as 'Days' or 'Minutes'.
+ *
+ * @return the name of this unit, not null
+ */
+ @Override
+ String toString();
+
+}
diff --git a/java/time/temporal/UnsupportedTemporalTypeException.java b/java/time/temporal/UnsupportedTemporalTypeException.java
new file mode 100644
index 0000000..e4b2b12
--- /dev/null
+++ b/java/time/temporal/UnsupportedTemporalTypeException.java
@@ -0,0 +1,101 @@
+/*
+ * 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.temporal;
+
+import java.time.DateTimeException;
+
+/**
+ * UnsupportedTemporalTypeException indicates that a ChronoField or ChronoUnit is
+ * not supported for a Temporal class.
+ *
+ * @implSpec
+ * This class is intended for use in a single thread.
+ *
+ * @since 1.8
+ */
+public class UnsupportedTemporalTypeException extends DateTimeException {
+
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = -6158898438688206006L;
+
+ /**
+ * Constructs a new UnsupportedTemporalTypeException with the specified message.
+ *
+ * @param message the message to use for this exception, may be null
+ */
+ public UnsupportedTemporalTypeException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs a new UnsupportedTemporalTypeException with the specified message and cause.
+ *
+ * @param message the message to use for this exception, may be null
+ * @param cause the cause of the exception, may be null
+ */
+ public UnsupportedTemporalTypeException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
diff --git a/java/time/temporal/ValueRange.java b/java/time/temporal/ValueRange.java
new file mode 100644
index 0000000..4e5c2fd
--- /dev/null
+++ b/java/time/temporal/ValueRange.java
@@ -0,0 +1,430 @@
+/*
+ * Copyright (c) 2012, 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) 2011-2012, 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.temporal;
+
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.time.DateTimeException;
+
+/**
+ * The range of valid values for a date-time field.
+ * <p>
+ * All {@link TemporalField} instances have a valid range of values.
+ * For example, the ISO day-of-month runs from 1 to somewhere between 28 and 31.
+ * This class captures that valid range.
+ * <p>
+ * It is important to be aware of the limitations of this class.
+ * Only the minimum and maximum values are provided.
+ * It is possible for there to be invalid values within the outer range.
+ * For example, a weird field may have valid values of 1, 2, 4, 6, 7, thus
+ * have a range of '1 - 7', despite that fact that values 3 and 5 are invalid.
+ * <p>
+ * Instances of this class are not tied to a specific field.
+ *
+ * @implSpec
+ * This class is immutable and thread-safe.
+ *
+ * @since 1.8
+ */
+public final class ValueRange implements Serializable {
+
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = -7317881728594519368L;
+
+ /**
+ * The smallest minimum value.
+ */
+ private final long minSmallest;
+ /**
+ * The largest minimum value.
+ */
+ private final long minLargest;
+ /**
+ * The smallest maximum value.
+ */
+ private final long maxSmallest;
+ /**
+ * The largest maximum value.
+ */
+ private final long maxLargest;
+
+ /**
+ * Obtains a fixed value range.
+ * <p>
+ * This factory obtains a range where the minimum and maximum values are fixed.
+ * For example, the ISO month-of-year always runs from 1 to 12.
+ *
+ * @param min the minimum value
+ * @param max the maximum value
+ * @return the ValueRange for min, max, not null
+ * @throws IllegalArgumentException if the minimum is greater than the maximum
+ */
+ public static ValueRange of(long min, long max) {
+ if (min > max) {
+ throw new IllegalArgumentException("Minimum value must be less than maximum value");
+ }
+ return new ValueRange(min, min, max, max);
+ }
+
+ /**
+ * Obtains a variable value range.
+ * <p>
+ * This factory obtains a range where the minimum value is fixed and the maximum value may vary.
+ * For example, the ISO day-of-month always starts at 1, but ends between 28 and 31.
+ *
+ * @param min the minimum value
+ * @param maxSmallest the smallest maximum value
+ * @param maxLargest the largest maximum value
+ * @return the ValueRange for min, smallest max, largest max, not null
+ * @throws IllegalArgumentException if
+ * the minimum is greater than the smallest maximum,
+ * or the smallest maximum is greater than the largest maximum
+ */
+ public static ValueRange of(long min, long maxSmallest, long maxLargest) {
+ return of(min, min, maxSmallest, maxLargest);
+ }
+
+ /**
+ * Obtains a fully variable value range.
+ * <p>
+ * This factory obtains a range where both the minimum and maximum value may vary.
+ *
+ * @param minSmallest the smallest minimum value
+ * @param minLargest the largest minimum value
+ * @param maxSmallest the smallest maximum value
+ * @param maxLargest the largest maximum value
+ * @return the ValueRange for smallest min, largest min, smallest max, largest max, not null
+ * @throws IllegalArgumentException if
+ * the smallest minimum is greater than the smallest maximum,
+ * or the smallest maximum is greater than the largest maximum
+ * or the largest minimum is greater than the largest maximum
+ */
+ public static ValueRange of(long minSmallest, long minLargest, long maxSmallest, long maxLargest) {
+ if (minSmallest > minLargest) {
+ throw new IllegalArgumentException("Smallest minimum value must be less than largest minimum value");
+ }
+ if (maxSmallest > maxLargest) {
+ throw new IllegalArgumentException("Smallest maximum value must be less than largest maximum value");
+ }
+ if (minLargest > maxLargest) {
+ throw new IllegalArgumentException("Minimum value must be less than maximum value");
+ }
+ return new ValueRange(minSmallest, minLargest, maxSmallest, maxLargest);
+ }
+
+ /**
+ * Restrictive constructor.
+ *
+ * @param minSmallest the smallest minimum value
+ * @param minLargest the largest minimum value
+ * @param maxSmallest the smallest minimum value
+ * @param maxLargest the largest minimum value
+ */
+ private ValueRange(long minSmallest, long minLargest, long maxSmallest, long maxLargest) {
+ this.minSmallest = minSmallest;
+ this.minLargest = minLargest;
+ this.maxSmallest = maxSmallest;
+ this.maxLargest = maxLargest;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Is the value range fixed and fully known.
+ * <p>
+ * For example, the ISO day-of-month runs from 1 to between 28 and 31.
+ * Since there is uncertainty about the maximum value, the range is not fixed.
+ * However, for the month of January, the range is always 1 to 31, thus it is fixed.
+ *
+ * @return true if the set of values is fixed
+ */
+ public boolean isFixed() {
+ return minSmallest == minLargest && maxSmallest == maxLargest;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the minimum value that the field can take.
+ * <p>
+ * For example, the ISO day-of-month always starts at 1.
+ * The minimum is therefore 1.
+ *
+ * @return the minimum value for this field
+ */
+ public long getMinimum() {
+ return minSmallest;
+ }
+
+ /**
+ * Gets the largest possible minimum value that the field can take.
+ * <p>
+ * For example, the ISO day-of-month always starts at 1.
+ * The largest minimum is therefore 1.
+ *
+ * @return the largest possible minimum value for this field
+ */
+ public long getLargestMinimum() {
+ return minLargest;
+ }
+
+ /**
+ * Gets the smallest possible maximum value that the field can take.
+ * <p>
+ * For example, the ISO day-of-month runs to between 28 and 31 days.
+ * The smallest maximum is therefore 28.
+ *
+ * @return the smallest possible maximum value for this field
+ */
+ public long getSmallestMaximum() {
+ return maxSmallest;
+ }
+
+ /**
+ * Gets the maximum value that the field can take.
+ * <p>
+ * For example, the ISO day-of-month runs to between 28 and 31 days.
+ * The maximum is therefore 31.
+ *
+ * @return the maximum value for this field
+ */
+ public long getMaximum() {
+ return maxLargest;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if all values in the range fit in an {@code int}.
+ * <p>
+ * This checks that all valid values are within the bounds of an {@code int}.
+ * <p>
+ * For example, the ISO month-of-year has values from 1 to 12, which fits in an {@code int}.
+ * By comparison, ISO nano-of-day runs from 1 to 86,400,000,000,000 which does not fit in an {@code int}.
+ * <p>
+ * This implementation uses {@link #getMinimum()} and {@link #getMaximum()}.
+ *
+ * @return true if a valid value always fits in an {@code int}
+ */
+ public boolean isIntValue() {
+ return getMinimum() >= Integer.MIN_VALUE && getMaximum() <= Integer.MAX_VALUE;
+ }
+
+ /**
+ * Checks if the value is within the valid range.
+ * <p>
+ * This checks that the value is within the stored range of values.
+ *
+ * @param value the value to check
+ * @return true if the value is valid
+ */
+ public boolean isValidValue(long value) {
+ return (value >= getMinimum() && value <= getMaximum());
+ }
+
+ /**
+ * Checks if the value is within the valid range and that all values
+ * in the range fit in an {@code int}.
+ * <p>
+ * This method combines {@link #isIntValue()} and {@link #isValidValue(long)}.
+ *
+ * @param value the value to check
+ * @return true if the value is valid and fits in an {@code int}
+ */
+ public boolean isValidIntValue(long value) {
+ return isIntValue() && isValidValue(value);
+ }
+
+ /**
+ * Checks that the specified value is valid.
+ * <p>
+ * This validates that the value is within the valid range of values.
+ * The field is only used to improve the error message.
+ *
+ * @param value the value to check
+ * @param field the field being checked, may be null
+ * @return the value that was passed in
+ * @see #isValidValue(long)
+ */
+ public long checkValidValue(long value, TemporalField field) {
+ if (isValidValue(value) == false) {
+ throw new DateTimeException(genInvalidFieldMessage(field, value));
+ }
+ return value;
+ }
+
+ /**
+ * Checks that the specified value is valid and fits in an {@code int}.
+ * <p>
+ * This validates that the value is within the valid range of values and that
+ * all valid values are within the bounds of an {@code int}.
+ * The field is only used to improve the error message.
+ *
+ * @param value the value to check
+ * @param field the field being checked, may be null
+ * @return the value that was passed in
+ * @see #isValidIntValue(long)
+ */
+ public int checkValidIntValue(long value, TemporalField field) {
+ if (isValidIntValue(value) == false) {
+ throw new DateTimeException(genInvalidFieldMessage(field, value));
+ }
+ return (int) value;
+ }
+
+ private String genInvalidFieldMessage(TemporalField field, long value) {
+ if (field != null) {
+ return "Invalid value for " + field + " (valid values " + this + "): " + value;
+ } else {
+ return "Invalid value (valid values " + this + "): " + value;
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Restore the state of an ValueRange from the stream.
+ * Check that the values are valid.
+ *
+ * @param s the stream to read
+ * @throws InvalidObjectException if
+ * the smallest minimum is greater than the smallest maximum,
+ * or the smallest maximum is greater than the largest maximum
+ * or the largest minimum is greater than the largest maximum
+ * @throws ClassNotFoundException if a class cannot be resolved
+ */
+ private void readObject(ObjectInputStream s)
+ throws IOException, ClassNotFoundException, InvalidObjectException
+ {
+ s.defaultReadObject();
+ if (minSmallest > minLargest) {
+ throw new InvalidObjectException("Smallest minimum value must be less than largest minimum value");
+ }
+ if (maxSmallest > maxLargest) {
+ throw new InvalidObjectException("Smallest maximum value must be less than largest maximum value");
+ }
+ if (minLargest > maxLargest) {
+ throw new InvalidObjectException("Minimum value must be less than maximum value");
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if this range is equal to another range.
+ * <p>
+ * The comparison is based on the four values, minimum, largest minimum,
+ * smallest maximum and maximum.
+ * Only objects of type {@code ValueRange} are compared, other types return false.
+ *
+ * @param obj the object to check, null returns false
+ * @return true if this is equal to the other range
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (obj instanceof ValueRange) {
+ ValueRange other = (ValueRange) obj;
+ return minSmallest == other.minSmallest && minLargest == other.minLargest &&
+ maxSmallest == other.maxSmallest && maxLargest == other.maxLargest;
+ }
+ return false;
+ }
+
+ /**
+ * A hash code for this range.
+ *
+ * @return a suitable hash code
+ */
+ @Override
+ public int hashCode() {
+ long hash = minSmallest + minLargest << 16 + minLargest >> 48 + maxSmallest << 32 +
+ maxSmallest >> 32 + maxLargest << 48 + maxLargest >> 16;
+ return (int) (hash ^ (hash >>> 32));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Outputs this range as a {@code String}.
+ * <p>
+ * The format will be '{min}/{largestMin} - {smallestMax}/{max}',
+ * where the largestMin or smallestMax sections may be omitted, together
+ * with associated slash, if they are the same as the min or max.
+ *
+ * @return a string representation of this range, not null
+ */
+ @Override
+ public String toString() {
+ StringBuilder buf = new StringBuilder();
+ buf.append(minSmallest);
+ if (minSmallest != minLargest) {
+ buf.append('/').append(minLargest);
+ }
+ buf.append(" - ").append(maxSmallest);
+ if (maxSmallest != maxLargest) {
+ buf.append('/').append(maxLargest);
+ }
+ return buf.toString();
+ }
+
+}
diff --git a/java/time/temporal/WeekFields.java b/java/time/temporal/WeekFields.java
new file mode 100644
index 0000000..57f481c
--- /dev/null
+++ b/java/time/temporal/WeekFields.java
@@ -0,0 +1,1160 @@
+/*
+ * Copyright (c) 2012, 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) 2011-2012, 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.temporal;
+
+import android.icu.text.DateTimePatternGenerator;
+import android.icu.util.Calendar;
+import android.icu.util.ULocale;
+
+import static java.time.temporal.ChronoField.DAY_OF_MONTH;
+import static java.time.temporal.ChronoField.DAY_OF_WEEK;
+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.ChronoUnit.DAYS;
+import static java.time.temporal.ChronoUnit.FOREVER;
+import static java.time.temporal.ChronoUnit.MONTHS;
+import static java.time.temporal.ChronoUnit.WEEKS;
+import static java.time.temporal.ChronoUnit.YEARS;
+
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.time.DateTimeException;
+import java.time.DayOfWeek;
+import java.time.chrono.ChronoLocalDate;
+import java.time.chrono.Chronology;
+import java.time.format.ResolverStyle;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * Localized definitions of the day-of-week, week-of-month and week-of-year fields.
+ * <p>
+ * A standard week is seven days long, but cultures have different definitions for some
+ * other aspects of a week. This class represents the definition of the week, for the
+ * purpose of providing {@link TemporalField} instances.
+ * <p>
+ * WeekFields provides five fields,
+ * {@link #dayOfWeek()}, {@link #weekOfMonth()}, {@link #weekOfYear()},
+ * {@link #weekOfWeekBasedYear()}, and {@link #weekBasedYear()}
+ * that provide access to the values from any {@linkplain Temporal temporal object}.
+ * <p>
+ * The computations for day-of-week, week-of-month, and week-of-year are based
+ * on the {@linkplain ChronoField#YEAR proleptic-year},
+ * {@linkplain ChronoField#MONTH_OF_YEAR month-of-year},
+ * {@linkplain ChronoField#DAY_OF_MONTH day-of-month}, and
+ * {@linkplain ChronoField#DAY_OF_WEEK ISO day-of-week} which are based on the
+ * {@linkplain ChronoField#EPOCH_DAY epoch-day} and the chronology.
+ * The values may not be aligned with the {@linkplain ChronoField#YEAR_OF_ERA year-of-Era}
+ * depending on the Chronology.
+ * <p>A week is defined by:
+ * <ul>
+ * <li>The first day-of-week.
+ * For example, the ISO-8601 standard considers Monday to be the first day-of-week.
+ * <li>The minimal number of days in the first week.
+ * For example, the ISO-8601 standard counts the first week as needing at least 4 days.
+ * </ul>
+ * Together these two values allow a year or month to be divided into weeks.
+ *
+ * <h3>Week of Month</h3>
+ * One field is used: week-of-month.
+ * The calculation ensures that weeks never overlap a month boundary.
+ * The month is divided into periods where each period starts on the defined first day-of-week.
+ * The earliest period is referred to as week 0 if it has less than the minimal number of days
+ * and week 1 if it has at least the minimal number of days.
+ *
+ * <table cellpadding="0" cellspacing="3" border="0" style="text-align: left; width: 50%;">
+ * <caption>Examples of WeekFields</caption>
+ * <tr><th>Date</th><td>Day-of-week</td>
+ * <td>First day: Monday<br>Minimal days: 4</td><td>First day: Monday<br>Minimal days: 5</td></tr>
+ * <tr><th>2008-12-31</th><td>Wednesday</td>
+ * <td>Week 5 of December 2008</td><td>Week 5 of December 2008</td></tr>
+ * <tr><th>2009-01-01</th><td>Thursday</td>
+ * <td>Week 1 of January 2009</td><td>Week 0 of January 2009</td></tr>
+ * <tr><th>2009-01-04</th><td>Sunday</td>
+ * <td>Week 1 of January 2009</td><td>Week 0 of January 2009</td></tr>
+ * <tr><th>2009-01-05</th><td>Monday</td>
+ * <td>Week 2 of January 2009</td><td>Week 1 of January 2009</td></tr>
+ * </table>
+ *
+ * <h3>Week of Year</h3>
+ * One field is used: week-of-year.
+ * The calculation ensures that weeks never overlap a year boundary.
+ * The year is divided into periods where each period starts on the defined first day-of-week.
+ * The earliest period is referred to as week 0 if it has less than the minimal number of days
+ * and week 1 if it has at least the minimal number of days.
+ *
+ * <h3>Week Based Year</h3>
+ * Two fields are used for week-based-year, one for the
+ * {@link #weekOfWeekBasedYear() week-of-week-based-year} and one for
+ * {@link #weekBasedYear() week-based-year}. In a week-based-year, each week
+ * belongs to only a single year. Week 1 of a year is the first week that
+ * starts on the first day-of-week and has at least the minimum number of days.
+ * The first and last weeks of a year may contain days from the
+ * previous calendar year or next calendar year respectively.
+ *
+ * <table cellpadding="0" cellspacing="3" border="0" style="text-align: left; width: 50%;">
+ * <caption>Examples of WeekFields for week-based-year</caption>
+ * <tr><th>Date</th><td>Day-of-week</td>
+ * <td>First day: Monday<br>Minimal days: 4</td><td>First day: Monday<br>Minimal days: 5</td></tr>
+ * <tr><th>2008-12-31</th><td>Wednesday</td>
+ * <td>Week 1 of 2009</td><td>Week 53 of 2008</td></tr>
+ * <tr><th>2009-01-01</th><td>Thursday</td>
+ * <td>Week 1 of 2009</td><td>Week 53 of 2008</td></tr>
+ * <tr><th>2009-01-04</th><td>Sunday</td>
+ * <td>Week 1 of 2009</td><td>Week 53 of 2008</td></tr>
+ * <tr><th>2009-01-05</th><td>Monday</td>
+ * <td>Week 2 of 2009</td><td>Week 1 of 2009</td></tr>
+ * </table>
+ *
+ * @implSpec
+ * This class is immutable and thread-safe.
+ *
+ * @since 1.8
+ */
+public final class WeekFields implements Serializable {
+ // implementation notes
+ // querying week-of-month or week-of-year should return the week value bound within the month/year
+ // however, setting the week value should be lenient (use plus/minus weeks)
+ // allow week-of-month outer range [0 to 6]
+ // allow week-of-year outer range [0 to 54]
+ // this is because callers shouldn't be expected to know the details of validity
+
+ /**
+ * The cache of rules by firstDayOfWeek plus minimalDays.
+ * Initialized first to be available for definition of ISO, etc.
+ */
+ private static final ConcurrentMap<String, WeekFields> CACHE = new ConcurrentHashMap<>(4, 0.75f, 2);
+
+ /**
+ * The ISO-8601 definition, where a week starts on Monday and the first week
+ * has a minimum of 4 days.
+ * <p>
+ * The ISO-8601 standard defines a calendar system based on weeks.
+ * It uses the week-based-year and week-of-week-based-year concepts to split
+ * up the passage of days instead of the standard year/month/day.
+ * <p>
+ * Note that the first week may start in the previous calendar year.
+ * Note also that the first few days of a calendar year may be in the
+ * week-based-year corresponding to the previous calendar year.
+ */
+ public static final WeekFields ISO = new WeekFields(DayOfWeek.MONDAY, 4);
+
+ /**
+ * The common definition of a week that starts on Sunday and the first week
+ * has a minimum of 1 day.
+ * <p>
+ * Defined as starting on Sunday and with a minimum of 1 day in the month.
+ * This week definition is in use in the US and other European countries.
+ */
+ public static final WeekFields SUNDAY_START = WeekFields.of(DayOfWeek.SUNDAY, 1);
+
+ /**
+ * The unit that represents week-based-years for the purpose of addition and subtraction.
+ * <p>
+ * This allows a number of week-based-years to be added to, or subtracted from, a date.
+ * The unit is equal to either 52 or 53 weeks.
+ * The estimated duration of a week-based-year is the same as that of a standard ISO
+ * year at {@code 365.2425 Days}.
+ * <p>
+ * The rules for addition add the number of week-based-years to the existing value
+ * for the week-based-year field retaining the week-of-week-based-year
+ * and day-of-week, unless the week number it too large for the target year.
+ * In that case, the week is set to the last week of the year
+ * with the same day-of-week.
+ * <p>
+ * This unit is an immutable and thread-safe singleton.
+ */
+ public static final TemporalUnit WEEK_BASED_YEARS = IsoFields.WEEK_BASED_YEARS;
+
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = -1177360819670808121L;
+
+ /**
+ * The first day-of-week.
+ */
+ private final DayOfWeek firstDayOfWeek;
+ /**
+ * The minimal number of days in the first week.
+ */
+ private final int minimalDays;
+ /**
+ * The field used to access the computed DayOfWeek.
+ */
+ private final transient TemporalField dayOfWeek = ComputedDayOfField.ofDayOfWeekField(this);
+ /**
+ * The field used to access the computed WeekOfMonth.
+ */
+ private final transient TemporalField weekOfMonth = ComputedDayOfField.ofWeekOfMonthField(this);
+ /**
+ * The field used to access the computed WeekOfYear.
+ */
+ private final transient TemporalField weekOfYear = ComputedDayOfField.ofWeekOfYearField(this);
+ /**
+ * The field that represents the week-of-week-based-year.
+ * <p>
+ * This field allows the week of the week-based-year value to be queried and set.
+ * <p>
+ * This unit is an immutable and thread-safe singleton.
+ */
+ private final transient TemporalField weekOfWeekBasedYear = ComputedDayOfField.ofWeekOfWeekBasedYearField(this);
+ /**
+ * The field that represents the week-based-year.
+ * <p>
+ * This field allows the week-based-year value to be queried and set.
+ * <p>
+ * This unit is an immutable and thread-safe singleton.
+ */
+ private final transient TemporalField weekBasedYear = ComputedDayOfField.ofWeekBasedYearField(this);
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance of {@code WeekFields} appropriate for a locale.
+ * <p>
+ * This will look up appropriate values from the provider of localization data.
+ *
+ * @param locale the locale to use, not null
+ * @return the week-definition, not null
+ */
+ public static WeekFields of(Locale locale) {
+ Objects.requireNonNull(locale, "locale");
+ // Android-changed: get Week data from ICU4J
+ ULocale ulocale = ULocale.forLocale(locale);
+ String region = ULocale.getRegionForSupplementalData(ulocale, /* inferRegion */ true);
+ Calendar.WeekData weekData = Calendar.getWeekDataForRegion(region);
+ DayOfWeek dow = DayOfWeek.SUNDAY.plus(weekData.firstDayOfWeek - 1);
+ return WeekFields.of(dow, weekData.minimalDaysInFirstWeek);
+ }
+
+ /**
+ * Obtains an instance of {@code WeekFields} from the first day-of-week and minimal days.
+ * <p>
+ * The first day-of-week defines the ISO {@code DayOfWeek} that is day 1 of the week.
+ * The minimal number of days in the first week defines how many days must be present
+ * in a month or year, starting from the first day-of-week, before the week is counted
+ * as the first week. A value of 1 will count the first day of the month or year as part
+ * of the first week, whereas a value of 7 will require the whole seven days to be in
+ * the new month or year.
+ * <p>
+ * WeekFields instances are singletons; for each unique combination
+ * of {@code firstDayOfWeek} and {@code minimalDaysInFirstWeek} the
+ * the same instance will be returned.
+ *
+ * @param firstDayOfWeek the first day of the week, not null
+ * @param minimalDaysInFirstWeek the minimal number of days in the first week, from 1 to 7
+ * @return the week-definition, not null
+ * @throws IllegalArgumentException if the minimal days value is less than one
+ * or greater than 7
+ */
+ public static WeekFields of(DayOfWeek firstDayOfWeek, int minimalDaysInFirstWeek) {
+ String key = firstDayOfWeek.toString() + minimalDaysInFirstWeek;
+ WeekFields rules = CACHE.get(key);
+ if (rules == null) {
+ rules = new WeekFields(firstDayOfWeek, minimalDaysInFirstWeek);
+ CACHE.putIfAbsent(key, rules);
+ rules = CACHE.get(key);
+ }
+ return rules;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Creates an instance of the definition.
+ *
+ * @param firstDayOfWeek the first day of the week, not null
+ * @param minimalDaysInFirstWeek the minimal number of days in the first week, from 1 to 7
+ * @throws IllegalArgumentException if the minimal days value is invalid
+ */
+ private WeekFields(DayOfWeek firstDayOfWeek, int minimalDaysInFirstWeek) {
+ Objects.requireNonNull(firstDayOfWeek, "firstDayOfWeek");
+ if (minimalDaysInFirstWeek < 1 || minimalDaysInFirstWeek > 7) {
+ throw new IllegalArgumentException("Minimal number of days is invalid");
+ }
+ this.firstDayOfWeek = firstDayOfWeek;
+ this.minimalDays = minimalDaysInFirstWeek;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Restore the state of a WeekFields from the stream.
+ * Check that the values are valid.
+ *
+ * @param s the stream to read
+ * @throws InvalidObjectException if the serialized object has an invalid
+ * value for firstDayOfWeek or minimalDays.
+ * @throws ClassNotFoundException if a class cannot be resolved
+ */
+ private void readObject(ObjectInputStream s)
+ throws IOException, ClassNotFoundException, InvalidObjectException
+ {
+ s.defaultReadObject();
+ if (firstDayOfWeek == null) {
+ throw new InvalidObjectException("firstDayOfWeek is null");
+ }
+
+ if (minimalDays < 1 || minimalDays > 7) {
+ throw new InvalidObjectException("Minimal number of days is invalid");
+ }
+ }
+
+ /**
+ * Return the singleton WeekFields associated with the
+ * {@code firstDayOfWeek} and {@code minimalDays}.
+ * @return the singleton WeekFields for the firstDayOfWeek and minimalDays.
+ * @throws InvalidObjectException if the serialized object has invalid
+ * values for firstDayOfWeek or minimalDays.
+ */
+ private Object readResolve() throws InvalidObjectException {
+ try {
+ return WeekFields.of(firstDayOfWeek, minimalDays);
+ } catch (IllegalArgumentException iae) {
+ throw new InvalidObjectException("Invalid serialized WeekFields: " + iae.getMessage());
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the first day-of-week.
+ * <p>
+ * The first day-of-week varies by culture.
+ * For example, the US uses Sunday, while France and the ISO-8601 standard use Monday.
+ * This method returns the first day using the standard {@code DayOfWeek} enum.
+ *
+ * @return the first day-of-week, not null
+ */
+ public DayOfWeek getFirstDayOfWeek() {
+ return firstDayOfWeek;
+ }
+
+ /**
+ * Gets the minimal number of days in the first week.
+ * <p>
+ * The number of days considered to define the first week of a month or year
+ * varies by culture.
+ * For example, the ISO-8601 requires 4 days (more than half a week) to
+ * be present before counting the first week.
+ *
+ * @return the minimal number of days in the first week of a month or year, from 1 to 7
+ */
+ public int getMinimalDaysInFirstWeek() {
+ return minimalDays;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a field to access the day of week based on this {@code WeekFields}.
+ * <p>
+ * This is similar to {@link ChronoField#DAY_OF_WEEK} but uses values for
+ * the day-of-week based on this {@code WeekFields}.
+ * The days are numbered from 1 to 7 where the
+ * {@link #getFirstDayOfWeek() first day-of-week} is assigned the value 1.
+ * <p>
+ * For example, if the first day-of-week is Sunday, then that will have the
+ * value 1, with other days ranging from Monday as 2 to Saturday as 7.
+ * <p>
+ * In the resolving phase of parsing, a localized day-of-week will be converted
+ * to a standardized {@code ChronoField} day-of-week.
+ * The day-of-week must be in the valid range 1 to 7.
+ * Other fields in this class build dates using the standardized day-of-week.
+ *
+ * @return a field providing access to the day-of-week with localized numbering, not null
+ */
+ public TemporalField dayOfWeek() {
+ return dayOfWeek;
+ }
+
+ /**
+ * Returns a field to access the week of month based on this {@code WeekFields}.
+ * <p>
+ * This represents the concept of the count of weeks within the month where weeks
+ * start on a fixed day-of-week, such as Monday.
+ * This field is typically used with {@link WeekFields#dayOfWeek()}.
+ * <p>
+ * Week one (1) is the week starting on the {@link WeekFields#getFirstDayOfWeek}
+ * where there are at least {@link WeekFields#getMinimalDaysInFirstWeek()} days in the month.
+ * Thus, week one may start up to {@code minDays} days before the start of the month.
+ * If the first week starts after the start of the month then the period before is week zero (0).
+ * <p>
+ * For example:<br>
+ * - if the 1st day of the month is a Monday, week one starts on the 1st and there is no week zero<br>
+ * - if the 2nd day of the month is a Monday, week one starts on the 2nd and the 1st is in week zero<br>
+ * - if the 4th day of the month is a Monday, week one starts on the 4th and the 1st to 3rd is in week zero<br>
+ * - if the 5th day of the month is a Monday, week two starts on the 5th and the 1st to 4th is in week one<br>
+ * <p>
+ * This field can be used with any calendar system.
+ * <p>
+ * In the resolving phase of parsing, a date can be created from a year,
+ * week-of-month, month-of-year and day-of-week.
+ * <p>
+ * In {@linkplain ResolverStyle#STRICT strict mode}, all four fields are
+ * validated against their range of valid values. The week-of-month field
+ * is validated to ensure that the resulting month is the month requested.
+ * <p>
+ * In {@linkplain ResolverStyle#SMART smart mode}, all four fields are
+ * validated against their range of valid values. The week-of-month field
+ * is validated from 0 to 6, meaning that the resulting date can be in a
+ * different month to that specified.
+ * <p>
+ * In {@linkplain ResolverStyle#LENIENT lenient mode}, the year and day-of-week
+ * are validated against the range of valid values. The resulting date is calculated
+ * equivalent to the following four stage approach.
+ * First, create a date on the first day of the first week of January in the requested year.
+ * Then take the month-of-year, subtract one, and add the amount in months to the date.
+ * Then take the week-of-month, subtract one, and add the amount in weeks to the date.
+ * Finally, adjust to the correct day-of-week within the localized week.
+ *
+ * @return a field providing access to the week-of-month, not null
+ */
+ public TemporalField weekOfMonth() {
+ return weekOfMonth;
+ }
+
+ /**
+ * Returns a field to access the week of year based on this {@code WeekFields}.
+ * <p>
+ * This represents the concept of the count of weeks within the year where weeks
+ * start on a fixed day-of-week, such as Monday.
+ * This field is typically used with {@link WeekFields#dayOfWeek()}.
+ * <p>
+ * Week one(1) is the week starting on the {@link WeekFields#getFirstDayOfWeek}
+ * where there are at least {@link WeekFields#getMinimalDaysInFirstWeek()} days in the year.
+ * Thus, week one may start up to {@code minDays} days before the start of the year.
+ * If the first week starts after the start of the year then the period before is week zero (0).
+ * <p>
+ * For example:<br>
+ * - if the 1st day of the year is a Monday, week one starts on the 1st and there is no week zero<br>
+ * - if the 2nd day of the year is a Monday, week one starts on the 2nd and the 1st is in week zero<br>
+ * - if the 4th day of the year is a Monday, week one starts on the 4th and the 1st to 3rd is in week zero<br>
+ * - if the 5th day of the year is a Monday, week two starts on the 5th and the 1st to 4th is in week one<br>
+ * <p>
+ * This field can be used with any calendar system.
+ * <p>
+ * In the resolving phase of parsing, a date can be created from a year,
+ * week-of-year and day-of-week.
+ * <p>
+ * In {@linkplain ResolverStyle#STRICT strict mode}, all three fields are
+ * validated against their range of valid values. The week-of-year field
+ * is validated to ensure that the resulting year is the year requested.
+ * <p>
+ * In {@linkplain ResolverStyle#SMART smart mode}, all three fields are
+ * validated against their range of valid values. The week-of-year field
+ * is validated from 0 to 54, meaning that the resulting date can be in a
+ * different year to that specified.
+ * <p>
+ * In {@linkplain ResolverStyle#LENIENT lenient mode}, the year and day-of-week
+ * are validated against the range of valid values. The resulting date is calculated
+ * equivalent to the following three stage approach.
+ * First, create a date on the first day of the first week in the requested year.
+ * Then take the week-of-year, subtract one, and add the amount in weeks to the date.
+ * Finally, adjust to the correct day-of-week within the localized week.
+ *
+ * @return a field providing access to the week-of-year, not null
+ */
+ public TemporalField weekOfYear() {
+ return weekOfYear;
+ }
+
+ /**
+ * Returns a field to access the week of a week-based-year based on this {@code WeekFields}.
+ * <p>
+ * This represents the concept of the count of weeks within the year where weeks
+ * start on a fixed day-of-week, such as Monday and each week belongs to exactly one year.
+ * This field is typically used with {@link WeekFields#dayOfWeek()} and
+ * {@link WeekFields#weekBasedYear()}.
+ * <p>
+ * Week one(1) is the week starting on the {@link WeekFields#getFirstDayOfWeek}
+ * where there are at least {@link WeekFields#getMinimalDaysInFirstWeek()} days in the year.
+ * If the first week starts after the start of the year then the period before
+ * is in the last week of the previous year.
+ * <p>
+ * For example:<br>
+ * - if the 1st day of the year is a Monday, week one starts on the 1st<br>
+ * - if the 2nd day of the year is a Monday, week one starts on the 2nd and
+ * the 1st is in the last week of the previous year<br>
+ * - if the 4th day of the year is a Monday, week one starts on the 4th and
+ * the 1st to 3rd is in the last week of the previous year<br>
+ * - if the 5th day of the year is a Monday, week two starts on the 5th and
+ * the 1st to 4th is in week one<br>
+ * <p>
+ * This field can be used with any calendar system.
+ * <p>
+ * In the resolving phase of parsing, a date can be created from a week-based-year,
+ * week-of-year and day-of-week.
+ * <p>
+ * In {@linkplain ResolverStyle#STRICT strict mode}, all three fields are
+ * validated against their range of valid values. The week-of-year field
+ * is validated to ensure that the resulting week-based-year is the
+ * week-based-year requested.
+ * <p>
+ * In {@linkplain ResolverStyle#SMART smart mode}, all three fields are
+ * validated against their range of valid values. The week-of-week-based-year field
+ * is validated from 1 to 53, meaning that the resulting date can be in the
+ * following week-based-year to that specified.
+ * <p>
+ * In {@linkplain ResolverStyle#LENIENT lenient mode}, the year and day-of-week
+ * are validated against the range of valid values. The resulting date is calculated
+ * equivalent to the following three stage approach.
+ * First, create a date on the first day of the first week in the requested week-based-year.
+ * Then take the week-of-week-based-year, subtract one, and add the amount in weeks to the date.
+ * Finally, adjust to the correct day-of-week within the localized week.
+ *
+ * @return a field providing access to the week-of-week-based-year, not null
+ */
+ public TemporalField weekOfWeekBasedYear() {
+ return weekOfWeekBasedYear;
+ }
+
+ /**
+ * Returns a field to access the year of a week-based-year based on this {@code WeekFields}.
+ * <p>
+ * This represents the concept of the year where weeks start on a fixed day-of-week,
+ * such as Monday and each week belongs to exactly one year.
+ * This field is typically used with {@link WeekFields#dayOfWeek()} and
+ * {@link WeekFields#weekOfWeekBasedYear()}.
+ * <p>
+ * Week one(1) is the week starting on the {@link WeekFields#getFirstDayOfWeek}
+ * where there are at least {@link WeekFields#getMinimalDaysInFirstWeek()} days in the year.
+ * Thus, week one may start before the start of the year.
+ * If the first week starts after the start of the year then the period before
+ * is in the last week of the previous year.
+ * <p>
+ * This field can be used with any calendar system.
+ * <p>
+ * In the resolving phase of parsing, a date can be created from a week-based-year,
+ * week-of-year and day-of-week.
+ * <p>
+ * In {@linkplain ResolverStyle#STRICT strict mode}, all three fields are
+ * validated against their range of valid values. The week-of-year field
+ * is validated to ensure that the resulting week-based-year is the
+ * week-based-year requested.
+ * <p>
+ * In {@linkplain ResolverStyle#SMART smart mode}, all three fields are
+ * validated against their range of valid values. The week-of-week-based-year field
+ * is validated from 1 to 53, meaning that the resulting date can be in the
+ * following week-based-year to that specified.
+ * <p>
+ * In {@linkplain ResolverStyle#LENIENT lenient mode}, the year and day-of-week
+ * are validated against the range of valid values. The resulting date is calculated
+ * equivalent to the following three stage approach.
+ * First, create a date on the first day of the first week in the requested week-based-year.
+ * Then take the week-of-week-based-year, subtract one, and add the amount in weeks to the date.
+ * Finally, adjust to the correct day-of-week within the localized week.
+ *
+ * @return a field providing access to the week-based-year, not null
+ */
+ public TemporalField weekBasedYear() {
+ return weekBasedYear;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if this {@code WeekFields} is equal to the specified object.
+ * <p>
+ * The comparison is based on the entire state of the rules, which is
+ * the first day-of-week and minimal days.
+ *
+ * @param object the other rules to compare to, null returns false
+ * @return true if this is equal to the specified rules
+ */
+ @Override
+ public boolean equals(Object object) {
+ if (this == object) {
+ return true;
+ }
+ if (object instanceof WeekFields) {
+ return hashCode() == object.hashCode();
+ }
+ return false;
+ }
+
+ /**
+ * A hash code for this {@code WeekFields}.
+ *
+ * @return a suitable hash code
+ */
+ @Override
+ public int hashCode() {
+ return firstDayOfWeek.ordinal() * 7 + minimalDays;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * A string representation of this {@code WeekFields} instance.
+ *
+ * @return the string representation, not null
+ */
+ @Override
+ public String toString() {
+ return "WeekFields[" + firstDayOfWeek + ',' + minimalDays + ']';
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Field type that computes DayOfWeek, WeekOfMonth, and WeekOfYear
+ * based on a WeekFields.
+ * A separate Field instance is required for each different WeekFields;
+ * combination of start of week and minimum number of days.
+ * Constructors are provided to create fields for DayOfWeek, WeekOfMonth,
+ * and WeekOfYear.
+ */
+ static class ComputedDayOfField implements TemporalField {
+
+ /**
+ * Returns a field to access the day of week,
+ * computed based on a WeekFields.
+ * <p>
+ * The WeekDefintion of the first day of the week is used with
+ * the ISO DAY_OF_WEEK field to compute week boundaries.
+ */
+ static ComputedDayOfField ofDayOfWeekField(WeekFields weekDef) {
+ return new ComputedDayOfField("DayOfWeek", weekDef, DAYS, WEEKS, DAY_OF_WEEK_RANGE);
+ }
+
+ /**
+ * Returns a field to access the week of month,
+ * computed based on a WeekFields.
+ * @see WeekFields#weekOfMonth()
+ */
+ static ComputedDayOfField ofWeekOfMonthField(WeekFields weekDef) {
+ return new ComputedDayOfField("WeekOfMonth", weekDef, WEEKS, MONTHS, WEEK_OF_MONTH_RANGE);
+ }
+
+ /**
+ * Returns a field to access the week of year,
+ * computed based on a WeekFields.
+ * @see WeekFields#weekOfYear()
+ */
+ static ComputedDayOfField ofWeekOfYearField(WeekFields weekDef) {
+ return new ComputedDayOfField("WeekOfYear", weekDef, WEEKS, YEARS, WEEK_OF_YEAR_RANGE);
+ }
+
+ /**
+ * Returns a field to access the week of week-based-year,
+ * computed based on a WeekFields.
+ * @see WeekFields#weekOfWeekBasedYear()
+ */
+ static ComputedDayOfField ofWeekOfWeekBasedYearField(WeekFields weekDef) {
+ return new ComputedDayOfField("WeekOfWeekBasedYear", weekDef, WEEKS, IsoFields.WEEK_BASED_YEARS, WEEK_OF_WEEK_BASED_YEAR_RANGE);
+ }
+
+ /**
+ * Returns a field to access the week of week-based-year,
+ * computed based on a WeekFields.
+ * @see WeekFields#weekBasedYear()
+ */
+ static ComputedDayOfField ofWeekBasedYearField(WeekFields weekDef) {
+ return new ComputedDayOfField("WeekBasedYear", weekDef, IsoFields.WEEK_BASED_YEARS, FOREVER, ChronoField.YEAR.range());
+ }
+
+ /**
+ * Return a new week-based-year date of the Chronology, year, week-of-year,
+ * and dow of week.
+ * @param chrono The chronology of the new date
+ * @param yowby the year of the week-based-year
+ * @param wowby the week of the week-based-year
+ * @param dow the day of the week
+ * @return a ChronoLocalDate for the requested year, week of year, and day of week
+ */
+ private ChronoLocalDate ofWeekBasedYear(Chronology chrono,
+ int yowby, int wowby, int dow) {
+ ChronoLocalDate date = chrono.date(yowby, 1, 1);
+ int ldow = localizedDayOfWeek(date);
+ int offset = startOfWeekOffset(1, ldow);
+
+ // Clamp the week of year to keep it in the same year
+ int yearLen = date.lengthOfYear();
+ int newYearWeek = computeWeek(offset, yearLen + weekDef.getMinimalDaysInFirstWeek());
+ wowby = Math.min(wowby, newYearWeek - 1);
+
+ int days = -offset + (dow - 1) + (wowby - 1) * 7;
+ return date.plus(days, DAYS);
+ }
+
+ private final String name;
+ private final WeekFields weekDef;
+ private final TemporalUnit baseUnit;
+ private final TemporalUnit rangeUnit;
+ private final ValueRange range;
+
+ private ComputedDayOfField(String name, WeekFields weekDef, TemporalUnit baseUnit, TemporalUnit rangeUnit, ValueRange range) {
+ this.name = name;
+ this.weekDef = weekDef;
+ this.baseUnit = baseUnit;
+ this.rangeUnit = rangeUnit;
+ this.range = range;
+ }
+
+ private static final ValueRange DAY_OF_WEEK_RANGE = ValueRange.of(1, 7);
+ private static final ValueRange WEEK_OF_MONTH_RANGE = ValueRange.of(0, 1, 4, 6);
+ private static final ValueRange WEEK_OF_YEAR_RANGE = ValueRange.of(0, 1, 52, 54);
+ private static final ValueRange WEEK_OF_WEEK_BASED_YEAR_RANGE = ValueRange.of(1, 52, 53);
+
+ @Override
+ public long getFrom(TemporalAccessor temporal) {
+ if (rangeUnit == WEEKS) { // day-of-week
+ return localizedDayOfWeek(temporal);
+ } else if (rangeUnit == MONTHS) { // week-of-month
+ return localizedWeekOfMonth(temporal);
+ } else if (rangeUnit == YEARS) { // week-of-year
+ return localizedWeekOfYear(temporal);
+ } else if (rangeUnit == WEEK_BASED_YEARS) {
+ return localizedWeekOfWeekBasedYear(temporal);
+ } else if (rangeUnit == FOREVER) {
+ return localizedWeekBasedYear(temporal);
+ } else {
+ throw new IllegalStateException("unreachable, rangeUnit: " + rangeUnit + ", this: " + this);
+ }
+ }
+
+ private int localizedDayOfWeek(TemporalAccessor temporal) {
+ int sow = weekDef.getFirstDayOfWeek().getValue();
+ int isoDow = temporal.get(DAY_OF_WEEK);
+ return Math.floorMod(isoDow - sow, 7) + 1;
+ }
+
+ private int localizedDayOfWeek(int isoDow) {
+ int sow = weekDef.getFirstDayOfWeek().getValue();
+ return Math.floorMod(isoDow - sow, 7) + 1;
+ }
+
+ private long localizedWeekOfMonth(TemporalAccessor temporal) {
+ int dow = localizedDayOfWeek(temporal);
+ int dom = temporal.get(DAY_OF_MONTH);
+ int offset = startOfWeekOffset(dom, dow);
+ return computeWeek(offset, dom);
+ }
+
+ private long localizedWeekOfYear(TemporalAccessor temporal) {
+ int dow = localizedDayOfWeek(temporal);
+ int doy = temporal.get(DAY_OF_YEAR);
+ int offset = startOfWeekOffset(doy, dow);
+ return computeWeek(offset, doy);
+ }
+
+ /**
+ * Returns the year of week-based-year for the temporal.
+ * The year can be the previous year, the current year, or the next year.
+ * @param temporal a date of any chronology, not null
+ * @return the year of week-based-year for the date
+ */
+ private int localizedWeekBasedYear(TemporalAccessor temporal) {
+ int dow = localizedDayOfWeek(temporal);
+ int year = temporal.get(YEAR);
+ int doy = temporal.get(DAY_OF_YEAR);
+ int offset = startOfWeekOffset(doy, dow);
+ int week = computeWeek(offset, doy);
+ if (week == 0) {
+ // Day is in end of week of previous year; return the previous year
+ return year - 1;
+ } else {
+ // If getting close to end of year, use higher precision logic
+ // Check if date of year is in partial week associated with next year
+ ValueRange dayRange = temporal.range(DAY_OF_YEAR);
+ int yearLen = (int)dayRange.getMaximum();
+ int newYearWeek = computeWeek(offset, yearLen + weekDef.getMinimalDaysInFirstWeek());
+ if (week >= newYearWeek) {
+ return year + 1;
+ }
+ }
+ return year;
+ }
+
+ /**
+ * Returns the week of week-based-year for the temporal.
+ * The week can be part of the previous year, the current year,
+ * or the next year depending on the week start and minimum number
+ * of days.
+ * @param temporal a date of any chronology
+ * @return the week of the year
+ * @see #localizedWeekBasedYear(java.time.temporal.TemporalAccessor)
+ */
+ private int localizedWeekOfWeekBasedYear(TemporalAccessor temporal) {
+ int dow = localizedDayOfWeek(temporal);
+ int doy = temporal.get(DAY_OF_YEAR);
+ int offset = startOfWeekOffset(doy, dow);
+ int week = computeWeek(offset, doy);
+ if (week == 0) {
+ // Day is in end of week of previous year
+ // Recompute from the last day of the previous year
+ ChronoLocalDate date = Chronology.from(temporal).date(temporal);
+ date = date.minus(doy, DAYS); // Back down into previous year
+ return localizedWeekOfWeekBasedYear(date);
+ } else if (week > 50) {
+ // If getting close to end of year, use higher precision logic
+ // Check if date of year is in partial week associated with next year
+ ValueRange dayRange = temporal.range(DAY_OF_YEAR);
+ int yearLen = (int)dayRange.getMaximum();
+ int newYearWeek = computeWeek(offset, yearLen + weekDef.getMinimalDaysInFirstWeek());
+ if (week >= newYearWeek) {
+ // Overlaps with week of following year; reduce to week in following year
+ week = week - newYearWeek + 1;
+ }
+ }
+ return week;
+ }
+
+ /**
+ * Returns an offset to align week start with a day of month or day of year.
+ *
+ * @param day the day; 1 through infinity
+ * @param dow the day of the week of that day; 1 through 7
+ * @return an offset in days to align a day with the start of the first 'full' week
+ */
+ private int startOfWeekOffset(int day, int dow) {
+ // offset of first day corresponding to the day of week in first 7 days (zero origin)
+ int weekStart = Math.floorMod(day - dow, 7);
+ int offset = -weekStart;
+ if (weekStart + 1 > weekDef.getMinimalDaysInFirstWeek()) {
+ // The previous week has the minimum days in the current month to be a 'week'
+ offset = 7 - weekStart;
+ }
+ return offset;
+ }
+
+ /**
+ * Returns the week number computed from the reference day and reference dayOfWeek.
+ *
+ * @param offset the offset to align a date with the start of week
+ * from {@link #startOfWeekOffset}.
+ * @param day the day for which to compute the week number
+ * @return the week number where zero is used for a partial week and 1 for the first full week
+ */
+ private int computeWeek(int offset, int day) {
+ return ((7 + offset + (day - 1)) / 7);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <R extends Temporal> R adjustInto(R temporal, long newValue) {
+ // Check the new value and get the old value of the field
+ int newVal = range.checkValidIntValue(newValue, this); // lenient check range
+ int currentVal = temporal.get(this);
+ if (newVal == currentVal) {
+ return temporal;
+ }
+
+ if (rangeUnit == FOREVER) { // replace year of WeekBasedYear
+ // Create a new date object with the same chronology,
+ // the desired year and the same week and dow.
+ int idow = temporal.get(weekDef.dayOfWeek);
+ int wowby = temporal.get(weekDef.weekOfWeekBasedYear);
+ return (R) ofWeekBasedYear(Chronology.from(temporal), (int)newValue, wowby, idow);
+ } else {
+ // Compute the difference and add that using the base unit of the field
+ return (R) temporal.plus(newVal - currentVal, baseUnit);
+ }
+ }
+
+ @Override
+ public ChronoLocalDate resolve(
+ Map<TemporalField, Long> fieldValues, TemporalAccessor partialTemporal, ResolverStyle resolverStyle) {
+ final long value = fieldValues.get(this);
+ final int newValue = Math.toIntExact(value); // broad limit makes overflow checking lighter
+ // first convert localized day-of-week to ISO day-of-week
+ // doing this first handles case where both ISO and localized were parsed and might mismatch
+ // day-of-week is always strict as two different day-of-week values makes lenient complex
+ if (rangeUnit == WEEKS) { // day-of-week
+ final int checkedValue = range.checkValidIntValue(value, this); // no leniency as too complex
+ final int startDow = weekDef.getFirstDayOfWeek().getValue();
+ long isoDow = Math.floorMod((startDow - 1) + (checkedValue - 1), 7) + 1;
+ fieldValues.remove(this);
+ fieldValues.put(DAY_OF_WEEK, isoDow);
+ return null;
+ }
+
+ // can only build date if ISO day-of-week is present
+ if (fieldValues.containsKey(DAY_OF_WEEK) == false) {
+ return null;
+ }
+ int isoDow = DAY_OF_WEEK.checkValidIntValue(fieldValues.get(DAY_OF_WEEK));
+ int dow = localizedDayOfWeek(isoDow);
+
+ // build date
+ Chronology chrono = Chronology.from(partialTemporal);
+ if (fieldValues.containsKey(YEAR)) {
+ int year = YEAR.checkValidIntValue(fieldValues.get(YEAR)); // validate
+ if (rangeUnit == MONTHS && fieldValues.containsKey(MONTH_OF_YEAR)) { // week-of-month
+ long month = fieldValues.get(MONTH_OF_YEAR); // not validated yet
+ return resolveWoM(fieldValues, chrono, year, month, newValue, dow, resolverStyle);
+ }
+ if (rangeUnit == YEARS) { // week-of-year
+ return resolveWoY(fieldValues, chrono, year, newValue, dow, resolverStyle);
+ }
+ } else if ((rangeUnit == WEEK_BASED_YEARS || rangeUnit == FOREVER) &&
+ fieldValues.containsKey(weekDef.weekBasedYear) &&
+ fieldValues.containsKey(weekDef.weekOfWeekBasedYear)) { // week-of-week-based-year and year-of-week-based-year
+ return resolveWBY(fieldValues, chrono, dow, resolverStyle);
+ }
+ return null;
+ }
+
+ private ChronoLocalDate resolveWoM(
+ Map<TemporalField, Long> fieldValues, Chronology chrono, int year, long month, long wom, int localDow, ResolverStyle resolverStyle) {
+ ChronoLocalDate date;
+ if (resolverStyle == ResolverStyle.LENIENT) {
+ date = chrono.date(year, 1, 1).plus(Math.subtractExact(month, 1), MONTHS);
+ long weeks = Math.subtractExact(wom, localizedWeekOfMonth(date));
+ int days = localDow - localizedDayOfWeek(date); // safe from overflow
+ date = date.plus(Math.addExact(Math.multiplyExact(weeks, 7), days), DAYS);
+ } else {
+ int monthValid = MONTH_OF_YEAR.checkValidIntValue(month); // validate
+ date = chrono.date(year, monthValid, 1);
+ int womInt = range.checkValidIntValue(wom, this); // validate
+ int weeks = (int) (womInt - localizedWeekOfMonth(date)); // safe from overflow
+ int days = localDow - localizedDayOfWeek(date); // safe from overflow
+ date = date.plus(weeks * 7 + days, DAYS);
+ if (resolverStyle == ResolverStyle.STRICT && date.getLong(MONTH_OF_YEAR) != month) {
+ throw new DateTimeException("Strict mode rejected resolved date as it is in a different month");
+ }
+ }
+ fieldValues.remove(this);
+ fieldValues.remove(YEAR);
+ fieldValues.remove(MONTH_OF_YEAR);
+ fieldValues.remove(DAY_OF_WEEK);
+ return date;
+ }
+
+ private ChronoLocalDate resolveWoY(
+ Map<TemporalField, Long> fieldValues, Chronology chrono, int year, long woy, int localDow, ResolverStyle resolverStyle) {
+ ChronoLocalDate date = chrono.date(year, 1, 1);
+ if (resolverStyle == ResolverStyle.LENIENT) {
+ long weeks = Math.subtractExact(woy, localizedWeekOfYear(date));
+ int days = localDow - localizedDayOfWeek(date); // safe from overflow
+ date = date.plus(Math.addExact(Math.multiplyExact(weeks, 7), days), DAYS);
+ } else {
+ int womInt = range.checkValidIntValue(woy, this); // validate
+ int weeks = (int) (womInt - localizedWeekOfYear(date)); // safe from overflow
+ int days = localDow - localizedDayOfWeek(date); // safe from overflow
+ date = date.plus(weeks * 7 + days, DAYS);
+ if (resolverStyle == ResolverStyle.STRICT && date.getLong(YEAR) != year) {
+ throw new DateTimeException("Strict mode rejected resolved date as it is in a different year");
+ }
+ }
+ fieldValues.remove(this);
+ fieldValues.remove(YEAR);
+ fieldValues.remove(DAY_OF_WEEK);
+ return date;
+ }
+
+ private ChronoLocalDate resolveWBY(
+ Map<TemporalField, Long> fieldValues, Chronology chrono, int localDow, ResolverStyle resolverStyle) {
+ int yowby = weekDef.weekBasedYear.range().checkValidIntValue(
+ fieldValues.get(weekDef.weekBasedYear), weekDef.weekBasedYear);
+ ChronoLocalDate date;
+ if (resolverStyle == ResolverStyle.LENIENT) {
+ date = ofWeekBasedYear(chrono, yowby, 1, localDow);
+ long wowby = fieldValues.get(weekDef.weekOfWeekBasedYear);
+ long weeks = Math.subtractExact(wowby, 1);
+ date = date.plus(weeks, WEEKS);
+ } else {
+ int wowby = weekDef.weekOfWeekBasedYear.range().checkValidIntValue(
+ fieldValues.get(weekDef.weekOfWeekBasedYear), weekDef.weekOfWeekBasedYear); // validate
+ date = ofWeekBasedYear(chrono, yowby, wowby, localDow);
+ if (resolverStyle == ResolverStyle.STRICT && localizedWeekBasedYear(date) != yowby) {
+ throw new DateTimeException("Strict mode rejected resolved date as it is in a different week-based-year");
+ }
+ }
+ fieldValues.remove(this);
+ fieldValues.remove(weekDef.weekBasedYear);
+ fieldValues.remove(weekDef.weekOfWeekBasedYear);
+ fieldValues.remove(DAY_OF_WEEK);
+ return date;
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ public String getDisplayName(Locale locale) {
+ Objects.requireNonNull(locale, "locale");
+ if (rangeUnit == YEARS) { // only have values for week-of-year
+ // Android-changed: Use ICU name values.
+ DateTimePatternGenerator dateTimePatternGenerator = DateTimePatternGenerator
+ .getFrozenInstance(ULocale.forLocale(locale));
+ String icuName = dateTimePatternGenerator
+ .getAppendItemName(DateTimePatternGenerator.WEEK_OF_YEAR);
+ return icuName != null && !icuName.isEmpty() ? icuName : name;
+ }
+ return name;
+ }
+
+ @Override
+ public TemporalUnit getBaseUnit() {
+ return baseUnit;
+ }
+
+ @Override
+ public TemporalUnit getRangeUnit() {
+ return rangeUnit;
+ }
+
+ @Override
+ public boolean isDateBased() {
+ return true;
+ }
+
+ @Override
+ public boolean isTimeBased() {
+ return false;
+ }
+
+ @Override
+ public ValueRange range() {
+ return range;
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ public boolean isSupportedBy(TemporalAccessor temporal) {
+ if (temporal.isSupported(DAY_OF_WEEK)) {
+ if (rangeUnit == WEEKS) { // day-of-week
+ return true;
+ } else if (rangeUnit == MONTHS) { // week-of-month
+ return temporal.isSupported(DAY_OF_MONTH);
+ } else if (rangeUnit == YEARS) { // week-of-year
+ return temporal.isSupported(DAY_OF_YEAR);
+ } else if (rangeUnit == WEEK_BASED_YEARS) {
+ return temporal.isSupported(DAY_OF_YEAR);
+ } else if (rangeUnit == FOREVER) {
+ return temporal.isSupported(YEAR);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public ValueRange rangeRefinedBy(TemporalAccessor temporal) {
+ if (rangeUnit == ChronoUnit.WEEKS) { // day-of-week
+ return range;
+ } else if (rangeUnit == MONTHS) { // week-of-month
+ return rangeByWeek(temporal, DAY_OF_MONTH);
+ } else if (rangeUnit == YEARS) { // week-of-year
+ return rangeByWeek(temporal, DAY_OF_YEAR);
+ } else if (rangeUnit == WEEK_BASED_YEARS) {
+ return rangeWeekOfWeekBasedYear(temporal);
+ } else if (rangeUnit == FOREVER) {
+ return YEAR.range();
+ } else {
+ throw new IllegalStateException("unreachable, rangeUnit: " + rangeUnit + ", this: " + this);
+ }
+ }
+
+ /**
+ * Map the field range to a week range
+ * @param temporal the temporal
+ * @param field the field to get the range of
+ * @return the ValueRange with the range adjusted to weeks.
+ */
+ private ValueRange rangeByWeek(TemporalAccessor temporal, TemporalField field) {
+ int dow = localizedDayOfWeek(temporal);
+ int offset = startOfWeekOffset(temporal.get(field), dow);
+ ValueRange fieldRange = temporal.range(field);
+ return ValueRange.of(computeWeek(offset, (int) fieldRange.getMinimum()),
+ computeWeek(offset, (int) fieldRange.getMaximum()));
+ }
+
+ /**
+ * Map the field range to a week range of a week year.
+ * @param temporal the temporal
+ * @return the ValueRange with the range adjusted to weeks.
+ */
+ private ValueRange rangeWeekOfWeekBasedYear(TemporalAccessor temporal) {
+ if (!temporal.isSupported(DAY_OF_YEAR)) {
+ return WEEK_OF_YEAR_RANGE;
+ }
+ int dow = localizedDayOfWeek(temporal);
+ int doy = temporal.get(DAY_OF_YEAR);
+ int offset = startOfWeekOffset(doy, dow);
+ int week = computeWeek(offset, doy);
+ if (week == 0) {
+ // Day is in end of week of previous year
+ // Recompute from the last day of the previous year
+ ChronoLocalDate date = Chronology.from(temporal).date(temporal);
+ date = date.minus(doy + 7, DAYS); // Back down into previous year
+ return rangeWeekOfWeekBasedYear(date);
+ }
+ // Check if day of year is in partial week associated with next year
+ ValueRange dayRange = temporal.range(DAY_OF_YEAR);
+ int yearLen = (int)dayRange.getMaximum();
+ int newYearWeek = computeWeek(offset, yearLen + weekDef.getMinimalDaysInFirstWeek());
+
+ if (week >= newYearWeek) {
+ // Overlaps with weeks of following year; recompute from a week in following year
+ ChronoLocalDate date = Chronology.from(temporal).date(temporal);
+ date = date.plus(yearLen - doy + 1 + 7, ChronoUnit.DAYS);
+ return rangeWeekOfWeekBasedYear(date);
+ }
+ return ValueRange.of(1, newYearWeek-1);
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ public String toString() {
+ return name + "[" + weekDef.toString() + "]";
+ }
+ }
+}
diff --git a/java/time/temporal/package-info.java b/java/time/temporal/package-info.java
new file mode 100644
index 0000000..d3e9c05
--- /dev/null
+++ b/java/time/temporal/package-info.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2012, 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) 2012, 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.
+ */
+
+/**
+ * <p>
+ * Access to date and time using fields and units, and date time adjusters.
+ * </p>
+ * <p>
+ * This package expands on the base package to provide additional functionality for
+ * more powerful use cases. Support is included for:
+ * </p>
+ * <ul>
+ * <li>Units of date-time, such as years, months, days and hours</li>
+ * <li>Fields of date-time, such as month-of-year, day-of-week or hour-of-day</li>
+ * <li>Date-time adjustment functions</li>
+ * <li>Different definitions of weeks</li>
+ * </ul>
+ *
+ * <h3>Fields and Units</h3>
+ * <p>
+ * Dates and times are expressed in terms of fields and units.
+ * A unit is used to measure an amount of time, such as years, days or minutes.
+ * All units implement {@link java.time.temporal.TemporalUnit}.
+ * The set of well known units is defined in {@link java.time.temporal.ChronoUnit}, such as {@code DAYS}.
+ * The unit interface is designed to allow application defined units.
+ * </p>
+ * <p>
+ * A field is used to express part of a larger date-time, such as year, month-of-year or second-of-minute.
+ * All fields implement {@link java.time.temporal.TemporalField}.
+ * The set of well known fields are defined in {@link java.time.temporal.ChronoField}, such as {@code HOUR_OF_DAY}.
+ * Additional fields are defined by {@link java.time.temporal.JulianFields}, {@link java.time.temporal.WeekFields}
+ * and {@link java.time.temporal.IsoFields}.
+ * The field interface is designed to allow application defined fields.
+ * </p>
+ * <p>
+ * This package provides tools that allow the units and fields of date and time to be accessed
+ * in a general way most suited for frameworks.
+ * {@link java.time.temporal.Temporal} provides the abstraction for date time types that support fields.
+ * Its methods support getting the value of a field, creating a new date time with the value of
+ * a field modified, and querying for additional information, typically used to extract the offset or time-zone.
+ * </p>
+ * <p>
+ * One use of fields in application code is to retrieve fields for which there is no convenience method.
+ * For example, getting the day-of-month is common enough that there is a method on {@code LocalDate}
+ * called {@code getDayOfMonth()}. However for more unusual fields it is necessary to use the field.
+ * For example, {@code date.get(ChronoField.ALIGNED_WEEK_OF_MONTH)}.
+ * The fields also provide access to the range of valid values.
+ * </p>
+ *
+ * <h3>Adjustment and Query</h3>
+ * <p>
+ * A key part of the date-time problem space is adjusting a date to a new, related value,
+ * such as the "last day of the month", or "next Wednesday".
+ * These are modeled as functions that adjust a base date-time.
+ * The functions implement {@link java.time.temporal.TemporalAdjuster} and operate on {@code Temporal}.
+ * A set of common functions are provided in {@link java.time.temporal.TemporalAdjusters}.
+ * For example, to find the first occurrence of a day-of-week after a given date, use
+ * {@link java.time.temporal.TemporalAdjusters#next(DayOfWeek)}, such as
+ * {@code date.with(next(MONDAY))}.
+ * Applications can also define adjusters by implementing {@link java.time.temporal.TemporalAdjuster}.
+ * </p>
+ * <p>
+ * The {@link java.time.temporal.TemporalAmount} interface models amounts of relative time.
+ * </p>
+ * <p>
+ * In addition to adjusting a date-time, an interface is provided to enable querying via
+ * {@link java.time.temporal.TemporalQuery}.
+ * The most common implementations of the query interface are method references.
+ * The {@code from(TemporalAccessor)} methods on major classes can all be used, such as
+ * {@code LocalDate::from} or {@code Month::from}.
+ * Further implementations are provided in {@link java.time.temporal.TemporalQueries} as static methods.
+ * Applications can also define queries by implementing {@link java.time.temporal.TemporalQuery}.
+ * </p>
+ *
+ * <h3>Weeks</h3>
+ * <p>
+ * Different locales have different definitions of the week.
+ * For example, in Europe the week typically starts on a Monday, while in the US it starts on a Sunday.
+ * The {@link java.time.temporal.WeekFields} class models this distinction.
+ * </p>
+ * <p>
+ * The ISO calendar system defines an additional week-based division of years.
+ * This defines a year based on whole Monday to Monday weeks.
+ * This is modeled in {@link java.time.temporal.IsoFields}.
+ * </p>
+ *
+ * <h3>Package specification</h3>
+ * <p>
+ * Unless otherwise noted, passing a null argument to a constructor or method in any class or interface
+ * in this package will cause a {@link java.lang.NullPointerException NullPointerException} to be thrown.
+ * The Javadoc "@param" definition is used to summarise the null-behavior.
+ * The "@throws {@link java.lang.NullPointerException}" is not explicitly documented in each method.
+ * </p>
+ * <p>
+ * All calculations should check for numeric overflow and throw either an {@link java.lang.ArithmeticException}
+ * or a {@link java.time.DateTimeException}.
+ * </p>
+ * @since JDK1.8
+ */
+package java.time.temporal;
diff --git a/java/time/zone/IcuZoneRulesProvider.java b/java/time/zone/IcuZoneRulesProvider.java
new file mode 100644
index 0000000..5a4e37d
--- /dev/null
+++ b/java/time/zone/IcuZoneRulesProvider.java
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * 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. The Android Open Source
+ * Project designates this particular file as subject to the "Classpath"
+ * exception as provided by The Android Open Source Project 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.
+ */
+
+package java.time.zone;
+
+import android.icu.util.AnnualTimeZoneRule;
+import android.icu.util.BasicTimeZone;
+import android.icu.util.DateTimeRule;
+import android.icu.util.InitialTimeZoneRule;
+import android.icu.util.TimeZone;
+import android.icu.util.TimeZoneRule;
+import android.icu.util.TimeZoneTransition;
+import java.time.DayOfWeek;
+import java.time.LocalTime;
+import java.time.Month;
+import java.time.ZoneOffset;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.NavigableMap;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.concurrent.TimeUnit;
+import libcore.util.BasicLruCache;
+
+/**
+ * A ZoneRulesProvider that generates rules from ICU4J TimeZones.
+ * This provider ensures that classes in {@link java.time} use the same time zone information
+ * as ICU4J.
+ */
+public class IcuZoneRulesProvider extends ZoneRulesProvider {
+
+ // Arbitrary upper limit to number of transitions including the final rules.
+ private static final int MAX_TRANSITIONS = 10000;
+
+ private static final int SECONDS_IN_DAY = 24 * 60 * 60;
+
+ private final BasicLruCache<String, ZoneRules> cache = new ZoneRulesCache(8);
+
+ @Override
+ protected Set<String> provideZoneIds() {
+ Set<String> zoneIds = TimeZone.getAvailableIDs(TimeZone.SystemTimeZoneType.ANY, null, null);
+ zoneIds = new HashSet<>(zoneIds);
+ // java.time assumes ZoneId that start with "GMT" fit the pattern "GMT+HH:mm:ss" which these
+ // do not. Since they are equivalent to GMT, just remove these aliases.
+ zoneIds.remove("GMT+0");
+ zoneIds.remove("GMT-0");
+ return zoneIds;
+ }
+
+ @Override
+ protected ZoneRules provideRules(String zoneId, boolean forCaching) {
+ // Ignore forCaching, as this is a static provider.
+ return cache.get(zoneId);
+ }
+
+ @Override
+ protected NavigableMap<String, ZoneRules> provideVersions(String zoneId) {
+ return new TreeMap<>(
+ Collections.singletonMap(TimeZone.getTZDataVersion(),
+ provideRules(zoneId, /* forCaching */ false)));
+ }
+
+ /*
+ * This implementation is only tested with BasicTimeZone objects and depends on
+ * implementation details of that class:
+ *
+ * 0. TimeZone.getFrozenTimeZone() always returns a BasicTimeZone object.
+ * 1. The first rule is always an InitialTimeZoneRule (guaranteed by spec).
+ * 2. AnnualTimeZoneRules are only used as "final rules".
+ * 3. The final rules are either 0 or 2 AnnualTimeZoneRules
+ * 4. The final rules have endYear set to MAX_YEAR.
+ * 5. Each transition generated by the rules changes either the raw offset, the total offset
+ * or both.
+ * 6. There is a non-immense number of transitions for any rule before the final rules apply
+ * (enforced via the arbitrary limit defined in MAX_TRANSITIONS).
+ *
+ * Assumptions #5 and #6 are not strictly required for this code to work, but hold for the
+ * the data and code at the time of implementation. If they were broken they would indicate
+ * an incomplete understanding of how ICU TimeZoneRules are used which would probably mean that
+ * this code needs to be updated.
+ *
+ * These assumptions are verified using the verify() method where appropriate.
+ */
+ static ZoneRules generateZoneRules(String zoneId) {
+ TimeZone timeZone = TimeZone.getFrozenTimeZone(zoneId);
+ // Assumption #0
+ verify(timeZone instanceof BasicTimeZone, zoneId,
+ "Unexpected time zone class " + timeZone.getClass());
+ BasicTimeZone tz = (BasicTimeZone) timeZone;
+ TimeZoneRule[] rules = tz.getTimeZoneRules();
+ // Assumption #1
+ InitialTimeZoneRule initial = (InitialTimeZoneRule) rules[0];
+
+ ZoneOffset baseStandardOffset = millisToOffset(initial.getRawOffset());
+ ZoneOffset baseWallOffset =
+ millisToOffset((initial.getRawOffset() + initial.getDSTSavings()));
+
+ List<ZoneOffsetTransition> standardOffsetTransitionList = new ArrayList<>();
+ List<ZoneOffsetTransition> transitionList = new ArrayList<>();
+ List<ZoneOffsetTransitionRule> lastRules = new ArrayList<>();
+
+ int preLastDstSavings = 0;
+ AnnualTimeZoneRule last1 = null;
+ AnnualTimeZoneRule last2 = null;
+
+ TimeZoneTransition transition = tz.getNextTransition(Long.MIN_VALUE, false);
+ int transitionCount = 1;
+ // This loop has two possible exit conditions (in normal operation):
+ // 1. for zones that end with a static value and have no ongoing DST changes, it will exit
+ // via the normal condition (transition != null)
+ // 2. for zones with ongoing DST changes (represented by a "final zone" in ICU4J, and by
+ // "last rules" in java.time) the "break transitionLoop" will be used to exit the loop.
+ transitionLoop:
+ while (transition != null) {
+ TimeZoneRule from = transition.getFrom();
+ TimeZoneRule to = transition.getTo();
+ boolean hadEffect = false;
+ if (from.getRawOffset() != to.getRawOffset()) {
+ standardOffsetTransitionList.add(new ZoneOffsetTransition(
+ TimeUnit.MILLISECONDS.toSeconds(transition.getTime()),
+ millisToOffset(from.getRawOffset()),
+ millisToOffset(to.getRawOffset())));
+ hadEffect = true;
+ }
+ int fromTotalOffset = from.getRawOffset() + from.getDSTSavings();
+ int toTotalOffset = to.getRawOffset() + to.getDSTSavings();
+ if (fromTotalOffset != toTotalOffset) {
+ transitionList.add(new ZoneOffsetTransition(
+ TimeUnit.MILLISECONDS.toSeconds(transition.getTime()),
+ millisToOffset(fromTotalOffset),
+ millisToOffset(toTotalOffset)));
+ hadEffect = true;
+ }
+ // Assumption #5
+ verify(hadEffect, zoneId, "Transition changed neither total nor raw offset.");
+ if (to instanceof AnnualTimeZoneRule) {
+ // The presence of an AnnualTimeZoneRule is taken as an indication of a final rule.
+ if (last1 == null) {
+ preLastDstSavings = from.getDSTSavings();
+ last1 = (AnnualTimeZoneRule) to;
+ // Assumption #4
+ verify(last1.getEndYear() == AnnualTimeZoneRule.MAX_YEAR, zoneId,
+ "AnnualTimeZoneRule is not permanent.");
+ } else {
+ last2 = (AnnualTimeZoneRule) to;
+ // Assumption #4
+ verify(last2.getEndYear() == AnnualTimeZoneRule.MAX_YEAR, zoneId,
+ "AnnualTimeZoneRule is not permanent.");
+
+ // Assumption #3
+ transition = tz.getNextTransition(transition.getTime(), false);
+ verify(transition.getTo() == last1, zoneId,
+ "Unexpected rule after 2 AnnualTimeZoneRules.");
+ break transitionLoop;
+ }
+ } else {
+ // Assumption #2
+ verify(last1 == null, zoneId, "Unexpected rule after AnnualTimeZoneRule.");
+ }
+ verify(transitionCount <= MAX_TRANSITIONS, zoneId,
+ "More than " + MAX_TRANSITIONS + " transitions.");
+ transition = tz.getNextTransition(transition.getTime(), false);
+ transitionCount++;
+ }
+ if (last1 != null) {
+ // Assumption #3
+ verify(last2 != null, zoneId, "Only one AnnualTimeZoneRule.");
+ lastRules.add(toZoneOffsetTransitionRule(last1, preLastDstSavings));
+ lastRules.add(toZoneOffsetTransitionRule(last2, last1.getDSTSavings()));
+ }
+
+ return ZoneRules.of(baseStandardOffset, baseWallOffset, standardOffsetTransitionList,
+ transitionList, lastRules);
+ }
+
+ /**
+ * Verify an assumption about the zone rules.
+ *
+ * @param check
+ * {@code true} if the assumption holds, {@code false} otherwise.
+ * @param zoneId
+ * Zone ID for which to check.
+ * @param message
+ * Error description of a failed check.
+ * @throws ZoneRulesException
+ * If and only if {@code check} is {@code false}.
+ */
+ private static void verify(boolean check, String zoneId, String message) {
+ if (!check) {
+ throw new ZoneRulesException(
+ String.format("Failed verification of zone %s: %s", zoneId, message));
+ }
+ }
+
+ /**
+ * Transform an {@link AnnualTimeZoneRule} into an equivalent {@link ZoneOffsetTransitionRule}.
+ * This is only used for the "final rules".
+ *
+ * @param rule
+ * The rule to transform.
+ * @param dstSavingMillisBefore
+ * The DST offset before the first transition in milliseconds.
+ */
+ private static ZoneOffsetTransitionRule toZoneOffsetTransitionRule(
+ AnnualTimeZoneRule rule, int dstSavingMillisBefore) {
+ DateTimeRule dateTimeRule = rule.getRule();
+ // Calendar.JANUARY is 0, transform it into a proper Month.
+ Month month = Month.JANUARY.plus(dateTimeRule.getRuleMonth());
+ int dayOfMonthIndicator;
+ // Calendar.SUNDAY is 1, transform it into a proper DayOfWeek.
+ DayOfWeek dayOfWeek = DayOfWeek.SATURDAY.plus(dateTimeRule.getRuleDayOfWeek());
+ switch (dateTimeRule.getDateRuleType()) {
+ case DateTimeRule.DOM:
+ // Transition always on a specific day of the month.
+ dayOfMonthIndicator = dateTimeRule.getRuleDayOfMonth();
+ dayOfWeek = null;
+ break;
+ case DateTimeRule.DOW_GEQ_DOM:
+ // ICU representation matches java.time representation.
+ dayOfMonthIndicator = dateTimeRule.getRuleDayOfMonth();
+ break;
+ case DateTimeRule.DOW_LEQ_DOM:
+ // java.time uses a negative dayOfMonthIndicator to represent "Sun<=X" or "lastSun"
+ // rules. ICU uses this constant and the normal day. So "lastSun" in January would
+ // ruleDayOfMonth = 31 in ICU and dayOfMonthIndicator = -1 in java.time.
+ dayOfMonthIndicator = -month.maxLength() + dateTimeRule.getRuleDayOfMonth() - 1;
+ break;
+ case DateTimeRule.DOW:
+ // DOW is unspecified in the documentation and seems to never be used.
+ throw new ZoneRulesException("Date rule type DOW is unsupported");
+ default:
+ throw new ZoneRulesException(
+ "Unexpected date rule type: " + dateTimeRule.getDateRuleType());
+ }
+ // Cast to int is save, as input is int.
+ int secondOfDay = (int) TimeUnit.MILLISECONDS.toSeconds(dateTimeRule.getRuleMillisInDay());
+ LocalTime time;
+ boolean timeEndOfDay;
+ if (secondOfDay == SECONDS_IN_DAY) {
+ time = LocalTime.MIDNIGHT;
+ timeEndOfDay = true;
+ } else {
+ time = LocalTime.ofSecondOfDay(secondOfDay);
+ timeEndOfDay = false;
+ }
+ ZoneOffsetTransitionRule.TimeDefinition timeDefinition;
+ switch (dateTimeRule.getTimeRuleType()) {
+ case DateTimeRule.WALL_TIME:
+ timeDefinition = ZoneOffsetTransitionRule.TimeDefinition.WALL;
+ break;
+ case DateTimeRule.STANDARD_TIME:
+ timeDefinition = ZoneOffsetTransitionRule.TimeDefinition.STANDARD;
+ break;
+ case DateTimeRule.UTC_TIME:
+ timeDefinition = ZoneOffsetTransitionRule.TimeDefinition.UTC;
+ break;
+ default:
+ throw new ZoneRulesException(
+ "Unexpected time rule type " + dateTimeRule.getTimeRuleType());
+ }
+ ZoneOffset standardOffset = millisToOffset(rule.getRawOffset());
+ ZoneOffset offsetBefore = millisToOffset(rule.getRawOffset() + dstSavingMillisBefore);
+ ZoneOffset offsetAfter = millisToOffset(
+ rule.getRawOffset() + rule.getDSTSavings());
+ return ZoneOffsetTransitionRule.of(
+ month, dayOfMonthIndicator, dayOfWeek, time, timeEndOfDay, timeDefinition,
+ standardOffset, offsetBefore, offsetAfter);
+ }
+
+ private static ZoneOffset millisToOffset(int offset) {
+ // Cast to int is save, as input is int.
+ return ZoneOffset.ofTotalSeconds((int) TimeUnit.MILLISECONDS.toSeconds(offset));
+ }
+
+ private static class ZoneRulesCache extends BasicLruCache<String, ZoneRules> {
+
+ ZoneRulesCache(int maxSize) {
+ super(maxSize);
+ }
+
+ @Override
+ protected ZoneRules create(String zoneId) {
+ String canonicalId = TimeZone.getCanonicalID(zoneId);
+ if (!canonicalId.equals(zoneId)) {
+ // Return the same object as the canonical one, to avoid wasting space, but cache
+ // it under the non-cannonical name as well, to avoid future getCanonicalID calls.
+ return get(canonicalId);
+ }
+ return generateZoneRules(zoneId);
+ }
+ }
+}
diff --git a/java/time/zone/Ser.java b/java/time/zone/Ser.java
new file mode 100644
index 0000000..31c2f2d
--- /dev/null
+++ b/java/time/zone/Ser.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 2012, 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) 2011-2012, 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.zone;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.InvalidClassException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.io.StreamCorruptedException;
+import java.time.ZoneOffset;
+
+/**
+ * The shared serialization delegate for this package.
+ *
+ * @implNote
+ * This class is mutable and should be created once per serialization.
+ *
+ * @serial include
+ * @since 1.8
+ */
+final class Ser implements Externalizable {
+
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = -8885321777449118786L;
+
+ /** Type for ZoneRules. */
+ static final byte ZRULES = 1;
+ /** Type for ZoneOffsetTransition. */
+ static final byte ZOT = 2;
+ /** Type for ZoneOffsetTransition. */
+ static final byte ZOTRULE = 3;
+
+ /** The type being serialized. */
+ private byte type;
+ /** The object being serialized. */
+ private Object object;
+
+ /**
+ * Constructor for deserialization.
+ */
+ public Ser() {
+ }
+
+ /**
+ * Creates an instance for serialization.
+ *
+ * @param type the type
+ * @param object the object
+ */
+ Ser(byte type, Object object) {
+ this.type = type;
+ this.object = object;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Implements the {@code Externalizable} interface to write the object.
+ * @serialData
+ * Each serializable class is mapped to a type that is the first byte
+ * in the stream. Refer to each class {@code writeReplace}
+ * serialized form for the value of the type and sequence of values for the type.
+ *
+ * <ul>
+ * <li><a href="../../../serialized-form.html#java.time.zone.ZoneRules">ZoneRules.writeReplace</a>
+ * <li><a href="../../../serialized-form.html#java.time.zone.ZoneOffsetTransition">ZoneOffsetTransition.writeReplace</a>
+ * <li><a href="../../../serialized-form.html#java.time.zone.ZoneOffsetTransitionRule">ZoneOffsetTransitionRule.writeReplace</a>
+ * </ul>
+ *
+ * @param out the data stream to write to, not null
+ */
+ @Override
+ public void writeExternal(ObjectOutput out) throws IOException {
+ writeInternal(type, object, out);
+ }
+
+ static void write(Object object, DataOutput out) throws IOException {
+ writeInternal(ZRULES, object, out);
+ }
+
+ private static void writeInternal(byte type, Object object, DataOutput out) throws IOException {
+ out.writeByte(type);
+ switch (type) {
+ case ZRULES:
+ ((ZoneRules) object).writeExternal(out);
+ break;
+ case ZOT:
+ ((ZoneOffsetTransition) object).writeExternal(out);
+ break;
+ case ZOTRULE:
+ ((ZoneOffsetTransitionRule) object).writeExternal(out);
+ break;
+ default:
+ throw new InvalidClassException("Unknown serialized type");
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Implements the {@code Externalizable} interface to read the object.
+ * @serialData
+ * The streamed type and parameters defined by the type's {@code writeReplace}
+ * method are read and passed to the corresponding static factory for the type
+ * to create a new instance. That instance is returned as the de-serialized
+ * {@code Ser} object.
+ *
+ * <ul>
+ * <li><a href="../../../serialized-form.html#java.time.zone.ZoneRules">ZoneRules</a>
+ * - {@code ZoneRules.of(standardTransitions, standardOffsets, savingsInstantTransitions, wallOffsets, lastRules);}
+ * <li><a href="../../../serialized-form.html#java.time.zone.ZoneOffsetTransition">ZoneOffsetTransition</a>
+ * - {@code ZoneOffsetTransition of(LocalDateTime.ofEpochSecond(epochSecond), offsetBefore, offsetAfter);}
+ * <li><a href="../../../serialized-form.html#java.time.zone.ZoneOffsetTransitionRule">ZoneOffsetTransitionRule</a>
+ * - {@code ZoneOffsetTransitionRule.of(month, dom, dow, time, timeEndOfDay, timeDefinition, standardOffset, offsetBefore, offsetAfter);}
+ * </ul>
+ * @param in the data to read, not null
+ */
+ @Override
+ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+ type = in.readByte();
+ object = readInternal(type, in);
+ }
+
+ static Object read(DataInput in) throws IOException, ClassNotFoundException {
+ byte type = in.readByte();
+ return readInternal(type, in);
+ }
+
+ private static Object readInternal(byte type, DataInput in) throws IOException, ClassNotFoundException {
+ switch (type) {
+ case ZRULES:
+ return ZoneRules.readExternal(in);
+ case ZOT:
+ return ZoneOffsetTransition.readExternal(in);
+ case ZOTRULE:
+ return ZoneOffsetTransitionRule.readExternal(in);
+ default:
+ throw new StreamCorruptedException("Unknown serialized type");
+ }
+ }
+
+ /**
+ * Returns the object that will replace this one.
+ *
+ * @return the read object, should never be null
+ */
+ private Object readResolve() {
+ return object;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Writes the state to the stream.
+ *
+ * @param offset the offset, not null
+ * @param out the output stream, not null
+ * @throws IOException if an error occurs
+ */
+ static void writeOffset(ZoneOffset offset, DataOutput out) throws IOException {
+ final int offsetSecs = offset.getTotalSeconds();
+ int offsetByte = offsetSecs % 900 == 0 ? offsetSecs / 900 : 127; // compress to -72 to +72
+ out.writeByte(offsetByte);
+ if (offsetByte == 127) {
+ out.writeInt(offsetSecs);
+ }
+ }
+
+ /**
+ * Reads the state from the stream.
+ *
+ * @param in the input stream, not null
+ * @return the created object, not null
+ * @throws IOException if an error occurs
+ */
+ static ZoneOffset readOffset(DataInput in) throws IOException {
+ int offsetByte = in.readByte();
+ return (offsetByte == 127 ? ZoneOffset.ofTotalSeconds(in.readInt()) : ZoneOffset.ofTotalSeconds(offsetByte * 900));
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Writes the state to the stream.
+ *
+ * @param epochSec the epoch seconds, not null
+ * @param out the output stream, not null
+ * @throws IOException if an error occurs
+ */
+ static void writeEpochSec(long epochSec, DataOutput out) throws IOException {
+ if (epochSec >= -4575744000L && epochSec < 10413792000L && epochSec % 900 == 0) { // quarter hours between 1825 and 2300
+ int store = (int) ((epochSec + 4575744000L) / 900);
+ out.writeByte((store >>> 16) & 255);
+ out.writeByte((store >>> 8) & 255);
+ out.writeByte(store & 255);
+ } else {
+ out.writeByte(255);
+ out.writeLong(epochSec);
+ }
+ }
+
+ /**
+ * Reads the state from the stream.
+ *
+ * @param in the input stream, not null
+ * @return the epoch seconds, not null
+ * @throws IOException if an error occurs
+ */
+ static long readEpochSec(DataInput in) throws IOException {
+ int hiByte = in.readByte() & 255;
+ if (hiByte == 255) {
+ return in.readLong();
+ } else {
+ int midByte = in.readByte() & 255;
+ int loByte = in.readByte() & 255;
+ long tot = ((hiByte << 16) + (midByte << 8) + loByte);
+ return (tot * 900) - 4575744000L;
+ }
+ }
+
+}
diff --git a/java/time/zone/ZoneOffsetTransition.java b/java/time/zone/ZoneOffsetTransition.java
new file mode 100644
index 0000000..bc9cafe
--- /dev/null
+++ b/java/time/zone/ZoneOffsetTransition.java
@@ -0,0 +1,455 @@
+/*
+ * Copyright (c) 2012, 2015, 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) 2009-2012, 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.zone;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A transition between two offsets caused by a discontinuity in the local time-line.
+ * <p>
+ * A transition between two offsets is normally the result of a daylight savings cutover.
+ * The discontinuity is normally a gap in spring and an overlap in autumn.
+ * {@code ZoneOffsetTransition} models the transition between the two offsets.
+ * <p>
+ * Gaps occur where there are local date-times that simply do not exist.
+ * An example would be when the offset changes from {@code +03:00} to {@code +04:00}.
+ * This might be described as 'the clocks will move forward one hour tonight at 1am'.
+ * <p>
+ * Overlaps occur where there are local date-times that exist twice.
+ * An example would be when the offset changes from {@code +04:00} to {@code +03:00}.
+ * This might be described as 'the clocks will move back one hour tonight at 2am'.
+ *
+ * @implSpec
+ * This class is immutable and thread-safe.
+ *
+ * @since 1.8
+ */
+public final class ZoneOffsetTransition
+ implements Comparable<ZoneOffsetTransition>, Serializable {
+
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = -6946044323557704546L;
+ /**
+ * The local transition date-time at the transition.
+ */
+ private final LocalDateTime transition;
+ /**
+ * The offset before transition.
+ */
+ private final ZoneOffset offsetBefore;
+ /**
+ * The offset after transition.
+ */
+ private final ZoneOffset offsetAfter;
+
+ //-----------------------------------------------------------------------
+ /**
+ * Obtains an instance defining a transition between two offsets.
+ * <p>
+ * Applications should normally obtain an instance from {@link ZoneRules}.
+ * This factory is only intended for use when creating {@link ZoneRules}.
+ *
+ * @param transition the transition date-time at the transition, which never
+ * actually occurs, expressed local to the before offset, not null
+ * @param offsetBefore the offset before the transition, not null
+ * @param offsetAfter the offset at and after the transition, not null
+ * @return the transition, not null
+ * @throws IllegalArgumentException if {@code offsetBefore} and {@code offsetAfter}
+ * are equal, or {@code transition.getNano()} returns non-zero value
+ */
+ public static ZoneOffsetTransition of(LocalDateTime transition, ZoneOffset offsetBefore, ZoneOffset offsetAfter) {
+ Objects.requireNonNull(transition, "transition");
+ Objects.requireNonNull(offsetBefore, "offsetBefore");
+ Objects.requireNonNull(offsetAfter, "offsetAfter");
+ if (offsetBefore.equals(offsetAfter)) {
+ throw new IllegalArgumentException("Offsets must not be equal");
+ }
+ if (transition.getNano() != 0) {
+ throw new IllegalArgumentException("Nano-of-second must be zero");
+ }
+ return new ZoneOffsetTransition(transition, offsetBefore, offsetAfter);
+ }
+
+ /**
+ * Creates an instance defining a transition between two offsets.
+ *
+ * @param transition the transition date-time with the offset before the transition, not null
+ * @param offsetBefore the offset before the transition, not null
+ * @param offsetAfter the offset at and after the transition, not null
+ */
+ ZoneOffsetTransition(LocalDateTime transition, ZoneOffset offsetBefore, ZoneOffset offsetAfter) {
+ this.transition = transition;
+ this.offsetBefore = offsetBefore;
+ this.offsetAfter = offsetAfter;
+ }
+
+ /**
+ * Creates an instance from epoch-second and offsets.
+ *
+ * @param epochSecond the transition epoch-second
+ * @param offsetBefore the offset before the transition, not null
+ * @param offsetAfter the offset at and after the transition, not null
+ */
+ ZoneOffsetTransition(long epochSecond, ZoneOffset offsetBefore, ZoneOffset offsetAfter) {
+ this.transition = LocalDateTime.ofEpochSecond(epochSecond, 0, offsetBefore);
+ this.offsetBefore = offsetBefore;
+ this.offsetAfter = offsetAfter;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Defend against malicious streams.
+ *
+ * @param s the stream to read
+ * @throws InvalidObjectException always
+ */
+ private void readObject(ObjectInputStream s) throws InvalidObjectException {
+ throw new InvalidObjectException("Deserialization via serialization delegate");
+ }
+
+ /**
+ * Writes the object using a
+ * <a href="../../../serialized-form.html#java.time.zone.Ser">dedicated serialized form</a>.
+ * @serialData
+ * Refer to the serialized form of
+ * <a href="../../../serialized-form.html#java.time.zone.ZoneRules">ZoneRules.writeReplace</a>
+ * for the encoding of epoch seconds and offsets.
+ * <pre style="font-size:1.0em">{@code
+ *
+ * out.writeByte(2); // identifies a ZoneOffsetTransition
+ * out.writeEpochSec(toEpochSecond);
+ * out.writeOffset(offsetBefore);
+ * out.writeOffset(offsetAfter);
+ * }
+ * </pre>
+ * @return the replacing object, not null
+ */
+ private Object writeReplace() {
+ return new Ser(Ser.ZOT, this);
+ }
+
+ /**
+ * Writes the state to the stream.
+ *
+ * @param out the output stream, not null
+ * @throws IOException if an error occurs
+ */
+ void writeExternal(DataOutput out) throws IOException {
+ Ser.writeEpochSec(toEpochSecond(), out);
+ Ser.writeOffset(offsetBefore, out);
+ Ser.writeOffset(offsetAfter, out);
+ }
+
+ /**
+ * Reads the state from the stream.
+ *
+ * @param in the input stream, not null
+ * @return the created object, not null
+ * @throws IOException if an error occurs
+ */
+ static ZoneOffsetTransition readExternal(DataInput in) throws IOException {
+ long epochSecond = Ser.readEpochSec(in);
+ ZoneOffset before = Ser.readOffset(in);
+ ZoneOffset after = Ser.readOffset(in);
+ if (before.equals(after)) {
+ throw new IllegalArgumentException("Offsets must not be equal");
+ }
+ return new ZoneOffsetTransition(epochSecond, before, after);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the transition instant.
+ * <p>
+ * This is the instant of the discontinuity, which is defined as the first
+ * instant that the 'after' offset applies.
+ * <p>
+ * The methods {@link #getInstant()}, {@link #getDateTimeBefore()} and {@link #getDateTimeAfter()}
+ * all represent the same instant.
+ *
+ * @return the transition instant, not null
+ */
+ public Instant getInstant() {
+ return transition.toInstant(offsetBefore);
+ }
+
+ /**
+ * Gets the transition instant as an epoch second.
+ *
+ * @return the transition epoch second
+ */
+ public long toEpochSecond() {
+ return transition.toEpochSecond(offsetBefore);
+ }
+
+ //-------------------------------------------------------------------------
+ /**
+ * Gets the local transition date-time, as would be expressed with the 'before' offset.
+ * <p>
+ * This is the date-time where the discontinuity begins expressed with the 'before' offset.
+ * At this instant, the 'after' offset is actually used, therefore the combination of this
+ * date-time and the 'before' offset will never occur.
+ * <p>
+ * The combination of the 'before' date-time and offset represents the same instant
+ * as the 'after' date-time and offset.
+ *
+ * @return the transition date-time expressed with the before offset, not null
+ */
+ public LocalDateTime getDateTimeBefore() {
+ return transition;
+ }
+
+ /**
+ * Gets the local transition date-time, as would be expressed with the 'after' offset.
+ * <p>
+ * This is the first date-time after the discontinuity, when the new offset applies.
+ * <p>
+ * The combination of the 'before' date-time and offset represents the same instant
+ * as the 'after' date-time and offset.
+ *
+ * @return the transition date-time expressed with the after offset, not null
+ */
+ public LocalDateTime getDateTimeAfter() {
+ return transition.plusSeconds(getDurationSeconds());
+ }
+
+ /**
+ * Gets the offset before the transition.
+ * <p>
+ * This is the offset in use before the instant of the transition.
+ *
+ * @return the offset before the transition, not null
+ */
+ public ZoneOffset getOffsetBefore() {
+ return offsetBefore;
+ }
+
+ /**
+ * Gets the offset after the transition.
+ * <p>
+ * This is the offset in use on and after the instant of the transition.
+ *
+ * @return the offset after the transition, not null
+ */
+ public ZoneOffset getOffsetAfter() {
+ return offsetAfter;
+ }
+
+ /**
+ * Gets the duration of the transition.
+ * <p>
+ * In most cases, the transition duration is one hour, however this is not always the case.
+ * The duration will be positive for a gap and negative for an overlap.
+ * Time-zones are second-based, so the nanosecond part of the duration will be zero.
+ *
+ * @return the duration of the transition, positive for gaps, negative for overlaps
+ */
+ public Duration getDuration() {
+ return Duration.ofSeconds(getDurationSeconds());
+ }
+
+ /**
+ * Gets the duration of the transition in seconds.
+ *
+ * @return the duration in seconds
+ */
+ private int getDurationSeconds() {
+ return getOffsetAfter().getTotalSeconds() - getOffsetBefore().getTotalSeconds();
+ }
+
+ /**
+ * Does this transition represent a gap in the local time-line.
+ * <p>
+ * Gaps occur where there are local date-times that simply do not exist.
+ * An example would be when the offset changes from {@code +01:00} to {@code +02:00}.
+ * This might be described as 'the clocks will move forward one hour tonight at 1am'.
+ *
+ * @return true if this transition is a gap, false if it is an overlap
+ */
+ public boolean isGap() {
+ return getOffsetAfter().getTotalSeconds() > getOffsetBefore().getTotalSeconds();
+ }
+
+ /**
+ * Does this transition represent an overlap in the local time-line.
+ * <p>
+ * Overlaps occur where there are local date-times that exist twice.
+ * An example would be when the offset changes from {@code +02:00} to {@code +01:00}.
+ * This might be described as 'the clocks will move back one hour tonight at 2am'.
+ *
+ * @return true if this transition is an overlap, false if it is a gap
+ */
+ public boolean isOverlap() {
+ return getOffsetAfter().getTotalSeconds() < getOffsetBefore().getTotalSeconds();
+ }
+
+ /**
+ * Checks if the specified offset is valid during this transition.
+ * <p>
+ * This checks to see if the given offset will be valid at some point in the transition.
+ * A gap will always return false.
+ * An overlap will return true if the offset is either the before or after offset.
+ *
+ * @param offset the offset to check, null returns false
+ * @return true if the offset is valid during the transition
+ */
+ public boolean isValidOffset(ZoneOffset offset) {
+ return isGap() ? false : (getOffsetBefore().equals(offset) || getOffsetAfter().equals(offset));
+ }
+
+ /**
+ * Gets the valid offsets during this transition.
+ * <p>
+ * A gap will return an empty list, while an overlap will return both offsets.
+ *
+ * @return the list of valid offsets
+ */
+ List<ZoneOffset> getValidOffsets() {
+ if (isGap()) {
+ return Collections.emptyList();
+ }
+ return Arrays.asList(getOffsetBefore(), getOffsetAfter());
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Compares this transition to another based on the transition instant.
+ * <p>
+ * This compares the instants of each transition.
+ * The offsets are ignored, making this order inconsistent with equals.
+ *
+ * @param transition the transition to compare to, not null
+ * @return the comparator value, negative if less, positive if greater
+ */
+ @Override
+ public int compareTo(ZoneOffsetTransition transition) {
+ return this.getInstant().compareTo(transition.getInstant());
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if this object equals another.
+ * <p>
+ * The entire state of the object is compared.
+ *
+ * @param other the other object to compare to, null returns false
+ * @return true if equal
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+ if (other instanceof ZoneOffsetTransition) {
+ ZoneOffsetTransition d = (ZoneOffsetTransition) other;
+ return transition.equals(d.transition) &&
+ offsetBefore.equals(d.offsetBefore) && offsetAfter.equals(d.offsetAfter);
+ }
+ return false;
+ }
+
+ /**
+ * Returns a suitable hash code.
+ *
+ * @return the hash code
+ */
+ @Override
+ public int hashCode() {
+ return transition.hashCode() ^ offsetBefore.hashCode() ^ Integer.rotateLeft(offsetAfter.hashCode(), 16);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a string describing this object.
+ *
+ * @return a string for debugging, not null
+ */
+ @Override
+ public String toString() {
+ StringBuilder buf = new StringBuilder();
+ buf.append("Transition[")
+ .append(isGap() ? "Gap" : "Overlap")
+ .append(" at ")
+ .append(transition)
+ .append(offsetBefore)
+ .append(" to ")
+ .append(offsetAfter)
+ .append(']');
+ return buf.toString();
+ }
+
+}
diff --git a/java/time/zone/ZoneOffsetTransitionRule.java b/java/time/zone/ZoneOffsetTransitionRule.java
new file mode 100644
index 0000000..db1a055
--- /dev/null
+++ b/java/time/zone/ZoneOffsetTransitionRule.java
@@ -0,0 +1,627 @@
+/*
+ * Copyright (c) 2012, 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) 2009-2012, 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.zone;
+
+import static java.time.temporal.TemporalAdjusters.nextOrSame;
+import static java.time.temporal.TemporalAdjusters.previousOrSame;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.time.DayOfWeek;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.Month;
+import java.time.ZoneOffset;
+import java.time.chrono.IsoChronology;
+import java.util.Objects;
+
+/**
+ * A rule expressing how to create a transition.
+ * <p>
+ * This class allows rules for identifying future transitions to be expressed.
+ * A rule might be written in many forms:
+ * <ul>
+ * <li>the 16th March
+ * <li>the Sunday on or after the 16th March
+ * <li>the Sunday on or before the 16th March
+ * <li>the last Sunday in February
+ * </ul>
+ * These different rule types can be expressed and queried.
+ *
+ * @implSpec
+ * This class is immutable and thread-safe.
+ *
+ * @since 1.8
+ */
+public final class ZoneOffsetTransitionRule implements Serializable {
+
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = 6889046316657758795L;
+
+ /**
+ * The month of the month-day of the first day of the cutover week.
+ * The actual date will be adjusted by the dowChange field.
+ */
+ private final Month month;
+ /**
+ * The day-of-month of the month-day of the cutover week.
+ * If positive, it is the start of the week where the cutover can occur.
+ * If negative, it represents the end of the week where cutover can occur.
+ * The value is the number of days from the end of the month, such that
+ * {@code -1} is the last day of the month, {@code -2} is the second
+ * to last day, and so on.
+ */
+ private final byte dom;
+ /**
+ * The cutover day-of-week, null to retain the day-of-month.
+ */
+ private final DayOfWeek dow;
+ /**
+ * The cutover time in the 'before' offset.
+ */
+ private final LocalTime time;
+ /**
+ * Whether the cutover time is midnight at the end of day.
+ */
+ private final boolean timeEndOfDay;
+ /**
+ * The definition of how the local time should be interpreted.
+ */
+ private final TimeDefinition timeDefinition;
+ /**
+ * The standard offset at the cutover.
+ */
+ private final ZoneOffset standardOffset;
+ /**
+ * The offset before the cutover.
+ */
+ private final ZoneOffset offsetBefore;
+ /**
+ * The offset after the cutover.
+ */
+ private final ZoneOffset offsetAfter;
+
+ /**
+ * Obtains an instance defining the yearly rule to create transitions between two offsets.
+ * <p>
+ * Applications should normally obtain an instance from {@link ZoneRules}.
+ * This factory is only intended for use when creating {@link ZoneRules}.
+ *
+ * @param month the month of the month-day of the first day of the cutover week, not null
+ * @param dayOfMonthIndicator the day of the month-day of the cutover week, positive if the week is that
+ * day or later, negative if the week is that day or earlier, counting from the last day of the month,
+ * from -28 to 31 excluding 0
+ * @param dayOfWeek the required day-of-week, null if the month-day should not be changed
+ * @param time the cutover time in the 'before' offset, not null
+ * @param timeEndOfDay whether the time is midnight at the end of day
+ * @param timeDefnition how to interpret the cutover
+ * @param standardOffset the standard offset in force at the cutover, not null
+ * @param offsetBefore the offset before the cutover, not null
+ * @param offsetAfter the offset after the cutover, not null
+ * @return the rule, not null
+ * @throws IllegalArgumentException if the day of month indicator is invalid
+ * @throws IllegalArgumentException if the end of day flag is true when the time is not midnight
+ */
+ public static ZoneOffsetTransitionRule of(
+ Month month,
+ int dayOfMonthIndicator,
+ DayOfWeek dayOfWeek,
+ LocalTime time,
+ boolean timeEndOfDay,
+ TimeDefinition timeDefnition,
+ ZoneOffset standardOffset,
+ ZoneOffset offsetBefore,
+ ZoneOffset offsetAfter) {
+ Objects.requireNonNull(month, "month");
+ Objects.requireNonNull(time, "time");
+ Objects.requireNonNull(timeDefnition, "timeDefnition");
+ Objects.requireNonNull(standardOffset, "standardOffset");
+ Objects.requireNonNull(offsetBefore, "offsetBefore");
+ Objects.requireNonNull(offsetAfter, "offsetAfter");
+ if (dayOfMonthIndicator < -28 || dayOfMonthIndicator > 31 || dayOfMonthIndicator == 0) {
+ throw new IllegalArgumentException("Day of month indicator must be between -28 and 31 inclusive excluding zero");
+ }
+ if (timeEndOfDay && time.equals(LocalTime.MIDNIGHT) == false) {
+ throw new IllegalArgumentException("Time must be midnight when end of day flag is true");
+ }
+ return new ZoneOffsetTransitionRule(month, dayOfMonthIndicator, dayOfWeek, time, timeEndOfDay, timeDefnition, standardOffset, offsetBefore, offsetAfter);
+ }
+
+ /**
+ * Creates an instance defining the yearly rule to create transitions between two offsets.
+ *
+ * @param month the month of the month-day of the first day of the cutover week, not null
+ * @param dayOfMonthIndicator the day of the month-day of the cutover week, positive if the week is that
+ * day or later, negative if the week is that day or earlier, counting from the last day of the month,
+ * from -28 to 31 excluding 0
+ * @param dayOfWeek the required day-of-week, null if the month-day should not be changed
+ * @param time the cutover time in the 'before' offset, not null
+ * @param timeEndOfDay whether the time is midnight at the end of day
+ * @param timeDefnition how to interpret the cutover
+ * @param standardOffset the standard offset in force at the cutover, not null
+ * @param offsetBefore the offset before the cutover, not null
+ * @param offsetAfter the offset after the cutover, not null
+ * @throws IllegalArgumentException if the day of month indicator is invalid
+ * @throws IllegalArgumentException if the end of day flag is true when the time is not midnight
+ */
+ ZoneOffsetTransitionRule(
+ Month month,
+ int dayOfMonthIndicator,
+ DayOfWeek dayOfWeek,
+ LocalTime time,
+ boolean timeEndOfDay,
+ TimeDefinition timeDefnition,
+ ZoneOffset standardOffset,
+ ZoneOffset offsetBefore,
+ ZoneOffset offsetAfter) {
+ this.month = month;
+ this.dom = (byte) dayOfMonthIndicator;
+ this.dow = dayOfWeek;
+ this.time = time;
+ this.timeEndOfDay = timeEndOfDay;
+ this.timeDefinition = timeDefnition;
+ this.standardOffset = standardOffset;
+ this.offsetBefore = offsetBefore;
+ this.offsetAfter = offsetAfter;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Defend against malicious streams.
+ *
+ * @param s the stream to read
+ * @throws InvalidObjectException always
+ */
+ private void readObject(ObjectInputStream s) throws InvalidObjectException {
+ throw new InvalidObjectException("Deserialization via serialization delegate");
+ }
+
+ /**
+ * Writes the object using a
+ * <a href="../../../serialized-form.html#java.time.zone.Ser">dedicated serialized form</a>.
+ * @serialData
+ * Refer to the serialized form of
+ * <a href="../../../serialized-form.html#java.time.zone.ZoneRules">ZoneRules.writeReplace</a>
+ * for the encoding of epoch seconds and offsets.
+ * <pre style="font-size:1.0em">{@code
+ *
+ * out.writeByte(3); // identifies a ZoneOffsetTransition
+ * final int timeSecs = (timeEndOfDay ? 86400 : time.toSecondOfDay());
+ * final int stdOffset = standardOffset.getTotalSeconds();
+ * final int beforeDiff = offsetBefore.getTotalSeconds() - stdOffset;
+ * final int afterDiff = offsetAfter.getTotalSeconds() - stdOffset;
+ * final int timeByte = (timeSecs % 3600 == 0 ? (timeEndOfDay ? 24 : time.getHour()) : 31);
+ * final int stdOffsetByte = (stdOffset % 900 == 0 ? stdOffset / 900 + 128 : 255);
+ * final int beforeByte = (beforeDiff == 0 || beforeDiff == 1800 || beforeDiff == 3600 ? beforeDiff / 1800 : 3);
+ * final int afterByte = (afterDiff == 0 || afterDiff == 1800 || afterDiff == 3600 ? afterDiff / 1800 : 3);
+ * final int dowByte = (dow == null ? 0 : dow.getValue());
+ * int b = (month.getValue() << 28) + // 4 bits
+ * ((dom + 32) << 22) + // 6 bits
+ * (dowByte << 19) + // 3 bits
+ * (timeByte << 14) + // 5 bits
+ * (timeDefinition.ordinal() << 12) + // 2 bits
+ * (stdOffsetByte << 4) + // 8 bits
+ * (beforeByte << 2) + // 2 bits
+ * afterByte; // 2 bits
+ * out.writeInt(b);
+ * if (timeByte == 31) {
+ * out.writeInt(timeSecs);
+ * }
+ * if (stdOffsetByte == 255) {
+ * out.writeInt(stdOffset);
+ * }
+ * if (beforeByte == 3) {
+ * out.writeInt(offsetBefore.getTotalSeconds());
+ * }
+ * if (afterByte == 3) {
+ * out.writeInt(offsetAfter.getTotalSeconds());
+ * }
+ * }
+ * </pre>
+ *
+ * @return the replacing object, not null
+ */
+ private Object writeReplace() {
+ return new Ser(Ser.ZOTRULE, this);
+ }
+
+ /**
+ * Writes the state to the stream.
+ *
+ * @param out the output stream, not null
+ * @throws IOException if an error occurs
+ */
+ void writeExternal(DataOutput out) throws IOException {
+ final int timeSecs = (timeEndOfDay ? 86400 : time.toSecondOfDay());
+ final int stdOffset = standardOffset.getTotalSeconds();
+ final int beforeDiff = offsetBefore.getTotalSeconds() - stdOffset;
+ final int afterDiff = offsetAfter.getTotalSeconds() - stdOffset;
+ final int timeByte = (timeSecs % 3600 == 0 ? (timeEndOfDay ? 24 : time.getHour()) : 31);
+ final int stdOffsetByte = (stdOffset % 900 == 0 ? stdOffset / 900 + 128 : 255);
+ final int beforeByte = (beforeDiff == 0 || beforeDiff == 1800 || beforeDiff == 3600 ? beforeDiff / 1800 : 3);
+ final int afterByte = (afterDiff == 0 || afterDiff == 1800 || afterDiff == 3600 ? afterDiff / 1800 : 3);
+ final int dowByte = (dow == null ? 0 : dow.getValue());
+ int b = (month.getValue() << 28) + // 4 bits
+ ((dom + 32) << 22) + // 6 bits
+ (dowByte << 19) + // 3 bits
+ (timeByte << 14) + // 5 bits
+ (timeDefinition.ordinal() << 12) + // 2 bits
+ (stdOffsetByte << 4) + // 8 bits
+ (beforeByte << 2) + // 2 bits
+ afterByte; // 2 bits
+ out.writeInt(b);
+ if (timeByte == 31) {
+ out.writeInt(timeSecs);
+ }
+ if (stdOffsetByte == 255) {
+ out.writeInt(stdOffset);
+ }
+ if (beforeByte == 3) {
+ out.writeInt(offsetBefore.getTotalSeconds());
+ }
+ if (afterByte == 3) {
+ out.writeInt(offsetAfter.getTotalSeconds());
+ }
+ }
+
+ /**
+ * Reads the state from the stream.
+ *
+ * @param in the input stream, not null
+ * @return the created object, not null
+ * @throws IOException if an error occurs
+ */
+ static ZoneOffsetTransitionRule readExternal(DataInput in) throws IOException {
+ int data = in.readInt();
+ Month month = Month.of(data >>> 28);
+ int dom = ((data & (63 << 22)) >>> 22) - 32;
+ int dowByte = (data & (7 << 19)) >>> 19;
+ DayOfWeek dow = dowByte == 0 ? null : DayOfWeek.of(dowByte);
+ int timeByte = (data & (31 << 14)) >>> 14;
+ TimeDefinition defn = TimeDefinition.values()[(data & (3 << 12)) >>> 12];
+ int stdByte = (data & (255 << 4)) >>> 4;
+ int beforeByte = (data & (3 << 2)) >>> 2;
+ int afterByte = (data & 3);
+ LocalTime time = (timeByte == 31 ? LocalTime.ofSecondOfDay(in.readInt()) : LocalTime.of(timeByte % 24, 0));
+ ZoneOffset std = (stdByte == 255 ? ZoneOffset.ofTotalSeconds(in.readInt()) : ZoneOffset.ofTotalSeconds((stdByte - 128) * 900));
+ ZoneOffset before = (beforeByte == 3 ? ZoneOffset.ofTotalSeconds(in.readInt()) : ZoneOffset.ofTotalSeconds(std.getTotalSeconds() + beforeByte * 1800));
+ ZoneOffset after = (afterByte == 3 ? ZoneOffset.ofTotalSeconds(in.readInt()) : ZoneOffset.ofTotalSeconds(std.getTotalSeconds() + afterByte * 1800));
+ return ZoneOffsetTransitionRule.of(month, dom, dow, time, timeByte == 24, defn, std, before, after);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the month of the transition.
+ * <p>
+ * If the rule defines an exact date then the month is the month of that date.
+ * <p>
+ * If the rule defines a week where the transition might occur, then the month
+ * if the month of either the earliest or latest possible date of the cutover.
+ *
+ * @return the month of the transition, not null
+ */
+ public Month getMonth() {
+ return month;
+ }
+
+ /**
+ * Gets the indicator of the day-of-month of the transition.
+ * <p>
+ * If the rule defines an exact date then the day is the month of that date.
+ * <p>
+ * If the rule defines a week where the transition might occur, then the day
+ * defines either the start of the end of the transition week.
+ * <p>
+ * If the value is positive, then it represents a normal day-of-month, and is the
+ * earliest possible date that the transition can be.
+ * The date may refer to 29th February which should be treated as 1st March in non-leap years.
+ * <p>
+ * If the value is negative, then it represents the number of days back from the
+ * end of the month where {@code -1} is the last day of the month.
+ * In this case, the day identified is the latest possible date that the transition can be.
+ *
+ * @return the day-of-month indicator, from -28 to 31 excluding 0
+ */
+ public int getDayOfMonthIndicator() {
+ return dom;
+ }
+
+ /**
+ * Gets the day-of-week of the transition.
+ * <p>
+ * If the rule defines an exact date then this returns null.
+ * <p>
+ * If the rule defines a week where the cutover might occur, then this method
+ * returns the day-of-week that the month-day will be adjusted to.
+ * If the day is positive then the adjustment is later.
+ * If the day is negative then the adjustment is earlier.
+ *
+ * @return the day-of-week that the transition occurs, null if the rule defines an exact date
+ */
+ public DayOfWeek getDayOfWeek() {
+ return dow;
+ }
+
+ /**
+ * Gets the local time of day of the transition which must be checked with
+ * {@link #isMidnightEndOfDay()}.
+ * <p>
+ * The time is converted into an instant using the time definition.
+ *
+ * @return the local time of day of the transition, not null
+ */
+ public LocalTime getLocalTime() {
+ return time;
+ }
+
+ /**
+ * Is the transition local time midnight at the end of day.
+ * <p>
+ * The transition may be represented as occurring at 24:00.
+ *
+ * @return whether a local time of midnight is at the start or end of the day
+ */
+ public boolean isMidnightEndOfDay() {
+ return timeEndOfDay;
+ }
+
+ /**
+ * Gets the time definition, specifying how to convert the time to an instant.
+ * <p>
+ * The local time can be converted to an instant using the standard offset,
+ * the wall offset or UTC.
+ *
+ * @return the time definition, not null
+ */
+ public TimeDefinition getTimeDefinition() {
+ return timeDefinition;
+ }
+
+ /**
+ * Gets the standard offset in force at the transition.
+ *
+ * @return the standard offset, not null
+ */
+ public ZoneOffset getStandardOffset() {
+ return standardOffset;
+ }
+
+ /**
+ * Gets the offset before the transition.
+ *
+ * @return the offset before, not null
+ */
+ public ZoneOffset getOffsetBefore() {
+ return offsetBefore;
+ }
+
+ /**
+ * Gets the offset after the transition.
+ *
+ * @return the offset after, not null
+ */
+ public ZoneOffset getOffsetAfter() {
+ return offsetAfter;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Creates a transition instance for the specified year.
+ * <p>
+ * Calculations are performed using the ISO-8601 chronology.
+ *
+ * @param year the year to create a transition for, not null
+ * @return the transition instance, not null
+ */
+ public ZoneOffsetTransition createTransition(int year) {
+ LocalDate date;
+ if (dom < 0) {
+ date = LocalDate.of(year, month, month.length(IsoChronology.INSTANCE.isLeapYear(year)) + 1 + dom);
+ if (dow != null) {
+ date = date.with(previousOrSame(dow));
+ }
+ } else {
+ date = LocalDate.of(year, month, dom);
+ if (dow != null) {
+ date = date.with(nextOrSame(dow));
+ }
+ }
+ if (timeEndOfDay) {
+ date = date.plusDays(1);
+ }
+ LocalDateTime localDT = LocalDateTime.of(date, time);
+ LocalDateTime transition = timeDefinition.createDateTime(localDT, standardOffset, offsetBefore);
+ return new ZoneOffsetTransition(transition, offsetBefore, offsetAfter);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if this object equals another.
+ * <p>
+ * The entire state of the object is compared.
+ *
+ * @param otherRule the other object to compare to, null returns false
+ * @return true if equal
+ */
+ @Override
+ public boolean equals(Object otherRule) {
+ if (otherRule == this) {
+ return true;
+ }
+ if (otherRule instanceof ZoneOffsetTransitionRule) {
+ ZoneOffsetTransitionRule other = (ZoneOffsetTransitionRule) otherRule;
+ return month == other.month && dom == other.dom && dow == other.dow &&
+ timeDefinition == other.timeDefinition &&
+ time.equals(other.time) &&
+ timeEndOfDay == other.timeEndOfDay &&
+ standardOffset.equals(other.standardOffset) &&
+ offsetBefore.equals(other.offsetBefore) &&
+ offsetAfter.equals(other.offsetAfter);
+ }
+ return false;
+ }
+
+ /**
+ * Returns a suitable hash code.
+ *
+ * @return the hash code
+ */
+ @Override
+ public int hashCode() {
+ int hash = ((time.toSecondOfDay() + (timeEndOfDay ? 1 : 0)) << 15) +
+ (month.ordinal() << 11) + ((dom + 32) << 5) +
+ ((dow == null ? 7 : dow.ordinal()) << 2) + (timeDefinition.ordinal());
+ return hash ^ standardOffset.hashCode() ^
+ offsetBefore.hashCode() ^ offsetAfter.hashCode();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns a string describing this object.
+ *
+ * @return a string for debugging, not null
+ */
+ @Override
+ public String toString() {
+ StringBuilder buf = new StringBuilder();
+ buf.append("TransitionRule[")
+ .append(offsetBefore.compareTo(offsetAfter) > 0 ? "Gap " : "Overlap ")
+ .append(offsetBefore).append(" to ").append(offsetAfter).append(", ");
+ if (dow != null) {
+ if (dom == -1) {
+ buf.append(dow.name()).append(" on or before last day of ").append(month.name());
+ } else if (dom < 0) {
+ buf.append(dow.name()).append(" on or before last day minus ").append(-dom - 1).append(" of ").append(month.name());
+ } else {
+ buf.append(dow.name()).append(" on or after ").append(month.name()).append(' ').append(dom);
+ }
+ } else {
+ buf.append(month.name()).append(' ').append(dom);
+ }
+ buf.append(" at ").append(timeEndOfDay ? "24:00" : time.toString())
+ .append(" ").append(timeDefinition)
+ .append(", standard offset ").append(standardOffset)
+ .append(']');
+ return buf.toString();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * A definition of the way a local time can be converted to the actual
+ * transition date-time.
+ * <p>
+ * Time zone rules are expressed in one of three ways:
+ * <ul>
+ * <li>Relative to UTC</li>
+ * <li>Relative to the standard offset in force</li>
+ * <li>Relative to the wall offset (what you would see on a clock on the wall)</li>
+ * </ul>
+ */
+ public static enum TimeDefinition {
+ /** The local date-time is expressed in terms of the UTC offset. */
+ UTC,
+ /** The local date-time is expressed in terms of the wall offset. */
+ WALL,
+ /** The local date-time is expressed in terms of the standard offset. */
+ STANDARD;
+
+ /**
+ * Converts the specified local date-time to the local date-time actually
+ * seen on a wall clock.
+ * <p>
+ * This method converts using the type of this enum.
+ * The output is defined relative to the 'before' offset of the transition.
+ * <p>
+ * The UTC type uses the UTC offset.
+ * The STANDARD type uses the standard offset.
+ * The WALL type returns the input date-time.
+ * The result is intended for use with the wall-offset.
+ *
+ * @param dateTime the local date-time, not null
+ * @param standardOffset the standard offset, not null
+ * @param wallOffset the wall offset, not null
+ * @return the date-time relative to the wall/before offset, not null
+ */
+ public LocalDateTime createDateTime(LocalDateTime dateTime, ZoneOffset standardOffset, ZoneOffset wallOffset) {
+ switch (this) {
+ case UTC: {
+ int difference = wallOffset.getTotalSeconds() - ZoneOffset.UTC.getTotalSeconds();
+ return dateTime.plusSeconds(difference);
+ }
+ case STANDARD: {
+ int difference = wallOffset.getTotalSeconds() - standardOffset.getTotalSeconds();
+ return dateTime.plusSeconds(difference);
+ }
+ default: // WALL
+ return dateTime;
+ }
+ }
+ }
+
+}
diff --git a/java/time/zone/ZoneRules.java b/java/time/zone/ZoneRules.java
new file mode 100644
index 0000000..c3b5bba
--- /dev/null
+++ b/java/time/zone/ZoneRules.java
@@ -0,0 +1,1028 @@
+/*
+ * Copyright (c) 2012, 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) 2009-2012, 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.zone;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.Year;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+// Android-changed: remove mention of ZoneRulesProvider.
+/**
+ * The rules defining how the zone offset varies for a single time-zone.
+ * <p>
+ * The rules model all the historic and future transitions for a time-zone.
+ * {@link ZoneOffsetTransition} is used for known transitions, typically historic.
+ * {@link ZoneOffsetTransitionRule} is used for future transitions that are based
+ * on the result of an algorithm.
+ * <p>
+ * The same rules may be shared internally between multiple zone IDs.
+ * <p>
+ * Serializing an instance of {@code ZoneRules} will store the entire set of rules.
+ * It does not store the zone ID as it is not part of the state of this object.
+ * <p>
+ * A rule implementation may or may not store full information about historic
+ * and future transitions, and the information stored is only as accurate as
+ * that supplied to the implementation by the rules provider.
+ * Applications should treat the data provided as representing the best information
+ * available to the implementation of this rule.
+ *
+ * @implSpec
+ * This class is immutable and thread-safe.
+ *
+ * @since 1.8
+ */
+public final class ZoneRules implements Serializable {
+
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = 3044319355680032515L;
+ /**
+ * The last year to have its transitions cached.
+ */
+ private static final int LAST_CACHED_YEAR = 2100;
+
+ /**
+ * The transitions between standard offsets (epoch seconds), sorted.
+ */
+ private final long[] standardTransitions;
+ /**
+ * The standard offsets.
+ */
+ private final ZoneOffset[] standardOffsets;
+ /**
+ * The transitions between instants (epoch seconds), sorted.
+ */
+ private final long[] savingsInstantTransitions;
+ /**
+ * The transitions between local date-times, sorted.
+ * This is a paired array, where the first entry is the start of the transition
+ * and the second entry is the end of the transition.
+ */
+ private final LocalDateTime[] savingsLocalTransitions;
+ /**
+ * The wall offsets.
+ */
+ private final ZoneOffset[] wallOffsets;
+ /**
+ * The last rule.
+ */
+ private final ZoneOffsetTransitionRule[] lastRules;
+ /**
+ * The map of recent transitions.
+ */
+ private final transient ConcurrentMap<Integer, ZoneOffsetTransition[]> lastRulesCache =
+ new ConcurrentHashMap<Integer, ZoneOffsetTransition[]>();
+ /**
+ * The zero-length long array.
+ */
+ private static final long[] EMPTY_LONG_ARRAY = new long[0];
+ /**
+ * The zero-length lastrules array.
+ */
+ private static final ZoneOffsetTransitionRule[] EMPTY_LASTRULES =
+ new ZoneOffsetTransitionRule[0];
+ /**
+ * The zero-length ldt array.
+ */
+ private static final LocalDateTime[] EMPTY_LDT_ARRAY = new LocalDateTime[0];
+
+ /**
+ * Obtains an instance of a ZoneRules.
+ *
+ * @param baseStandardOffset the standard offset to use before legal rules were set, not null
+ * @param baseWallOffset the wall offset to use before legal rules were set, not null
+ * @param standardOffsetTransitionList the list of changes to the standard offset, not null
+ * @param transitionList the list of transitions, not null
+ * @param lastRules the recurring last rules, size 16 or less, not null
+ * @return the zone rules, not null
+ */
+ public static ZoneRules of(ZoneOffset baseStandardOffset,
+ ZoneOffset baseWallOffset,
+ List<ZoneOffsetTransition> standardOffsetTransitionList,
+ List<ZoneOffsetTransition> transitionList,
+ List<ZoneOffsetTransitionRule> lastRules) {
+ Objects.requireNonNull(baseStandardOffset, "baseStandardOffset");
+ Objects.requireNonNull(baseWallOffset, "baseWallOffset");
+ Objects.requireNonNull(standardOffsetTransitionList, "standardOffsetTransitionList");
+ Objects.requireNonNull(transitionList, "transitionList");
+ Objects.requireNonNull(lastRules, "lastRules");
+ return new ZoneRules(baseStandardOffset, baseWallOffset,
+ standardOffsetTransitionList, transitionList, lastRules);
+ }
+
+ /**
+ * Obtains an instance of ZoneRules that has fixed zone rules.
+ *
+ * @param offset the offset this fixed zone rules is based on, not null
+ * @return the zone rules, not null
+ * @see #isFixedOffset()
+ */
+ public static ZoneRules of(ZoneOffset offset) {
+ Objects.requireNonNull(offset, "offset");
+ return new ZoneRules(offset);
+ }
+
+ /**
+ * Creates an instance.
+ *
+ * @param baseStandardOffset the standard offset to use before legal rules were set, not null
+ * @param baseWallOffset the wall offset to use before legal rules were set, not null
+ * @param standardOffsetTransitionList the list of changes to the standard offset, not null
+ * @param transitionList the list of transitions, not null
+ * @param lastRules the recurring last rules, size 16 or less, not null
+ */
+ ZoneRules(ZoneOffset baseStandardOffset,
+ ZoneOffset baseWallOffset,
+ List<ZoneOffsetTransition> standardOffsetTransitionList,
+ List<ZoneOffsetTransition> transitionList,
+ List<ZoneOffsetTransitionRule> lastRules) {
+ super();
+
+ // convert standard transitions
+
+ this.standardTransitions = new long[standardOffsetTransitionList.size()];
+
+ this.standardOffsets = new ZoneOffset[standardOffsetTransitionList.size() + 1];
+ this.standardOffsets[0] = baseStandardOffset;
+ for (int i = 0; i < standardOffsetTransitionList.size(); i++) {
+ this.standardTransitions[i] = standardOffsetTransitionList.get(i).toEpochSecond();
+ this.standardOffsets[i + 1] = standardOffsetTransitionList.get(i).getOffsetAfter();
+ }
+
+ // convert savings transitions to locals
+ List<LocalDateTime> localTransitionList = new ArrayList<>();
+ List<ZoneOffset> localTransitionOffsetList = new ArrayList<>();
+ localTransitionOffsetList.add(baseWallOffset);
+ for (ZoneOffsetTransition trans : transitionList) {
+ if (trans.isGap()) {
+ localTransitionList.add(trans.getDateTimeBefore());
+ localTransitionList.add(trans.getDateTimeAfter());
+ } else {
+ localTransitionList.add(trans.getDateTimeAfter());
+ localTransitionList.add(trans.getDateTimeBefore());
+ }
+ localTransitionOffsetList.add(trans.getOffsetAfter());
+ }
+ this.savingsLocalTransitions = localTransitionList.toArray(new LocalDateTime[localTransitionList.size()]);
+ this.wallOffsets = localTransitionOffsetList.toArray(new ZoneOffset[localTransitionOffsetList.size()]);
+
+ // convert savings transitions to instants
+ this.savingsInstantTransitions = new long[transitionList.size()];
+ for (int i = 0; i < transitionList.size(); i++) {
+ this.savingsInstantTransitions[i] = transitionList.get(i).toEpochSecond();
+ }
+
+ // last rules
+ if (lastRules.size() > 16) {
+ throw new IllegalArgumentException("Too many transition rules");
+ }
+ this.lastRules = lastRules.toArray(new ZoneOffsetTransitionRule[lastRules.size()]);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param standardTransitions the standard transitions, not null
+ * @param standardOffsets the standard offsets, not null
+ * @param savingsInstantTransitions the standard transitions, not null
+ * @param wallOffsets the wall offsets, not null
+ * @param lastRules the recurring last rules, size 15 or less, not null
+ */
+ private ZoneRules(long[] standardTransitions,
+ ZoneOffset[] standardOffsets,
+ long[] savingsInstantTransitions,
+ ZoneOffset[] wallOffsets,
+ ZoneOffsetTransitionRule[] lastRules) {
+ super();
+
+ this.standardTransitions = standardTransitions;
+ this.standardOffsets = standardOffsets;
+ this.savingsInstantTransitions = savingsInstantTransitions;
+ this.wallOffsets = wallOffsets;
+ this.lastRules = lastRules;
+
+ if (savingsInstantTransitions.length == 0) {
+ this.savingsLocalTransitions = EMPTY_LDT_ARRAY;
+ } else {
+ // convert savings transitions to locals
+ List<LocalDateTime> localTransitionList = new ArrayList<>();
+ for (int i = 0; i < savingsInstantTransitions.length; i++) {
+ ZoneOffset before = wallOffsets[i];
+ ZoneOffset after = wallOffsets[i + 1];
+ ZoneOffsetTransition trans = new ZoneOffsetTransition(savingsInstantTransitions[i], before, after);
+ if (trans.isGap()) {
+ localTransitionList.add(trans.getDateTimeBefore());
+ localTransitionList.add(trans.getDateTimeAfter());
+ } else {
+ localTransitionList.add(trans.getDateTimeAfter());
+ localTransitionList.add(trans.getDateTimeBefore());
+ }
+ }
+ this.savingsLocalTransitions = localTransitionList.toArray(new LocalDateTime[localTransitionList.size()]);
+ }
+ }
+
+ /**
+ * Creates an instance of ZoneRules that has fixed zone rules.
+ *
+ * @param offset the offset this fixed zone rules is based on, not null
+ * @return the zone rules, not null
+ * @see #isFixedOffset()
+ */
+ private ZoneRules(ZoneOffset offset) {
+ this.standardOffsets = new ZoneOffset[1];
+ this.standardOffsets[0] = offset;
+ this.standardTransitions = EMPTY_LONG_ARRAY;
+ this.savingsInstantTransitions = EMPTY_LONG_ARRAY;
+ this.savingsLocalTransitions = EMPTY_LDT_ARRAY;
+ this.wallOffsets = standardOffsets;
+ this.lastRules = EMPTY_LASTRULES;
+ }
+
+ /**
+ * Defend against malicious streams.
+ *
+ * @param s the stream to read
+ * @throws InvalidObjectException always
+ */
+ private void readObject(ObjectInputStream s) throws InvalidObjectException {
+ throw new InvalidObjectException("Deserialization via serialization delegate");
+ }
+
+ /**
+ * Writes the object using a
+ * <a href="../../../serialized-form.html#java.time.zone.Ser">dedicated serialized form</a>.
+ * @serialData
+ * <pre style="font-size:1.0em">{@code
+ *
+ * out.writeByte(1); // identifies a ZoneRules
+ * out.writeInt(standardTransitions.length);
+ * for (long trans : standardTransitions) {
+ * Ser.writeEpochSec(trans, out);
+ * }
+ * for (ZoneOffset offset : standardOffsets) {
+ * Ser.writeOffset(offset, out);
+ * }
+ * out.writeInt(savingsInstantTransitions.length);
+ * for (long trans : savingsInstantTransitions) {
+ * Ser.writeEpochSec(trans, out);
+ * }
+ * for (ZoneOffset offset : wallOffsets) {
+ * Ser.writeOffset(offset, out);
+ * }
+ * out.writeByte(lastRules.length);
+ * for (ZoneOffsetTransitionRule rule : lastRules) {
+ * rule.writeExternal(out);
+ * }
+ * }
+ * </pre>
+ * <p>
+ * Epoch second values used for offsets are encoded in a variable
+ * length form to make the common cases put fewer bytes in the stream.
+ * <pre style="font-size:1.0em">{@code
+ *
+ * static void writeEpochSec(long epochSec, DataOutput out) throws IOException {
+ * if (epochSec >= -4575744000L && epochSec < 10413792000L && epochSec % 900 == 0) { // quarter hours between 1825 and 2300
+ * int store = (int) ((epochSec + 4575744000L) / 900);
+ * out.writeByte((store >>> 16) & 255);
+ * out.writeByte((store >>> 8) & 255);
+ * out.writeByte(store & 255);
+ * } else {
+ * out.writeByte(255);
+ * out.writeLong(epochSec);
+ * }
+ * }
+ * }
+ * </pre>
+ * <p>
+ * ZoneOffset values are encoded in a variable length form so the
+ * common cases put fewer bytes in the stream.
+ * <pre style="font-size:1.0em">{@code
+ *
+ * static void writeOffset(ZoneOffset offset, DataOutput out) throws IOException {
+ * final int offsetSecs = offset.getTotalSeconds();
+ * int offsetByte = offsetSecs % 900 == 0 ? offsetSecs / 900 : 127; // compress to -72 to +72
+ * out.writeByte(offsetByte);
+ * if (offsetByte == 127) {
+ * out.writeInt(offsetSecs);
+ * }
+ * }
+ *}
+ * </pre>
+ * @return the replacing object, not null
+ */
+ private Object writeReplace() {
+ return new Ser(Ser.ZRULES, this);
+ }
+
+ /**
+ * Writes the state to the stream.
+ *
+ * @param out the output stream, not null
+ * @throws IOException if an error occurs
+ */
+ void writeExternal(DataOutput out) throws IOException {
+ out.writeInt(standardTransitions.length);
+ for (long trans : standardTransitions) {
+ Ser.writeEpochSec(trans, out);
+ }
+ for (ZoneOffset offset : standardOffsets) {
+ Ser.writeOffset(offset, out);
+ }
+ out.writeInt(savingsInstantTransitions.length);
+ for (long trans : savingsInstantTransitions) {
+ Ser.writeEpochSec(trans, out);
+ }
+ for (ZoneOffset offset : wallOffsets) {
+ Ser.writeOffset(offset, out);
+ }
+ out.writeByte(lastRules.length);
+ for (ZoneOffsetTransitionRule rule : lastRules) {
+ rule.writeExternal(out);
+ }
+ }
+
+ /**
+ * Reads the state from the stream.
+ *
+ * @param in the input stream, not null
+ * @return the created object, not null
+ * @throws IOException if an error occurs
+ */
+ static ZoneRules readExternal(DataInput in) throws IOException, ClassNotFoundException {
+ int stdSize = in.readInt();
+ long[] stdTrans = (stdSize == 0) ? EMPTY_LONG_ARRAY
+ : new long[stdSize];
+ for (int i = 0; i < stdSize; i++) {
+ stdTrans[i] = Ser.readEpochSec(in);
+ }
+ ZoneOffset[] stdOffsets = new ZoneOffset[stdSize + 1];
+ for (int i = 0; i < stdOffsets.length; i++) {
+ stdOffsets[i] = Ser.readOffset(in);
+ }
+ int savSize = in.readInt();
+ long[] savTrans = (savSize == 0) ? EMPTY_LONG_ARRAY
+ : new long[savSize];
+ for (int i = 0; i < savSize; i++) {
+ savTrans[i] = Ser.readEpochSec(in);
+ }
+ ZoneOffset[] savOffsets = new ZoneOffset[savSize + 1];
+ for (int i = 0; i < savOffsets.length; i++) {
+ savOffsets[i] = Ser.readOffset(in);
+ }
+ int ruleSize = in.readByte();
+ ZoneOffsetTransitionRule[] rules = (ruleSize == 0) ?
+ EMPTY_LASTRULES : new ZoneOffsetTransitionRule[ruleSize];
+ for (int i = 0; i < ruleSize; i++) {
+ rules[i] = ZoneOffsetTransitionRule.readExternal(in);
+ }
+ return new ZoneRules(stdTrans, stdOffsets, savTrans, savOffsets, rules);
+ }
+
+ /**
+ * Checks of the zone rules are fixed, such that the offset never varies.
+ *
+ * @return true if the time-zone is fixed and the offset never changes
+ */
+ public boolean isFixedOffset() {
+ return savingsInstantTransitions.length == 0;
+ }
+
+ /**
+ * Gets the offset applicable at the specified instant in these rules.
+ * <p>
+ * The mapping from an instant to an offset is simple, there is only
+ * one valid offset for each instant.
+ * This method returns that offset.
+ *
+ * @param instant the instant to find the offset for, not null, but null
+ * may be ignored if the rules have a single offset for all instants
+ * @return the offset, not null
+ */
+ public ZoneOffset getOffset(Instant instant) {
+ if (savingsInstantTransitions.length == 0) {
+ return standardOffsets[0];
+ }
+ long epochSec = instant.getEpochSecond();
+ // check if using last rules
+ if (lastRules.length > 0 &&
+ epochSec > savingsInstantTransitions[savingsInstantTransitions.length - 1]) {
+ int year = findYear(epochSec, wallOffsets[wallOffsets.length - 1]);
+ ZoneOffsetTransition[] transArray = findTransitionArray(year);
+ ZoneOffsetTransition trans = null;
+ for (int i = 0; i < transArray.length; i++) {
+ trans = transArray[i];
+ if (epochSec < trans.toEpochSecond()) {
+ return trans.getOffsetBefore();
+ }
+ }
+ return trans.getOffsetAfter();
+ }
+
+ // using historic rules
+ int index = Arrays.binarySearch(savingsInstantTransitions, epochSec);
+ if (index < 0) {
+ // switch negative insert position to start of matched range
+ index = -index - 2;
+ }
+ return wallOffsets[index + 1];
+ }
+
+ /**
+ * Gets a suitable offset for the specified local date-time in these rules.
+ * <p>
+ * The mapping from a local date-time to an offset is not straightforward.
+ * There are three cases:
+ * <ul>
+ * <li>Normal, with one valid offset. For the vast majority of the year, the normal
+ * case applies, where there is a single valid offset for the local date-time.</li>
+ * <li>Gap, with zero valid offsets. This is when clocks jump forward typically
+ * due to the spring daylight savings change from "winter" to "summer".
+ * In a gap there are local date-time values with no valid offset.</li>
+ * <li>Overlap, with two valid offsets. This is when clocks are set back typically
+ * due to the autumn daylight savings change from "summer" to "winter".
+ * In an overlap there are local date-time values with two valid offsets.</li>
+ * </ul>
+ * Thus, for any given local date-time there can be zero, one or two valid offsets.
+ * This method returns the single offset in the Normal case, and in the Gap or Overlap
+ * case it returns the offset before the transition.
+ * <p>
+ * Since, in the case of Gap and Overlap, the offset returned is a "best" value, rather
+ * than the "correct" value, it should be treated with care. Applications that care
+ * about the correct offset should use a combination of this method,
+ * {@link #getValidOffsets(LocalDateTime)} and {@link #getTransition(LocalDateTime)}.
+ *
+ * @param localDateTime the local date-time to query, not null, but null
+ * may be ignored if the rules have a single offset for all instants
+ * @return the best available offset for the local date-time, not null
+ */
+ public ZoneOffset getOffset(LocalDateTime localDateTime) {
+ Object info = getOffsetInfo(localDateTime);
+ if (info instanceof ZoneOffsetTransition) {
+ return ((ZoneOffsetTransition) info).getOffsetBefore();
+ }
+ return (ZoneOffset) info;
+ }
+
+ /**
+ * Gets the offset applicable at the specified local date-time in these rules.
+ * <p>
+ * The mapping from a local date-time to an offset is not straightforward.
+ * There are three cases:
+ * <ul>
+ * <li>Normal, with one valid offset. For the vast majority of the year, the normal
+ * case applies, where there is a single valid offset for the local date-time.</li>
+ * <li>Gap, with zero valid offsets. This is when clocks jump forward typically
+ * due to the spring daylight savings change from "winter" to "summer".
+ * In a gap there are local date-time values with no valid offset.</li>
+ * <li>Overlap, with two valid offsets. This is when clocks are set back typically
+ * due to the autumn daylight savings change from "summer" to "winter".
+ * In an overlap there are local date-time values with two valid offsets.</li>
+ * </ul>
+ * Thus, for any given local date-time there can be zero, one or two valid offsets.
+ * This method returns that list of valid offsets, which is a list of size 0, 1 or 2.
+ * In the case where there are two offsets, the earlier offset is returned at index 0
+ * and the later offset at index 1.
+ * <p>
+ * There are various ways to handle the conversion from a {@code LocalDateTime}.
+ * One technique, using this method, would be:
+ * <pre>
+ * List<ZoneOffset> validOffsets = rules.getOffset(localDT);
+ * if (validOffsets.size() == 1) {
+ * // Normal case: only one valid offset
+ * zoneOffset = validOffsets.get(0);
+ * } else {
+ * // Gap or Overlap: determine what to do from transition (which will be non-null)
+ * ZoneOffsetTransition trans = rules.getTransition(localDT);
+ * }
+ * </pre>
+ * <p>
+ * In theory, it is possible for there to be more than two valid offsets.
+ * This would happen if clocks to be put back more than once in quick succession.
+ * This has never happened in the history of time-zones and thus has no special handling.
+ * However, if it were to happen, then the list would return more than 2 entries.
+ *
+ * @param localDateTime the local date-time to query for valid offsets, not null, but null
+ * may be ignored if the rules have a single offset for all instants
+ * @return the list of valid offsets, may be immutable, not null
+ */
+ public List<ZoneOffset> getValidOffsets(LocalDateTime localDateTime) {
+ // should probably be optimized
+ Object info = getOffsetInfo(localDateTime);
+ if (info instanceof ZoneOffsetTransition) {
+ return ((ZoneOffsetTransition) info).getValidOffsets();
+ }
+ return Collections.singletonList((ZoneOffset) info);
+ }
+
+ /**
+ * Gets the offset transition applicable at the specified local date-time in these rules.
+ * <p>
+ * The mapping from a local date-time to an offset is not straightforward.
+ * There are three cases:
+ * <ul>
+ * <li>Normal, with one valid offset. For the vast majority of the year, the normal
+ * case applies, where there is a single valid offset for the local date-time.</li>
+ * <li>Gap, with zero valid offsets. This is when clocks jump forward typically
+ * due to the spring daylight savings change from "winter" to "summer".
+ * In a gap there are local date-time values with no valid offset.</li>
+ * <li>Overlap, with two valid offsets. This is when clocks are set back typically
+ * due to the autumn daylight savings change from "summer" to "winter".
+ * In an overlap there are local date-time values with two valid offsets.</li>
+ * </ul>
+ * A transition is used to model the cases of a Gap or Overlap.
+ * The Normal case will return null.
+ * <p>
+ * There are various ways to handle the conversion from a {@code LocalDateTime}.
+ * One technique, using this method, would be:
+ * <pre>
+ * ZoneOffsetTransition trans = rules.getTransition(localDT);
+ * if (trans == null) {
+ * // Gap or Overlap: determine what to do from transition
+ * } else {
+ * // Normal case: only one valid offset
+ * zoneOffset = rule.getOffset(localDT);
+ * }
+ * </pre>
+ *
+ * @param localDateTime the local date-time to query for offset transition, not null, but null
+ * may be ignored if the rules have a single offset for all instants
+ * @return the offset transition, null if the local date-time is not in transition
+ */
+ public ZoneOffsetTransition getTransition(LocalDateTime localDateTime) {
+ Object info = getOffsetInfo(localDateTime);
+ return (info instanceof ZoneOffsetTransition ? (ZoneOffsetTransition) info : null);
+ }
+
+ private Object getOffsetInfo(LocalDateTime dt) {
+ if (savingsInstantTransitions.length == 0) {
+ return standardOffsets[0];
+ }
+ // check if using last rules
+ if (lastRules.length > 0 &&
+ dt.isAfter(savingsLocalTransitions[savingsLocalTransitions.length - 1])) {
+ ZoneOffsetTransition[] transArray = findTransitionArray(dt.getYear());
+ Object info = null;
+ for (ZoneOffsetTransition trans : transArray) {
+ info = findOffsetInfo(dt, trans);
+ if (info instanceof ZoneOffsetTransition || info.equals(trans.getOffsetBefore())) {
+ return info;
+ }
+ }
+ return info;
+ }
+
+ // using historic rules
+ int index = Arrays.binarySearch(savingsLocalTransitions, dt);
+ if (index == -1) {
+ // before first transition
+ return wallOffsets[0];
+ }
+ if (index < 0) {
+ // switch negative insert position to start of matched range
+ index = -index - 2;
+ } else if (index < savingsLocalTransitions.length - 1 &&
+ savingsLocalTransitions[index].equals(savingsLocalTransitions[index + 1])) {
+ // handle overlap immediately following gap
+ index++;
+ }
+ if ((index & 1) == 0) {
+ // gap or overlap
+ LocalDateTime dtBefore = savingsLocalTransitions[index];
+ LocalDateTime dtAfter = savingsLocalTransitions[index + 1];
+ ZoneOffset offsetBefore = wallOffsets[index / 2];
+ ZoneOffset offsetAfter = wallOffsets[index / 2 + 1];
+ if (offsetAfter.getTotalSeconds() > offsetBefore.getTotalSeconds()) {
+ // gap
+ return new ZoneOffsetTransition(dtBefore, offsetBefore, offsetAfter);
+ } else {
+ // overlap
+ return new ZoneOffsetTransition(dtAfter, offsetBefore, offsetAfter);
+ }
+ } else {
+ // normal (neither gap or overlap)
+ return wallOffsets[index / 2 + 1];
+ }
+ }
+
+ /**
+ * Finds the offset info for a local date-time and transition.
+ *
+ * @param dt the date-time, not null
+ * @param trans the transition, not null
+ * @return the offset info, not null
+ */
+ private Object findOffsetInfo(LocalDateTime dt, ZoneOffsetTransition trans) {
+ LocalDateTime localTransition = trans.getDateTimeBefore();
+ if (trans.isGap()) {
+ if (dt.isBefore(localTransition)) {
+ return trans.getOffsetBefore();
+ }
+ if (dt.isBefore(trans.getDateTimeAfter())) {
+ return trans;
+ } else {
+ return trans.getOffsetAfter();
+ }
+ } else {
+ if (dt.isBefore(localTransition) == false) {
+ return trans.getOffsetAfter();
+ }
+ if (dt.isBefore(trans.getDateTimeAfter())) {
+ return trans.getOffsetBefore();
+ } else {
+ return trans;
+ }
+ }
+ }
+
+ /**
+ * Finds the appropriate transition array for the given year.
+ *
+ * @param year the year, not null
+ * @return the transition array, not null
+ */
+ private ZoneOffsetTransition[] findTransitionArray(int year) {
+ Integer yearObj = year; // should use Year class, but this saves a class load
+ ZoneOffsetTransition[] transArray = lastRulesCache.get(yearObj);
+ if (transArray != null) {
+ return transArray;
+ }
+ ZoneOffsetTransitionRule[] ruleArray = lastRules;
+ transArray = new ZoneOffsetTransition[ruleArray.length];
+ for (int i = 0; i < ruleArray.length; i++) {
+ transArray[i] = ruleArray[i].createTransition(year);
+ }
+ if (year < LAST_CACHED_YEAR) {
+ lastRulesCache.putIfAbsent(yearObj, transArray);
+ }
+ return transArray;
+ }
+
+ /**
+ * Gets the standard offset for the specified instant in this zone.
+ * <p>
+ * This provides access to historic information on how the standard offset
+ * has changed over time.
+ * The standard offset is the offset before any daylight saving time is applied.
+ * This is typically the offset applicable during winter.
+ *
+ * @param instant the instant to find the offset information for, not null, but null
+ * may be ignored if the rules have a single offset for all instants
+ * @return the standard offset, not null
+ */
+ public ZoneOffset getStandardOffset(Instant instant) {
+ if (savingsInstantTransitions.length == 0) {
+ return standardOffsets[0];
+ }
+ long epochSec = instant.getEpochSecond();
+ int index = Arrays.binarySearch(standardTransitions, epochSec);
+ if (index < 0) {
+ // switch negative insert position to start of matched range
+ index = -index - 2;
+ }
+ return standardOffsets[index + 1];
+ }
+
+ /**
+ * Gets the amount of daylight savings in use for the specified instant in this zone.
+ * <p>
+ * This provides access to historic information on how the amount of daylight
+ * savings has changed over time.
+ * This is the difference between the standard offset and the actual offset.
+ * Typically the amount is zero during winter and one hour during summer.
+ * Time-zones are second-based, so the nanosecond part of the duration will be zero.
+ * <p>
+ * This default implementation calculates the duration from the
+ * {@link #getOffset(java.time.Instant) actual} and
+ * {@link #getStandardOffset(java.time.Instant) standard} offsets.
+ *
+ * @param instant the instant to find the daylight savings for, not null, but null
+ * may be ignored if the rules have a single offset for all instants
+ * @return the difference between the standard and actual offset, not null
+ */
+ public Duration getDaylightSavings(Instant instant) {
+ if (savingsInstantTransitions.length == 0) {
+ return Duration.ZERO;
+ }
+ ZoneOffset standardOffset = getStandardOffset(instant);
+ ZoneOffset actualOffset = getOffset(instant);
+ return Duration.ofSeconds(actualOffset.getTotalSeconds() - standardOffset.getTotalSeconds());
+ }
+
+ /**
+ * Checks if the specified instant is in daylight savings.
+ * <p>
+ * This checks if the standard offset and the actual offset are the same
+ * for the specified instant.
+ * If they are not, it is assumed that daylight savings is in operation.
+ * <p>
+ * This default implementation compares the {@link #getOffset(java.time.Instant) actual}
+ * and {@link #getStandardOffset(java.time.Instant) standard} offsets.
+ *
+ * @param instant the instant to find the offset information for, not null, but null
+ * may be ignored if the rules have a single offset for all instants
+ * @return the standard offset, not null
+ */
+ public boolean isDaylightSavings(Instant instant) {
+ return (getStandardOffset(instant).equals(getOffset(instant)) == false);
+ }
+
+ /**
+ * Checks if the offset date-time is valid for these rules.
+ * <p>
+ * To be valid, the local date-time must not be in a gap and the offset
+ * must match one of the valid offsets.
+ * <p>
+ * This default implementation checks if {@link #getValidOffsets(java.time.LocalDateTime)}
+ * contains the specified offset.
+ *
+ * @param localDateTime the date-time to check, not null, but null
+ * may be ignored if the rules have a single offset for all instants
+ * @param offset the offset to check, null returns false
+ * @return true if the offset date-time is valid for these rules
+ */
+ public boolean isValidOffset(LocalDateTime localDateTime, ZoneOffset offset) {
+ return getValidOffsets(localDateTime).contains(offset);
+ }
+
+ /**
+ * Gets the next transition after the specified instant.
+ * <p>
+ * This returns details of the next transition after the specified instant.
+ * For example, if the instant represents a point where "Summer" daylight savings time
+ * applies, then the method will return the transition to the next "Winter" time.
+ *
+ * @param instant the instant to get the next transition after, not null, but null
+ * may be ignored if the rules have a single offset for all instants
+ * @return the next transition after the specified instant, null if this is after the last transition
+ */
+ public ZoneOffsetTransition nextTransition(Instant instant) {
+ if (savingsInstantTransitions.length == 0) {
+ return null;
+ }
+ long epochSec = instant.getEpochSecond();
+ // check if using last rules
+ if (epochSec >= savingsInstantTransitions[savingsInstantTransitions.length - 1]) {
+ if (lastRules.length == 0) {
+ return null;
+ }
+ // search year the instant is in
+ int year = findYear(epochSec, wallOffsets[wallOffsets.length - 1]);
+ ZoneOffsetTransition[] transArray = findTransitionArray(year);
+ for (ZoneOffsetTransition trans : transArray) {
+ if (epochSec < trans.toEpochSecond()) {
+ return trans;
+ }
+ }
+ // use first from following year
+ if (year < Year.MAX_VALUE) {
+ transArray = findTransitionArray(year + 1);
+ return transArray[0];
+ }
+ return null;
+ }
+
+ // using historic rules
+ int index = Arrays.binarySearch(savingsInstantTransitions, epochSec);
+ if (index < 0) {
+ index = -index - 1; // switched value is the next transition
+ } else {
+ index += 1; // exact match, so need to add one to get the next
+ }
+ return new ZoneOffsetTransition(savingsInstantTransitions[index], wallOffsets[index], wallOffsets[index + 1]);
+ }
+
+ /**
+ * Gets the previous transition before the specified instant.
+ * <p>
+ * This returns details of the previous transition after the specified instant.
+ * For example, if the instant represents a point where "summer" daylight saving time
+ * applies, then the method will return the transition from the previous "winter" time.
+ *
+ * @param instant the instant to get the previous transition after, not null, but null
+ * may be ignored if the rules have a single offset for all instants
+ * @return the previous transition after the specified instant, null if this is before the first transition
+ */
+ public ZoneOffsetTransition previousTransition(Instant instant) {
+ if (savingsInstantTransitions.length == 0) {
+ return null;
+ }
+ long epochSec = instant.getEpochSecond();
+ if (instant.getNano() > 0 && epochSec < Long.MAX_VALUE) {
+ epochSec += 1; // allow rest of method to only use seconds
+ }
+
+ // check if using last rules
+ long lastHistoric = savingsInstantTransitions[savingsInstantTransitions.length - 1];
+ if (lastRules.length > 0 && epochSec > lastHistoric) {
+ // search year the instant is in
+ ZoneOffset lastHistoricOffset = wallOffsets[wallOffsets.length - 1];
+ int year = findYear(epochSec, lastHistoricOffset);
+ ZoneOffsetTransition[] transArray = findTransitionArray(year);
+ for (int i = transArray.length - 1; i >= 0; i--) {
+ if (epochSec > transArray[i].toEpochSecond()) {
+ return transArray[i];
+ }
+ }
+ // use last from preceding year
+ int lastHistoricYear = findYear(lastHistoric, lastHistoricOffset);
+ if (--year > lastHistoricYear) {
+ transArray = findTransitionArray(year);
+ return transArray[transArray.length - 1];
+ }
+ // drop through
+ }
+
+ // using historic rules
+ int index = Arrays.binarySearch(savingsInstantTransitions, epochSec);
+ if (index < 0) {
+ index = -index - 1;
+ }
+ if (index <= 0) {
+ return null;
+ }
+ return new ZoneOffsetTransition(savingsInstantTransitions[index - 1], wallOffsets[index - 1], wallOffsets[index]);
+ }
+
+ private int findYear(long epochSecond, ZoneOffset offset) {
+ // inline for performance
+ long localSecond = epochSecond + offset.getTotalSeconds();
+ long localEpochDay = Math.floorDiv(localSecond, 86400);
+ return LocalDate.ofEpochDay(localEpochDay).getYear();
+ }
+
+ /**
+ * Gets the complete list of fully defined transitions.
+ * <p>
+ * The complete set of transitions for this rules instance is defined by this method
+ * and {@link #getTransitionRules()}. This method returns those transitions that have
+ * been fully defined. These are typically historical, but may be in the future.
+ * <p>
+ * The list will be empty for fixed offset rules and for any time-zone where there has
+ * only ever been a single offset. The list will also be empty if the transition rules are unknown.
+ *
+ * @return an immutable list of fully defined transitions, not null
+ */
+ public List<ZoneOffsetTransition> getTransitions() {
+ List<ZoneOffsetTransition> list = new ArrayList<>();
+ for (int i = 0; i < savingsInstantTransitions.length; i++) {
+ list.add(new ZoneOffsetTransition(savingsInstantTransitions[i], wallOffsets[i], wallOffsets[i + 1]));
+ }
+ return Collections.unmodifiableList(list);
+ }
+
+ /**
+ * Gets the list of transition rules for years beyond those defined in the transition list.
+ * <p>
+ * The complete set of transitions for this rules instance is defined by this method
+ * and {@link #getTransitions()}. This method returns instances of {@link ZoneOffsetTransitionRule}
+ * that define an algorithm for when transitions will occur.
+ * <p>
+ * For any given {@code ZoneRules}, this list contains the transition rules for years
+ * beyond those years that have been fully defined. These rules typically refer to future
+ * daylight saving time rule changes.
+ * <p>
+ * If the zone defines daylight savings into the future, then the list will normally
+ * be of size two and hold information about entering and exiting daylight savings.
+ * If the zone does not have daylight savings, or information about future changes
+ * is uncertain, then the list will be empty.
+ * <p>
+ * The list will be empty for fixed offset rules and for any time-zone where there is no
+ * daylight saving time. The list will also be empty if the transition rules are unknown.
+ *
+ * @return an immutable list of transition rules, not null
+ */
+ public List<ZoneOffsetTransitionRule> getTransitionRules() {
+ return Collections.unmodifiableList(Arrays.asList(lastRules));
+ }
+
+ /**
+ * Checks if this set of rules equals another.
+ * <p>
+ * Two rule sets are equal if they will always result in the same output
+ * for any given input instant or local date-time.
+ * Rules from two different groups may return false even if they are in fact the same.
+ * <p>
+ * This definition should result in implementations comparing their entire state.
+ *
+ * @param otherRules the other rules, null returns false
+ * @return true if this rules is the same as that specified
+ */
+ @Override
+ public boolean equals(Object otherRules) {
+ if (this == otherRules) {
+ return true;
+ }
+ if (otherRules instanceof ZoneRules) {
+ ZoneRules other = (ZoneRules) otherRules;
+ return Arrays.equals(standardTransitions, other.standardTransitions) &&
+ Arrays.equals(standardOffsets, other.standardOffsets) &&
+ Arrays.equals(savingsInstantTransitions, other.savingsInstantTransitions) &&
+ Arrays.equals(wallOffsets, other.wallOffsets) &&
+ Arrays.equals(lastRules, other.lastRules);
+ }
+ return false;
+ }
+
+ /**
+ * Returns a suitable hash code given the definition of {@code #equals}.
+ *
+ * @return the hash code
+ */
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(standardTransitions) ^
+ Arrays.hashCode(standardOffsets) ^
+ Arrays.hashCode(savingsInstantTransitions) ^
+ Arrays.hashCode(wallOffsets) ^
+ Arrays.hashCode(lastRules);
+ }
+
+ /**
+ * Returns a string describing this object.
+ *
+ * @return a string for debugging, not null
+ */
+ @Override
+ public String toString() {
+ return "ZoneRules[currentStandardOffset=" + standardOffsets[standardOffsets.length - 1] + "]";
+ }
+
+}
diff --git a/java/time/zone/ZoneRulesException.java b/java/time/zone/ZoneRulesException.java
new file mode 100644
index 0000000..2c845ca
--- /dev/null
+++ b/java/time/zone/ZoneRulesException.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2012, 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) 2008-2012, 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.zone;
+
+import java.time.DateTimeException;
+
+/**
+ * Thrown to indicate a problem with time-zone configuration.
+ * <p>
+ * This exception is used to indicate a problems with the configured
+ * time-zone rules.
+ *
+ * @implSpec
+ * This class is intended for use in a single thread.
+ *
+ * @since 1.8
+ */
+public class ZoneRulesException extends DateTimeException {
+
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = -1632418723876261839L;
+
+ /**
+ * Constructs a new date-time exception with the specified message.
+ *
+ * @param message the message to use for this exception, may be null
+ */
+ public ZoneRulesException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs a new date-time exception with the specified message and cause.
+ *
+ * @param message the message to use for this exception, may be null
+ * @param cause the cause of the exception, may be null
+ */
+ public ZoneRulesException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
diff --git a/java/time/zone/ZoneRulesProvider.java b/java/time/zone/ZoneRulesProvider.java
new file mode 100644
index 0000000..a90727a
--- /dev/null
+++ b/java/time/zone/ZoneRulesProvider.java
@@ -0,0 +1,395 @@
+/*
+ * Copyright (c) 2012, 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) 2009-2012, 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.zone;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NavigableMap;
+import java.util.Objects;
+import java.util.ServiceConfigurationError;
+import java.util.ServiceLoader;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Provider of time-zone rules to the system.
+ * <p>
+ * This class manages the configuration of time-zone rules.
+ * The static methods provide the public API that can be used to manage the providers.
+ * The abstract methods provide the SPI that allows rules to be provided.
+ * <p>
+ * ZoneRulesProvider may be installed in an instance of the Java Platform as
+ * extension classes, that is, jar files placed into any of the usual extension
+ * directories. Installed providers are loaded using the service-provider loading
+ * facility defined by the {@link ServiceLoader} class. A ZoneRulesProvider
+ * identifies itself with a provider configuration file named
+ * {@code java.time.zone.ZoneRulesProvider} in the resource directory
+ * {@code META-INF/services}. The file should contain a line that specifies the
+ * fully qualified concrete zonerules-provider class name.
+ * Providers may also be made available by adding them to the class path or by
+ * registering themselves via {@link #registerProvider} method.
+ * <p>
+ * The Java virtual machine has a default provider that provides zone rules
+ * for the time-zones defined by IANA Time Zone Database (TZDB). If the system
+ * property {@code java.time.zone.DefaultZoneRulesProvider} is defined then
+ * it is taken to be the fully-qualified name of a concrete ZoneRulesProvider
+ * class to be loaded as the default provider, using the system class loader.
+ * If this system property is not defined, a system-default provider will be
+ * loaded to serve as the default provider.
+ * <p>
+ * Rules are looked up primarily by zone ID, as used by {@link ZoneId}.
+ * Only zone region IDs may be used, zone offset IDs are not used here.
+ * <p>
+ * Time-zone rules are political, thus the data can change at any time.
+ * Each provider will provide the latest rules for each zone ID, but they
+ * may also provide the history of how the rules changed.
+ *
+ * @implSpec
+ * This interface is a service provider that can be called by multiple threads.
+ * Implementations must be immutable and thread-safe.
+ * <p>
+ * Providers must ensure that once a rule has been seen by the application, the
+ * rule must continue to be available.
+ * <p>
+* Providers are encouraged to implement a meaningful {@code toString} method.
+ * <p>
+ * Many systems would like to update time-zone rules dynamically without stopping the JVM.
+ * When examined in detail, this is a complex problem.
+ * Providers may choose to handle dynamic updates, however the default provider does not.
+ *
+ * @since 1.8
+ */
+public abstract class ZoneRulesProvider {
+
+ /**
+ * The set of loaded providers.
+ */
+ private static final CopyOnWriteArrayList<ZoneRulesProvider> PROVIDERS = new CopyOnWriteArrayList<>();
+ /**
+ * The lookup from zone ID to provider.
+ */
+ private static final ConcurrentMap<String, ZoneRulesProvider> ZONES = new ConcurrentHashMap<>(512, 0.75f, 2);
+
+ static {
+ // Android-changed: use a single hard-coded provider.
+ ZoneRulesProvider provider = new IcuZoneRulesProvider();
+ registerProvider(provider);
+ }
+
+ //-------------------------------------------------------------------------
+ /**
+ * Gets the set of available zone IDs.
+ * <p>
+ * These IDs are the string form of a {@link ZoneId}.
+ *
+ * @return a modifiable copy of the set of zone IDs, not null
+ */
+ public static Set<String> getAvailableZoneIds() {
+ return new HashSet<>(ZONES.keySet());
+ }
+
+ /**
+ * Gets the rules for the zone ID.
+ * <p>
+ * This returns the latest available rules for the zone ID.
+ * <p>
+ * This method relies on time-zone data provider files that are configured.
+ * These are loaded using a {@code ServiceLoader}.
+ * <p>
+ * The caching flag is designed to allow provider implementations to
+ * prevent the rules being cached in {@code ZoneId}.
+ * Under normal circumstances, the caching of zone rules is highly desirable
+ * as it will provide greater performance. However, there is a use case where
+ * the caching would not be desirable, see {@link #provideRules}.
+ *
+ * @param zoneId the zone ID as defined by {@code ZoneId}, not null
+ * @param forCaching whether the rules are being queried for caching,
+ * true if the returned rules will be cached by {@code ZoneId},
+ * false if they will be returned to the user without being cached in {@code ZoneId}
+ * @return the rules, null if {@code forCaching} is true and this
+ * is a dynamic provider that wants to prevent caching in {@code ZoneId},
+ * otherwise not null
+ * @throws ZoneRulesException if rules cannot be obtained for the zone ID
+ */
+ public static ZoneRules getRules(String zoneId, boolean forCaching) {
+ Objects.requireNonNull(zoneId, "zoneId");
+ return getProvider(zoneId).provideRules(zoneId, forCaching);
+ }
+
+ /**
+ * Gets the history of rules for the zone ID.
+ * <p>
+ * Time-zones are defined by governments and change frequently.
+ * This method allows applications to find the history of changes to the
+ * rules for a single zone ID. The map is keyed by a string, which is the
+ * version string associated with the rules.
+ * <p>
+ * The exact meaning and format of the version is provider specific.
+ * The version must follow lexicographical order, thus the returned map will
+ * be order from the oldest known rules to the newest available rules.
+ * The default 'TZDB' group uses version numbering consisting of the year
+ * followed by a letter, such as '2009e' or '2012f'.
+ * <p>
+ * Implementations must provide a result for each valid zone ID, however
+ * they do not have to provide a history of rules.
+ * Thus the map will always contain one element, and will only contain more
+ * than one element if historical rule information is available.
+ *
+ * @param zoneId the zone ID as defined by {@code ZoneId}, not null
+ * @return a modifiable copy of the history of the rules for the ID, sorted
+ * from oldest to newest, not null
+ * @throws ZoneRulesException if history cannot be obtained for the zone ID
+ */
+ public static NavigableMap<String, ZoneRules> getVersions(String zoneId) {
+ Objects.requireNonNull(zoneId, "zoneId");
+ return getProvider(zoneId).provideVersions(zoneId);
+ }
+
+ /**
+ * Gets the provider for the zone ID.
+ *
+ * @param zoneId the zone ID as defined by {@code ZoneId}, not null
+ * @return the provider, not null
+ * @throws ZoneRulesException if the zone ID is unknown
+ */
+ private static ZoneRulesProvider getProvider(String zoneId) {
+ ZoneRulesProvider provider = ZONES.get(zoneId);
+ if (provider == null) {
+ if (ZONES.isEmpty()) {
+ throw new ZoneRulesException("No time-zone data files registered");
+ }
+ throw new ZoneRulesException("Unknown time-zone ID: " + zoneId);
+ }
+ return provider;
+ }
+
+ //-------------------------------------------------------------------------
+ /**
+ * Registers a zone rules provider.
+ * <p>
+ * This adds a new provider to those currently available.
+ * A provider supplies rules for one or more zone IDs.
+ * A provider cannot be registered if it supplies a zone ID that has already been
+ * registered. See the notes on time-zone IDs in {@link ZoneId}, especially
+ * the section on using the concept of a "group" to make IDs unique.
+ * <p>
+ * To ensure the integrity of time-zones already created, there is no way
+ * to deregister providers.
+ *
+ * @param provider the provider to register, not null
+ * @throws ZoneRulesException if a zone ID is already registered
+ */
+ public static void registerProvider(ZoneRulesProvider provider) {
+ Objects.requireNonNull(provider, "provider");
+ registerProvider0(provider);
+ PROVIDERS.add(provider);
+ }
+
+ /**
+ * Registers the provider.
+ *
+ * @param provider the provider to register, not null
+ * @throws ZoneRulesException if unable to complete the registration
+ */
+ private static void registerProvider0(ZoneRulesProvider provider) {
+ for (String zoneId : provider.provideZoneIds()) {
+ Objects.requireNonNull(zoneId, "zoneId");
+ ZoneRulesProvider old = ZONES.putIfAbsent(zoneId, provider);
+ if (old != null) {
+ throw new ZoneRulesException(
+ "Unable to register zone as one already registered with that ID: " + zoneId +
+ ", currently loading from provider: " + provider);
+ }
+ }
+ }
+
+ /**
+ * Refreshes the rules from the underlying data provider.
+ * <p>
+ * This method allows an application to request that the providers check
+ * for any updates to the provided rules.
+ * After calling this method, the offset stored in any {@link ZonedDateTime}
+ * may be invalid for the zone ID.
+ * <p>
+ * Dynamic update of rules is a complex problem and most applications
+ * should not use this method or dynamic rules.
+ * To achieve dynamic rules, a provider implementation will have to be written
+ * as per the specification of this class.
+ * In addition, instances of {@code ZoneRules} must not be cached in the
+ * application as they will become stale. However, the boolean flag on
+ * {@link #provideRules(String, boolean)} allows provider implementations
+ * to control the caching of {@code ZoneId}, potentially ensuring that
+ * all objects in the system see the new rules.
+ * Note that there is likely to be a cost in performance of a dynamic rules
+ * provider. Note also that no dynamic rules provider is in this specification.
+ *
+ * @return true if the rules were updated
+ * @throws ZoneRulesException if an error occurs during the refresh
+ */
+ public static boolean refresh() {
+ boolean changed = false;
+ for (ZoneRulesProvider provider : PROVIDERS) {
+ changed |= provider.provideRefresh();
+ }
+ return changed;
+ }
+
+ /**
+ * Constructor.
+ */
+ protected ZoneRulesProvider() {
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * SPI method to get the available zone IDs.
+ * <p>
+ * This obtains the IDs that this {@code ZoneRulesProvider} provides.
+ * A provider should provide data for at least one zone ID.
+ * <p>
+ * The returned zone IDs remain available and valid for the lifetime of the application.
+ * A dynamic provider may increase the set of IDs as more data becomes available.
+ *
+ * @return the set of zone IDs being provided, not null
+ * @throws ZoneRulesException if a problem occurs while providing the IDs
+ */
+ protected abstract Set<String> provideZoneIds();
+
+ /**
+ * SPI method to get the rules for the zone ID.
+ * <p>
+ * This loads the rules for the specified zone ID.
+ * The provider implementation must validate that the zone ID is valid and
+ * available, throwing a {@code ZoneRulesException} if it is not.
+ * The result of the method in the valid case depends on the caching flag.
+ * <p>
+ * If the provider implementation is not dynamic, then the result of the
+ * method must be the non-null set of rules selected by the ID.
+ * <p>
+ * If the provider implementation is dynamic, then the flag gives the option
+ * of preventing the returned rules from being cached in {@link ZoneId}.
+ * When the flag is true, the provider is permitted to return null, where
+ * null will prevent the rules from being cached in {@code ZoneId}.
+ * When the flag is false, the provider must return non-null rules.
+ *
+ * @param zoneId the zone ID as defined by {@code ZoneId}, not null
+ * @param forCaching whether the rules are being queried for caching,
+ * true if the returned rules will be cached by {@code ZoneId},
+ * false if they will be returned to the user without being cached in {@code ZoneId}
+ * @return the rules, null if {@code forCaching} is true and this
+ * is a dynamic provider that wants to prevent caching in {@code ZoneId},
+ * otherwise not null
+ * @throws ZoneRulesException if rules cannot be obtained for the zone ID
+ */
+ protected abstract ZoneRules provideRules(String zoneId, boolean forCaching);
+
+ /**
+ * SPI method to get the history of rules for the zone ID.
+ * <p>
+ * This returns a map of historical rules keyed by a version string.
+ * The exact meaning and format of the version is provider specific.
+ * The version must follow lexicographical order, thus the returned map will
+ * be order from the oldest known rules to the newest available rules.
+ * The default 'TZDB' group uses version numbering consisting of the year
+ * followed by a letter, such as '2009e' or '2012f'.
+ * <p>
+ * Implementations must provide a result for each valid zone ID, however
+ * they do not have to provide a history of rules.
+ * Thus the map will contain at least one element, and will only contain
+ * more than one element if historical rule information is available.
+ * <p>
+ * The returned versions remain available and valid for the lifetime of the application.
+ * A dynamic provider may increase the set of versions as more data becomes available.
+ *
+ * @param zoneId the zone ID as defined by {@code ZoneId}, not null
+ * @return a modifiable copy of the history of the rules for the ID, sorted
+ * from oldest to newest, not null
+ * @throws ZoneRulesException if history cannot be obtained for the zone ID
+ */
+ protected abstract NavigableMap<String, ZoneRules> provideVersions(String zoneId);
+
+ /**
+ * SPI method to refresh the rules from the underlying data provider.
+ * <p>
+ * This method provides the opportunity for a provider to dynamically
+ * recheck the underlying data provider to find the latest rules.
+ * This could be used to load new rules without stopping the JVM.
+ * Dynamic behavior is entirely optional and most providers do not support it.
+ * <p>
+ * This implementation returns false.
+ *
+ * @return true if the rules were updated
+ * @throws ZoneRulesException if an error occurs during the refresh
+ */
+ protected boolean provideRefresh() {
+ return false;
+ }
+
+}
diff --git a/java/time/zone/package-info.java b/java/time/zone/package-info.java
new file mode 100644
index 0000000..f50b3de
--- /dev/null
+++ b/java/time/zone/package-info.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2012, 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) 2012, 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.
+ */
+
+/**
+ * <p>
+ * Support for time-zones and their rules.
+ * </p>
+ * <p>
+ * Daylight Saving Time and Time-Zones are concepts used by Governments to alter local time.
+ * This package provides support for time-zones, their rules and the resulting
+ * gaps and overlaps in the local time-line typically caused by Daylight Saving Time.
+ * </p>
+ *
+ * <h3>Package specification</h3>
+ * <p>
+ * Unless otherwise noted, passing a null argument to a constructor or method in any class or interface
+ * in this package will cause a {@link java.lang.NullPointerException NullPointerException} to be thrown.
+ * The Javadoc "@param" definition is used to summarise the null-behavior.
+ * The "@throws {@link java.lang.NullPointerException}" is not explicitly documented in each method.
+ * </p>
+ * <p>
+ * All calculations should check for numeric overflow and throw either an {@link java.lang.ArithmeticException}
+ * or a {@link java.time.DateTimeException}.
+ * </p>
+ * @since JDK1.8
+ */
+package java.time.zone;