Path: blob/master/src/java.base/share/classes/java/time/chrono/ChronoLocalDateTime.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.EPOCH_DAY;64import static java.time.temporal.ChronoField.NANO_OF_DAY;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.LocalDateTime;72import java.time.LocalTime;73import java.time.ZoneId;74import java.time.ZoneOffset;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.zone.ZoneRules;87import java.util.Comparator;88import java.util.Objects;8990/**91* A date-time without a time-zone in an arbitrary chronology, intended92* for advanced globalization use cases.93* <p>94* <b>Most applications should declare method signatures, fields and variables95* as {@link LocalDateTime}, not this interface.</b>96* <p>97* A {@code ChronoLocalDateTime} is the abstract representation of a local date-time98* where the {@code Chronology chronology}, or calendar system, is pluggable.99* The date-time is defined in terms of fields expressed by {@link TemporalField},100* where most common implementations are defined in {@link ChronoField}.101* The chronology defines how the calendar system operates and the meaning of102* the standard fields.103*104* <h2>When to use this interface</h2>105* The design of the API encourages the use of {@code LocalDateTime} rather than this106* interface, even in the case where the application needs to deal with multiple107* calendar systems. The rationale for this is explored in detail in {@link ChronoLocalDate}.108* <p>109* Ensure that the discussion in {@code ChronoLocalDate} has been read and understood110* before using this interface.111*112* @implSpec113* This interface must be implemented with care to ensure other classes operate correctly.114* All implementations that can be instantiated must be final, immutable and thread-safe.115* Subclasses should be Serializable wherever possible.116*117* @param <D> the concrete type for the date of this date-time118* @since 1.8119*/120public interface ChronoLocalDateTime<D extends ChronoLocalDate>121extends Temporal, TemporalAdjuster, Comparable<ChronoLocalDateTime<?>> {122123/**124* Gets a comparator that compares {@code ChronoLocalDateTime} in125* time-line order ignoring the chronology.126* <p>127* This comparator differs from the comparison in {@link #compareTo} in that it128* only compares the underlying date-time and not the chronology.129* This allows dates in different calendar systems to be compared based130* on the position of the date-time on the local time-line.131* The underlying comparison is equivalent to comparing the epoch-day and nano-of-day.132*133* @return a comparator that compares in time-line order ignoring the chronology134* @see #isAfter135* @see #isBefore136* @see #isEqual137*/138static Comparator<ChronoLocalDateTime<?>> timeLineOrder() {139return (Comparator<ChronoLocalDateTime<? extends ChronoLocalDate>> & Serializable) (dateTime1, dateTime2) -> {140int cmp = Long.compare(dateTime1.toLocalDate().toEpochDay(), dateTime2.toLocalDate().toEpochDay());141if (cmp == 0) {142cmp = Long.compare(dateTime1.toLocalTime().toNanoOfDay(), dateTime2.toLocalTime().toNanoOfDay());143}144return cmp;145};146}147148//-----------------------------------------------------------------------149/**150* Obtains an instance of {@code ChronoLocalDateTime} from a temporal object.151* <p>152* This obtains a local date-time based on the specified temporal.153* A {@code TemporalAccessor} represents an arbitrary set of date and time information,154* which this factory converts to an instance of {@code ChronoLocalDateTime}.155* <p>156* The conversion extracts and combines the chronology and the date-time157* from the temporal object. The behavior is equivalent to using158* {@link Chronology#localDateTime(TemporalAccessor)} with the extracted chronology.159* Implementations are permitted to perform optimizations such as accessing160* those fields that are equivalent to the relevant objects.161* <p>162* This method matches the signature of the functional interface {@link TemporalQuery}163* allowing it to be used as a query via method reference, {@code ChronoLocalDateTime::from}.164*165* @param temporal the temporal object to convert, not null166* @return the date-time, not null167* @throws DateTimeException if unable to convert to a {@code ChronoLocalDateTime}168* @see Chronology#localDateTime(TemporalAccessor)169*/170static ChronoLocalDateTime<?> from(TemporalAccessor temporal) {171if (temporal instanceof ChronoLocalDateTime) {172return (ChronoLocalDateTime<?>) temporal;173}174Objects.requireNonNull(temporal, "temporal");175Chronology chrono = temporal.query(TemporalQueries.chronology());176if (chrono == null) {177throw new DateTimeException("Unable to obtain ChronoLocalDateTime from TemporalAccessor: " + temporal.getClass());178}179return chrono.localDateTime(temporal);180}181182//-----------------------------------------------------------------------183/**184* Gets the chronology of this date-time.185* <p>186* The {@code Chronology} represents the calendar system in use.187* The era and other fields in {@link ChronoField} are defined by the chronology.188*189* @return the chronology, not null190*/191default Chronology getChronology() {192return toLocalDate().getChronology();193}194195/**196* Gets the local date part of this date-time.197* <p>198* This returns a local date with the same year, month and day199* as this date-time.200*201* @return the date part of this date-time, not null202*/203D toLocalDate();204205/**206* Gets the local time part of this date-time.207* <p>208* This returns a local time with the same hour, minute, second and209* nanosecond as this date-time.210*211* @return the time part of this date-time, not null212*/213LocalTime toLocalTime();214215/**216* Checks if the specified field is supported.217* <p>218* This checks if the specified field can be queried on this date-time.219* If false, then calling the {@link #range(TemporalField) range},220* {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}221* methods will throw an exception.222* <p>223* The set of supported fields is defined by the chronology and normally includes224* all {@code ChronoField} date and time fields.225* <p>226* If the field is not a {@code ChronoField}, then the result of this method227* is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}228* passing {@code this} as the argument.229* Whether the field is supported is determined by the field.230*231* @param field the field to check, null returns false232* @return true if the field can be queried, false if not233*/234@Override235boolean isSupported(TemporalField field);236237/**238* Checks if the specified unit is supported.239* <p>240* This checks if the specified unit can be added to or subtracted from this date-time.241* If false, then calling the {@link #plus(long, TemporalUnit)} and242* {@link #minus(long, TemporalUnit) minus} methods will throw an exception.243* <p>244* The set of supported units is defined by the chronology and normally includes245* all {@code ChronoUnit} units except {@code FOREVER}.246* <p>247* If the unit is not a {@code ChronoUnit}, then the result of this method248* is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}249* passing {@code this} as the argument.250* Whether the unit is supported is determined by the unit.251*252* @param unit the unit to check, null returns false253* @return true if the unit can be added/subtracted, false if not254*/255@Override256default boolean isSupported(TemporalUnit unit) {257if (unit instanceof ChronoUnit) {258return unit != FOREVER;259}260return unit != null && unit.isSupportedBy(this);261}262263//-----------------------------------------------------------------------264// override for covariant return type265/**266* {@inheritDoc}267* @throws DateTimeException {@inheritDoc}268* @throws ArithmeticException {@inheritDoc}269*/270@Override271default ChronoLocalDateTime<D> with(TemporalAdjuster adjuster) {272return ChronoLocalDateTimeImpl.ensureValid(getChronology(), Temporal.super.with(adjuster));273}274275/**276* {@inheritDoc}277* @throws DateTimeException {@inheritDoc}278* @throws ArithmeticException {@inheritDoc}279*/280@Override281ChronoLocalDateTime<D> with(TemporalField field, long newValue);282283/**284* {@inheritDoc}285* @throws DateTimeException {@inheritDoc}286* @throws ArithmeticException {@inheritDoc}287*/288@Override289default ChronoLocalDateTime<D> plus(TemporalAmount amount) {290return ChronoLocalDateTimeImpl.ensureValid(getChronology(), Temporal.super.plus(amount));291}292293/**294* {@inheritDoc}295* @throws DateTimeException {@inheritDoc}296* @throws ArithmeticException {@inheritDoc}297*/298@Override299ChronoLocalDateTime<D> plus(long amountToAdd, TemporalUnit unit);300301/**302* {@inheritDoc}303* @throws DateTimeException {@inheritDoc}304* @throws ArithmeticException {@inheritDoc}305*/306@Override307default ChronoLocalDateTime<D> minus(TemporalAmount amount) {308return ChronoLocalDateTimeImpl.ensureValid(getChronology(), Temporal.super.minus(amount));309}310311/**312* {@inheritDoc}313* @throws DateTimeException {@inheritDoc}314* @throws ArithmeticException {@inheritDoc}315*/316@Override317default ChronoLocalDateTime<D> minus(long amountToSubtract, TemporalUnit unit) {318return ChronoLocalDateTimeImpl.ensureValid(getChronology(), Temporal.super.minus(amountToSubtract, unit));319}320321//-----------------------------------------------------------------------322/**323* Queries this date-time using the specified query.324* <p>325* This queries this date-time using the specified query strategy object.326* The {@code TemporalQuery} object defines the logic to be used to327* obtain the result. Read the documentation of the query to understand328* what the result of this method will be.329* <p>330* The result of this method is obtained by invoking the331* {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the332* specified query passing {@code this} as the argument.333*334* @param <R> the type of the result335* @param query the query to invoke, not null336* @return the query result, null may be returned (defined by the query)337* @throws DateTimeException if unable to query (defined by the query)338* @throws ArithmeticException if numeric overflow occurs (defined by the query)339*/340@SuppressWarnings("unchecked")341@Override342default <R> R query(TemporalQuery<R> query) {343if (query == TemporalQueries.zoneId() || query == TemporalQueries.zone() || query == TemporalQueries.offset()) {344return null;345} else if (query == TemporalQueries.localTime()) {346return (R) toLocalTime();347} else if (query == TemporalQueries.chronology()) {348return (R) getChronology();349} else if (query == TemporalQueries.precision()) {350return (R) NANOS;351}352// inline TemporalAccessor.super.query(query) as an optimization353// non-JDK classes are not permitted to make this optimization354return query.queryFrom(this);355}356357/**358* Adjusts the specified temporal object to have the same date and time as this object.359* <p>360* This returns a temporal object of the same observable type as the input361* with the date and time changed to be the same as this.362* <p>363* The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)}364* twice, passing {@link ChronoField#EPOCH_DAY} and365* {@link ChronoField#NANO_OF_DAY} as the fields.366* <p>367* In most cases, it is clearer to reverse the calling pattern by using368* {@link Temporal#with(TemporalAdjuster)}:369* <pre>370* // these two lines are equivalent, but the second approach is recommended371* temporal = thisLocalDateTime.adjustInto(temporal);372* temporal = temporal.with(thisLocalDateTime);373* </pre>374* <p>375* This instance is immutable and unaffected by this method call.376*377* @param temporal the target object to be adjusted, not null378* @return the adjusted object, not null379* @throws DateTimeException if unable to make the adjustment380* @throws ArithmeticException if numeric overflow occurs381*/382@Override383default Temporal adjustInto(Temporal temporal) {384return temporal385.with(EPOCH_DAY, toLocalDate().toEpochDay())386.with(NANO_OF_DAY, toLocalTime().toNanoOfDay());387}388389/**390* Formats this date-time using the specified formatter.391* <p>392* This date-time will be passed to the formatter to produce a string.393* <p>394* The default implementation must behave as follows:395* <pre>396* return formatter.format(this);397* </pre>398*399* @param formatter the formatter to use, not null400* @return the formatted date-time string, not null401* @throws DateTimeException if an error occurs during printing402*/403default String format(DateTimeFormatter formatter) {404Objects.requireNonNull(formatter, "formatter");405return formatter.format(this);406}407408//-----------------------------------------------------------------------409/**410* Combines this time with a time-zone to create a {@code ChronoZonedDateTime}.411* <p>412* This returns a {@code ChronoZonedDateTime} formed from this date-time at the413* specified time-zone. The result will match this date-time as closely as possible.414* Time-zone rules, such as daylight savings, mean that not every local date-time415* is valid for the specified zone, thus the local date-time may be adjusted.416* <p>417* The local date-time is resolved to a single instant on the time-line.418* This is achieved by finding a valid offset from UTC/Greenwich for the local419* date-time as defined by the {@link ZoneRules rules} of the zone ID.420*<p>421* In most cases, there is only one valid offset for a local date-time.422* In the case of an overlap, where clocks are set back, there are two valid offsets.423* This method uses the earlier offset typically corresponding to "summer".424* <p>425* In the case of a gap, where clocks jump forward, there is no valid offset.426* Instead, the local date-time is adjusted to be later by the length of the gap.427* For a typical one hour daylight savings change, the local date-time will be428* moved one hour later into the offset typically corresponding to "summer".429* <p>430* To obtain the later offset during an overlap, call431* {@link ChronoZonedDateTime#withLaterOffsetAtOverlap()} on the result of this method.432*433* @param zone the time-zone to use, not null434* @return the zoned date-time formed from this date-time, not null435*/436ChronoZonedDateTime<D> atZone(ZoneId zone);437438//-----------------------------------------------------------------------439/**440* Converts this date-time to an {@code Instant}.441* <p>442* This combines this local date-time and the specified offset to form443* an {@code Instant}.444* <p>445* This default implementation calculates from the epoch-day of the date and the446* second-of-day of the time.447*448* @param offset the offset to use for the conversion, not null449* @return an {@code Instant} representing the same instant, not null450*/451default Instant toInstant(ZoneOffset offset) {452return Instant.ofEpochSecond(toEpochSecond(offset), toLocalTime().getNano());453}454455/**456* Converts this date-time to the number of seconds from the epoch457* of 1970-01-01T00:00:00Z.458* <p>459* This combines this local date-time and the specified offset to calculate the460* epoch-second value, which is the number of elapsed seconds from 1970-01-01T00:00:00Z.461* Instants on the time-line after the epoch are positive, earlier are negative.462* <p>463* This default implementation calculates from the epoch-day of the date and the464* second-of-day of the time.465*466* @param offset the offset to use for the conversion, not null467* @return the number of seconds from the epoch of 1970-01-01T00:00:00Z468*/469default long toEpochSecond(ZoneOffset offset) {470Objects.requireNonNull(offset, "offset");471long epochDay = toLocalDate().toEpochDay();472long secs = epochDay * 86400 + toLocalTime().toSecondOfDay();473secs -= offset.getTotalSeconds();474return secs;475}476477//-----------------------------------------------------------------------478/**479* Compares this date-time to another date-time, including the chronology.480* <p>481* The comparison is based first on the underlying time-line date-time, then482* on the chronology.483* It is "consistent with equals", as defined by {@link Comparable}.484* <p>485* For example, the following is the comparator order:486* <ol>487* <li>{@code 2012-12-03T12:00 (ISO)}</li>488* <li>{@code 2012-12-04T12:00 (ISO)}</li>489* <li>{@code 2555-12-04T12:00 (ThaiBuddhist)}</li>490* <li>{@code 2012-12-05T12:00 (ISO)}</li>491* </ol>492* Values #2 and #3 represent the same date-time on the time-line.493* When two values represent the same date-time, the chronology ID is compared to distinguish them.494* This step is needed to make the ordering "consistent with equals".495* <p>496* If all the date-time objects being compared are in the same chronology, then the497* additional chronology stage is not required and only the local date-time is used.498* <p>499* This default implementation performs the comparison defined above.500*501* @param other the other date-time to compare to, not null502* @return the comparator value, negative if less, positive if greater503*/504@Override505default int compareTo(ChronoLocalDateTime<?> other) {506int cmp = toLocalDate().compareTo(other.toLocalDate());507if (cmp == 0) {508cmp = toLocalTime().compareTo(other.toLocalTime());509if (cmp == 0) {510cmp = getChronology().compareTo(other.getChronology());511}512}513return cmp;514}515516/**517* Checks if this date-time is after the specified date-time ignoring the chronology.518* <p>519* This method differs from the comparison in {@link #compareTo} in that it520* only compares the underlying date-time and not the chronology.521* This allows dates in different calendar systems to be compared based522* on the time-line position.523* <p>524* This default implementation performs the comparison based on the epoch-day525* and nano-of-day.526*527* @param other the other date-time to compare to, not null528* @return true if this is after the specified date-time529*/530default boolean isAfter(ChronoLocalDateTime<?> other) {531long thisEpDay = this.toLocalDate().toEpochDay();532long otherEpDay = other.toLocalDate().toEpochDay();533return thisEpDay > otherEpDay ||534(thisEpDay == otherEpDay && this.toLocalTime().toNanoOfDay() > other.toLocalTime().toNanoOfDay());535}536537/**538* Checks if this date-time is before the specified date-time ignoring the chronology.539* <p>540* This method differs from the comparison in {@link #compareTo} in that it541* only compares the underlying date-time and not the chronology.542* This allows dates in different calendar systems to be compared based543* on the time-line position.544* <p>545* This default implementation performs the comparison based on the epoch-day546* and nano-of-day.547*548* @param other the other date-time to compare to, not null549* @return true if this is before the specified date-time550*/551default boolean isBefore(ChronoLocalDateTime<?> other) {552long thisEpDay = this.toLocalDate().toEpochDay();553long otherEpDay = other.toLocalDate().toEpochDay();554return thisEpDay < otherEpDay ||555(thisEpDay == otherEpDay && this.toLocalTime().toNanoOfDay() < other.toLocalTime().toNanoOfDay());556}557558/**559* Checks if this date-time is equal to the specified date-time ignoring the chronology.560* <p>561* This method differs from the comparison in {@link #compareTo} in that it562* only compares the underlying date and time and not the chronology.563* This allows date-times in different calendar systems to be compared based564* on the time-line position.565* <p>566* This default implementation performs the comparison based on the epoch-day567* and nano-of-day.568*569* @param other the other date-time to compare to, not null570* @return true if the underlying date-time is equal to the specified date-time on the timeline571*/572default boolean isEqual(ChronoLocalDateTime<?> other) {573// Do the time check first, it is cheaper than computing EPOCH day.574return this.toLocalTime().toNanoOfDay() == other.toLocalTime().toNanoOfDay() &&575this.toLocalDate().toEpochDay() == other.toLocalDate().toEpochDay();576}577578/**579* Checks if this date-time is equal to another date-time, including the chronology.580* <p>581* Compares this date-time with another ensuring that the date-time and chronology are the same.582*583* @param obj the object to check, null returns false584* @return true if this is equal to the other date585*/586@Override587boolean equals(Object obj);588589/**590* A hash code for this date-time.591*592* @return a suitable hash code593*/594@Override595int hashCode();596597//-----------------------------------------------------------------------598/**599* Outputs this date-time as a {@code String}.600* <p>601* The output will include the full local date-time.602*603* @return a string representation of this date-time, not null604*/605@Override606String toString();607608}609610611