Path: blob/master/src/java.base/share/classes/java/time/chrono/ChronoZonedDateTime.java
41159 views
/*1* Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425/*26* This file is available under and governed by the GNU General Public27* License version 2 only, as published by the Free Software Foundation.28* However, the following notice accompanied the original version of this29* file:30*31* Copyright (c) 2007-2012, Stephen Colebourne & Michael Nascimento Santos32*33* All rights reserved.34*35* Redistribution and use in source and binary forms, with or without36* modification, are permitted provided that the following conditions are met:37*38* * Redistributions of source code must retain the above copyright notice,39* this list of conditions and the following disclaimer.40*41* * Redistributions in binary form must reproduce the above copyright notice,42* this list of conditions and the following disclaimer in the documentation43* and/or other materials provided with the distribution.44*45* * Neither the name of JSR-310 nor the names of its contributors46* may be used to endorse or promote products derived from this software47* without specific prior written permission.48*49* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS50* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT51* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR52* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR53* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,54* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,55* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR56* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF57* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING58* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS59* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.60*/61package java.time.chrono;6263import static java.time.temporal.ChronoField.INSTANT_SECONDS;64import static java.time.temporal.ChronoField.OFFSET_SECONDS;65import static java.time.temporal.ChronoUnit.FOREVER;66import static java.time.temporal.ChronoUnit.NANOS;6768import java.io.Serializable;69import java.time.DateTimeException;70import java.time.Instant;71import java.time.LocalTime;72import java.time.ZoneId;73import java.time.ZoneOffset;74import java.time.ZonedDateTime;75import java.time.format.DateTimeFormatter;76import java.time.temporal.ChronoField;77import java.time.temporal.ChronoUnit;78import java.time.temporal.Temporal;79import java.time.temporal.TemporalAccessor;80import java.time.temporal.TemporalAdjuster;81import java.time.temporal.TemporalAmount;82import java.time.temporal.TemporalField;83import java.time.temporal.TemporalQueries;84import java.time.temporal.TemporalQuery;85import java.time.temporal.TemporalUnit;86import java.time.temporal.UnsupportedTemporalTypeException;87import java.time.temporal.ValueRange;88import java.util.Comparator;89import java.util.Objects;9091/**92* A date-time with a time-zone in an arbitrary chronology,93* intended for advanced globalization use cases.94* <p>95* <b>Most applications should declare method signatures, fields and variables96* as {@link ZonedDateTime}, not this interface.</b>97* <p>98* A {@code ChronoZonedDateTime} is the abstract representation of an offset date-time99* where the {@code Chronology chronology}, or calendar system, is pluggable.100* The date-time is defined in terms of fields expressed by {@link TemporalField},101* where most common implementations are defined in {@link ChronoField}.102* The chronology defines how the calendar system operates and the meaning of103* the standard fields.104*105* <h2>When to use this interface</h2>106* The design of the API encourages the use of {@code ZonedDateTime} rather than this107* interface, even in the case where the application needs to deal with multiple108* calendar systems. The rationale for this is explored in detail in {@link ChronoLocalDate}.109* <p>110* Ensure that the discussion in {@code ChronoLocalDate} has been read and understood111* before using this interface.112*113* @implSpec114* This interface must be implemented with care to ensure other classes operate correctly.115* All implementations that can be instantiated must be final, immutable and thread-safe.116* Subclasses should be Serializable wherever possible.117*118* @param <D> the concrete type for the date of this date-time119* @since 1.8120*/121public interface ChronoZonedDateTime<D extends ChronoLocalDate>122extends Temporal, Comparable<ChronoZonedDateTime<?>> {123124/**125* Gets a comparator that compares {@code ChronoZonedDateTime} in126* time-line order ignoring the chronology.127* <p>128* This comparator differs from the comparison in {@link #compareTo} in that it129* only compares the underlying instant and not the chronology.130* This allows dates in different calendar systems to be compared based131* on the position of the date-time on the instant time-line.132* The underlying comparison is equivalent to comparing the epoch-second and nano-of-second.133*134* @return a comparator that compares in time-line order ignoring the chronology135* @see #isAfter136* @see #isBefore137* @see #isEqual138*/139static Comparator<ChronoZonedDateTime<?>> timeLineOrder() {140return (Comparator<ChronoZonedDateTime<?>> & Serializable) (dateTime1, dateTime2) -> {141int cmp = Long.compare(dateTime1.toEpochSecond(), dateTime2.toEpochSecond());142if (cmp == 0) {143cmp = Long.compare(dateTime1.toLocalTime().getNano(), dateTime2.toLocalTime().getNano());144}145return cmp;146};147}148149//-----------------------------------------------------------------------150/**151* Obtains an instance of {@code ChronoZonedDateTime} from a temporal object.152* <p>153* This creates a zoned date-time based on the specified temporal.154* A {@code TemporalAccessor} represents an arbitrary set of date and time information,155* which this factory converts to an instance of {@code ChronoZonedDateTime}.156* <p>157* The conversion extracts and combines the chronology, date, time and zone158* from the temporal object. The behavior is equivalent to using159* {@link Chronology#zonedDateTime(TemporalAccessor)} with the extracted chronology.160* Implementations are permitted to perform optimizations such as accessing161* those fields that are equivalent to the relevant objects.162* <p>163* This method matches the signature of the functional interface {@link TemporalQuery}164* allowing it to be used as a query via method reference, {@code ChronoZonedDateTime::from}.165*166* @param temporal the temporal object to convert, not null167* @return the date-time, not null168* @throws DateTimeException if unable to convert to a {@code ChronoZonedDateTime}169* @see Chronology#zonedDateTime(TemporalAccessor)170*/171static ChronoZonedDateTime<?> from(TemporalAccessor temporal) {172if (temporal instanceof ChronoZonedDateTime) {173return (ChronoZonedDateTime<?>) temporal;174}175Objects.requireNonNull(temporal, "temporal");176Chronology chrono = temporal.query(TemporalQueries.chronology());177if (chrono == null) {178throw new DateTimeException("Unable to obtain ChronoZonedDateTime from TemporalAccessor: " + temporal.getClass());179}180return chrono.zonedDateTime(temporal);181}182183//-----------------------------------------------------------------------184@Override185default ValueRange range(TemporalField field) {186if (field instanceof ChronoField) {187if (field == INSTANT_SECONDS || field == OFFSET_SECONDS) {188return field.range();189}190return toLocalDateTime().range(field);191}192return field.rangeRefinedBy(this);193}194195@Override196default int get(TemporalField field) {197if (field instanceof ChronoField chronoField) {198switch (chronoField) {199case INSTANT_SECONDS:200throw new UnsupportedTemporalTypeException("Invalid field 'InstantSeconds' for get() method, use getLong() instead");201case OFFSET_SECONDS:202return getOffset().getTotalSeconds();203}204return toLocalDateTime().get(field);205}206return Temporal.super.get(field);207}208209@Override210default long getLong(TemporalField field) {211if (field instanceof ChronoField chronoField) {212switch (chronoField) {213case INSTANT_SECONDS: return toEpochSecond();214case OFFSET_SECONDS: return getOffset().getTotalSeconds();215}216return toLocalDateTime().getLong(field);217}218return field.getFrom(this);219}220221/**222* Gets the local date part of this date-time.223* <p>224* This returns a local date with the same year, month and day225* as this date-time.226*227* @return the date part of this date-time, not null228*/229default D toLocalDate() {230return toLocalDateTime().toLocalDate();231}232233/**234* Gets the local time part of this date-time.235* <p>236* This returns a local time with the same hour, minute, second and237* nanosecond as this date-time.238*239* @return the time part of this date-time, not null240*/241default LocalTime toLocalTime() {242return toLocalDateTime().toLocalTime();243}244245/**246* Gets the local date-time part of this date-time.247* <p>248* This returns a local date with the same year, month and day249* as this date-time.250*251* @return the local date-time part of this date-time, not null252*/253ChronoLocalDateTime<D> toLocalDateTime();254255/**256* Gets the chronology of this date-time.257* <p>258* The {@code Chronology} represents the calendar system in use.259* The era and other fields in {@link ChronoField} are defined by the chronology.260*261* @return the chronology, not null262*/263default Chronology getChronology() {264return toLocalDate().getChronology();265}266267/**268* Gets the zone offset, such as '+01:00'.269* <p>270* This is the offset of the local date-time from UTC/Greenwich.271*272* @return the zone offset, not null273*/274ZoneOffset getOffset();275276/**277* Gets the zone ID, such as 'Europe/Paris'.278* <p>279* This returns the stored time-zone id used to determine the time-zone rules.280*281* @return the zone ID, not null282*/283ZoneId getZone();284285//-----------------------------------------------------------------------286/**287* Returns a copy of this date-time changing the zone offset to the288* earlier of the two valid offsets at a local time-line overlap.289* <p>290* This method only has any effect when the local time-line overlaps, such as291* at an autumn daylight savings cutover. In this scenario, there are two292* valid offsets for the local date-time. Calling this method will return293* a zoned date-time with the earlier of the two selected.294* <p>295* If this method is called when it is not an overlap, {@code this}296* is returned.297* <p>298* This instance is immutable and unaffected by this method call.299*300* @return a {@code ChronoZonedDateTime} based on this date-time with the earlier offset, not null301* @throws DateTimeException if no rules can be found for the zone302* @throws DateTimeException if no rules are valid for this date-time303*/304ChronoZonedDateTime<D> withEarlierOffsetAtOverlap();305306/**307* Returns a copy of this date-time changing the zone offset to the308* later of the two valid offsets at a local time-line overlap.309* <p>310* This method only has any effect when the local time-line overlaps, such as311* at an autumn daylight savings cutover. In this scenario, there are two312* valid offsets for the local date-time. Calling this method will return313* a zoned date-time with the later of the two selected.314* <p>315* If this method is called when it is not an overlap, {@code this}316* is returned.317* <p>318* This instance is immutable and unaffected by this method call.319*320* @return a {@code ChronoZonedDateTime} based on this date-time with the later offset, not null321* @throws DateTimeException if no rules can be found for the zone322* @throws DateTimeException if no rules are valid for this date-time323*/324ChronoZonedDateTime<D> withLaterOffsetAtOverlap();325326/**327* Returns a copy of this date-time with a different time-zone,328* retaining the local date-time if possible.329* <p>330* This method changes the time-zone and retains the local date-time.331* The local date-time is only changed if it is invalid for the new zone.332* <p>333* To change the zone and adjust the local date-time,334* use {@link #withZoneSameInstant(ZoneId)}.335* <p>336* This instance is immutable and unaffected by this method call.337*338* @param zone the time-zone to change to, not null339* @return a {@code ChronoZonedDateTime} based on this date-time with the requested zone, not null340*/341ChronoZonedDateTime<D> withZoneSameLocal(ZoneId zone);342343/**344* Returns a copy of this date-time with a different time-zone,345* retaining the instant.346* <p>347* This method changes the time-zone and retains the instant.348* This normally results in a change to the local date-time.349* <p>350* This method is based on retaining the same instant, thus gaps and overlaps351* in the local time-line have no effect on the result.352* <p>353* To change the offset while keeping the local time,354* use {@link #withZoneSameLocal(ZoneId)}.355*356* @param zone the time-zone to change to, not null357* @return a {@code ChronoZonedDateTime} based on this date-time with the requested zone, not null358* @throws DateTimeException if the result exceeds the supported date range359*/360ChronoZonedDateTime<D> withZoneSameInstant(ZoneId zone);361362/**363* Checks if the specified field is supported.364* <p>365* This checks if the specified field can be queried on this date-time.366* If false, then calling the {@link #range(TemporalField) range},367* {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}368* methods will throw an exception.369* <p>370* The set of supported fields is defined by the chronology and normally includes371* all {@code ChronoField} fields.372* <p>373* If the field is not a {@code ChronoField}, then the result of this method374* is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}375* passing {@code this} as the argument.376* Whether the field is supported is determined by the field.377*378* @param field the field to check, null returns false379* @return true if the field can be queried, false if not380*/381@Override382boolean isSupported(TemporalField field);383384/**385* Checks if the specified unit is supported.386* <p>387* This checks if the specified unit can be added to or subtracted from this date-time.388* If false, then calling the {@link #plus(long, TemporalUnit)} and389* {@link #minus(long, TemporalUnit) minus} methods will throw an exception.390* <p>391* The set of supported units is defined by the chronology and normally includes392* all {@code ChronoUnit} units except {@code FOREVER}.393* <p>394* If the unit is not a {@code ChronoUnit}, then the result of this method395* is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}396* passing {@code this} as the argument.397* Whether the unit is supported is determined by the unit.398*399* @param unit the unit to check, null returns false400* @return true if the unit can be added/subtracted, false if not401*/402@Override403default boolean isSupported(TemporalUnit unit) {404if (unit instanceof ChronoUnit) {405return unit != FOREVER;406}407return unit != null && unit.isSupportedBy(this);408}409410//-----------------------------------------------------------------------411// override for covariant return type412/**413* {@inheritDoc}414* @throws DateTimeException {@inheritDoc}415* @throws ArithmeticException {@inheritDoc}416*/417@Override418default ChronoZonedDateTime<D> with(TemporalAdjuster adjuster) {419return ChronoZonedDateTimeImpl.ensureValid(getChronology(), Temporal.super.with(adjuster));420}421422/**423* {@inheritDoc}424* @throws DateTimeException {@inheritDoc}425* @throws ArithmeticException {@inheritDoc}426*/427@Override428ChronoZonedDateTime<D> with(TemporalField field, long newValue);429430/**431* {@inheritDoc}432* @throws DateTimeException {@inheritDoc}433* @throws ArithmeticException {@inheritDoc}434*/435@Override436default ChronoZonedDateTime<D> plus(TemporalAmount amount) {437return ChronoZonedDateTimeImpl.ensureValid(getChronology(), Temporal.super.plus(amount));438}439440/**441* {@inheritDoc}442* @throws DateTimeException {@inheritDoc}443* @throws ArithmeticException {@inheritDoc}444*/445@Override446ChronoZonedDateTime<D> plus(long amountToAdd, TemporalUnit unit);447448/**449* {@inheritDoc}450* @throws DateTimeException {@inheritDoc}451* @throws ArithmeticException {@inheritDoc}452*/453@Override454default ChronoZonedDateTime<D> minus(TemporalAmount amount) {455return ChronoZonedDateTimeImpl.ensureValid(getChronology(), Temporal.super.minus(amount));456}457458/**459* {@inheritDoc}460* @throws DateTimeException {@inheritDoc}461* @throws ArithmeticException {@inheritDoc}462*/463@Override464default ChronoZonedDateTime<D> minus(long amountToSubtract, TemporalUnit unit) {465return ChronoZonedDateTimeImpl.ensureValid(getChronology(), Temporal.super.minus(amountToSubtract, unit));466}467468//-----------------------------------------------------------------------469/**470* Queries this date-time using the specified query.471* <p>472* This queries this date-time using the specified query strategy object.473* The {@code TemporalQuery} object defines the logic to be used to474* obtain the result. Read the documentation of the query to understand475* what the result of this method will be.476* <p>477* The result of this method is obtained by invoking the478* {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the479* specified query passing {@code this} as the argument.480*481* @param <R> the type of the result482* @param query the query to invoke, not null483* @return the query result, null may be returned (defined by the query)484* @throws DateTimeException if unable to query (defined by the query)485* @throws ArithmeticException if numeric overflow occurs (defined by the query)486*/487@SuppressWarnings("unchecked")488@Override489default <R> R query(TemporalQuery<R> query) {490if (query == TemporalQueries.zone() || query == TemporalQueries.zoneId()) {491return (R) getZone();492} else if (query == TemporalQueries.offset()) {493return (R) getOffset();494} else if (query == TemporalQueries.localTime()) {495return (R) toLocalTime();496} else if (query == TemporalQueries.chronology()) {497return (R) getChronology();498} else if (query == TemporalQueries.precision()) {499return (R) NANOS;500}501// inline TemporalAccessor.super.query(query) as an optimization502// non-JDK classes are not permitted to make this optimization503return query.queryFrom(this);504}505506/**507* Formats this date-time using the specified formatter.508* <p>509* This date-time will be passed to the formatter to produce a string.510* <p>511* The default implementation must behave as follows:512* <pre>513* return formatter.format(this);514* </pre>515*516* @param formatter the formatter to use, not null517* @return the formatted date-time string, not null518* @throws DateTimeException if an error occurs during printing519*/520default String format(DateTimeFormatter formatter) {521Objects.requireNonNull(formatter, "formatter");522return formatter.format(this);523}524525//-----------------------------------------------------------------------526/**527* Converts this date-time to an {@code Instant}.528* <p>529* This returns an {@code Instant} representing the same point on the530* time-line as this date-time. The calculation combines the531* {@linkplain #toLocalDateTime() local date-time} and532* {@linkplain #getOffset() offset}.533*534* @return an {@code Instant} representing the same instant, not null535*/536default Instant toInstant() {537return Instant.ofEpochSecond(toEpochSecond(), toLocalTime().getNano());538}539540/**541* Converts this date-time to the number of seconds from the epoch542* of 1970-01-01T00:00:00Z.543* <p>544* This uses the {@linkplain #toLocalDateTime() local date-time} and545* {@linkplain #getOffset() offset} to calculate the epoch-second value,546* which is the number of elapsed seconds from 1970-01-01T00:00:00Z.547* Instants on the time-line after the epoch are positive, earlier are negative.548*549* @return the number of seconds from the epoch of 1970-01-01T00:00:00Z550*/551default long toEpochSecond() {552long epochDay = toLocalDate().toEpochDay();553long secs = epochDay * 86400 + toLocalTime().toSecondOfDay();554secs -= getOffset().getTotalSeconds();555return secs;556}557558//-----------------------------------------------------------------------559/**560* Compares this date-time to another date-time, including the chronology.561* <p>562* The comparison is based first on the instant, then on the local date-time,563* then on the zone ID, then on the chronology.564* It is "consistent with equals", as defined by {@link Comparable}.565* <p>566* If all the date-time objects being compared are in the same chronology, then the567* additional chronology stage is not required.568* <p>569* This default implementation performs the comparison defined above.570*571* @param other the other date-time to compare to, not null572* @return the comparator value, negative if less, positive if greater573*/574@Override575default int compareTo(ChronoZonedDateTime<?> other) {576int cmp = Long.compare(toEpochSecond(), other.toEpochSecond());577if (cmp == 0) {578cmp = toLocalTime().getNano() - other.toLocalTime().getNano();579if (cmp == 0) {580cmp = toLocalDateTime().compareTo(other.toLocalDateTime());581if (cmp == 0) {582cmp = getZone().getId().compareTo(other.getZone().getId());583if (cmp == 0) {584cmp = getChronology().compareTo(other.getChronology());585}586}587}588}589return cmp;590}591592/**593* Checks if the instant of this date-time is before that of the specified date-time.594* <p>595* This method differs from the comparison in {@link #compareTo} in that it596* only compares the instant of the date-time. This is equivalent to using597* {@code dateTime1.toInstant().isBefore(dateTime2.toInstant());}.598* <p>599* This default implementation performs the comparison based on the epoch-second600* and nano-of-second.601*602* @param other the other date-time to compare to, not null603* @return true if this point is before the specified date-time604*/605default boolean isBefore(ChronoZonedDateTime<?> other) {606long thisEpochSec = toEpochSecond();607long otherEpochSec = other.toEpochSecond();608return thisEpochSec < otherEpochSec ||609(thisEpochSec == otherEpochSec && toLocalTime().getNano() < other.toLocalTime().getNano());610}611612/**613* Checks if the instant of this date-time is after that of the specified date-time.614* <p>615* This method differs from the comparison in {@link #compareTo} in that it616* only compares the instant of the date-time. This is equivalent to using617* {@code dateTime1.toInstant().isAfter(dateTime2.toInstant());}.618* <p>619* This default implementation performs the comparison based on the epoch-second620* and nano-of-second.621*622* @param other the other date-time to compare to, not null623* @return true if this is after the specified date-time624*/625default boolean isAfter(ChronoZonedDateTime<?> other) {626long thisEpochSec = toEpochSecond();627long otherEpochSec = other.toEpochSecond();628return thisEpochSec > otherEpochSec ||629(thisEpochSec == otherEpochSec && toLocalTime().getNano() > other.toLocalTime().getNano());630}631632/**633* Checks if the instant of this date-time is equal to that of the specified date-time.634* <p>635* This method differs from the comparison in {@link #compareTo} and {@link #equals}636* in that it only compares the instant of the date-time. This is equivalent to using637* {@code dateTime1.toInstant().equals(dateTime2.toInstant());}.638* <p>639* This default implementation performs the comparison based on the epoch-second640* and nano-of-second.641*642* @param other the other date-time to compare to, not null643* @return true if the instant equals the instant of the specified date-time644*/645default boolean isEqual(ChronoZonedDateTime<?> other) {646return toEpochSecond() == other.toEpochSecond() &&647toLocalTime().getNano() == other.toLocalTime().getNano();648}649650//-----------------------------------------------------------------------651/**652* Checks if this date-time is equal to another date-time.653* <p>654* The comparison is based on the offset date-time and the zone.655* To compare for the same instant on the time-line, use {@link #compareTo}.656* Only objects of type {@code ChronoZonedDateTime} are compared, other types return false.657*658* @param obj the object to check, null returns false659* @return true if this is equal to the other date-time660*/661@Override662boolean equals(Object obj);663664/**665* A hash code for this date-time.666*667* @return a suitable hash code668*/669@Override670int hashCode();671672//-----------------------------------------------------------------------673/**674* Outputs this date-time as a {@code String}.675* <p>676* The output will include the full zoned date-time.677*678* @return a string representation of this date-time, not null679*/680@Override681String toString();682683}684685686