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 &plusmn; 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 &plusmn;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 &plusmn;59, sign matches hours and seconds
+     * @param seconds  the time-zone offset in seconds, from 0 to &plusmn;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 &plusmn;59
+     * @param seconds  the time-zone offset in seconds, from 0 to &plusmn;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 &plusmn;59, sign matches hours and seconds
+     * @param seconds  the time-zone offset in seconds, from 0 to &plusmn;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&lt;Chronology&gt; 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&lt;Chronology&gt; 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&nbsp;Dec&nbsp;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&lt;Long, String&gt; map = new HashMap&lt;&gt;();
+     * 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&lt;ZoneOffset&gt; 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;