pax_global_header 0000666 0000000 0000000 00000000064 13434511741 0014515 g ustar 00root root 0000000 0000000 52 comment=b92b352ceed45194b7d2cdce0844da6c27d81ce5
threeten-extra-1.5.0/ 0000775 0000000 0000000 00000000000 13434511741 0014457 5 ustar 00root root 0000000 0000000 threeten-extra-1.5.0/.gitattributes 0000664 0000000 0000000 00000000431 13434511741 0017350 0 ustar 00root root 0000000 0000000 # Manage line endings, info from GitHub
# Set default behaviour, in case users don't have core.autocrlf set.
* text=auto
# Denote all files that are truly binary and should not be modified.
*.png binary
*.jpg binary
*.jar binary
*.dat binary
*.zip binary
*.tar binary
*.gz binary
threeten-extra-1.5.0/.github/ 0000775 0000000 0000000 00000000000 13434511741 0016017 5 ustar 00root root 0000000 0000000 threeten-extra-1.5.0/.github/maven-settings.xml 0000664 0000000 0000000 00000000640 13434511741 0021505 0 ustar 00root root 0000000 0000000
* Not every piece of date/time logic is destined for the JDK. Some concepts are too * specialized or too bulky to make it in. This project provides some of those additional * classes as a well-tested and reliable module. */ module org.threeten.extra { // only annotations are used, thus they are optional requires static org.joda.convert; // export all packages exports org.threeten.extra; exports org.threeten.extra.chrono; exports org.threeten.extra.scale; // provide the services provides java.time.chrono.Chronology with org.threeten.extra.chrono.BritishCutoverChronology, org.threeten.extra.chrono.CopticChronology, org.threeten.extra.chrono.DiscordianChronology, org.threeten.extra.chrono.EthiopicChronology, org.threeten.extra.chrono.InternationalFixedChronology, org.threeten.extra.chrono.JulianChronology, org.threeten.extra.chrono.PaxChronology, org.threeten.extra.chrono.Symmetry010Chronology, org.threeten.extra.chrono.Symmetry454Chronology; } threeten-extra-1.5.0/src/main/java/org/ 0000775 0000000 0000000 00000000000 13434511741 0017702 5 ustar 00root root 0000000 0000000 threeten-extra-1.5.0/src/main/java/org/threeten/ 0000775 0000000 0000000 00000000000 13434511741 0021520 5 ustar 00root root 0000000 0000000 threeten-extra-1.5.0/src/main/java/org/threeten/extra/ 0000775 0000000 0000000 00000000000 13434511741 0022643 5 ustar 00root root 0000000 0000000 threeten-extra-1.5.0/src/main/java/org/threeten/extra/AmPm.java 0000664 0000000 0000000 00000037243 13434511741 0024351 0 ustar 00root root 0000000 0000000 /* * Copyright (c) 2007-present, 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 org.threeten.extra; import static java.time.temporal.ChronoField.AMPM_OF_DAY; import static java.time.temporal.ChronoField.HOUR_OF_DAY; import static java.time.temporal.ChronoUnit.HALF_DAYS; import java.time.DateTimeException; 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.Calendar; import java.util.Locale; /** * A half-day before or after midday, with the values 'AM' and 'PM'. *
* {@code AmPm} is an enum representing the half-day concepts of AM and PM. * AM is defined as from 00:00 to 11:59, while PM is defined from 12:00 to 23:59. *
* All date-time fields have an {@code int} value. * The {@code int} value follows {@link Calendar}, assigning 0 to AM and 1 to PM. * It is recommended that applications use the enum rather than the {@code int} value * to ensure code clarity. *
* Do not use {@code ordinal()} to obtain the numeric representation of {@code AmPm}. * Use {@code getValue()} instead. *
* 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 AM/PM * concept defined exactly equivalent to the ISO calendar system. * *
* {@code AmPm} is an enum representing before and after midday. * This factory allows the enum to be obtained from the {@code int} value. * The {@code int} value follows {@link Calendar}, assigning 0 to AM and 1 to PM. * * @param amPmValue the AM/PM value to represent, from 0 (AM) to 1 (PM) * @return the AM/PM, not null * @throws DateTimeException if the am-pm is invalid */ public static AmPm of(int amPmValue) { switch (amPmValue) { case 0: return AM; case 1: return PM; default: throw new DateTimeException("Invalid value for AM/PM: " + amPmValue); } } /** * Obtains an instance of {@code AmPm} from an hour-of-day. *
* {@code AmPm} is an enum representing before and after midday. * This factory allows the enum to be obtained from the hour-of-day value, from 0 to 23. * * @param hourOfDay the hour-of-day to extract from, from 0 to 23 * @return the AM/PM, not null * @throws DateTimeException if the hour-of-day is invalid */ public static AmPm ofHour(int hourOfDay) { HOUR_OF_DAY.checkValidValue(hourOfDay); return hourOfDay < 12 ? AM : PM; } //----------------------------------------------------------------------- /** * Obtains an instance of {@code AmPm} from a temporal object. *
* This obtains an am-pm 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 AmPm}. *
* The conversion extracts the {@link ChronoField#AMPM_OF_DAY AMPM_OF_DAY} field. *
* This method matches the signature of the functional interface {@link TemporalQuery} * allowing it to be used as a query via method reference, {@code AmPm::from}. * * @param temporal the temporal object to convert, not null * @return the AM/PM, not null * @throws DateTimeException if unable to convert to a {@code AmPm} */ public static AmPm from(TemporalAccessor temporal) { if (temporal instanceof AmPm) { return (AmPm) temporal; } try { return of(temporal.get(AMPM_OF_DAY)); } catch (DateTimeException ex) { throw new DateTimeException("Unable to obtain AmPm from TemporalAccessor: " + temporal + " of type " + temporal.getClass().getName(), ex); } } //----------------------------------------------------------------------- /** * Gets the AM/PM {@code int} value. *
* The values are numbered following {@link Calendar}, assigning 0 to AM and 1 to PM. * * @return the AM/PM value, from 0 (AM) to 1 (PM) */ public int getValue() { return ordinal(); } //----------------------------------------------------------------------- /** * Gets the textual representation, such as 'AM' or 'PM'. *
* This returns the textual name used to identify the am-pm, * suitable for presentation to the user. * The parameters control the style of the returned text and the locale. *
* 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 am-pm, not null */ public String getDisplayName(TextStyle style, Locale locale) { return new DateTimeFormatterBuilder().appendText(AMPM_OF_DAY, style).toFormatter(locale).format(this); } //----------------------------------------------------------------------- /** * Checks if the specified field is supported. *
* This checks if this am-pm 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. *
* If the field is {@link ChronoField#AMPM_OF_DAY AMPM_OF_DAY} then * this method returns true. * All other {@code ChronoField} instances will return false. *
* 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 am-pm, false if not */ @Override public boolean isSupported(TemporalField field) { if (field instanceof ChronoField) { return field == AMPM_OF_DAY; } return field != null && field.isSupportedBy(this); } /** * Gets the range of valid values for the specified field. *
* The range object expresses the minimum and maximum valid values for a field. * This am-pm 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. *
* If the field is {@link ChronoField#AMPM_OF_DAY AMPM_OF_DAY} then the * range of the am-pm, from 0 to 1, will be returned. * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}. *
* 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 == AMPM_OF_DAY) { return field.range(); } else if (field instanceof ChronoField) { throw new UnsupportedTemporalTypeException("Unsupported field: " + field); } return field.rangeRefinedBy(this); } /** * Gets the value of the specified field from this am-pm as an {@code int}. *
* This queries this am-pm for the value for 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. *
* If the field is {@link ChronoField#AMPM_OF_DAY AMPM_OF_DAY} then the * value of the am-pm, from 0 (AM) to 1 (PM), will be returned. * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}. *
* 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 == AMPM_OF_DAY) { return getValue(); } return range(field).checkValidIntValue(getLong(field), field); } /** * Gets the value of the specified field from this am-pm as a {@code long}. *
* This queries this am-pm for the value for 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. *
* If the field is {@link ChronoField#AMPM_OF_DAY AMPM_OF_DAY} then the * value of the am-pm, from 0 (AM) to 1 (PM), will be returned. * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}. *
* 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 == AMPM_OF_DAY) { return getValue(); } else if (field instanceof ChronoField) { throw new UnsupportedTemporalTypeException("Unsupported field: " + field); } return field.getFrom(this); } //----------------------------------------------------------------------- /** * Queries this am-pm using the specified query. *
* This queries this am-pm 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. *
* 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
* This returns a temporal object of the same observable type as the input
* with the am-pm changed to be the same as this.
*
* The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)}
* passing {@link ChronoField#AMPM_OF_DAY} as the field.
* Note that this adjusts forwards or backwards within a day.
*
* In most cases, it is clearer to reverse the calling pattern by using
* {@link Temporal#with(TemporalAdjuster)}:
*
* 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(AMPM_OF_DAY, getValue());
}
}
threeten-extra-1.5.0/src/main/java/org/threeten/extra/AmountFormats.java 0000664 0000000 0000000 00000036047 13434511741 0026317 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2007-present, 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 org.threeten.extra;
import java.time.Duration;
import java.time.Period;
import java.time.temporal.TemporalAmount;
import java.util.Locale;
import java.util.Objects;
import java.util.ResourceBundle;
import java.util.function.IntPredicate;
import java.util.regex.Pattern;
import java.util.stream.Stream;
/**
* Provides the ability to format a temporal amount.
*
* This allows a {@link TemporalAmount}, such as {@link Duration} or {@link Period},
* to be formatted. Only selected formatting options are provided.
*
*
* To obtain the ISO-8601 format of a {@code Period} or {@code Duration}
* individually, simply call {@code toString()}.
* See also {@link PeriodDuration}.
*
* @param period the period to format
* @param duration the duration to format
* @return the ISO-8601 format for the period and duration
*/
public static String iso8601(Period period, Duration duration) {
Objects.requireNonNull(period, "period must not be null");
Objects.requireNonNull(duration, "duration must not be null");
if (period.isZero()) {
return duration.toString();
}
if (duration.isZero()) {
return period.toString();
}
return period.toString() + duration.toString().substring(1);
}
//-------------------------------------------------------------------------
/**
* Formats a period to a string in a localized word-based format.
*
* This returns a word-based format for the period.
* The words are configured in a resource bundle text file -
* {@code org.threeten.extra.wordbased.properties} - with overrides per language.
*
* @param period the period to format
* @param locale the locale to use
* @return the localized word-based format for the period
*/
public static String wordBased(Period period, Locale locale) {
Objects.requireNonNull(period, "period must not be null");
Objects.requireNonNull(locale, "locale must not be null");
ResourceBundle bundle = ResourceBundle.getBundle(BUNDLE_NAME, locale);
UnitFormat[] formats = {
UnitFormat.of(bundle, WORDBASED_YEAR),
UnitFormat.of(bundle, WORDBASED_MONTH),
UnitFormat.of(bundle, WORDBASED_WEEK),
UnitFormat.of(bundle, WORDBASED_DAY)};
WordBased wb = new WordBased(formats, bundle.getString(WORDBASED_COMMASPACE), bundle.getString(WORDBASED_SPACEANDSPACE));
Period normPeriod = period.normalized();
int weeks = 0, days = 0;
if (normPeriod.getDays() % DAYS_PER_WEEK == 0) {
weeks = normPeriod.getDays() / DAYS_PER_WEEK;
} else {
days = normPeriod.getDays();
}
int[] values = {normPeriod.getYears(), normPeriod.getMonths(), weeks, days};
return wb.format(values);
}
/**
* Formats a duration to a string in a localized word-based format.
*
* This returns a word-based format for the duration.
* The words are configured in a resource bundle text file -
* {@code org.threeten.extra.wordbased.properties} - with overrides per language.
*
* @param duration the duration to format
* @param locale the locale to use
* @return the localized word-based format for the duration
*/
public static String wordBased(Duration duration, Locale locale) {
Objects.requireNonNull(duration, "duration must not be null");
Objects.requireNonNull(locale, "locale must not be null");
ResourceBundle bundle = ResourceBundle.getBundle(BUNDLE_NAME, locale);
UnitFormat[] formats = {
UnitFormat.of(bundle, WORDBASED_HOUR),
UnitFormat.of(bundle, WORDBASED_MINUTE),
UnitFormat.of(bundle, WORDBASED_SECOND),
UnitFormat.of(bundle, WORDBASED_MILLISECOND)};
WordBased wb = new WordBased(formats, bundle.getString(WORDBASED_COMMASPACE), bundle.getString(WORDBASED_SPACEANDSPACE));
long hours = duration.toHours();
long mins = duration.toMinutes() % MINUTES_PER_HOUR;
long secs = duration.getSeconds() % SECONDS_PER_MINUTE;
int millis = duration.getNano() / NANOS_PER_MILLIS;
int[] values = {(int) hours, (int) mins, (int) secs, millis};
return wb.format(values);
}
/**
* Formats a period and duration to a string in a localized word-based format.
*
* This returns a word-based format for the period.
* The words are configured in a resource bundle text file -
* {@code org.threeten.extra.wordbased.properties} - with overrides per language.
*
* @param period the period to format
* @param duration the duration to format
* @param locale the locale to use
* @return the localized word-based format for the period and duration
*/
public static String wordBased(Period period, Duration duration, Locale locale) {
Objects.requireNonNull(period, "period must not be null");
Objects.requireNonNull(duration, "duration must not be null");
Objects.requireNonNull(locale, "locale must not be null");
ResourceBundle bundle = ResourceBundle.getBundle(BUNDLE_NAME, locale);
UnitFormat[] formats = {
UnitFormat.of(bundle, WORDBASED_YEAR),
UnitFormat.of(bundle, WORDBASED_MONTH),
UnitFormat.of(bundle, WORDBASED_WEEK),
UnitFormat.of(bundle, WORDBASED_DAY),
UnitFormat.of(bundle, WORDBASED_HOUR),
UnitFormat.of(bundle, WORDBASED_MINUTE),
UnitFormat.of(bundle, WORDBASED_SECOND),
UnitFormat.of(bundle, WORDBASED_MILLISECOND)};
WordBased wb = new WordBased(formats, bundle.getString(WORDBASED_COMMASPACE), bundle.getString(WORDBASED_SPACEANDSPACE));
Period normPeriod = period.normalized();
int weeks = 0, days = 0;
if (normPeriod.getDays() % DAYS_PER_WEEK == 0) {
weeks = normPeriod.getDays() / DAYS_PER_WEEK;
} else {
days = normPeriod.getDays();
}
long totalHours = duration.toHours();
days += (int) (totalHours / HOURS_PER_DAY);
int hours = (int) (totalHours % HOURS_PER_DAY);
int mins = (int) (duration.toMinutes() % MINUTES_PER_HOUR);
int secs = (int) (duration.getSeconds() % SECONDS_PER_MINUTE);
int millis = duration.getNano() / NANOS_PER_MILLIS;
int[] values = {
normPeriod.getYears(), normPeriod.getMonths(), weeks, days,
(int) hours, mins, secs, millis};
return wb.format(values);
}
private AmountFormats() {
}
//-------------------------------------------------------------------------
// data holder for word-based formats
static final class WordBased {
private final UnitFormat[] units;
private final String separator;
private final String lastSeparator;
public WordBased(UnitFormat[] units, String separator, String lastSeparator) {
this.units = units;
this.separator = separator;
this.lastSeparator = lastSeparator;
}
String format(int[] values) {
StringBuilder buf = new StringBuilder(32);
int nonZeroCount = 0;
for (int i = 0; i < values.length; i++) {
if (values[i] != 0) {
nonZeroCount++;
}
}
int count = 0;
for (int i = 0; i < values.length; i++) {
if (values[i] != 0 || (count == 0 && i == values.length - 1)) {
units[i].formatTo(values[i], buf);
if (count < nonZeroCount - 2) {
buf.append(separator);
} else if (count == nonZeroCount - 2) {
buf.append(lastSeparator);
}
count++;
}
}
return buf.toString();
}
}
// data holder for single/plural formats
static interface UnitFormat {
static UnitFormat of(ResourceBundle bundle, String keyStem) {
if (bundle.containsKey(keyStem + "s.predicates")) {
String predicateList = bundle.getString(keyStem + "s.predicates");
String textList = bundle.getString(keyStem + "s.list");
String[] regexes = SPLITTER.split(predicateList);
String[] text = SPLITTER.split(textList);
return new PredicateFormat(regexes, text);
} else {
String single = bundle.getString(keyStem);
String plural = bundle.getString(keyStem + "s");
return new SinglePluralFormat(single, plural);
}
}
void formatTo(int value, StringBuilder buf);
}
// data holder for single/plural formats
static final class SinglePluralFormat implements UnitFormat {
private final String single;
private final String plural;
SinglePluralFormat(String single, String plural) {
this.single = single;
this.plural = plural;
}
@Override
public void formatTo(int value, StringBuilder buf) {
buf.append(value).append(value == 1 || value == -1 ? single : plural);
}
}
// data holder for predicate formats
static final class PredicateFormat implements UnitFormat {
private final IntPredicate[] predicates;
private final String[] text;
PredicateFormat(String[] predicateStrs, String[] text) {
if (predicateStrs.length + 1 != text.length) {
throw new IllegalStateException("Invalid word-based resource");
}
this.predicates = Stream.of(predicateStrs)
.map(predicateStr -> findPredicate(predicateStr))
.toArray(IntPredicate[]::new);
this.text = text;
}
private IntPredicate findPredicate(String predicateStr) {
switch (predicateStr) {
case "One": return PREDICATE_1;
case "End234NotTeens": return PREDICATE_END234_NOTTEENS;
default: throw new IllegalStateException("Invalid word-based resource");
}
}
@Override
public void formatTo(int value, StringBuilder buf) {
for (int i = 0; i < predicates.length; i++) {
if (predicates[i].test(value)) {
buf.append(value).append(text[i]);
return;
}
}
buf.append(value).append(text[predicates.length]);
return;
}
}
}
threeten-extra-1.5.0/src/main/java/org/threeten/extra/DayOfMonth.java 0000664 0000000 0000000 00000052664 13434511741 0025533 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2007-present, 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 org.threeten.extra;
import static java.time.temporal.ChronoField.DAY_OF_MONTH;
import java.io.Serializable;
import java.time.Clock;
import java.time.DateTimeException;
import java.time.LocalDate;
import java.time.Month;
import java.time.MonthDay;
import java.time.YearMonth;
import java.time.ZoneId;
import java.time.chrono.Chronology;
import java.time.chrono.IsoChronology;
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;
/**
* A day-of-month in the ISO-8601 calendar system.
*
* {@code DayOfMonth} is an immutable date-time object that represents a day-of-month.
* It is a type-safe way of representing a day-of-month in an application.
* Any field that can be derived from a day-of-month can be obtained.
*
* This class does not store or represent a year, month, time or time-zone.
* For example, the value "21" can be stored in a {@code DayOfMonth} and
* would represent the 21st day of any month.
*
*
* This class must be treated as a value type. Do not synchronize, rely on the
* identity hash code or use the distinction between equals() and ==.
*/
public final class DayOfMonth
implements TemporalAccessor, TemporalAdjuster, Comparable
* This will query the {@link java.time.Clock#systemDefaultZone() system clock} in the default
* time-zone to obtain the current day-of-month.
* The zone and offset will be set based on the time-zone in the clock.
*
* Using this method will prevent the ability to use an alternate clock for testing
* because the clock is hard-coded.
*
* @return the current day-of-month using the system clock and default time-zone, not null
*/
public static DayOfMonth now() {
return now(Clock.systemDefaultZone());
}
/**
* Obtains the current day-of-month from the system clock in the specified time-zone.
*
* This will query the {@link Clock#system(java.time.ZoneId) system clock} to obtain the current day-of-month.
* Specifying the time-zone avoids dependence on the default time-zone.
*
* 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 day-of-month using the system clock, not null
*/
public static DayOfMonth now(ZoneId zone) {
return now(Clock.system(zone));
}
/**
* Obtains the current day-of-month from the specified clock.
*
* This will query the specified clock to obtain the current day-of-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 day-of-month, not null
*/
public static DayOfMonth now(Clock clock) {
final LocalDate now = LocalDate.now(clock); // called once
return DayOfMonth.of(now.getDayOfMonth());
}
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code DayOfMonth}.
*
* A day-of-month object represents one of the 31 days of the month, from 1 to 31.
*
* @param dayOfMonth the day-of-month to represent, from 1 to 31
* @return the day-of-month, not null
* @throws DateTimeException if the day-of-month is invalid
*/
public static DayOfMonth of(int dayOfMonth) {
try {
return VALUES[dayOfMonth - 1];
} catch (IndexOutOfBoundsException ex) {
throw new DateTimeException("Invalid value for DayOfMonth: " + dayOfMonth);
}
}
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code DayOfMonth} from a date-time object.
*
* This obtains a day-of-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 DayOfMonth}.
*
* The conversion extracts the {@link ChronoField#DAY_OF_MONTH day-of-month} field.
* The extraction is only permitted if the temporal object has an ISO
* chronology, or can be converted to a {@code LocalDate}.
*
* This method matches the signature of the functional interface {@link TemporalQuery}
* allowing it to be used in queries via method reference, {@code DayOfMonth::from}.
*
* @param temporal the temporal object to convert, not null
* @return the day-of-month, not null
* @throws DateTimeException if unable to convert to a {@code DayOfMonth}
*/
public static DayOfMonth from(TemporalAccessor temporal) {
if (temporal instanceof DayOfMonth) {
return (DayOfMonth) temporal;
}
Objects.requireNonNull(temporal, "temporal");
try {
if (IsoChronology.INSTANCE.equals(Chronology.from(temporal)) == false) {
temporal = LocalDate.from(temporal);
}
return of(temporal.get(DAY_OF_MONTH));
} catch (DateTimeException ex) {
throw new DateTimeException("Unable to obtain DayOfMonth from TemporalAccessor: " +
temporal + " of type " + temporal.getClass().getName(), ex);
}
}
//-----------------------------------------------------------------------
/**
* Constructor.
*
* @param dayOfMonth the day-of-month to represent
*/
private DayOfMonth(int dayOfMonth) {
this.day = dayOfMonth;
}
/**
* Validates the input.
*
* @return the valid object, not null
*/
private Object readResolve() {
return of(day);
}
//-----------------------------------------------------------------------
/**
* Gets the day-of-month value.
*
* @return the day-of-month, from 1 to 31
*/
public int getValue() {
return day;
}
//-----------------------------------------------------------------------
/**
* Checks if the specified field is supported.
*
* This checks if this day-of-month can be queried for the specified field.
* If false, then calling the {@link #range(TemporalField) range},
* {@link #get(TemporalField) get} and {@link #getLong(TemporalField) getLong}
* methods will throw an exception.
*
* If the field is a {@link ChronoField} then the query is implemented here.
* The supported fields are:
*
* 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-month, false if not
*/
@Override
public boolean isSupported(TemporalField field) {
if (field instanceof ChronoField) {
return field == DAY_OF_MONTH;
}
return field != null && field.isSupportedBy(this);
}
//-----------------------------------------------------------------------
/**
* Gets the range of valid values for the specified field.
*
* The range object expresses the minimum and maximum valid values for a field.
* This day-of-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.
*
* 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}.
*
* 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) {
return TemporalAccessor.super.range(field);
}
/**
* Gets the value of the specified field from this day-of-month as an {@code int}.
*
* This queries this day-of-month for the value for 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.
*
* 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 day-of-month.
* All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
*
* 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) {
return TemporalAccessor.super.get(field);
}
/**
* Gets the value of the specified field from this day-of-month as a {@code long}.
*
* This queries this day-of-month for the value for 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.
*
* 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 day-of-month.
* All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
*
* 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_MONTH) {
return day;
} else if (field instanceof ChronoField) {
throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
return field.getFrom(this);
}
//-----------------------------------------------------------------------
/**
* Checks if the year-month is valid for this year.
*
* This method checks whether this day and the input year and month form
* a valid date.
*
* @param yearMonth the year month to validate, null returns false
* @return true if the year and month are valid for this day
*/
public boolean isValidYearMonth(YearMonth yearMonth) {
return yearMonth != null && yearMonth.isValidDay(day);
}
//-----------------------------------------------------------------------
/**
* Queries this day-of-month using the specified query.
*
* This queries this day-of-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.
*
* 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
* This returns a temporal object of the same observable type as the input
* with the day-of-month changed to be the same as this.
*
* The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)}
* passing {@link ChronoField#DAY_OF_MONTH} as the field.
* If the specified temporal object does not use the ISO calendar system then
* a {@code DateTimeException} is thrown.
*
* In most cases, it is clearer to reverse the calling pattern by using
* {@link Temporal#with(TemporalAdjuster)}:
*
* 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(DAY_OF_MONTH, day);
}
//-----------------------------------------------------------------------
/**
* Combines this day-of-month with a month to create a {@code MonthDay}.
*
* This returns a {@code MonthDay} formed from this day and the specified month.
*
* This method can be used as part of a chain to produce a date:
*
* If this day-of-month is invalid for the month then it will be changed
* to the last valid date for the month.
*
* @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 MonthDay atMonth(Month month) {
return MonthDay.of(month, Math.min(day, month.maxLength()));
}
/**
* Combines this day-of-month with a month to create a {@code MonthDay}.
*
* This returns a {@code MonthDay} formed from this day and the specified month.
*
* This method can be used as part of a chain to produce a date:
*
* If this day-of-month is invalid for the month then it will be changed
* to the last valid date for the month.
*
* @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 MonthDay atMonth(int month) {
return atMonth(Month.of(month));
}
/**
* Combines this day-of-month with a year-month to create a {@code LocalDate}.
*
* This returns a {@code LocalDate} formed from this year and the specified year-month.
*
* If this day-of-month is invalid for the year-month then it will be changed
* to the last valid date for the month.
*
* @param yearMonth the year-month to use, not null
* @return the local date formed from this year and the specified year-month, not null
*/
public LocalDate atYearMonth(YearMonth yearMonth) {
return yearMonth.atDay(Math.min(day, yearMonth.lengthOfMonth()));
}
//-----------------------------------------------------------------------
/**
* Compares this day-of-month to another.
*
* The comparison is based on the value of the day.
* It is "consistent with equals", as defined by {@link Comparable}.
*
* @param other the other day-of-month instance, not null
* @return the comparator value, negative if less, positive if greater
*/
@Override
public int compareTo(DayOfMonth other) {
return day - other.day;
}
//-----------------------------------------------------------------------
/**
* Checks if this day-of-month is equal to another day-of-month.
*
* @param obj the other day-of-month instance, null returns false
* @return true if the day-of-month is the same
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof DayOfMonth) {
return day == ((DayOfMonth) obj).day;
}
return false;
}
/**
* A hash code for this day-of-month.
*
* @return a suitable hash code
*/
@Override
public int hashCode() {
return day;
}
//-----------------------------------------------------------------------
/**
* Outputs this day-of-month as a {@code String}.
*
* @return a string representation of this day-of-month, not null
*/
@Override
public String toString() {
return "DayOfMonth:" + day;
}
}
threeten-extra-1.5.0/src/main/java/org/threeten/extra/DayOfYear.java 0000664 0000000 0000000 00000051221 13434511741 0025332 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2007-present, 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 org.threeten.extra;
import static java.time.temporal.ChronoField.DAY_OF_YEAR;
import java.io.Serializable;
import java.time.Clock;
import java.time.DateTimeException;
import java.time.LocalDate;
import java.time.Year;
import java.time.ZoneId;
import java.time.chrono.Chronology;
import java.time.chrono.IsoChronology;
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;
/**
* A day-of-year in the ISO-8601 calendar system.
*
* {@code DayOfYear} is an immutable date-time object that represents a day-of-year.
* It is a type-safe way of representing a day-of-year in an application.
* Any field that can be derived from a day-of-year can be obtained.
*
* This class does not store or represent a year, month, time or time-zone.
* For example, the value "51" can be stored in a {@code DayOfYear} and
* would represent the 51st day of any year.
*
*
* This class must be treated as a value type. Do not synchronize, rely on the
* identity hash code or use the distinction between equals() and ==.
*/
public final class DayOfYear
implements TemporalAccessor, TemporalAdjuster, Comparable
* This will query the {@link java.time.Clock#systemDefaultZone() system clock} in the default
* time-zone to obtain the current day-of-year.
* The zone and offset will be set based on the time-zone in the clock.
*
* Using this method will prevent the ability to use an alternate clock for testing
* because the clock is hard-coded.
*
* @return the current day-of-year using the system clock and default time-zone, not null
*/
public static DayOfYear now() {
return now(Clock.systemDefaultZone());
}
/**
* Obtains the current day-of-year from the system clock in the specified time-zone.
*
* This will query the {@link Clock#system(java.time.ZoneId) system clock} to obtain the current day-of-year.
* Specifying the time-zone avoids dependence on the default time-zone.
*
* 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 day-of-year using the system clock, not null
*/
public static DayOfYear now(ZoneId zone) {
return now(Clock.system(zone));
}
/**
* Obtains the current day-of-year from the specified clock.
*
* This will query the specified clock to obtain the current day-of-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 day-of-year, not null
*/
public static DayOfYear now(Clock clock) {
final LocalDate now = LocalDate.now(clock); // called once
return DayOfYear.of(now.getDayOfYear());
}
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code DayOfYear}.
*
* A day-of-year object represents one of the 366 days of the year, from 1 to 366.
*
* @param dayOfYear the day-of-year to represent, from 1 to 366
* @return the day-of-year, not null
* @throws DateTimeException if the day-of-year is invalid
*/
public static DayOfYear of(int dayOfYear) {
try {
return VALUES[dayOfYear - 1];
} catch (IndexOutOfBoundsException ex) {
throw new DateTimeException("Invalid value for DayOfYear: " + dayOfYear);
}
}
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code DayOfYear} from a date-time object.
*
* This obtains a day-of-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 DayOfYear}.
*
* The conversion extracts the {@link ChronoField#DAY_OF_YEAR day-of-year} field.
* The extraction is only permitted if the temporal object has an ISO
* chronology, or can be converted to a {@code LocalDate}.
*
* This method matches the signature of the functional interface {@link TemporalQuery}
* allowing it to be used in queries via method reference, {@code DayOfYear::from}.
*
* @param temporal the temporal object to convert, not null
* @return the day-of-year, not null
* @throws DateTimeException if unable to convert to a {@code DayOfYear}
*/
public static DayOfYear from(TemporalAccessor temporal) {
if (temporal instanceof DayOfYear) {
return (DayOfYear) temporal;
}
Objects.requireNonNull(temporal, "temporal");
try {
if (IsoChronology.INSTANCE.equals(Chronology.from(temporal)) == false) {
temporal = LocalDate.from(temporal);
}
return of(temporal.get(DAY_OF_YEAR));
} catch (DateTimeException ex) {
throw new DateTimeException("Unable to obtain DayOfYear from TemporalAccessor: " +
temporal + " of type " + temporal.getClass().getName(), ex);
}
}
//-----------------------------------------------------------------------
/**
* Constructor.
*
* @param dayOfYear the day-of-year to represent
*/
private DayOfYear(int dayOfYear) {
this.day = dayOfYear;
}
/**
* Validates the input.
*
* @return the valid object, not null
*/
private Object readResolve() {
return of(day);
}
//-----------------------------------------------------------------------
/**
* Gets the day-of-year value.
*
* @return the day-of-year, from 1 to 366
*/
public int getValue() {
return day;
}
//-----------------------------------------------------------------------
/**
* Checks if the specified field is supported.
*
* This checks if this day-of-year can be queried for the specified field.
* If false, then calling the {@link #range(TemporalField) range},
* {@link #get(TemporalField) get} and {@link #getLong(TemporalField) getLong}
* methods will throw an exception.
*
* If the field is a {@link ChronoField} then the query is implemented here.
* The supported fields are:
*
* 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-year, false if not
*/
@Override
public boolean isSupported(TemporalField field) {
if (field instanceof ChronoField) {
return field == DAY_OF_YEAR;
}
return field != null && field.isSupportedBy(this);
}
//-----------------------------------------------------------------------
/**
* Gets the range of valid values for the specified field.
*
* The range object expresses the minimum and maximum valid values for a field.
* This day-of-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.
*
* 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}.
*
* 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) {
return TemporalAccessor.super.range(field);
}
/**
* Gets the value of the specified field from this day-of-year as an {@code int}.
*
* This queries this day-of-year for the value for 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.
*
* 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 day-of-year.
* All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
*
* 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) {
return TemporalAccessor.super.get(field);
}
/**
* Gets the value of the specified field from this day-of-year as a {@code long}.
*
* This queries this day-of-year for the value for 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.
*
* 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 day-of-year.
* All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
*
* 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_YEAR) {
return day;
} else if (field instanceof ChronoField) {
throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
return field.getFrom(this);
}
//-----------------------------------------------------------------------
/**
* Checks if the year is valid for this day-of-year.
*
* This method checks whether this day-of-yearand the input year form
* a valid date. This can only return false for day-of-year 366.
*
* @param year the year to validate
* @return true if the year is valid for this day-of-year
*/
public boolean isValidYear(int year) {
return (day < 366 || Year.isLeap(year));
}
//-----------------------------------------------------------------------
/**
* Queries this day-of-year using the specified query.
*
* This queries this day-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.
*
* 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
* This returns a temporal object of the same observable type as the input
* with the day-of-year changed to be the same as this.
*
* The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)}
* passing {@link ChronoField#DAY_OF_YEAR} as the field.
* If the specified temporal object does not use the ISO calendar system then
* a {@code DateTimeException} is thrown.
*
* In most cases, it is clearer to reverse the calling pattern by using
* {@link Temporal#with(TemporalAdjuster)}:
*
* 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(DAY_OF_YEAR, day);
}
//-----------------------------------------------------------------------
/**
* Combines this day-of-year with a year to create a {@code LocalDate}.
*
* This returns a {@code LocalDate} formed from this day and the specified year.
*
* This method can be used as part of a chain to produce a date:
*
* The day-of-year value 366 is only valid in a leap year.
*
* @param year the year to use, not null
* @return the local date formed from this day and the specified year, not null
* @throws DateTimeException if the year is invalid or this is day 366 and the year is not a leap year
*/
public LocalDate atYear(Year year) {
Objects.requireNonNull(year, "year");
return year.atDay(day);
}
/**
* Combines this day-of-year with a year to create a {@code LocalDate}.
*
* This returns a {@code LocalDate} formed from this day and the specified year.
*
* This method can be used as part of a chain to produce a date:
*
* The day-of-year value 366 is only valid in a leap year.
*
* @param year the year to use, from MIN_YEAR to MAX_YEAR
* @return the local date formed from this day and the specified year, not null
* @throws DateTimeException if the year is invalid or this is day 366 and the year is not a leap year
*/
public LocalDate atYear(int year) {
return LocalDate.ofYearDay(year, day);
}
//-----------------------------------------------------------------------
/**
* Compares this day-of-year to another.
*
* The comparison is based on the value of the day.
* It is "consistent with equals", as defined by {@link Comparable}.
*
* @param other the other day-of-year instance, not null
* @return the comparator value, negative if less, positive if greater
*/
@Override
public int compareTo(DayOfYear other) {
return day - other.day;
}
//-----------------------------------------------------------------------
/**
* Checks if this day-of-year is equal to another day-of-year.
*
* @param obj the other day-of-year instance, null returns false
* @return true if the day-of-year is the same
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof DayOfYear) {
return day == ((DayOfYear) obj).day;
}
return false;
}
/**
* A hash code for this day-of-year.
*
* @return a suitable hash code
*/
@Override
public int hashCode() {
return day;
}
//-----------------------------------------------------------------------
/**
* Outputs this day-of-year as a {@code String}.
*
* @return a string representation of this day-of-year, not null
*/
@Override
public String toString() {
return "DayOfYear:" + day;
}
}
threeten-extra-1.5.0/src/main/java/org/threeten/extra/Days.java 0000664 0000000 0000000 00000052122 13434511741 0024410 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2007-present, 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 org.threeten.extra;
import static java.time.temporal.ChronoUnit.DAYS;
import java.io.Serializable;
import java.time.DateTimeException;
import java.time.Period;
import java.time.format.DateTimeParseException;
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.Collections;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.joda.convert.FromString;
import org.joda.convert.ToString;
/**
* A day-based amount of time, such as '12 days'.
*
* This class models a quantity or amount of time in terms of days.
* It is a type-safe way of representing a number of days in an application.
*
* The model is of a directed amount, meaning that the amount may be negative.
*
*
* This class must be treated as a value type. Do not synchronize, rely on the
* identity hash code or use the distinction between equals() and ==.
*/
public final class Days
implements TemporalAmount, Comparable
* The resulting amount will have the specified days.
*
* @param days the number of days, positive or negative
* @return the number of days, not null
*/
public static Days of(int days) {
if (days == 0) {
return ZERO;
} else if (days == 1) {
return ONE;
}
return new Days(days);
}
/**
* Obtains a {@code Days} representing the number of days
* equivalent to a number of weeks.
*
* The resulting amount will be day-based, with the number of days
* equal to the number of weeks multiplied by 7.
*
* @param weeks the number of weeks, positive or negative
* @return the amount with the input weeks converted to days, not null
* @throws ArithmeticException if numeric overflow occurs
*/
public static Days ofWeeks(int weeks) {
if (weeks == 0) {
return ZERO;
}
return new Days(Math.multiplyExact(weeks, DAYS_PER_WEEK));
}
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code Days} from a temporal amount.
*
* This obtains an instance 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 Days}.
*
* The result is calculated by looping around each unit in the specified amount.
* Each amount is converted to days using {@link Temporals#convertAmount}.
* If the conversion yields a remainder, an exception is thrown.
* If the amount is zero, the unit is ignored.
*
* @param amount the temporal amount to convert, not null
* @return the equivalent amount, not null
* @throws DateTimeException if unable to convert to a {@code Days}
* @throws ArithmeticException if numeric overflow occurs
*/
public static Days from(TemporalAmount amount) {
if (amount instanceof Days) {
return (Days) amount;
}
Objects.requireNonNull(amount, "amount");
int days = 0;
for (TemporalUnit unit : amount.getUnits()) {
long value = amount.get(unit);
if (value != 0) {
long[] converted = Temporals.convertAmount(value, unit, DAYS);
if (converted[1] != 0) {
throw new DateTimeException(
"Amount could not be converted to a whole number of days: " + value + " " + unit);
}
days = Math.addExact(days, Math.toIntExact(converted[0]));
}
}
return of(days);
}
//-----------------------------------------------------------------------
/**
* Obtains a {@code Days} from a text string such as {@code PnD}.
*
* This will parse the string produced by {@code toString()} which is
* based on the ISO-8601 period formats {@code PnD} and {@code PnW}.
*
* The string starts with an optional sign, denoted by the ASCII negative
* or positive symbol. If negative, the whole amount is negated.
* The ASCII letter "P" is next in upper or lower case.
* There are then two sections, each consisting of a number and a suffix.
* At least one of the two sections must be present.
* The sections have suffixes in ASCII of "W" and "D" for 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}.
*
* The leading plus/minus sign, and negative values for weeks and days are
* not part of the ISO-8601 standard.
*
* For example, the following are valid inputs:
*
* The start date is included, but the end date is not.
* The result of this method can be negative if the end is before the start.
*
* @param startDateInclusive the start date, inclusive, not null
* @param endDateExclusive the end date, exclusive, not null
* @return the number of days between this date and the end date, not null
*/
public static Days between(Temporal startDateInclusive, Temporal endDateExclusive) {
return of(Math.toIntExact(DAYS.between(startDateInclusive, endDateExclusive)));
}
//-----------------------------------------------------------------------
/**
* Constructs an instance using a specific number of days.
*
* @param days the days to use
*/
private Days(int days) {
super();
this.days = days;
}
/**
* Resolves singletons.
*
* @return the singleton instance
*/
private Object readResolve() {
return Days.of(days);
}
//-----------------------------------------------------------------------
/**
* Gets the value of the requested unit.
*
* This returns a value for the supported unit - {@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 UnsupportedTemporalTypeException if the unit is not supported
*/
@Override
public long get(TemporalUnit unit) {
if (unit == DAYS) {
return days;
}
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
}
/**
* Gets the set of units supported by this amount.
*
* The single supported unit is {@link ChronoUnit#DAYS DAYS}.
*
* This set can be used in conjunction with {@link #get(TemporalUnit)}
* to access the entire state of the amount.
*
* @return a list containing the days unit, not null
*/
@Override
public List
* The parameter is converted using {@link Days#from(TemporalAmount)}.
*
* This instance is immutable and unaffected by this method call.
*
* @param amountToAdd the amount to add, not null
* @return a {@code Days} based on this instance with the requested amount added, not null
* @throws DateTimeException if the specified amount contains an invalid unit
* @throws ArithmeticException if numeric overflow occurs
*/
public Days plus(TemporalAmount amountToAdd) {
return plus(Days.from(amountToAdd).getAmount());
}
/**
* Returns a copy of this amount with the specified number of days added.
*
* This instance is immutable and unaffected by this method call.
*
* @param days the amount of days to add, may be negative
* @return a {@code Days} based on this instance with the requested amount added, not null
* @throws ArithmeticException if the result overflows an int
*/
public Days plus(int days) {
if (days == 0) {
return this;
}
return of(Math.addExact(this.days, days));
}
//-----------------------------------------------------------------------
/**
* Returns a copy of this amount with the specified amount subtracted.
*
* The parameter is converted using {@link Days#from(TemporalAmount)}.
*
* This instance is immutable and unaffected by this method call.
*
* @param amountToAdd the amount to add, not null
* @return a {@code Days} based on this instance with the requested amount subtracted, not null
* @throws DateTimeException if the specified amount contains an invalid unit
* @throws ArithmeticException if numeric overflow occurs
*/
public Days minus(TemporalAmount amountToAdd) {
return minus(Days.from(amountToAdd).getAmount());
}
/**
* Returns a copy of this amount with the specified number of days subtracted.
*
* This instance is immutable and unaffected by this method call.
*
* @param days the amount of days to add, may be negative
* @return a {@code Days} based on this instance with the requested amount subtracted, not null
* @throws ArithmeticException if the result overflows an int
*/
public Days minus(int days) {
if (days == 0) {
return this;
}
return of(Math.subtractExact(this.days, days));
}
//-----------------------------------------------------------------------
/**
* Returns an instance with the amount multiplied by the specified scalar.
*
* This instance is immutable and unaffected by this method call.
*
* @param scalar the scalar to multiply by, not null
* @return the amount multiplied by the specified scalar, not null
* @throws ArithmeticException if numeric overflow occurs
*/
public Days multipliedBy(int scalar) {
if (scalar == 1) {
return this;
}
return of(Math.multiplyExact(days, scalar));
}
/**
* Returns an instance with the amount divided by the specified divisor.
*
* The calculation uses integer division, thus 3 divided by 2 is 1.
*
* This instance is immutable and unaffected by this method call.
*
* @param divisor the amount to divide by, may be negative
* @return the amount divided by the specified divisor, not null
* @throws ArithmeticException if the divisor is zero
*/
public Days dividedBy(int divisor) {
if (divisor == 1) {
return this;
}
return of(days / divisor);
}
/**
* Returns an instance with the amount negated.
*
* This instance is immutable and unaffected by this method call.
*
* @return the negated amount, not null
* @throws ArithmeticException if numeric overflow occurs, which only happens if
* the amount is {@code Long.MIN_VALUE}
*/
public Days negated() {
return multipliedBy(-1);
}
/**
* Returns a copy of this duration with a positive length.
*
* This method returns a positive duration by effectively removing the sign from any negative total length.
*
* This instance is immutable and unaffected by this method call.
*
* @return the absolute amount, not null
* @throws ArithmeticException if numeric overflow occurs, which only happens if
* the amount is {@code Long.MIN_VALUE}
*/
public Days abs() {
return days < 0 ? negated() : this;
}
//-------------------------------------------------------------------------
/**
* Gets the number of days as a {@code Period}.
*
* This returns a period with the same number of days.
*
* @return the equivalent period, not null
*/
public Period toPeriod() {
return Period.ofDays(days);
}
//-----------------------------------------------------------------------
/**
* Adds this amount to the specified temporal object.
*
* This returns a temporal object of the same observable type as the input
* with this amount added.
*
* In most cases, it is clearer to reverse the calling pattern by using
* {@link Temporal#plus(TemporalAmount)}.
*
* Only non-zero amounts will be added.
*
* 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 UnsupportedTemporalTypeException if the DAYS unit is not supported
* @throws ArithmeticException if numeric overflow occurs
*/
@Override
public Temporal addTo(Temporal temporal) {
if (days != 0) {
temporal = temporal.plus(days, DAYS);
}
return temporal;
}
/**
* Subtracts this amount from the specified temporal object.
*
* This returns a temporal object of the same observable type as the input
* with this amount subtracted.
*
* In most cases, it is clearer to reverse the calling pattern by using
* {@link Temporal#minus(TemporalAmount)}.
*
* Only non-zero amounts will be subtracted.
*
* 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 UnsupportedTemporalTypeException if the DAYS unit is not supported
* @throws ArithmeticException if numeric overflow occurs
*/
@Override
public Temporal subtractFrom(Temporal temporal) {
if (days != 0) {
temporal = temporal.minus(days, DAYS);
}
return temporal;
}
//-----------------------------------------------------------------------
/**
* Compares this amount to the specified {@code Days}.
*
* The comparison is based on the total length of the amounts.
* It is "consistent with equals", as defined by {@link Comparable}.
*
* @param otherAmount the other amount, not null
* @return the comparator value, negative if less, positive if greater
*/
@Override
public int compareTo(Days otherAmount) {
int thisValue = this.days;
int otherValue = otherAmount.days;
return Integer.compare(thisValue, otherValue);
}
//-----------------------------------------------------------------------
/**
* Checks if this amount is equal to the specified {@code Days}.
*
* The comparison is based on the total length of the durations.
*
* @param otherAmount the other amount, null returns false
* @return true if the other amount is equal to this one
*/
@Override
public boolean equals(Object otherAmount) {
if (this == otherAmount) {
return true;
}
if (otherAmount instanceof Days) {
Days other = (Days) otherAmount;
return this.days == other.days;
}
return false;
}
/**
* A hash code for this amount.
*
* @return a suitable hash code
*/
@Override
public int hashCode() {
return days;
}
//-----------------------------------------------------------------------
/**
* Returns a string representation of the number of days.
* This will be in the format 'PnD' where n is the number of days.
*
* @return the number of days in ISO-8601 string format
*/
@Override
@ToString
public String toString() {
return "P" + days + "D";
}
}
threeten-extra-1.5.0/src/main/java/org/threeten/extra/Hours.java 0000664 0000000 0000000 00000052033 13434511741 0024611 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2007-present, 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 org.threeten.extra;
import static java.time.temporal.ChronoUnit.HOURS;
import java.io.Serializable;
import java.time.DateTimeException;
import java.time.Duration;
import java.time.format.DateTimeParseException;
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.Collections;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.joda.convert.FromString;
import org.joda.convert.ToString;
/**
* A hour-based amount of time, such as '4 hours'.
*
* This class models a quantity or amount of time in terms of hours.
* It is a type-safe way of representing a number of hours in an application.
*
* The model is of a directed amount, meaning that the amount may be negative.
*
*
* This class must be treated as a value type. Do not synchronize, rely on the
* identity hash code or use the distinction between equals() and ==.
*/
public final class Hours
implements TemporalAmount, Comparable
* The resulting amount will have the specified hours.
*
* @param hours the number of hours, positive or negative
* @return the number of hours, not null
*/
public static Hours of(int hours) {
if (hours == 0) {
return ZERO;
} else {
return new Hours(hours);
}
}
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code Hours} from a temporal amount.
*
* This obtains an instance 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 Hours}.
*
* The result is calculated by looping around each unit in the specified amount.
* Each amount is converted to hours using {@link Temporals#convertAmount}.
* If the conversion yields a remainder, an exception is thrown.
* If the amount is zero, the unit is ignored.
*
* @param amount the temporal amount to convert, not null
* @return the equivalent amount, not null
* @throws DateTimeException if unable to convert to a {@code Hours}
* @throws ArithmeticException if numeric overflow occurs
*/
public static Hours from(TemporalAmount amount) {
if (amount instanceof Hours) {
return (Hours) amount;
}
Objects.requireNonNull(amount, "amount");
int hours = 0;
for (TemporalUnit unit : amount.getUnits()) {
long value = amount.get(unit);
if (value != 0) {
long[] converted = Temporals.convertAmount(value, unit, HOURS);
if (converted[1] != 0) {
throw new DateTimeException(
"Amount could not be converted to a whole number of hours: " + value + " " + unit);
}
hours = Math.addExact(hours, Math.toIntExact(converted[0]));
}
}
return of(hours);
}
//-----------------------------------------------------------------------
/**
* Obtains a {@code Hours} from a text string such as {@code PTnH}.
*
* This will parse the string produced by {@code toString()} and other
* related formats based on ISO-8601 {@code PnDTnH}.
*
* The string starts with an optional sign, denoted by the ASCII negative
* or positive symbol. If negative, the whole amount is negated.
* The ASCII letter "P" is next in upper or lower case.
* There are two sections consisting of a number and a suffix.
* There is one section for days suffixed by "D",
* followed by one section for hours suffixed by "H".
* At least one section must be present.
* If the hours section is present it must be prefixed by "T".
* If the hours section is omitted the "T" must be omitted.
* Letters must be in ASCII upper or lower case.
* 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}.
*
* The leading plus/minus sign, and negative values for days and hours
* are not part of the ISO-8601 standard.
*
* For example, the following are valid inputs:
*
* The start temporal is included, but the end temporal is not.
* The result of this method can be negative if the end is before the start.
*
* @param startInclusive the start temporal, inclusive, not null
* @param endExclusive the end temporal, exclusive, not null
* @return the number of hours between the start and end temporals, not null
*/
public static Hours between(Temporal startInclusive, Temporal endExclusive) {
return of(Math.toIntExact(HOURS.between(startInclusive, endExclusive)));
}
//-----------------------------------------------------------------------
/**
* Constructs an instance using a specific number of hours.
*
* @param hours the amount of hours
*/
private Hours(int hours) {
this.hours = hours;
}
/**
* Resolves singletons.
*
* @return the singleton instance
*/
private Object readResolve() {
return Hours.of(hours);
}
//-----------------------------------------------------------------------
/**
* Gets the value of the requested unit.
*
* This returns a value for the supported unit - {@link ChronoUnit#HOURS HOURS}.
* 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 UnsupportedTemporalTypeException if the unit is not supported
*/
@Override
public long get(TemporalUnit unit) {
if (unit == HOURS) {
return hours;
}
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
}
/**
* Gets the set of units supported by this amount.
*
* The single supported unit is {@link ChronoUnit#HOURS HOURS}.
*
* This set can be used in conjunction with {@link #get(TemporalUnit)} to
* access the entire state of the amount.
*
* @return a list containing the hours unit, not null
*/
@Override
public List
* The parameter is converted using {@link Hours#from(TemporalAmount)}.
*
* This instance is immutable and unaffected by this method call.
*
* @param amountToAdd the amount to add, not null
* @return a {@code Hours} based on this instance with the requested amount added, not null
* @throws DateTimeException if the specified amount contains an invalid unit
* @throws ArithmeticException if numeric overflow occurs
*/
public Hours plus(TemporalAmount amountToAdd) {
return plus(Hours.from(amountToAdd).getAmount());
}
/**
* Returns a copy of this amount with the specified number of hours added.
*
* This instance is immutable and unaffected by this method call.
*
* @param hours the amount of hours to add, may be negative
* @return a {@code Hours} based on this instance with the requested amount added, not null
* @throws ArithmeticException if the result overflows an int
*/
public Hours plus(int hours) {
if (hours == 0) {
return this;
}
return of(Math.addExact(this.hours, hours));
}
//-----------------------------------------------------------------------
/**
* Returns a copy of this amount with the specified amount subtracted.
*
* The parameter is converted using {@link Hours#from(TemporalAmount)}.
*
* This instance is immutable and unaffected by this method call.
*
* @param amountToAdd the amount to add, not null
* @return a {@code Hours} based on this instance with the requested amount subtracted, not null
* @throws DateTimeException if the specified amount contains an invalid unit
* @throws ArithmeticException if numeric overflow occurs
*/
public Hours minus(TemporalAmount amountToAdd) {
return minus(Hours.from(amountToAdd).getAmount());
}
/**
* Returns a copy of this amount with the specified number of hours subtracted.
*
* This instance is immutable and unaffected by this method call.
*
* @param hours the amount of hours to add, may be negative
* @return a {@code Hours} based on this instance with the requested amount subtracted, not null
* @throws ArithmeticException if the result overflows an int
*/
public Hours minus(int hours) {
if (hours == 0) {
return this;
}
return of(Math.subtractExact(this.hours, hours));
}
//-----------------------------------------------------------------------
/**
* Returns an instance with the amount multiplied by the specified scalar.
*
* This instance is immutable and unaffected by this method call.
*
* @param scalar the scalar to multiply by, not null
* @return the amount multiplied by the specified scalar, not null
* @throws ArithmeticException if numeric overflow occurs
*/
public Hours multipliedBy(int scalar) {
if (scalar == 1) {
return this;
}
return of(Math.multiplyExact(hours, scalar));
}
/**
* Returns an instance with the amount divided by the specified divisor.
*
* The calculation uses integer division, thus 3 divided by 2 is 1.
*
* This instance is immutable and unaffected by this method call.
*
* @param divisor the amount to divide by, may be negative
* @return the amount divided by the specified divisor, not null
* @throws ArithmeticException if the divisor is zero
*/
public Hours dividedBy(int divisor) {
if (divisor == 1) {
return this;
}
return of(hours / divisor);
}
/**
* Returns an instance with the amount negated.
*
* This instance is immutable and unaffected by this method call.
*
* @return the negated amount, not null
* @throws ArithmeticException if numeric overflow occurs, which only happens if
* the amount is {@code Long.MIN_VALUE}
*/
public Hours negated() {
return multipliedBy(-1);
}
/**
* Returns a copy of this duration with a positive length.
*
* This method returns a positive duration by effectively removing the sign from any negative total length.
*
* This instance is immutable and unaffected by this method call.
*
* @return the absolute amount, not null
* @throws ArithmeticException if numeric overflow occurs, which only happens if
* the amount is {@code Long.MIN_VALUE}
*/
public Hours abs() {
return hours < 0 ? negated() : this;
}
//-------------------------------------------------------------------------
/**
* Gets the number of hours as a {@code Duration}.
*
* This returns a duration with the same number of hours.
*
* @return the equivalent duration, not null
* @deprecated Use {@link #toDuration()}
*/
@Deprecated
public Duration toPeriod() {
return Duration.ofHours(hours);
}
//-------------------------------------------------------------------------
/**
* Gets the number of hours as a {@code Duration}.
*
* This returns a duration with the same number of hours.
*
* @return the equivalent duration, not null
*/
public Duration toDuration() {
return Duration.ofHours(hours);
}
//-----------------------------------------------------------------------
/**
* Adds this amount to the specified temporal object.
*
* This returns a temporal object of the same observable type as the input
* with this amount added.
*
* In most cases, it is clearer to reverse the calling pattern by using
* {@link Temporal#plus(TemporalAmount)}.
*
* Only non-zero amounts will be added.
*
* 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 UnsupportedTemporalTypeException if the HOURS unit is not supported
* @throws ArithmeticException if numeric overflow occurs
*/
@Override
public Temporal addTo(Temporal temporal) {
if (hours != 0) {
temporal = temporal.plus(hours, HOURS);
}
return temporal;
}
/**
* Subtracts this amount from the specified temporal object.
*
* This returns a temporal object of the same observable type as the input
* with this amount subtracted.
*
* In most cases, it is clearer to reverse the calling pattern by using
* {@link Temporal#minus(TemporalAmount)}.
*
* Only non-zero amounts will be subtracted.
*
* 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 UnsupportedTemporalTypeException if the HOURS unit is not supported
* @throws ArithmeticException if numeric overflow occurs
*/
@Override
public Temporal subtractFrom(Temporal temporal) {
if (hours != 0) {
temporal = temporal.minus(hours, HOURS);
}
return temporal;
}
//-----------------------------------------------------------------------
/**
* Compares this amount to the specified {@code Hours}.
*
* The comparison is based on the total length of the amounts.
* It is "consistent with equals", as defined by {@link Comparable}.
*
* @param otherAmount the other amount, not null
* @return the comparator value, negative if less, positive if greater
*/
@Override
public int compareTo(Hours otherAmount) {
int thisValue = this.hours;
int otherValue = otherAmount.hours;
return Integer.compare(thisValue, otherValue);
}
//-----------------------------------------------------------------------
/**
* Checks if this amount is equal to the specified {@code Hours}.
*
* The comparison is based on the total length of the durations.
*
* @param otherAmount the other amount, null returns false
* @return true if the other amount is equal to this one
*/
@Override
public boolean equals(Object otherAmount) {
if (this == otherAmount) {
return true;
}
if (otherAmount instanceof Hours) {
Hours other = (Hours) otherAmount;
return this.hours == other.hours;
}
return false;
}
/**
* A hash code for this amount.
*
* @return a suitable hash code
*/
@Override
public int hashCode() {
return hours;
}
//-----------------------------------------------------------------------
/**
* Returns a string representation of the number of hours.
* This will be in the format 'PTnH' where n is the number of hours.
*
* @return the number of hours in ISO-8601 string format
*/
@Override
@ToString
public String toString() {
return "PT" + hours + "H";
}
}
threeten-extra-1.5.0/src/main/java/org/threeten/extra/Interval.java 0000664 0000000 0000000 00000060554 13434511741 0025304 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2007-present, 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 org.threeten.extra;
import java.io.Serializable;
import java.time.DateTimeException;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.temporal.TemporalAccessor;
import java.util.Objects;
import org.joda.convert.FromString;
import org.joda.convert.ToString;
/**
* An immutable interval of time between two instants.
*
* An interval represents the time on the time-line between two {@link Instant}s.
* The class stores the start and end instants, with the start inclusive and the end exclusive.
* The end instant is always greater than or equal to the start instant.
*
* The {@link Duration} of an interval can be obtained, but is a separate concept.
* An interval is connected to the time-line, whereas a duration is not.
*
* Intervals are not comparable. To compare the length of two intervals, it is
* generally recommended to compare their durations.
*
*
* This class must be treated as a value type. Do not synchronize, rely on the
* identity hash code or use the distinction between equals() and ==.
*/
public final class Interval
implements Serializable {
/**
* An interval over the whole time-line.
*/
public static final Interval ALL = new Interval(Instant.MIN, Instant.MAX);
/**
* Serialization version.
*/
private static final long serialVersionUID = 8375285238652L;
/**
* The start instant (inclusive).
*/
private final Instant start;
/**
* The end instant (exclusive).
*/
private final Instant end;
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code Interval} from the start and end instant.
*
* The end instant must not be before the start instant.
*
* @param startInclusive the start instant, inclusive, MIN_DATE treated as unbounded, not null
* @param endExclusive the end instant, exclusive, MAX_DATE treated as unbounded, not null
* @return the half-open interval, not null
* @throws DateTimeException if the end is before the start
*/
public static Interval of(Instant startInclusive, Instant endExclusive) {
Objects.requireNonNull(startInclusive, "startInclusive");
Objects.requireNonNull(endExclusive, "endExclusive");
if (endExclusive.isBefore(startInclusive)) {
throw new DateTimeException("End instant must be equal or after start instant");
}
return new Interval(startInclusive, endExclusive);
}
/**
* Obtains an instance of {@code Interval} from the start and a duration.
*
* The end instant is calculated as the start plus the duration.
* The duration must not be negative.
*
* @param startInclusive the start instant, inclusive, not null
* @param duration the duration from the start to the end, not null
* @return the interval, not null
* @throws DateTimeException if the end is before the start,
* or if the duration addition cannot be made
* @throws ArithmeticException if numeric overflow occurs when adding the duration
*/
public static Interval of(Instant startInclusive, Duration duration) {
Objects.requireNonNull(startInclusive, "startInclusive");
Objects.requireNonNull(duration, "duration");
if (duration.isNegative()) {
throw new DateTimeException("Duration must not be negative");
}
return new Interval(startInclusive, startInclusive.plus(duration));
}
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code Interval} from a text string such as
* {@code 2007-12-03T10:15:30Z/2007-12-04T10:15:30Z}, where the end instant is exclusive.
*
* The string must consist of one of the following four formats:
*
* ISO-8601 supports a very wide range of possible inputs, many of which are not supported here.
* For example, basic format, week-based dates, ordinal dates and date-style period formats are not supported.
*
* @param text the text to parse, not null
* @return the parsed interval, not null
* @throws DateTimeParseException if the text cannot be parsed
*/
@FromString
public static Interval parse(CharSequence text) {
Objects.requireNonNull(text, "text");
for (int i = 0; i < text.length(); i++) {
if (text.charAt(i) == '/') {
return parseSplit(text.subSequence(0, i), text.subSequence(i + 1, text.length()));
}
}
throw new DateTimeParseException("Interval cannot be parsed, no forward slash found", text, 0);
}
private static Interval parseSplit(CharSequence startStr, CharSequence endStr) {
char firstChar = startStr.charAt(0);
if (firstChar == 'P' || firstChar == 'p') {
// duration followed by instant
PeriodDuration amount = PeriodDuration.parse(startStr);
try {
OffsetDateTime end = OffsetDateTime.parse(endStr);
return Interval.of(end.minus(amount).toInstant(), end.toInstant());
} catch (DateTimeParseException ex) {
// handle case where Instant is outside the bounds of OffsetDateTime
Instant end = Instant.parse(endStr);
// addition of PeriodDuration only supported by OffsetDateTime,
// but to make that work need to move point being subtracted from closer to EPOCH
long move = end.isBefore(Instant.EPOCH) ? 1000 * 86400 : -1000 * 86400;
Instant start = end.plusSeconds(move).atOffset(ZoneOffset.UTC).minus(amount).toInstant().minusSeconds(move);
return Interval.of(start, end);
}
}
// instant followed by instant or duration
OffsetDateTime start;
try {
start = OffsetDateTime.parse(startStr);
} catch (DateTimeParseException ex) {
return parseStartExtended(startStr, endStr);
}
if (endStr.length() > 0) {
char c = endStr.charAt(0);
if (c == 'P' || c == 'p') {
PeriodDuration amount = PeriodDuration.parse(endStr);
return Interval.of(start.toInstant(), start.plus(amount).toInstant());
}
}
return parseEndDateTime(start.toInstant(), start.getOffset(), endStr);
}
// handle case where Instant is outside the bounds of OffsetDateTime
private static Interval parseStartExtended(CharSequence startStr, CharSequence endStr) {
Instant start = Instant.parse(startStr);
if (endStr.length() > 0) {
char c = endStr.charAt(0);
if (c == 'P' || c == 'p') {
PeriodDuration amount = PeriodDuration.parse(endStr);
// addition of PeriodDuration only supported by OffsetDateTime,
// but to make that work need to move point being added to closer to EPOCH
long move = start.isBefore(Instant.EPOCH) ? 1000 * 86400 : -1000 * 86400;
Instant end = start.plusSeconds(move).atOffset(ZoneOffset.UTC).plus(amount).toInstant().minusSeconds(move);
return Interval.of(start, end);
}
}
// infer offset from start if not specified by end
return parseEndDateTime(start, ZoneOffset.UTC, endStr);
}
// parse when there are two date-times
private static Interval parseEndDateTime(Instant start, ZoneOffset offset, CharSequence endStr) {
try {
TemporalAccessor temporal = DateTimeFormatter.ISO_DATE_TIME.parseBest(endStr, OffsetDateTime::from, LocalDateTime::from);
if (temporal instanceof OffsetDateTime) {
OffsetDateTime odt = (OffsetDateTime) temporal;
return Interval.of(start, odt.toInstant());
} else {
// infer offset from start if not specified by end
LocalDateTime ldt = (LocalDateTime) temporal;
return Interval.of(start, ldt.toInstant(offset));
}
} catch (DateTimeParseException ex) {
Instant end = Instant.parse(endStr);
return Interval.of(start, end);
}
}
//-----------------------------------------------------------------------
/**
* Constructor.
*
* @param startInclusive the start instant, inclusive, validated not null
* @param endExclusive the end instant, exclusive, validated not null
*/
private Interval(Instant startInclusive, Instant endExclusive) {
this.start = startInclusive;
this.end = endExclusive;
}
//-----------------------------------------------------------------------
/**
* Gets the start of this time interval, inclusive.
*
* This will return {@link Instant#MIN} if the range is unbounded at the start.
* In this case, the range includes all dates into the far-past.
*
* @return the start of the time interval
*/
public Instant getStart() {
return start;
}
/**
* Gets the end of this time interval, exclusive.
*
* This will return {@link Instant#MAX} if the range is unbounded at the end.
* In this case, the range includes all dates into the far-future.
*
* @return the end of the time interval, exclusive
*/
public Instant getEnd() {
return end;
}
//-----------------------------------------------------------------------
/**
* Checks if the range is empty.
*
* An empty range occurs when the start date equals the inclusive end date.
*
* @return true if the range is empty
*/
public boolean isEmpty() {
return start.equals(end);
}
/**
* Checks if the start of the interval is unbounded.
*
* @return true if start is unbounded
*/
public boolean isUnboundedStart() {
return start.equals(Instant.MIN);
}
/**
* Checks if the end of the interval is unbounded.
*
* @return true if end is unbounded
*/
public boolean isUnboundedEnd() {
return end.equals(Instant.MAX);
}
//-----------------------------------------------------------------------
/**
* Returns a copy of this range with the specified start instant.
*
* @param start the start instant for the new interval, not null
* @return an interval with the end from this interval and the specified start
* @throws DateTimeException if the resulting interval has end before start
*/
public Interval withStart(Instant start) {
return Interval.of(start, end);
}
/**
* Returns a copy of this range with the specified end instant.
*
* @param end the end instant for the new interval, not null
* @return an interval with the start from this interval and the specified end
* @throws DateTimeException if the resulting interval has end before start
*/
public Interval withEnd(Instant end) {
return Interval.of(start, end);
}
//-----------------------------------------------------------------------
/**
* Checks if this interval contains the specified instant.
*
* This checks if the specified instant is within the bounds of this interval.
* If this range has an unbounded start then {@code contains(Instant#MIN)} returns true.
* If this range has an unbounded end then {@code contains(Instant#MAX)} returns true.
* If this range is empty then this method always returns false.
*
* @param instant the instant, not null
* @return true if this interval contains the instant
*/
public boolean contains(Instant instant) {
Objects.requireNonNull(instant, "instant");
return start.compareTo(instant) <= 0 && (instant.compareTo(end) < 0 || isUnboundedEnd());
}
/**
* Checks if this interval encloses the specified interval.
*
* This checks if the bounds of the specified interval are within the bounds of this interval.
* An empty interval encloses itself.
*
* @param other the other interval, not null
* @return true if this interval contains the other interval
*/
public boolean encloses(Interval other) {
Objects.requireNonNull(other, "other");
return start.compareTo(other.start) <= 0 && other.end.compareTo(end) <= 0;
}
/**
* Checks if this interval abuts the specified interval.
*
* The result is true if the end of this interval is the start of the other, or vice versa.
* An empty interval does not abut itself.
*
* @param other the other interval, not null
* @return true if this interval abuts the other interval
*/
public boolean abuts(Interval other) {
Objects.requireNonNull(other, "other");
return end.equals(other.start) ^ start.equals(other.end);
}
/**
* Checks if this interval is connected to the specified interval.
*
* The result is true if the two intervals have an enclosed interval in common, even if that interval is empty.
* An empty interval is connected to itself.
*
* This is equivalent to {@code (overlaps(other) || abuts(other))}.
*
* @param other the other interval, not null
* @return true if this interval is connected to the other interval
*/
public boolean isConnected(Interval other) {
Objects.requireNonNull(other, "other");
return this.equals(other) || (start.compareTo(other.end) <= 0 && other.start.compareTo(end) <= 0);
}
/**
* Checks if this interval overlaps the specified interval.
*
* The result is true if the the two intervals share some part of the time-line.
* An empty interval overlaps itself.
*
* This is equivalent to {@code (isConnected(other) && !abuts(other))}.
*
* @param other the time interval to compare to, null means a zero length interval now
* @return true if the time intervals overlap
*/
public boolean overlaps(Interval other) {
Objects.requireNonNull(other, "other");
return other.equals(this) || (start.compareTo(other.end) < 0 && other.start.compareTo(end) < 0);
}
//-----------------------------------------------------------------------
/**
* Calculates the interval that is the intersection of this interval and the specified interval.
*
* This finds the intersection of two intervals.
* This throws an exception if the two intervals are not {@linkplain #isConnected(Interval) connected}.
*
* @param other the other interval to check for, not null
* @return the interval that is the intersection of the two intervals
* @throws DateTimeException if the intervals do not connect
*/
public Interval intersection(Interval other) {
Objects.requireNonNull(other, "other");
if (isConnected(other) == false) {
throw new DateTimeException("Intervals do not connect: " + this + " and " + other);
}
int cmpStart = start.compareTo(other.start);
int cmpEnd = end.compareTo(other.end);
if (cmpStart >= 0 && cmpEnd <= 0) {
return this;
} else if (cmpStart <= 0 && cmpEnd >= 0) {
return other;
} else {
Instant newStart = (cmpStart >= 0 ? start : other.start);
Instant newEnd = (cmpEnd <= 0 ? end : other.end);
return Interval.of(newStart, newEnd);
}
}
/**
* Calculates the interval that is the union of this interval and the specified interval.
*
* This finds the union of two intervals.
* This throws an exception if the two intervals are not {@linkplain #isConnected(Interval) connected}.
*
* @param other the other interval to check for, not null
* @return the interval that is the union of the two intervals
* @throws DateTimeException if the intervals do not connect
*/
public Interval union(Interval other) {
Objects.requireNonNull(other, "other");
if (isConnected(other) == false) {
throw new DateTimeException("Intervals do not connect: " + this + " and " + other);
}
int cmpStart = start.compareTo(other.start);
int cmpEnd = end.compareTo(other.end);
if (cmpStart >= 0 && cmpEnd <= 0) {
return other;
} else if (cmpStart <= 0 && cmpEnd >= 0) {
return this;
} else {
Instant newStart = (cmpStart >= 0 ? other.start : start);
Instant newEnd = (cmpEnd <= 0 ? other.end : end);
return Interval.of(newStart, newEnd);
}
}
/**
* Calculates the smallest interval that encloses this interval and the specified interval.
*
* The result of this method will {@linkplain #encloses(Interval) enclose}
* this interval and the specified interval.
*
* @param other the other interval to check for, not null
* @return the interval that spans the two intervals
*/
public Interval span(Interval other) {
Objects.requireNonNull(other, "other");
int cmpStart = start.compareTo(other.start);
int cmpEnd = end.compareTo(other.end);
Instant newStart = (cmpStart >= 0 ? other.start : start);
Instant newEnd = (cmpEnd <= 0 ? other.end : end);
return Interval.of(newStart, newEnd);
}
//-------------------------------------------------------------------------
/**
* Checks if this interval is after the specified instant.
*
* The result is true if this instant starts after the specified instant.
* An empty interval behaves as though it is an instant for comparison purposes.
*
* @param instant the other instant to compare to, not null
* @return true if the start of this interval is after the specified instant
*/
public boolean isAfter(Instant instant) {
return start.compareTo(instant) > 0;
}
/**
* Checks if this interval is before the specified instant.
*
* The result is true if this instant ends before the specified instant.
* Since intervals do not include their end points, this will return true if the
* instant equals the end of the interval.
* An empty interval behaves as though it is an instant for comparison purposes.
*
* @param instant the other instant to compare to, not null
* @return true if the start of this interval is before the specified instant
*/
public boolean isBefore(Instant instant) {
return end.compareTo(instant) <= 0 && start.compareTo(instant) < 0;
}
//-------------------------------------------------------------------------
/**
* Checks if this interval is after the specified interval.
*
* The result is true if this instant starts after the end of the specified interval.
* Since intervals do not include their end points, this will return true if the
* instant equals the end of the interval.
* An empty interval behaves as though it is an instant for comparison purposes.
*
* @param interval the other interval to compare to, not null
* @return true if this instant is after the specified instant
*/
public boolean isAfter(Interval interval) {
return start.compareTo(interval.end) >= 0 && !interval.equals(this);
}
/**
* Checks if this interval is before the specified interval.
*
* The result is true if this instant ends before the start of the specified interval.
* Since intervals do not include their end points, this will return true if the
* two intervals abut.
* An empty interval behaves as though it is an instant for comparison purposes.
*
* @param interval the other interval to compare to, not null
* @return true if this instant is before the specified instant
*/
public boolean isBefore(Interval interval) {
return end.compareTo(interval.start) <= 0 && !interval.equals(this);
}
//-----------------------------------------------------------------------
/**
* Obtains the duration of this interval.
*
* An {@code Interval} is associated with two specific instants on the time-line.
* A {@code Duration} is simply an amount of time, separate from the time-line.
*
* @return the duration of the time interval
* @throws ArithmeticException if the calculation exceeds the capacity of {@code Duration}
*/
public Duration toDuration() {
return Duration.between(start, end);
}
//-----------------------------------------------------------------------
/**
* Checks if this interval is equal to another interval.
*
* Compares this {@code Interval} with another ensuring that the two instants are the same.
* Only objects of type {@code Interval} are compared, other types return false.
*
* @param obj the object to check, null returns false
* @return true if this is equal to the other interval
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof Interval) {
Interval other = (Interval) obj;
return start.equals(other.start) && end.equals(other.end);
}
return false;
}
/**
* A hash code for this interval.
*
* @return a suitable hash code
*/
@Override
public int hashCode() {
return start.hashCode() ^ end.hashCode();
}
//-----------------------------------------------------------------------
/**
* Outputs this interval as a {@code String}, such as {@code 2007-12-03T10:15:30/2007-12-04T10:15:30}.
*
* The output will be the ISO-8601 format formed by combining the
* {@code toString()} methods of the two instants, separated by a forward slash.
*
* @return a string representation of this instant, not null
*/
@Override
@ToString
public String toString() {
return start.toString() + '/' + end.toString();
}
}
threeten-extra-1.5.0/src/main/java/org/threeten/extra/LocalDateRange.java 0000664 0000000 0000000 00000076076 13434511741 0026333 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2007-present, 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 org.threeten.extra;
import java.io.Serializable;
import java.time.DateTimeException;
import java.time.LocalDate;
import java.time.Period;
import java.time.format.DateTimeParseException;
import java.time.temporal.TemporalAdjuster;
import java.util.Comparator;
import java.util.Objects;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.Consumer;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import org.joda.convert.FromString;
import org.joda.convert.ToString;
/**
* A range of local dates.
*
* A {@code LocalDateRange} represents a range of dates, from a start date to an end date.
* Instances can be constructed from either a half-open or a closed range of dates.
* Internally, the class stores the start and end dates, with the start inclusive and the end exclusive.
* The end date is always greater than or equal to the start date.
*
* The constants {@code LocalDate.MIN} and {@code LocalDate.MAX} can be used
* to indicate an unbounded far-past or far-future. Note that there is no difference
* between a half-open and a closed range when the end is {@code LocalDate.MAX}.
* Empty ranges are allowed.
*
* No range can end at {@code LocalDate.MIN} or {@code LocalDate.MIN.plusDays(1)}.
* No range can start at {@code LocalDate.MAX} or {@code LocalDate.MAX.minusDays(1)}.
* No empty range can exist at {@code LocalDate.MIN} or {@code LocalDate.MAX}.
*
* Date ranges are not comparable. To compare the length of two ranges, it is
* generally recommended to compare the number of days they contain.
*
*
* This class must be treated as a value type. Do not synchronize, rely on the
* identity hash code or use the distinction between equals() and ==.
*/
public final class LocalDateRange
implements Serializable {
/**
* The day after the MIN date.
*/
private static final LocalDate MINP1 = LocalDate.MIN.plusDays(1);
/**
* The day before the MAX date.
*/
private static final LocalDate MAXM1 = LocalDate.MAX.minusDays(1);
/**
* A range over the whole time-line.
*/
public static final LocalDateRange ALL = new LocalDateRange(LocalDate.MIN, LocalDate.MAX);
/**
* Serialization version.
*/
private static final long serialVersionUID = 3358656715467L;
/**
* The start date (inclusive).
*/
private final LocalDate start;
/**
* The end date (exclusive).
*/
private final LocalDate end;
//-----------------------------------------------------------------------
/**
* Obtains a half-open range of dates, including the start and excluding the end.
*
* The range includes the start date and excludes the end date, unless the end is {@code LocalDate.MAX}.
* The end date must be equal to or after the start date.
* This definition permits an empty range located at a specific date.
*
* The constants {@code LocalDate.MIN} and {@code LocalDate.MAX} can be used
* to indicate an unbounded far-past or far-future.
*
* The start inclusive date must not be {@code LocalDate.MAX} or {@code LocalDate.MAX.minusDays(1)}.
* The end inclusive date must not be {@code LocalDate.MIN} or {@code LocalDate.MIN.plusDays(1)}.
* No empty range can exist at {@code LocalDate.MIN} or {@code LocalDate.MAX}.
*
* @param startInclusive the inclusive start date, not null
* @param endExclusive the exclusive end date, not null
* @return the half-open range, not null
* @throws DateTimeException if the end is before the start,
* or the start date is {@code LocalDate.MAX} or {@code LocalDate.MAX.minusDays(1)},
* or the end date is {@code LocalDate.MIN} or {@code LocalDate.MIN.plusDays(1)}
*/
public static LocalDateRange of(LocalDate startInclusive, LocalDate endExclusive) {
Objects.requireNonNull(startInclusive, "startInclusive");
Objects.requireNonNull(endExclusive, "endExclusive");
return new LocalDateRange(startInclusive, endExclusive);
}
/**
* Obtains a closed range of dates, including the start and end.
*
* The range includes the start date and the end date.
* The end date must be equal to or after the start date.
*
* The constants {@code LocalDate.MIN} and {@code LocalDate.MAX} can be used
* to indicate an unbounded far-past or far-future. In addition, an end date of
* {@code LocalDate.MAX.minusDays(1)} will also create an unbounded far-future range.
*
* The start inclusive date must not be {@code LocalDate.MAX} or {@code LocalDate.MAX.minusDays(1)}.
* The end inclusive date must not be {@code LocalDate.MIN}.
*
* @param startInclusive the inclusive start date, not null
* @param endInclusive the inclusive end date, not null
* @return the closed range
* @throws DateTimeException if the end is before the start,
* or the start date is {@code LocalDate.MAX} or {@code LocalDate.MAX.minusDays(1)},
* or the end date is {@code LocalDate.MIN}
*/
public static LocalDateRange ofClosed(LocalDate startInclusive, LocalDate endInclusive) {
Objects.requireNonNull(startInclusive, "startInclusive");
Objects.requireNonNull(endInclusive, "endInclusive");
if (endInclusive.isBefore(startInclusive)) {
throw new DateTimeException("Start date must be on or before end date");
}
LocalDate end = (endInclusive.equals(LocalDate.MAX) ? LocalDate.MAX : endInclusive.plusDays(1));
return new LocalDateRange(startInclusive, end);
}
/**
* Obtains an instance of {@code LocalDateRange} from the start and a period.
*
* The end date is calculated as the start plus the duration.
* The period must not be negative.
*
* The constant {@code LocalDate.MIN} can be used to indicate an unbounded far-past.
*
* The period must not be zero or one day when the start date is {@code LocalDate.MIN}.
*
* @param startInclusive the inclusive start date, not null
* @param period the period from the start to the end, not null
* @return the range, not null
* @throws DateTimeException if the end is before the start,
* or if the period addition cannot be made
* @throws ArithmeticException if numeric overflow occurs when adding the period
*/
public static LocalDateRange of(LocalDate startInclusive, Period period) {
Objects.requireNonNull(startInclusive, "startInclusive");
Objects.requireNonNull(period, "period");
if (period.isNegative()) {
throw new DateTimeException("Period must not be zero or negative");
}
return new LocalDateRange(startInclusive, startInclusive.plus(period));
}
/**
* Obtains an empty date range located at the specified date.
*
* The empty range has zero length and contains no other dates or ranges.
* An empty range cannot be located at {@code LocalDate.MIN}, {@code LocalDate.MIN.plusDays(1)},
* {@code LocalDate.MAX} or {@code LocalDate.MAX.minusDays(1)}.
*
* @param date the date where the empty range is located, not null
* @return the empty range, not null
* @throws DateTimeException if the date is {@code LocalDate.MIN}, {@code LocalDate.MIN.plusDays(1)},
* {@code LocalDate.MAX} or {@code LocalDate.MAX.minusDays(1)}
*/
public static LocalDateRange ofEmpty(LocalDate date) {
Objects.requireNonNull(date, "date");
return new LocalDateRange(date, date);
}
/**
* Obtains a range that is unbounded at the start and end.
*
* @return the range, with an unbounded start and unbounded end
*/
public static LocalDateRange ofUnbounded() {
return ALL;
}
/**
* Obtains a range up to, but not including, the specified end date.
*
* The range includes all dates from the unbounded start, denoted by {@code LocalDate.MIN}, to the end date.
* The end date is exclusive and cannot be {@code LocalDate.MIN} or {@code LocalDate.MIN.plusDays(1)}.
*
* @param endExclusive the exclusive end date, {@code LocalDate.MAX} treated as unbounded, not null
* @return the range, with an unbounded start
* @throws DateTimeException if the end date is {@code LocalDate.MIN} or {@code LocalDate.MIN.plusDays(1)}
*/
public static LocalDateRange ofUnboundedStart(LocalDate endExclusive) {
return LocalDateRange.of(LocalDate.MIN, endExclusive);
}
/**
* Obtains a range from and including the specified start date.
*
* The range includes all dates from the start date to the unbounded end, denoted by {@code LocalDate.MAX}.
* The start date is inclusive and cannot be {@code LocalDate.MAX} or {@code LocalDate.MAX.minusDays(1)}.
*
* @param startInclusive the inclusive start date, {@code LocalDate.MIN} treated as unbounded, not null
* @return the range, with an unbounded end
* @throws DateTimeException if the start date is {@code LocalDate.MAX} or {@code LocalDate.MAX.minusDays(1)}
*/
public static LocalDateRange ofUnboundedEnd(LocalDate startInclusive) {
return LocalDateRange.of(startInclusive, LocalDate.MAX);
}
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code LocalDateRange} from a text string such as
* {@code 2007-12-03/2007-12-04}, where the end date is exclusive.
*
* The string must consist of one of the following three formats:
*
* This will return {@code LocalDate#MIN} if the range is unbounded at the start.
* In this case, the range includes all dates into the far-past.
*
* This never returns {@code LocalDate.MAX} or {@code LocalDate.MAX.minusDays(1)}.
*
* @return the start date
*/
public LocalDate getStart() {
return start;
}
/**
* Gets the end date of this range, exclusive.
*
* This will return {@code LocalDate.MAX} if the range is unbounded at the end.
* In this case, the range includes all dates into the far-future.
*
* This never returns {@code LocalDate.MIN} or {@code LocalDate.MIN.plusDays(1)}.
*
* @return the end date, exclusive
*/
public LocalDate getEnd() {
return end;
}
/**
* Gets the end date of this range, inclusive.
*
* This will return {@code LocalDate.MAX} if the range is unbounded at the end.
* In this case, the range includes all dates into the far-future.
*
* This returns the date before the end date.
*
* This never returns {@code LocalDate.MIN}.
*
* @return the end date, inclusive
*/
public LocalDate getEndInclusive() {
if (isUnboundedEnd()) {
return LocalDate.MAX;
}
return end.minusDays(1);
}
//-----------------------------------------------------------------------
/**
* Checks if the range is empty.
*
* An empty range occurs when the start date equals the end date.
*
* An empty range is never unbounded.
*
* @return true if the range is empty
*/
public boolean isEmpty() {
return start.equals(end);
}
/**
* Checks if the start of the range is unbounded.
*
* An unbounded range is never empty.
*
* @return true if start is unbounded
*/
public boolean isUnboundedStart() {
return start.equals(LocalDate.MIN);
}
/**
* Checks if the end of the range is unbounded.
*
* An unbounded range is never empty.
*
* @return true if end is unbounded
*/
public boolean isUnboundedEnd() {
return end.equals(LocalDate.MAX);
}
//-----------------------------------------------------------------------
/**
* Returns a copy of this range with the start date adjusted.
*
* This returns a new instance with the start date altered.
* Since {@code LocalDate} implements {@code TemporalAdjuster} any
* local date can simply be passed in.
*
* For example, to adjust the start to one week earlier:
*
* This returns a new instance with the exclusive end date altered.
* Since {@code LocalDate} implements {@code TemporalAdjuster} any
* local date can simply be passed in.
*
* For example, to adjust the end to one week later:
*
* This checks if the specified date is within the bounds of this range.
* If this range is empty then this method always returns false.
* Else if this range has an unbounded start then {@code contains(LocalDate#MIN)} returns true.
* Else if this range has an unbounded end then {@code contains(LocalDate#MAX)} returns true.
*
* @param date the date to check for, not null
* @return true if this range contains the date
*/
public boolean contains(LocalDate date) {
Objects.requireNonNull(date, "date");
return start.compareTo(date) <= 0 && (date.compareTo(end) < 0 || isUnboundedEnd());
}
/**
* Checks if this range encloses the specified range.
*
* This checks if the bounds of the specified range are within the bounds of this range.
* An empty range encloses itself.
*
* @param other the other range to check for, not null
* @return true if this range contains all dates in the other range
*/
public boolean encloses(LocalDateRange other) {
Objects.requireNonNull(other, "other");
return start.compareTo(other.start) <= 0 && other.end.compareTo(end) <= 0;
}
/**
* Checks if this range abuts the specified range.
*
* The result is true if the end of this range is the start of the other, or vice versa.
* An empty range does not abut itself.
*
* @param other the other range, not null
* @return true if this range abuts the other range
*/
public boolean abuts(LocalDateRange other) {
Objects.requireNonNull(other, "other");
return end.equals(other.start) ^ start.equals(other.end);
}
/**
* Checks if this range is connected to the specified range.
*
* The result is true if the two ranges have an enclosed range in common, even if that range is empty.
* An empty range is connected to itself.
*
* This is equivalent to {@code (overlaps(other) || abuts(other))}.
*
* @param other the other range, not null
* @return true if this range is connected to the other range
*/
public boolean isConnected(LocalDateRange other) {
Objects.requireNonNull(other, "other");
return this.equals(other) || (start.compareTo(other.end) <= 0 && other.start.compareTo(end) <= 0);
}
/**
* Checks if this range overlaps the specified range.
*
* The result is true if the the two ranges share some part of the time-line.
* An empty range overlaps itself.
*
* This is equivalent to {@code (isConnected(other) && !abuts(other))}.
*
* @param other the time range to compare to, null means a zero length range now
* @return true if the time ranges overlap
*/
public boolean overlaps(LocalDateRange other) {
Objects.requireNonNull(other, "other");
return other.equals(this) || (start.compareTo(other.end) < 0 && other.start.compareTo(end) < 0);
}
//-----------------------------------------------------------------------
/**
* Calculates the range that is the intersection of this range and the specified range.
*
* This finds the intersection of two ranges.
* This throws an exception if the two ranges are not {@linkplain #isConnected(LocalDateRange) connected}.
*
* @param other the other range to check for, not null
* @return the range that is the intersection of the two ranges
* @throws DateTimeException if the ranges do not connect
*/
public LocalDateRange intersection(LocalDateRange other) {
Objects.requireNonNull(other, "other");
if (isConnected(other) == false) {
throw new DateTimeException("Ranges do not connect: " + this + " and " + other);
}
int cmpStart = start.compareTo(other.start);
int cmpEnd = end.compareTo(other.end);
if (cmpStart >= 0 && cmpEnd <= 0) {
return this;
} else if (cmpStart <= 0 && cmpEnd >= 0) {
return other;
} else {
LocalDate newStart = (cmpStart >= 0 ? start : other.start);
LocalDate newEnd = (cmpEnd <= 0 ? end : other.end);
return LocalDateRange.of(newStart, newEnd);
}
}
/**
* Calculates the range that is the union of this range and the specified range.
*
* This finds the union of two ranges.
* This throws an exception if the two ranges are not {@linkplain #isConnected(LocalDateRange) connected}.
*
* @param other the other range to check for, not null
* @return the range that is the union of the two ranges
* @throws DateTimeException if the ranges do not connect
*/
public LocalDateRange union(LocalDateRange other) {
Objects.requireNonNull(other, "other");
if (isConnected(other) == false) {
throw new DateTimeException("Ranges do not connect: " + this + " and " + other);
}
int cmpStart = start.compareTo(other.start);
int cmpEnd = end.compareTo(other.end);
if (cmpStart >= 0 && cmpEnd <= 0) {
return other;
} else if (cmpStart <= 0 && cmpEnd >= 0) {
return this;
} else {
LocalDate newStart = (cmpStart >= 0 ? other.start : start);
LocalDate newEnd = (cmpEnd <= 0 ? other.end : end);
return LocalDateRange.of(newStart, newEnd);
}
}
/**
* Calculates the smallest range that encloses this range and the specified range.
*
* The result of this method will {@linkplain #encloses(LocalDateRange) enclose}
* this range and the specified range.
*
* @param other the other range to check for, not null
* @return the range that spans the two ranges
*/
public LocalDateRange span(LocalDateRange other) {
Objects.requireNonNull(other, "other");
int cmpStart = start.compareTo(other.start);
int cmpEnd = end.compareTo(other.end);
LocalDate newStart = (cmpStart >= 0 ? other.start : start);
LocalDate newEnd = (cmpEnd <= 0 ? other.end : end);
return LocalDateRange.of(newStart, newEnd);
}
//-----------------------------------------------------------------------
/**
* Streams the set of dates included in the range.
*
* This returns a stream consisting of each date in the range.
* The stream is ordered.
*
* @return the stream of dates from the start to the end
*/
public Stream
* The result is true if every date in this range is after the specified date.
* An empty range behaves as though it is a date for comparison purposes.
*
* @param date the other date to compare to, not null
* @return true if the start of this range is after the specified date
*/
public boolean isAfter(LocalDate date) {
return start.compareTo(date) > 0;
}
/**
* Checks if this range is before the specified date.
*
* The result is true if every date in this range is before the specified date.
* An empty range behaves as though it is a date for comparison purposes.
*
* @param date the other date to compare to, not null
* @return true if the start of this range is before the specified date
*/
public boolean isBefore(LocalDate date) {
return end.compareTo(date) <= 0 && start.compareTo(date) < 0;
}
//-----------------------------------------------------------------------
/**
* Checks if this range is after the specified range.
*
* The result is true if every date in this range is after every date in the specified range.
* An empty range behaves as though it is a date for comparison purposes.
*
* @param other the other range to compare to, not null
* @return true if every date in this range is after every date in the other range
*/
public boolean isAfter(LocalDateRange other) {
return start.compareTo(other.end) >= 0 && !other.equals(this);
}
/**
* Checks if this range is before the specified range.
*
* The result is true if every date in this range is before every date in the specified range.
* An empty range behaves as though it is a date for comparison purposes.
*
* @param range the other range to compare to, not null
* @return true if every date in this range is before every date in the other range
*/
public boolean isBefore(LocalDateRange range) {
return end.compareTo(range.start) <= 0 && !range.equals(this);
}
//-----------------------------------------------------------------------
/**
* Obtains the length of this range in days.
*
* This returns the number of days between the start and end dates.
* If the range is too large, the length will be {@code Integer.MAX_VALUE}.
* Unbounded ranges return {@code Integer.MAX_VALUE}.
*
* @return the length in days, Integer.MAX_VALUE if unbounded or too large
*/
public int lengthInDays() {
if (isUnboundedStart() || isUnboundedEnd()) {
return Integer.MAX_VALUE;
}
long length = end.toEpochDay() - start.toEpochDay();
return length > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) length;
}
/**
* Obtains the length of this range as a period.
*
* This returns the {@link Period} between the start and end dates.
* Unbounded ranges throw {@link ArithmeticException}.
*
* @return the period of the range
* @throws ArithmeticException if the calculation exceeds the capacity of {@code Period},
* or the range is unbounded
*/
public Period toPeriod() {
if (isUnboundedStart() || isUnboundedEnd()) {
throw new ArithmeticException("Unbounded range cannot be converted to a Period");
}
return Period.between(start, end);
}
//-----------------------------------------------------------------------
/**
* Checks if this range is equal to another range.
*
* Compares this {@code LocalDateRange} with another ensuring that the two dates are the same.
* Only objects of type {@code LocalDateRange} 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 (this == obj) {
return true;
}
if (obj instanceof LocalDateRange) {
LocalDateRange other = (LocalDateRange) obj;
return start.equals(other.start) && end.equals(other.end);
}
return false;
}
/**
* A hash code for this range.
*
* @return a suitable hash code
*/
@Override
public int hashCode() {
return start.hashCode() ^ end.hashCode();
}
//-----------------------------------------------------------------------
/**
* Outputs this range as a {@code String}, such as {@code 2007-12-03/2007-12-04}.
*
* The output will be the ISO-8601 format formed by combining the
* {@code toString()} methods of the two dates, separated by a forward slash.
*
* @return a string representation of this date, not null
*/
@Override
@ToString
public String toString() {
return start.toString() + '/' + end.toString();
}
}
threeten-extra-1.5.0/src/main/java/org/threeten/extra/Minutes.java 0000664 0000000 0000000 00000054626 13434511741 0025147 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2007-present, 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 org.threeten.extra;
import static java.time.temporal.ChronoUnit.MINUTES;
import java.io.Serializable;
import java.time.DateTimeException;
import java.time.Duration;
import java.time.format.DateTimeParseException;
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.Collections;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.joda.convert.FromString;
import org.joda.convert.ToString;
/**
* A minute-based amount of time, such as '8 minutes'.
*
* This class models a quantity or amount of time in terms of minutes.
* It is a type-safe way of representing a number of minutes in an application.
*
* The model is of a directed amount, meaning that the amount may be negative.
*
*
* This class must be treated as a value type. Do not synchronize, rely on the
* identity hash code or use the distinction between equals() and ==.
*/
public final class Minutes
implements TemporalAmount, Comparable
* The resulting amount will have the specified minutes.
*
* @param minutes the number of minutes, positive or negative
* @return the number of minutes, not null
*/
public static Minutes of(int minutes) {
if (minutes == 0) {
return ZERO;
}
return new Minutes(minutes);
}
/**
* Obtains a {@code Minutes} representing the number of minutes
* equivalent to a number of hours.
*
* The resulting amount will be minute-based, with the number of minutes
* equal to the number of hours multiplied by 60.
*
* @param hours the number of hours, positive or negative
* @return the amount with the input hours converted to minutes, not null
* @throws ArithmeticException if numeric overflow occurs
*/
public static Minutes ofHours(int hours) {
if (hours == 0) {
return ZERO;
}
return new Minutes(Math.multiplyExact(hours, MINUTES_PER_HOUR));
}
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code Minutes} from a temporal amount.
*
* This obtains an instance 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 Minutes}.
*
* The result is calculated by looping around each unit in the specified amount.
* Each amount is converted to minutes using {@link Temporals#convertAmount}.
* If the conversion yields a remainder, an exception is thrown.
* If the amount is zero, the unit is ignored.
*
* @param amount the temporal amount to convert, not null
* @return the equivalent amount, not null
* @throws DateTimeException if unable to convert to a {@code Minutes}
* @throws ArithmeticException if numeric overflow occurs
*/
public static Minutes from(TemporalAmount amount) {
if (amount instanceof Minutes) {
return (Minutes) amount;
}
Objects.requireNonNull(amount, "amount");
int minutes = 0;
for (TemporalUnit unit : amount.getUnits()) {
long value = amount.get(unit);
if (value != 0) {
long[] converted = Temporals.convertAmount(value, unit, MINUTES);
if (converted[1] != 0) {
throw new DateTimeException(
"Amount could not be converted to a whole number of minutes: " + value + " " + unit);
}
minutes = Math.addExact(minutes, Math.toIntExact(converted[0]));
}
}
return of(minutes);
}
//-----------------------------------------------------------------------
/**
* Obtains a {@code Minutes} from a text string such as {@code PTnM}.
*
* This will parse the string produced by {@code toString()} and other
* related formats based on ISO-8601 {@code PnDTnHnM}.
*
* The string starts with an optional sign, denoted by the ASCII negative
* or positive symbol. If negative, the whole amount is negated.
* The ASCII letter "P" is next in upper or lower case.
* There are three sections consisting of a number and a suffix.
* There is one section for days suffixed by "D",
* followed by one section for hours suffixed by "H",
* followed by one section for minutes suffixed by "M".
* At least one section must be present.
* If the hours or minutes section is present it must be prefixed by "T".
* If the hours or minutes section is omitted the "T" must be omitted.
* Letters must be in ASCII upper or lower case.
* 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}.
*
* The leading plus/minus sign, and negative values for days, hours and
* minutes are not part of the ISO-8601 standard.
*
* For example, the following are valid inputs:
*
* The start temporal is included, but the end temporal is not.
* The result of this method can be negative if the end is before the start.
*
* @param startInclusive the start temporal, inclusive, not null
* @param endExclusive the end temporal, exclusive, not null
* @return the number of minutes between the start and end temporals, not null
*/
public static Minutes between(Temporal startInclusive, Temporal endExclusive) {
return of(Math.toIntExact(MINUTES.between(startInclusive, endExclusive)));
}
//-----------------------------------------------------------------------
/**
* Constructs an instance using a specific number of minutes.
*
* @param minutes the amount of minutes
*/
private Minutes(int minutes) {
this.minutes = minutes;
}
/**
* Resolves singletons.
*
* @return the singleton instance
*/
private Object readResolve() {
return Minutes.of(minutes);
}
//-----------------------------------------------------------------------
/**
* Gets the value of the requested unit.
*
* This returns a value for the supported unit - {@link ChronoUnit#MINUTES MINUTES}.
* 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 UnsupportedTemporalTypeException if the unit is not supported
*/
@Override
public long get(TemporalUnit unit) {
if (unit == MINUTES) {
return minutes;
}
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
}
/**
* Gets the set of units supported by this amount.
*
* The single supported unit is {@link ChronoUnit#MINUTES MINUTES}.
*
* This set can be used in conjunction with {@link #get(TemporalUnit)} to
* access the entire state of the amount.
*
* @return a list containing the minutes unit, not null
*/
@Override
public List
* The parameter is converted using {@link Minutes#from(TemporalAmount)}.
*
* This instance is immutable and unaffected by this method call.
*
* @param amountToAdd the amount to add, not null
* @return a {@code Minutes} based on this instance with the requested amount added, not null
* @throws DateTimeException if the specified amount contains an invalid unit
* @throws ArithmeticException if numeric overflow occurs
*/
public Minutes plus(TemporalAmount amountToAdd) {
return plus(Minutes.from(amountToAdd).getAmount());
}
/**
* Returns a copy of this amount with the specified number of minutes added.
*
* This instance is immutable and unaffected by this method call.
*
* @param minutes the amount of minutes to add, may be negative
* @return a {@code Minutes} based on this instance with the requested amount added, not null
* @throws ArithmeticException if the result overflows an int
*/
public Minutes plus(int minutes) {
if (minutes == 0) {
return this;
}
return of(Math.addExact(this.minutes, minutes));
}
//-----------------------------------------------------------------------
/**
* Returns a copy of this amount with the specified amount subtracted.
*
* The parameter is converted using {@link Minutes#from(TemporalAmount)}.
*
* This instance is immutable and unaffected by this method call.
*
* @param amountToAdd the amount to add, not null
* @return a {@code Minutes} based on this instance with the requested amount subtracted, not null
* @throws DateTimeException if the specified amount contains an invalid unit
* @throws ArithmeticException if numeric overflow occurs
*/
public Minutes minus(TemporalAmount amountToAdd) {
return minus(Minutes.from(amountToAdd).getAmount());
}
/**
* Returns a copy of this amount with the specified number of minutes subtracted.
*
* This instance is immutable and unaffected by this method call.
*
* @param minutes the amount of minutes to add, may be negative
* @return a {@code Minutes} based on this instance with the requested amount subtracted, not null
* @throws ArithmeticException if the result overflows an int
*/
public Minutes minus(int minutes) {
if (minutes == 0) {
return this;
}
return of(Math.subtractExact(this.minutes, minutes));
}
//-----------------------------------------------------------------------
/**
* Returns an instance with the amount multiplied by the specified scalar.
*
* This instance is immutable and unaffected by this method call.
*
* @param scalar the scalar to multiply by, not null
* @return the amount multiplied by the specified scalar, not null
* @throws ArithmeticException if numeric overflow occurs
*/
public Minutes multipliedBy(int scalar) {
if (scalar == 1) {
return this;
}
return of(Math.multiplyExact(minutes, scalar));
}
/**
* Returns an instance with the amount divided by the specified divisor.
*
* The calculation uses integer division, thus 3 divided by 2 is 1.
*
* This instance is immutable and unaffected by this method call.
*
* @param divisor the amount to divide by, may be negative
* @return the amount divided by the specified divisor, not null
* @throws ArithmeticException if the divisor is zero
*/
public Minutes dividedBy(int divisor) {
if (divisor == 1) {
return this;
}
return of(minutes / divisor);
}
/**
* Returns an instance with the amount negated.
*
* This instance is immutable and unaffected by this method call.
*
* @return the negated amount, not null
* @throws ArithmeticException if numeric overflow occurs, which only happens if
* the amount is {@code Long.MIN_VALUE}
*/
public Minutes negated() {
return multipliedBy(-1);
}
/**
* Returns a copy of this duration with a positive length.
*
* This method returns a positive duration by effectively removing the sign from any negative total length.
*
* This instance is immutable and unaffected by this method call.
*
* @return the absolute amount, not null
* @throws ArithmeticException if numeric overflow occurs, which only happens if
* the amount is {@code Long.MIN_VALUE}
*/
public Minutes abs() {
return minutes < 0 ? negated() : this;
}
//-------------------------------------------------------------------------
/**
* Gets the number of minutes as a {@code Duration}.
*
* This returns a duration with the same number of minutes.
*
* @return the equivalent duration, not null
*/
public Duration toDuration() {
return Duration.ofMinutes(minutes);
}
//-----------------------------------------------------------------------
/**
* Adds this amount to the specified temporal object.
*
* This returns a temporal object of the same observable type as the input
* with this amount added.
*
* In most cases, it is clearer to reverse the calling pattern by using
* {@link Temporal#plus(TemporalAmount)}.
*
* Only non-zero amounts will be added.
*
* 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 UnsupportedTemporalTypeException if the MINUTES unit is not supported
* @throws ArithmeticException if numeric overflow occurs
*/
@Override
public Temporal addTo(Temporal temporal) {
if (minutes != 0) {
temporal = temporal.plus(minutes, MINUTES);
}
return temporal;
}
/**
* Subtracts this amount from the specified temporal object.
*
* This returns a temporal object of the same observable type as the input
* with this amount subtracted.
*
* In most cases, it is clearer to reverse the calling pattern by using
* {@link Temporal#minus(TemporalAmount)}.
*
* Only non-zero amounts will be subtracted.
*
* 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 UnsupportedTemporalTypeException if the MINUTES unit is not supported
* @throws ArithmeticException if numeric overflow occurs
*/
@Override
public Temporal subtractFrom(Temporal temporal) {
if (minutes != 0) {
temporal = temporal.minus(minutes, MINUTES);
}
return temporal;
}
//-----------------------------------------------------------------------
/**
* Compares this amount to the specified {@code Minutes}.
*
* The comparison is based on the total length of the amounts.
* It is "consistent with equals", as defined by {@link Comparable}.
*
* @param otherAmount the other amount, not null
* @return the comparator value, negative if less, positive if greater
*/
@Override
public int compareTo(Minutes otherAmount) {
int thisValue = this.minutes;
int otherValue = otherAmount.minutes;
return Integer.compare(thisValue, otherValue);
}
//-----------------------------------------------------------------------
/**
* Checks if this amount is equal to the specified {@code Minutes}.
*
* The comparison is based on the total length of the durations.
*
* @param otherAmount the other amount, null returns false
* @return true if the other amount is equal to this one
*/
@Override
public boolean equals(Object otherAmount) {
if (this == otherAmount) {
return true;
}
if (otherAmount instanceof Minutes) {
Minutes other = (Minutes) otherAmount;
return this.minutes == other.minutes;
}
return false;
}
/**
* A hash code for this amount.
*
* @return a suitable hash code
*/
@Override
public int hashCode() {
return minutes;
}
//-----------------------------------------------------------------------
/**
* Returns a string representation of the number of minutes.
* This will be in the format 'PTnM' where n is the number of minutes.
*
* @return the number of minutes in ISO-8601 string format
*/
@Override
@ToString
public String toString() {
return "PT" + minutes + "M";
}
}
threeten-extra-1.5.0/src/main/java/org/threeten/extra/Months.java 0000664 0000000 0000000 00000052575 13434511741 0024774 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2007-present, 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 org.threeten.extra;
import static java.time.temporal.ChronoUnit.MONTHS;
import java.io.Serializable;
import java.time.DateTimeException;
import java.time.Period;
import java.time.format.DateTimeParseException;
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.Collections;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.joda.convert.FromString;
import org.joda.convert.ToString;
/**
* A month-based amount of time, such as '12 months'.
*
* This class models a quantity or amount of time in terms of months.
* It is a type-safe way of representing a number of months in an application.
*
* The model is of a directed amount, meaning that the amount may be negative.
*
*
* This class must be treated as a value type. Do not synchronize, rely on the
* identity hash code or use the distinction between equals() and ==.
*/
public final class Months
implements TemporalAmount, Comparable
* The resulting amount will have the specified months.
*
* @param months the number of months, positive or negative
* @return the number of months, not null
*/
public static Months of(int months) {
if (months == 0) {
return ZERO;
} else if (months == 1) {
return ONE;
}
return new Months(months);
}
/**
* Obtains a {@code Months} representing the number of months
* equivalent to a number of years.
*
* The resulting amount will be month-based, with the number of months
* equal to the number of years multiplied by 12.
*
* @param years the number of years, positive or negative
* @return the amount with the input years converted to months, not null
* @throws ArithmeticException if numeric overflow occurs
*/
public static Months ofYears(int years) {
if (years == 0) {
return ZERO;
}
return new Months(Math.multiplyExact(years, MONTHS_PER_YEAR));
}
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code Months} from a temporal amount.
*
* This obtains an instance 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 Months}.
*
* The result is calculated by looping around each unit in the specified amount.
* Each amount is converted to months using {@link Temporals#convertAmount}.
* If the conversion yields a remainder, an exception is thrown.
* If the amount is zero, the unit is ignored.
*
* @param amount the temporal amount to convert, not null
* @return the equivalent amount, not null
* @throws DateTimeException if unable to convert to a {@code Months}
* @throws ArithmeticException if numeric overflow occurs
*/
public static Months from(TemporalAmount amount) {
if (amount instanceof Months) {
return (Months) amount;
}
Objects.requireNonNull(amount, "amount");
int months = 0;
for (TemporalUnit unit : amount.getUnits()) {
long value = amount.get(unit);
if (value != 0) {
long[] converted = Temporals.convertAmount(value, unit, MONTHS);
if (converted[1] != 0) {
throw new DateTimeException(
"Amount could not be converted to a whole number of months: " + value + " " + unit);
}
months = Math.addExact(months, Math.toIntExact(converted[0]));
}
}
return of(months);
}
//-----------------------------------------------------------------------
/**
* Obtains a {@code Months} from a text string such as {@code PnM}.
*
* This will parse the string produced by {@code toString()} which is
* based on the ISO-8601 period format {@code PnYnM}.
*
* The string starts with an optional sign, denoted by the ASCII negative
* or positive symbol. If negative, the whole amount is negated.
* The ASCII letter "P" is next in upper or lower case.
* There are then two sections, each consisting of a number and a suffix.
* At least one of the two sections must be present.
* The sections have suffixes in ASCII of "Y" and "M" for years and months,
* 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}.
*
* The leading plus/minus sign, and negative values for years and months are
* not part of the ISO-8601 standard.
*
* For example, the following are valid inputs:
*
* The start date is included, but the end date is not.
* The result of this method can be negative if the end is before the start.
*
* @param startDateInclusive the start date, inclusive, not null
* @param endDateExclusive the end date, exclusive, not null
* @return the number of months between this date and the end date, not null
*/
public static Months between(Temporal startDateInclusive, Temporal endDateExclusive) {
return of(Math.toIntExact(MONTHS.between(startDateInclusive, endDateExclusive)));
}
//-----------------------------------------------------------------------
/**
* Constructs an instance using a specific number of months.
*
* @param months the months to use
*/
private Months(int months) {
super();
this.months = months;
}
/**
* Resolves singletons.
*
* @return the singleton instance
*/
private Object readResolve() {
return Months.of(months);
}
//-----------------------------------------------------------------------
/**
* Gets the value of the requested unit.
*
* This returns a value for the supported unit - {@link ChronoUnit#MONTHS MONTHS}.
* 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 UnsupportedTemporalTypeException if the unit is not supported
*/
@Override
public long get(TemporalUnit unit) {
if (unit == MONTHS) {
return months;
}
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
}
/**
* Gets the set of units supported by this amount.
*
* The single supported unit is {@link ChronoUnit#MONTHS MONTHS}.
*
* This set can be used in conjunction with {@link #get(TemporalUnit)}
* to access the entire state of the amount.
*
* @return a list containing the months unit, not null
*/
@Override
public List
* The parameter is converted using {@link Months#from(TemporalAmount)}.
*
* This instance is immutable and unaffected by this method call.
*
* @param amountToAdd the amount to add, not null
* @return a {@code Months} based on this instance with the requested amount added, not null
* @throws DateTimeException if the specified amount contains an invalid unit
* @throws ArithmeticException if numeric overflow occurs
*/
public Months plus(TemporalAmount amountToAdd) {
return plus(Months.from(amountToAdd).getAmount());
}
/**
* Returns a copy of this amount with the specified number of months added.
*
* This instance is immutable and unaffected by this method call.
*
* @param months the amount of months to add, may be negative
* @return a {@code Months} based on this instance with the requested amount added, not null
* @throws ArithmeticException if the result overflows an int
*/
public Months plus(int months) {
if (months == 0) {
return this;
}
return of(Math.addExact(this.months, months));
}
//-----------------------------------------------------------------------
/**
* Returns a copy of this amount with the specified amount subtracted.
*
* The parameter is converted using {@link Months#from(TemporalAmount)}.
*
* This instance is immutable and unaffected by this method call.
*
* @param amountToAdd the amount to add, not null
* @return a {@code Months} based on this instance with the requested amount subtracted, not null
* @throws DateTimeException if the specified amount contains an invalid unit
* @throws ArithmeticException if numeric overflow occurs
*/
public Months minus(TemporalAmount amountToAdd) {
return minus(Months.from(amountToAdd).getAmount());
}
/**
* Returns a copy of this amount with the specified number of months subtracted.
*
* This instance is immutable and unaffected by this method call.
*
* @param months the amount of months to add, may be negative
* @return a {@code Months} based on this instance with the requested amount subtracted, not null
* @throws ArithmeticException if the result overflows an int
*/
public Months minus(int months) {
if (months == 0) {
return this;
}
return of(Math.subtractExact(this.months, months));
}
//-----------------------------------------------------------------------
/**
* Returns an instance with the amount multiplied by the specified scalar.
*
* This instance is immutable and unaffected by this method call.
*
* @param scalar the scalar to multiply by, not null
* @return the amount multiplied by the specified scalar, not null
* @throws ArithmeticException if numeric overflow occurs
*/
public Months multipliedBy(int scalar) {
if (scalar == 1) {
return this;
}
return of(Math.multiplyExact(months, scalar));
}
/**
* Returns an instance with the amount divided by the specified divisor.
*
* The calculation uses integer division, thus 3 divided by 2 is 1.
*
* This instance is immutable and unaffected by this method call.
*
* @param divisor the amount to divide by, may be negative
* @return the amount divided by the specified divisor, not null
* @throws ArithmeticException if the divisor is zero
*/
public Months dividedBy(int divisor) {
if (divisor == 1) {
return this;
}
return of(months / divisor);
}
/**
* Returns an instance with the amount negated.
*
* This instance is immutable and unaffected by this method call.
*
* @return the negated amount, not null
* @throws ArithmeticException if numeric overflow occurs, which only happens if
* the amount is {@code Long.MIN_VALUE}
*/
public Months negated() {
return multipliedBy(-1);
}
/**
* Returns a copy of this duration with a positive length.
*
* This method returns a positive duration by effectively removing the sign from any negative total length.
*
* This instance is immutable and unaffected by this method call.
*
* @return the absolute amount, not null
* @throws ArithmeticException if numeric overflow occurs, which only happens if
* the amount is {@code Long.MIN_VALUE}
*/
public Months abs() {
return months < 0 ? negated() : this;
}
//-------------------------------------------------------------------------
/**
* Gets the number of months as a {@code Period}.
*
* This returns a period with the same number of months.
*
* @return the equivalent period, not null
*/
public Period toPeriod() {
return Period.ofMonths(months);
}
//-----------------------------------------------------------------------
/**
* Adds this amount to the specified temporal object.
*
* This returns a temporal object of the same observable type as the input
* with this amount added.
*
* In most cases, it is clearer to reverse the calling pattern by using
* {@link Temporal#plus(TemporalAmount)}.
*
* Only non-zero amounts will be added.
*
* 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 UnsupportedTemporalTypeException if the MONTHS unit is not supported
* @throws ArithmeticException if numeric overflow occurs
*/
@Override
public Temporal addTo(Temporal temporal) {
if (months != 0) {
temporal = temporal.plus(months, MONTHS);
}
return temporal;
}
/**
* Subtracts this amount from the specified temporal object.
*
* This returns a temporal object of the same observable type as the input
* with this amount subtracted.
*
* In most cases, it is clearer to reverse the calling pattern by using
* {@link Temporal#minus(TemporalAmount)}.
*
* Only non-zero amounts will be subtracted.
*
* 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 UnsupportedTemporalTypeException if the MONTHS unit is not supported
* @throws ArithmeticException if numeric overflow occurs
*/
@Override
public Temporal subtractFrom(Temporal temporal) {
if (months != 0) {
temporal = temporal.minus(months, MONTHS);
}
return temporal;
}
//-----------------------------------------------------------------------
/**
* Compares this amount to the specified {@code Months}.
*
* The comparison is based on the total length of the amounts.
* It is "consistent with equals", as defined by {@link Comparable}.
*
* @param otherAmount the other amount, not null
* @return the comparator value, negative if less, positive if greater
*/
@Override
public int compareTo(Months otherAmount) {
int thisValue = this.months;
int otherValue = otherAmount.months;
return Integer.compare(thisValue, otherValue);
}
//-----------------------------------------------------------------------
/**
* Checks if this amount is equal to the specified {@code Months}.
*
* The comparison is based on the total length of the durations.
*
* @param otherAmount the other amount, null returns false
* @return true if the other amount is equal to this one
*/
@Override
public boolean equals(Object otherAmount) {
if (this == otherAmount) {
return true;
}
if (otherAmount instanceof Months) {
Months other = (Months) otherAmount;
return this.months == other.months;
}
return false;
}
/**
* A hash code for this amount.
*
* @return a suitable hash code
*/
@Override
public int hashCode() {
return months;
}
//-----------------------------------------------------------------------
/**
* Returns a string representation of the number of months.
* This will be in the format 'PnM' where n is the number of months.
*
* @return the number of months in ISO-8601 string format
*/
@Override
@ToString
public String toString() {
return "P" + months + "M";
}
}
threeten-extra-1.5.0/src/main/java/org/threeten/extra/MutableClock.java 0000664 0000000 0000000 00000040062 13434511741 0026055 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2007-present, 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 org.threeten.extra;
import static java.time.Instant.EPOCH;
import static java.time.ZoneOffset.UTC;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.time.Clock;
import java.time.DateTimeException;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
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.util.Objects;
/**
* A clock that does not advance on its own and that must be updated manually.
*
* This class is designed for testing clock-sensitive components by simulating
* the passage of time. This class differs from {@link
* Clock#fixed(Instant, ZoneId)} and {@link Clock#offset(Clock, Duration)} in
* that it permits arbitrary, unrestricted updates to its instant. This allows
* for testing patterns that are not well-supported by the {@code fixed} and
* {@code offset} clocks such as the following pattern:
*
* This class is mutable. The time-zone of the clock is fixed, but the instant
* may be updated at will.
*
* The instant may be set to any value even if that new value is less than the
* previous value. Caution should be exercised when moving the clock backwards,
* since clock-sensitive components are likely to assume that time is
* monotonically increasing.
*
* Update semantics are expressed in terms of {@link ZonedDateTime}. The steps
* of each update are as follows:
*
* Therefore, whenever there is a question about what argument types, units,
* fields, or values an update operation supports, or what the result will be,
* refer to the corresponding method of {@code ZonedDateTime}. Links are
* provided from the documentation of each update operation of this class to the
* corresponding method of {@code ZonedDateTime}.
*
*
* While update semantics are expressed in terms of {@code ZonedDateTime}, that
* imposes no requirements on implementation details. The implementation may
* avoid using {@code ZonedDateTime} completely or only sometimes, for
* convenience, efficiency, or any other reason.
*
* @serial exclude
*/
public final class MutableClock
extends Clock
implements Serializable {
/**
* Serialization version.
*/
private static final long serialVersionUID = -6152029959790119695L;
/**
* The mutable instant of this clock.
*/
private final transient InstantHolder instantHolder;
/**
* The fixed time-zone of this clock.
*/
private final transient ZoneId zone;
/**
* Obtains a new {@code MutableClock} set to the epoch of
* 1970-01-01T00:00:00Z, converting to date and time using the UTC
* time-zone.
*
* Use this method when a {@code MutableClock} is needed and neither its
* initial value nor its time-zone are important. This is often true when
* testing behavior that depends on elapsed relative time rather
* than absolute time.
*
* @return a new {@code MutableClock}, not null
*/
public static MutableClock epochUTC() {
return MutableClock.of(EPOCH, UTC);
}
/**
* Obtains a new {@code MutableClock} set to the specified instant,
* converting to date and time using the specified time-zone.
*
* @param instant the initial value for the clock, not null
* @param zone the time-zone to use, not null
* @return a new {@code MutableClock}, not null
*/
public static MutableClock of(Instant instant, ZoneId zone) {
Objects.requireNonNull(instant, "instant");
Objects.requireNonNull(zone, "zone");
return new MutableClock(new InstantHolder(instant), zone);
}
/**
* Constructor.
*
* @param instantHolder the mutable instant, validated not null
* @param zone the fixed time-zone, validated not null
*/
private MutableClock(InstantHolder instantHolder, ZoneId zone) {
this.instantHolder = instantHolder;
this.zone = zone;
}
/**
* Overrides the instant of this clock with the specified value.
*
* @param instant the new instant for this clock, not null
*/
public void setInstant(Instant instant) {
Objects.requireNonNull(instant, "instant");
instantHolder.set(instant);
}
/**
* Adds the specified amount to this clock.
*
* Atomically updates this clock to the value of the following expression:
*
* Atomically updates this clock to the value of the following expression:
*
* Atomically updates this clock to the value of the following expression:
*
* Atomically updates this clock to the value of the following expression:
*
* Two clocks with shared updates always have the same instant, and all
* updates applied to either clock affect both clocks.
*
* @param zone the time-zone to use for the returned clock, not null
* @return a view of this clock in the specified time-zone, not null
*/
@Override
public MutableClock withZone(ZoneId zone) {
Objects.requireNonNull(zone, "zone");
if (zone.equals(this.zone)) {
return this;
}
return new MutableClock(instantHolder, zone);
}
@Override
public Instant instant() {
return instantHolder.get();
}
/**
* Returns {@code true} if {@code obj} is a {@code MutableClock} that uses
* the same time-zone as this clock and has shared updates with this clock.
*
* Two clocks with shared updates always have the same instant, and all
* updates applied to either clock affect both clocks.
*
* A deserialized {@code MutableClock} is not equal to the original clock
* that was serialized, since the two clocks do not have shared updates.
*
* @param obj the object to check, null returns {@code false}
* @return {@code true} if this is equal to the other clock
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof MutableClock) {
MutableClock other = (MutableClock) obj;
return instantHolder == other.instantHolder && zone.equals(other.zone);
}
return false;
}
/**
* A hash code for this clock, which is constant for this instance.
*
* @return a constant hash code for this instance
*/
@Override
public int hashCode() {
return System.identityHashCode(instantHolder) ^ zone.hashCode();
}
@Override
public String toString() {
return "MutableClock[" + instant() + "," + getZone() + "]";
}
/**
* Returns the serialization proxy to replace this {@code MutableClock}.
*
* @return the serialization proxy, not null
*/
private Object writeReplace() {
return new SerializationProxy(this);
}
/**
* Throws {@link InvalidObjectException}.
*
* @param s ignored
* @throws InvalidObjectException always
*/
private void readObject(ObjectInputStream s) throws InvalidObjectException {
throw new InvalidObjectException("Proxy required");
}
/**
* The serialized form of a {@code MutableClock}.
*
* @serial include
*/
private static final class SerializationProxy
implements Serializable {
/**
* Serialization version.
*/
private static final long serialVersionUID = 8602110640241828260L;
/**
* A snapshot of the instant of the {@code MutableClock}, taken when the
* clock was serialized, not null.
*
* @serial
*/
private final Instant instant;
/**
* The time-zone of the {@code MutableClock}, not null.
*
* @serial
*/
private final ZoneId zone;
/**
* Constructor.
*
* @param clock the {@code MutableClock} to be serialized, not null
*/
SerializationProxy(MutableClock clock) {
instant = clock.instant();
zone = clock.getZone();
}
/**
* Returns the {@code MutableClock} to replace this serialization proxy.
*
* @return the {@code MutableClock}, not null
* @throws InvalidObjectException if the instant or time-zone is null
*/
private Object readResolve() throws InvalidObjectException {
if (instant == null) {
throw new InvalidObjectException("null instant");
}
if (zone == null) {
throw new InvalidObjectException("null zone");
}
return MutableClock.of(instant, zone);
}
}
/**
* An identity-having holder object for a mutable instant value.
*
* Clocks have shared updates when they share a holder object. Clocks rely
* on the identity of the holder object in their {@code equals} and {@code
* hashCode} methods.
*
* Reads of the value are volatile and are never stale. Blind writes to the
* value are volatile and do not need to synchronize. Atomic read-and-write
* operations must synchronize on the holder object instance.
*/
private static final class InstantHolder {
/**
* The current value.
*/
private volatile Instant value;
/**
* Constructor.
*
* @param value the initial value, validated not null
*/
InstantHolder(Instant value) {
this.value = value;
}
/**
* Reads the value.
*
* @return the current value, not null
*/
Instant get() {
return value;
}
/**
* Writes the value.
*
* @param value the new value, validated not null
*/
void set(Instant value) {
this.value = value;
}
}
}
threeten-extra-1.5.0/src/main/java/org/threeten/extra/PackedFields.java 0000664 0000000 0000000 00000036027 13434511741 0026034 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2007-present, 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 org.threeten.extra;
import static java.time.temporal.ChronoField.EPOCH_DAY;
import static java.time.temporal.ChronoField.HOUR_OF_DAY;
import static java.time.temporal.ChronoField.MINUTE_OF_DAY;
import static java.time.temporal.ChronoField.MINUTE_OF_HOUR;
import static java.time.temporal.ChronoField.SECOND_OF_DAY;
import static java.time.temporal.ChronoField.SECOND_OF_MINUTE;
import static java.time.temporal.ChronoUnit.DAYS;
import static java.time.temporal.ChronoUnit.FOREVER;
import static java.time.temporal.ChronoUnit.MINUTES;
import static java.time.temporal.ChronoUnit.SECONDS;
import java.time.DateTimeException;
import java.time.LocalDate;
import java.time.chrono.ChronoLocalDate;
import java.time.chrono.Chronology;
import java.time.format.ResolverStyle;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalField;
import java.time.temporal.TemporalUnit;
import java.time.temporal.ValueRange;
import java.util.Map;
/**
* Temporal fields based on a packed representation.
*
* This provides three fields that use a packed integer representation for dates and times.
*/
public final class PackedFields {
/**
* Packed date field.
*
* This returns the date as a single integer value.
* Only dates from year 1000 to year 9999 are supported.
* The output is always an 8 digit integer.
* For example, the date 2015-12-03 is packed to the integer 20151203.
*
* This field has invalid values within the range of value values.
* For example, 20121301 is invalid as it implies month 13.
*
* When parsing in {@linkplain ResolverStyle#LENIENT lenient mode}, invalid
* dates will be accepted. For example, 20121301 will result in 2013-01-01.
*/
public static final TemporalField PACKED_DATE = PackedDate.INSTANCE;
/**
* Packed hour-minute time field.
*
* This returns the time as a single integer value.
* The output is an integer from 0 to 2359.
* For example, the date 11:30 is packed to the integer 1130.
*
* This field has invalid values within the range of value values.
* For example, 1073 is invalid as it implies the minute is 73.
*
* When parsing in {@linkplain ResolverStyle#LENIENT lenient mode}, invalid
* times will be accepted. For example, 1073 will result in 11:13.
*/
public static final TemporalField PACKED_HOUR_MIN = PackedHourMin.INSTANCE;
/**
* Packed hour-minute-second time field.
*
* This returns the time as a single integer value.
* The output is an integer from 0 to 235959.
* For example, the date 11:30:52 is packed to the integer 113052.
*
* This field has invalid values within the range of value values.
* For example, 107310 is invalid as it implies the minute is 73.
*
* When parsing in {@linkplain ResolverStyle#LENIENT lenient mode}, invalid
* times will be accepted. For example, 107310 will result in 11:13:10.
*/
public static final TemporalField PACKED_TIME = PackedTime.INSTANCE;
/**
* Restricted constructor.
*/
private PackedFields() {
}
//-------------------------------------------------------------------------
/**
* Implementation of packed date.
*/
private static enum PackedDate implements TemporalField {
INSTANCE;
private static final ValueRange RANGE = ValueRange.of(10000101, 99991231);
private static final long serialVersionUID = -38752465672576L;
//-----------------------------------------------------------------------
@Override
public TemporalUnit getBaseUnit() {
return DAYS;
}
@Override
public TemporalUnit getRangeUnit() {
return FOREVER;
}
@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 (!temporal.isSupported(this)) {
throw new DateTimeException("Unsupported field: " + this);
}
return range();
}
@Override
public long getFrom(TemporalAccessor temporal) {
LocalDate date = LocalDate.ofEpochDay(temporal.getLong(EPOCH_DAY));
int year = date.getYear();
if (year < 1000 || year > 9999) {
throw new DateTimeException("Unable to obtain PackedDate from LocalDate: " + date);
}
int moy = date.getMonthValue();
int dom = date.getDayOfMonth();
return year * 10000 + moy * 100 + dom;
}
@SuppressWarnings("unchecked")
@Override
public
* This class models a quantity or amount of time in terms of a {@code Period} and {@code Duration}.
* A period is a date-based amount of time, consisting of years, months and days.
* A duration is a time-based amount of time, consisting of seconds and nanoseconds.
* See the {@link Period} and {@link Duration} classes for more details.
*
* The days in a period take account of daylight saving changes (23 or 25 hour days).
* When performing calculations, the period is added first, then the duration.
*
* The model is of a directed amount, meaning that the amount may be negative.
*
*
* This class must be treated as a value type. Do not synchronize, rely on the
* identity hash code or use the distinction between equals() and ==.
*/
public final class PeriodDuration
implements TemporalAmount, Serializable {
/**
* A constant for a duration of zero.
*/
public static final PeriodDuration ZERO = new PeriodDuration(Period.ZERO, Duration.ZERO);
/**
* A serialization identifier for this class.
*/
private static final long serialVersionUID = 8815521625671589L;
/**
* The supported units.
*/
private static final List
* The total amount of time of the resulting instance is the period plus the duration.
*
* @param period the period, not null
* @param duration the duration, not null
* @return the combined period-duration, not null
*/
public static PeriodDuration of(Period period, Duration duration) {
Objects.requireNonNull(period, "The period must not be null");
Objects.requireNonNull(duration, "The duration must not be null");
return new PeriodDuration(period, duration);
}
/**
* Obtains an instance based on a period.
*
* The duration will be zero.
*
* @param period the period, not null
* @return the combined period-duration, not null
*/
public static PeriodDuration of(Period period) {
Objects.requireNonNull(period, "The period must not be null");
return new PeriodDuration(period, Duration.ZERO);
}
/**
* Obtains an instance based on a duration.
*
* The period will be zero.
*
* @param duration the duration, not null
* @return the combined period-duration, not null
*/
public static PeriodDuration of(Duration duration) {
Objects.requireNonNull(duration, "The duration must not be null");
return new PeriodDuration(Period.ZERO, duration);
}
//-----------------------------------------------------------------------
/**
* Obtains an instance from a temporal amount.
*
* This obtains an instance based on the specified amount.
* A {@code TemporalAmount} represents an amount of time which this factory
* extracts to a {@code PeriodDuration}.
*
* The result is calculated by looping around each unit in the specified amount.
* Any amount that is zero is ignore.
* If a unit has an exact duration, it will be totalled using {@link Duration#plus(Duration)}.
* If the unit is days or weeks, it will be totalled into the days part of the period.
* If the unit is months or quarters, it will be totalled into the months part of the period.
* If the unit is years, decades, centuries or millennia, it will be totalled into the years part of the period.
*
* @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 PeriodDuration from(TemporalAmount amount) {
if (amount instanceof PeriodDuration) {
return (PeriodDuration) amount;
}
if (amount instanceof Period) {
return PeriodDuration.of((Period) amount);
}
if (amount instanceof Duration) {
return PeriodDuration.of((Duration) 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;
Duration duration = Duration.ZERO;
for (TemporalUnit unit : amount.getUnits()) {
long value = amount.get(unit);
if (value != 0) {
// ignore unless non-zero
if (unit.isDurationEstimated()) {
if (unit == ChronoUnit.DAYS) {
days = Math.addExact(days, Math.toIntExact(value));
} else if (unit == ChronoUnit.WEEKS) {
days = Math.addExact(days, Math.toIntExact(Math.multiplyExact(value, 7)));
} else if (unit == ChronoUnit.MONTHS) {
months = Math.addExact(months, Math.toIntExact(value));
} else if (unit == IsoFields.QUARTER_YEARS) {
months = Math.addExact(months, Math.toIntExact(Math.multiplyExact(value, 3)));
} else if (unit == ChronoUnit.YEARS) {
years = Math.addExact(years, Math.toIntExact(value));
} else if (unit == ChronoUnit.DECADES) {
years = Math.addExact(years, Math.toIntExact(Math.multiplyExact(value, 10)));
} else if (unit == ChronoUnit.CENTURIES) {
years = Math.addExact(years, Math.toIntExact(Math.multiplyExact(value, 100)));
} else if (unit == ChronoUnit.MILLENNIA) {
years = Math.addExact(years, Math.toIntExact(Math.multiplyExact(value, 1000)));
} else {
throw new DateTimeException("Unknown unit: " + unit);
}
} else {
// total of exact durations
duration = duration.plus(amount.get(unit), unit);
}
}
}
return PeriodDuration.of(Period.of(years, months, days), duration);
}
//-----------------------------------------------------------------------
/**
* Obtains an instance from a text string such as {@code PnYnMnDTnHnMnS}.
*
* This will parse the string produced by {@code toString()} which is
* based on the ISO-8601 period formats {@code PnYnMnDTnHnMnS} and {@code PnW}.
*
* The string starts with an optional sign, denoted by the ASCII negative
* or positive symbol. If negative, the whole amount is negated.
* The ASCII letter "P" is next in upper or lower case.
* There are then a number of sections, each consisting of a number and a suffix.
* At least one of the sections must be present.
* The sections have suffixes in ASCII of "Y" for years, "M" for months,
* "W" for weeks, "D" for days, "H" for hours, "M" for minutes, "S" for seconds,
* accepted in upper or lower case. Note that the ASCII letter "T" separates
* the date and time parts and must be present if any time part is present.
* 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}.
* Any week-based input is multiplied by 7 and treated as a number of days.
*
* The leading plus/minus sign, and negative values for weeks and days are
* not part of the ISO-8601 standard.
*
* Note that the date style format {@code PYYYY-MM-DDTHH:MM:SS} is not supported.
*
* For example, the following are valid inputs:
*
* The start is included, but the end is not.
* The result of this method can be negative if the end is before the start.
*
* The calculation examines the temporals and extracts {@link LocalDate} and {@link LocalTime}.
* If the time is missing, it will be defaulted to midnight.
* If one date is missing, it will be defaulted to the other date.
* It then finds the amount of time between the two dates and between the two times.
*
* @param startInclusive the start, inclusive, not null
* @param endExclusive the end, exclusive, not null
* @return the number of days between this date and the end date, not null
*/
public static PeriodDuration between(Temporal startInclusive, Temporal endExclusive) {
LocalDate startDate = startInclusive.query(TemporalQueries.localDate());
LocalDate endDate = endExclusive.query(TemporalQueries.localDate());
Period period = Period.ZERO;
if (startDate != null && endDate != null) {
period = Period.between(startDate, endDate);
}
LocalTime startTime = startInclusive.query(TemporalQueries.localTime());
LocalTime endTime = endExclusive.query(TemporalQueries.localTime());
startTime = startTime != null ? startTime : LocalTime.MIDNIGHT;
endTime = endTime != null ? endTime : LocalTime.MIDNIGHT;
Duration duration = Duration.between(startTime, endTime);
return PeriodDuration.of(period, duration);
}
//-----------------------------------------------------------------------
/**
* Constructs an instance.
*
* @param period the period
* @param duration the duration
*/
private PeriodDuration(Period period, Duration duration) {
this.period = period;
this.duration = duration;
}
/**
* Resolves singletons.
*
* @return the singleton instance
*/
private Object readResolve() {
return PeriodDuration.of(period, duration);
}
//-----------------------------------------------------------------------
/**
* Gets the value of the requested unit.
*
* This returns a value for the supported units - {@link ChronoUnit#YEARS},
* {@link ChronoUnit#MONTHS}, {@link ChronoUnit#DAYS}, {@link ChronoUnit#SECONDS}
* and {@link ChronoUnit#NANOS}.
* All other units throw an exception.
* Note that hours and minutes throw an exception.
*
* @param unit the {@code TemporalUnit} for which to return the value
* @return the long value of the unit
* @throws UnsupportedTemporalTypeException if the unit is not supported
*/
@Override
public long get(TemporalUnit unit) {
if (unit instanceof ChronoUnit) {
switch ((ChronoUnit) unit) {
case YEARS:
return period.getYears();
case MONTHS:
return period.getMonths();
case DAYS:
return period.getDays();
case SECONDS:
return duration.getSeconds();
case NANOS:
return duration.getNano();
default:
break;
}
}
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
}
/**
* Gets the set of units supported by this amount.
*
* This returns the list {@link ChronoUnit#YEARS}, {@link ChronoUnit#MONTHS},
* {@link ChronoUnit#DAYS}, {@link ChronoUnit#SECONDS} and {@link ChronoUnit#NANOS}.
*
* This set can be used in conjunction with {@link #get(TemporalUnit)}
* to access the entire state of the amount.
*
* @return a list containing the days unit, not null
*/
@Override
public List
* This instance is immutable and unaffected by this method call.
*
* @param period the new period
* @return the updated period-duration
*/
public PeriodDuration withPeriod(Period period) {
return PeriodDuration.of(period, duration);
}
/**
* Gets the duration part.
*
* @return the duration part
*/
public Duration getDuration() {
return duration;
}
/**
* Returns a copy of this period-duration with a different duration.
*
* This instance is immutable and unaffected by this method call.
*
* @param duration the new duration
* @return the updated period-duration
*/
public PeriodDuration withDuration(Duration duration) {
return PeriodDuration.of(period, duration);
}
//-----------------------------------------------------------------------
/**
* Checks if all parts of this amount are zero.
*
* This returns true if both {@link Period#isZero()} and {@link Duration#isZero()}
* return true.
*
* @return true if this period is zero-length
*/
public boolean isZero() {
return period.isZero() && duration.isZero();
}
//-----------------------------------------------------------------------
/**
* Returns a copy of this amount with the specified amount added.
*
* The parameter is converted using {@link PeriodDuration#from(TemporalAmount)}.
* The period and duration are combined separately.
*
* This instance is immutable and unaffected by this method call.
*
* @param amountToAdd the amount to add, not null
* @return a {@code Days} based on this instance with the requested amount added, not null
* @throws DateTimeException if the specified amount contains an invalid unit
* @throws ArithmeticException if numeric overflow occurs
*/
public PeriodDuration plus(TemporalAmount amountToAdd) {
PeriodDuration other = PeriodDuration.from(amountToAdd);
return of(period.plus(other.period), duration.plus(other.duration));
}
//-----------------------------------------------------------------------
/**
* Returns a copy of this amount with the specified amount subtracted.
*
* The parameter is converted using {@link PeriodDuration#from(TemporalAmount)}.
* The period and duration are combined separately.
*
* This instance is immutable and unaffected by this method call.
*
* @param amountToAdd the amount to add, not null
* @return a {@code Days} based on this instance with the requested amount subtracted, not null
* @throws DateTimeException if the specified amount contains an invalid unit
* @throws ArithmeticException if numeric overflow occurs
*/
public PeriodDuration minus(TemporalAmount amountToAdd) {
PeriodDuration other = PeriodDuration.from(amountToAdd);
return of(period.minus(other.period), duration.minus(other.duration));
}
//-----------------------------------------------------------------------
/**
* Returns an instance with the amount multiplied by the specified scalar.
*
* This instance is immutable and unaffected by this method call.
*
* @param scalar the scalar to multiply by, not null
* @return the amount multiplied by the specified scalar, not null
* @throws ArithmeticException if numeric overflow occurs
*/
public PeriodDuration multipliedBy(int scalar) {
if (scalar == 1) {
return this;
}
return of(period.multipliedBy(scalar), duration.multipliedBy(scalar));
}
/**
* Returns an instance with the amount negated.
*
* This instance is immutable and unaffected by this method call.
*
* @return the negated amount, not null
* @throws ArithmeticException if numeric overflow occurs, which only happens if
* the amount is {@code Long.MIN_VALUE}
*/
public PeriodDuration negated() {
return multipliedBy(-1);
}
//-----------------------------------------------------------------------
/**
* Returns a copy of this instance with the years and months exactly normalized.
*
* This normalizes the years and months units, leaving the days unit unchanged.
* The result is exact, always representing the same amount of time.
*
* 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".
*
* 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".
*
* Note that no normalization is performed on the days or duration.
*
* This instance is immutable and unaffected by this method call.
*
* @return a {@code PeriodDuration} based on this one with excess months normalized to years, not null
* @throws ArithmeticException if numeric overflow occurs
*/
public PeriodDuration normalizedYears() {
return withPeriod(period.normalized());
}
/**
* Returns a copy of this instance with the days and duration normalized using the standard day of 24 hours.
*
* This normalizes the days and duration, leaving the years and months unchanged.
* The result uses a standard day length of 24 hours.
*
* This combines the duration seconds with the number of days and shares the total
* seconds between the two fields. For example, a period of
* "2 days and 86401 seconds" will be normalized to "3 days and 1 second".
*
* The sign of the days and duration will be the same after normalization.
* For example, a period of "1 day and -172801 seconds" will be normalized to
* "-1 day and -1 second".
*
* Note that no normalization is performed on the years or months.
*
* This instance is immutable and unaffected by this method call.
*
* @return a {@code PeriodDuration} based on this one with excess duration normalized to days, not null
* @throws ArithmeticException if numeric overflow occurs
*/
public PeriodDuration normalizedStandardDays() {
long totalSecs = period.getDays() * SECONDS_PER_DAY + duration.getSeconds();
int splitDays = Math.toIntExact(totalSecs / SECONDS_PER_DAY);
long splitSecs = totalSecs % SECONDS_PER_DAY;
if (splitDays == period.getDays() && splitSecs == duration.getSeconds()) {
return this;
}
return PeriodDuration.of(period.withDays(splitDays), duration.withSeconds(splitSecs));
}
//-----------------------------------------------------------------------
/**
* Adds this amount to the specified temporal object.
*
* This returns a temporal object of the same observable type as the input
* with this amount added. This simply adds the period and duration to the temporal.
*
* 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 UnsupportedTemporalTypeException if the DAYS unit is not supported
* @throws ArithmeticException if numeric overflow occurs
*/
@Override
public Temporal addTo(Temporal temporal) {
return temporal.plus(period).plus(duration);
}
/**
* Subtracts this amount from the specified temporal object.
*
* This returns a temporal object of the same observable type as the input
* with this amount subtracted. This simply subtracts the period and duration from the temporal.
*
* 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 UnsupportedTemporalTypeException if the DAYS unit is not supported
* @throws ArithmeticException if numeric overflow occurs
*/
@Override
public Temporal subtractFrom(Temporal temporal) {
return temporal.minus(period).minus(duration);
}
//-----------------------------------------------------------------------
/**
* Checks if this amount is equal to the specified {@code PeriodDuration}.
*
* The comparison is based on the underlying period and duration.
*
* @param otherAmount the other amount, null returns false
* @return true if the other amount is equal to this one
*/
@Override
public boolean equals(Object otherAmount) {
if (this == otherAmount) {
return true;
}
if (otherAmount instanceof PeriodDuration) {
PeriodDuration other = (PeriodDuration) otherAmount;
return this.period.equals(other.period) && this.duration.equals(other.duration);
}
return false;
}
/**
* A hash code for this amount.
*
* @return a suitable hash code
*/
@Override
public int hashCode() {
return period.hashCode() ^ duration.hashCode();
}
//-----------------------------------------------------------------------
/**
* Returns a string representation of the amount.
* This will be in the format 'PnYnMnDTnHnMnS', with sections omitted as necessary.
* An empty amount will return "PT0S".
*
* @return the period in ISO-8601 string format
*/
@Override
@ToString
public String toString() {
if (period.isZero()) {
return duration.toString();
}
if (duration.isZero()) {
return period.toString();
}
return period.toString() + duration.toString().substring(1);
}
}
threeten-extra-1.5.0/src/main/java/org/threeten/extra/Quarter.java 0000664 0000000 0000000 00000051406 13434511741 0025137 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2007-present, 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 org.threeten.extra;
import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
import static java.time.temporal.IsoFields.QUARTER_OF_YEAR;
import static java.time.temporal.IsoFields.QUARTER_YEARS;
import java.time.DateTimeException;
import java.time.LocalDate;
import java.time.Month;
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.IsoFields;
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 quarter-of-year, such as 'Q2'.
*
* {@code Quarter} is an enum representing the 4 quarters of the year -
* Q1, Q2, Q3 and Q4. These are defined as January to March, April to June,
* July to September and October to December.
*
* The {@code int} value follows the quarter, from 1 (Q1) to 4 (Q4).
* It is recommended that applications use the enum rather than the {@code int} value
* to ensure code clarity.
*
* Do not use {@code ordinal()} to obtain the numeric representation of {@code Quarter}.
* Use {@code getValue()} instead.
*
* 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 quarter-of-year
* concept defined exactly equivalent to the ISO calendar system.
*
*
* {@code Quarter} is an enum representing the 4 quarters of the year.
* This factory allows the enum to be obtained from the {@code int} value.
* The {@code int} value follows the quarter, from 1 (Q1) to 4 (Q4).
*
* @param quarterOfYear the quarter-of-year to represent, from 1 (Q1) to 4 (Q4)
* @return the quarter-of-year, not null
* @throws DateTimeException if the quarter-of-year is invalid
*/
public static Quarter of(int quarterOfYear) {
switch (quarterOfYear) {
case 1:
return Q1;
case 2:
return Q2;
case 3:
return Q3;
case 4:
return Q4;
default:
throw new DateTimeException("Invalid value for Quarter: " + quarterOfYear);
}
}
/**
* Obtains an instance of {@code Quarter} from a month-of-year.
*
* {@code Quarter} is an enum representing the 4 quarters of the year.
* This factory allows the enum to be obtained from the {@code Month} value.
*
* January to March are Q1, April to June are Q2, July to September are Q3
* and October to December are Q4.
*
* @param monthOfYear the month-of-year to convert from, from 1 to 12
* @return the quarter-of-year, not null
* @throws DateTimeException if the month-of-year is invalid
*/
public static Quarter ofMonth(int monthOfYear) {
MONTH_OF_YEAR.range().checkValidValue(monthOfYear, MONTH_OF_YEAR);
return of((monthOfYear - 1) / 3 + 1);
}
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code Quarter} from a temporal object.
*
* This obtains a quarter 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 Quarter}.
*
* The conversion extracts the {@link IsoFields#QUARTER_OF_YEAR QUARTER_OF_YEAR} field.
* The extraction is only permitted if the temporal object has an ISO
* chronology, or can be converted to a {@code LocalDate}.
*
* This method matches the signature of the functional interface {@link TemporalQuery}
* allowing it to be used in queries via method reference, {@code Quarter::from}.
*
* @param temporal the temporal-time object to convert, not null
* @return the quarter-of-year, not null
* @throws DateTimeException if unable to convert to a {@code Quarter}
*/
public static Quarter from(TemporalAccessor temporal) {
if (temporal instanceof Quarter) {
return (Quarter) temporal;
} else if (temporal instanceof Month) {
Month month = (Month) temporal;
return of(month.ordinal() / 3 + 1);
}
try {
if (IsoChronology.INSTANCE.equals(Chronology.from(temporal)) == false) {
temporal = LocalDate.from(temporal);
}
// need to use getLong() as JDK Parsed class get() doesn't work properly
int qoy = Math.toIntExact(temporal.getLong(QUARTER_OF_YEAR));
return of(qoy);
} catch (DateTimeException ex) {
throw new DateTimeException("Unable to obtain Quarter from TemporalAccessor: " +
temporal + " of type " + temporal.getClass().getName(), ex);
}
}
//-----------------------------------------------------------------------
/**
* Gets the quarter-of-year {@code int} value.
*
* The values are numbered following the ISO-8601 standard,
* from 1 (Q1) to 4 (Q4).
*
* @return the quarter-of-year, from 1 (Q1) to 4 (Q4)
*/
public int getValue() {
return ordinal() + 1;
}
//-----------------------------------------------------------------------
/**
* Gets the textual representation, such as 'Q1' or '4th quarter'.
*
* This returns the textual name used to identify the quarter-of-year,
* suitable for presentation to the user.
* The parameters control the style of the returned text and the locale.
*
* 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 quarter-of-year, not null
*/
public String getDisplayName(TextStyle style, Locale locale) {
return new DateTimeFormatterBuilder().appendText(QUARTER_OF_YEAR, style).toFormatter(locale).format(this);
}
//-----------------------------------------------------------------------
/**
* Checks if the specified field is supported.
*
* This checks if this quarter-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.
*
* If the field is {@link IsoFields#QUARTER_OF_YEAR QUARTER_OF_YEAR} then
* this method returns true.
* All {@code ChronoField} instances will return false.
*
* 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 quarter-of-year, false if not
*/
@Override
public boolean isSupported(TemporalField field) {
if (field == QUARTER_OF_YEAR) {
return true;
} else if (field instanceof ChronoField) {
return false;
}
return field != null && field.isSupportedBy(this);
}
/**
* Gets the range of valid values for the specified field.
*
* The range object expresses the minimum and maximum valid values for a field.
* This quarter 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.
*
* If the field is {@link IsoFields#QUARTER_OF_YEAR QUARTER_OF_YEAR} then the
* range of the quarter-of-year, from 1 to 4, will be returned.
* All {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
*
* 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 == QUARTER_OF_YEAR) {
return field.range();
} else if (field instanceof ChronoField) {
throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
return TemporalAccessor.super.range(field);
}
/**
* Gets the value of the specified field from this quarter-of-year as an {@code int}.
*
* This queries this quarter for the value for 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.
*
* If the field is {@link IsoFields#QUARTER_OF_YEAR QUARTER_OF_YEAR} then the
* value of the quarter-of-year, from 1 to 4, will be returned.
* All {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
*
* 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 == QUARTER_OF_YEAR) {
return getValue();
} else if (field instanceof ChronoField) {
throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
return TemporalAccessor.super.get(field);
}
/**
* Gets the value of the specified field from this quarter-of-year as a {@code long}.
*
* This queries this quarter for the value for 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.
*
* If the field is {@link IsoFields#QUARTER_OF_YEAR QUARTER_OF_YEAR} then the
* value of the quarter-of-year, from 1 to 4, will be returned.
* All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
*
* 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 == QUARTER_OF_YEAR) {
return getValue();
} else if (field instanceof ChronoField) {
throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
return field.getFrom(this);
}
//-----------------------------------------------------------------------
/**
* Returns the quarter that is the specified number of quarters after this one.
*
* The calculation rolls around the end of the year from Q4 to Q1.
* The specified period may be negative.
*
* This instance is immutable and unaffected by this method call.
*
* @param quarters the quarters to add, positive or negative
* @return the resulting quarter, not null
*/
public Quarter plus(long quarters) {
int amount = (int) quarters % 4;
return values()[(ordinal() + (amount + 4)) % 4];
}
/**
* Returns the quarter that is the specified number of quarters before this one.
*
* The calculation rolls around the start of the year from Q1 to Q4.
* The specified period may be negative.
*
* This instance is immutable and unaffected by this method call.
*
* @param quarters the quarters to subtract, positive or negative
* @return the resulting quarter, not null
*/
public Quarter minus(long quarters) {
return plus(-(quarters % 4));
}
//-----------------------------------------------------------------------
/**
* Gets the length of this quarter in days.
*
* This takes a flag to determine whether to return the length for a leap year or not.
*
* Q1 has 90 in a standard year and 91 days in a leap year.
* Q2 has 91 days.
* Q3 and Q4 have 92 days.
*
* @param leapYear true if the length is required for a leap year
* @return the length of this month in days, from 90 to 92
*/
public int length(boolean leapYear) {
switch (this) {
case Q1:
return (leapYear ? 91 : 90);
case Q2:
return 91;
default:
return 92;
}
}
//-----------------------------------------------------------------------
/**
* Gets the first of the three months that this quarter refers to.
*
* Q1 will return January.
* To obtain the other two months of the quarter, simply use {@link Month#plus(long)}
* on the returned month.
*
* @return the first month in the quarter, not null
*/
public Month firstMonth() {
switch (this) {
case Q1:
return Month.JANUARY;
case Q2:
return Month.APRIL;
case Q3:
return Month.JULY;
case Q4:
return Month.OCTOBER;
default:
throw new IllegalStateException("Unreachable");
}
}
//-----------------------------------------------------------------------
/**
* Queries this quarter-of-year using the specified query.
*
* This queries this quarter-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.
*
* 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
* This returns a temporal object of the same observable type as the input
* with the quarter-of-year changed to be the same as this.
*
* The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)}
* passing {@link IsoFields#QUARTER_OF_YEAR} as the field.
* If the specified temporal object does not use the ISO calendar system then
* a {@code DateTimeException} is thrown.
*
* In most cases, it is clearer to reverse the calling pattern by using
* {@link Temporal#with(TemporalAdjuster)}:
*
* For example, given a date in May, the following are output:
*
* 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(QUARTER_OF_YEAR, getValue());
}
}
threeten-extra-1.5.0/src/main/java/org/threeten/extra/Seconds.java 0000664 0000000 0000000 00000057760 13434511741 0025123 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2007-present, 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 org.threeten.extra;
import static java.time.temporal.ChronoUnit.SECONDS;
import java.io.Serializable;
import java.time.DateTimeException;
import java.time.Duration;
import java.time.format.DateTimeParseException;
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.Collections;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.joda.convert.FromString;
import org.joda.convert.ToString;
/**
* A second-based amount of time, such as '8 seconds'.
*
* This class models a quantity or amount of time in terms of seconds.
* It is a type-safe way of representing a number of seconds in an application.
* Note that {@link Duration} also models time in terms of seconds, but that
* class allows nanoseconds, which this class does not.
*
* The model is of a directed amount, meaning that the amount may be negative.
*
*
* This class must be treated as a value type. Do not synchronize, rely on the
* identity hash code or use the distinction between equals() and ==.
*/
public final class Seconds
implements TemporalAmount, Comparable
* The resulting amount will have the specified seconds.
*
* @param seconds the number of seconds, positive or negative
* @return the number of seconds, not null
*/
public static Seconds of(int seconds) {
if (seconds == 0) {
return ZERO;
}
return new Seconds(seconds);
}
/**
* Obtains a {@code Seconds} representing the number of seconds
* equivalent to a number of hours.
*
* The resulting amount will be second-based, with the number of seconds
* equal to the number of hours multiplied by 3600.
*
* @param hours the number of hours, positive or negative
* @return the amount with the input hours converted to seconds, not null
* @throws ArithmeticException if numeric overflow occurs
*/
public static Seconds ofHours(int hours) {
if (hours == 0) {
return ZERO;
}
return new Seconds(Math.multiplyExact(hours, SECONDS_PER_HOUR));
}
/**
* Obtains a {@code Seconds} representing the number of seconds
* equivalent to a number of hours.
*
* The resulting amount will be second-based, with the number of seconds
* equal to the number of minutes multiplied by 60.
*
* @param minutes the number of minutes, positive or negative
* @return the amount with the input minutes converted to seconds, not null
* @throws ArithmeticException if numeric overflow occurs
*/
public static Seconds ofMinutes(int minutes) {
if (minutes == 0) {
return ZERO;
}
return new Seconds(Math.multiplyExact(minutes, SECONDS_PER_MINUTE));
}
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code Seconds} from a temporal amount.
*
* This obtains an instance 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 Seconds}.
*
* The result is calculated by looping around each unit in the specified amount.
* Each amount is converted to seconds using {@link Temporals#convertAmount}.
* If the conversion yields a remainder, an exception is thrown.
* If the amount is zero, the unit is ignored.
*
* @param amount the temporal amount to convert, not null
* @return the equivalent amount, not null
* @throws DateTimeException if unable to convert to a {@code Seconds}
* @throws ArithmeticException if numeric overflow occurs
*/
public static Seconds from(TemporalAmount amount) {
if (amount instanceof Seconds) {
return (Seconds) amount;
}
Objects.requireNonNull(amount, "amount");
int seconds = 0;
for (TemporalUnit unit : amount.getUnits()) {
long value = amount.get(unit);
if (value != 0) {
long[] converted = Temporals.convertAmount(value, unit, SECONDS);
if (converted[1] != 0) {
throw new DateTimeException(
"Amount could not be converted to a whole number of seconds: " + value + " " + unit);
}
seconds = Math.addExact(seconds, Math.toIntExact(converted[0]));
}
}
return of(seconds);
}
//-----------------------------------------------------------------------
/**
* Obtains a {@code Seconds} from a text string such as {@code PTnS}.
*
* This will parse the string produced by {@code toString()} and other
* related formats based on ISO-8601 {@code PnDTnHnMnS}.
*
* The string starts with an optional sign, denoted by the ASCII negative
* or positive symbol. If negative, the whole amount is negated.
* The ASCII letter "P" is next in upper or lower case.
* There are four sections consisting of a number and a suffix.
* There is one section for days suffixed by "D",
* followed by one section for hours suffixed by "H",
* followed by one section for minutes suffixed by "M",
* followed by one section for seconds suffixed by "S".
* At least one section must be present.
* If the hours, minutes or seconds section is present it must be prefixed by "T".
* If the hours, minutes or seconds section is omitted the "T" must be omitted.
* Letters must be in ASCII upper or lower case.
* 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}.
*
* The leading plus/minus sign, and negative values for days, hours, minutes
* and seconds are not part of the ISO-8601 standard.
*
* For example, the following are valid inputs:
*
* The start temporal is included, but the end temporal is not.
* The result of this method can be negative if the end is before the start.
*
* @param startInclusive the start temporal, inclusive, not null
* @param endExclusive the end temporal, exclusive, not null
* @return the number of seconds between the start and end temporals, not null
*/
public static Seconds between(Temporal startInclusive, Temporal endExclusive) {
return of(Math.toIntExact(SECONDS.between(startInclusive, endExclusive)));
}
//-----------------------------------------------------------------------
/**
* Constructs an instance using a specific number of seconds.
*
* @param seconds the amount of seconds
*/
private Seconds(int seconds) {
this.seconds = seconds;
}
/**
* Resolves singletons.
*
* @return the singleton instance
*/
private Object readResolve() {
return Seconds.of(seconds);
}
//-----------------------------------------------------------------------
/**
* Gets the value of the requested unit.
*
* This returns a value for the supported unit - {@link ChronoUnit#SECONDS SECONDS}.
* 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 UnsupportedTemporalTypeException if the unit is not supported
*/
@Override
public long get(TemporalUnit unit) {
if (unit == SECONDS) {
return seconds;
}
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
}
/**
* Gets the set of units supported by this amount.
*
* The single supported unit is {@link ChronoUnit#SECONDS SECONDS}.
*
* This set can be used in conjunction with {@link #get(TemporalUnit)} to
* access the entire state of the amount.
*
* @return a list containing the seconds unit, not null
*/
@Override
public List
* The parameter is converted using {@link Seconds#from(TemporalAmount)}.
*
* This instance is immutable and unaffected by this method call.
*
* @param amountToAdd the amount to add, not null
* @return a {@code Seconds} based on this instance with the requested amount added, not null
* @throws DateTimeException if the specified amount contains an invalid unit
* @throws ArithmeticException if numeric overflow occurs
*/
public Seconds plus(TemporalAmount amountToAdd) {
return plus(Seconds.from(amountToAdd).getAmount());
}
/**
* Returns a copy of this amount with the specified number of seconds added.
*
* This instance is immutable and unaffected by this method call.
*
* @param seconds the amount of seconds to add, may be negative
* @return a {@code Seconds} based on this instance with the requested amount added, not null
* @throws ArithmeticException if the result overflows an int
*/
public Seconds plus(int seconds) {
if (seconds == 0) {
return this;
}
return of(Math.addExact(this.seconds, seconds));
}
//-----------------------------------------------------------------------
/**
* Returns a copy of this amount with the specified amount subtracted.
*
* The parameter is converted using {@link Seconds#from(TemporalAmount)}.
*
* This instance is immutable and unaffected by this method call.
*
* @param amountToAdd the amount to add, not null
* @return a {@code Seconds} based on this instance with the requested amount subtracted, not null
* @throws DateTimeException if the specified amount contains an invalid unit
* @throws ArithmeticException if numeric overflow occurs
*/
public Seconds minus(TemporalAmount amountToAdd) {
return minus(Seconds.from(amountToAdd).getAmount());
}
/**
* Returns a copy of this amount with the specified number of seconds subtracted.
*
* This instance is immutable and unaffected by this method call.
*
* @param seconds the amount of seconds to add, may be negative
* @return a {@code Seconds} based on this instance with the requested amount subtracted, not null
* @throws ArithmeticException if the result overflows an int
*/
public Seconds minus(int seconds) {
if (seconds == 0) {
return this;
}
return of(Math.subtractExact(this.seconds, seconds));
}
//-----------------------------------------------------------------------
/**
* Returns an instance with the amount multiplied by the specified scalar.
*
* This instance is immutable and unaffected by this method call.
*
* @param scalar the scalar to multiply by, not null
* @return the amount multiplied by the specified scalar, not null
* @throws ArithmeticException if numeric overflow occurs
*/
public Seconds multipliedBy(int scalar) {
if (scalar == 1) {
return this;
}
return of(Math.multiplyExact(seconds, scalar));
}
/**
* Returns an instance with the amount divided by the specified divisor.
*
* The calculation uses integer division, thus 3 divided by 2 is 1.
*
* This instance is immutable and unaffected by this method call.
*
* @param divisor the amount to divide by, may be negative
* @return the amount divided by the specified divisor, not null
* @throws ArithmeticException if the divisor is zero
*/
public Seconds dividedBy(int divisor) {
if (divisor == 1) {
return this;
}
return of(seconds / divisor);
}
/**
* Returns an instance with the amount negated.
*
* This instance is immutable and unaffected by this method call.
*
* @return the negated amount, not null
* @throws ArithmeticException if numeric overflow occurs, which only happens if
* the amount is {@code Long.MIN_VALUE}
*/
public Seconds negated() {
return multipliedBy(-1);
}
/**
* Returns a copy of this duration with a positive length.
*
* This method returns a positive duration by effectively removing the sign from any negative total length.
*
* This instance is immutable and unaffected by this method call.
*
* @return the absolute amount, not null
* @throws ArithmeticException if numeric overflow occurs, which only happens if
* the amount is {@code Long.MIN_VALUE}
*/
public Seconds abs() {
return seconds < 0 ? negated() : this;
}
//-------------------------------------------------------------------------
/**
* Gets the number of seconds as a {@code Duration}.
*
* This returns a duration with the same number of seconds.
*
* @return the equivalent duration, not null
*/
public Duration toDuration() {
return Duration.ofSeconds(seconds);
}
//-----------------------------------------------------------------------
/**
* Adds this amount to the specified temporal object.
*
* This returns a temporal object of the same observable type as the input
* with this amount added.
*
* In most cases, it is clearer to reverse the calling pattern by using
* {@link Temporal#plus(TemporalAmount)}.
*
* Only non-zero amounts will be added.
*
* 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 UnsupportedTemporalTypeException if the SECONDS unit is not supported
* @throws ArithmeticException if numeric overflow occurs
*/
@Override
public Temporal addTo(Temporal temporal) {
if (seconds != 0) {
temporal = temporal.plus(seconds, SECONDS);
}
return temporal;
}
/**
* Subtracts this amount from the specified temporal object.
*
* This returns a temporal object of the same observable type as the input
* with this amount subtracted.
*
* In most cases, it is clearer to reverse the calling pattern by using
* {@link Temporal#minus(TemporalAmount)}.
*
* Only non-zero amounts will be subtracted.
*
* 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 UnsupportedTemporalTypeException if the SECONDS unit is not supported
* @throws ArithmeticException if numeric overflow occurs
*/
@Override
public Temporal subtractFrom(Temporal temporal) {
if (seconds != 0) {
temporal = temporal.minus(seconds, SECONDS);
}
return temporal;
}
//-----------------------------------------------------------------------
/**
* Compares this amount to the specified {@code Seconds}.
*
* The comparison is based on the total length of the amounts.
* It is "consistent with equals", as defined by {@link Comparable}.
*
* @param otherAmount the other amount, not null
* @return the comparator value, negative if less, positive if greater
*/
@Override
public int compareTo(Seconds otherAmount) {
int thisValue = this.seconds;
int otherValue = otherAmount.seconds;
return Integer.compare(thisValue, otherValue);
}
//-----------------------------------------------------------------------
/**
* Checks if this amount is equal to the specified {@code Seconds}.
*
* The comparison is based on the total length of the durations.
*
* @param otherAmount the other amount, null returns false
* @return true if the other amount is equal to this one
*/
@Override
public boolean equals(Object otherAmount) {
if (this == otherAmount) {
return true;
}
if (otherAmount instanceof Seconds) {
Seconds other = (Seconds) otherAmount;
return this.seconds == other.seconds;
}
return false;
}
/**
* A hash code for this amount.
*
* @return a suitable hash code
*/
@Override
public int hashCode() {
return seconds;
}
//-----------------------------------------------------------------------
/**
* Returns a string representation of the number of seconds.
* This will be in the format 'PTnS' where n is the number of seconds.
*
* @return the number of seconds in ISO-8601 string format
*/
@Override
@ToString
public String toString() {
return "PT" + seconds + "S";
}
}
threeten-extra-1.5.0/src/main/java/org/threeten/extra/Temporals.java 0000664 0000000 0000000 00000036163 13434511741 0025465 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2007-present, 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 org.threeten.extra;
import static java.time.temporal.ChronoField.DAY_OF_WEEK;
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.WEEKS;
import java.text.ParsePosition;
import java.time.DateTimeException;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.temporal.ChronoUnit;
import java.time.temporal.IsoFields;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAdjuster;
import java.time.temporal.TemporalQuery;
import java.time.temporal.TemporalUnit;
import java.time.temporal.UnsupportedTemporalTypeException;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
* Additional utilities for working with temporal classes.
*
* This includes:
*
* Some territories have weekends that do not consist of Saturday and Sunday.
* No implementation is supplied to support this, however an adjuster
* can be easily written to do so.
*
* @return the next working day adjuster, not null
*/
public static TemporalAdjuster nextWorkingDay() {
return Adjuster.NEXT_WORKING;
}
/**
* Returns an adjuster that returns the next working day or same day if already working day, ignoring Saturday and Sunday.
*
* Some territories have weekends that do not consist of Saturday and Sunday.
* No implementation is supplied to support this, however an adjuster
* can be easily written to do so.
*
* @return the next working day or same adjuster, not null
*/
public static TemporalAdjuster nextWorkingDayOrSame() {
return Adjuster.NEXT_WORKING_OR_SAME;
}
/**
* Returns an adjuster that returns the previous working day, ignoring Saturday and Sunday.
*
* Some territories have weekends that do not consist of Saturday and Sunday.
* No implementation is supplied to support this, however an adjuster
* can be easily written to do so.
*
* @return the previous working day adjuster, not null
*/
public static TemporalAdjuster previousWorkingDay() {
return Adjuster.PREVIOUS_WORKING;
}
/**
* Returns an adjuster that returns the previous working day or same day if already working day, ignoring Saturday and Sunday.
*
* Some territories have weekends that do not consist of Saturday and Sunday.
* No implementation is supplied to support this, however an adjuster
* can be easily written to do so.
*
* @return the previous working day or same adjuster, not null
*/
public static TemporalAdjuster previousWorkingDayOrSame() {
return Adjuster.PREVIOUS_WORKING_OR_SAME;
}
//-----------------------------------------------------------------------
/**
* Enum implementing the adjusters.
*/
private static enum Adjuster implements TemporalAdjuster {
/** Next working day adjuster. */
NEXT_WORKING {
@Override
public Temporal adjustInto(Temporal temporal) {
int dow = temporal.get(DAY_OF_WEEK);
switch (dow) {
case 6: // Saturday
return temporal.plus(2, DAYS);
case 5: // Friday
return temporal.plus(3, DAYS);
default:
return temporal.plus(1, DAYS);
}
}
},
/** Previous working day adjuster. */
PREVIOUS_WORKING {
@Override
public Temporal adjustInto(Temporal temporal) {
int dow = temporal.get(DAY_OF_WEEK);
switch (dow) {
case 1: // Monday
return temporal.minus(3, DAYS);
case 7: // Sunday
return temporal.minus(2, DAYS);
default:
return temporal.minus(1, DAYS);
}
}
},
/** Next working day or same adjuster. */
NEXT_WORKING_OR_SAME {
@Override
public Temporal adjustInto(Temporal temporal) {
int dow = temporal.get(DAY_OF_WEEK);
switch (dow) {
case 6: // Saturday
return temporal.plus(2, DAYS);
case 7: // Sunday
return temporal.plus(1, DAYS);
default:
return temporal;
}
}
},
/** Previous working day or same adjuster. */
PREVIOUS_WORKING_OR_SAME {
@Override
public Temporal adjustInto(Temporal temporal) {
int dow = temporal.get(DAY_OF_WEEK);
switch (dow) {
case 6: //Saturday
return temporal.minus(1, DAYS);
case 7: // Sunday
return temporal.minus(2, DAYS);
default:
return temporal;
}
}
}
}
//-------------------------------------------------------------------------
/**
* Parses the text using one of the formatters.
*
* This will try each formatter in turn, attempting to fully parse the specified text.
* The temporal query is typically a method reference to a {@code from(TemporalAccessor)} method.
* For example:
*
* This handles the seven units declared in {@code TimeUnit}.
*
* @param unit the unit to convert, not null
* @return the converted unit, not null
*/
public static ChronoUnit chronoUnit(TimeUnit unit) {
Objects.requireNonNull(unit, "unit");
switch (unit) {
case NANOSECONDS:
return ChronoUnit.NANOS;
case MICROSECONDS:
return ChronoUnit.MICROS;
case MILLISECONDS:
return ChronoUnit.MILLIS;
case SECONDS:
return ChronoUnit.SECONDS;
case MINUTES:
return ChronoUnit.MINUTES;
case HOURS:
return ChronoUnit.HOURS;
case DAYS:
return ChronoUnit.DAYS;
default:
throw new IllegalArgumentException("Unknown TimeUnit constant");
}
}
/**
* Converts a {@code ChronoUnit} to a {@code TimeUnit}.
*
* This handles the seven units declared in {@code TimeUnit}.
*
* @param unit the unit to convert, not null
* @return the converted unit, not null
* @throws IllegalArgumentException if the unit cannot be converted
*/
public static TimeUnit timeUnit(ChronoUnit unit) {
Objects.requireNonNull(unit, "unit");
switch (unit) {
case NANOS:
return TimeUnit.NANOSECONDS;
case MICROS:
return TimeUnit.MICROSECONDS;
case MILLIS:
return TimeUnit.MILLISECONDS;
case SECONDS:
return TimeUnit.SECONDS;
case MINUTES:
return TimeUnit.MINUTES;
case HOURS:
return TimeUnit.HOURS;
case DAYS:
return TimeUnit.DAYS;
default:
throw new IllegalArgumentException("ChronoUnit cannot be converted to TimeUnit: " + unit);
}
}
//-------------------------------------------------------------------------
/**
* Converts an amount from one unit to another.
*
* This works on the units in {@code ChronoUnit} and {@code IsoFields}.
* The {@code DAYS} and {@code WEEKS} units are handled as exact multiple of 24 hours.
* The {@code ERAS} and {@code FOREVER} units are not supported.
*
* @param amount the input amount in terms of the {@code fromUnit}
* @param fromUnit the unit to convert from, not null
* @param toUnit the unit to convert to, not null
* @return the conversion array,
* element 0 is the signed whole number,
* element 1 is the signed remainder in terms of the input unit,
* not null
* @throws DateTimeException if the units cannot be converted
* @throws UnsupportedTemporalTypeException if the units are not supported
* @throws ArithmeticException if numeric overflow occurs
*/
public static long[] convertAmount(long amount, TemporalUnit fromUnit, TemporalUnit toUnit) {
Objects.requireNonNull(fromUnit, "fromUnit");
Objects.requireNonNull(toUnit, "toUnit");
validateUnit(fromUnit);
validateUnit(toUnit);
if (fromUnit.equals(toUnit)) {
return new long[] {amount, 0};
}
// precise-based
if (isPrecise(fromUnit) && isPrecise(toUnit)) {
long fromNanos = fromUnit.getDuration().toNanos();
long toNanos = toUnit.getDuration().toNanos();
if (fromNanos > toNanos) {
long multiple = fromNanos / toNanos;
return new long[] {Math.multiplyExact(amount, multiple), 0};
} else {
long multiple = toNanos / fromNanos;
return new long[] {amount / multiple, amount % multiple};
}
}
// month-based
int fromMonthFactor = monthMonthFactor(fromUnit, fromUnit, toUnit);
int toMonthFactor = monthMonthFactor(toUnit, fromUnit, toUnit);
if (fromMonthFactor > toMonthFactor) {
long multiple = fromMonthFactor / toMonthFactor;
return new long[] {Math.multiplyExact(amount, multiple), 0};
} else {
long multiple = toMonthFactor / fromMonthFactor;
return new long[] {amount / multiple, amount % multiple};
}
}
private static void validateUnit(TemporalUnit unit) {
if (unit instanceof ChronoUnit) {
if (unit.equals(ERAS) || unit.equals(FOREVER)) {
throw new UnsupportedTemporalTypeException("Unsupported TemporalUnit: " + unit);
}
} else if (unit.equals(IsoFields.QUARTER_YEARS) == false) {
throw new UnsupportedTemporalTypeException("Unsupported TemporalUnit: " + unit);
}
}
private static boolean isPrecise(TemporalUnit unit) {
return unit instanceof ChronoUnit && ((ChronoUnit) unit).compareTo(WEEKS) <= 0;
}
private static int monthMonthFactor(TemporalUnit unit, TemporalUnit fromUnit, TemporalUnit toUnit) {
if (unit instanceof ChronoUnit) {
switch ((ChronoUnit) unit) {
case MONTHS:
return 1;
case YEARS:
return 12;
case DECADES:
return 120;
case CENTURIES:
return 1200;
case MILLENNIA:
return 12000;
default:
throw new DateTimeException(
String.format("Unable to convert between units: %s to %s", fromUnit, toUnit));
}
}
return 3; // quarters
}
}
threeten-extra-1.5.0/src/main/java/org/threeten/extra/Weeks.java 0000664 0000000 0000000 00000046521 13434511741 0024574 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2007-present, 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 org.threeten.extra;
import static java.time.temporal.ChronoUnit.WEEKS;
import java.io.Serializable;
import java.time.DateTimeException;
import java.time.Period;
import java.time.format.DateTimeParseException;
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.Collections;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.joda.convert.FromString;
import org.joda.convert.ToString;
/**
* A week-based amount of time, such as '12 weeks'.
*
* This class models a quantity or amount of time in terms of weeks.
* It is a type-safe way of representing a number of weeks in an application.
*
* The model is of a directed amount, meaning that the amount may be negative.
*
*
* This class must be treated as a value type. Do not synchronize, rely on the
* identity hash code or use the distinction between equals() and ==.
*/
public final class Weeks
implements TemporalAmount, Comparable
* The resulting amount will have the specified weeks.
*
* @param weeks the number of weeks, positive or negative
* @return the number of weeks, not null
*/
public static Weeks of(int weeks) {
if (weeks == 0) {
return ZERO;
} else if (weeks == 1) {
return ONE;
}
return new Weeks(weeks);
}
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code Weeks} from a temporal amount.
*
* This obtains an instance 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 Weeks}.
*
* The result is calculated by looping around each unit in the specified amount.
* Each amount is converted to weeks using {@link Temporals#convertAmount}.
* If the conversion yields a remainder, an exception is thrown.
* If the amount is zero, the unit is ignored.
* For example, "7 days" can be converted to weeks but "6 days" cannot.
*
* @param amount the temporal amount to convert, not null
* @return the equivalent amount, not null
* @throws DateTimeException if unable to convert to a {@code Weeks}
* @throws ArithmeticException if numeric overflow occurs
*/
public static Weeks from(TemporalAmount amount) {
if (amount instanceof Weeks) {
return (Weeks) amount;
}
Objects.requireNonNull(amount, "amount");
int weeks = 0;
for (TemporalUnit unit : amount.getUnits()) {
long value = amount.get(unit);
if (value != 0) {
long[] converted = Temporals.convertAmount(value, unit, WEEKS);
if (converted[1] != 0) {
throw new DateTimeException(
"Amount could not be converted to a whole number of weeks: " + value + " " + unit);
}
weeks = Math.addExact(weeks, Math.toIntExact(converted[0]));
}
}
return of(weeks);
}
//-----------------------------------------------------------------------
/**
* Obtains a {@code Weeks} from a text string such as {@code PnW}.
*
* This will parse the string produced by {@code toString()} which is
* based on the ISO-8601 period formats {@code PnW}.
*
* The string starts with an optional sign, denoted by the ASCII negative
* or positive symbol. If negative, the whole amount is negated.
* The ASCII letter "P" is next in upper or lower case.
* The ASCII integer amount is next, which may be negative.
* The ASCII letter "W" is next in upper or lower case.
*
* The leading plus/minus sign, and negative values for weeks are
* not part of the ISO-8601 standard.
*
* For example, the following are valid inputs:
*
* The start date is included, but the end date is not.
* The result of this method can be negative if the end is before the start.
*
* @param startDateInclusive the start date, inclusive, not null
* @param endDateExclusive the end date, exclusive, not null
* @return the number of weeks between this date and the end date, not null
*/
public static Weeks between(Temporal startDateInclusive, Temporal endDateExclusive) {
return of(Math.toIntExact(WEEKS.between(startDateInclusive, endDateExclusive)));
}
//-----------------------------------------------------------------------
/**
* Constructs an instance using a specific number of weeks.
*
* @param weeks the weeks to use
*/
private Weeks(int weeks) {
super();
this.weeks = weeks;
}
/**
* Resolves singletons.
*
* @return the singleton instance
*/
private Object readResolve() {
return Weeks.of(weeks);
}
//-----------------------------------------------------------------------
/**
* Gets the value of the requested unit.
*
* This returns a value for the supported unit - {@link ChronoUnit#WEEKS WEEKS}.
* 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 UnsupportedTemporalTypeException if the unit is not supported
*/
@Override
public long get(TemporalUnit unit) {
if (unit == WEEKS) {
return weeks;
}
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
}
/**
* Gets the set of units supported by this amount.
*
* The single supported unit is {@link ChronoUnit#WEEKS WEEKS}.
*
* This set can be used in conjunction with {@link #get(TemporalUnit)}
* to access the entire state of the amount.
*
* @return a list containing the weeks unit, not null
*/
@Override
public List
* The parameter is converted using {@link Weeks#from(TemporalAmount)}.
*
* This instance is immutable and unaffected by this method call.
*
* @param amountToAdd the amount to add, not null
* @return a {@code Weeks} based on this instance with the requested amount added, not null
* @throws DateTimeException if the specified amount contains an invalid unit
* @throws ArithmeticException if numeric overflow occurs
*/
public Weeks plus(TemporalAmount amountToAdd) {
return plus(Weeks.from(amountToAdd).getAmount());
}
/**
* Returns a copy of this amount with the specified number of weeks added.
*
* This instance is immutable and unaffected by this method call.
*
* @param weeks the amount of weeks to add, may be negative
* @return a {@code Weeks} based on this instance with the requested amount added, not null
* @throws ArithmeticException if the result overflows an int
*/
public Weeks plus(int weeks) {
if (weeks == 0) {
return this;
}
return of(Math.addExact(this.weeks, weeks));
}
//-----------------------------------------------------------------------
/**
* Returns a copy of this amount with the specified amount subtracted.
*
* The parameter is converted using {@link Weeks#from(TemporalAmount)}.
*
* This instance is immutable and unaffected by this method call.
*
* @param amountToAdd the amount to add, not null
* @return a {@code Weeks} based on this instance with the requested amount subtracted, not null
* @throws DateTimeException if the specified amount contains an invalid unit
* @throws ArithmeticException if numeric overflow occurs
*/
public Weeks minus(TemporalAmount amountToAdd) {
return minus(Weeks.from(amountToAdd).getAmount());
}
/**
* Returns a copy of this amount with the specified number of weeks subtracted.
*
* This instance is immutable and unaffected by this method call.
*
* @param weeks the amount of weeks to add, may be negative
* @return a {@code Weeks} based on this instance with the requested amount subtracted, not null
* @throws ArithmeticException if the result overflows an int
*/
public Weeks minus(int weeks) {
if (weeks == 0) {
return this;
}
return of(Math.subtractExact(this.weeks, weeks));
}
//-----------------------------------------------------------------------
/**
* Returns an instance with the amount multiplied by the specified scalar.
*
* This instance is immutable and unaffected by this method call.
*
* @param scalar the scalar to multiply by, not null
* @return the amount multiplied by the specified scalar, not null
* @throws ArithmeticException if numeric overflow occurs
*/
public Weeks multipliedBy(int scalar) {
if (scalar == 1) {
return this;
}
return of(Math.multiplyExact(weeks, scalar));
}
/**
* Returns an instance with the amount divided by the specified divisor.
*
* The calculation uses integer division, thus 3 divided by 2 is 1.
*
* This instance is immutable and unaffected by this method call.
*
* @param divisor the amount to divide by, may be negative
* @return the amount divided by the specified divisor, not null
* @throws ArithmeticException if the divisor is zero
*/
public Weeks dividedBy(int divisor) {
if (divisor == 1) {
return this;
}
return of(weeks / divisor);
}
/**
* Returns an instance with the amount negated.
*
* This instance is immutable and unaffected by this method call.
*
* @return the negated amount, not null
* @throws ArithmeticException if numeric overflow occurs, which only happens if
* the amount is {@code Long.MIN_VALUE}
*/
public Weeks negated() {
return multipliedBy(-1);
}
/**
* Returns a copy of this duration with a positive length.
*
* This method returns a positive duration by effectively removing the sign from any negative total length.
*
* This instance is immutable and unaffected by this method call.
*
* @return the absolute amount, not null
* @throws ArithmeticException if numeric overflow occurs, which only happens if
* the amount is {@code Long.MIN_VALUE}
*/
public Weeks abs() {
return weeks < 0 ? negated() : this;
}
//-------------------------------------------------------------------------
/**
* Gets the number of weeks as a {@code Period}.
*
* This returns a period with the same number of weeks.
*
* @return the equivalent period, not null
*/
public Period toPeriod() {
return Period.ofWeeks(weeks);
}
//-----------------------------------------------------------------------
/**
* Adds this amount to the specified temporal object.
*
* This returns a temporal object of the same observable type as the input
* with this amount added.
*
* In most cases, it is clearer to reverse the calling pattern by using
* {@link Temporal#plus(TemporalAmount)}.
*
* Only non-zero amounts will be added.
*
* 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 UnsupportedTemporalTypeException if the WEEKS unit is not supported
* @throws ArithmeticException if numeric overflow occurs
*/
@Override
public Temporal addTo(Temporal temporal) {
if (weeks != 0) {
temporal = temporal.plus(weeks, WEEKS);
}
return temporal;
}
/**
* Subtracts this amount from the specified temporal object.
*
* This returns a temporal object of the same observable type as the input
* with this amount subtracted.
*
* In most cases, it is clearer to reverse the calling pattern by using
* {@link Temporal#minus(TemporalAmount)}.
*
* Only non-zero amounts will be subtracted.
*
* 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 UnsupportedTemporalTypeException if the WEEKS unit is not supported
* @throws ArithmeticException if numeric overflow occurs
*/
@Override
public Temporal subtractFrom(Temporal temporal) {
if (weeks != 0) {
temporal = temporal.minus(weeks, WEEKS);
}
return temporal;
}
//-----------------------------------------------------------------------
/**
* Compares this amount to the specified {@code Weeks}.
*
* The comparison is based on the total length of the amounts.
* It is "consistent with equals", as defined by {@link Comparable}.
*
* @param otherAmount the other amount, not null
* @return the comparator value, negative if less, positive if greater
*/
@Override
public int compareTo(Weeks otherAmount) {
int thisValue = this.weeks;
int otherValue = otherAmount.weeks;
return Integer.compare(thisValue, otherValue);
}
//-----------------------------------------------------------------------
/**
* Checks if this amount is equal to the specified {@code Weeks}.
*
* The comparison is based on the total length of the durations.
*
* @param otherAmount the other amount, null returns false
* @return true if the other amount is equal to this one
*/
@Override
public boolean equals(Object otherAmount) {
if (this == otherAmount) {
return true;
}
if (otherAmount instanceof Weeks) {
Weeks other = (Weeks) otherAmount;
return this.weeks == other.weeks;
}
return false;
}
/**
* A hash code for this amount.
*
* @return a suitable hash code
*/
@Override
public int hashCode() {
return weeks;
}
//-----------------------------------------------------------------------
/**
* Returns a string representation of the number of weeks.
* This will be in the format 'PnW' where n is the number of weeks.
*
* @return the number of weeks in ISO-8601 string format
*/
@Override
@ToString
public String toString() {
return "P" + weeks + "W";
}
}
threeten-extra-1.5.0/src/main/java/org/threeten/extra/YearQuarter.java 0000664 0000000 0000000 00000153427 13434511741 0025766 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2007-present, 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 org.threeten.extra;
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 static java.time.temporal.IsoFields.DAY_OF_QUARTER;
import static java.time.temporal.IsoFields.QUARTER_OF_YEAR;
import static java.time.temporal.IsoFields.QUARTER_YEARS;
import java.io.Serializable;
import java.time.Clock;
import java.time.DateTimeException;
import java.time.LocalDate;
import java.time.Month;
import java.time.Period;
import java.time.Year;
import java.time.ZoneId;
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.IsoFields;
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;
import java.util.stream.LongStream;
import java.util.stream.Stream;
import org.joda.convert.FromString;
import org.joda.convert.ToString;
/**
* A year-quarter in the ISO-8601 calendar system, such as {@code 2007-Q2}.
*
* {@code YearQuarter} is an immutable date-time object that represents the combination
* of a year and quarter. Any field that can be derived from a year and quarter can be obtained.
* A quarter is defined by {@link Quarter} and {@link Month#firstMonthOfQuarter()} - Q1, Q2, Q3 and Q4.
* Q1 is January to March, Q2 is April to June, Q3 is July to September and Q4 is October to December.
*
* This class does not store or represent a day, time or time-zone.
* For example, the value "2nd quarter 2007" can be stored in a {@code YearQuarter}.
*
* 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.
* Note that the ISO-8601 standard does not define or refer to quarters.
*
*
* This class must be treated as a value type. Do not synchronize, rely on the
* identity hash code or use the distinction between equals() and ==.
*/
public final class YearQuarter
implements Temporal, TemporalAdjuster, Comparable
* This will query the {@link java.time.Clock#systemDefaultZone() system clock} in the default
* time-zone to obtain the current year-quarter.
* The zone and offset will be set based on the time-zone in the clock.
*
* Using this method will prevent the ability to use an alternate clock for testing
* because the clock is hard-coded.
*
* @return the current year-quarter using the system clock and default time-zone, not null
*/
public static YearQuarter now() {
return now(Clock.systemDefaultZone());
}
/**
* Obtains the current year-quarter from the system clock in the specified time-zone.
*
* This will query the {@link Clock#system(java.time.ZoneId) system clock} to obtain the current year-quarter.
* Specifying the time-zone avoids dependence on the default time-zone.
*
* 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-quarter using the system clock, not null
*/
public static YearQuarter now(ZoneId zone) {
return now(Clock.system(zone));
}
/**
* Obtains the current year-quarter from the specified clock.
*
* This will query the specified clock to obtain the current year-quarter.
* 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-quarter, not null
*/
public static YearQuarter now(Clock clock) {
final LocalDate now = LocalDate.now(clock); // called once
return YearQuarter.of(now.getYear(), Quarter.from(now.getMonth()));
}
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code YearQuarter} from a year and quarter.
*
* @param year the year to represent, from MIN_YEAR to MAX_YEAR
* @param quarter the quarter-of-year to represent, not null
* @return the year-quarter, not null
* @throws DateTimeException if the year value is invalid
*/
public static YearQuarter of(int year, Quarter quarter) {
YEAR.checkValidValue(year);
Objects.requireNonNull(quarter, "quarter");
return new YearQuarter(year, quarter);
}
/**
* Obtains an instance of {@code YearQuarter} from a year and quarter.
*
* @param year the year to represent, from MIN_YEAR to MAX_YEAR
* @param quarter the quarter-of-year to represent, from 1 to 4
* @return the year-quarter, not null
* @throws DateTimeException if either field value is invalid
*/
public static YearQuarter of(int year, int quarter) {
YEAR.checkValidValue(year);
return new YearQuarter(year, Quarter.of(quarter));
}
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code YearQuarter} from a temporal object.
*
* This obtains a year-quarter 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 YearQuarter}.
*
* The conversion extracts the {@link ChronoField#YEAR YEAR} and
* {@link IsoFields#QUARTER_OF_YEAR QUARTER_OF_YEAR} fields.
* The extraction is only permitted if the temporal object has an ISO
* chronology, or can be converted to a {@code LocalDate}.
*
* This method matches the signature of the functional interface {@link TemporalQuery}
* allowing it to be used in queries via method reference, {@code YearQuarter::from}.
*
* @param temporal the temporal object to convert, not null
* @return the year-quarter, not null
* @throws DateTimeException if unable to convert to a {@code YearQuarter}
*/
public static YearQuarter from(TemporalAccessor temporal) {
if (temporal instanceof YearQuarter) {
return (YearQuarter) temporal;
}
Objects.requireNonNull(temporal, "temporal");
try {
if (IsoChronology.INSTANCE.equals(Chronology.from(temporal)) == false) {
temporal = LocalDate.from(temporal);
}
// need to use getLong() as JDK Parsed class get() doesn't work properly
int year = Math.toIntExact(temporal.getLong(YEAR));
int qoy = Math.toIntExact(temporal.getLong(QUARTER_OF_YEAR));
return of(year, qoy);
} catch (DateTimeException ex) {
throw new DateTimeException("Unable to obtain YearQuarter from TemporalAccessor: " +
temporal + " of type " + temporal.getClass().getName(), ex);
}
}
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code YearQuarter} from a text string such as {@code 2007-Q2}.
*
* The string must represent a valid year-quarter.
* The format must be {@code uuuu-'Q'Q} where the 'Q' is case insensitive.
* 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-Q2", not null
* @return the parsed year-quarter, not null
* @throws DateTimeParseException if the text cannot be parsed
*/
@FromString
public static YearQuarter parse(CharSequence text) {
return parse(text, PARSER);
}
/**
* Obtains an instance of {@code YearQuarter} from a text string using a specific formatter.
*
* The text is parsed using the formatter, returning a year-quarter.
*
* @param text the text to parse, not null
* @param formatter the formatter to use, not null
* @return the parsed year-quarter, not null
* @throws DateTimeParseException if the text cannot be parsed
*/
public static YearQuarter parse(CharSequence text, DateTimeFormatter formatter) {
Objects.requireNonNull(formatter, "formatter");
return formatter.parse(text, YearQuarter::from);
}
//-----------------------------------------------------------------------
/**
* Constructor.
*
* @param year the year to represent, validated from MIN_YEAR to MAX_YEAR
* @param quarter the quarter-of-year to represent, validated not null
*/
private YearQuarter(int year, Quarter quarter) {
this.year = year;
this.quarter = quarter;
}
/**
* Validates the input.
*
* @return the valid object, not null
*/
private Object readResolve() {
return of(year, quarter);
}
/**
* Returns a copy of this year-quarter with the new year and quarter, 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 newQuarter the quarter-of-year to represent, validated not null
* @return the year-quarter, not null
*/
private YearQuarter with(int newYear, Quarter newQuarter) {
if (year == newYear && quarter == newQuarter) {
return this;
}
return new YearQuarter(newYear, newQuarter);
}
//-----------------------------------------------------------------------
/**
* Checks if the specified field is supported.
*
* This checks if this year-quarter 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.
*
* If the field is a {@link ChronoField} then the query is implemented here.
* The supported fields are:
*
* 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-quarter, false if not
*/
@Override
public boolean isSupported(TemporalField field) {
if (field == QUARTER_OF_YEAR) {
return true;
} else 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.
*
* 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.
*
* If the unit is a {@link ChronoUnit} then the query is implemented here.
* The supported units are:
*
* 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 == QUARTER_YEARS) {
return true;
} else 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.
*
* The range object expresses the minimum and maximum valid values for a field.
* This year-quarter 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.
*
* 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}.
*
* 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 == QUARTER_OF_YEAR) {
return QUARTER_OF_YEAR.range();
}
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-quarter as an {@code int}.
*
* This queries this year-quarter for the value for 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.
*
* 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-quarter,.
* All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
*
* 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 == QUARTER_OF_YEAR) {
return quarter.getValue();
} else 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);
default:
throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
}
return Temporal.super.get(field);
}
/**
* Gets the value of the specified field from this year-quarter as a {@code long}.
*
* This queries this year-quarter for the value for 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.
*
* 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-quarter.
* All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
*
* 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 == QUARTER_OF_YEAR) {
return quarter.getValue();
} else 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);
default:
throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
}
return field.getFrom(this);
}
private long getProlepticQuarter() {
return year * 4L + (quarter.getValue() - 1);
}
//-----------------------------------------------------------------------
/**
* Gets the year field.
*
* This method returns the primitive {@code int} value for the year.
*
* 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 quarter-of-year field from 1 to 4.
*
* This method returns the quarter as an {@code int} from 1 to 4.
* Application code is frequently clearer if the enum {@link Quarter}
* is used by calling {@link #getQuarter()}.
*
* @return the quarter-of-year, from 1 to 4
* @see #getQuarter()
*/
public int getQuarterValue() {
return quarter.getValue();
}
/**
* Gets the quarter-of-year field using the {@code Quarter} enum.
*
* This method returns the enum {@link Quarter} for the quarter.
* 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 Quarter#getValue() int value}.
*
* @return the quarter-of-year, not null
* @see #getQuarterValue()
*/
public Quarter getQuarter() {
return quarter;
}
//-----------------------------------------------------------------------
/**
* Checks if the year is a leap year, according to the ISO proleptic
* calendar system rules.
*
* 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.
*
* 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.
*
* 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-quarter is valid for this year-quarter.
*
* This method checks whether this year and quarter and the input day form
* a valid date.
*
* @param dayOfQuarter the day-of-quarter to validate, from 1 to 92, invalid value returns false
* @return true if the day is valid for this year-quarter
*/
public boolean isValidDay(int dayOfQuarter) {
return dayOfQuarter >= 1 && dayOfQuarter <= lengthOfQuarter();
}
/**
* Returns the length of the quarter, taking account of the year.
*
* This returns the length of the quarter in days.
*
* @return the length of the quarter in days, from 90 to 92
*/
public int lengthOfQuarter() {
return quarter.length(isLeapYear());
}
/**
* Returns the length of the year.
*
* 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-quarter.
*
* This returns a {@code YearQuarter}, based on this one, with the year-quarter adjusted.
* The adjustment takes place using the specified adjuster strategy object.
* Read the documentation of the adjuster to understand what adjustment will be made.
*
* A simple adjuster might simply set the one of the fields, such as the year field.
* A more complex adjuster might set the year-quarter to the next quarter that
* Halley's comet will pass the Earth.
*
* 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.
*
* This instance is immutable and unaffected by this method call.
*
* @param adjuster the adjuster to use, not null
* @return a {@code YearQuarter} 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 YearQuarter with(TemporalAdjuster adjuster) {
return (YearQuarter) adjuster.adjustInto(this);
}
/**
* Returns a copy of this year-quarter with the specified field set to a new value.
*
* This returns a {@code YearQuarter}, 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 quarter.
* If it is not possible to set the value, because the field is not supported or for
* some other reason, an exception is thrown.
*
* If the field is a {@link ChronoField} then the adjustment is implemented here.
* The supported fields behave as follows:
*
* In all cases, if the new value is outside the valid range of values for the field
* then a {@code DateTimeException} will be thrown.
*
* All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
*
* 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.
*
* 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 YearQuarter} 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 YearQuarter with(TemporalField field, long newValue) {
if (field == QUARTER_OF_YEAR) {
return withQuarter(QUARTER_OF_YEAR.range().checkValidIntValue(newValue, QUARTER_OF_YEAR));
} else if (field instanceof ChronoField) {
ChronoField f = (ChronoField) field;
f.checkValidValue(newValue);
switch (f) {
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));
default:
throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
}
return field.adjustInto(this, newValue);
}
//-----------------------------------------------------------------------
/**
* Returns a copy of this {@code YearQuarter} with the year altered.
*
* This instance is immutable and unaffected by this method call.
*
* @param year the year to set in the returned year-quarter, from MIN_YEAR to MAX_YEAR
* @return a {@code YearQuarter} based on this year-quarter with the requested year, not null
* @throws DateTimeException if the year value is invalid
*/
public YearQuarter withYear(int year) {
YEAR.checkValidValue(year);
return with(year, quarter);
}
/**
* Returns a copy of this {@code YearQuarter} with the quarter-of-year altered.
*
* This instance is immutable and unaffected by this method call.
*
* @param quarter the quarter-of-year to set in the returned year-quarter, from 1 to 4
* @return a {@code YearQuarter} based on this year-quarter with the requested quarter, not null
* @throws DateTimeException if the quarter-of-year value is invalid
*/
public YearQuarter withQuarter(int quarter) {
QUARTER_OF_YEAR.range().checkValidValue(quarter, QUARTER_OF_YEAR);
return with(year, Quarter.of(quarter));
}
//-----------------------------------------------------------------------
/**
* Returns a copy of this year-quarter with the specified amount added.
*
* This returns a {@code YearQuarter}, 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.
*
* 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.
*
* This instance is immutable and unaffected by this method call.
*
* @param amountToAdd the amount to add, not null
* @return a {@code YearQuarter} based on this year-quarter with the addition made, not null
* @throws DateTimeException if the addition cannot be made
* @throws ArithmeticException if numeric overflow occurs
*/
@Override
public YearQuarter plus(TemporalAmount amountToAdd) {
return (YearQuarter) amountToAdd.addTo(this);
}
/**
* Returns a copy of this year-quarter with the specified amount added.
*
* This returns a {@code YearQuarter}, 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.
*
* If the field is a {@link ChronoUnit} then the addition is implemented here.
* The supported fields behave as follows:
*
* All other {@code ChronoUnit} instances will throw an {@code UnsupportedTemporalTypeException}.
*
* 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.
*
* 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 YearQuarter} based on this year-quarter 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 YearQuarter plus(long amountToAdd, TemporalUnit unit) {
if (unit == QUARTER_YEARS) {
return plusQuarters(amountToAdd);
} else 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));
default:
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
}
}
return unit.addTo(this, amountToAdd);
}
/**
* Returns a copy of this year-quarter with the specified period in years added.
*
* This instance is immutable and unaffected by this method call.
*
* @param yearsToAdd the years to add, may be negative
* @return a {@code YearQuarter} based on this year-quarter with the years added, not null
* @throws DateTimeException if the result exceeds the supported range
*/
public YearQuarter plusYears(long yearsToAdd) {
if (yearsToAdd == 0) {
return this;
}
int newYear = YEAR.checkValidIntValue(year + yearsToAdd); // safe overflow
return with(newYear, quarter);
}
/**
* Returns a copy of this year-quarter with the specified period in quarters added.
*
* This instance is immutable and unaffected by this method call.
*
* @param quartersToAdd the quarters to add, may be negative
* @return a {@code YearQuarter} based on this year-quarter with the quarters added, not null
* @throws DateTimeException if the result exceeds the supported range
*/
public YearQuarter plusQuarters(long quartersToAdd) {
if (quartersToAdd == 0) {
return this;
}
long quarterCount = year * 4L + (quarter.getValue() - 1);
long calcQuarters = quarterCount + quartersToAdd; // safe overflow
int newYear = YEAR.checkValidIntValue(Math.floorDiv(calcQuarters, 4));
int newQuarter = (int) Math.floorMod(calcQuarters, 4) + 1;
return with(newYear, Quarter.of(newQuarter));
}
//-----------------------------------------------------------------------
/**
* Returns a copy of this year-quarter with the specified amount subtracted.
*
* This returns a {@code YearQuarter}, 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.
*
* 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.
*
* This instance is immutable and unaffected by this method call.
*
* @param amountToSubtract the amount to subtract, not null
* @return a {@code YearQuarter} based on this year-quarter with the subtraction made, not null
* @throws DateTimeException if the subtraction cannot be made
* @throws ArithmeticException if numeric overflow occurs
*/
@Override
public YearQuarter minus(TemporalAmount amountToSubtract) {
return (YearQuarter) amountToSubtract.subtractFrom(this);
}
/**
* Returns a copy of this year-quarter with the specified amount subtracted.
*
* This returns a {@code YearQuarter}, 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.
*
* 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.
*
* 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 YearQuarter} based on this year-quarter 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 YearQuarter 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 year-quarter with the specified period in years subtracted.
*
* This instance is immutable and unaffected by this method call.
*
* @param yearsToSubtract the years to subtract, may be negative
* @return a {@code YearQuarter} based on this year-quarter with the years subtracted, not null
* @throws DateTimeException if the result exceeds the supported range
*/
public YearQuarter minusYears(long yearsToSubtract) {
return (yearsToSubtract == Long.MIN_VALUE ? plusYears(Long.MAX_VALUE).plusYears(1) : plusYears(-yearsToSubtract));
}
/**
* Returns a copy of this year-quarter with the specified period in quarters subtracted.
*
* This instance is immutable and unaffected by this method call.
*
* @param quartersToSubtract the quarters to subtract, may be negative
* @return a {@code YearQuarter} based on this year-quarter with the quarters subtracted, not null
* @throws DateTimeException if the result exceeds the supported range
*/
public YearQuarter minusQuarters(long quartersToSubtract) {
return (quartersToSubtract == Long.MIN_VALUE ? plusQuarters(Long.MAX_VALUE).plusQuarters(1) : plusQuarters(-quartersToSubtract));
}
//-----------------------------------------------------------------------
/**
* Queries this year-quarter using the specified query.
*
* This queries this year-quarter 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.
*
* 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
* This returns a temporal object of the same observable type as the input
* with the year and quarter changed to be the same as this.
*
* The adjustment is equivalent to using {@link Temporal#plus(long, TemporalUnit)}
* passing the number of quarters to adjust by.
* If the specified temporal object does not use the ISO calendar system then
* a {@code DateTimeException} is thrown.
*
* In most cases, it is clearer to reverse the calling pattern by using
* {@link Temporal#with(TemporalAdjuster)}:
*
* 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");
}
long newProlepticQuarter = getProlepticQuarter();
long oldProlepticQuarter = temporal.get(YEAR) * 4L + (temporal.get(QUARTER_OF_YEAR) - 1);
return temporal.plus(newProlepticQuarter - oldProlepticQuarter, QUARTER_YEARS);
}
/**
* Calculates the amount of time until another year-quarter in terms of the specified unit.
*
* This calculates the amount of time between two {@code YearQuarter}
* objects in terms of a single {@code TemporalUnit}.
* The start and end points are {@code this} and the specified year-quarter.
* The result will be negative if the end is before the start.
* The {@code Temporal} passed to this method is converted to a
* {@code YearQuarter} using {@link #from(TemporalAccessor)}.
* For example, the period in years between two year-quarters can be calculated
* using {@code startYearQuarter.until(endYearQuarter, YEARS)}.
*
* The calculation returns a whole number, representing the number of
* complete units between the two year-quarters.
* For example, the period in decades between 2012-Q3 and 2032-Q2
* will only be one decade as it is one quarter short of two decades.
*
* 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)}:
*
* The calculation is implemented in this method for {@link ChronoUnit}.
* The units {@code QUARTER_YEARS}, {@code YEARS}, {@code DECADES},
* {@code CENTURIES}, {@code MILLENNIA} and {@code ERAS} are supported.
* Other {@code ChronoUnit} values will throw an exception.
*
* 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.
*
* This instance is immutable and unaffected by this method call.
*
* @param endExclusive the end date, exclusive, which is converted to a {@code YearQuarter}, not null
* @param unit the unit to measure the amount in, not null
* @return the amount of time between this year-quarter and the end year-quarter
* @throws DateTimeException if the amount cannot be calculated, or the end
* temporal cannot be converted to a {@code YearQuarter}
* @throws UnsupportedTemporalTypeException if the unit is not supported
* @throws ArithmeticException if numeric overflow occurs
*/
@Override
public long until(Temporal endExclusive, TemporalUnit unit) {
YearQuarter end = YearQuarter.from(endExclusive);
long quartersUntil = end.getProlepticQuarter() - getProlepticQuarter(); // no overflow
if (unit == QUARTER_YEARS) {
return quartersUntil;
} else if (unit instanceof ChronoUnit) {
switch ((ChronoUnit) unit) {
case YEARS:
return quartersUntil / 4;
case DECADES:
return quartersUntil / 40;
case CENTURIES:
return quartersUntil / 400;
case MILLENNIA:
return quartersUntil / 4000;
case ERAS:
return end.getLong(ERA) - getLong(ERA);
default:
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
}
}
return unit.between(this, end);
}
/**
* Returns a sequential ordered stream of year-quarter. The returned stream starts from this year-quarter
* (inclusive) and goes to {@code endExclusive} (exclusive) by an incremental step of 1 {@code QUARTER_YEARS}.
*
* This instance is immutable and unaffected by this method call.
*
* @param endExclusive the end year-quarter, exclusive, not null
* @return a sequential {@code Stream} for the range of {@code YearQuarter} values
* @throws IllegalArgumentException if end year-quarter is before this year-quarter
*/
public Stream
* This year-quarter will be passed to the formatter to produce a string.
*
* @param formatter the formatter to use, not null
* @return the formatted year-quarter 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-quarter with a day-of-quarter to create a {@code LocalDate}.
*
* This returns a {@code LocalDate} formed from this year-quarter and the specified day-of-quarter.
*
* The day-of-quarter value must be valid for the year-quarter.
*
* This method can be used as part of a chain to produce a date:
*
* This returns a {@code LocalDate} based on this year-quarter.
* The day-of-quarter is set to the last valid day of the quarter, taking
* into account leap years.
*
* This method can be used as part of a chain to produce a date:
*
* The comparison is based first on the value of the year, then on the value of the quarter.
* It is "consistent with equals", as defined by {@link Comparable}.
*
* @param other the other year-quarter to compare to, not null
* @return the comparator value, negative if less, positive if greater
*/
@Override
public int compareTo(YearQuarter other) {
int cmp = (year - other.year);
if (cmp == 0) {
cmp = quarter.compareTo(other.quarter);
}
return cmp;
}
/**
* Is this year-quarter after the specified year-quarter.
*
* @param other the other year-quarter to compare to, not null
* @return true if this is after the specified year-quarter
*/
public boolean isAfter(YearQuarter other) {
return compareTo(other) > 0;
}
/**
* Is this year-quarter before the specified year-quarter.
*
* @param other the other year-quarter to compare to, not null
* @return true if this point is before the specified year-quarter
*/
public boolean isBefore(YearQuarter other) {
return compareTo(other) < 0;
}
//-----------------------------------------------------------------------
/**
* Checks if this year-quarter is equal to another year-quarter.
*
* The comparison is based on the time-line position of the year-quarters.
*
* @param obj the object to check, null returns false
* @return true if this is equal to the other year-quarter
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof YearQuarter) {
YearQuarter other = (YearQuarter) obj;
return year == other.year && quarter == other.quarter;
}
return false;
}
/**
* A hash code for this year-quarter.
*
* @return a suitable hash code
*/
@Override
public int hashCode() {
return year ^ (quarter.getValue() << 27);
}
//-----------------------------------------------------------------------
/**
* Outputs this year-quarter as a {@code String}, such as {@code 2007-Q2}.
*
* The output will be in the format {@code uuuu-'Q'Q}:
*
* @return a string representation of this year-quarter, not null
*/
@Override
@ToString
public String toString() {
int absYear = Math.abs(year);
StringBuilder buf = new StringBuilder(10);
if (absYear < 1000) {
if (year < 0) {
buf.append(year - 10000).deleteCharAt(1);
} else {
buf.append(year + 10000).deleteCharAt(0);
}
} else {
if (year > 9999) {
buf.append('+');
}
buf.append(year);
}
return buf.append('-').append(quarter).toString();
}
}
threeten-extra-1.5.0/src/main/java/org/threeten/extra/YearWeek.java 0000664 0000000 0000000 00000075671 13434511741 0025242 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2007-present, 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 org.threeten.extra;
import static java.time.DayOfWeek.THURSDAY;
import static java.time.DayOfWeek.WEDNESDAY;
import static java.time.temporal.IsoFields.WEEK_BASED_YEAR;
import static java.time.temporal.IsoFields.WEEK_OF_WEEK_BASED_YEAR;
import java.io.Serializable;
import java.time.Clock;
import java.time.DateTimeException;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.Year;
import java.time.ZoneId;
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.IsoFields;
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;
import org.joda.convert.FromString;
import org.joda.convert.ToString;
/**
* A year-week in the ISO week date system such as {@code 2015-W13}
*
* {@code YearWeek} is an immutable date-time object that represents the combination
* of a week-based-year and week-of-week-based-year.
* Any field that can be derived from those two fields can be obtained.
*
* This class does not store or represent a day, time or time-zone.
* For example, the value "13th week of 2007" can be stored in a {@code YearWeek}.
*
* 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.
*
* ISO-8601 defines the week as always starting with Monday.
* The first week is the week which contains the first Thursday of the calendar year.
* As such, the week-based-year used in this class does not align with the calendar year.
*
*
* This class must be treated as a value type. Do not synchronize, rely on the
* identity hash code or use the distinction between equals() and ==.
*/
public final class YearWeek
implements TemporalAccessor, TemporalAdjuster, Comparable
* This will query the {@link java.time.Clock#systemDefaultZone() system clock} in the default
* time-zone to obtain the current year-week.
* The zone and offset will be set based on the time-zone in the clock.
*
* Using this method will prevent the ability to use an alternate clock for testing
* because the clock is hard-coded.
*
* @return the current year-week using the system clock and default time-zone, not null
*/
public static YearWeek now() {
return now(Clock.systemDefaultZone());
}
/**
* Obtains the current year-week from the system clock in the specified time-zone.
*
* This will query the {@link Clock#system(java.time.ZoneId) system clock} to obtain the current year-week.
* Specifying the time-zone avoids dependence on the default time-zone.
*
* 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-week using the system clock, not null
*/
public static YearWeek now(ZoneId zone) {
return now(Clock.system(zone));
}
/**
* Obtains the current year-week from the specified clock.
*
* This will query the specified clock to obtain the current year-week.
* 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-week, not null
*/
public static YearWeek now(Clock clock) {
final LocalDate now = LocalDate.now(clock); // called once
return YearWeek.of(now.get(WEEK_BASED_YEAR), now.get(WEEK_OF_WEEK_BASED_YEAR));
}
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code YearWeek} from a week-based-year and week.
*
* If the week is 53 and the year does not have 53 weeks, week one of the following
* year is selected.
*
* @param weekBasedYear the week-based-year to represent, from MIN_YEAR to MAX_YEAR
* @param week the week-of-week-based-year to represent, from 1 to 53
* @return the year-week, not null
* @throws DateTimeException if either field is invalid
*/
public static YearWeek of(int weekBasedYear, int week) {
WEEK_BASED_YEAR.range().checkValidValue(weekBasedYear, WEEK_BASED_YEAR);
WEEK_OF_WEEK_BASED_YEAR.range().checkValidValue(week, WEEK_OF_WEEK_BASED_YEAR);
if (week == 53 && weekRange(weekBasedYear) < 53) {
week = 1;
weekBasedYear++;
WEEK_BASED_YEAR.range().checkValidValue(weekBasedYear, WEEK_BASED_YEAR);
}
return new YearWeek(weekBasedYear, week);
}
// from IsoFields in ThreeTen-Backport
private static int weekRange(int weekBasedYear) {
LocalDate date = LocalDate.of(weekBasedYear, 1, 1);
// 53 weeks if year starts on Thursday, or Wed in a leap year
if (date.getDayOfWeek() == THURSDAY || (date.getDayOfWeek() == WEDNESDAY && date.isLeapYear())) {
return 53;
}
return 52;
}
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code YearWeek} from a temporal object.
*
* This obtains a year-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 YearWeek}.
*
* The conversion extracts the {@link IsoFields#WEEK_BASED_YEAR WEEK_BASED_YEAR} and
* {@link IsoFields#WEEK_OF_WEEK_BASED_YEAR WEEK_OF_WEEK_BASED_YEAR} fields.
* The extraction is only permitted if the temporal object has an ISO
* chronology, or can be converted to a {@code LocalDate}.
*
* This method matches the signature of the functional interface {@link TemporalQuery}
* allowing it to be used in queries via method reference, {@code YearWeek::from}.
*
* @param temporal the temporal object to convert, not null
* @return the year-week, not null
* @throws DateTimeException if unable to convert to a {@code YearWeek}
*/
public static YearWeek from(TemporalAccessor temporal) {
if (temporal instanceof YearWeek) {
return (YearWeek) temporal;
}
Objects.requireNonNull(temporal, "temporal");
try {
if (!IsoChronology.INSTANCE.equals(Chronology.from(temporal))) {
temporal = LocalDate.from(temporal);
}
// need to use getLong() as JDK Parsed class get() doesn't work properly
int year = Math.toIntExact(temporal.getLong(WEEK_BASED_YEAR));
int week = Math.toIntExact(temporal.getLong(WEEK_OF_WEEK_BASED_YEAR));
return of(year, week);
} catch (DateTimeException ex) {
throw new DateTimeException("Unable to obtain YearWeek from TemporalAccessor: " +
temporal + " of type " + temporal.getClass().getName(), ex);
}
}
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code YearWeek} from a text string such as {@code 2007-W13}.
*
* The string must represent a valid year-week.
* Week 53 will be adjusted to week 1 of the following year if necessary.
* The format must be {@code YYYY-'W'ww}.
* 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-W13", not null
* @return the parsed year-week, not null
* @throws DateTimeParseException if the text cannot be parsed
*/
@FromString
public static YearWeek parse(CharSequence text) {
return parse(text, PARSER);
}
/**
* Obtains an instance of {@code YearWeek} from a text string using a specific formatter.
*
* The text is parsed using the formatter, returning a year-week.
*
* @param text the text to parse, not null
* @param formatter the formatter to use, not null
* @return the parsed year-week, not null
* @throws DateTimeParseException if the text cannot be parsed
*/
public static YearWeek parse(CharSequence text, DateTimeFormatter formatter) {
Objects.requireNonNull(formatter, "formatter");
return formatter.parse(text, YearWeek::from);
}
//-----------------------------------------------------------------------
/**
* Constructor.
*
* @param weekBasedYear the week-based-year to represent, validated from MIN_YEAR to MAX_YEAR
* @param week the week to represent, validated
*/
private YearWeek(int weekBasedYear, int week) {
this.year = weekBasedYear;
this.week = week;
}
/**
* Validates the input.
*
* @return the valid object, not null
*/
private Object readResolve() {
return of(year, week);
}
/**
* Returns a copy of this year-week with the new year and week, 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 newWeek the week to represent, validated from 1 to 53
* @return the year-week, not null
*/
private YearWeek with(int newYear, int newWeek) {
if (year == newYear && week == newWeek) {
return this;
}
return of(newYear, newWeek);
}
//-----------------------------------------------------------------------
/**
* Checks if the specified field is supported.
*
* This checks if this year-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.
*
* If the field is a {@link ChronoField} then the query is implemented here.
* The supported fields are:
*
* 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-week, false if not
*/
@Override
public boolean isSupported(TemporalField field) {
if (field == WEEK_OF_WEEK_BASED_YEAR || field == WEEK_BASED_YEAR) {
return true;
} else if (field instanceof ChronoField) {
return false;
}
return field != null && field.isSupportedBy(this);
}
//-----------------------------------------------------------------------
/**
* Gets the range of valid values for the specified field.
*
* The range object expresses the minimum and maximum valid values for a field.
* This year-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.
*
* The range for the {@link IsoFields#WEEK_BASED_YEAR WEEK_BASED_YEAR} and
* {@link IsoFields#WEEK_OF_WEEK_BASED_YEAR WEEK_OF_WEEK_BASED_YEAR} fields is returned.
* All {@link ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
*
* @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 == WEEK_BASED_YEAR) {
return WEEK_BASED_YEAR.range();
}
if (field == WEEK_OF_WEEK_BASED_YEAR) {
return ValueRange.of(1, weekRange(year));
}
return TemporalAccessor.super.range(field);
}
/**
* Gets the value of the specified field from this year-week as an {@code int}.
*
* This queries this year-week for the value for 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.
*
* The value for the {@link IsoFields#WEEK_BASED_YEAR WEEK_BASED_YEAR} and
* {@link IsoFields#WEEK_OF_WEEK_BASED_YEAR WEEK_OF_WEEK_BASED_YEAR} fields is returned.
* All {@link ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
*
* @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 == WEEK_BASED_YEAR) {
return year;
}
if (field == WEEK_OF_WEEK_BASED_YEAR) {
return week;
}
return TemporalAccessor.super.get(field);
}
/**
* Gets the value of the specified field from this year-week as a {@code long}.
*
* This queries this year-week for the value for 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.
*
* The value for the {@link IsoFields#WEEK_BASED_YEAR WEEK_BASED_YEAR} and
* {@link IsoFields#WEEK_OF_WEEK_BASED_YEAR WEEK_OF_WEEK_BASED_YEAR} fields is returned.
* All {@link ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
*
* @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 == WEEK_BASED_YEAR) {
return year;
}
if (field == WEEK_OF_WEEK_BASED_YEAR) {
return week;
}
if (field instanceof ChronoField) {
throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
return field.getFrom(this);
}
//-----------------------------------------------------------------------
/**
* Gets the week-based-year field.
*
* This method returns the primitive {@code int} value for the week-based-year.
*
* Note that the ISO week-based-year does not align with the standard Gregorian/ISO calendar year.
*
* @return the week-based-year
*/
public int getYear() {
return year;
}
/**
* Gets the week-of-week-based-year field.
*
* This method returns the primitive {@code int} value for the week of the week-based-year.
*
* @return the week-of-week-based-year
*/
public int getWeek() {
return week;
}
//-----------------------------------------------------------------------
/**
* Checks if the week-based-year has 53 weeks.
*
* This determines if the year has 53 weeks, returning true.
* If false, the year has 52 weeks.
*
* @return true if the year has 53 weeks, false otherwise
*/
public boolean is53WeekYear() {
return weekRange(year) == 53;
}
/**
* Returns the length of the week-based-year.
*
* This returns the length of the year in days, either 364 or 371.
*
* @return 364 if the year has 52 weeks, 371 if it has 53 weeks
*/
public int lengthOfYear() {
return (is53WeekYear() ? 371 : 364);
}
//-----------------------------------------------------------------------
/**
* Returns a copy of this {@code YearWeek} with the week-based-year altered.
*
* This returns a year-week with the specified week-based-year.
* If the week of this instance is 53 and the new year does not have 53 weeks,
* the week will be adjusted to be 52.
*
* This instance is immutable and unaffected by this method call.
*
* @param weekBasedYear the week-based-year to set in the returned year-week
* @return a {@code YearWeek} based on this year-week with the requested year, not null
* @throws DateTimeException if the week-based-year value is invalid
*/
public YearWeek withYear(int weekBasedYear) {
if (week == 53 && weekRange(weekBasedYear) < 53) {
return YearWeek.of(weekBasedYear, 52);
}
return with(weekBasedYear, week);
}
/**
* Returns a copy of this {@code YearWeek} with the week altered.
*
* This returns a year-week with the specified week-of-week-based-year.
* If the new week is 53 and the year does not have 53 weeks, week one of the
* following year is selected.
*
* This instance is immutable and unaffected by this method call.
*
* @param week the week-of-week-based-year to set in the returned year-week
* @return a {@code YearWeek} based on this year-week with the requested week, not null
* @throws DateTimeException if the week-of-week-based-year value is invalid
*/
public YearWeek withWeek(int week) {
return with(year, week);
}
//-----------------------------------------------------------------------
/**
* Returns a copy of this year-week with the specified number of years added.
*
* If the week of this instance is 53 and the new year does not have 53 weeks,
* the week will be adjusted to be 52.
*
* This instance is immutable and unaffected by this method call.
*
* @param yearsToAdd the years to add, may be negative
* @return the year-week with the years added, not null
*/
public YearWeek plusYears(long yearsToAdd) {
if (yearsToAdd == 0) {
return this;
}
int newYear = Math.toIntExact(Math.addExact(year, yearsToAdd));
return withYear(newYear);
}
/**
* Returns a copy of this year-week with the specified number of weeks added.
*
* This instance is immutable and unaffected by this method call.
*
* @param weeksToAdd the weeks to add, may be negative
* @return the year-week with the weeks added, not null
*/
public YearWeek plusWeeks(long weeksToAdd) {
if (weeksToAdd == 0) {
return this;
}
LocalDate mondayOfWeek = atDay(DayOfWeek.MONDAY).plusWeeks(weeksToAdd);
return YearWeek.from(mondayOfWeek);
}
/**
* Returns a copy of this year-week with the specified number of years subtracted.
*
* If the week of this instance is 53 and the new year does not have 53 weeks,
* the week will be adjusted to be 52.
*
* This instance is immutable and unaffected by this method call.
*
* @param yearsToSubtract the years to subtract, may be negative
* @return the year-week with the years subtracted, not null
*/
public YearWeek minusYears(long yearsToSubtract) {
if (yearsToSubtract == 0) {
return this;
}
int newYear = Math.toIntExact(Math.subtractExact(year, yearsToSubtract));
return withYear(newYear);
}
/**
* Returns a copy of this year-week with the specified number of weeks subtracted.
*
* This instance is immutable and unaffected by this method call.
*
* @param weeksToSubtract the weeks to subtract, may be negative
* @return the year-week with the weeks subtracted, not null
*/
public YearWeek minusWeeks(long weeksToSubtract) {
if (weeksToSubtract == 0) {
return this;
}
LocalDate mondayOfWeek = atDay(DayOfWeek.MONDAY).minusWeeks(weeksToSubtract);
return YearWeek.from(mondayOfWeek);
}
//-----------------------------------------------------------------------
/**
* Queries this year-week using the specified query.
*
* This queries this year-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.
*
* 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
* This returns a temporal object of the same observable type as the input
* with the week-based-year and week changed to be the same as this.
*
* The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)}
* twice, passing {@link IsoFields#WEEK_BASED_YEAR} and
* {@link IsoFields#WEEK_OF_WEEK_BASED_YEAR} as the fields.
* If the specified temporal object does not use the ISO calendar system then
* a {@code DateTimeException} is thrown.
*
* In most cases, it is clearer to reverse the calling pattern by using
* {@link Temporal#with(TemporalAdjuster)}:
*
* 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)) {
throw new DateTimeException("Adjustment only supported on ISO date-time");
}
return temporal.with(WEEK_BASED_YEAR, year).with(WEEK_OF_WEEK_BASED_YEAR, week);
}
/**
* Formats this year-week using the specified formatter.
*
* This year-week will be passed to the formatter to produce a string.
*
* @param formatter the formatter to use, not null
* @return the formatted year-week 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-week with a day-of-week to create a {@code LocalDate}.
*
* This returns a {@code LocalDate} formed from this year-week and the specified day-of-Week.
*
* This method can be used as part of a chain to produce a date:
*
* The comparison is based first on the value of the year, then on the value of the week.
* It is "consistent with equals", as defined by {@link Comparable}.
*
* @param other the other year-week to compare to, not null
* @return the comparator value, negative if less, positive if greater
*/
@Override
public int compareTo(YearWeek other) {
int cmp = (year - other.year);
if (cmp == 0) {
cmp = (week - other.week);
}
return cmp;
}
/**
* Is this year-week after the specified year-week.
*
* @param other the other year-week to compare to, not null
* @return true if this is after the specified year-week
*/
public boolean isAfter(YearWeek other) {
return compareTo(other) > 0;
}
/**
* Is this year-week before the specified year-week.
*
* @param other the other year-week to compare to, not null
* @return true if this point is before the specified year-week
*/
public boolean isBefore(YearWeek other) {
return compareTo(other) < 0;
}
//-----------------------------------------------------------------------
/**
* Checks if this year-week is equal to another year-week.
*
* @param obj the object to check, null returns false
* @return true if this is equal to the other year-week
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof YearWeek) {
YearWeek other = (YearWeek) obj;
return year == other.year && week == other.week;
}
return false;
}
/**
* A hash code for this year-week.
*
* @return a suitable hash code
*/
@Override
public int hashCode() {
return year ^ (week << 25);
}
//-----------------------------------------------------------------------
/**
* Outputs this year-week as a {@code String}, such as {@code 2015-W13}.
*
* The output will be in the format {@code YYYY-'W'ww}:
*
* @return a string representation of this year-week, not null
*/
@Override
@ToString
public String toString() {
int absYear = Math.abs(year);
StringBuilder buf = new StringBuilder(10);
if (absYear < 1000) {
if (year < 0) {
buf.append(year - 10000).deleteCharAt(1);
} else {
buf.append(year + 10000).deleteCharAt(0);
}
} else {
if (year > 9999) {
buf.append('+');
}
buf.append(year);
}
return buf.append(week < 10 ? "-W0" : "-W").append(week).toString();
}
}
threeten-extra-1.5.0/src/main/java/org/threeten/extra/Years.java 0000664 0000000 0000000 00000046527 13434511741 0024607 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2007-present, 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 org.threeten.extra;
import static java.time.temporal.ChronoUnit.YEARS;
import java.io.Serializable;
import java.time.DateTimeException;
import java.time.Period;
import java.time.format.DateTimeParseException;
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.Collections;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.joda.convert.FromString;
import org.joda.convert.ToString;
/**
* A year-based amount of time, such as '12 years'.
*
* This class models a quantity or amount of time in terms of years.
* It is a type-safe way of representing a number of years in an application.
*
* The model is of a directed amount, meaning that the amount may be negative.
*
*
* This class must be treated as a value type. Do not synchronize, rely on the
* identity hash code or use the distinction between equals() and ==.
*/
public final class Years
implements TemporalAmount, Comparable
* The resulting amount will have the specified years.
*
* @param years the number of years, positive or negative
* @return the number of years, not null
*/
public static Years of(int years) {
if (years == 0) {
return ZERO;
} else if (years == 1) {
return ONE;
}
return new Years(years);
}
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code Years} from a temporal amount.
*
* This obtains an instance 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 Years}.
*
* The result is calculated by looping around each unit in the specified amount.
* Each amount is converted to years using {@link Temporals#convertAmount}.
* If the conversion yields a remainder, an exception is thrown.
* If the amount is zero, the unit is ignored.
* For example, "12 months" can be converted to years but "11 months" cannot.
*
* @param amount the temporal amount to convert, not null
* @return the equivalent amount, not null
* @throws DateTimeException if unable to convert to a {@code Years}
* @throws ArithmeticException if numeric overflow occurs
*/
public static Years from(TemporalAmount amount) {
if (amount instanceof Years) {
return (Years) amount;
}
Objects.requireNonNull(amount, "amount");
int years = 0;
for (TemporalUnit unit : amount.getUnits()) {
long value = amount.get(unit);
if (value != 0) {
long[] converted = Temporals.convertAmount(value, unit, YEARS);
if (converted[1] != 0) {
throw new DateTimeException(
"Amount could not be converted to a whole number of years: " + value + " " + unit);
}
years = Math.addExact(years, Math.toIntExact(converted[0]));
}
}
return of(years);
}
//-----------------------------------------------------------------------
/**
* Obtains a {@code Years} from a text string such as {@code PnY}.
*
* This will parse the string produced by {@code toString()} which is
* based on the ISO-8601 period formats {@code PnY}.
*
* The string starts with an optional sign, denoted by the ASCII negative
* or positive symbol. If negative, the whole amount is negated.
* The ASCII letter "P" is next in upper or lower case.
* The ASCII integer amount is next, which may be negative.
* The ASCII letter "Y" is next in upper or lower case.
*
* The leading plus/minus sign, and negative values for years are
* not part of the ISO-8601 standard.
*
* For example, the following are valid inputs:
*
* The start date is included, but the end date is not.
* The result of this method can be negative if the end is before the start.
*
* @param startDateInclusive the start date, inclusive, not null
* @param endDateExclusive the end date, exclusive, not null
* @return the number of years between this date and the end date, not null
*/
public static Years between(Temporal startDateInclusive, Temporal endDateExclusive) {
return of(Math.toIntExact(YEARS.between(startDateInclusive, endDateExclusive)));
}
//-----------------------------------------------------------------------
/**
* Constructs an instance using a specific number of years.
*
* @param years the years to use
*/
private Years(int years) {
super();
this.years = years;
}
/**
* Resolves singletons.
*
* @return the singleton instance
*/
private Object readResolve() {
return Years.of(years);
}
//-----------------------------------------------------------------------
/**
* Gets the value of the requested unit.
*
* This returns a value for the supported unit - {@link ChronoUnit#YEARS YEARS}.
* 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 UnsupportedTemporalTypeException if the unit is not supported
*/
@Override
public long get(TemporalUnit unit) {
if (unit == YEARS) {
return years;
}
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
}
/**
* Gets the set of units supported by this amount.
*
* The single supported unit is {@link ChronoUnit#YEARS YEARS}.
*
* This set can be used in conjunction with {@link #get(TemporalUnit)}
* to access the entire state of the amount.
*
* @return a list containing the years unit, not null
*/
@Override
public List
* The parameter is converted using {@link Years#from(TemporalAmount)}.
*
* This instance is immutable and unaffected by this method call.
*
* @param amountToAdd the amount to add, not null
* @return a {@code Years} based on this instance with the requested amount added, not null
* @throws DateTimeException if the specified amount contains an invalid unit
* @throws ArithmeticException if numeric overflow occurs
*/
public Years plus(TemporalAmount amountToAdd) {
return plus(Years.from(amountToAdd).getAmount());
}
/**
* Returns a copy of this amount with the specified number of years added.
*
* This instance is immutable and unaffected by this method call.
*
* @param years the amount of years to add, may be negative
* @return a {@code Years} based on this instance with the requested amount added, not null
* @throws ArithmeticException if the result overflows an int
*/
public Years plus(int years) {
if (years == 0) {
return this;
}
return of(Math.addExact(this.years, years));
}
//-----------------------------------------------------------------------
/**
* Returns a copy of this amount with the specified amount subtracted.
*
* The parameter is converted using {@link Years#from(TemporalAmount)}.
*
* This instance is immutable and unaffected by this method call.
*
* @param amountToAdd the amount to add, not null
* @return a {@code Years} based on this instance with the requested amount subtracted, not null
* @throws DateTimeException if the specified amount contains an invalid unit
* @throws ArithmeticException if numeric overflow occurs
*/
public Years minus(TemporalAmount amountToAdd) {
return minus(Years.from(amountToAdd).getAmount());
}
/**
* Returns a copy of this amount with the specified number of years subtracted.
*
* This instance is immutable and unaffected by this method call.
*
* @param years the amount of years to add, may be negative
* @return a {@code Years} based on this instance with the requested amount subtracted, not null
* @throws ArithmeticException if the result overflows an int
*/
public Years minus(int years) {
if (years == 0) {
return this;
}
return of(Math.subtractExact(this.years, years));
}
//-----------------------------------------------------------------------
/**
* Returns an instance with the amount multiplied by the specified scalar.
*
* This instance is immutable and unaffected by this method call.
*
* @param scalar the scalar to multiply by, not null
* @return the amount multiplied by the specified scalar, not null
* @throws ArithmeticException if numeric overflow occurs
*/
public Years multipliedBy(int scalar) {
if (scalar == 1) {
return this;
}
return of(Math.multiplyExact(years, scalar));
}
/**
* Returns an instance with the amount divided by the specified divisor.
*
* The calculation uses integer division, thus 3 divided by 2 is 1.
*
* This instance is immutable and unaffected by this method call.
*
* @param divisor the amount to divide by, may be negative
* @return the amount divided by the specified divisor, not null
* @throws ArithmeticException if the divisor is zero
*/
public Years dividedBy(int divisor) {
if (divisor == 1) {
return this;
}
return of(years / divisor);
}
/**
* Returns an instance with the amount negated.
*
* This instance is immutable and unaffected by this method call.
*
* @return the negated amount, not null
* @throws ArithmeticException if numeric overflow occurs, which only happens if
* the amount is {@code Long.MIN_VALUE}
*/
public Years negated() {
return multipliedBy(-1);
}
/**
* Returns a copy of this duration with a positive length.
*
* This method returns a positive duration by effectively removing the sign from any negative total length.
*
* This instance is immutable and unaffected by this method call.
*
* @return the absolute amount, not null
* @throws ArithmeticException if numeric overflow occurs, which only happens if
* the amount is {@code Long.MIN_VALUE}
*/
public Years abs() {
return years < 0 ? negated() : this;
}
//-------------------------------------------------------------------------
/**
* Gets the number of years as a {@code Period}.
*
* This returns a period with the same number of years.
*
* @return the equivalent period, not null
*/
public Period toPeriod() {
return Period.ofYears(years);
}
//-----------------------------------------------------------------------
/**
* Adds this amount to the specified temporal object.
*
* This returns a temporal object of the same observable type as the input
* with this amount added.
*
* In most cases, it is clearer to reverse the calling pattern by using
* {@link Temporal#plus(TemporalAmount)}.
*
* Only non-zero amounts will be added.
*
* 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 UnsupportedTemporalTypeException if the YEARS unit is not supported
* @throws ArithmeticException if numeric overflow occurs
*/
@Override
public Temporal addTo(Temporal temporal) {
if (years != 0) {
temporal = temporal.plus(years, YEARS);
}
return temporal;
}
/**
* Subtracts this amount from the specified temporal object.
*
* This returns a temporal object of the same observable type as the input
* with this amount subtracted.
*
* In most cases, it is clearer to reverse the calling pattern by using
* {@link Temporal#minus(TemporalAmount)}.
*
* Only non-zero amounts will be subtracted.
*
* 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 UnsupportedTemporalTypeException if the YEARS unit is not supported
* @throws ArithmeticException if numeric overflow occurs
*/
@Override
public Temporal subtractFrom(Temporal temporal) {
if (years != 0) {
temporal = temporal.minus(years, YEARS);
}
return temporal;
}
//-----------------------------------------------------------------------
/**
* Compares this amount to the specified {@code Years}.
*
* The comparison is based on the total length of the amounts.
* It is "consistent with equals", as defined by {@link Comparable}.
*
* @param otherAmount the other amount, not null
* @return the comparator value, negative if less, positive if greater
*/
@Override
public int compareTo(Years otherAmount) {
int thisValue = this.years;
int otherValue = otherAmount.years;
return Integer.compare(thisValue, otherValue);
}
//-----------------------------------------------------------------------
/**
* Checks if this amount is equal to the specified {@code Years}.
*
* The comparison is based on the total length of the durations.
*
* @param otherAmount the other amount, null returns false
* @return true if the other amount is equal to this one
*/
@Override
public boolean equals(Object otherAmount) {
if (this == otherAmount) {
return true;
}
if (otherAmount instanceof Years) {
Years other = (Years) otherAmount;
return this.years == other.years;
}
return false;
}
/**
* A hash code for this amount.
*
* @return a suitable hash code
*/
@Override
public int hashCode() {
return years;
}
//-----------------------------------------------------------------------
/**
* Returns a string representation of the number of years.
* This will be in the format 'PnY' where n is the number of years.
*
* @return the number of years in ISO-8601 string format
*/
@Override
@ToString
public String toString() {
return "P" + years + "Y";
}
}
threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/ 0000775 0000000 0000000 00000000000 13434511741 0024133 5 ustar 00root root 0000000 0000000 threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/AbstractDate.java 0000664 0000000 0000000 00000035772 13434511741 0027355 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2007-present, 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 org.threeten.extra.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.ERA;
import static java.time.temporal.ChronoField.YEAR;
import java.time.chrono.ChronoLocalDate;
import java.time.chrono.ChronoPeriod;
import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalField;
import java.time.temporal.TemporalUnit;
import java.time.temporal.UnsupportedTemporalTypeException;
import java.time.temporal.ValueRange;
/**
* An abstract date based on a year, month and day.
*
*
* Compares this date with another ensuring that the date is the same.
*
* Only objects of this concrete type 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 != null && this.getClass() == obj.getClass()) {
AbstractDate otherDate = (AbstractDate) obj;
return this.getProlepticYear() == otherDate.getProlepticYear() &&
this.getMonth() == otherDate.getMonth() &&
this.getDayOfMonth() == otherDate.getDayOfMonth();
}
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() ^
((getProlepticYear() & 0xFFFFF800) ^ ((getProlepticYear() << 11) +
(getMonth() << 6) + (getDayOfMonth())));
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder(30);
buf.append(getChronology().toString())
.append(" ")
.append(getEra())
.append(" ")
.append(getYearOfEra())
.append(getMonth() < 10 ? "-0" : "-").append(getMonth())
.append(getDayOfMonth() < 10 ? "-0" : "-").append(getDayOfMonth());
return buf.toString();
}
}
threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/AbstractNileChronology.java 0000664 0000000 0000000 00000010345 13434511741 0031420 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2007-present, 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 org.threeten.extra.chrono;
import java.time.chrono.AbstractChronology;
import java.time.temporal.ChronoField;
import java.time.temporal.ValueRange;
/**
* A chronology describing one of the Nile river calendar systems.
*
*
* The proleptic-year is leap if the remainder after division by four equals three.
* 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 Math.floorMod(prolepticYear, 4) == 3;
}
//-----------------------------------------------------------------------
@Override
public ValueRange range(ChronoField field) {
switch (field) {
case DAY_OF_MONTH:
return DOM_RANGE;
case ALIGNED_WEEK_OF_MONTH:
return ALIGNED_WOM_RANGE;
case MONTH_OF_YEAR:
return MOY_RANGE;
case PROLEPTIC_MONTH:
return PROLEPTIC_MONTH_RANGE;
case YEAR_OF_ERA:
return YOE_RANGE;
case YEAR:
return YEAR_RANGE;
default:
break;
}
return field.range();
}
}
threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/AbstractNileDate.java 0000664 0000000 0000000 00000006417 13434511741 0030157 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2007-present, 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 org.threeten.extra.chrono;
import java.time.temporal.ValueRange;
/**
* A date in one of the Nile river calendar systems.
*
*
* This returns the length of the month in days.
* Months 1 to 12 have 30 days. Month 13 has 5 or 6 days.
*
* @return the length of the month in days, from 5 to 30
*/
@Override
public int lengthOfMonth() {
if (getMonth() == 13) {
return (isLeapYear() ? 6 : 5);
}
return 30;
}
@Override
public long toEpochDay() {
long year = (long) getProlepticYear();
long calendarEpochDay = ((year - 1) * 365) + Math.floorDiv(year, 4) + (getDayOfYear() - 1);
return calendarEpochDay - getEpochDayDifference();
}
}
threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/AccountingChronology.java 0000664 0000000 0000000 00000061201 13434511741 0031134 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2007-present, 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 org.threeten.extra.chrono;
import static org.threeten.extra.chrono.AccountingYearDivision.THIRTEEN_EVEN_MONTHS_OF_4_WEEKS;
import java.io.Serializable;
import java.time.Clock;
import java.time.DateTimeException;
import java.time.DayOfWeek;
import java.time.Instant;
import java.time.LocalDate;
import java.time.Month;
import java.time.ZoneId;
import java.time.chrono.AbstractChronology;
import java.time.chrono.ChronoLocalDateTime;
import java.time.chrono.ChronoZonedDateTime;
import java.time.chrono.Chronology;
import java.time.chrono.Era;
import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalAdjusters;
import java.time.temporal.ValueRange;
import java.util.Arrays;
import java.util.List;
/**
* An Accounting calendar system.
*
* This chronology defines the rules of a proleptic 52/53-week Accounting calendar system.
* This calendar system follows the rules as laid down in IRS Publication 538
* and the International Financial Reporting Standards.
* The start of the Accounting calendar will vary against the ISO calendar.
* Depending on options chosen, it can start as early as {@code 0000-01-26 (ISO)} or as late as {@code 0001-01-04 (ISO)}.
*
* This class is proleptic. It implements Accounting chronology rules for the entire time-line.
*
* The fields are defined as follows:
*
* The ID uniquely identifies the {@code Chronology},
* but does not differentiate between instances of {@code AccountingChronology}.
* It cannot be used to lookup the {@code Chronology} using {@link Chronology#of(String)},
* because each instance requires setup.
*
* @return the chronology ID - 'Accounting'
* @see #getCalendarType()
*/
@Override
public String getId() {
return "Accounting";
}
/**
* Gets the calendar type of the underlying calendar system, which is null.
*
* The Unicode Locale Data Markup Language (LDML) specification
* does not define an identifier for 52/53 week calendars used for accounting purposes,
* and given that setup required is unlikely to do so.
* For this reason, the calendar type is null.
*
* @return null, as the calendar is unlikely to be specified in LDML
* @see #getId()
*/
@Override
public String getCalendarType() {
return null;
}
//-----------------------------------------------------------------------
/**
* Obtains a local date in Accounting calendar system from the
* era, year-of-era, month-of-year and day-of-month fields.
*
* @param era the Accounting era, not null
* @param yearOfEra the year-of-era
* @param month the month-of-year
* @param dayOfMonth the day-of-month
* @return the Accounting local date, not null
* @throws DateTimeException if unable to create the date
* @throws ClassCastException if the {@code era} is not a {@code AccountingEra}
*/
@Override
public AccountingDate date(Era era, int yearOfEra, int month, int dayOfMonth) {
return date(prolepticYear(era, yearOfEra), month, dayOfMonth);
}
/**
* Obtains a local date in Accounting 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 Accounting local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override
public AccountingDate date(int prolepticYear, int month, int dayOfMonth) {
return AccountingDate.of(this, prolepticYear, month, dayOfMonth);
}
/**
* Obtains a local date in Accounting calendar system from the
* era, year-of-era and day-of-year fields.
*
* @param era the Accounting era, not null
* @param yearOfEra the year-of-era
* @param dayOfYear the day-of-year
* @return the Accounting local date, not null
* @throws DateTimeException if unable to create the date
* @throws ClassCastException if the {@code era} is not a {@code AccountingEra}
*/
@Override
public AccountingDate dateYearDay(Era era, int yearOfEra, int dayOfYear) {
return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear);
}
/**
* Obtains a local date in Accounting calendar system from the
* proleptic-year and day-of-year fields.
*
* @param prolepticYear the proleptic-year
* @param dayOfYear the day-of-year
* @return the Accounting local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override
public AccountingDate dateYearDay(int prolepticYear, int dayOfYear) {
return AccountingDate.ofYearDay(this, prolepticYear, dayOfYear);
}
/**
* Obtains a local date in the Accounting calendar system from the epoch-day.
*
* @param epochDay the epoch day
* @return the Accounting local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override // override with covariant return type
public AccountingDate dateEpochDay(long epochDay) {
return AccountingDate.ofEpochDay(this, epochDay);
}
//-------------------------------------------------------------------------
/**
* Obtains the current Accounting local date from the system clock in the default time-zone.
*
* This will query the {@link Clock#systemDefaultZone() system clock} in the default
* time-zone to obtain the current date.
*
* Using this method will prevent the ability to use an alternate clock for testing
* because the clock is hard-coded.
*
* @return the current Accounting 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 AccountingDate dateNow() {
return AccountingDate.now(this);
}
/**
* Obtains the current Accounting local date from the system clock in the specified time-zone.
*
* 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.
*
* 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 Accounting local date using the system clock, not null
* @throws DateTimeException if unable to create the date
*/
@Override // override with covariant return type
public AccountingDate dateNow(ZoneId zone) {
return AccountingDate.now(this, zone);
}
/**
* Obtains the current Accounting local date from the specified clock.
*
* 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 Accounting local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override // override with covariant return type
public AccountingDate dateNow(Clock clock) {
return AccountingDate.now(this, clock);
}
//-------------------------------------------------------------------------
/**
* Obtains a Accounting local date from another date-time object.
*
* @param temporal the date-time object to convert, not null
* @return the Accounting local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override
public AccountingDate date(TemporalAccessor temporal) {
return AccountingDate.from(this, temporal);
}
/**
* Obtains a Accounting local date-time from another date-time object.
*
* @param temporal the date-time object to convert, not null
* @return the Accounting local date-time, not null
* @throws DateTimeException if unable to create the date-time
*/
@Override
@SuppressWarnings("unchecked")
public ChronoLocalDateTime
* An Accounting proleptic-year is leap if the time between the end of the previous year
* and the end of the current year is 371 days.
* 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 Math.floorMod(prolepticYear + getISOLeapYearCount(prolepticYear) + yearZeroDifference, 7) == 0
|| Math.floorMod(prolepticYear + getISOLeapYearCount(prolepticYear + 1) + yearZeroDifference, 7) == 0;
}
/**
* Return the number of ISO Leap Years since Accounting Year 1.
*
* This method calculates how many ISO leap years have passed since year 1.
* The count returned will be negative for years before 1.
* 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 the count of leap years since year 1.
*/
private long getISOLeapYearCount(long prolepticYear) {
long offsetYear = prolepticYear - (end == Month.JANUARY ? 1 : 0) - 1;
return Math.floorDiv(offsetYear, 4) - Math.floorDiv(offsetYear, 100) + Math.floorDiv(offsetYear, 400) + (end == Month.JANUARY ? 1 : 0);
}
/**
* Returns the count of leap years since year 1.
*
* This method calculates how many Accounting leap years have passed since year 1.
* The count returned will be negative for years before 1.
* 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 the count of leap years since year 1.
*/
long previousLeapYears(long prolepticYear) {
return Math.floorDiv(prolepticYear - 1 + getISOLeapYearCount(prolepticYear) + yearZeroDifference, 7);
}
@Override
public int prolepticYear(Era era, int yearOfEra) {
if (!(era instanceof AccountingEra)) {
throw new ClassCastException("Era must be AccountingEra");
}
return (era == AccountingEra.CE ? yearOfEra : 1 - yearOfEra);
}
@Override
public AccountingEra eraOf(int era) {
return AccountingEra.of(era);
}
@Override
public List
* Accounting calendars require setup before use, given how they are used.
* The following information is required:
*
* There are approximately 7 x 2 x 12 x 4 x 12/13 = 4032 combinations.
*
*
* This date operates using a given {@linkplain AccountingChronology Accounting calendar}.
* An Accounting calendar differs greatly from the ISO calendar.
* The start of the Accounting calendar will vary against the ISO calendar.
* Depending on options chosen, it can start as early as {@code 0000-01-26 (ISO)} or as late as {@code 0001-01-04 (ISO)}.
*
*
* This class must be treated as a value type. Do not synchronize, rely on the
* identity hash code or use the distinction between equals() and ==.
*/
public final class AccountingDate extends AbstractDate implements ChronoLocalDate, Serializable {
/**
* Serialization version.
*/
private static final long serialVersionUID = -126140328940081914L;
/**
* Number of days in a week.
*/
private static final int DAYS_IN_WEEK = 7;
/**
* Number of weeks in a regular (non-leap) year.
*/
private static final int WEEKS_IN_YEAR = 52;
/**
* Number of days in a long (400-year) cycle.
*/
private static final int DAYS_PER_LONG_CYCLE = 400 * 365 + 3 * 24 + 1 * 25;
/**
* The chronology for manipulating this date.
*/
private final AccountingChronology chronology;
/**
* The proleptic year.
*/
private final int prolepticYear;
/**
* The month (period).
*/
private final short month;
/**
* The day.
*/
private final short day;
//-----------------------------------------------------------------------
/**
* Obtains the current {@code AccountingDate} from the system clock in the default time-zone,
* translated with the given AccountingChronology.
*
* This will query the {@link Clock#systemDefaultZone() system clock} in the default
* time-zone to obtain the current date.
*
* Using this method will prevent the ability to use an alternate clock for testing
* because the clock is hard-coded.
*
* @param chronology the Accounting chronology to base the date on, not null
* @return the current date using the system clock and default time-zone, not null
* @throws DateTimeException if the current date cannot be obtained,
* NullPointerException if an AccountingChronology was not provided
*/
public static AccountingDate now(AccountingChronology chronology) {
return now(chronology, Clock.systemDefaultZone());
}
/**
* Obtains the current {@code AccountingDate} from the system clock in the specified time-zone,
* translated with the given AccountingChronology.
*
* 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.
*
* Using this method will prevent the ability to use an alternate clock for testing
* because the clock is hard-coded.
*
* @param chronology the Accounting chronology to base the date on, not null
* @param zone the zone ID to use, not null
* @return the current date using the system clock, not null
* @throws DateTimeException if the current date cannot be obtained,
* NullPointerException if an AccountingChronology was not provided
*/
public static AccountingDate now(AccountingChronology chronology, ZoneId zone) {
return now(chronology, Clock.system(zone));
}
/**
* Obtains the current {@code AccountingDate} from the specified clock,
* translated with the given AccountingChronology.
*
* 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 chronology the Accounting chronology to base the date on, not null
* @param clock the clock to use, not null
* @return the current date, not null
* @throws DateTimeException if the current date cannot be obtained,
* NullPointerException if an AccountingChronology was not provided
*/
public static AccountingDate now(AccountingChronology chronology, Clock clock) {
LocalDate now = LocalDate.now(clock);
return ofEpochDay(chronology, now.toEpochDay());
}
/**
* Obtains a {@code AccountingDate} representing a date in the given accounting calendar
* system from the proleptic-year, month-of-year and day-of-month fields.
*
* This returns a {@code AccountingDate} with the specified fields.
* The day must be valid for the year and month, otherwise an exception will be thrown.
* @param chronology the Accounting chronology to base the date on, not null
* @param prolepticYear the Accounting proleptic-year
* @param month the Accounting month-of-year, from 1 to 12 or 1 to 13
* @param dayOfMonth the Accounting day-of-month, from 1 to 35 or 1 to 42
* @return the date in the given Accounting calendar system, not null
* @throws DateTimeException if the value of any field is out of range,
* if the day-of-month is invalid for the month-year,
* or if an AccountingChronology was not provided
*/
public static AccountingDate of(AccountingChronology chronology, int prolepticYear, int month, int dayOfMonth) {
return create(chronology, prolepticYear, month, dayOfMonth);
}
/**
* Obtains an {@code AccountingDate} from a temporal object.
*
* This obtains a date in the specified Accounting 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 AccountingDate}.
*
* The conversion typically uses the {@link ChronoField#EPOCH_DAY EPOCH_DAY}
* field, which is standardized across calendar systems.
*
* This method almost matches the signature of the functional interface {@link TemporalQuery}
* and must be used as a query via something that supplies the missing parameter,
* such as a curried method reference, {@code temporal -> AccountingDate.from(chronology, temporal)}
* (where {@code chronology} resolves to a set up {@code AccountingChronology}).
*
* @param chronology the Accounting chronology to base the date on, not null
* @param temporal the temporal object to convert, not null
* @return the date in Accounting calendar system, not null
* @throws DateTimeException if unable to convert to an {@code AccountingDate},
* NullPointerException if an AccountingChronology was not provided
*/
public static AccountingDate from(AccountingChronology chronology, TemporalAccessor temporal) {
if (temporal instanceof AccountingDate && ((AccountingDate) temporal).getChronology().equals(chronology)) {
return (AccountingDate) temporal;
}
return ofEpochDay(chronology, temporal.getLong(EPOCH_DAY));
}
//-----------------------------------------------------------------------
/**
* Obtains an {@code AccountingDate} representing a date in the given Accounting calendar
* system from the proleptic-year and day-of-year fields.
*
* This returns an {@code AccountingDate} with the specified fields.
* The day must be valid for the year, otherwise an exception will be thrown.
*
* @param chronology the Accounting chronology to base the date on, not null
* @param prolepticYear the Accounting proleptic-year
* @param dayOfYear the Accounting day-of-year, from 1 to 371
* @return the date in Accounting 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,
* NullPointerException if an AccountingChronology was not provided
*/
static AccountingDate ofYearDay(AccountingChronology chronology, int prolepticYear, int dayOfYear) {
Objects.requireNonNull(chronology, "A previously setup chronology is required.");
YEAR.checkValidValue(prolepticYear);
DAY_OF_YEAR_RANGE.checkValidValue(dayOfYear, DAY_OF_YEAR);
boolean leap = chronology.isLeapYear(prolepticYear);
if (dayOfYear > WEEKS_IN_YEAR * DAYS_IN_WEEK && !leap) {
throw new DateTimeException("Invalid date 'DayOfYear " + dayOfYear + "' as '" + prolepticYear + "' is not a leap year");
}
int month = (leap ? chronology.getDivision().getMonthFromElapsedWeeks((dayOfYear - 1) / DAYS_IN_WEEK, chronology.getLeapWeekInMonth())
: chronology.getDivision().getMonthFromElapsedWeeks((dayOfYear - 1) / DAYS_IN_WEEK));
int dayOfMonth = dayOfYear - (leap ? chronology.getDivision().getWeeksAtStartOfMonth(month, chronology.getLeapWeekInMonth())
: chronology.getDivision().getWeeksAtStartOfMonth(month)) * DAYS_IN_WEEK;
return new AccountingDate(chronology, prolepticYear, month, dayOfMonth);
}
/**
* Obtains an {@code AccountingDate} representing a date in the given Accounting calendar
* system from the epoch-day.
*
* @param chronology the Accounting chronology to base the date on, not null
* @param epochDay the epoch day to convert based on 1970-01-01 (ISO)
* @return the date in given Accounting calendar system, not null
* @throws DateTimeException if the epoch-day is out of range,
* NullPointerException if an AccountingChronology was not provided
*/
static AccountingDate ofEpochDay(AccountingChronology chronology, long epochDay) {
EPOCH_DAY.range().checkValidValue(epochDay, EPOCH_DAY); // validate outer bounds
// Use Accounting 1 to help with 0-counts. Leap years can occur at any time.
long accountingEpochDay = epochDay + chronology.getDays0001ToIso1970();
int longCycle = (int) Math.floorDiv(accountingEpochDay, DAYS_PER_LONG_CYCLE);
int daysInLongCycle = (int) Math.floorMod(accountingEpochDay, DAYS_PER_LONG_CYCLE);
// Value is an estimate, as the floating leap-years make this difficult.
int year = (daysInLongCycle - (daysInLongCycle / 365 + daysInLongCycle / (4 * 365 + 1) - daysInLongCycle / (100 * 365 + 24)) / 7) / (DAYS_IN_WEEK * WEEKS_IN_YEAR);
int yearStart = (int) (WEEKS_IN_YEAR * (year - 1) + chronology.previousLeapYears(year)) * DAYS_IN_WEEK;
// Despite the year being an estimate, the effect should still be within a few days.
if (yearStart > daysInLongCycle) {
year--;
yearStart -= (WEEKS_IN_YEAR + (chronology.isLeapYear(year) ? 1 : 0)) * DAYS_IN_WEEK;
} else if (daysInLongCycle - yearStart >= (WEEKS_IN_YEAR + (chronology.isLeapYear(year) ? 1 : 0)) * DAYS_IN_WEEK) {
yearStart += (WEEKS_IN_YEAR + (chronology.isLeapYear(year) ? 1 : 0)) * DAYS_IN_WEEK;
year++;
}
return ofYearDay(chronology, year + 400 * longCycle, daysInLongCycle - yearStart + 1);
}
private static AccountingDate resolvePreviousValid(AccountingChronology chronology, int prolepticYear, int month, int day) {
day = Math.min(day, lengthOfMonth(chronology, prolepticYear, month));
return new AccountingDate(chronology, prolepticYear, month, day);
}
private static int lengthOfMonth(AccountingChronology chronology, int prolepticYear, int month) {
return (chronology.isLeapYear(prolepticYear) ? chronology.getDivision().getWeeksInMonth(month, chronology.getLeapWeekInMonth())
: chronology.getDivision().getWeeksInMonth(month)) * DAYS_IN_WEEK;
}
/**
* Creates an {@code AccountingDate} validating the input.
*
* @param chronology the Accounting chronology to base the date on, not null
* @param prolepticYear the Accounting proleptic-year
* @return the date in Accounting 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 month-year,
* NullPointerException if an AccountingChronology was not provided
*/
static AccountingDate create(AccountingChronology chronology, int prolepticYear, int month, int dayOfMonth) {
Objects.requireNonNull(chronology, "A previously setup chronology is required.");
YEAR.checkValidValue(prolepticYear);
chronology.range(MONTH_OF_YEAR).checkValidValue(month, MONTH_OF_YEAR);
if (dayOfMonth < 1 || dayOfMonth > lengthOfMonth(chronology, prolepticYear, month)) {
if (month == chronology.getLeapWeekInMonth() && dayOfMonth < (chronology.getDivision().getWeeksInMonth(month) + 1) * DAYS_IN_WEEK
&& !chronology.isLeapYear(prolepticYear)) {
throw new DateTimeException("Invalid date '" + month + "/" + dayOfMonth + "' as '" + prolepticYear + "' is not a leap year");
} else {
throw new DateTimeException("Invalid date '" + month + "/" + dayOfMonth + "'");
}
}
return new AccountingDate(chronology, prolepticYear, month, dayOfMonth);
}
//-----------------------------------------------------------------------
/**
* Creates an instance from validated data.
*
* @param chronology the Accounting chronology to base the date on, not null
* @param prolepticYear the Accounting proleptic-year
* @param month the Accounting month (period), from 1 to 12 or 1 to 13
* @param dayOfMonth the Accounting day-of-month, from 1 to 35 or 1 to 42
*/
private AccountingDate(AccountingChronology chronology, int prolepticYear, int month, int dayOfMonth) {
this.chronology = chronology;
this.prolepticYear = prolepticYear;
this.month = (short) month;
this.day = (short) dayOfMonth;
}
/**
* Validates the object.
*
* @return the resolved date, not null
*/
private Object readResolve() {
return AccountingDate.create(chronology, prolepticYear, month, day);
}
//-----------------------------------------------------------------------
@Override
int getProlepticYear() {
return prolepticYear;
}
@Override
int getMonth() {
return month;
}
@Override
int getDayOfMonth() {
return day;
}
@Override
int getDayOfYear() {
int weeksAtStartOfMonth = (isLeapYear() ? chronology.getDivision().getWeeksAtStartOfMonth(month, chronology.getLeapWeekInMonth())
: chronology.getDivision().getWeeksAtStartOfMonth(month));
return weeksAtStartOfMonth * DAYS_IN_WEEK + day;
}
@Override
AbstractDate withDayOfYear(int value) {
return plusDays(value - getDayOfYear());
}
@Override
int lengthOfYearInMonths() {
return chronology.getDivision().lengthOfYearInMonths();
}
@Override
ValueRange rangeAlignedWeekOfMonth() {
return ValueRange.of(1, (lengthOfMonth() - 1) / DAYS_IN_WEEK + 1);
}
@Override
AccountingDate resolvePrevious(int newYear, int newMonth, int dayOfMonth) {
return resolvePreviousValid(chronology, newYear, newMonth, dayOfMonth);
}
//-----------------------------------------------------------------------
/**
* Gets the chronology of this date, which is an Accounting calendar system.
*
* The {@code Chronology} represents the calendar system in use.
* The era and other fields in {@link ChronoField} are defined by the chronology.
*
* @return the Accounting chronology, not null
*/
@Override
public AccountingChronology getChronology() {
return chronology;
}
@Override
public int lengthOfMonth() {
return lengthOfMonth(chronology, prolepticYear, month);
}
@Override
public int lengthOfYear() {
return (WEEKS_IN_YEAR + (isLeapYear() ? 1 : 0)) * DAYS_IN_WEEK;
};
//-------------------------------------------------------------------------
@Override
public AccountingDate with(TemporalAdjuster adjuster) {
return (AccountingDate) adjuster.adjustInto(this);
}
@Override
public AccountingDate with(TemporalField field, long newValue) {
return (AccountingDate) super.with(field, newValue);
}
//-----------------------------------------------------------------------
@Override
public AccountingDate plus(TemporalAmount amount) {
return (AccountingDate) amount.addTo(this);
}
@Override
public AccountingDate plus(long amountToAdd, TemporalUnit unit) {
return (AccountingDate) super.plus(amountToAdd, unit);
}
@Override
public AccountingDate minus(TemporalAmount amount) {
return (AccountingDate) amount.subtractFrom(this);
}
@Override
public AccountingDate minus(long amountToSubtract, TemporalUnit unit) {
return (amountToSubtract == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit));
}
//-------------------------------------------------------------------------
@Override // for covariant return type
@SuppressWarnings("unchecked")
public ChronoLocalDateTime
* Compares this date with another ensuring that the date is the same.
*
* Only objects of this concrete type 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 AccountingDate) {
AccountingDate other = (AccountingDate) obj;
return this.prolepticYear == other.prolepticYear &&
this.month == other.month &&
this.day == other.day &&
this.chronology.equals(other.chronology);
}
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 chronology.hashCode() ^
((prolepticYear & 0xFFFFF800) ^ ((prolepticYear << 11) + (month << 6) + (day)));
}
}
threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/AccountingEra.java 0000664 0000000 0000000 00000007637 13434511741 0027535 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2007-present, 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 org.threeten.extra.chrono;
import java.time.DateTimeException;
import java.time.chrono.Era;
/**
* An era in the Accounting calendar system.
*
* The Accounting calendar system has two eras.
* The current era, for years from 1 onwards, is known as the 'Current 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 Current Era' era.
*
* The start of accounting epochs {@code 0001-01-01 (Accounting)} will vary against the ISO calendar.
* Depending on options chosen, it can start as early as {@code 0000-01-26 (ISO)} or as late as {@code 0001-01-04 (ISO)}.
*
* Do not use {@code ordinal()} to obtain the numeric representation of {@code AccountingEra}.
* Use {@code getValue()} instead.
*
*
* {@code AccountingEra} is an enum representing the Accounting eras of BCE/CE.
* This factory allows the enum to be obtained from the {@code int} value.
*
* @param era 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 AccountingEra of(int era) {
switch (era) {
case 0:
return BCE;
case 1:
return CE;
default:
throw new DateTimeException("Invalid era: " + era);
}
}
//-----------------------------------------------------------------------
/**
* Gets the numeric era {@code int} value.
*
* 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();
}
}
threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/AccountingYearDivision.java 0000664 0000000 0000000 00000022163 13434511741 0031422 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2007-present, 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 org.threeten.extra.chrono;
import java.time.DateTimeException;
import java.time.temporal.ChronoField;
import java.time.temporal.ValueRange;
import java.util.Arrays;
/**
* How an Accounting year is divided.
*
* An Accounting calendar system generally divides a year into smaller periods, similar in length to regular calendar months.
* The most common divisions either use 12 such 'months' (requiring one every quarter to be 5 weeks instead of 4),
* or use 13 of 4 weeks each (making one quarter have an extra month, or each quarter have partial months).
*
*
*
* The British calendar system follows the rules of the Julian calendar
* until 1752 and the rules of the Gregorian (ISO) calendar since then.
* The Julian differs from the Gregorian only in terms of the leap year rule.
*
* The Julian and Gregorian calendar systems are linked to Rome and the Vatican
* with the Julian preceding the Gregorian. The Gregorian was introduced to
* handle the drift of the seasons through the year due to the inaccurate
* Julian leap year rules. When first introduced by the Vatican in 1582,
* the cutover resulted in a "gap" of 10 days.
*
* While the calendar was introduced in 1582, it was not adopted everywhere.
* Britain did not adopt it until the 1752, when Wednesday 2nd September 1752
* was followed by Thursday 14th September 1752.
*
* This chronology implements the proleptic Julian calendar system followed by
* the proleptic Gregorian calendar system (identical to the ISO calendar system).
* Dates are aligned such that {@code 0001-01-01 (British)} is {@code 0000-12-30 (ISO)}.
*
* This class implements a calendar where January 1st is the start of the year.
* The history of the start of the year is complex and using the current standard
* is the most consistent.
*
* The eras of this calendar system are defined by {@link JulianEra} to avoid unnecessary duplication.
*
* The fields are defined as follows:
*
* The date returned is the first date that the Gregorian (ISO) calendar applies,
* which is Thursday 14th September 1752.
*
* @return the first date after the cutover, not null
*/
public LocalDate getCutover() {
return CUTOVER;
}
//-----------------------------------------------------------------------
/**
* Gets the ID of the chronology - 'BritishCutover'.
*
* 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 - 'BritishCutover'
* @see #getCalendarType()
*/
@Override
public String getId() {
return "BritishCutover";
}
/**
* Gets the calendar type of the underlying calendar system, which returns null.
*
* The Unicode Locale Data Markup Language (LDML) specification
* does not define an identifier for this calendar system, thus null is returned.
*
* @return the calendar system type, null
* @see #getId()
*/
@Override
public String getCalendarType() {
return null;
}
//-----------------------------------------------------------------------
/**
* Obtains a local date in British Cutover calendar system from the
* era, year-of-era, month-of-year and day-of-month fields.
*
* Dates in the middle of the cutover gap, such as the 10th September 1752,
* will not throw an exception. Instead, the date will be treated as a Julian date
* and converted to an ISO date, with the day of month shifted by 11 days.
*
* @param era the British Cutover era, not null
* @param yearOfEra the year-of-era
* @param month the month-of-year
* @param dayOfMonth the day-of-month
* @return the British Cutover local date, not null
* @throws DateTimeException if unable to create the date
* @throws ClassCastException if the {@code era} is not a {@code JulianEra}
*/
@Override
public BritishCutoverDate date(Era era, int yearOfEra, int month, int dayOfMonth) {
return date(prolepticYear(era, yearOfEra), month, dayOfMonth);
}
/**
* Obtains a local date in British Cutover calendar system from the
* proleptic-year, month-of-year and day-of-month fields.
*
* Dates in the middle of the cutover gap, such as the 10th September 1752,
* will not throw an exception. Instead, the date will be treated as a Julian date
* and converted to an ISO date, with the day of month shifted by 11 days.
*
* @param prolepticYear the proleptic-year
* @param month the month-of-year
* @param dayOfMonth the day-of-month
* @return the British Cutover local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override
public BritishCutoverDate date(int prolepticYear, int month, int dayOfMonth) {
return BritishCutoverDate.of(prolepticYear, month, dayOfMonth);
}
/**
* Obtains a local date in British Cutover calendar system from the
* era, year-of-era and day-of-year fields.
*
* The day-of-year takes into account the cutover, thus there are only 355 days in 1752.
*
* @param era the British Cutover era, not null
* @param yearOfEra the year-of-era
* @param dayOfYear the day-of-year
* @return the British Cutover local date, not null
* @throws DateTimeException if unable to create the date
* @throws ClassCastException if the {@code era} is not a {@code JulianEra}
*/
@Override
public BritishCutoverDate dateYearDay(Era era, int yearOfEra, int dayOfYear) {
return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear);
}
/**
* Obtains a local date in British Cutover calendar system from the
* proleptic-year and day-of-year fields.
*
* The day-of-year takes into account the cutover, thus there are only 355 days in 1752.
*
* @param prolepticYear the proleptic-year
* @param dayOfYear the day-of-year
* @return the British Cutover local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override
public BritishCutoverDate dateYearDay(int prolepticYear, int dayOfYear) {
return BritishCutoverDate.ofYearDay(prolepticYear, dayOfYear);
}
/**
* Obtains a local date in the British Cutover calendar system from the epoch-day.
*
* @param epochDay the epoch day
* @return the British Cutover local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override // override with covariant return type
public BritishCutoverDate dateEpochDay(long epochDay) {
return BritishCutoverDate.ofEpochDay(epochDay);
}
//-------------------------------------------------------------------------
/**
* Obtains the current British Cutover local date from the system clock in the default time-zone.
*
* This will query the {@link Clock#systemDefaultZone() system clock} in the default
* time-zone to obtain the current date.
*
* Using this method will prevent the ability to use an alternate clock for testing
* because the clock is hard-coded.
*
* @return the current British Cutover 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 BritishCutoverDate dateNow() {
return BritishCutoverDate.now();
}
/**
* Obtains the current British Cutover local date from the system clock in the specified time-zone.
*
* 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.
*
* 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 British Cutover local date using the system clock, not null
* @throws DateTimeException if unable to create the date
*/
@Override // override with covariant return type
public BritishCutoverDate dateNow(ZoneId zone) {
return BritishCutoverDate.now(zone);
}
/**
* Obtains the current British Cutover local date from the specified clock.
*
* 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 British Cutover local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override // override with covariant return type
public BritishCutoverDate dateNow(Clock clock) {
return BritishCutoverDate.now(clock);
}
//-------------------------------------------------------------------------
/**
* Obtains a British Cutover local date from another date-time object.
*
* @param temporal the date-time object to convert, not null
* @return the British Cutover local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override
public BritishCutoverDate date(TemporalAccessor temporal) {
return BritishCutoverDate.from(temporal);
}
/**
* Obtains a British Cutover local date-time from another date-time object.
*
* @param temporal the date-time object to convert, not null
* @return the British Cutover local date-time, not null
* @throws DateTimeException if unable to create the date-time
*/
@Override
@SuppressWarnings("unchecked")
public ChronoLocalDateTime
* The result will return the same as {@link JulianChronology#isLeapYear(long)} for
* year 1752 and earlier, and {@link IsoChronology#isLeapYear(long)} otherwise.
* 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) {
if (prolepticYear <= CUTOVER_YEAR) {
return JulianChronology.INSTANCE.isLeapYear(prolepticYear);
}
return IsoChronology.INSTANCE.isLeapYear(prolepticYear);
}
@Override
public int prolepticYear(Era era, int yearOfEra) {
if (era instanceof JulianEra == false) {
throw new ClassCastException("Era must be JulianEra");
}
return (era == JulianEra.AD ? yearOfEra : 1 - yearOfEra);
}
@Override
public JulianEra eraOf(int eraValue) {
return JulianEra.of(eraValue);
}
@Override
public List
* This date operates using the {@linkplain BritishCutoverChronology British Cutover calendar}.
*
*
* This class must be treated as a value type. Do not synchronize, rely on the
* identity hash code or use the distinction between equals() and ==.
*/
public final class BritishCutoverDate
extends AbstractDate
implements ChronoLocalDate, Serializable {
/**
* Serialization version.
*/
private static final long serialVersionUID = -9626278512674L;
/**
* The underlying date.
*/
private final LocalDate isoDate;
/**
* The underlying Julian date if before the cutover.
*/
private final transient JulianDate julianDate;
//-----------------------------------------------------------------------
/**
* Obtains the current {@code BritishCutoverDate} from the system clock in the default time-zone.
*
* This will query the {@link Clock#systemDefaultZone() system clock} in the default
* time-zone to obtain the current date.
*
* 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 BritishCutoverDate now() {
return now(Clock.systemDefaultZone());
}
/**
* Obtains the current {@code BritishCutoverDate} from the system clock in the specified time-zone.
*
* 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.
*
* 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 BritishCutoverDate now(ZoneId zone) {
return now(Clock.system(zone));
}
/**
* Obtains the current {@code BritishCutoverDate} from the specified clock.
*
* 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 BritishCutoverDate now(Clock clock) {
return new BritishCutoverDate(LocalDate.now(clock));
}
/**
* Obtains a {@code BritishCutoverDate} representing a date in the British Cutover calendar
* system from the proleptic-year, month-of-year and day-of-month fields.
*
* This returns a {@code BritishCutoverDate} with the specified fields.
*
* Dates in the middle of the cutover gap, such as the 10th September 1752,
* will not throw an exception. Instead, the date will be treated as a Julian date
* and converted to an ISO date, with the day of month shifted by 11 days.
*
* Invalid dates, such as September 31st will throw an exception.
*
* @param prolepticYear the British Cutover proleptic-year
* @param month the British Cutover month-of-year, from 1 to 12
* @param dayOfMonth the British Cutover day-of-month, from 1 to 31
* @return the date in British Cutover 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 BritishCutoverDate of(int prolepticYear, int month, int dayOfMonth) {
return BritishCutoverDate.create(prolepticYear, month, dayOfMonth);
}
/**
* Obtains a {@code BritishCutoverDate} from a temporal object.
*
* This obtains a date in the British Cutover 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 BritishCutoverDate}.
*
* The conversion uses the {@link ChronoField#EPOCH_DAY EPOCH_DAY}
* field, which is standardized across calendar systems.
*
* This method matches the signature of the functional interface {@link TemporalQuery}
* allowing it to be used as a query via method reference, {@code BritishCutoverDate::from}.
*
* @param temporal the temporal object to convert, not null
* @return the date in British Cutover calendar system, not null
* @throws DateTimeException if unable to convert to a {@code BritishCutoverDate}
*/
public static BritishCutoverDate from(TemporalAccessor temporal) {
if (temporal instanceof BritishCutoverDate) {
return (BritishCutoverDate) temporal;
}
return new BritishCutoverDate(LocalDate.from(temporal));
}
//-----------------------------------------------------------------------
/**
* Obtains a {@code BritishCutoverDate} representing a date in the British Cutover calendar
* system from the proleptic-year and day-of-year fields.
*
* This returns a {@code BritishCutoverDate} with the specified fields.
* The day must be valid for the year, otherwise an exception will be thrown.
*
* @param prolepticYear the British Cutover proleptic-year
* @param dayOfYear the British Cutover day-of-year, from 1 to 366
* @return the date in British Cutover 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 BritishCutoverDate ofYearDay(int prolepticYear, int dayOfYear) {
if (prolepticYear < CUTOVER_YEAR || (prolepticYear == CUTOVER_YEAR && dayOfYear <= 246)) {
JulianDate julian = JulianDate.ofYearDay(prolepticYear, dayOfYear);
return new BritishCutoverDate(julian);
} else if (prolepticYear == CUTOVER_YEAR) {
LocalDate iso = LocalDate.ofYearDay(prolepticYear, dayOfYear + CUTOVER_DAYS);
return new BritishCutoverDate(iso);
} else {
LocalDate iso = LocalDate.ofYearDay(prolepticYear, dayOfYear);
return new BritishCutoverDate(iso);
}
}
/**
* Obtains a {@code BritishCutoverDate} representing a date in the British Cutover calendar
* system from the epoch-day.
*
* @param epochDay the epoch day to convert based on 1970-01-01 (ISO)
* @return the date in British Cutover calendar system, not null
* @throws DateTimeException if the epoch-day is out of range
*/
static BritishCutoverDate ofEpochDay(final long epochDay) {
return new BritishCutoverDate(LocalDate.ofEpochDay(epochDay));
}
/**
* Creates a {@code BritishCutoverDate} validating the input.
*
* @param prolepticYear the British Cutover proleptic-year
* @param month the British Cutover month-of-year, from 1 to 12
* @param dayOfMonth the British Cutover day-of-month, from 1 to 31
* @return the date in British Cutover 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
*/
static BritishCutoverDate create(int prolepticYear, int month, int dayOfMonth) {
if (prolepticYear < CUTOVER_YEAR) {
JulianDate julian = JulianDate.of(prolepticYear, month, dayOfMonth);
return new BritishCutoverDate(julian);
} else {
LocalDate iso = LocalDate.of(prolepticYear, month, dayOfMonth);
if (iso.isBefore(CUTOVER)) {
JulianDate julian = JulianDate.of(prolepticYear, month, dayOfMonth);
return new BritishCutoverDate(julian);
}
return new BritishCutoverDate(iso);
}
}
//-----------------------------------------------------------------------
/**
* Creates an instance from an ISO date.
*
* @param isoDate the standard local date, not null
*/
BritishCutoverDate(LocalDate isoDate) {
Objects.requireNonNull(isoDate, "isoDate");
this.isoDate = isoDate;
this.julianDate = (isoDate.isBefore(CUTOVER) ? JulianDate.from(isoDate) : null);
}
/**
* Creates an instance from a Julian date.
*
* @param julianDate the Julian date before the cutover, not null
*/
BritishCutoverDate(JulianDate julianDate) {
Objects.requireNonNull(julianDate, "julianDate");
this.isoDate = LocalDate.from(julianDate);
this.julianDate = (isoDate.isBefore(CUTOVER) ? julianDate : null);
}
/**
* Validates the object.
*
* @return the resolved date, not null
*/
private Object readResolve() {
return new BritishCutoverDate(isoDate);
}
//-----------------------------------------------------------------------
private boolean isCutoverYear() {
return isoDate.getYear() == CUTOVER_YEAR && isoDate.getDayOfYear() > CUTOVER_DAYS;
}
private boolean isCutoverMonth() {
return isoDate.getYear() == CUTOVER_YEAR && isoDate.getMonthValue() == 9 && isoDate.getDayOfMonth() > CUTOVER_DAYS;
}
//-------------------------------------------------------------------------
@Override
int getAlignedDayOfWeekInMonth() {
if (isCutoverMonth() && julianDate == null) {
return ((getDayOfMonth() - 1 - CUTOVER_DAYS) % lengthOfWeek()) + 1;
}
return super.getAlignedDayOfWeekInMonth();
}
@Override
int getAlignedWeekOfMonth() {
if (isCutoverMonth() && julianDate == null) {
return ((getDayOfMonth() - 1 - CUTOVER_DAYS) / lengthOfWeek()) + 1;
}
return super.getAlignedWeekOfMonth();
}
@Override
int getProlepticYear() {
return (julianDate != null ? julianDate.getProlepticYear() : isoDate.getYear());
}
@Override
int getMonth() {
return (julianDate != null ? julianDate.getMonth() : isoDate.getMonthValue());
}
@Override
int getDayOfMonth() {
return (julianDate != null ? julianDate.getDayOfMonth() : isoDate.getDayOfMonth());
}
@Override
int getDayOfYear() {
if (julianDate != null) {
return julianDate.getDayOfYear();
}
if (isoDate.getYear() == CUTOVER_YEAR) {
return isoDate.getDayOfYear() - CUTOVER_DAYS;
}
return isoDate.getDayOfYear();
}
@Override
public ValueRange rangeChrono(ChronoField field) {
switch (field) {
case DAY_OF_MONTH:
// short length, but value range still 1 to 30
if (isCutoverMonth()) {
return ValueRange.of(1, 30);
}
return ValueRange.of(1, lengthOfMonth());
case DAY_OF_YEAR:
// 1 to 355 in cutover year, otherwise 1 to 365/366
return ValueRange.of(1, lengthOfYear());
case ALIGNED_WEEK_OF_MONTH:
// 1 to 3 in cutover month, otherwise 1 to 4/5
return rangeAlignedWeekOfMonth();
case ALIGNED_WEEK_OF_YEAR:
// 1 to 51 in cutover year, otherwise 1 to 53
if (isCutoverYear()) {
return ValueRange.of(1, 51);
}
return ChronoField.ALIGNED_WEEK_OF_YEAR.range();
default:
return getChronology().range(field);
}
}
@Override
ValueRange rangeAlignedWeekOfMonth() {
if (isCutoverMonth()) {
return ValueRange.of(1, 3);
}
return ValueRange.of(1, getMonth() == 2 && isLeapYear() == false ? 4 : 5);
}
@Override
BritishCutoverDate resolvePrevious(int year, int month, int dayOfMonth) {
switch (month) {
case 2:
dayOfMonth = Math.min(dayOfMonth, getChronology().isLeapYear(year) ? 29 : 28);
break;
case 4:
case 6:
case 9:
case 11:
dayOfMonth = Math.min(dayOfMonth, 30);
break;
default:
break;
}
return create(year, month, dayOfMonth);
}
//-----------------------------------------------------------------------
/**
* Gets the chronology of this date, which is the British Cutover calendar system.
*
* The {@code Chronology} represents the calendar system in use.
* The era and other fields in {@link ChronoField} are defined by the chronology.
*
* @return the British Cutover chronology, not null
*/
@Override
public BritishCutoverChronology getChronology() {
return BritishCutoverChronology.INSTANCE;
}
/**
* Gets the era applicable at this date.
*
* The British Cutover calendar system has two eras, 'AD' and 'BC',
* defined by {@link JulianEra}.
*
* @return the era applicable at this date, not null
*/
@Override
public JulianEra getEra() {
return (getProlepticYear() >= 1 ? JulianEra.AD : JulianEra.BC);
}
/**
* Returns the length of the month represented by this date.
*
* This returns the length of the month in days.
* This takes into account the cutover, returning 19 in September 1752.
*
* @return the length of the month in days, from 19 to 31
*/
@Override
public int lengthOfMonth() {
if (isCutoverMonth()) {
return 19;
}
return (julianDate != null ? julianDate.lengthOfMonth() : isoDate.lengthOfMonth());
}
/**
* Returns the length of the year represented by this date.
*
* This returns the length of the year in days.
* This takes into account the cutover, returning 355 in 1752.
*
* @return the length of the month in days, from 19 to 31
*/
@Override
public int lengthOfYear() {
if (isCutoverYear()) {
return 355;
}
return (julianDate != null ? julianDate.lengthOfYear() : isoDate.lengthOfYear());
}
//-------------------------------------------------------------------------
@Override
public BritishCutoverDate with(TemporalAdjuster adjuster) {
return (BritishCutoverDate) adjuster.adjustInto(this);
}
@Override
public BritishCutoverDate with(TemporalField field, long newValue) {
return (BritishCutoverDate) super.with(field, newValue);
}
//-----------------------------------------------------------------------
@Override
public BritishCutoverDate plus(TemporalAmount amount) {
return (BritishCutoverDate) amount.addTo(this);
}
@Override
public BritishCutoverDate plus(long amountToAdd, TemporalUnit unit) {
return (BritishCutoverDate) super.plus(amountToAdd, unit);
}
@Override
public BritishCutoverDate minus(TemporalAmount amount) {
return (BritishCutoverDate) amount.subtractFrom(this);
}
@Override
public BritishCutoverDate minus(long amountToSubtract, TemporalUnit unit) {
return (amountToSubtract == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit));
}
//-------------------------------------------------------------------------
@Override // for covariant return type
@SuppressWarnings("unchecked")
public ChronoLocalDateTime
* This chronology defines the rules of the Coptic calendar system.
* This calendar system is primarily used in Christian Egypt.
* Dates are aligned such that {@code 0001-01-01 (Coptic)} is {@code 0284-08-29 (ISO)}.
*
* The fields are defined as follows:
*
* 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 - 'Coptic'
* @see #getCalendarType()
*/
@Override
public String getId() {
return "Coptic";
}
/**
* Gets the calendar type of the underlying calendar system - 'coptic'.
*
* The calendar type is an identifier defined by the
* Unicode Locale Data Markup Language (LDML) 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 - 'coptic'
* @see #getId()
*/
@Override
public String getCalendarType() {
return "coptic";
}
//-----------------------------------------------------------------------
/**
* Obtains a local date in Coptic calendar system from the
* era, year-of-era, month-of-year and day-of-month fields.
*
* @param era the Coptic era, not null
* @param yearOfEra the year-of-era
* @param month the month-of-year
* @param dayOfMonth the day-of-month
* @return the Coptic local date, not null
* @throws DateTimeException if unable to create the date
* @throws ClassCastException if the {@code era} is not a {@code CopticEra}
*/
@Override
public CopticDate date(Era era, int yearOfEra, int month, int dayOfMonth) {
return date(prolepticYear(era, yearOfEra), month, dayOfMonth);
}
/**
* Obtains a local date in Coptic 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 Coptic local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override
public CopticDate date(int prolepticYear, int month, int dayOfMonth) {
return CopticDate.of(prolepticYear, month, dayOfMonth);
}
/**
* Obtains a local date in Coptic calendar system from the
* era, year-of-era and day-of-year fields.
*
* @param era the Coptic era, not null
* @param yearOfEra the year-of-era
* @param dayOfYear the day-of-year
* @return the Coptic local date, not null
* @throws DateTimeException if unable to create the date
* @throws ClassCastException if the {@code era} is not a {@code CopticEra}
*/
@Override
public CopticDate dateYearDay(Era era, int yearOfEra, int dayOfYear) {
return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear);
}
/**
* Obtains a local date in Coptic calendar system from the
* proleptic-year and day-of-year fields.
*
* @param prolepticYear the proleptic-year
* @param dayOfYear the day-of-year
* @return the Coptic local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override
public CopticDate dateYearDay(int prolepticYear, int dayOfYear) {
return CopticDate.ofYearDay(prolepticYear, dayOfYear);
}
/**
* Obtains a local date in the Coptic calendar system from the epoch-day.
*
* @param epochDay the epoch day
* @return the Coptic local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override // override with covariant return type
public CopticDate dateEpochDay(long epochDay) {
return CopticDate.ofEpochDay(epochDay);
}
//-------------------------------------------------------------------------
/**
* Obtains the current Coptic local date from the system clock in the default time-zone.
*
* This will query the {@link Clock#systemDefaultZone() system clock} in the default
* time-zone to obtain the current date.
*
* Using this method will prevent the ability to use an alternate clock for testing
* because the clock is hard-coded.
*
* @return the current Coptic 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 CopticDate dateNow() {
return CopticDate.now();
}
/**
* Obtains the current Coptic local date from the system clock in the specified time-zone.
*
* 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.
*
* 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 Coptic local date using the system clock, not null
* @throws DateTimeException if unable to create the date
*/
@Override // override with covariant return type
public CopticDate dateNow(ZoneId zone) {
return CopticDate.now(zone);
}
/**
* Obtains the current Coptic local date from the specified clock.
*
* 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 Coptic local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override // override with covariant return type
public CopticDate dateNow(Clock clock) {
return CopticDate.now(clock);
}
//-------------------------------------------------------------------------
/**
* Obtains a Coptic local date from another date-time object.
*
* @param temporal the date-time object to convert, not null
* @return the Coptic local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override
public CopticDate date(TemporalAccessor temporal) {
return CopticDate.from(temporal);
}
/**
* Obtains a Coptic local date-time from another date-time object.
*
* @param temporal the date-time object to convert, not null
* @return the Coptic local date-time, not null
* @throws DateTimeException if unable to create the date-time
*/
@Override
@SuppressWarnings("unchecked")
public ChronoLocalDateTime
* This date operates using the {@linkplain CopticChronology Coptic calendar}.
* This calendar system is primarily used in Christian Egypt.
* Dates are aligned such that {@code 0001-01-01 (Coptic)} is {@code 0284-08-29 (ISO)}.
*
*
* This class must be treated as a value type. Do not synchronize, rely on the
* identity hash code or use the distinction between equals() and ==.
*/
public final class CopticDate
extends AbstractNileDate
implements ChronoLocalDate, Serializable {
/**
* Serialization version.
*/
private static final long serialVersionUID = -7920528871688876868L;
/**
* The difference between the ISO and Coptic epoch day count.
*/
private static final int EPOCH_DAY_DIFFERENCE = 574971 + 40587; // MJD values
/**
* The proleptic year.
*/
private final int prolepticYear;
/**
* The month.
*/
private final short month;
/**
* The day.
*/
private final short day;
//-----------------------------------------------------------------------
/**
* Obtains the current {@code CopticDate} from the system clock in the default time-zone.
*
* This will query the {@link Clock#systemDefaultZone() system clock} in the default
* time-zone to obtain the current date.
*
* 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 CopticDate now() {
return now(Clock.systemDefaultZone());
}
/**
* Obtains the current {@code CopticDate} from the system clock in the specified time-zone.
*
* 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.
*
* 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 CopticDate now(ZoneId zone) {
return now(Clock.system(zone));
}
/**
* Obtains the current {@code CopticDate} from the specified clock.
*
* 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 CopticDate now(Clock clock) {
LocalDate now = LocalDate.now(clock);
return CopticDate.ofEpochDay(now.toEpochDay());
}
/**
* Obtains a {@code CopticDate} representing a date in the Coptic calendar
* system from the proleptic-year, month-of-year and day-of-month fields.
*
* This returns a {@code CopticDate} with the specified fields.
* The day must be valid for the year and month, otherwise an exception will be thrown.
*
* @param prolepticYear the Coptic proleptic-year
* @param month the Coptic month-of-year, from 1 to 13
* @param dayOfMonth the Coptic day-of-month, from 1 to 30
* @return the date in Coptic 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 CopticDate of(int prolepticYear, int month, int dayOfMonth) {
return CopticDate.create(prolepticYear, month, dayOfMonth);
}
/**
* Obtains a {@code CopticDate} from a temporal object.
*
* This obtains a date in the Coptic 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 CopticDate}.
*
* The conversion typically uses the {@link ChronoField#EPOCH_DAY EPOCH_DAY}
* field, which is standardized across calendar systems.
*
* This method matches the signature of the functional interface {@link TemporalQuery}
* allowing it to be used as a query via method reference, {@code CopticDate::from}.
*
* @param temporal the temporal object to convert, not null
* @return the date in Coptic calendar system, not null
* @throws DateTimeException if unable to convert to a {@code CopticDate}
*/
public static CopticDate from(TemporalAccessor temporal) {
if (temporal instanceof CopticDate) {
return (CopticDate) temporal;
}
return CopticDate.ofEpochDay(temporal.getLong(EPOCH_DAY));
}
//-----------------------------------------------------------------------
/**
* Obtains a {@code CopticDate} representing a date in the Coptic calendar
* system from the proleptic-year and day-of-year fields.
*
* This returns a {@code CopticDate} with the specified fields.
* The day must be valid for the year, otherwise an exception will be thrown.
*
* @param prolepticYear the Coptic proleptic-year
* @param dayOfYear the Coptic day-of-year, from 1 to 366
* @return the date in Coptic 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 CopticDate ofYearDay(int prolepticYear, int dayOfYear) {
CopticChronology.YEAR_RANGE.checkValidValue(prolepticYear, YEAR);
DAY_OF_YEAR.range().checkValidValue(dayOfYear, DAY_OF_YEAR);
if (dayOfYear == 366 && CopticChronology.INSTANCE.isLeapYear(prolepticYear) == false) {
throw new DateTimeException("Invalid date 'Nasie 6' as '" + prolepticYear + "' is not a leap year");
}
return new CopticDate(prolepticYear, (dayOfYear - 1) / 30 + 1, (dayOfYear - 1) % 30 + 1);
}
/**
* Obtains a {@code CopticDate} representing a date in the Coptic calendar
* system from the epoch-day.
*
* @param epochDay the epoch day to convert based on 1970-01-01 (ISO)
* @return the date in Coptic calendar system, not null
* @throws DateTimeException if the epoch-day is out of range
*/
static CopticDate ofEpochDay(final long epochDay) {
EPOCH_DAY.range().checkValidValue(epochDay, EPOCH_DAY); // validate outer bounds
long copticED = epochDay + EPOCH_DAY_DIFFERENCE;
int adjustment = 0;
if (copticED < 0) {
copticED = copticED + (1461L * (1_000_000L / 4));
adjustment = -1_000_000;
}
int prolepticYear = (int) (((copticED * 4) + 1463) / 1461);
int startYearEpochDay = (prolepticYear - 1) * 365 + (prolepticYear / 4);
int doy0 = (int) (copticED - startYearEpochDay);
int month = doy0 / 30 + 1;
int dom = doy0 % 30 + 1;
return new CopticDate(prolepticYear + adjustment, month, dom);
}
private static CopticDate resolvePreviousValid(int prolepticYear, int month, int day) {
if (month == 13 && day > 5) {
day = CopticChronology.INSTANCE.isLeapYear(prolepticYear) ? 6 : 5;
}
return new CopticDate(prolepticYear, month, day);
}
/**
* Creates a {@code CopticDate} validating the input.
*
* @param prolepticYear the Coptic proleptic-year
* @param month the Coptic month-of-year, from 1 to 13
* @param dayOfMonth the Coptic day-of-month, from 1 to 30
* @return the date in Coptic 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
*/
static CopticDate create(int prolepticYear, int month, int dayOfMonth) {
CopticChronology.YEAR_RANGE.checkValidValue(prolepticYear, YEAR);
CopticChronology.MOY_RANGE.checkValidValue(month, MONTH_OF_YEAR);
CopticChronology.DOM_RANGE.checkValidValue(dayOfMonth, DAY_OF_MONTH);
if (month == 13 && dayOfMonth > 5) {
if (CopticChronology.INSTANCE.isLeapYear(prolepticYear)) {
if (dayOfMonth > 6) {
throw new DateTimeException("Invalid date 'Nasie " + dayOfMonth + "', valid range from 1 to 5, or 1 to 6 in a leap year");
}
} else {
if (dayOfMonth == 6) {
throw new DateTimeException("Invalid date 'Nasie 6' as '" + prolepticYear + "' is not a leap year");
} else {
throw new DateTimeException("Invalid date 'Nasie " + dayOfMonth + "', valid range from 1 to 5, or 1 to 6 in a leap year");
}
}
}
return new CopticDate(prolepticYear, month, dayOfMonth);
}
//-----------------------------------------------------------------------
/**
* Creates an instance from validated data.
*
* @param prolepticYear the Coptic proleptic-year
* @param month the Coptic month, from 1 to 13
* @param dayOfMonth the Coptic day-of-month, from 1 to 30
*/
private CopticDate(int prolepticYear, int month, int dayOfMonth) {
this.prolepticYear = prolepticYear;
this.month = (short) month;
this.day = (short) dayOfMonth;
}
/**
* Validates the object.
*
* @return the resolved date, not null
*/
private Object readResolve() {
return CopticDate.create(prolepticYear, month, day);
}
//-----------------------------------------------------------------------
@Override
int getEpochDayDifference() {
return EPOCH_DAY_DIFFERENCE;
}
@Override
int getProlepticYear() {
return prolepticYear;
}
@Override
int getMonth() {
return month;
}
@Override
int getDayOfMonth() {
return day;
}
@Override
CopticDate resolvePrevious(int newYear, int newMonth, int dayOfMonth) {
return resolvePreviousValid(newYear, newMonth, dayOfMonth);
}
//-----------------------------------------------------------------------
/**
* Gets the chronology of this date, which is the Coptic calendar system.
*
* The {@code Chronology} represents the calendar system in use.
* The era and other fields in {@link ChronoField} are defined by the chronology.
*
* @return the Coptic chronology, not null
*/
@Override
public CopticChronology getChronology() {
return CopticChronology.INSTANCE;
}
/**
* Gets the era applicable at this date.
*
* The Coptic calendar system has two eras, 'AM' and 'BEFORE_AM',
* defined by {@link CopticEra}.
*
* @return the era applicable at this date, not null
*/
@Override
public CopticEra getEra() {
return (prolepticYear >= 1 ? CopticEra.AM : CopticEra.BEFORE_AM);
}
//-------------------------------------------------------------------------
@Override
public CopticDate with(TemporalAdjuster adjuster) {
return (CopticDate) adjuster.adjustInto(this);
}
@Override
public CopticDate with(TemporalField field, long newValue) {
return (CopticDate) super.with(field, newValue);
}
//-----------------------------------------------------------------------
@Override
public CopticDate plus(TemporalAmount amount) {
return (CopticDate) amount.addTo(this);
}
@Override
public CopticDate plus(long amountToAdd, TemporalUnit unit) {
return (CopticDate) super.plus(amountToAdd, unit);
}
@Override
public CopticDate minus(TemporalAmount amount) {
return (CopticDate) amount.subtractFrom(this);
}
@Override
public CopticDate minus(long amountToSubtract, TemporalUnit unit) {
return (amountToSubtract == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit));
}
//-------------------------------------------------------------------------
@Override // for covariant return type
@SuppressWarnings("unchecked")
public ChronoLocalDateTime
* The Coptic calendar system has two eras.
* The current era, for years from 1 onwards, is known as the 'Era of the Martyrs'.
* 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 Era of the Martyrs' era.
*
* The start of the Coptic epoch {@code 0001-01-01 (Coptic)} is {@code 0284-08-29 (ISO)}.
*
* Do not use {@code ordinal()} to obtain the numeric representation of {@code CopticEra}.
* Use {@code getValue()} instead.
*
*
* {@code CopticEra} is an enum representing the Coptic eras of BEFORE_AM/AM.
* This factory allows the enum to be obtained from the {@code int} value.
*
* @param era the BEFORE_AM/AM value to represent, from 0 (BEFORE_AM) to 1 (AM)
* @return the era singleton, not null
* @throws DateTimeException if the value is invalid
*/
public static CopticEra of(int era) {
switch (era) {
case 0:
return BEFORE_AM;
case 1:
return AM;
default:
throw new DateTimeException("Invalid era: " + era);
}
}
//-----------------------------------------------------------------------
/**
* Gets the numeric era {@code int} value.
*
* The era BEFORE_AM has the value 0, while the era AM has the value 1.
*
* @return the era value, from 0 (BEFORE_AM) to 1 (AM)
*/
@Override
public int getValue() {
return ordinal();
}
}
threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/DiscordianChronology.java 0000664 0000000 0000000 00000042331 13434511741 0031124 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2007-present, 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 org.threeten.extra.chrono;
import java.io.Serializable;
import java.time.Clock;
import java.time.DateTimeException;
import java.time.Instant;
import java.time.ZoneId;
import java.time.chrono.AbstractChronology;
import java.time.chrono.ChronoLocalDateTime;
import java.time.chrono.ChronoZonedDateTime;
import java.time.chrono.Chronology;
import java.time.chrono.Era;
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.Map;
/**
* The Discordian calendar system.
*
* This chronology defines the rules of the proleptic Discordian calendar system.
* The Discordian differs from the Gregorian in terms of month and week lengths, with an offset year.
* Dates are aligned such that each Discordian year starts with each ISO year, with an offset index.
*
* This class is not proleptic. It implements Discordian rules only since YOLD 1 (ISO BCE 1166).
*
* The fields are defined as follows:
*
* 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 - 'Discordian'
* @see #getCalendarType()
*/
@Override
public String getId() {
return "Discordian";
}
/**
* Gets the calendar type of the underlying calendar system - 'discordian'.
*
* The Unicode Locale Data Markup Language (LDML) specification
* does not define an identifier for the Discordian calendar, but were it to
* do so, 'discordian' is highly likely to be chosen.
*
* @return the calendar system type - 'discordian'
* @see #getId()
*/
@Override
public String getCalendarType() {
return "discordian";
}
//-----------------------------------------------------------------------
/**
* Obtains a local date in Discordian calendar system from the
* era, year-of-era, month-of-year and day-of-month fields.
*
* @param era the Discordian era, not null
* @param yearOfEra the year-of-era
* @param month the month-of-year
* @param dayOfMonth the day-of-month
* @return the Discordian local date, not null
* @throws DateTimeException if unable to create the date
* @throws ClassCastException if the {@code era} is not a {@code DiscordianEra}
*/
@Override
public DiscordianDate date(Era era, int yearOfEra, int month, int dayOfMonth) {
return date(prolepticYear(era, yearOfEra), month, dayOfMonth);
}
/**
* Obtains a local date in Discordian 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 Discordian local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override
public DiscordianDate date(int prolepticYear, int month, int dayOfMonth) {
return DiscordianDate.of(prolepticYear, month, dayOfMonth);
}
/**
* Obtains a local date in Discordian calendar system from the
* era, year-of-era and day-of-year fields.
*
* @param era the Discordian era, not null
* @param yearOfEra the year-of-era
* @param dayOfYear the day-of-year
* @return the Discordian local date, not null
* @throws DateTimeException if unable to create the date
* @throws ClassCastException if the {@code era} is not a {@code DiscordianEra}
*/
@Override
public DiscordianDate dateYearDay(Era era, int yearOfEra, int dayOfYear) {
return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear);
}
/**
* Obtains a local date in the Discordian calendar system from the
* proleptic-year and day-of-year fields.
*
* @param prolepticYear the proleptic-year
* @param dayOfYear the day-of-year
* @return the Discordian local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override
public DiscordianDate dateYearDay(int prolepticYear, int dayOfYear) {
return DiscordianDate.ofYearDay(prolepticYear, dayOfYear);
}
/**
* Obtains a local date in the Discordian calendar system from the epoch-day.
*
* @param epochDay the epoch day
* @return the Discordian local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override // override with covariant return type
public DiscordianDate dateEpochDay(long epochDay) {
return DiscordianDate.ofEpochDay(epochDay);
}
//-------------------------------------------------------------------------
/**
* Obtains the current Discordian local date from the system clock in the default time-zone.
*
* This will query the {@link Clock#systemDefaultZone() system clock} in the default
* time-zone to obtain the current date.
*
* Using this method will prevent the ability to use an alternate clock for testing
* because the clock is hard-coded.
*
* @return the current Discordian 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 DiscordianDate dateNow() {
return DiscordianDate.now();
}
/**
* Obtains the current Discordian local date from the system clock in the specified time-zone.
*
* 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.
*
* 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 Discordian local date using the system clock, not null
* @throws DateTimeException if unable to create the date
*/
@Override // override with covariant return type
public DiscordianDate dateNow(ZoneId zone) {
return DiscordianDate.now(zone);
}
/**
* Obtains the current Discordian local date from the specified clock.
*
* 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 Discordian local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override // override with covariant return type
public DiscordianDate dateNow(Clock clock) {
return DiscordianDate.now(clock);
}
//-------------------------------------------------------------------------
/**
* Obtains a Discordian local date from another date-time object.
*
* @param temporal the date-time object to convert, not null
* @return the Discordian local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override
public DiscordianDate date(TemporalAccessor temporal) {
return DiscordianDate.from(temporal);
}
/**
* Obtains a Discordian local date-time from another date-time object.
*
* @param temporal the date-time object to convert, not null
* @return the Discordian local date-time, not null
* @throws DateTimeException if unable to create the date-time
*/
@Override
@SuppressWarnings("unchecked")
public ChronoLocalDateTime
* A Discordian proleptic-year is leap if the remainder after division by four equals zero.
* There are two special cases.
* If the year minus 1166 is divisible by 100 it is not a leap year, unless
* it is also divisible by 400, when it is a leap year.
* These rules produce leap days on the same dates as the ISO-8601 calendar system.
*
* @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) {
long offsetYear = prolepticYear - OFFSET_FROM_ISO_0000;
return (offsetYear % 4 == 0) && ((offsetYear % 400 == 0) || (offsetYear % 100 != 0));
}
@Override
public int prolepticYear(Era era, int yearOfEra) {
if (!DiscordianEra.YOLD.equals(era)) {
throw new ClassCastException("Era must be DiscordianEra.YOLD");
}
return YEAR_RANGE.checkValidIntValue(yearOfEra, ChronoField.YEAR_OF_ERA);
}
@Override
public DiscordianEra eraOf(int era) {
return DiscordianEra.of(era);
}
@Override
public List
* This date operates using the {@linkplain DiscordianChronology Discordian calendar}.
* This calendar system is used by some adherents to Discordianism.
* The Discordian differs from the Gregorian in terms of the length of the week and month, and uses an offset year.
* Dates are aligned such that {@code 0001-01-01 (Discordian)} is {@code -1165-01-01 (ISO)}.
*
*
* This class must be treated as a value type. Do not synchronize, rely on the
* identity hash code or use the distinction between equals() and ==.
*/
public final class DiscordianDate
extends AbstractDate
implements ChronoLocalDate, Serializable {
/**
* Serialization version.
*/
private static final long serialVersionUID = -4340508226506164852L;
/**
* The difference between the Discordian and ISO epoch day count (Discordian 1167-01-01 to ISO 1970-01-01).
*/
private static final int DISCORDIAN_1167_TO_ISO_1970 = 719162;
/**
* The days per short 4 year cycle.
*/
private static final int DAYS_PER_SHORT_CYCLE = (365 * 4) + 1;
/**
* The days per 100 year cycle.
*/
private static final int DAYS_PER_CYCLE = (DAYS_PER_SHORT_CYCLE * 25) - 1;
/**
* The days per 400 year long cycle.
*/
private static final int DAYS_PER_LONG_CYCLE = (DAYS_PER_CYCLE * 4) + 1;
/**
* Offset in days from start of year to St. Tib's Day.
*/
private static final int ST_TIBS_OFFSET = 60;
/**
* The proleptic year.
*/
private final int prolepticYear;
/**
* The month.
*/
private final short month;
/**
* The day.
*/
private final short day;
//-----------------------------------------------------------------------
/**
* Obtains the current {@code DiscordianDate} from the system clock in the default time-zone.
*
* This will query the {@link Clock#systemDefaultZone() system clock} in the default
* time-zone to obtain the current date.
*
* 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 DiscordianDate now() {
return now(Clock.systemDefaultZone());
}
/**
* Obtains the current {@code DiscordianDate} from the system clock in the specified time-zone.
*
* 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.
*
* 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 DiscordianDate now(ZoneId zone) {
return now(Clock.system(zone));
}
/**
* Obtains the current {@code DiscordianDate} from the specified clock.
*
* 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 DiscordianDate now(Clock clock) {
LocalDate now = LocalDate.now(clock);
return DiscordianDate.ofEpochDay(now.toEpochDay());
}
/**
* Obtains a {@code DiscordianDate} representing a date in the Discordian calendar
* system from the proleptic-year, month-of-year and day-of-month fields.
*
* This returns a {@code DiscordianDate} with the specified fields.
* The day must be valid for the year and month, otherwise an exception will be thrown.
*
* St. Tib's Day is indicated by specifying 0 for both month and day-of-month.
*
* @param prolepticYear the Discordian proleptic-year
* @param month the Discordian month-of-year, from 1 to 5
* @param dayOfMonth the Discordian day-of-month, from 1 to 73
* @return the date in Discordian 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 DiscordianDate of(int prolepticYear, int month, int dayOfMonth) {
return DiscordianDate.create(prolepticYear, month, dayOfMonth);
}
/**
* Obtains a {@code DiscordianDate} from a temporal object.
*
* This obtains a date in the Discordian 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 DiscordianDate}.
*
* The conversion typically uses the {@link ChronoField#EPOCH_DAY EPOCH_DAY}
* field, which is standardized across calendar systems.
*
* This method matches the signature of the functional interface {@link TemporalQuery}
* allowing it to be used as a query via method reference, {@code DiscordianDate::from}.
*
* @param temporal the temporal object to convert, not null
* @return the date in Discordian calendar system, not null
* @throws DateTimeException if unable to convert to a {@code DiscordianDate}
*/
public static DiscordianDate from(TemporalAccessor temporal) {
if (temporal instanceof DiscordianDate) {
return (DiscordianDate) temporal;
}
return DiscordianDate.ofEpochDay(temporal.getLong(EPOCH_DAY));
}
//-----------------------------------------------------------------------
/**
* Obtains a {@code DiscordianDate} representing a date in the Discordian calendar
* system from the proleptic-year and day-of-year fields.
*
* This returns a {@code DiscordianDate} with the specified fields.
* The day must be valid for the year, otherwise an exception will be thrown.
*
* @param prolepticYear the Discordian proleptic-year
* @param dayOfYear the Discordian day-of-year, from 1 to 366
* @return the date in Discordian 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 DiscordianDate ofYearDay(int prolepticYear, int dayOfYear) {
DiscordianChronology.YEAR_RANGE.checkValidValue(prolepticYear, YEAR);
DAY_OF_YEAR.checkValidValue(dayOfYear);
boolean leap = DiscordianChronology.INSTANCE.isLeapYear(prolepticYear);
if (dayOfYear == 366 && !leap) {
throw new DateTimeException("Invalid date 'DayOfYear 366' as '" + prolepticYear + "' is not a leap year");
}
if (leap) {
if (dayOfYear == ST_TIBS_OFFSET) {
// Take care of special case of St Tib's Day.
return new DiscordianDate(prolepticYear, 0, 0);
} else if (dayOfYear > ST_TIBS_OFFSET) {
// Offset dayOfYear to account for added day.
dayOfYear--;
}
}
int month = (dayOfYear - 1) / DAYS_IN_MONTH + 1;
int dayOfMonth = (dayOfYear - 1) % DAYS_IN_MONTH + 1;
return new DiscordianDate(prolepticYear, month, dayOfMonth);
}
/**
* Obtains a {@code DiscordianDate} representing a date in the Discordian calendar
* system from the epoch-day.
*
* @param epochDay the epoch day to convert based on 1970-01-01 (ISO)
* @return the date in Discordian calendar system, not null
* @throws DateTimeException if the epoch-day is out of range
*/
static DiscordianDate ofEpochDay(final long epochDay) {
DiscordianChronology.EPOCH_DAY_RANGE.checkValidValue(epochDay, EPOCH_DAY);
// use of Discordian 1167 makes leap year at end of long cycle
long discordianEpochDay = epochDay + DISCORDIAN_1167_TO_ISO_1970;
long longCycle = Math.floorDiv(discordianEpochDay, DAYS_PER_LONG_CYCLE);
long daysInLongCycle = Math.floorMod(discordianEpochDay, DAYS_PER_LONG_CYCLE);
if (daysInLongCycle == DAYS_PER_LONG_CYCLE - 1) {
int year = (int) (longCycle * 400) + 400;
return ofYearDay(year + OFFSET_FROM_ISO_0000, 366);
}
int cycle = (int) daysInLongCycle / DAYS_PER_CYCLE;
int dayInCycle = (int) daysInLongCycle % DAYS_PER_CYCLE;
int shortCycle = dayInCycle / DAYS_PER_SHORT_CYCLE;
int dayInShortCycle = dayInCycle % DAYS_PER_SHORT_CYCLE;
if (dayInShortCycle == DAYS_PER_SHORT_CYCLE - 1) {
int year = (int) (longCycle * 400) + (cycle * 100) + (shortCycle * 4) + 4;
return ofYearDay(year + OFFSET_FROM_ISO_0000, 366);
}
int year = (int) (longCycle * 400) + (cycle * 100) + (shortCycle * 4) + (dayInShortCycle / 365) + 1;
int dayOfYear = (dayInShortCycle % 365) + 1;
return ofYearDay(year + OFFSET_FROM_ISO_0000, dayOfYear);
}
private static DiscordianDate resolvePreviousValid(int prolepticYear, int month, int day) {
switch (month) {
case 0:
day = 0;
if (DiscordianChronology.INSTANCE.isLeapYear(prolepticYear)) {
break;
}
month = 1;
// fall through
default:
if (day == 0) {
day = ST_TIBS_OFFSET;
}
}
return new DiscordianDate(prolepticYear, month, day);
}
private static long getLeapYearsBefore(long year) {
long offsetYear = year - OFFSET_FROM_ISO_0000 - 1;
return Math.floorDiv(offsetYear, 4) - Math.floorDiv(offsetYear, 100) + Math.floorDiv(offsetYear, 400);
}
/**
* Creates a {@code DiscordianDate} validating the input.
*
* @param prolepticYear the Discordian proleptic-year
* @param month the Discordian month-of-year, from 1 to 5
* @param dayOfMonth the Discordian day-of-month, from 1 to 73
* @return the date in Discordian 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 month-year
*/
static DiscordianDate create(int prolepticYear, int month, int dayOfMonth) {
DiscordianChronology.YEAR_RANGE.checkValidValue(prolepticYear, YEAR);
DiscordianChronology.MONTH_OF_YEAR_RANGE.checkValidValue(month, MONTH_OF_YEAR);
DiscordianChronology.DAY_OF_MONTH_RANGE.checkValidValue(dayOfMonth, DAY_OF_MONTH);
if (month == 0 || dayOfMonth == 0) {
if (month != 0 || dayOfMonth != 0) {
throw new DateTimeException("Invalid date '" + month + " " + dayOfMonth + "' as St. Tib's Day is the only special day inserted in a nonexistant month.");
} else if (!DiscordianChronology.INSTANCE.isLeapYear(prolepticYear)) {
throw new DateTimeException("Invalid date 'St. Tibs Day' as '" + prolepticYear + "' is not a leap year");
}
}
return new DiscordianDate(prolepticYear, month, dayOfMonth);
}
//-----------------------------------------------------------------------
/**
* Creates an instance from validated data.
*
* @param prolepticYear the Discordian proleptic-year
* @param month the Discordian month, from 1 to 5
* @param dayOfMonth the Discordian day-of-month, from 1 to 73
*/
private DiscordianDate(int prolepticYear, int month, int dayOfMonth) {
this.prolepticYear = prolepticYear;
this.month = (short) month;
this.day = (short) dayOfMonth;
}
/**
* Validates the object.
*
* @return the resolved date, not null
*/
private Object readResolve() {
return DiscordianDate.create(prolepticYear, month, day);
}
//-----------------------------------------------------------------------
@Override
int getProlepticYear() {
return prolepticYear;
}
@Override
int getMonth() {
return month;
}
@Override
int getDayOfMonth() {
return day;
}
@Override
int getDayOfYear() {
// St. Tib's Day isn't part of any month, but would be the 60th day of the year.
if (month == 0 && day == 0) {
return ST_TIBS_OFFSET;
}
int dayOfYear = (month - 1) * DAYS_IN_MONTH + day;
// If after St. Tib's day, need to offset to account for it.
return dayOfYear + (dayOfYear >= ST_TIBS_OFFSET && isLeapYear() ? 1 : 0);
}
@Override
AbstractDate withDayOfYear(int value) {
return plusDays(value - getDayOfYear());
}
@Override
int lengthOfWeek() {
return DAYS_IN_WEEK;
}
@Override
int lengthOfYearInMonths() {
return MONTHS_IN_YEAR;
}
@Override
ValueRange rangeAlignedWeekOfMonth() {
return month == 0 ? ValueRange.of(0, 0) : ValueRange.of(1, 15);
}
@Override
DiscordianDate resolvePrevious(int newYear, int newMonth, int dayOfMonth) {
return resolvePreviousValid(newYear, newMonth, dayOfMonth);
}
//-----------------------------------------------------------------------
@Override
public ValueRange range(TemporalField field) {
if (field instanceof ChronoField) {
if (isSupported(field)) {
ChronoField f = (ChronoField) field;
switch (f) {
case ALIGNED_DAY_OF_WEEK_IN_MONTH:
return month == 0 ? ValueRange.of(0, 0) : ValueRange.of(1, DAYS_IN_WEEK);
case ALIGNED_DAY_OF_WEEK_IN_YEAR:
return ValueRange.of(isLeapYear() ? 0 : 1, DAYS_IN_WEEK);
case ALIGNED_WEEK_OF_YEAR:
return ValueRange.of(isLeapYear() ? 0 : 1, WEEKS_IN_YEAR);
case DAY_OF_MONTH:
return month == 0 ? ValueRange.of(0, 0) : ValueRange.of(1, DAYS_IN_MONTH);
case DAY_OF_WEEK:
return month == 0 ? ValueRange.of(0, 0) : ValueRange.of(1, DAYS_IN_WEEK);
case MONTH_OF_YEAR:
return ValueRange.of(isLeapYear() ? 0 : 1, MONTHS_IN_YEAR);
default:
break;
}
}
}
return super.range(field);
}
//-----------------------------------------------------------------------
@Override
public long getLong(TemporalField field) {
if (field instanceof ChronoField) {
switch ((ChronoField) field) {
case ALIGNED_DAY_OF_WEEK_IN_MONTH:
return month == 0 ? 0 : super.getLong(field);
case ALIGNED_DAY_OF_WEEK_IN_YEAR:
return getDayOfWeek();
case ALIGNED_WEEK_OF_MONTH:
return month == 0 ? 0 : super.getLong(field);
case ALIGNED_WEEK_OF_YEAR:
if (month == 0) {
return 0;
} else {
return ((getDayOfYear() - (getDayOfYear() >= ST_TIBS_OFFSET && isLeapYear() ? 1 : 0) - 1) / DAYS_IN_WEEK) + 1;
}
default:
break;
}
}
return super.getLong(field);
}
@Override
int getDayOfWeek() {
if (month == 0) {
return 0;
}
// Need to offset to account for added day.
int dayOfYear = getDayOfYear() - (getDayOfYear() >= ST_TIBS_OFFSET && isLeapYear() ? 1 : 0);
return (dayOfYear - 1) % DAYS_IN_WEEK + 1;
}
@Override
long getProlepticMonth() {
// Consider St. Tib's day to be part of the 1st month for this count.
return prolepticYear * MONTHS_IN_YEAR + (month == 0 ? 1 : month) - 1;
}
long getProlepticWeek() {
// Consider St. Tib's day to be part of the 12th week for this count.
return ((long) prolepticYear) * WEEKS_IN_YEAR + (month == 0 ? ST_TIBS_OFFSET / DAYS_IN_WEEK : getLong(ALIGNED_WEEK_OF_YEAR)) - 1;
}
//-----------------------------------------------------------------------
/**
* Gets the chronology of this date, which is the Discordian calendar system.
*
* The {@code Chronology} represents the calendar system in use.
* The era and other fields in {@link ChronoField} are defined by the chronology.
*
* @return the Discordian chronology, not null
*/
@Override
public DiscordianChronology getChronology() {
return DiscordianChronology.INSTANCE;
}
/**
* Gets the era applicable at this date.
*
* The Discordian calendar system has one era, 'YOLD',
* defined by {@link DiscordianEra}.
*
* @return the era YOLD, not null
*/
@Override
public DiscordianEra getEra() {
return DiscordianEra.YOLD;
}
@Override
public int lengthOfMonth() {
return month == 0 ? 1 : DAYS_IN_MONTH;
}
//-------------------------------------------------------------------------
@Override
public DiscordianDate with(TemporalAdjuster adjuster) {
return (DiscordianDate) adjuster.adjustInto(this);
}
@Override
public DiscordianDate with(TemporalField field, long newValue) {
if (field instanceof ChronoField) {
ChronoField f = (ChronoField) field;
DiscordianChronology.INSTANCE.range(f).checkValidValue(newValue, f);
int nvalue = (int) newValue;
// trying to move to St Tibs
if (nvalue == 0 && isLeapYear()) {
switch (f) {
case DAY_OF_WEEK:
case ALIGNED_DAY_OF_WEEK_IN_MONTH:
case ALIGNED_DAY_OF_WEEK_IN_YEAR:
case ALIGNED_WEEK_OF_MONTH:
case ALIGNED_WEEK_OF_YEAR:
case DAY_OF_MONTH:
case MONTH_OF_YEAR:
if (month == 0) {
return this;
}
return DiscordianDate.create(prolepticYear, 0, 0);
default:
break;
}
}
// currently on St Tibs
if (month == 0) {
switch (f) {
case YEAR:
case YEAR_OF_ERA:
if (DiscordianChronology.INSTANCE.isLeapYear(nvalue)) {
return DiscordianDate.create(nvalue, 0, 0);
}
// fall through
default:
return DiscordianDate.create(prolepticYear, 1, 60).with(field, newValue);
}
}
// validate range (generally excluding zero value)
range(f).checkValidValue(newValue, f);
switch (f) {
case DAY_OF_WEEK:
case ALIGNED_DAY_OF_WEEK_IN_MONTH:
case ALIGNED_DAY_OF_WEEK_IN_YEAR:
if (month == 1
&& day >= ST_TIBS_OFFSET - DAYS_IN_WEEK + 1 && day < ST_TIBS_OFFSET + 1
&& isLeapYear()) {
int currentDayOfWeek = getDayOfWeek();
// St. Tib's Day is between the 4th and 5th days of the week...
if (currentDayOfWeek < DAYS_IN_WEEK && nvalue == DAYS_IN_WEEK) {
return (DiscordianDate) plusDays(nvalue - currentDayOfWeek + 1);
} else if (currentDayOfWeek == DAYS_IN_WEEK && nvalue < DAYS_IN_WEEK) {
return (DiscordianDate) plusDays(nvalue - currentDayOfWeek - 1);
}
}
break;
case ALIGNED_WEEK_OF_MONTH:
case ALIGNED_WEEK_OF_YEAR:
if ((month == 1 || field == ALIGNED_WEEK_OF_YEAR) && isLeapYear()) {
// St. Tib's Day is in the middle of the 12th week...
int alignedWeek = (int) getLong(field);
int currentDayOfWeek = getDayOfWeek();
if ((alignedWeek > ST_TIBS_OFFSET / DAYS_IN_WEEK || (alignedWeek == ST_TIBS_OFFSET / DAYS_IN_WEEK && currentDayOfWeek == DAYS_IN_WEEK))
&& (nvalue < ST_TIBS_OFFSET / DAYS_IN_WEEK || (nvalue == ST_TIBS_OFFSET / DAYS_IN_WEEK && currentDayOfWeek < DAYS_IN_WEEK))) {
return (DiscordianDate) plusDays((newValue - alignedWeek) * DAYS_IN_WEEK - 1);
} else if ((nvalue > ST_TIBS_OFFSET / DAYS_IN_WEEK || (nvalue == ST_TIBS_OFFSET / DAYS_IN_WEEK && currentDayOfWeek == DAYS_IN_WEEK))
&& (alignedWeek < ST_TIBS_OFFSET / DAYS_IN_WEEK || (alignedWeek == ST_TIBS_OFFSET / DAYS_IN_WEEK && currentDayOfWeek < DAYS_IN_WEEK))) {
return (DiscordianDate) plusDays((newValue - alignedWeek) * lengthOfWeek() + 1);
}
}
break;
default:
break;
}
}
return (DiscordianDate) super.with(field, newValue);
}
//-----------------------------------------------------------------------
@Override
public DiscordianDate plus(TemporalAmount amount) {
return (DiscordianDate) amount.addTo(this);
}
@Override
public DiscordianDate plus(long amountToAdd, TemporalUnit unit) {
if (unit instanceof ChronoUnit) {
ChronoUnit f = (ChronoUnit) unit;
switch (f) {
case WEEKS:
return plusWeeks(amountToAdd);
case MONTHS:
return plusMonths(amountToAdd);
default:
break;
}
}
return (DiscordianDate) super.plus(amountToAdd, unit);
}
@Override
DiscordianDate plusMonths(long months) {
if (months == 0) {
return this;
}
long calcEm = Math.addExact(getProlepticMonth(), months);
int newYear = Math.toIntExact(Math.floorDiv(calcEm, MONTHS_IN_YEAR));
int newMonth = (int) (Math.floorMod(calcEm, MONTHS_IN_YEAR) + 1);
// If the starting date was St. Tib's Day, attempt to retain that status.
if (month == 0 && newMonth == 1) {
newMonth = 0;
}
return resolvePrevious(newYear, newMonth, day);
}
@Override
DiscordianDate plusWeeks(long weeks) {
if (weeks == 0) {
return this;
}
long calcEm = Math.addExact(getProlepticWeek(), weeks);
int newYear = Math.toIntExact(Math.floorDiv(calcEm, WEEKS_IN_YEAR));
// Give St. Tib's Day the same day-of-week as the day after it.
int newDayOfYear = (int) (Math.floorMod(calcEm, WEEKS_IN_YEAR)) * DAYS_IN_WEEK + (month == 0 ? DAYS_IN_WEEK : get(DAY_OF_WEEK));
// Need to offset day-of-year if leap year, and not heading to St. Tib's Day again.
if (DiscordianChronology.INSTANCE.isLeapYear(newYear)
&& (newDayOfYear > ST_TIBS_OFFSET || (newDayOfYear == ST_TIBS_OFFSET && month != 0))) {
newDayOfYear++;
}
return ofYearDay(newYear, newDayOfYear);
}
@Override
public DiscordianDate minus(TemporalAmount amount) {
return (DiscordianDate) amount.subtractFrom(this);
}
@Override
public DiscordianDate minus(long amountToSubtract, TemporalUnit unit) {
return (amountToSubtract == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit));
}
//-------------------------------------------------------------------------
@Override // for covariant return type
@SuppressWarnings("unchecked")
public ChronoLocalDateTime
* The Discordian calendar system has one era.
* The current era, for years from 1 onwards, is known as the 'Year of Our Lady of Discord'.
* No other eras are supported.
*
* The start of the Discordian epoch {@code 0001-01-01 (Discordian)} is {@code -1165-01-01 (ISO)}.
*
* Do not use {@code ordinal()} to obtain the numeric representation of {@code DiscordianEra}.
* Use {@code getValue()} instead.
*
*
* {@code DiscordianEra} is an enum representing the Discordian era of YOLD.
* This factory allows the enum to be obtained from the {@code int} value.
* Only the value 1 is ever accepted.
*
* @param era the YOLD value to represent, which must be 1 (YOLD)
* @return the era singleton, not null
* @throws DateTimeException if the value is invalid
*/
public static DiscordianEra of(int era) {
switch (era) {
case 1:
return YOLD;
default:
throw new DateTimeException("Invalid era: " + era);
}
}
//-----------------------------------------------------------------------
/**
* Gets the numeric era {@code int} value.
*
* The era YOLD has the value 1.
*
* @return the era value, 1 for YOLD
*/
@Override
public int getValue() {
return ordinal();
}
}
threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/EthiopicChronology.java 0000664 0000000 0000000 00000032124 13434511741 0030610 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2007-present, 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 org.threeten.extra.chrono;
import java.io.Serializable;
import java.time.Clock;
import java.time.DateTimeException;
import java.time.Instant;
import java.time.ZoneId;
import java.time.chrono.ChronoLocalDateTime;
import java.time.chrono.ChronoZonedDateTime;
import java.time.chrono.Chronology;
import java.time.chrono.Era;
import java.time.format.ResolverStyle;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalField;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Map;
/**
* The Ethiopic calendar system.
*
* This chronology defines the rules of the Ethiopic calendar system.
* This calendar system is primarily used in Ethiopia.
* Dates are aligned such that {@code 0001-01-01 (Ethiopic)} is {@code 0284-08-29 (ISO)}.
*
* The fields are defined as follows:
*
* 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 - 'Ethiopic'
* @see #getCalendarType()
*/
@Override
public String getId() {
return "Ethiopic";
}
/**
* Gets the calendar type of the underlying calendar system - 'ethiopic'.
*
* The calendar type is an identifier defined by the
* Unicode Locale Data Markup Language (LDML) 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 - 'ethiopic'
* @see #getId()
*/
@Override
public String getCalendarType() {
return "ethiopic";
}
//-----------------------------------------------------------------------
/**
* Obtains a local date in Ethiopic calendar system from the
* era, year-of-era, month-of-year and day-of-month fields.
*
* @param era the Ethiopic era, not null
* @param yearOfEra the year-of-era
* @param month the month-of-year
* @param dayOfMonth the day-of-month
* @return the Ethiopic local date, not null
* @throws DateTimeException if unable to create the date
* @throws ClassCastException if the {@code era} is not a {@code EthiopicEra}
*/
@Override
public EthiopicDate date(Era era, int yearOfEra, int month, int dayOfMonth) {
return date(prolepticYear(era, yearOfEra), month, dayOfMonth);
}
/**
* Obtains a local date in Ethiopic 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 Ethiopic local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override
public EthiopicDate date(int prolepticYear, int month, int dayOfMonth) {
return EthiopicDate.of(prolepticYear, month, dayOfMonth);
}
/**
* Obtains a local date in Ethiopic calendar system from the
* era, year-of-era and day-of-year fields.
*
* @param era the Ethiopic era, not null
* @param yearOfEra the year-of-era
* @param dayOfYear the day-of-year
* @return the Ethiopic local date, not null
* @throws DateTimeException if unable to create the date
* @throws ClassCastException if the {@code era} is not a {@code EthiopicEra}
*/
@Override
public EthiopicDate dateYearDay(Era era, int yearOfEra, int dayOfYear) {
return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear);
}
/**
* Obtains a local date in Ethiopic calendar system from the
* proleptic-year and day-of-year fields.
*
* @param prolepticYear the proleptic-year
* @param dayOfYear the day-of-year
* @return the Ethiopic local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override
public EthiopicDate dateYearDay(int prolepticYear, int dayOfYear) {
return EthiopicDate.ofYearDay(prolepticYear, dayOfYear);
}
/**
* Obtains a local date in the Ethiopic calendar system from the epoch-day.
*
* @param epochDay the epoch day
* @return the Ethiopic local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override // override with covariant return type
public EthiopicDate dateEpochDay(long epochDay) {
return EthiopicDate.ofEpochDay(epochDay);
}
//-------------------------------------------------------------------------
/**
* Obtains the current Ethiopic local date from the system clock in the default time-zone.
*
* This will query the {@link Clock#systemDefaultZone() system clock} in the default
* time-zone to obtain the current date.
*
* Using this method will prevent the ability to use an alternate clock for testing
* because the clock is hard-coded.
*
* @return the current Ethiopic 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 EthiopicDate dateNow() {
return EthiopicDate.now();
}
/**
* Obtains the current Ethiopic local date from the system clock in the specified time-zone.
*
* 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.
*
* 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 Ethiopic local date using the system clock, not null
* @throws DateTimeException if unable to create the date
*/
@Override // override with covariant return type
public EthiopicDate dateNow(ZoneId zone) {
return EthiopicDate.now(zone);
}
/**
* Obtains the current Ethiopic local date from the specified clock.
*
* 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 Ethiopic local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override // override with covariant return type
public EthiopicDate dateNow(Clock clock) {
return EthiopicDate.now(clock);
}
//-------------------------------------------------------------------------
/**
* Obtains a Ethiopic local date from another date-time object.
*
* @param temporal the date-time object to convert, not null
* @return the Ethiopic local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override
public EthiopicDate date(TemporalAccessor temporal) {
return EthiopicDate.from(temporal);
}
/**
* Obtains a Ethiopic local date-time from another date-time object.
*
* @param temporal the date-time object to convert, not null
* @return the Ethiopic local date-time, not null
* @throws DateTimeException if unable to create the date-time
*/
@Override
@SuppressWarnings("unchecked")
public ChronoLocalDateTime
* This date operates using the {@linkplain EthiopicChronology Ethiopic calendar}.
* This calendar system is primarily used in Ethiopia.
* Dates are aligned such that {@code 0001-01-01 (Ethiopic)} is {@code 0008-08-27 (ISO)}.
*
*
* This class must be treated as a value type. Do not synchronize, rely on the
* identity hash code or use the distinction between equals() and ==.
*/
public final class EthiopicDate
extends AbstractNileDate
implements ChronoLocalDate, Serializable {
// TODO: Check epoch day
// TODO: Check conversion year (and Coptic)
/**
* Serialization version.
*/
private static final long serialVersionUID = -268768729L;
/**
* The difference between the ISO and Ethiopic epoch day count.
*/
private static final int EPOCH_DAY_DIFFERENCE = 716367;
/**
* The proleptic year.
*/
private final int prolepticYear;
/**
* The month.
*/
private final short month;
/**
* The day.
*/
private final short day;
//-----------------------------------------------------------------------
/**
* Obtains the current {@code EthiopicDate} from the system clock in the default time-zone.
*
* This will query the {@link Clock#systemDefaultZone() system clock} in the default
* time-zone to obtain the current date.
*
* 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 EthiopicDate now() {
return now(Clock.systemDefaultZone());
}
/**
* Obtains the current {@code EthiopicDate} from the system clock in the specified time-zone.
*
* 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.
*
* 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 EthiopicDate now(ZoneId zone) {
return now(Clock.system(zone));
}
/**
* Obtains the current {@code EthiopicDate} from the specified clock.
*
* 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 EthiopicDate now(Clock clock) {
LocalDate now = LocalDate.now(clock);
return EthiopicDate.ofEpochDay(now.toEpochDay());
}
/**
* Obtains a {@code EthiopicDate} representing a date in the Ethiopic calendar
* system from the proleptic-year, month-of-year and day-of-month fields.
*
* This returns a {@code EthiopicDate} with the specified fields.
* The day must be valid for the year and month, otherwise an exception will be thrown.
*
* @param prolepticYear the Ethiopic proleptic-year
* @param month the Ethiopic month-of-year, from 1 to 13
* @param dayOfMonth the Ethiopic day-of-month, from 1 to 30
* @return the date in Ethiopic 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 EthiopicDate of(int prolepticYear, int month, int dayOfMonth) {
return EthiopicDate.create(prolepticYear, month, dayOfMonth);
}
/**
* Obtains a {@code EthiopicDate} from a temporal object.
*
* This obtains a date in the Ethiopic 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 EthiopicDate}.
*
* The conversion typically uses the {@link ChronoField#EPOCH_DAY EPOCH_DAY}
* field, which is standardized across calendar systems.
*
* This method matches the signature of the functional interface {@link TemporalQuery}
* allowing it to be used as a query via method reference, {@code EthiopicDate::from}.
*
* @param temporal the temporal object to convert, not null
* @return the date in Ethiopic calendar system, not null
* @throws DateTimeException if unable to convert to a {@code EthiopicDate}
*/
public static EthiopicDate from(TemporalAccessor temporal) {
if (temporal instanceof EthiopicDate) {
return (EthiopicDate) temporal;
}
return EthiopicDate.ofEpochDay(temporal.getLong(EPOCH_DAY));
}
//-----------------------------------------------------------------------
/**
* Obtains a {@code EthiopicDate} representing a date in the Ethiopic calendar
* system from the proleptic-year and day-of-year fields.
*
* This returns a {@code EthiopicDate} with the specified fields.
* The day must be valid for the year, otherwise an exception will be thrown.
*
* @param prolepticYear the Ethiopic proleptic-year
* @param dayOfYear the Ethiopic day-of-year, from 1 to 366
* @return the date in Ethiopic 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 EthiopicDate ofYearDay(int prolepticYear, int dayOfYear) {
EthiopicChronology.YEAR_RANGE.checkValidValue(prolepticYear, YEAR);
DAY_OF_YEAR.range().checkValidValue(dayOfYear, DAY_OF_YEAR);
if (dayOfYear == 366 && EthiopicChronology.INSTANCE.isLeapYear(prolepticYear) == false) {
throw new DateTimeException("Invalid date 'Pagumen 6' as '" + prolepticYear + "' is not a leap year");
}
return new EthiopicDate(prolepticYear, (dayOfYear - 1) / 30 + 1, (dayOfYear - 1) % 30 + 1);
}
/**
* Obtains a {@code EthiopicDate} representing a date in the Ethiopic calendar
* system from the epoch-day.
*
* @param epochDay the epoch day to convert based on 1970-01-01 (ISO)
* @return the date in Ethiopic calendar system, not null
* @throws DateTimeException if the epoch-day is out of range
*/
static EthiopicDate ofEpochDay(final long epochDay) {
EPOCH_DAY.range().checkValidValue(epochDay, EPOCH_DAY); // validate outer bounds
long ethiopicED = epochDay + EPOCH_DAY_DIFFERENCE;
int adjustment = 0;
if (ethiopicED < 0) {
ethiopicED = ethiopicED + (1461L * (1_000_000L / 4));
adjustment = -1_000_000;
}
int prolepticYear = (int) (((ethiopicED * 4) + 1463) / 1461);
int startYearEpochDay = (prolepticYear - 1) * 365 + (prolepticYear / 4);
int doy0 = (int) (ethiopicED - startYearEpochDay);
int month = doy0 / 30 + 1;
int dom = doy0 % 30 + 1;
return new EthiopicDate(prolepticYear + adjustment, month, dom);
}
private static EthiopicDate resolvePreviousValid(int prolepticYear, int month, int day) {
if (month == 13 && day > 5) {
day = EthiopicChronology.INSTANCE.isLeapYear(prolepticYear) ? 6 : 5;
}
return new EthiopicDate(prolepticYear, month, day);
}
/**
* Creates a {@code EthiopicDate} validating the input.
*
* @param prolepticYear the Ethiopic proleptic-year
* @param month the Ethiopic month-of-year, from 1 to 13
* @param dayOfMonth the Ethiopic day-of-month, from 1 to 30
* @return the date in Ethiopic 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 month-year
*/
static EthiopicDate create(int prolepticYear, int month, int dayOfMonth) {
EthiopicChronology.YEAR_RANGE.checkValidValue(prolepticYear, YEAR);
EthiopicChronology.MOY_RANGE.checkValidValue(month, MONTH_OF_YEAR);
EthiopicChronology.DOM_RANGE.checkValidValue(dayOfMonth, DAY_OF_MONTH);
if (month == 13 && dayOfMonth > 5) {
if (EthiopicChronology.INSTANCE.isLeapYear(prolepticYear)) {
if (dayOfMonth > 6) {
throw new DateTimeException("Invalid date 'Pagumen " + dayOfMonth + "', valid range from 1 to 5, or 1 to 6 in a leap year");
}
} else {
if (dayOfMonth == 6) {
throw new DateTimeException("Invalid date 'Pagumen 6' as '" + prolepticYear + "' is not a leap year");
} else {
throw new DateTimeException("Invalid date 'Pagumen " + dayOfMonth + "', valid range from 1 to 5, or 1 to 6 in a leap year");
}
}
}
return new EthiopicDate(prolepticYear, month, dayOfMonth);
}
//-----------------------------------------------------------------------
/**
* Creates an instance from validated data.
*
* @param prolepticYear the Ethiopic proleptic-year
* @param month the Ethiopic month, from 1 to 13
* @param dayOfMonth the Ethiopic day-of-month, from 1 to 30
*/
private EthiopicDate(int prolepticYear, int month, int dayOfMonth) {
this.prolepticYear = prolepticYear;
this.month = (short) month;
this.day = (short) dayOfMonth;
}
/**
* Validates the object.
*
* @return the resolved date, not null
*/
private Object readResolve() {
return EthiopicDate.create(prolepticYear, month, day);
}
//-----------------------------------------------------------------------
@Override
int getEpochDayDifference() {
return EPOCH_DAY_DIFFERENCE;
}
@Override
int getProlepticYear() {
return prolepticYear;
}
@Override
int getMonth() {
return month;
}
@Override
int getDayOfMonth() {
return day;
}
@Override
EthiopicDate resolvePrevious(int newYear, int newMonth, int dayOfMonth) {
return resolvePreviousValid(newYear, newMonth, dayOfMonth);
}
//-----------------------------------------------------------------------
/**
* Gets the chronology of this date, which is the Ethiopic calendar system.
*
* The {@code Chronology} represents the calendar system in use.
* The era and other fields in {@link ChronoField} are defined by the chronology.
*
* @return the Ethiopic chronology, not null
*/
@Override
public EthiopicChronology getChronology() {
return EthiopicChronology.INSTANCE;
}
/**
* Gets the era applicable at this date.
*
* The Ethiopic calendar system has two eras, 'INCARNATION' and 'BEFORE_INCARNATION',
* defined by {@link EthiopicEra}.
*
* @return the era applicable at this date, not null
*/
@Override
public EthiopicEra getEra() {
return (prolepticYear >= 1 ? EthiopicEra.INCARNATION : EthiopicEra.BEFORE_INCARNATION);
}
//-------------------------------------------------------------------------
@Override
public EthiopicDate with(TemporalAdjuster adjuster) {
return (EthiopicDate) adjuster.adjustInto(this);
}
@Override
public EthiopicDate with(TemporalField field, long newValue) {
return (EthiopicDate) super.with(field, newValue);
}
//-----------------------------------------------------------------------
@Override
public EthiopicDate plus(TemporalAmount amount) {
return (EthiopicDate) amount.addTo(this);
}
@Override
public EthiopicDate plus(long amountToAdd, TemporalUnit unit) {
return (EthiopicDate) super.plus(amountToAdd, unit);
}
@Override
public EthiopicDate minus(TemporalAmount amount) {
return (EthiopicDate) amount.subtractFrom(this);
}
@Override
public EthiopicDate minus(long amountToSubtract, TemporalUnit unit) {
return (amountToSubtract == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit));
}
//-------------------------------------------------------------------------
@Override // for covariant return type
@SuppressWarnings("unchecked")
public ChronoLocalDateTime
* The Ethiopic calendar system has two eras.
* The current era, for years from 1 onwards, is known as the 'Incarnation 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 Incarnation Era' era.
*
* The start of the Ethiopic epoch {@code 0001-01-01 (Ethiopic)} is {@code 0284-08-29 (ISO)}.
*
* Do not use {@code ordinal()} to obtain the numeric representation of {@code EthiopicEra}.
* Use {@code getValue()} instead.
*
*
* {@code EthiopicEra} is an enum representing the Ethiopic eras of BEFORE_INCARNATION/INCARNATION.
* This factory allows the enum to be obtained from the {@code int} value.
*
* @param era the BEFORE_INCARNATION/INCARNATION value to represent, from 0 (BEFORE_INCARNATION) to 1 (INCARNATION)
* @return the era singleton, not null
* @throws DateTimeException if the value is invalid
*/
public static EthiopicEra of(int era) {
switch (era) {
case 0:
return BEFORE_INCARNATION;
case 1:
return INCARNATION;
default:
throw new DateTimeException("Invalid era: " + era);
}
}
//-----------------------------------------------------------------------
/**
* Gets the numeric era {@code int} value.
*
* The era BEFORE_INCARNATION has the value 0, while the era INCARNATION has the value 1.
*
* @return the era value, from 0 (BEFORE_INCARNATION) to 1 (INCARNATION)
*/
@Override
public int getValue() {
return ordinal();
}
}
threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/InternationalFixedChronology.java 0000664 0000000 0000000 00000044740 13434511741 0032642 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2007-present, 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 org.threeten.extra.chrono;
import java.io.Serializable;
import java.time.Clock;
import java.time.DateTimeException;
import java.time.Instant;
import java.time.ZoneId;
import java.time.chrono.AbstractChronology;
import java.time.chrono.ChronoLocalDateTime;
import java.time.chrono.ChronoZonedDateTime;
import java.time.chrono.Chronology;
import java.time.chrono.Era;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.ValueRange;
import java.util.Arrays;
import java.util.List;
/**
* The International Fixed calendar system.
*
* This chronology defines the rules of the International Fixed calendar system.
* It shares the leap year rule with the Gregorian calendar.
* Dates are aligned such that {@code 0001-01-01 (International Fixed)} is {@code 0001-01-01 (ISO)}.
*
* This class is proleptic. It implements only years greater or equal to 1.
*
* This class implements a calendar where January 1st is the start of the year.
*
* The fields are defined as follows:
*
* 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 - 'Ifc'
* @see #getCalendarType()
*/
@Override
public String getId() {
return "Ifc";
}
/**
* Gets the calendar type of the underlying calendar system, which returns null.
*
* The Unicode Locale Data Markup Language (LDML) specification
* does not define an identifier for this calendar system, thus null is returned.
*
* @return the calendar system type, null
* @see #getId()
*/
@Override
public String getCalendarType() {
return null;
}
//-----------------------------------------------------------------------
/**
* Obtains a local date in International Fixed calendar system from the
* era, year-of-era, month-of-year and day-of-month fields.
*
* @param era the International Fixed era, not null
* @param yearOfEra the year-of-era
* @param month the month-of-year
* @param dayOfMonth the day-of-month
* @return the International Fixed local date, not null
* @throws DateTimeException if unable to create the date
* @throws ClassCastException if the {@code era} is not a {@code InternationalFixedEra}
*/
@Override
public InternationalFixedDate date(Era era, int yearOfEra, int month, int dayOfMonth) {
return date(prolepticYear(era, yearOfEra), month, dayOfMonth);
}
/**
* Obtains a local date in International Fixed 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 International Fixed local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override
public InternationalFixedDate date(int prolepticYear, int month, int dayOfMonth) {
return InternationalFixedDate.of(prolepticYear, month, dayOfMonth);
}
/**
* Obtains a local date in International Fixed calendar system from the
* era, year-of-era and day-of-year fields.
*
* @param era the International Fixed era, not null
* @param yearOfEra the year-of-era
* @param dayOfYear the day-of-year
* @return the International Fixed local date, not null
* @throws DateTimeException if unable to create the date
* @throws ClassCastException if the {@code era} is not a {@code InternationalFixedEra}
*/
@Override
public InternationalFixedDate dateYearDay(Era era, int yearOfEra, int dayOfYear) {
return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear);
}
/**
* Obtains a local date in International Fixed calendar system from the
* proleptic-year and day-of-year fields.
*
* @param prolepticYear the proleptic-year
* @param dayOfYear the day-of-year
* @return the International Fixed local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override
public InternationalFixedDate dateYearDay(int prolepticYear, int dayOfYear) {
return InternationalFixedDate.ofYearDay(prolepticYear, dayOfYear);
}
/**
* Obtains a local date in the International Fixed calendar system from the epoch-day.
*
* @param epochDay the epoch day
* @return the International Fixed local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override // override with covariant return type
public InternationalFixedDate dateEpochDay(long epochDay) {
return InternationalFixedDate.ofEpochDay(epochDay);
}
//-------------------------------------------------------------------------
/**
* Obtains the current International Fixed local date from the system clock in the default time-zone.
*
* This will query the {@link Clock#systemDefaultZone() system clock} in the default
* time-zone to obtain the current date.
*
* Using this method will prevent the ability to use an alternate clock for testing
* because the clock is hard-coded.
*
* @return the current International Fixed 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 InternationalFixedDate dateNow() {
return InternationalFixedDate.now();
}
/**
* Obtains the current International Fixed local date from the system clock in the specified time-zone.
*
* 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.
*
* 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 International Fixed local date using the system clock, not null
* @throws DateTimeException if unable to create the date
*/
@Override // override with covariant return type
public InternationalFixedDate dateNow(ZoneId zone) {
return InternationalFixedDate.now(zone);
}
/**
* Obtains the current International Fixed local date from the specified clock.
*
* 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 International Fixed local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override // override with covariant return type
public InternationalFixedDate dateNow(Clock clock) {
return InternationalFixedDate.now(clock);
}
//-------------------------------------------------------------------------
/**
* Obtains a International Fixed local date from another date-time object.
*
* @param temporal the date-time object to convert, not null
* @return the International Fixed local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override
public InternationalFixedDate date(TemporalAccessor temporal) {
return InternationalFixedDate.from(temporal);
}
/**
* Obtains a International Fixed local date-time from another date-time object.
*
* @param temporal the date-time object to convert, not null
* @return the International Fixed local date-time, not null
* @throws DateTimeException if unable to create the date-time
*/
@Override
@SuppressWarnings("unchecked")
public ChronoLocalDateTime
* A leap-year is a year of a longer length than normal.
* Leap years in the calendar system match those of the ISO calendar system.
*
* @param year the proleptic-year to check, not validated for range
* @return true if the year is a leap year
*/
@Override
public boolean isLeapYear(long year) {
return ((year & 3) == 0) && ((year % 100) != 0 || (year % 400) == 0);
}
//-----------------------------------------------------------------------
/**
* Creates the chronology era object from the numeric value.
*
* Only one era is supported, CE, with the value 1.
*
* @param eraValue the era value
* @return the calendar system era, not null
* @throws DateTimeException if unable to create the era
*/
@Override
public InternationalFixedEra eraOf(int eraValue) {
return InternationalFixedEra.of(eraValue);
}
/**
* Gets the list of eras for the chronology.
*
* Only one era is supported, CE, with the value 1.
*
* @return the list of eras for the chronology, may be immutable, not null
*/
@Override
public List
* Implements a pure International Fixed calendar (also known as the Cotsworth plan, the Eastman plan,
* the 13 Month calendar or the Equal Month calendar) a solar calendar proposal for calendar reform designed by
* Moses B. Cotsworth, who presented it in 1902.
*
* It provides for a year of 13 months of 28 days each.
* Month 6 has 29 days in a leap year, but the additional day is not part of any week.
* Month 12 always has 29 days, but the additional day is not part of any week.
* It is therefore a perennial calendar, with every date fixed always on the same weekday.
* Though it was never officially adopted in any country, it was the official calendar of the Eastman Kodak Company
* from 1928 to 1989.
*
* This date operates using the {@linkplain InternationalFixedChronology International fixed calendar}.
* This calendar system is a proposed reform calendar system, and is not in common use.
* The International fixed differs from the Gregorian in terms of month count and length, and the leap year rule.
* Dates are aligned such that {@code 0001/01/01 (International fixed)} is {@code 0001-01-01 (ISO)}.
*
* More information is available in the
* International Fixed Calendar
* Wikipedia article.
*
*
* This class must be treated as a value type. Do not synchronize, rely on the
* identity hash code or use the distinction between equals() and ==.
*/
public final class InternationalFixedDate
extends AbstractDate
implements ChronoLocalDate, Serializable {
/**
* Serialization version.
*/
private static final long serialVersionUID = -5501342824322148215L;
/**
* Leap Day as day-of-year
*/
private static final int LEAP_DAY_AS_DAY_OF_YEAR = 6 * DAYS_IN_MONTH + 1;
/**
* The proleptic year.
*/
private final int prolepticYear;
/**
* The month of the year.
*/
private final int month;
/**
* The day of the month.
*/
private final int day;
/**
* The day of year.
*/
private final transient int dayOfYear;
/**
* Is the proleptic year a Leap year ?
*/
private final transient boolean isLeapYear;
/**
* Is the day-of-year a Leap Day ?
*/
private final transient boolean isLeapDay;
/**
* Is the day-of-year a Year Day ?
*/
private final transient boolean isYearDay;
//-----------------------------------------------------------------------
/**
* Obtains the current {@code InternationalFixedDate} from the system clock in the default time-zone.
*
* This will query the {@link Clock#systemDefaultZone() system clock} in the default
* time-zone to obtain the current date.
*
* 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 InternationalFixedDate now() {
return now(Clock.systemDefaultZone());
}
/**
* Obtains the current {@code InternationalFixedDate} from the system clock in the specified time-zone.
*
* 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.
*
* 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 InternationalFixedDate now(ZoneId zone) {
return now(Clock.system(zone));
}
/**
* Obtains the current {@code InternationalFixedDate} from the specified clock.
*
* 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 InternationalFixedDate now(Clock clock) {
LocalDate now = LocalDate.now(clock);
return InternationalFixedDate.ofEpochDay(now.toEpochDay());
}
/**
* Obtains a {@code InternationalFixedDate} representing a date in the International fixed calendar
* system from the proleptic-year, month-of-year and day-of-month fields.
*
* This returns a {@code InternationalFixedDate} with the specified fields.
* The day must be valid for the year and month, otherwise an exception will be thrown.
*
* @param prolepticYear the International fixed proleptic-year
* @param month the International fixed month-of-year, from 1 to 13
* @param dayOfMonth the International fixed day-of-month, from 1 to 28 (29 for Leap Day or Year Day)
* @return the date in International fixed 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 InternationalFixedDate of(int prolepticYear, int month, int dayOfMonth) {
return create(prolepticYear, month, dayOfMonth);
}
//-----------------------------------------------------------------------
/**
* Obtains a {@code InternationalFixedDate} from a temporal object.
*
* This obtains a date in the International fixed 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 InternationalFixedDate}.
*
* The conversion typically uses the {@link ChronoField#EPOCH_DAY EPOCH_DAY}
* field, which is standardized across calendar systems.
*
* This method matches the signature of the functional interface {@link TemporalQuery}
* allowing it to be used as a query via method reference, {@code InternationalFixedDate::from}.
*
* @param temporal the temporal object to convert, not null
* @return the date in the International fixed calendar system, not null
* @throws DateTimeException if unable to convert to a {@code InternationalFixedDate}
*/
public static InternationalFixedDate from(TemporalAccessor temporal) {
if (temporal instanceof InternationalFixedDate) {
return (InternationalFixedDate) temporal;
}
return InternationalFixedDate.ofEpochDay(temporal.getLong(ChronoField.EPOCH_DAY));
}
//-----------------------------------------------------------------------
/**
* Obtains a {@code InternationalFixedDate} representing a date in the International fixed calendar
* system from the proleptic-year and day-of-year fields.
*
* This returns a {@code InternationalFixedDate} with the specified fields.
* The day must be valid for the year, otherwise an exception will be thrown.
*
* @param prolepticYear the International fixed proleptic-year
* @param dayOfYear the International fixed day-of-year, from 1 to 366
* @return the date in International fixed 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 InternationalFixedDate ofYearDay(int prolepticYear, int dayOfYear) {
YEAR_RANGE.checkValidValue(prolepticYear, ChronoField.YEAR_OF_ERA);
ChronoField.DAY_OF_YEAR.checkValidValue(dayOfYear);
boolean isLeapYear = INSTANCE.isLeapYear(prolepticYear);
int lastDoy = (DAYS_IN_YEAR + (isLeapYear ? 1 : 0));
if (dayOfYear > lastDoy) {
throw new DateTimeException("Invalid date 'DayOfYear 366' as '" + prolepticYear + "' is not a leap year");
}
if (dayOfYear == lastDoy) {
return new InternationalFixedDate(prolepticYear, 13, 29);
}
if (dayOfYear == LEAP_DAY_AS_DAY_OF_YEAR && isLeapYear) {
return new InternationalFixedDate(prolepticYear, 6, 29);
}
int doy0 = dayOfYear - 1;
if (dayOfYear >= LEAP_DAY_AS_DAY_OF_YEAR && isLeapYear) {
doy0--;
}
int month = (doy0 / DAYS_IN_MONTH) + 1;
int day = (doy0 % DAYS_IN_MONTH) + 1;
return new InternationalFixedDate(prolepticYear, month, day);
}
/**
* Obtains a {@code InternationalFixedDate} representing a date in the International fixed calendar
* system from the epoch-day.
*
* @param epochDay the epoch day to convert based on 1970-01-01 (ISO)
* @return the date in International fixed calendar system, not null
* @throws DateTimeException if the epoch-day is out of range
*/
static InternationalFixedDate ofEpochDay(long epochDay) {
EPOCH_DAY_RANGE.checkValidValue(epochDay, ChronoField.EPOCH_DAY);
long zeroDay = epochDay + DAYS_0000_TO_1970;
// The two values work great for any dates, just not the first (N/01/01) or the last of the year (N/0/0).
long year = (400 * zeroDay) / DAYS_PER_CYCLE;
long doy = zeroDay - (DAYS_IN_YEAR * year + InternationalFixedChronology.getLeapYearsBefore(year));
boolean isLeapYear = INSTANCE.isLeapYear(year);
// In some cases, N/01/01 (January 1st) results in (N-1)/0/0, i.e. -1 day off.
if (doy == (DAYS_IN_YEAR + 1) && !isLeapYear) {
year += 1;
doy = 1;
}
// In some cases, N/0/0 results in (N+1)/0/0 (rubbish), in a way +1 year off.
if (doy == 0) {
year -= 1;
doy = DAYS_IN_YEAR + (isLeapYear ? 1 : 0);
}
return ofYearDay((int) year, (int) doy);
}
/**
* Consistency check for dates manipulations after calls to
* {@link #plus(long, TemporalUnit)},
* {@link #minus(long, TemporalUnit)},
* {@link #until(AbstractDate, TemporalUnit)} or
* {@link #with(TemporalField, long)}.
*
* @param prolepticYear the International fixed proleptic-year
* @param month the International fixed month, from 1 to 13
* @param day the International fixed day-of-month, from 1 to 28 (29 for Leap Day or Year Day)
* @return the resolved date
*/
private static InternationalFixedDate resolvePreviousValid(int prolepticYear, int month, int day) {
int monthR = Math.min(month, MONTHS_IN_YEAR);
int dayR = Math.min(day,
(monthR == 13 || (monthR == 6 && INSTANCE.isLeapYear(prolepticYear)) ? DAYS_IN_LONG_MONTH : DAYS_IN_MONTH));
return create(prolepticYear, monthR, dayR);
}
//-----------------------------------------------------------------------
/**
* Factory method, validates the given triplet year, month and dayOfMonth.
*
* @param prolepticYear the International fixed proleptic-year
* @param month the International fixed month, from 1 to 13
* @param dayOfMonth the International fixed day-of-month, from 1 to 28 (29 for Leap Day or Year Day)
* @return the International fixed date
* @throws DateTimeException if the date is invalid
*/
static InternationalFixedDate create(int prolepticYear, int month, int dayOfMonth) {
YEAR_RANGE.checkValidValue(prolepticYear, ChronoField.YEAR_OF_ERA);
MONTH_OF_YEAR_RANGE.checkValidValue(month, ChronoField.MONTH_OF_YEAR);
DAY_OF_MONTH_RANGE.checkValidValue(dayOfMonth, ChronoField.DAY_OF_MONTH);
if (dayOfMonth == DAYS_IN_LONG_MONTH && month != 6 && month != MONTHS_IN_YEAR) {
throw new DateTimeException("Invalid date: " + prolepticYear + '/' + month + '/' + dayOfMonth);
}
if (month == 6 && dayOfMonth == DAYS_IN_LONG_MONTH && !INSTANCE.isLeapYear(prolepticYear)) {
throw new DateTimeException("Invalid Leap Day as '" + prolepticYear + "' is not a leap year");
}
return new InternationalFixedDate(prolepticYear, month, dayOfMonth);
}
//-----------------------------------------------------------------------
/**
* Creates an instance from validated data.
*
* @param prolepticYear the International fixed proleptic-year
* @param month the International fixed month, from 1 to 13
* @param dayOfMonth the International fixed day-of-month, from 1 to 28 (29 for Leap Day or Year Day)
*/
private InternationalFixedDate(int prolepticYear, int month, int dayOfMonth) {
this.prolepticYear = prolepticYear;
this.month = month;
this.day = dayOfMonth;
this.isLeapYear = INSTANCE.isLeapYear(prolepticYear);
this.isLeapDay = this.month == 6 && this.day == 29;
this.isYearDay = this.month == 13 && this.day == 29;
this.dayOfYear = ((month - 1) * DAYS_IN_MONTH + day) + (month > 6 && isLeapYear ? 1 : 0);
}
/**
*
* Validates the object.
*
* @return InternationalFixedDate the resolved date, not null
*/
private Object readResolve() {
return InternationalFixedDate.of(prolepticYear, month, day);
}
//-----------------------------------------------------------------------
@Override
int getProlepticYear() {
return prolepticYear;
}
@Override
int getMonth() {
return month;
}
@Override
int getDayOfMonth() {
return day;
}
@Override
int getDayOfYear() {
return dayOfYear;
}
@Override
int lengthOfYearInMonths() {
return MONTHS_IN_YEAR;
}
@Override
int getAlignedDayOfWeekInMonth() {
return getDayOfWeek();
}
@Override
int getAlignedDayOfWeekInYear() {
return getDayOfWeek();
}
@Override
int getAlignedWeekOfMonth() {
if (isSpecialDay()) {
return 0;
}
return ((day - 1) / DAYS_IN_WEEK) + 1;
}
@Override
int getAlignedWeekOfYear() {
if (isSpecialDay()) {
return 0;
}
return (month - 1) * WEEKS_IN_MONTH + ((day - 1) / DAYS_IN_WEEK) + 1;
}
/**
* Returns the day of the week represented by this date.
*
* Leap Day and Year Day are not considered week-days, thus return 0.
*
* @return the day of the week: between 1 and 7, or 0 (Leap Day, Year Day)
*/
@Override
int getDayOfWeek() {
if (isSpecialDay()) {
return 0;
}
return ((day - 1) % DAYS_IN_WEEK) + 1;
}
long getProlepticWeek() {
return getProlepticMonth() * WEEKS_IN_MONTH + ((getDayOfMonth() - 1) / DAYS_IN_WEEK) - 1;
}
private boolean isSpecialDay() {
return day == DAYS_IN_LONG_MONTH;
}
//-----------------------------------------------------------------------
@Override
public ValueRange range(TemporalField field) {
if (field instanceof ChronoField) {
if (isSupported(field)) {
// day 29 is treated as being outside the normal week
ChronoField f = (ChronoField) field;
switch (f) {
case ALIGNED_DAY_OF_WEEK_IN_MONTH:
case ALIGNED_DAY_OF_WEEK_IN_YEAR:
case DAY_OF_WEEK:
return isSpecialDay() ? EMPTY_RANGE : ValueRange.of(1, DAYS_IN_WEEK);
case ALIGNED_WEEK_OF_MONTH:
return isSpecialDay() ? EMPTY_RANGE : ValueRange.of(1, WEEKS_IN_MONTH);
case ALIGNED_WEEK_OF_YEAR:
return isSpecialDay() ? EMPTY_RANGE : ValueRange.of(1, WEEKS_IN_YEAR);
case DAY_OF_MONTH:
return ValueRange.of(1, lengthOfMonth());
case DAY_OF_YEAR:
return isLeapYear ? DAY_OF_YEAR_LEAP_RANGE : DAY_OF_YEAR_NORMAL_RANGE;
case EPOCH_DAY:
return EPOCH_DAY_RANGE;
case ERA:
return ERA_RANGE;
case MONTH_OF_YEAR:
return MONTH_OF_YEAR_RANGE;
default:
break;
}
} else {
throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
}
return super.range(field);
}
@Override
ValueRange rangeAlignedWeekOfMonth() {
// never invoked
return isSpecialDay() ? EMPTY_RANGE : ValueRange.of(1, WEEKS_IN_MONTH);
}
@Override
InternationalFixedDate resolvePrevious(int newYear, int newMonth, int dayOfMonth) {
return resolvePreviousValid(newYear, newMonth, dayOfMonth);
}
//-----------------------------------------------------------------------
/**
* Gets the chronology of this date, which is the International fixed calendar system.
*
* The {@code Chronology} represents the calendar system in use.
* The era and other fields in {@link ChronoField} are defined by the chronology.
*
* @return the International fixed chronology, not null
*/
@Override
public InternationalFixedChronology getChronology() {
return INSTANCE;
}
/**
* Gets the era applicable at this date.
*
* The International fixed calendar system only has one era, 'CE',
* defined by {@link InternationalFixedEra}.
*
* @return the era applicable at this date, not null
*/
@Override
public InternationalFixedEra getEra() {
return InternationalFixedEra.CE;
}
/**
* Returns the length of the month represented by this date.
*
* This returns the length of the month in days.
* Month lengths do not match those of the ISO calendar system.
*
* Months have 28 days, except June which has 29 in leap years
* and December (month 13) which always has 29 days.
*
* @return the length of the month in days
*/
@Override
public int lengthOfMonth() {
return (isLongMonth() ? DAYS_IN_LONG_MONTH : DAYS_IN_MONTH);
}
private boolean isLongMonth() {
return month == 13 || (month == 6 && isLeapYear);
}
/**
* Returns the length of the year represented by this date.
*
* This returns the length of the year in days.
* Year lengths match those of the ISO calendar system.
*
* @return the length of the year in days: 365 or 366
*/
@Override
public int lengthOfYear() {
return DAYS_IN_YEAR + (isLeapYear ? 1 : 0);
}
//-------------------------------------------------------------------------
@Override
public InternationalFixedDate with(TemporalAdjuster adjuster) {
return (InternationalFixedDate) adjuster.adjustInto(this);
}
@Override
public InternationalFixedDate with(TemporalField field, long newValue) {
if (field instanceof ChronoField) {
if (newValue == 0 && isSpecialDay()) {
return this;
}
ChronoField f = (ChronoField) field;
getChronology().range(f).checkValidValue(newValue, f);
int nval = (int) newValue;
switch (f) {
case ALIGNED_DAY_OF_WEEK_IN_MONTH:
case ALIGNED_DAY_OF_WEEK_IN_YEAR:
case DAY_OF_WEEK:
if (newValue == 0 && !isSpecialDay()) {
range(f).checkValidValue(newValue, field);
}
int dom = isSpecialDay() ? 21 : ((getDayOfMonth() - 1) / DAYS_IN_WEEK) * DAYS_IN_WEEK;
return resolvePreviousValid(prolepticYear, month, dom + nval);
case ALIGNED_WEEK_OF_MONTH:
if (newValue == 0 && !isSpecialDay()) {
range(f).checkValidValue(newValue, field);
}
int d = isSpecialDay() ? 1 : day % DAYS_IN_WEEK;
return resolvePreviousValid(prolepticYear, month, (nval - 1) * DAYS_IN_WEEK + d);
case ALIGNED_WEEK_OF_YEAR:
if (newValue == 0 && !isSpecialDay()) {
range(f).checkValidValue(newValue, field);
}
int newMonth = 1 + ((nval - 1) / WEEKS_IN_MONTH);
int newDay = ((nval - 1) % WEEKS_IN_MONTH) * DAYS_IN_WEEK + 1 + ((day - 1) % DAYS_IN_WEEK);
return resolvePreviousValid(prolepticYear, newMonth, newDay);
case DAY_OF_MONTH:
return create(prolepticYear, month, nval);
default:
break;
}
}
return (InternationalFixedDate) super.with(field, newValue);
}
@Override
InternationalFixedDate withDayOfYear(int value) {
return ofYearDay(prolepticYear, value);
}
//-----------------------------------------------------------------------
@Override
public InternationalFixedDate plus(TemporalAmount amount) {
return (InternationalFixedDate) amount.addTo(this);
}
@Override
public InternationalFixedDate plus(long amountToAdd, TemporalUnit unit) {
return (InternationalFixedDate) super.plus(amountToAdd, unit);
}
@Override
InternationalFixedDate plusWeeks(long weeks) {
if (weeks == 0) {
return this;
}
if (weeks % WEEKS_IN_MONTH == 0) {
return plusMonths(weeks / WEEKS_IN_MONTH);
}
long calcEm = Math.addExact(getProlepticWeek(), weeks);
int newYear = Math.toIntExact(Math.floorDiv(calcEm, WEEKS_IN_YEAR));
int newWeek = Math.toIntExact(Math.floorMod(calcEm, WEEKS_IN_YEAR));
int newMonth = 1 + Math.floorDiv(newWeek, WEEKS_IN_MONTH);
//int newDay = 1 + ((newWeek * DAYS_IN_WEEK + ((day - 1) % DAYS_IN_WEEK)) % DAYS_IN_MONTH);
int newDay = 1 + ((newWeek * DAYS_IN_WEEK + 8 +
(isLeapDay ? 0 : isYearDay ? -1 : (day - 1) % DAYS_IN_WEEK) - 1) % DAYS_IN_MONTH);
return create(newYear, newMonth, newDay);
}
@Override
InternationalFixedDate plusMonths(long months) {
if (months == 0) {
return this;
}
if (months % MONTHS_IN_YEAR == 0) {
return plusYears(months / MONTHS_IN_YEAR);
}
int newMonth = (int) Math.addExact(getProlepticMonth(), months);
int newYear = newMonth / MONTHS_IN_YEAR;
newMonth = 1 + (newMonth % MONTHS_IN_YEAR);
return resolvePreviousValid(newYear, newMonth, day);
}
@Override
InternationalFixedDate plusYears(long yearsToAdd) {
if (yearsToAdd == 0) {
return this;
}
int newYear = YEAR_RANGE.checkValidIntValue(Math.addExact(prolepticYear, yearsToAdd), ChronoField.YEAR);
return resolvePreviousValid(newYear, month, day);
}
@Override
public InternationalFixedDate minus(TemporalAmount amount) {
return (InternationalFixedDate) amount.subtractFrom(this);
}
@Override
public InternationalFixedDate minus(long amountToSubtract, TemporalUnit unit) {
return (InternationalFixedDate) super.minus(amountToSubtract, unit);
}
//-------------------------------------------------------------------------
@Override // for covariant return type
@SuppressWarnings("unchecked")
public ChronoLocalDateTime
* The International Fixed calendar system only has one era.
* The current era, for years from 1 onwards, is known as 'Current Era'.
* All previous years are invalid.
*
* The start of the International Fixed epoch {@code 0001/01/01 (IFC)} is {@code 0001-01-01 (ISO)}.
*
* Do not use {@code ordinal()} to obtain the numeric representation of {@code InternationalFixedEra}.
* Use {@code getValue()} instead.
*
*
* {@code InternationalFixedEra} is an enum representing the International Fixed era of CE.
* This factory allows the enum to be obtained from the {@code int} value.
*
* @param era the CE value to represent, only 1 (CE)
* @return the era singleton, not null
* @throws DateTimeException if the value is invalid
*/
public static InternationalFixedEra of(final int era) {
if (era == 1) {
return CE;
}
throw new DateTimeException("Invalid era: " + era);
}
//-----------------------------------------------------------------------
/**
* Gets the numeric era {@code int} value.
*
* The era CE has the value 1.
*
* @return the era value, only 1 (CE)
*/
@Override
public int getValue() {
return ordinal();
}
}
threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/JulianChronology.java 0000664 0000000 0000000 00000035240 13434511741 0030270 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2007-present, 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 org.threeten.extra.chrono;
import java.io.Serializable;
import java.time.Clock;
import java.time.DateTimeException;
import java.time.Instant;
import java.time.ZoneId;
import java.time.chrono.AbstractChronology;
import java.time.chrono.ChronoLocalDateTime;
import java.time.chrono.ChronoZonedDateTime;
import java.time.chrono.Chronology;
import java.time.chrono.Era;
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.Map;
/**
* The Julian calendar system.
*
* This chronology defines the rules of the proleptic Julian calendar system.
* This calendar system is the forerunner to the modern Gregorian and ISO calendars.
* The Julian differs from the Gregorian only in terms of the leap year rule.
* Dates are aligned such that {@code 0001-01-01 (Julian)} is {@code 0000-12-30 (ISO)}.
*
* This class is proleptic. It implements Julian rules to the entire time-line.
*
* This class implements a calendar where January 1st is the start of the year.
* The history of the start of the year is complex and using the current standard
* is the most consistent.
*
* The fields are defined as follows:
*
* 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 - 'Julian'
* @see #getCalendarType()
*/
@Override
public String getId() {
return "Julian";
}
/**
* Gets the calendar type of the underlying calendar system - 'julian'.
*
* The Unicode Locale Data Markup Language (LDML) specification
* does not define an identifier for the Julian calendar, but were it to
* do so, 'julian' is highly likely to be chosen.
*
* @return the calendar system type - 'julian'
* @see #getId()
*/
@Override
public String getCalendarType() {
return "julian";
}
//-----------------------------------------------------------------------
/**
* Obtains a local date in Julian calendar system from the
* era, year-of-era, month-of-year and day-of-month fields.
*
* @param era the Julian era, not null
* @param yearOfEra the year-of-era
* @param month the month-of-year
* @param dayOfMonth the day-of-month
* @return the Julian local date, not null
* @throws DateTimeException if unable to create the date
* @throws ClassCastException if the {@code era} is not a {@code JulianEra}
*/
@Override
public JulianDate date(Era era, int yearOfEra, int month, int dayOfMonth) {
return date(prolepticYear(era, yearOfEra), month, dayOfMonth);
}
/**
* Obtains a local date in Julian 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 Julian local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override
public JulianDate date(int prolepticYear, int month, int dayOfMonth) {
return JulianDate.of(prolepticYear, month, dayOfMonth);
}
/**
* Obtains a local date in Julian calendar system from the
* era, year-of-era and day-of-year fields.
*
* @param era the Julian era, not null
* @param yearOfEra the year-of-era
* @param dayOfYear the day-of-year
* @return the Julian local date, not null
* @throws DateTimeException if unable to create the date
* @throws ClassCastException if the {@code era} is not a {@code JulianEra}
*/
@Override
public JulianDate dateYearDay(Era era, int yearOfEra, int dayOfYear) {
return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear);
}
/**
* Obtains a local date in Julian calendar system from the
* proleptic-year and day-of-year fields.
*
* @param prolepticYear the proleptic-year
* @param dayOfYear the day-of-year
* @return the Julian local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override
public JulianDate dateYearDay(int prolepticYear, int dayOfYear) {
return JulianDate.ofYearDay(prolepticYear, dayOfYear);
}
/**
* Obtains a local date in the Julian calendar system from the epoch-day.
*
* @param epochDay the epoch day
* @return the Julian local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override // override with covariant return type
public JulianDate dateEpochDay(long epochDay) {
return JulianDate.ofEpochDay(epochDay);
}
//-------------------------------------------------------------------------
/**
* Obtains the current Julian local date from the system clock in the default time-zone.
*
* This will query the {@link Clock#systemDefaultZone() system clock} in the default
* time-zone to obtain the current date.
*
* Using this method will prevent the ability to use an alternate clock for testing
* because the clock is hard-coded.
*
* @return the current Julian 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 JulianDate dateNow() {
return JulianDate.now();
}
/**
* Obtains the current Julian local date from the system clock in the specified time-zone.
*
* 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.
*
* 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 Julian local date using the system clock, not null
* @throws DateTimeException if unable to create the date
*/
@Override // override with covariant return type
public JulianDate dateNow(ZoneId zone) {
return JulianDate.now(zone);
}
/**
* Obtains the current Julian local date from the specified clock.
*
* 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 Julian local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override // override with covariant return type
public JulianDate dateNow(Clock clock) {
return JulianDate.now(clock);
}
//-------------------------------------------------------------------------
/**
* Obtains a Julian local date from another date-time object.
*
* @param temporal the date-time object to convert, not null
* @return the Julian local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override
public JulianDate date(TemporalAccessor temporal) {
return JulianDate.from(temporal);
}
/**
* Obtains a Julian local date-time from another date-time object.
*
* @param temporal the date-time object to convert, not null
* @return the Julian local date-time, not null
* @throws DateTimeException if unable to create the date-time
*/
@Override
@SuppressWarnings("unchecked")
public ChronoLocalDateTime
* A Julian proleptic-year is leap if the remainder after division by four equals zero.
* 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 (prolepticYear % 4) == 0;
}
@Override
public int prolepticYear(Era era, int yearOfEra) {
if (era instanceof JulianEra == false) {
throw new ClassCastException("Era must be JulianEra");
}
return (era == JulianEra.AD ? yearOfEra : 1 - yearOfEra);
}
@Override
public JulianEra eraOf(int eraValue) {
return JulianEra.of(eraValue);
}
@Override
public List
* This date operates using the {@linkplain JulianChronology Julian calendar}.
* This calendar system is the forerunner to the modern Gregorian and ISO calendars.
* The Julian differs from the Gregorian only in terms of the leap year rule.
* Dates are aligned such that {@code 0001-01-01 (Julian)} is {@code 0000-12-30 (ISO)}.
*
*
* This class must be treated as a value type. Do not synchronize, rely on the
* identity hash code or use the distinction between equals() and ==.
*/
public final class JulianDate
extends AbstractDate
implements ChronoLocalDate, Serializable {
/**
* Serialization version.
*/
private static final long serialVersionUID = -7920528871688876868L;
/**
* The difference between the ISO and Julian epoch day count (Julian 0001-01-01 to ISO 1970-01-01).
*/
private static final int JULIAN_0001_TO_ISO_1970 = 678577 + 40587; // MJD values
/**
* The days per 4 year cycle.
*/
private static final int DAYS_PER_CYCLE = (365 * 4) + 1;
/**
* The proleptic year.
*/
private final int prolepticYear;
/**
* The month.
*/
private final short month;
/**
* The day.
*/
private final short day;
//-----------------------------------------------------------------------
/**
* Obtains the current {@code JulianDate} from the system clock in the default time-zone.
*
* This will query the {@link Clock#systemDefaultZone() system clock} in the default
* time-zone to obtain the current date.
*
* 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 JulianDate now() {
return now(Clock.systemDefaultZone());
}
/**
* Obtains the current {@code JulianDate} from the system clock in the specified time-zone.
*
* 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.
*
* 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 JulianDate now(ZoneId zone) {
return now(Clock.system(zone));
}
/**
* Obtains the current {@code JulianDate} from the specified clock.
*
* 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 JulianDate now(Clock clock) {
LocalDate now = LocalDate.now(clock);
return JulianDate.ofEpochDay(now.toEpochDay());
}
/**
* Obtains a {@code JulianDate} representing a date in the Julian calendar
* system from the proleptic-year, month-of-year and day-of-month fields.
*
* This returns a {@code JulianDate} with the specified fields.
* The day must be valid for the year and month, otherwise an exception will be thrown.
*
* @param prolepticYear the Julian proleptic-year
* @param month the Julian month-of-year, from 1 to 12
* @param dayOfMonth the Julian day-of-month, from 1 to 31
* @return the date in Julian 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 JulianDate of(int prolepticYear, int month, int dayOfMonth) {
return JulianDate.create(prolepticYear, month, dayOfMonth);
}
/**
* Obtains a {@code JulianDate} from a temporal object.
*
* This obtains a date in the Julian 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 JulianDate}.
*
* The conversion typically uses the {@link ChronoField#EPOCH_DAY EPOCH_DAY}
* field, which is standardized across calendar systems.
*
* This method matches the signature of the functional interface {@link TemporalQuery}
* allowing it to be used as a query via method reference, {@code JulianDate::from}.
*
* @param temporal the temporal object to convert, not null
* @return the date in Julian calendar system, not null
* @throws DateTimeException if unable to convert to a {@code JulianDate}
*/
public static JulianDate from(TemporalAccessor temporal) {
if (temporal instanceof JulianDate) {
return (JulianDate) temporal;
}
return JulianDate.ofEpochDay(temporal.getLong(EPOCH_DAY));
}
//-----------------------------------------------------------------------
/**
* Obtains a {@code JulianDate} representing a date in the Julian calendar
* system from the proleptic-year and day-of-year fields.
*
* This returns a {@code JulianDate} with the specified fields.
* The day must be valid for the year, otherwise an exception will be thrown.
*
* @param prolepticYear the Julian proleptic-year
* @param dayOfYear the Julian day-of-year, from 1 to 366
* @return the date in Julian 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 JulianDate ofYearDay(int prolepticYear, int dayOfYear) {
JulianChronology.YEAR_RANGE.checkValidValue(prolepticYear, YEAR);
DAY_OF_YEAR.checkValidValue(dayOfYear);
boolean leap = JulianChronology.INSTANCE.isLeapYear(prolepticYear);
if (dayOfYear == 366 && leap == false) {
throw new DateTimeException("Invalid date 'DayOfYear 366' as '" + prolepticYear + "' 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 JulianDate(prolepticYear, moy.getValue(), dom);
}
/**
* Obtains a {@code JulianDate} representing a date in the Julian calendar
* system from the epoch-day.
*
* @param epochDay the epoch day to convert based on 1970-01-01 (ISO)
* @return the date in Julian calendar system, not null
* @throws DateTimeException if the epoch-day is out of range
*/
static JulianDate ofEpochDay(final long epochDay) {
EPOCH_DAY.range().checkValidValue(epochDay, EPOCH_DAY); // validate outer bounds
// use of Julian 0001 makes leap year at end of cycle
long julianEpochDay = epochDay + JULIAN_0001_TO_ISO_1970;
long cycle = Math.floorDiv(julianEpochDay, DAYS_PER_CYCLE);
long daysInCycle = Math.floorMod(julianEpochDay, DAYS_PER_CYCLE);
if (daysInCycle == DAYS_PER_CYCLE - 1) {
int year = (int) ((cycle * 4 + 3) + 1);
return ofYearDay(year, 366);
}
int year = (int) ((cycle * 4 + daysInCycle / 365) + 1);
int doy = (int) ((daysInCycle % 365) + 1);
return ofYearDay(year, doy);
}
private static JulianDate resolvePreviousValid(int prolepticYear, int month, int day) {
switch (month) {
case 2:
day = Math.min(day, JulianChronology.INSTANCE.isLeapYear(prolepticYear) ? 29 : 28);
break;
case 4:
case 6:
case 9:
case 11:
day = Math.min(day, 30);
break;
default:
break;
}
return new JulianDate(prolepticYear, month, day);
}
/**
* Creates a {@code JulianDate} validating the input.
*
* @param prolepticYear the Julian proleptic-year
* @param month the Julian month-of-year, from 1 to 12
* @param dayOfMonth the Julian day-of-month, from 1 to 31
* @return the date in Julian 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 month-year
*/
static JulianDate create(int prolepticYear, int month, int dayOfMonth) {
JulianChronology.YEAR_RANGE.checkValidValue(prolepticYear, YEAR);
MONTH_OF_YEAR.checkValidValue(month);
DAY_OF_MONTH.checkValidValue(dayOfMonth);
if (dayOfMonth > 28) {
int dom = 31;
switch (month) {
case 2:
dom = (JulianChronology.INSTANCE.isLeapYear(prolepticYear) ? 29 : 28);
break;
case 4:
case 6:
case 9:
case 11:
dom = 30;
break;
default:
break;
}
if (dayOfMonth > dom) {
if (dayOfMonth == 29) {
throw new DateTimeException("Invalid date 'February 29' as '" + prolepticYear + "' is not a leap year");
} else {
throw new DateTimeException("Invalid date '" + Month.of(month).name() + " " + dayOfMonth + "'");
}
}
}
return new JulianDate(prolepticYear, month, dayOfMonth);
}
//-----------------------------------------------------------------------
/**
* Creates an instance from validated data.
*
* @param prolepticYear the Julian proleptic-year
* @param month the Julian month, from 1 to 12
* @param dayOfMonth the Julian day-of-month, from 1 to 31
*/
private JulianDate(int prolepticYear, int month, int dayOfMonth) {
this.prolepticYear = prolepticYear;
this.month = (short) month;
this.day = (short) dayOfMonth;
}
/**
* Validates the object.
*
* @return the resolved date, not null
*/
private Object readResolve() {
return JulianDate.create(prolepticYear, month, day);
}
//-----------------------------------------------------------------------
@Override
int getProlepticYear() {
return prolepticYear;
}
@Override
int getMonth() {
return month;
}
@Override
int getDayOfMonth() {
return day;
}
@Override
int getDayOfYear() {
return Month.of(month).firstDayOfYear(isLeapYear()) + day - 1;
}
@Override
ValueRange rangeAlignedWeekOfMonth() {
return ValueRange.of(1, month == 2 && isLeapYear() == false ? 4 : 5);
}
@Override
JulianDate resolvePrevious(int newYear, int newMonth, int dayOfMonth) {
return resolvePreviousValid(newYear, newMonth, dayOfMonth);
}
//-----------------------------------------------------------------------
/**
* Gets the chronology of this date, which is the Julian calendar system.
*
* The {@code Chronology} represents the calendar system in use.
* The era and other fields in {@link ChronoField} are defined by the chronology.
*
* @return the Julian chronology, not null
*/
@Override
public JulianChronology getChronology() {
return JulianChronology.INSTANCE;
}
/**
* Gets the era applicable at this date.
*
* The Julian calendar system has two eras, 'AD' and 'BC',
* defined by {@link JulianEra}.
*
* @return the era applicable at this date, not null
*/
@Override
public JulianEra getEra() {
return (prolepticYear >= 1 ? JulianEra.AD : JulianEra.BC);
}
/**
* Returns the length of the month represented by this date.
*
* 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, from 28 to 31
*/
@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;
}
}
//-------------------------------------------------------------------------
@Override
public JulianDate with(TemporalAdjuster adjuster) {
return (JulianDate) adjuster.adjustInto(this);
}
@Override
public JulianDate with(TemporalField field, long newValue) {
return (JulianDate) super.with(field, newValue);
}
//-----------------------------------------------------------------------
@Override
public JulianDate plus(TemporalAmount amount) {
return (JulianDate) amount.addTo(this);
}
@Override
public JulianDate plus(long amountToAdd, TemporalUnit unit) {
return (JulianDate) super.plus(amountToAdd, unit);
}
@Override
public JulianDate minus(TemporalAmount amount) {
return (JulianDate) amount.subtractFrom(this);
}
@Override
public JulianDate minus(long amountToSubtract, TemporalUnit unit) {
return (amountToSubtract == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit));
}
//-------------------------------------------------------------------------
@Override // for covariant return type
@SuppressWarnings("unchecked")
public ChronoLocalDateTime
* The Julian calendar system has two eras.
* The current era, for years from 1 onwards, is known as 'Anno Domini'.
* 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 Christ' era.
*
* The start of the Julian epoch {@code 0001-01-01 (Julian)} is {@code 0000-12-30 (ISO)}.
*
* Do not use {@code ordinal()} to obtain the numeric representation of {@code JulianEra}.
* Use {@code getValue()} instead.
*
*
* {@code JulianEra} is an enum representing the Julian eras of BC/AD.
* This factory allows the enum to be obtained from the {@code int} value.
*
* @param era the BC/AD value to represent, from 0 (BC) to 1 (AD)
* @return the era singleton, not null
* @throws DateTimeException if the value is invalid
*/
public static JulianEra of(int era) {
switch (era) {
case 0:
return BC;
case 1:
return AD;
default:
throw new DateTimeException("Invalid era: " + era);
}
}
//-----------------------------------------------------------------------
/**
* Gets the numeric era {@code int} value.
*
* The era BC has the value 0, while the era AD has the value 1.
*
* @return the era value, from 0 (BC) to 1 (AD)
*/
@Override
public int getValue() {
return ordinal();
}
}
threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/PaxChronology.java 0000664 0000000 0000000 00000040356 13434511741 0027602 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2007-present, 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 org.threeten.extra.chrono;
import java.io.Serializable;
import java.time.Clock;
import java.time.DateTimeException;
import java.time.Instant;
import java.time.ZoneId;
import java.time.chrono.AbstractChronology;
import java.time.chrono.ChronoLocalDateTime;
import java.time.chrono.ChronoZonedDateTime;
import java.time.chrono.Chronology;
import java.time.chrono.Era;
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.Map;
/**
* The Pax calendar system.
*
* This chronology defines the rules of the proleptic Pax calendar system.
* This calendar system is a proposed reform calendar system, and is not in common use.
* The Pax differs from the Gregorian in terms of month count and length, and the leap year rule.
* Dates are aligned such that {@code 0001-01-01 (Pax)} is {@code 0000-12-31 (ISO)}.
*
* This class is proleptic. It implements Pax rules to the entire time-line.
*
* The fields are defined as follows:
*
* For more information, please read the Pax Calendar Wikipedia article.
*
*
* 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 - 'Pax'
* @see #getCalendarType()
*/
@Override
public String getId() {
return "Pax";
}
/**
* Gets the calendar type of the underlying calendar system - 'pax'.
*
* The Unicode Locale Data Markup Language (LDML) specification
* does not define an identifier for the Pax calendar, but were it to
* do so, 'pax' is highly likely to be chosen.
*
* @return the calendar system type - 'pax'
* @see #getId()
*/
@Override
public String getCalendarType() {
return "pax";
}
//-----------------------------------------------------------------------
/**
* Obtains a local date in Pax calendar system from the
* era, year-of-era, month-of-year and day-of-month fields.
*
* @param era the Pax era, not null
* @param yearOfEra the year-of-era
* @param month the month-of-year
* @param dayOfMonth the day-of-month
* @return the Pax local date, not null
* @throws DateTimeException if unable to create the date
* @throws ClassCastException if the {@code era} is not a {@code PaxEra}
*/
@Override
public PaxDate date(Era era, int yearOfEra, int month, int dayOfMonth) {
return date(prolepticYear(era, yearOfEra), month, dayOfMonth);
}
/**
* Obtains a local date in Pax 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 Pax local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override
public PaxDate date(int prolepticYear, int month, int dayOfMonth) {
return PaxDate.of(prolepticYear, month, dayOfMonth);
}
/**
* Obtains a local date in Pax calendar system from the
* era, year-of-era and day-of-year fields.
*
* @param era the Pax era, not null
* @param yearOfEra the year-of-era
* @param dayOfYear the day-of-year
* @return the Pax local date, not null
* @throws DateTimeException if unable to create the date
* @throws ClassCastException if the {@code era} is not a {@code PaxEra}
*/
@Override
public PaxDate dateYearDay(Era era, int yearOfEra, int dayOfYear) {
return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear);
}
/**
* Obtains a local date in Pax calendar system from the
* proleptic-year and day-of-year fields.
*
* @param prolepticYear the proleptic-year
* @param dayOfYear the day-of-year
* @return the Pax local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override
public PaxDate dateYearDay(int prolepticYear, int dayOfYear) {
return PaxDate.ofYearDay(prolepticYear, dayOfYear);
}
/**
* Obtains a local date in the Pax calendar system from the epoch-day.
*
* @param epochDay the epoch day
* @return the Pax local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override
public PaxDate dateEpochDay(long epochDay) {
return PaxDate.ofEpochDay(epochDay);
}
//-------------------------------------------------------------------------
/**
* Obtains the current Pax local date from the system clock in the default time-zone.
*
* This will query the {@link Clock#systemDefaultZone() system clock} in the default
* time-zone to obtain the current date.
*
* Using this method will prevent the ability to use an alternate clock for testing
* because the clock is hard-coded.
*
* @return the current Pax local date using the system clock and default time-zone, not null
* @throws DateTimeException if unable to create the date
*/
@Override
public PaxDate dateNow() {
return PaxDate.now();
}
/**
* Obtains the current Pax local date from the system clock in the specified time-zone.
*
* 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.
*
* Using this method will prevent the ability to use an alternate clock for testing
* because the clock is hard-coded.
*
* @param zone the ZoneId to use, not null
* @return the current Pax local date using the system clock, not null
* @throws DateTimeException if unable to create the date
*/
@Override
public PaxDate dateNow(ZoneId zone) {
return PaxDate.now(zone);
}
/**
* Obtains the current Pax local date from the specified clock.
*
* 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 Pax local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override
public PaxDate dateNow(Clock clock) {
return PaxDate.now(clock);
}
//-------------------------------------------------------------------------
/**
* Obtains a Pax local date from another date-time object.
*
* @param temporal the date-time object to convert, not null
* @return the Pax local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override
public PaxDate date(TemporalAccessor temporal) {
return PaxDate.from(temporal);
}
/**
* Obtains a Pax local date-time from another date-time object.
*
* @param temporal the date-time object to convert, not null
* @return the Pax local date-time, not null
* @throws DateTimeException if unable to create the date-time
*/
@Override
@SuppressWarnings("unchecked")
public ChronoLocalDateTime
* In general, a year is a leap year if the last two digits are divisible by 6 without remainder, or are 99.
* Years with the last two digits of 00 are also leap years, with the exception of years divisible by 400, which are not.
*
* For example, 2012 is a leap year because the last two digits (12) are divisible by 6.
* 1999 is a leap year as the last two digits are both 9's (99).
* 1900 is a leap year as the last two digits are both 0's (00), however 2000 was not a leap year as it is divisible by 400.
* The year 0 is not a leap year.
*
* @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) {
long lastTwoDigits = prolepticYear % 100;
return Math.abs(lastTwoDigits) == 99 || (prolepticYear % 400 != 0 && (lastTwoDigits == 0 || lastTwoDigits % 6 == 0));
}
@Override
public int prolepticYear(Era era, int yearOfEra) {
if (!(era instanceof PaxEra)) {
throw new ClassCastException("Era must be PaxEra");
}
return (era == PaxEra.CE ? yearOfEra : 1 - yearOfEra);
}
@Override
public PaxEra eraOf(int eraValue) {
return PaxEra.of(eraValue);
}
@Override
public List
* This date operates using the {@linkplain PaxChronology Pax calendar}.
* This calendar system is a proposed reform calendar system, and is not in common use.
* The Pax differs from the Gregorian in terms of month count and length, and the leap year rule.
* Dates are aligned such that {@code 0001-01-01 (Pax)} is {@code 0000-12-31 (ISO)}.
*
* More information is available in the Pax Calendar Wikipedia article.
*
*
* This class must be treated as a value type. Do not synchronize, rely on the
* identity hash code or use the distinction between equals() and ==.
*/
public final class PaxDate
extends AbstractDate
implements ChronoLocalDate, Serializable {
/**
* Serialization version.
*/
private static final long serialVersionUID = -2229133057743750072L;
/**
* The difference between the ISO and Pax epoch day count (Pax 0001-01-01 to ISO 1970-01-01).
*/
private static final int PAX_0001_TO_ISO_1970 = 719163;
/**
* The days per 400 year cycle.
*/
private static final int DAYS_PER_LONG_CYCLE = (DAYS_IN_YEAR * 400) + (DAYS_IN_WEEK * 71);
/**
* The days per 100 year cycle.
*/
private static final int DAYS_PER_CYCLE = (DAYS_IN_YEAR * 100) + (DAYS_IN_WEEK * 18);
/**
* The days per 6 year cycle.
*/
private static final int DAYS_PER_SIX_CYCLE = (DAYS_IN_YEAR * 6) + (DAYS_IN_WEEK * 1);
/**
* Number of years in a decade.
*/
private static final int YEARS_IN_DECADE = 10;
/**
* Number of years in a century.
*/
private static final int YEARS_IN_CENTURY = 100;
/**
* Number of years in a millennium.
*/
private static final int YEARS_IN_MILLENNIUM = 1000;
/**
* The proleptic year.
*/
private final int prolepticYear;
/**
* The month.
*/
private final short month;
/**
* The day.
*/
private final short day;
//-----------------------------------------------------------------------
/**
* Obtains the current {@code PaxDate} from the system clock in the default time-zone.
*
* This will query the {@link Clock#systemDefaultZone() system clock} in the default
* time-zone to obtain the current date.
*
* 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 PaxDate now() {
return now(Clock.systemDefaultZone());
}
/**
* Obtains the current {@code PaxDate} from the system clock in the specified time-zone.
*
* 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.
*
* 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 PaxDate now(ZoneId zone) {
return now(Clock.system(zone));
}
/**
* Obtains the current {@code PaxDate} from the specified clock.
*
* 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 PaxDate now(Clock clock) {
LocalDate now = LocalDate.now(clock);
return PaxDate.ofEpochDay(now.toEpochDay());
}
/**
* Obtains a {@code PaxDate} representing a date in the Pax calendar
* system from the proleptic-year, month-of-year and day-of-month fields.
*
* This returns a {@code PaxDate} with the specified fields.
* The day must be valid for the year and month, otherwise an exception will be thrown.
*
* @param prolepticYear the Pax proleptic-year
* @param month the Pax month-of-year, from 1 to 14
* @param dayOfMonth the Pax day-of-month, from 1 to 28
* @return the date in Pax 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 PaxDate of(int prolepticYear, int month, int dayOfMonth) {
YEAR.checkValidValue(prolepticYear);
PaxChronology.MONTH_OF_YEAR_RANGE.checkValidValue(month, MONTH_OF_YEAR);
PaxChronology.DAY_OF_MONTH_RANGE.checkValidValue(dayOfMonth, DAY_OF_MONTH);
if (month == MONTHS_IN_YEAR + 1 && !PaxChronology.INSTANCE.isLeapYear(prolepticYear)) {
throw new DateTimeException("Invalid month 14 as " + prolepticYear + "is not a leap year");
}
if (dayOfMonth > DAYS_IN_WEEK && month == MONTHS_IN_YEAR && PaxChronology.INSTANCE.isLeapYear(prolepticYear)) {
throw new DateTimeException("Invalid date during Pax as " + prolepticYear + " is a leap year");
}
return new PaxDate(prolepticYear, month, dayOfMonth);
}
/**
* Obtains a {@code PaxDate} from a temporal object.
*
* This obtains a date in the Pax 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 PaxDate}.
*
* The conversion typically uses the {@link ChronoField#EPOCH_DAY EPOCH_DAY}
* field, which is standardized across calendar systems.
*
* This method matches the signature of the functional interface {@link TemporalQuery}
* allowing it to be used as a query via method reference, {@code PaxDate::from}.
*
* @param temporal the temporal object to convert, not null
* @return the date in the Pax calendar system, not null
* @throws DateTimeException if unable to convert to a {@code PaxDate}
*/
public static PaxDate from(TemporalAccessor temporal) {
if (temporal instanceof PaxDate) {
return (PaxDate) temporal;
}
return PaxDate.ofEpochDay(temporal.getLong(EPOCH_DAY));
}
//-----------------------------------------------------------------------
/**
* Obtains a {@code PaxDate} representing a date in the Pax calendar
* system from the proleptic-year and day-of-year fields.
*
* This returns a {@code PaxDate} with the specified fields.
* The day must be valid for the year, otherwise an exception will be thrown.
*
* @param prolepticYear the Pax proleptic-year
* @param dayOfYear the Pax day-of-year, from 1 to 371
* @return the date in Pax 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 PaxDate ofYearDay(int prolepticYear, int dayOfYear) {
YEAR.checkValidValue(prolepticYear);
PaxChronology.DAY_OF_YEAR_RANGE.checkValidValue(dayOfYear, DAY_OF_YEAR);
boolean leap = PaxChronology.INSTANCE.isLeapYear(prolepticYear);
if (dayOfYear > DAYS_IN_YEAR && !leap) {
throw new DateTimeException("Invalid date 'DayOfYear " + dayOfYear + "' as '" + prolepticYear + "' is not a leap year");
}
int month = ((dayOfYear - 1) / DAYS_IN_MONTH) + 1;
// In leap years, the leap-month is shorter than the following month, so needs to be adjusted.
if (leap && month == MONTHS_IN_YEAR && dayOfYear >= (DAYS_IN_YEAR + DAYS_IN_WEEK) - DAYS_IN_MONTH + 1) {
month++;
}
// Subtract days-at-start-of-month from days in year
int dayOfMonth = dayOfYear - (month - 1) * DAYS_IN_MONTH;
// Adjust for shorter inserted leap-month.
if (month == MONTHS_IN_YEAR + 1) {
dayOfMonth += (DAYS_IN_MONTH - DAYS_IN_WEEK);
}
return of(prolepticYear, month, dayOfMonth);
}
/**
* Obtains a {@code PaxDate} representing a date in the Pax calendar
* system from the epoch-day.
*
* @param epochDay the epoch day to convert based on 1970-01-01 (ISO)
* @return the date in Pax calendar system, not null
* @throws DateTimeException if the epoch-day is out of range
*/
static PaxDate ofEpochDay(long epochDay) {
EPOCH_DAY.range().checkValidValue(epochDay, EPOCH_DAY);
// use of Pax 0001 makes non-leap century at end of (long) cycle.
long paxEpochDay = epochDay + PAX_0001_TO_ISO_1970;
int longCycle = (int) Math.floorDiv(paxEpochDay, DAYS_PER_LONG_CYCLE);
int cycle = (int) (paxEpochDay - longCycle * DAYS_PER_LONG_CYCLE) / DAYS_PER_CYCLE;
int dayOfCycle = (int) Math.floorMod(paxEpochDay - longCycle * DAYS_PER_LONG_CYCLE, DAYS_PER_CYCLE);
if (dayOfCycle >= DAYS_PER_CYCLE - DAYS_IN_YEAR - DAYS_IN_WEEK) {
// Is in the century year
int dayOfYear = dayOfCycle - (DAYS_PER_CYCLE - DAYS_IN_YEAR - DAYS_IN_WEEK) + 1;
return ofYearDay(longCycle * (4 * YEARS_IN_CENTURY) + cycle * YEARS_IN_CENTURY + YEARS_IN_CENTURY, dayOfYear);
}
// For negative years, the cycle of leap years runs the other direction for 99s and 6s.
if (paxEpochDay >= 0) {
if (dayOfCycle >= DAYS_PER_CYCLE - 2 * DAYS_IN_YEAR - 2 * DAYS_IN_WEEK) {
// Is in the '99 year
int dayOfYear = dayOfCycle - (DAYS_PER_CYCLE - 2 * DAYS_IN_YEAR - 2 * DAYS_IN_WEEK) + 1;
return ofYearDay(longCycle * (4 * YEARS_IN_CENTURY) + cycle * YEARS_IN_CENTURY + (YEARS_IN_CENTURY - 1), dayOfYear);
}
// Otherwise, part of the regular 6-year cycle.
int sixCycle = dayOfCycle / DAYS_PER_SIX_CYCLE;
int dayOfSixCycle = dayOfCycle % DAYS_PER_SIX_CYCLE;
int year = dayOfSixCycle / DAYS_IN_YEAR + 1;
int dayOfYear = dayOfSixCycle % DAYS_IN_YEAR + 1;
if (year == 7) {
year--;
dayOfYear += DAYS_IN_YEAR;
}
return ofYearDay(longCycle * (4 * YEARS_IN_CENTURY) + cycle * YEARS_IN_CENTURY + sixCycle * 6 + year, dayOfYear);
} else {
if (dayOfCycle < DAYS_IN_YEAR + DAYS_IN_WEEK) {
// -'99 year is at _start_ of cycle (first year encountered).
return ofYearDay(longCycle * (4 * YEARS_IN_CENTURY) + cycle * YEARS_IN_CENTURY + 1, dayOfCycle + 1);
}
// Otherwise, part of the regular 6-year cycle, but offset -'96 to be end of six-year-cycle first.
int offsetCycle = dayOfCycle + 2 * DAYS_IN_YEAR - DAYS_IN_WEEK;
int sixCycle = offsetCycle / DAYS_PER_SIX_CYCLE;
int dayOfSixCycle = offsetCycle % DAYS_PER_SIX_CYCLE;
int year = dayOfSixCycle / DAYS_IN_YEAR + 1;
int dayOfYear = dayOfSixCycle % DAYS_IN_YEAR + 1;
if (year == 7) {
year--;
dayOfYear += DAYS_IN_YEAR;
}
return ofYearDay(longCycle * (4 * YEARS_IN_CENTURY) + cycle * YEARS_IN_CENTURY - 2 + (sixCycle * 6 + year), dayOfYear);
}
}
private static PaxDate resolvePreviousValid(int prolepticYear, int month, int day) {
int monthR = Math.min(month, MONTHS_IN_YEAR + (PaxChronology.INSTANCE.isLeapYear(prolepticYear) ? 1 : 0));
int dayR = Math.min(day, month == MONTHS_IN_YEAR && PaxChronology.INSTANCE.isLeapYear(prolepticYear) ? DAYS_IN_WEEK : DAYS_IN_MONTH);
return PaxDate.of(prolepticYear, monthR, dayR);
}
/**
* Get the count of leap months since proleptic month 0.
*
* This number is negative if the month is prior to Pax year 0.
**
* Remember that if using this for things like turning months into days, you must first subtract this number from the proleptic month count.
*
* @param prolepticMonth The month.
* @return The number of leap months since proleptic month 0.
*/
private static long getLeapMonthsBefore(long prolepticMonth) {
long offsetMonth = prolepticMonth - (prolepticMonth <= 0 ? 13 : 13 - 1);
// First, see getLeapYearsBefore(...) for explanations.
// return 18 * Math.floorDiv(offsetMonth, 100 * MONTHS_IN_YEAR + (getLeapYearsBefore(100) + 1))
// - Math.floorDiv(offsetMonth, 400 * MONTHS_IN_YEAR + (getLeapYearsBefore(400) + 1))
// + (((Math.floorMod(offsetMonth, 100 * MONTHS_IN_YEAR + (getLeapYearsBefore(100) + 1)) - (offsetMonth <= 0 ? 100 * MONTHS_IN_YEAR + getLeapYearsBefore(100) : 0))
// / (99 * MONTHS_IN_YEAR + (getLeapYearsBefore(99) + 1))) + (offsetMonth <= 0 ? 1 : 0))
// + (Math.floorMod(offsetMonth, 100 * MONTHS_IN_YEAR + (getLeapYearsBefore(100) + 1)) + (offsetMonth <= 0 ? 2 * MONTHS_IN_YEAR - 1 : 0)) / (6 * MONTHS_IN_YEAR + 1);
return 18L * Math.floorDiv(offsetMonth, 1318)
- Math.floorDiv(offsetMonth, 5272)
+ (((Math.floorMod(offsetMonth, 1318) - (offsetMonth <= 0 ? 1317 : 0)) / 1304) + (offsetMonth <= 0 ? 1 : 0))
+ (Math.floorMod(offsetMonth, 1318) + (offsetMonth <= 0 ? 25 : 0)) / 79;
}
/**
* Get the count of leap years since Pax year 0.
*
* This number is negative if the year is prior to Pax year 0.
*
* @param prolepticYear The year.
* @return The number of leap years since Pax year 0.
*/
private static long getLeapYearsBefore(long prolepticYear) {
// Some explanations are in order:
// - First, because this calculates "leap years from 0 to the start of the year",
// The 'current' year must be counted when the year is negative (hence Math.floorDiv(...)).
// - However, this means that there's a negative count which must be offset for the in-century years,
// which still occur at the "last two digits divisible by 6" (or 99) point.
// - Math.floorMod(...) returns a nicely positive result for negative years, counting 'down', but
// thus needs to be offset to make sure the first leap year is -6, and not -4...
// The second line, which calculates the '99 occurrences, runs "backwards", so must first be reversed, then the results flipped.
return 18L * Math.floorDiv(prolepticYear - 1, YEARS_IN_CENTURY) - Math.floorDiv(prolepticYear - 1, 4 * YEARS_IN_CENTURY) +
(Math.floorMod(prolepticYear - 1, 100) - (prolepticYear <= 0 ? 99 : 0)) / 99 + (prolepticYear <= 0 ? 1 : 0) +
((Math.floorMod(prolepticYear - 1, 100) + (prolepticYear <= 0 ? 2 : 0)) / 6);
}
//-----------------------------------------------------------------------
/**
* Creates an instance from validated data.
*
* @param prolepticYear the Pax proleptic-year
* @param month the Pax month, from 1 to 14
* @param dayOfMonth the Pax day-of-month, from 1 to 28
*/
private PaxDate(int prolepticYear, int month, int dayOfMonth) {
this.prolepticYear = prolepticYear;
this.month = (short) month;
this.day = (short) dayOfMonth;
}
/**
* Validates the object.
*
* @return the resolved date, not null
*/
private Object readResolve() {
return PaxDate.of(prolepticYear, month, day);
}
//-----------------------------------------------------------------------
@Override
int getProlepticYear() {
return prolepticYear;
}
@Override
int getMonth() {
return month;
}
@Override
int getDayOfMonth() {
return day;
}
@Override
int getDayOfYear() {
return (month - 1) * DAYS_IN_MONTH
- (month == MONTHS_IN_YEAR + 1 ? DAYS_IN_MONTH - DAYS_IN_WEEK : 0) + getDayOfMonth();
}
@Override
int lengthOfYearInMonths() {
return MONTHS_IN_YEAR + (isLeapYear() ? 1 : 0);
}
@Override
ValueRange rangeAlignedWeekOfMonth() {
return ValueRange.of(1, month == MONTHS_IN_YEAR && isLeapYear() ? WEEKS_IN_LEAP_MONTH : WEEKS_IN_MONTH);
}
@Override
PaxDate resolvePrevious(int newYear, int newMonth, int dayOfMonth) {
return resolvePreviousValid(newYear, newMonth, dayOfMonth);
}
@Override
public ValueRange range(TemporalField field) {
if (field == ChronoField.ALIGNED_WEEK_OF_YEAR) {
return ValueRange.of(1, WEEKS_IN_YEAR + (isLeapYear() ? 1 : 0));
} else if (field == ChronoField.MONTH_OF_YEAR) {
return ValueRange.of(1, MONTHS_IN_YEAR + (isLeapYear() ? 1 : 0));
}
return super.range(field);
}
/**
* Get the proleptic month from the start of the epoch (Pax 0000).
*
* @return The proleptic month.
*/
@Override
long getProlepticMonth() {
return ((long) getProlepticYear()) * MONTHS_IN_YEAR + getLeapYearsBefore(getProlepticYear()) + month - 1;
}
//-----------------------------------------------------------------------
/**
* Gets the chronology of this date, which is the Pax calendar system.
*
* The {@code Chronology} represents the calendar system in use.
* The era and other fields in {@link ChronoField} are defined by the chronology.
*
* @return the Pax chronology, not null
*/
@Override
public PaxChronology getChronology() {
return PaxChronology.INSTANCE;
}
/**
* Gets the era applicable at this date.
*
* The Pax calendar system has two eras, 'CE' and 'BCE',
* defined by {@link PaxEra}.
*
* @return the era applicable at this date, not null
*/
@Override
public PaxEra getEra() {
return (prolepticYear >= 1 ? PaxEra.CE : PaxEra.BCE);
}
/**
* Returns the length of the month represented by this date.
*
* This returns the length of the month in days.
* Month lengths do not match those of the ISO calendar system.
*
* @return the length of the month in days, from 7 to 28
*/
@Override
public int lengthOfMonth() {
switch (month) {
case 13:
return (isLeapYear() ? DAYS_IN_WEEK : DAYS_IN_MONTH);
default:
return DAYS_IN_MONTH;
}
}
@Override
public int lengthOfYear() {
return DAYS_IN_YEAR + (isLeapYear() ? DAYS_IN_WEEK : 0);
}
//-------------------------------------------------------------------------
@Override
public PaxDate with(TemporalAdjuster adjuster) {
return (PaxDate) adjuster.adjustInto(this);
}
@Override
public PaxDate with(TemporalField field, long newValue) {
// Evaluate years as a special case, to deal with inserted leap months.
if (field == ChronoField.YEAR) {
return plusYears(Math.subtractExact(newValue, getProlepticYear()));
}
return (PaxDate) super.with(field, newValue);
}
//-----------------------------------------------------------------------
@Override
public PaxDate plus(TemporalAmount amount) {
return (PaxDate) amount.addTo(this);
}
@Override
public PaxDate plus(long amountToAdd, TemporalUnit unit) {
return (PaxDate) super.plus(amountToAdd, unit);
}
/**
* Returns a copy of this {@code PaxDate} with the specified period in years added.
*
* This method adds the specified amount to the years field in two steps:
*
* In the Pax Calendar, the month of December is 13 in non-leap-years, and 14 in leap years.
* Shifting the index of the month thus means the month would still be the same.
*
* In the case of moving from the inserted leap-month (destination year is non-leap), the month index is retained.
* This has the effect of retaining the same day-of-year.
*
* This instance is immutable and unaffected by this method call.
*
* @param yearsToAdd the years to add, may be negative
* @return a {@code PaxDate} based on this date with the years added, not null
* @throws DateTimeException if the result exceeds the supported date range
*/
@Override
PaxDate plusYears(long yearsToAdd) {
if (yearsToAdd == 0) {
return this;
}
int newYear = YEAR.checkValidIntValue(getProlepticYear() + yearsToAdd);
// Retain actual month (not index) in the case where a leap month is to be inserted.
if (month == MONTHS_IN_YEAR && !isLeapYear() && PaxChronology.INSTANCE.isLeapYear(newYear)) {
return of(newYear, MONTHS_IN_YEAR + 1, getDayOfMonth());
}
// Otherwise, one of the following is true:
// 1 - Before the leap month, nothing to do (most common)
// 2 - Both source and destination in leap-month, nothing to do
// 3 - Both source and destination after leap month in leap year, nothing to do
// 4 - Source in leap month, but destination year not leap. Retain month index, preserving day-of-year.
// 5 - Source after leap month, but destination year not leap. Move month index back.
return resolvePreviousValid(newYear, month, day);
}
/**
* Returns a copy of this {@code PaxDate} with the specified period in months added.
*
* This method adds the specified amount to the months field in three steps:
*
* For example, 2006-12-13 plus one month would result in the invalid date 2006-13-13.
* Instead of returning an invalid result, the last valid day of the month, 2006-13-07, is selected instead.
*
* This instance is immutable and unaffected by this method call.
*
* @param monthsToAdd the months to add, may be negative
* @return a {@code PaxDate} based on this date with the months added, not null
* @throws DateTimeException if the result exceeds the supported date range
*/
@Override
PaxDate plusMonths(long monthsToAdd) {
if (monthsToAdd == 0) {
return this;
}
long calcMonths = Math.addExact(getProlepticMonth(), monthsToAdd);
// "Regularize" the month count, as if years were all 13 months long.
long monthsRegularized = calcMonths - getLeapMonthsBefore(calcMonths);
int newYear = YEAR.checkValidIntValue(Math.floorDiv(monthsRegularized, MONTHS_IN_YEAR));
int newMonth = Math.toIntExact(calcMonths - ((long) newYear * MONTHS_IN_YEAR + getLeapYearsBefore(newYear)) + 1);
return resolvePreviousValid(newYear, newMonth, getDayOfMonth());
}
@Override
public PaxDate minus(TemporalAmount amount) {
return (PaxDate) amount.subtractFrom(this);
}
@Override
public PaxDate minus(long amountToSubtract, TemporalUnit unit) {
return (amountToSubtract == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit));
}
//-------------------------------------------------------------------------
@Override // for covariant return type
@SuppressWarnings("unchecked")
public ChronoLocalDateTime
* The Pax calendar system has two eras.
* The current era, for years from 1 onwards, is known as 'Current 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 Current Era' era.
*
* The start of the Pax epoch {@code 0001-01-01 (Pax)} is {@code 0000-12-31 (ISO)}.
*
* Do not use {@code ordinal()} to obtain the numeric representation of {@code PaxEra}.
* Use {@code getValue()} instead.
*
*
* {@code PaxEra} is an enum representing the Pax eras of BCE/CE.
* This factory allows the enum to be obtained from the {@code int} value.
*
* @param era 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 PaxEra of(final int era) {
switch (era) {
case 0:
return BCE;
case 1:
return CE;
default:
throw new DateTimeException("Invalid era: " + era);
}
}
//-----------------------------------------------------------------------
/**
* Gets the numeric era {@code int} value.
*
* 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();
}
}
threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/Symmetry010Chronology.java 0000664 0000000 0000000 00000046417 13434511741 0031130 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2007-present, 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 org.threeten.extra.chrono;
import java.io.Serializable;
import java.time.Clock;
import java.time.DateTimeException;
import java.time.Instant;
import java.time.ZoneId;
import java.time.chrono.AbstractChronology;
import java.time.chrono.ChronoLocalDateTime;
import java.time.chrono.ChronoZonedDateTime;
import java.time.chrono.Chronology;
import java.time.chrono.Era;
import java.time.chrono.IsoEra;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.ValueRange;
import java.util.Arrays;
import java.util.List;
/**
* The Symmetry010 calendar system.
*
* This chronology defines the rules of the Symmetry010 calendar system.
* Dates are aligned such that {@code 0001/01/01 (Sym010)} is {@code 0001-01-01 (ISO)}.
*
* The calendar implemented by this class is proleptic, with January 1st as the start of the year.
* Each month either has 30 days or 31 days, in an alternating pattern; January has 30 days,
* February has 31 days and March again has 30 days. Due to this, each quarter consists of 13 weeks.
*
* Normal years thus have 364 days, whereas leap years have an extra week, aptly called leap week,
* added to the end, extending the year to 371 days. Thus, December in a leap year has 37 days.
*
* The fields are defined as follows:
*
* 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 - 'Sym010'
* @see #getCalendarType()
*/
@Override
public String getId() {
return "Sym010";
}
/**
* Gets the calendar type of the underlying calendar system, which returns null.
*
* The Unicode Locale Data Markup Language (LDML) specification
* does not define an identifier for this calendar system, thus null is returned.
*
* @return the calendar system type, null
* @see #getId()
*/
@Override
public String getCalendarType() {
return null;
}
//-----------------------------------------------------------------------
/**
* Obtains a local date in Symmetry010 calendar system from the
* era, year-of-era, month-of-year and day-of-month fields.
*
* @param era the Symmetry010 era, not null
* @param yearOfEra the year-of-era
* @param month the month-of-year
* @param dayOfMonth the day-of-month
* @return the Symmetry010 local date, not null
* @throws DateTimeException if unable to create the date
* @throws ClassCastException if the {@code era} is not a {@code IsoEra}
*/
@Override
public Symmetry010Date date(Era era, int yearOfEra, int month, int dayOfMonth) {
return date(prolepticYear(era, yearOfEra), month, dayOfMonth);
}
/**
* Obtains a local date in Symmetry010 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 Symmetry010 local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override
public Symmetry010Date date(int prolepticYear, int month, int dayOfMonth) {
return Symmetry010Date.of(prolepticYear, month, dayOfMonth);
}
/**
* Obtains a local date in Symmetry010 calendar system from the
* era, year-of-era and day-of-year fields.
*
* @param era the Symmetry010 era, not null
* @param yearOfEra the year-of-era
* @param dayOfYear the day-of-year
* @return the Symmetry010 local date, not null
* @throws DateTimeException if unable to create the date
* @throws ClassCastException if the {@code era} is not a {@code IsoEra}
*/
@Override
public Symmetry010Date dateYearDay(Era era, int yearOfEra, int dayOfYear) {
return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear);
}
/**
* Obtains a local date in Symmetry010 calendar system from the
* proleptic-year and day-of-year fields.
*
* @param prolepticYear the proleptic-year
* @param dayOfYear the day-of-year
* @return the Symmetry010 local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override
public Symmetry010Date dateYearDay(int prolepticYear, int dayOfYear) {
return Symmetry010Date.ofYearDay(prolepticYear, dayOfYear);
}
/**
* Obtains a local date in the Symmetry010 calendar system from the epoch-day.
*
* @param epochDay the epoch day
* @return the Symmetry010 local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override // override with covariant return type
public Symmetry010Date dateEpochDay(long epochDay) {
return Symmetry010Date.ofEpochDay(epochDay);
}
//-------------------------------------------------------------------------
/**
* Obtains the current Symmetry010 local date from the system clock in the default time-zone.
*
* This will query the {@link Clock#systemDefaultZone() system clock} in the default
* time-zone to obtain the current date.
*
* Using this method will prevent the ability to use an alternate clock for testing
* because the clock is hard-coded.
*
* @return the current Symmetry010 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 Symmetry010Date dateNow() {
return Symmetry010Date.now();
}
/**
* Obtains the current Symmetry010 local date from the system clock in the specified time-zone.
*
* 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.
*
* 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 Symmetry010 local date using the system clock, not null
* @throws DateTimeException if unable to create the date
*/
@Override // override with covariant return type
public Symmetry010Date dateNow(ZoneId zone) {
return Symmetry010Date.now(zone);
}
/**
* Obtains the current Symmetry010 local date from the specified clock.
*
* 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 Symmetry010 local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override // override with covariant return type
public Symmetry010Date dateNow(Clock clock) {
return Symmetry010Date.now(clock);
}
//-------------------------------------------------------------------------
/**
* Obtains a Symmetry010 local date from another date-time object.
*
* @param temporal the date-time object to convert, not null
* @return the Symmetry010 local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override
public Symmetry010Date date(TemporalAccessor temporal) {
return Symmetry010Date.from(temporal);
}
/**
* Obtains a Symmetry010 local date-time from another date-time object.
*
* @param temporal the date-time object to convert, not null
* @return the Symmetry010 local date-time, not null
* @throws DateTimeException if unable to create the date-time
*/
@Override
@SuppressWarnings("unchecked")
public ChronoLocalDateTime
* A leap-year is a year of a longer length than normal.
* Leap years in the calendar system match those of the ISO calendar system.
*
* @param year the proleptic-year to check, not validated for range
* @return true if the year is a leap year
*/
@Override
public boolean isLeapYear(long year) {
return WEEKS_IN_YEAR > ((WEEKS_IN_YEAR * year + 146) % YEARS_IN_CYCLE);
}
//-----------------------------------------------------------------------
/**
* Creates the chronology era object from the numeric value.
*
* The list of eras is shared with {@link IsoEra}.
*
* @param eraValue the era value
* @return the calendar system era, not null
* @throws DateTimeException if unable to create the era
*/
@Override
public IsoEra eraOf(int eraValue) {
return IsoEra.of(eraValue);
}
/**
* Gets the list of eras for the chronology.
*
* The list of eras is shared with {@link IsoEra}.
*
* @return the list of eras for the chronology, may be immutable, not null
*/
@Override
public List
* This date operates using the {@linkplain Symmetry010Chronology Symmetry010 calendar}.
* This calendar system is a proposed reform calendar system, and is not in common use.
* The Symmetry010 differs from the Gregorian in terms of month length, and the leap year rule.
* Dates are aligned such that {@code 0001/01/01 (Sym010)} is {@code 0001-01-01 (ISO)}.
* The alignment of January 1st happens 40 times within a 293 years cycle, skipping 5, 6, 11 or 12 years in between:
* 1, 7, 18, 24, 29, 35, 46, 52, 57, 63, 74, 80, 85, 91, 103, 114, 120, 125, 131, 142,
* 148, 153, 159, 170, 176, 181, 187, 198, 210, 216, 221, 227, 238, 244, 249, 255, 266, 272, 277, 283.
*
* The implementation is a pure Symmetry010 calendar, as proposed by Dr. Irv Bromberg.
* The year shares the 12 months with the Gregorian calendar.
* The months February, May, August, November span 31 days, all other months consist of 30 days.
* In leap years, December is extended with a full week, the so-called "leap week".
* Thus December in a leap year has 37.
* Since each month is made of full weeks, the calendar is perennial, with every date fixed always on the same weekday.
* Each month starts on a Monday and ends on a Sunday; so does each year.
* The 13th day of a month is always a Saturday.
*
* More information is available on Wikipedia at
* Symmetry010 or on the calendar's
* home page.
*
*
*
* This class must be treated as a value type. Do not synchronize, rely on the
* identity hash code or use the distinction between equals() and ==.
*/
public final class Symmetry010Date
extends AbstractDate
implements ChronoLocalDate, Serializable {
/**
* Serialization version.
*/
private static final long serialVersionUID = -8275627894629629L;
/**
* The proleptic year.
*/
private final int prolepticYear;
/**
* The month of the year.
*/
private final int month;
/**
* The day of the month.
*/
private final int day;
/**
* The day of year.
*/
private final transient int dayOfYear;
//-----------------------------------------------------------------------
/**
* Obtains the current {@code Symmetry010Date} from the system clock in the default time-zone.
*
* This will query the {@link Clock#systemDefaultZone() system clock} in the default
* time-zone to obtain the current date.
*
* 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 Symmetry010Date now() {
return now(Clock.systemDefaultZone());
}
/**
* Obtains the current {@code Symmetry010Date} from the system clock in the specified time-zone.
*
* 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.
*
* 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 Symmetry010Date now(ZoneId zone) {
return now(Clock.system(zone));
}
/**
* Obtains the current {@code Symmetry010Date} from the specified clock.
*
* 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 Symmetry010Date now(Clock clock) {
LocalDate now = LocalDate.now(clock);
return Symmetry010Date.ofEpochDay(now.toEpochDay());
}
/**
* Obtains a {@code Symmetry010Date} representing a date in the Symmetry010 calendar
* system from the proleptic-year, month-of-year and day-of-month fields.
*
* This returns a {@code Symmetry010Date} with the specified fields.
* The day must be valid for the year and month, otherwise an exception will be thrown.
*
* @param prolepticYear the Symmetry010 proleptic-year
* @param month the Symmetry010 month-of-year, from 1 to 12
* @param dayOfMonth the Symmetry010 day-of-month, from 1 to 30, or 1 to 31 in February, May, August, November,
* or 1 to 37 in December in a Leap Year
* @return the date in Symmetry010 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 Symmetry010Date of(int prolepticYear, int month, int dayOfMonth) {
return create(prolepticYear, month, dayOfMonth);
}
//-----------------------------------------------------------------------
/**
* Obtains a {@code Symmetry010Date} from a temporal object.
*
* This obtains a date in the Symmetry010 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 Symmetry010Date}.
*
* The conversion typically uses the {@link ChronoField#EPOCH_DAY EPOCH_DAY}
* field, which is standardized across calendar systems.
*
* This method matches the signature of the functional interface {@link TemporalQuery}
* allowing it to be used as a query via method reference, {@code Symmetry010Date::from}.
*
* @param temporal the temporal object to convert, not null
* @return the date in the Symmetry010 calendar system, not null
* @throws DateTimeException if unable to convert to a {@code Symmetry010Date}
*/
public static Symmetry010Date from(TemporalAccessor temporal) {
if (temporal instanceof Symmetry010Date) {
return (Symmetry010Date) temporal;
}
return Symmetry010Date.ofEpochDay(temporal.getLong(ChronoField.EPOCH_DAY));
}
//-----------------------------------------------------------------------
/**
* Obtains a {@code Symmetry010Date} representing a date in the Symmetry010 calendar
* system from the proleptic-year and day-of-year fields.
*
* This returns a {@code Symmetry010Date} with the specified fields.
* The day must be valid for the year, otherwise an exception will be thrown.
*
* @param prolepticYear the Symmetry010 proleptic-year
* @param dayOfYear the Symmetry010 day-of-year, from 1 to 364/371
* @return the date in Symmetry010 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 Symmetry010Date ofYearDay(int prolepticYear, int dayOfYear) {
YEAR_RANGE.checkValidValue(prolepticYear, ChronoField.YEAR_OF_ERA);
DAY_OF_YEAR_RANGE.checkValidValue(dayOfYear, ChronoField.DAY_OF_YEAR);
boolean leap = INSTANCE.isLeapYear(prolepticYear);
if (dayOfYear > DAYS_IN_YEAR && !leap) {
throw new DateTimeException("Invalid date 'DayOfYear " + dayOfYear + "' as '" + prolepticYear + "' is not a leap year");
}
int offset = Math.min(dayOfYear, DAYS_IN_YEAR) - 1;
int quarter = offset / DAYS_IN_QUARTER;
int day = ((dayOfYear - 1) - quarter * DAYS_IN_QUARTER) + 1;
int month = 1 + quarter * 3;
if (day > DAYS_IN_MONTH + DAYS_IN_MONTH + 1) {
month += 2;
day -= DAYS_IN_MONTH + DAYS_IN_MONTH + 1;
} else if (day > DAYS_IN_MONTH) {
month += 1;
day -= DAYS_IN_MONTH;
}
return new Symmetry010Date(prolepticYear, month, day);
}
/**
* Obtains a {@code Symmetry010Date} representing a date in the Symmetry010 calendar
* system from the epoch-day.
*
* @param epochDay the epoch day to convert based on 1970-01-01 (ISO), corresponds to 1970-01-04 (Sym010)
* @return the date in Symmetry010 calendar system, not null
* @throws DateTimeException if the epoch-day is out of range
*/
static Symmetry010Date ofEpochDay(long epochDay) {
EPOCH_DAY_RANGE.checkValidValue(epochDay + 3, ChronoField.EPOCH_DAY);
long zeroDay = epochDay + DAYS_0001_TO_1970 + 1;
long year = 1 + ((293 * zeroDay) / DAYS_PER_CYCLE);
long doy = zeroDay - (DAYS_IN_YEAR * (year - 1) + Symmetry010Chronology.getLeapYearsBefore(year) * DAYS_IN_WEEK);
if (doy < 1) {
year--;
doy += INSTANCE.isLeapYear(year) ? DAYS_IN_YEAR_LONG : DAYS_IN_YEAR;
}
int diy = INSTANCE.isLeapYear(year) ? DAYS_IN_YEAR_LONG : DAYS_IN_YEAR;
if (doy > diy) {
doy -= diy;
year++;
}
return ofYearDay((int) year, (int) doy);
}
/**
* Consistency check for dates manipulations after calls to
* {@link #plus(long, TemporalUnit)},
* {@link #minus(long, TemporalUnit)},
* {@link #until(AbstractDate, TemporalUnit)} or
* {@link #with(TemporalField, long)}.
*
* @param prolepticYear the Symmetry010 proleptic-year
* @param month the Symmetry010 month, from 1 to 12
* @param dayOfMonth the Symmetry010 day-of-month, from 1 to 30, or 1 to 31 in February, May, August, November,
* or 1 to 37 in December in a Leap Year
* @return the resolved date
*/
private static Symmetry010Date resolvePreviousValid(int prolepticYear, int month, int dayOfMonth) {
int monthR = Math.min(month, MONTHS_IN_YEAR);
int dayR = Math.min(dayOfMonth,
monthR == 12 && INSTANCE.isLeapYear(prolepticYear) ? DAYS_IN_MONTH + 7 :
monthR % 3 == 2 ? DAYS_IN_MONTH_LONG : DAYS_IN_MONTH);
return create(prolepticYear, monthR, dayR);
}
//-----------------------------------------------------------------------
/**
* Factory method, validates the given triplet year, month and dayOfMonth.
*
* @param prolepticYear the Symmetry010 proleptic-year
* @param month the Symmetry010 month, from 1 to 12
* @param dayOfMonth the Symmetry010 day-of-month, from 1 to 30, or 1 to 31 in February, May, August, November,
* or 1 to 37 in December in a Leap Year
* @return the Symmetry010 date
* @throws DateTimeException if the date is invalid
*/
static Symmetry010Date create(int prolepticYear, int month, int dayOfMonth) {
YEAR_RANGE.checkValidValue(prolepticYear, ChronoField.YEAR_OF_ERA);
MONTH_OF_YEAR_RANGE.checkValidValue(month, ChronoField.MONTH_OF_YEAR);
DAY_OF_MONTH_RANGE.checkValidValue(dayOfMonth, ChronoField.DAY_OF_MONTH);
if (dayOfMonth > DAYS_IN_MONTH) {
if (month == MONTHS_IN_YEAR) {
if (!INSTANCE.isLeapYear(prolepticYear)) {
throw new DateTimeException("Invalid Leap Day as '" + prolepticYear + "' is not a leap year");
}
} else if (((month % 3 == 2) && dayOfMonth > DAYS_IN_MONTH_LONG) || (month % 3 != 2)) {
throw new DateTimeException("Invalid date: " + prolepticYear + '/' + month + '/' + dayOfMonth);
}
}
return new Symmetry010Date(prolepticYear, month, dayOfMonth);
}
//-----------------------------------------------------------------------
/**
* Creates an instance from validated data.
*
* @param prolepticYear the Symmetry010 proleptic-year
* @param month the Symmetry010 month, from 1 to 12
* @param dayOfMonth the Symmetry010 day-of-month, from 1 to 30, or 1 to 31 in February, May, August, November,
* or 1 to 37 in December in a Leap Year
*/
private Symmetry010Date(int prolepticYear, int month, int dayOfMonth) {
this.prolepticYear = prolepticYear;
this.month = month;
this.day = dayOfMonth;
this.dayOfYear = DAYS_IN_MONTH * (month - 1) + (month / 3) + dayOfMonth;
}
/**
* Validates the object.
*
* @return Symmetry010Date the resolved date, not null
*/
private Object readResolve() {
return Symmetry010Date.of(prolepticYear, month, day);
}
//-----------------------------------------------------------------------
@Override
int getProlepticYear() {
return prolepticYear;
}
@Override
int getMonth() {
return month;
}
@Override
int getDayOfMonth() {
return day;
}
@Override
int getDayOfYear() {
return dayOfYear;
}
@Override
int lengthOfYearInMonths() {
return MONTHS_IN_YEAR;
}
@Override
int getDayOfWeek() {
return ((dayOfYear - 1 + getDayOfMonthOffset()) % DAYS_IN_WEEK) + 1;
}
long getProlepticWeek() {
return prolepticYear * WEEKS_IN_YEAR +
Symmetry010Chronology.getLeapYearsBefore(prolepticYear) +
((dayOfYear - 1) / DAYS_IN_WEEK) - 1;
}
/**
* Each 1st month of a quarter (month % 3 == 1) starts on a Monday, offset is 0.
* Each 2nd month of a quarter (month % 3 == 2) starts on a Wednesday, offset is 2.
* Each 3rd month of a quarter (month % 3 == 0) starts on a Saturday, offset is 5.
*/
private static final int[] dayOfMonthOffset = {5, 0, 2};
private int getDayOfMonthOffset() {
return dayOfMonthOffset[month % 3];
}
/**
* Checks if the date is within the leap week.
*
* @return true if this date is in the leap week
*/
public boolean isLeapWeek() {
return isLeapYear() && this.dayOfYear > DAYS_IN_YEAR;
}
//-----------------------------------------------------------------------
@Override
public ValueRange range(TemporalField field) {
if (field instanceof ChronoField) {
if (isSupported(field)) {
ChronoField f = (ChronoField) field;
switch (f) {
case ALIGNED_DAY_OF_WEEK_IN_MONTH:
case ALIGNED_DAY_OF_WEEK_IN_YEAR:
case DAY_OF_WEEK:
return ValueRange.of(1, DAYS_IN_WEEK);
case ALIGNED_WEEK_OF_MONTH:
return ValueRange.of(1, lengthOfMonth() / DAYS_IN_WEEK);
case ALIGNED_WEEK_OF_YEAR:
return ValueRange.of(1, WEEKS_IN_YEAR + (isLeapYear() ? 1 : 0));
case DAY_OF_MONTH:
return ValueRange.of(1, lengthOfMonth());
case DAY_OF_YEAR:
return ValueRange.of(1, lengthOfYear());
case EPOCH_DAY:
return EPOCH_DAY_RANGE;
case ERA:
return ERA_RANGE;
case MONTH_OF_YEAR:
return MONTH_OF_YEAR_RANGE;
default:
break;
}
} else {
throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
}
return super.range(field);
}
@Override
ValueRange rangeAlignedWeekOfMonth() {
// never invoked
return ValueRange.of(1, WEEKS_IN_MONTH);
}
@Override
Symmetry010Date resolvePrevious(int newYear, int newMonth, int dayOfMonth) {
return resolvePreviousValid(newYear, newMonth, dayOfMonth);
}
//-----------------------------------------------------------------------
/**
* Gets the chronology of this date, which is the Symmetry010 calendar system.
*
* The {@code Chronology} represents the calendar system in use.
* The era and other fields in {@link ChronoField} are defined by the chronology.
*
* @return the Symmetry010 chronology, not null
*/
@Override
public Symmetry010Chronology getChronology() {
return INSTANCE;
}
/**
* Gets the era applicable at this date.
*
* The Symmetry454 calendar system uses {@link IsoEra}.
*
* @return the era applicable at this date, not null
*/
@Override
public IsoEra getEra() {
return (prolepticYear >= 1 ? IsoEra.CE : IsoEra.BCE);
}
/**
* Returns the length of the month represented by this date.
*
* This returns the length of the month in days.
* Month lengths do not match those of the ISO calendar system.
*
* Most months have 30 days, except for February, May, August, November each have 31 days.
* December in a leap year has 37 days.
*
* @return the length of the month in days
*/
@Override
public int lengthOfMonth() {
return this.isLeapYear() && this.month == MONTHS_IN_YEAR ? DAYS_IN_MONTH + 7 :
this.month % 3 == 2 ? DAYS_IN_MONTH_LONG : DAYS_IN_MONTH;
}
/**
* Returns the length of the year represented by this date.
*
* This returns the length of the year in days.
* Year lengths do NOT match those of the ISO calendar system.
*
* @return the length of the year in days: 364 or 371
*/
@Override
public int lengthOfYear() {
return isLeapYear() ? DAYS_IN_YEAR_LONG : DAYS_IN_YEAR;
}
//-------------------------------------------------------------------------
@Override
public Symmetry010Date with(TemporalAdjuster adjuster) {
return (Symmetry010Date) adjuster.adjustInto(this);
}
@Override
public Symmetry010Date with(TemporalField field, long newValue) {
if (field instanceof ChronoField) {
if (newValue == 0) {
return this;
}
ChronoField f = (ChronoField) field;
getChronology().range(f).checkValidValue(newValue, f);
int nval = (int) newValue;
switch (f) {
case DAY_OF_MONTH:
return create(prolepticYear, month, nval);
case DAY_OF_WEEK:
int week = (this.dayOfYear - 1) / 7;
int yd = 7 * week + nval;
return ofYearDay(prolepticYear, yd);
default:
break;
}
}
return (Symmetry010Date) super.with(field, newValue);
}
@Override
Symmetry010Date withDayOfYear(int value) {
return ofYearDay(prolepticYear, value);
}
//-----------------------------------------------------------------------
@Override
public Symmetry010Date plus(TemporalAmount amount) {
return (Symmetry010Date) amount.addTo(this);
}
@Override
public Symmetry010Date plus(long amountToAdd, TemporalUnit unit) {
return (Symmetry010Date) super.plus(amountToAdd, unit);
}
@Override
public Symmetry010Date minus(TemporalAmount amount) {
return (Symmetry010Date) amount.subtractFrom(this);
}
@Override
public Symmetry010Date minus(long amountToSubtract, TemporalUnit unit) {
return (Symmetry010Date) super.minus(amountToSubtract, unit);
}
//-------------------------------------------------------------------------
@Override // for covariant return type
@SuppressWarnings("unchecked")
public ChronoLocalDateTime
* This chronology defines the rules of the Symmetry454 calendar system.
* Dates are aligned such that {@code 0001/01/01 (Sym454)} is {@code 0001-01-01 (ISO)}.
*
* The calendar implemented by this class is proleptic, with January 1st as the start of the year.
* Each month either has 28 days or 35 days, in an alternating pattern; January has 28 days,
* February has 35 days and March again has 28 days. Due to this, each quarter consists of 13 weeks.
*
* Normal years thus have 364 days, whereas leap years have an extra week, aptly called leap week,
* added to the end, extending the year to 371 days.
*
* The fields are defined as follows:
*
* 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 - 'Sym454'
* @see #getCalendarType()
*/
@Override
public String getId() {
return "Sym454";
}
/**
* Gets the calendar type of the underlying calendar system, which returns null.
*
* The Unicode Locale Data Markup Language (LDML) specification
* does not define an identifier for this calendar system, thus null is returned.
*
* @return the calendar system type, null
* @see #getId()
*/
@Override
public String getCalendarType() {
return null;
}
//-----------------------------------------------------------------------
/**
* Obtains a local date in Symmetry454 calendar system from the
* era, year-of-era, month-of-year and day-of-month fields.
*
* @param era the Symmetry454 era, not null
* @param yearOfEra the year-of-era
* @param month the month-of-year
* @param dayOfMonth the day-of-month
* @return the Symmetry454 local date, not null
* @throws DateTimeException if unable to create the date
* @throws ClassCastException if the {@code era} is not a {@code IsoEra}
*/
@Override
public Symmetry454Date date(Era era, int yearOfEra, int month, int dayOfMonth) {
return date(prolepticYear(era, yearOfEra), month, dayOfMonth);
}
/**
* Obtains a local date in Symmetry454 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 Symmetry454 local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override
public Symmetry454Date date(int prolepticYear, int month, int dayOfMonth) {
return Symmetry454Date.of(prolepticYear, month, dayOfMonth);
}
/**
* Obtains a local date in Symmetry454 calendar system from the
* era, year-of-era and day-of-year fields.
*
* @param era the Symmetry454 era, not null
* @param yearOfEra the year-of-era
* @param dayOfYear the day-of-year
* @return the Symmetry454 local date, not null
* @throws DateTimeException if unable to create the date
* @throws ClassCastException if the {@code era} is not a {@code IsoEra}
*/
@Override
public Symmetry454Date dateYearDay(Era era, int yearOfEra, int dayOfYear) {
return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear);
}
/**
* Obtains a local date in Symmetry454 calendar system from the
* proleptic-year and day-of-year fields.
*
* @param prolepticYear the proleptic-year
* @param dayOfYear the day-of-year
* @return the Symmetry454 local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override
public Symmetry454Date dateYearDay(int prolepticYear, int dayOfYear) {
return Symmetry454Date.ofYearDay(prolepticYear, dayOfYear);
}
/**
* Obtains a local date in the Symmetry454 calendar system from the epoch-day.
*
* @param epochDay the epoch day
* @return the Symmetry454 local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override // override with covariant return type
public Symmetry454Date dateEpochDay(long epochDay) {
return Symmetry454Date.ofEpochDay(epochDay);
}
//-------------------------------------------------------------------------
/**
* Obtains the current Symmetry454 local date from the system clock in the default time-zone.
*
* This will query the {@link Clock#systemDefaultZone() system clock} in the default
* time-zone to obtain the current date.
*
* Using this method will prevent the ability to use an alternate clock for testing
* because the clock is hard-coded.
*
* @return the current Symmetry454 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 Symmetry454Date dateNow() {
return Symmetry454Date.now();
}
/**
* Obtains the current Symmetry454 local date from the system clock in the specified time-zone.
*
* 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.
*
* 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 Symmetry454 local date using the system clock, not null
* @throws DateTimeException if unable to create the date
*/
@Override // override with covariant return type
public Symmetry454Date dateNow(ZoneId zone) {
return Symmetry454Date.now(zone);
}
/**
* Obtains the current Symmetry454 local date from the specified clock.
*
* 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 Symmetry454 local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override // override with covariant return type
public Symmetry454Date dateNow(Clock clock) {
return Symmetry454Date.now(clock);
}
//-------------------------------------------------------------------------
/**
* Obtains a Symmetry454 local date from another date-time object.
*
* @param temporal the date-time object to convert, not null
* @return the Symmetry454 local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override
public Symmetry454Date date(TemporalAccessor temporal) {
return Symmetry454Date.from(temporal);
}
/**
* Obtains a Symmetry454 local date-time from another date-time object.
*
* @param temporal the date-time object to convert, not null
* @return the Symmetry454 local date-time, not null
* @throws DateTimeException if unable to create the date-time
*/
@Override
@SuppressWarnings("unchecked")
public ChronoLocalDateTime
* A leap-year is a year of a longer length than normal.
* Leap years in the calendar system match those of the ISO calendar system.
*
* @param year the proleptic-year to check, not validated for range
* @return true if the year is a leap year
*/
@Override
public boolean isLeapYear(long year) {
return WEEKS_IN_YEAR > ((WEEKS_IN_YEAR * year + 146) % YEARS_IN_CYCLE);
}
//-----------------------------------------------------------------------
/**
* Creates the chronology era object from the numeric value.
*
* The list of eras is shared with {@link IsoEra}.
*
* @param eraValue the era value
* @return the calendar system era, not null
* @throws DateTimeException if unable to create the era
*/
@Override
public IsoEra eraOf(int eraValue) {
return IsoEra.of(eraValue);
}
/**
* Gets the list of eras for the chronology.
*
* The list of eras is shared with {@link IsoEra}.
*
* @return the list of eras for the chronology, may be immutable, not null
*/
@Override
public List
* This date operates using the {@linkplain Symmetry454Chronology Symmetry454 calendar}.
* This calendar system is a proposed reform calendar system, and is not in common use.
* The Symmetry454 calendar differs from the Gregorian in terms of month length, and the leap year rule.
* Dates are aligned such that {@code 0001/01/01 (Sym454)} is {@code 0001-01-01 (ISO)}.
* The alignment of January 1st happens 40 times within a 293 years cycle, skipping 5, 6, 11 or 12 years in between:
* 1, 7, 18, 24, 29, 35, 46, 52, 57, 63, 74, 80, 85, 91, 103, 114, 120, 125, 131, 142,
* 148, 153, 159, 170, 176, 181, 187, 198, 210, 216, 221, 227, 238, 244, 249, 255, 266, 272, 277, 283.
*
* The implementation is a pure Symmetry454 calendar, as proposed by Dr. Irv Bromberg.
* The year shares the 12 months with the Gregorian calendar.
* The months February, May, August, November span 35 days, all other months consist of 28 days.
* In leap years, December is extended with a full week, the so-called "leap week".
* Since each month is made of full weeks, the calendar is perennial, with every date fixed always on the same weekday.
* Each month starts on a Monday and ends on a Sunday; so does each year.
* The 13th day of a month is always a Saturday.
*
* More information is available on Wikipedia at
* Symmetry454 or on the calendar's
* home page.
*
*
*
* This class must be treated as a value type. Do not synchronize, rely on the
* identity hash code or use the distinction between equals() and ==.
*/
public final class Symmetry454Date
extends AbstractDate
implements ChronoLocalDate, Serializable {
/**
* Serialization version.
*/
private static final long serialVersionUID = -3540913335234762448L;
/**
* The proleptic year.
*/
private final int prolepticYear;
/**
* The month of the year.
*/
private final int month;
/**
* The day of the month.
*/
private final int day;
/**
* The day of year.
*/
private final transient int dayOfYear;
//-----------------------------------------------------------------------
/**
* Obtains the current {@code Symmetry454Date} from the system clock in the default time-zone.
*
* This will query the {@link Clock#systemDefaultZone() system clock} in the default
* time-zone to obtain the current date.
*
* 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 Symmetry454Date now() {
return now(Clock.systemDefaultZone());
}
/**
* Obtains the current {@code Symmetry454Date} from the system clock in the specified time-zone.
*
* 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.
*
* 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 Symmetry454Date now(ZoneId zone) {
return now(Clock.system(zone));
}
/**
* Obtains the current {@code Symmetry454Date} from the specified clock.
*
* 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 Symmetry454Date now(Clock clock) {
LocalDate now = LocalDate.now(clock);
return Symmetry454Date.ofEpochDay(now.toEpochDay());
}
/**
* Obtains a {@code Symmetry454Date} representing a date in the Symmetry454 calendar
* system from the proleptic-year, month-of-year and day-of-month fields.
*
* This returns a {@code Symmetry454Date} with the specified fields.
* The day must be valid for the year and month, otherwise an exception will be thrown.
*
* @param prolepticYear the Symmetry454 proleptic-year
* @param month the Symmetry454 month-of-year, from 1 to 12
* @param dayOfMonth the Symmetry454 day-of-month, from 1 to 28, or 1 to 35 in February, May, August, November
* and December in a Leap Year
* @return the date in Symmetry454 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 Symmetry454Date of(int prolepticYear, int month, int dayOfMonth) {
return create(prolepticYear, month, dayOfMonth);
}
//-----------------------------------------------------------------------
/**
* Obtains a {@code Symmetry454Date} from a temporal object.
*
* This obtains a date in the Symmetry454 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 Symmetry454Date}.
*
* The conversion typically uses the {@link ChronoField#EPOCH_DAY EPOCH_DAY}
* field, which is standardized across calendar systems.
*
* This method matches the signature of the functional interface {@link TemporalQuery}
* allowing it to be used as a query via method reference, {@code Symmetry454Date::from}.
*
* @param temporal the temporal object to convert, not null
* @return the date in the Symmetry454 calendar system, not null
* @throws DateTimeException if unable to convert to a {@code Symmetry454Date}
*/
public static Symmetry454Date from(TemporalAccessor temporal) {
if (temporal instanceof Symmetry454Date) {
return (Symmetry454Date) temporal;
}
return Symmetry454Date.ofEpochDay(temporal.getLong(ChronoField.EPOCH_DAY));
}
//-----------------------------------------------------------------------
/**
* Obtains a {@code Symmetry454Date} representing a date in the Symmetry454 calendar
* system from the proleptic-year and day-of-year fields.
*
* This returns a {@code Symmetry454Date} with the specified fields.
* The day must be valid for the year, otherwise an exception will be thrown.
*
* @param prolepticYear the Symmetry454 proleptic-year
* @param dayOfYear the Symmetry454 day-of-year, from 1 to 364/371
* @return the date in Symmetry454 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 Symmetry454Date ofYearDay(int prolepticYear, int dayOfYear) {
YEAR_RANGE.checkValidValue(prolepticYear, ChronoField.YEAR_OF_ERA);
DAY_OF_YEAR_RANGE.checkValidValue(dayOfYear, ChronoField.DAY_OF_YEAR);
boolean leap = INSTANCE.isLeapYear(prolepticYear);
if (dayOfYear > DAYS_IN_YEAR && !leap) {
throw new DateTimeException("Invalid date 'DayOfYear " + dayOfYear + "' as '" + prolepticYear + "' is not a leap year");
}
int offset = Math.min(dayOfYear, DAYS_IN_YEAR) - 1;
int quarter = offset / DAYS_IN_QUARTER;
int day = dayOfYear - quarter * DAYS_IN_QUARTER;
int month = 1 + quarter * 3;
if (day > DAYS_IN_MONTH + DAYS_IN_MONTH + DAYS_IN_WEEK) {
month += 2;
day -= DAYS_IN_MONTH + DAYS_IN_MONTH + DAYS_IN_WEEK;
} else if (day > DAYS_IN_MONTH) {
month += 1;
day -= DAYS_IN_MONTH;
}
return new Symmetry454Date(prolepticYear, month, day);
}
/**
* Obtains a {@code Symmetry454Date} representing a date in the Symmetry454 calendar
* system from the epoch-day.
*
* @param epochDay the epoch day to convert based on 1970-01-01 (ISO), corresponds to 1970-01-04 (Sym454)
* @return the date in Symmetry454 calendar system, not null
* @throws DateTimeException if the epoch-day is out of range
*/
static Symmetry454Date ofEpochDay(long epochDay) {
EPOCH_DAY_RANGE.checkValidValue(epochDay + 3, ChronoField.EPOCH_DAY);
long zeroDay = epochDay + DAYS_0001_TO_1970 + 1;
long year = 1 + ((293 * zeroDay) / DAYS_PER_CYCLE);
long doy = zeroDay - (DAYS_IN_YEAR * (year - 1) + Symmetry454Chronology.getLeapYearsBefore(year) * DAYS_IN_WEEK);
if (doy < 1) {
year--;
doy += INSTANCE.isLeapYear(year) ? DAYS_IN_YEAR_LONG : DAYS_IN_YEAR;
}
int diy = INSTANCE.isLeapYear(year) ? DAYS_IN_YEAR_LONG : DAYS_IN_YEAR;
if (doy > diy) {
doy -= diy;
year++;
}
return ofYearDay((int) year, (int) doy);
}
/**
* Consistency check for dates manipulations after calls to
* {@link #plus(long, TemporalUnit)},
* {@link #minus(long, TemporalUnit)},
* {@link #until(AbstractDate, TemporalUnit)} or
* {@link #with(TemporalField, long)}.
*
* @param prolepticYear the Symmetry454 proleptic-year
* @param month the Symmetry454 month, from 1 to 12
* @return the resolved date
*/
private static Symmetry454Date resolvePreviousValid(int prolepticYear, int month, int day) {
int monthR = Math.min(month, MONTHS_IN_YEAR);
int dayR = Math.min(day,
(monthR % 3 == 2) || (monthR == 12 && INSTANCE.isLeapYear(prolepticYear)) ? DAYS_IN_MONTH_LONG : DAYS_IN_MONTH);
return create(prolepticYear, monthR, dayR);
}
//-----------------------------------------------------------------------
/**
* Factory method, validates the given triplet year, month and dayOfMonth.
*
* @param prolepticYear the Symmetry454 proleptic-year
* @param month the Symmetry454 month, from 1 to 12
* @param dayOfMonth the Symmetry454 day-of-month, from 1 to 28, or 1 to 35 in February, May, August, November
* and December in a Leap Year
* @return the Symmetry454 date
* @throws DateTimeException if the date is invalid
*/
static Symmetry454Date create(int prolepticYear, int month, int dayOfMonth) {
YEAR_RANGE.checkValidValue(prolepticYear, ChronoField.YEAR_OF_ERA);
MONTH_OF_YEAR_RANGE.checkValidValue(month, ChronoField.MONTH_OF_YEAR);
DAY_OF_MONTH_RANGE.checkValidValue(dayOfMonth, ChronoField.DAY_OF_MONTH);
if (dayOfMonth > DAYS_IN_MONTH) {
if (month == MONTHS_IN_YEAR) {
if (!INSTANCE.isLeapYear(prolepticYear)) {
throw new DateTimeException("Invalid Leap Day as '" + prolepticYear + "' is not a leap year");
}
} else if (month % 3 != 2) {
throw new DateTimeException("Invalid date: " + prolepticYear + '/' + month + '/' + dayOfMonth);
}
}
return new Symmetry454Date(prolepticYear, month, dayOfMonth);
}
//-----------------------------------------------------------------------
/**
* Creates an instance from validated data.
*
* @param prolepticYear the Symmetry454 proleptic-year
* @param month the Symmetry454 month, from 1 to 12
* @param dayOfMonth the Symmetry454 day-of-month, from 1 to 28, or 1 to 35 in February, May, August, November
* and December in a Leap Year
*/
private Symmetry454Date(int prolepticYear, int month, int dayOfMonth) {
this.prolepticYear = prolepticYear;
this.month = month;
this.day = dayOfMonth;
this.dayOfYear = DAYS_IN_MONTH * (month - 1) + DAYS_IN_WEEK * (month / 3) + dayOfMonth;
}
/**
* Validates the object.
*
* @return Symmetry454Date the resolved date, not null
*/
private Object readResolve() {
return Symmetry454Date.of(prolepticYear, month, day);
}
//-----------------------------------------------------------------------
@Override
int getProlepticYear() {
return prolepticYear;
}
@Override
int getMonth() {
return month;
}
@Override
int getDayOfMonth() {
return day;
}
@Override
int getDayOfYear() {
return dayOfYear;
}
@Override
int lengthOfYearInMonths() {
return MONTHS_IN_YEAR;
}
@Override
int getAlignedDayOfWeekInMonth() {
return getDayOfWeek();
}
@Override
int getAlignedDayOfWeekInYear() {
return getDayOfWeek();
}
@Override
int getAlignedWeekOfMonth() {
return ((day - 1) / DAYS_IN_WEEK) + 1;
}
@Override
int getAlignedWeekOfYear() {
return ((dayOfYear - 1) / DAYS_IN_WEEK) + 1;
}
@Override
int getDayOfWeek() {
return ((day - 1) % DAYS_IN_WEEK) + 1;
}
long getProlepticWeek() {
return getProlepticMonth() * WEEKS_IN_MONTH + ((getDayOfMonth() - 1) / DAYS_IN_WEEK) - 1;
}
/**
* Checks if the date is within the leap week.
*
* @return true if this date is in the leap week
*/
public boolean isLeapWeek() {
return isLeapYear() && this.dayOfYear > DAYS_IN_YEAR;
}
//-----------------------------------------------------------------------
@Override
public ValueRange range(TemporalField field) {
if (field instanceof ChronoField) {
if (isSupported(field)) {
ChronoField f = (ChronoField) field;
switch (f) {
case ALIGNED_DAY_OF_WEEK_IN_MONTH:
case ALIGNED_DAY_OF_WEEK_IN_YEAR:
case DAY_OF_WEEK:
return ValueRange.of(1, DAYS_IN_WEEK);
case ALIGNED_WEEK_OF_MONTH:
return ValueRange.of(1, WEEKS_IN_MONTH + (isLongMonth() ? 1 : 0));
case ALIGNED_WEEK_OF_YEAR:
return ValueRange.of(1, WEEKS_IN_YEAR + (isLeapYear() ? 1 : 0));
case DAY_OF_MONTH:
return ValueRange.of(1, lengthOfMonth());
case DAY_OF_YEAR:
return ValueRange.of(1, lengthOfYear());
case EPOCH_DAY:
return EPOCH_DAY_RANGE;
case ERA:
return ERA_RANGE;
case MONTH_OF_YEAR:
return MONTH_OF_YEAR_RANGE;
default:
break;
}
} else {
throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
}
return super.range(field);
}
@Override
ValueRange rangeAlignedWeekOfMonth() {
// never invoked
return ValueRange.of(1, WEEKS_IN_MONTH);
}
@Override
Symmetry454Date resolvePrevious(int newYear, int newMonth, int dayOfMonth) {
return resolvePreviousValid(newYear, newMonth, dayOfMonth);
}
//-----------------------------------------------------------------------
/**
* Gets the chronology of this date, which is the Symmetry454 calendar system.
*
* The {@code Chronology} represents the calendar system in use.
* The era and other fields in {@link ChronoField} are defined by the chronology.
*
* @return the Symmetry454 chronology, not null
*/
@Override
public Symmetry454Chronology getChronology() {
return INSTANCE;
}
/**
* Gets the era applicable at this date.
*
* The Symmetry454 calendar system uses {@link IsoEra}.
*
* @return the era applicable at this date, not null
*/
@Override
public IsoEra getEra() {
return (prolepticYear >= 1 ? IsoEra.CE : IsoEra.BCE);
}
/**
* Returns the length of the month represented by this date.
*
* This returns the length of the month in days.
* Month lengths do not match those of the ISO calendar system.
*
* Most months have 28 days, except for February, May, August, November, and
* December in a leap year, each has 35 days.
*
* @return the length of the month in days
*/
@Override
public int lengthOfMonth() {
return isLongMonth() ? DAYS_IN_MONTH_LONG : DAYS_IN_MONTH;
}
private boolean isLongMonth() {
return this.month % 3 == 2 || (isLeapYear() && this.month == MONTHS_IN_YEAR);
}
/**
* Returns the length of the year represented by this date.
*
* This returns the length of the year in days.
* Year lengths do NOT match those of the ISO calendar system.
*
* @return the length of the year in days: 364 or 371
*/
@Override
public int lengthOfYear() {
return DAYS_IN_YEAR + (isLeapYear() ? DAYS_IN_WEEK : 0);
}
//-------------------------------------------------------------------------
@Override
public Symmetry454Date with(TemporalAdjuster adjuster) {
return (Symmetry454Date) adjuster.adjustInto(this);
}
@Override
public Symmetry454Date with(TemporalField field, long newValue) {
if (field instanceof ChronoField) {
if (newValue == 0) {
return this;
}
ChronoField f = (ChronoField) field;
getChronology().range(f).checkValidValue(newValue, f);
int nval = (int) newValue;
switch (f) {
case ALIGNED_DAY_OF_WEEK_IN_MONTH:
case ALIGNED_DAY_OF_WEEK_IN_YEAR:
case DAY_OF_WEEK:
range(f).checkValidValue(newValue, field);
int dom = ((getDayOfMonth() - 1) / DAYS_IN_WEEK) * DAYS_IN_WEEK;
return resolvePreviousValid(prolepticYear, month, dom + nval);
case ALIGNED_WEEK_OF_MONTH:
range(f).checkValidValue(newValue, field);
int d = day % DAYS_IN_WEEK;
return resolvePreviousValid(prolepticYear, month, (nval - 1) * DAYS_IN_WEEK + d);
case ALIGNED_WEEK_OF_YEAR:
range(f).checkValidValue(newValue, field);
int newMonth = 1 + ((nval - 1) / WEEKS_IN_MONTH);
int newDay = ((nval - 1) % WEEKS_IN_MONTH) * DAYS_IN_WEEK + 1 + ((day - 1) % DAYS_IN_WEEK);
return resolvePreviousValid(prolepticYear, newMonth, newDay);
case DAY_OF_MONTH:
return create(prolepticYear, month, nval);
default:
break;
}
}
return (Symmetry454Date) super.with(field, newValue);
}
@Override
Symmetry454Date withDayOfYear(int value) {
return ofYearDay(prolepticYear, value);
}
//-----------------------------------------------------------------------
@Override
public Symmetry454Date plus(TemporalAmount amount) {
return (Symmetry454Date) amount.addTo(this);
}
@Override
public Symmetry454Date plus(long amountToAdd, TemporalUnit unit) {
return (Symmetry454Date) super.plus(amountToAdd, unit);
}
@Override
public Symmetry454Date minus(TemporalAmount amount) {
return (Symmetry454Date) amount.subtractFrom(this);
}
@Override
public Symmetry454Date minus(long amountToSubtract, TemporalUnit unit) {
return (Symmetry454Date) super.minus(amountToSubtract, unit);
}
//-------------------------------------------------------------------------
@Override // for covariant return type
@SuppressWarnings("unchecked")
public ChronoLocalDateTime
* The
* This class is an alternative representation based on the TAI time-scale.
* TAI is a single incrementing count of SI seconds.
* There are no leap seconds or other discontinuities.
*
* As a result of the simple definition, this time-scale would make an excellent timestamp.
* However, there are, at the time of writing, few easy ways to obtain an accurate TAI instant,
* but it is relatively easy to obtain a GPS instant.
* GPS and TAI differ by the fixed amount of 19 seconds.
*
* The duration between two points on the TAI time-scale is calculated solely using this class.
* Do not use the {@code between} method on {@code Duration} as that will lose information.
* Instead use {@link #durationUntil(TaiInstant)} on this class.
*
* It is intended that most applications will use the {@code Instant} class
* which uses the UTC-SLS mapping from UTC to guarantee 86400 seconds per day.
* Specialist applications with access to an accurate time-source may find this class useful.
*
*
* The TAI time-scale is a very simple well-regarded representation of time.
* The scale is defined using atomic clocks counting SI seconds.
* It has proceeded in a continuous uninterrupted manner since the defined
* epoch of {@code 1958-01-01T00:00:00(TAI)}.
* There are no leap seconds or other discontinuities.
*
* This class may be used for instants in the far past and far future.
* Since some instants will be prior to 1958, it is not strictly an implementation of TAI.
* Instead, it is a proleptic time-scale based on TAI and equivalent to it since 1958.
*
*
* This class must be treated as a value type. Do not synchronize, rely on the
* identity hash code or use the distinction between equals() and ==.
*/
public final class TaiInstant
implements Comparable
* 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:
*
* Converting a UTC-SLS instant to TAI requires leap second rules.
* This method uses the latest available system rules.
* The conversion first maps from UTC-SLS to UTC, then converts to TAI.
*
* Conversion from an {@code Instant} will not be completely accurate near
* a leap second in accordance with UTC-SLS.
*
* @param instant the instant to convert, not null
* @return the TAI instant, not null
* @throws DateTimeException if the range of {@code TaiInstant} is exceeded
* @throws ArithmeticException if numeric overflow occurs
*/
public static TaiInstant of(Instant instant) {
return UtcRules.system().convertToTai(instant);
}
/**
* Obtains an instance of {@code TaiInstant} from a {@code UtcInstant}.
*
* Converting a UTC instant to TAI requires leap second rules.
* This method uses the latest available system rules.
*
* The {@code TaiInstant} will represent exactly the same point on the
* time-line as per the available leap-second rules.
* If the leap-second rules change then conversion back to UTC may
* result in a different instant.
*
* @param instant the instant to convert, not null
* @return the TAI instant, not null
* @throws DateTimeException if the range of {@code TaiInstant} is exceeded
* @throws ArithmeticException if numeric overflow occurs
*/
public static TaiInstant of(UtcInstant instant) {
return UtcRules.system().convertToTai(instant);
}
//-------------------------------------------------------------------------
/**
* Obtains an instance of {@code TaiInstant} from a text string.
*
* The following format is accepted:
*
* The accepted format is strict.
* The seconds part must contain only numbers and a possible leading negative sign.
* The nanoseconds part must contain exactly nine digits.
* The trailing literal must be exactly specified.
* This format parses the {@code toString} format.
*
* @param text the text to parse such as "12345.123456789s(TAI)", not null
* @return the parsed instant, not null
* @throws DateTimeParseException if the text cannot be parsed
*/
@FromString
public static TaiInstant parse(CharSequence text) {
Objects.requireNonNull(text, "text");
Matcher matcher = PARSER.matcher(text);
if (matcher.matches()) {
try {
long seconds = Long.parseLong(matcher.group(1));
long nanos = Long.parseLong(matcher.group(2));
return TaiInstant.ofTaiSeconds(seconds, nanos);
} catch (NumberFormatException ex) {
throw new DateTimeParseException("The text could not be parsed", text, 0, ex);
}
}
throw new DateTimeParseException("The text could not be parsed", text, 0);
}
//-----------------------------------------------------------------------
/**
* Constructs an instance.
*
* @param taiSeconds the number of TAI seconds from the epoch
* @param nanoOfSecond the nanoseconds within the second, from 0 to 999,999,999
*/
private TaiInstant(long taiSeconds, int nanoOfSecond) {
super();
this.seconds = taiSeconds;
this.nanos = nanoOfSecond;
}
//-----------------------------------------------------------------------
/**
* Gets the number of seconds from the TAI epoch of 1958-01-01T00:00:00(TAI).
*
* The TAI second count is a simple incrementing count of seconds where
* second 0 is 1958-01-01T00:00:00(TAI).
* The nanosecond part of the day is returned by {@code getNanosOfSecond}.
*
* @return the seconds from the epoch of 1958-01-01T00:00:00(TAI)
*/
public long getTaiSeconds() {
return seconds;
}
/**
* Returns a copy of this {@code TaiInstant} with the number of seconds
* from the TAI epoch of 1958-01-01T00:00:00(TAI).
*
* The TAI second count is a simple incrementing count of seconds where
* second 0 is 1958-01-01T00:00:00(TAI).
* The nanosecond part of the day is returned by {@code getNanosOfSecond}.
*
* This instance is immutable and unaffected by this method call.
*
* @param taiSeconds the number of seconds from the epoch of 1958-01-01T00:00:00(TAI)
* @return a {@code TaiInstant} based on this instant with the requested second, not null
*/
public TaiInstant withTaiSeconds(long taiSeconds) {
return ofTaiSeconds(taiSeconds, nanos);
}
/**
* Gets the number of nanoseconds, later along the time-line, from the start
* of the second.
*
* The nanosecond-of-second value measures the total number of nanoseconds from
* the second returned by {@code getTaiSeconds()}.
*
* @return the nanoseconds within the second, from 0 to 999,999,999
*/
public int getNano() {
return nanos;
}
/**
* Returns a copy of this {@code TaiInstant} with the nano-of-second value changed.
*
* The nanosecond-of-second value measures the total number of nanoseconds from
* the second returned by {@code getTaiSeconds()}.
*
* This instance is immutable and unaffected by this method call.
*
* @param nanoOfSecond the nano-of-second, from 0 to 999,999,999
* @return a {@code TaiInstant} based on this instant with the requested nano-of-second, not null
* @throws IllegalArgumentException if nanoOfSecond is out of range
*/
public TaiInstant withNano(int nanoOfSecond) {
if (nanoOfSecond < 0 || nanoOfSecond >= NANOS_PER_SECOND) {
throw new IllegalArgumentException("NanoOfSecond must be from 0 to 999,999,999");
}
return ofTaiSeconds(seconds, nanoOfSecond);
}
//-----------------------------------------------------------------------
/**
* Returns a copy of this instant with the specified duration added.
*
* The duration is added using simple addition of the seconds and nanoseconds
* in the duration to the seconds and nanoseconds of this instant.
* As a result, the duration is treated as being measured in TAI compatible seconds
* for the purpose of this method.
*
* This instance is immutable and unaffected by this method call.
*
* @param duration the duration to add, not null
* @return a {@code TaiInstant} based on this instant with the duration added, not null
* @throws ArithmeticException if the calculation exceeds the supported range
*/
public TaiInstant plus(Duration duration) {
long secsToAdd = duration.getSeconds();
int nanosToAdd = duration.getNano();
if ((secsToAdd | nanosToAdd) == 0) {
return this;
}
long secs = Math.addExact(seconds, secsToAdd);
long nanoAdjustment = ((long) nanos) + nanosToAdd; // safe int+int
return ofTaiSeconds(secs, nanoAdjustment);
}
//-----------------------------------------------------------------------
/**
* Returns a copy of this instant with the specified duration subtracted.
*
* The duration is subtracted using simple subtraction of the seconds and nanoseconds
* in the duration from the seconds and nanoseconds of this instant.
* As a result, the duration is treated as being measured in TAI compatible seconds
* for the purpose of this method.
*
* This instance is immutable and unaffected by this method call.
*
* @param duration the duration to subtract, not null
* @return a {@code TaiInstant} based on this instant with the duration subtracted, not null
* @throws ArithmeticException if the calculation exceeds the supported range
*/
public TaiInstant minus(Duration duration) {
long secsToSubtract = duration.getSeconds();
int nanosToSubtract = duration.getNano();
if ((secsToSubtract | nanosToSubtract) == 0) {
return this;
}
long secs = Math.subtractExact(seconds, secsToSubtract);
long nanoAdjustment = ((long) nanos) - nanosToSubtract; // safe int+int
return ofTaiSeconds(secs, nanoAdjustment);
}
//-----------------------------------------------------------------------
/**
* Returns the duration between this instant and the specified instant.
*
* This calculates the duration between this instant and another based on
* the TAI time-scale. Adding the duration to this instant using {@link #plus}
* will always result in an instant equal to the specified instant.
*
* @param otherInstant the instant to calculate the duration until, not null
* @return the duration until the specified instant, may be negative, not null
* @throws ArithmeticException if the calculation exceeds the supported range
*/
public Duration durationUntil(TaiInstant otherInstant) {
long durSecs = Math.subtractExact(otherInstant.seconds, seconds);
long durNanos = otherInstant.nanos - nanos;
return Duration.ofSeconds(durSecs, durNanos);
}
//-----------------------------------------------------------------------
/**
* Converts this instant to an {@code Instant}.
*
* Converting a TAI instant to UTC-SLS requires leap second rules.
* This method uses the latest available system rules.
* The conversion first maps from TAI to UTC, then converts to UTC-SLS.
*
* Conversion to an {@code Instant} will not be completely accurate near
* a leap second in accordance with UTC-SLS.
*
* @return an {@code Instant} representing the best approximation of this instant, not null
* @throws DateTimeException if the range of {@code Instant} is exceeded
* @throws ArithmeticException if numeric overflow occurs
*/
public Instant toInstant() {
return UtcRules.system().convertToInstant(this);
}
/**
* Converts this instant to a {@code UtcInstant}.
*
* Converting a TAI instant to UTC requires leap second rules.
* This method uses the latest available system rules.
*
* The {@code UtcInstant} will represent exactly the same point on the
* time-line as per the available leap-second rules.
* If the leap-second rules change then conversion back to TAI may
* result in a different instant.
*
* @return a {@code UtcInstant} representing the same instant, not null
* @throws DateTimeException if the range of {@code UtcInstant} is exceeded
* @throws ArithmeticException if numeric overflow occurs
*/
public UtcInstant toUtcInstant() {
return UtcRules.system().convertToUtc(this);
}
//-----------------------------------------------------------------------
/**
* Compares this instant to another based on the time-line.
*
* @param otherInstant the other instant to compare to, not null
* @return the comparator value, negative if less, positive if greater
*/
@Override
public int compareTo(TaiInstant otherInstant) {
int cmp = Long.compare(seconds, otherInstant.seconds);
if (cmp != 0) {
return cmp;
}
return nanos - otherInstant.nanos;
}
//-----------------------------------------------------------------------
/**
* Checks if this instant is equal to the specified {@code TaiInstant}.
*
* @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 TaiInstant) {
TaiInstant other = (TaiInstant) 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() {
// TODO: Evaluate hash code
return ((int) (seconds ^ (seconds >>> 32))) + 51 * nanos;
}
//-----------------------------------------------------------------------
/**
* A string representation of this instant.
*
* The string is formatted as {@code {seconds).(nanosOfSecond}s(TAI)}.
* At least one second digit will be present.
* The nanoseconds will always be nine digits.
*
* @return a representation of this instant, not null
*/
@Override
@ToString
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append(seconds);
int pos = buf.length();
buf.append(nanos + NANOS_PER_SECOND);
buf.setCharAt(pos, '.');
buf.append("s(TAI)");
return buf.toString();
}
}
threeten-extra-1.5.0/src/main/java/org/threeten/extra/scale/TimeSource.java 0000664 0000000 0000000 00000010355 13434511741 0026660 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2007-present, 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 org.threeten.extra.scale;
import java.time.Clock;
import java.time.Instant;
/**
* A clock that provides the current UTC or TAI instant.
*
* This clock differs from {@link Clock} in providing access to the current instant
* in the UTC and TAI time-scales. However, there is currently no implementation that
* provides accurate UTC or TAI.
*
*
* 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.
*
* Subclass implementations should implement {@code Serializable} wherever possible.
* They should also be immutable and thread-safe, implementing {@code equals()},
* {@code hashCode()} and {@code toString()} based on their state.
*/
public interface TimeSource {
/**
* Gets the current {@code Instant}.
*
* The instant returned is based on the Java time-scale defined in {@link Instant}.
* An accurate implementation of this interface will return the correct instant
* as per that definition.
*
* @return the current {@code Instant} from this time-source, not null
* @throws RuntimeException if the instant cannot be obtained, not thrown by most implementations
*/
Instant instant();
/**
* Gets the current {@code UtcInstant}.
*
* The UTC time-scale is the current world civil time and includes leap seconds.
* An accurate implementation of this interface will return the correct UTC instant.
*
* @return the current {@code UtcInstant} from this time-source, not null
* @throws RuntimeException if the instant cannot be obtained, not thrown by most implementations
*/
UtcInstant utcInstant();
/**
* Gets the current {@code TaiInstant}.
*
* The TAI time-scale is a simple incrementing number of seconds from the TAI epoch of 1958-01-01(TAI).
* It ignores all human concepts of time such as days.
* An accurate implementation of this interface will return the correct TAI instant.
*
* @return the current {@code TaiInstant} from this time-source, not null
* @throws RuntimeException if the instant cannot be obtained, not thrown by most implementations
*/
TaiInstant taiInstant();
}
threeten-extra-1.5.0/src/main/java/org/threeten/extra/scale/UtcInstant.java 0000664 0000000 0000000 00000053017 13434511741 0026677 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2007-present, 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 org.threeten.extra.scale;
import static java.time.temporal.ChronoField.INSTANT_SECONDS;
import static java.time.temporal.ChronoField.NANO_OF_SECOND;
import static org.threeten.extra.scale.UtcRules.NANOS_PER_SECOND;
import static org.threeten.extra.scale.UtcRules.OFFSET_MJD_EPOCH;
import static org.threeten.extra.scale.UtcRules.SECS_PER_DAY;
import java.io.Serializable;
import java.time.DateTimeException;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.temporal.JulianFields;
import java.time.temporal.TemporalAccessor;
import org.joda.convert.FromString;
import org.joda.convert.ToString;
/**
* An instantaneous point on the time-line measured in the UTC time-scale
* with leap seconds.
*
* The
* This class is an alternative representation based on the UTC time-scale which
* includes leap-seconds. Leap-seconds are additional seconds that are inserted into the
* year-month-day-hour-minute-second time-line in order to keep UTC in line with the solar day.
* When a leap second occurs, an accurate clock will show the time {@code 23:59:60} just before midnight.
*
* Leap-seconds are announced in advance, typically at least six months.
* The {@link UtcRules} class models which dates have leap-seconds.
* All the methods on this class use the latest available system rules.
*
* The system leap-second rules fix the start point of UTC as 1972.
* This date was chosen as UTC was more complex before 1972.
*
* The duration between two points on the UTC time-scale is calculated solely using this class.
* Do not use the {@code between} method on {@code Duration} as that will lose information.
* Instead use {@link #durationUntil(UtcInstant)} on this class.
*
* It is intended that most applications will use the {@code Instant} class
* which uses the UTC-SLS mapping from UTC to guarantee 86400 seconds per day.
* Specialist applications with access to an accurate time-source may find this class useful.
*
*
* The length of the solar day is the standard way that humans measure time.
* As the Earth's rotation changes, the length of the day varies.
* In general, a solar day is slightly longer than 86400 SI seconds.
* The actual length is not predictable and can only be determined by measurement.
* The UT1 time-scale captures these measurements.
*
* 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 leap-seconds.
* A leap-second may be added or removed depending on the Earth's rotational changes.
* If it is removed, then the relevant date will have no time of {@code 23:59:59}.
* If it is added, then the relevant date will have an extra second of {@code 23:59:60}.
*
* 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.
*
* This class may be used for instants in the far past and far future.
* Since some instants will be prior to 1972, it is not strictly an implementation of UTC.
* Instead, it is a proleptic time-scale based on TAI and equivalent to it since 1972.
*
*
* This class must be treated as a value type. Do not synchronize, rely on the
* identity hash code or use the distinction between equals() and ==.
*/
public final class UtcInstant
implements Comparable
* Modified Julian Day is a simple incrementing count of days where day 0 is 1858-11-17.
* Nanosecond-of-day is a simple count of nanoseconds from the start of the day
* including any additional leap-second.
* This method validates the nanosecond-of-day value against the Modified Julian Day.
*
* The nanosecond-of-day value has a valid range from {@code 0} to
* {@code 86,400,000,000,000 - 1} on most days, and a larger or smaller range
* on leap-second days.
*
* The nanosecond value must be positive even for negative values of Modified
* Julian Day. One nanosecond before Modified Julian Day zero will be
* {@code -1} days and the maximum nanosecond value.
*
* @param mjDay the date as a Modified Julian Day (number of days from the epoch of 1858-11-17)
* @param nanoOfDay the nanoseconds within the day, including leap seconds
* @return the UTC instant, not null
* @throws IllegalArgumentException if nanoOfDay is out of range
*/
public static UtcInstant ofModifiedJulianDay(long mjDay, long nanoOfDay) {
UtcRules.system().validateModifiedJulianDay(mjDay, nanoOfDay);
return new UtcInstant(mjDay, nanoOfDay);
}
/**
* Obtains an instance of {@code UtcInstant} from an {@code Instant}.
*
* Converting a UTC-SLS instant to UTC requires leap second rules.
* This method uses the latest available system rules.
*
* Conversion from an {@code Instant} will not be completely accurate near
* a leap second in accordance with UTC-SLS.
*
* @param instant the instant to convert, not null
* @return the UTC instant, not null
* @throws DateTimeException if the range of {@code UtcInstant} is exceeded
* @throws ArithmeticException if numeric overflow occurs
*/
public static UtcInstant of(Instant instant) {
return UtcRules.system().convertToUtc(instant);
}
/**
* Obtains an instance of {@code UtcInstant} from a {@code TaiInstant}.
*
* Converting a TAI instant to UTC requires leap second rules.
* This method uses the latest available system rules.
*
* The {@code UtcInstant} will represent exactly the same point on the
* time-line as per the available leap-second rules.
* If the leap-second rules change then conversion back to TAI may
* result in a different instant.
*
* @param instant the instant to convert, not null
* @return the UTC instant, not null
* @throws DateTimeException if the range of {@code UtcInstant} is exceeded
* @throws ArithmeticException if numeric overflow occurs
*/
public static UtcInstant of(TaiInstant instant) {
return UtcRules.system().convertToUtc(instant);
}
//-------------------------------------------------------------------------
/**
* Obtains an instance of {@code UtcInstant} from a text string
* {@code 2007-12-03T10:15:30.00Z}.
*
* The string must represent a valid instant in UTC and is parsed using
* {@link DateTimeFormatter#ISO_INSTANT} with leap seconds handled.
*
* @param text the text to parse such as "12345.123456789s(TAI)", not null
* @return the parsed instant, not null
* @throws DateTimeParseException if the text cannot be parsed
* @throws DateTimeException if parsed text represents an invalid leap second
*/
@FromString
public static UtcInstant parse(CharSequence text) {
TemporalAccessor parsed = DateTimeFormatter.ISO_INSTANT.parse(text);
long epochSecond = parsed.getLong(INSTANT_SECONDS);
long nanoOfSecond = parsed.getLong(NANO_OF_SECOND);
boolean leap = parsed.query(DateTimeFormatter.parsedLeapSecond());
long epochDay = Math.floorDiv(epochSecond, SECS_PER_DAY);
long mjd = epochDay + OFFSET_MJD_EPOCH;
long nanoOfDay = Math.floorMod(epochSecond, SECS_PER_DAY) * NANOS_PER_SECOND + nanoOfSecond;
if (leap) {
nanoOfDay += NANOS_PER_SECOND;
}
return UtcInstant.ofModifiedJulianDay(mjd, nanoOfDay);
}
//-----------------------------------------------------------------------
/**
* Constructs an instance.
*
* @param mjDay the date as a Modified Julian Day (number of days from the epoch of 1858-11-17)
* @param nanoOfDay the nanoseconds within the day, including leap seconds
*/
private UtcInstant(long mjDay, long nanoOfDay) {
super();
this.mjDay = mjDay;
this.nanoOfDay = nanoOfDay;
}
//-----------------------------------------------------------------------
/**
* Gets the Modified Julian Day (MJD).
*
* The Modified Julian Day is a simple incrementing count of days where day 0 is 1858-11-17.
* The nanosecond part of the day is returned by {@code getNanosOfDay}.
* The day varies in length, being one second longer on a leap day.
*
* @return the Modified Julian Day based on the epoch 1858-11-17
*/
public long getModifiedJulianDay() {
return mjDay;
}
/**
* Returns a copy of this {@code UtcInstant} with the Modified Julian Day (MJD) altered.
*
* The Modified Julian Day is a simple incrementing count of days where day 0 is 1858-11-17.
* The nanosecond part of the day is returned by {@code getNanosOfDay}.
* The day varies in length, being one second longer on a leap day.
*
* This instance is immutable and unaffected by this method call.
*
* @param mjDay the date as a Modified Julian Day (number of days from the epoch of 1858-11-17)
* @return a {@code UtcInstant} based on this instant with the requested day, not null
* @throws DateTimeException if nanoOfDay becomes invalid
*/
public UtcInstant withModifiedJulianDay(long mjDay) {
return UtcInstant.ofModifiedJulianDay(mjDay, nanoOfDay);
}
/**
* Gets the number of nanoseconds, later along the time-line, from the start
* of the Modified Julian Day.
*
* The nanosecond-of-day value measures the total number of nanoseconds within
* the day from the start of the day returned by {@code getModifiedJulianDay}.
* This value will include any additional leap seconds.
*
* @return the nanoseconds within the day, including leap seconds
*/
public long getNanoOfDay() {
return nanoOfDay;
}
/**
* Returns a copy of this {@code UtcInstant} with the nano-of-day altered.
*
* The nanosecond-of-day value measures the total number of nanoseconds within
* the day from the start of the day returned by {@code getModifiedJulianDay}.
* This value will include any additional leap seconds.
*
* This instance is immutable and unaffected by this method call.
*
* @param nanoOfDay the nanoseconds within the day, including leap seconds
* @return a {@code UtcInstant} based on this instant with the requested nano-of-day, not null
* @throws DateTimeException if the nanoOfDay value is invalid
*/
public UtcInstant withNanoOfDay(long nanoOfDay) {
return UtcInstant.ofModifiedJulianDay(mjDay, nanoOfDay);
}
//-----------------------------------------------------------------------
/**
* Checks if the instant is within a leap second.
*
* This method returns true when an accurate clock would return a seconds
* field of 60.
*
* @return true if this instant is within a leap second
*/
public boolean isLeapSecond() {
return nanoOfDay > SECS_PER_DAY * NANOS_PER_SECOND;
}
//-----------------------------------------------------------------------
/**
* Returns a copy of this instant with the specified duration added.
*
* The duration is added using simple addition of the seconds and nanoseconds
* in the duration to the seconds and nanoseconds of this instant.
* As a result, the duration is treated as being measured in TAI compatible seconds
* for the purpose of this method.
*
* This instance is immutable and unaffected by this method call.
*
* @param duration the duration to add, not null
* @return a {@code UtcInstant} with the duration added, not null
* @throws ArithmeticException if the calculation exceeds the supported range
*/
public UtcInstant plus(Duration duration) {
return UtcInstant.of(toTaiInstant().plus(duration));
}
//-----------------------------------------------------------------------
/**
* Returns a copy of this instant with the specified duration subtracted.
*
* The duration is subtracted using simple subtraction of the seconds and nanoseconds
* in the duration from the seconds and nanoseconds of this instant.
* As a result, the duration is treated as being measured in TAI compatible seconds
* for the purpose of this method.
*
* This instance is immutable and unaffected by this method call.
*
* @param duration the duration to subtract, not null
* @return a {@code UtcInstant} with the duration subtracted, not null
* @throws ArithmeticException if the calculation exceeds the supported range
*/
public UtcInstant minus(Duration duration) {
return UtcInstant.of(toTaiInstant().minus(duration));
}
//-----------------------------------------------------------------------
/**
* Returns the duration between this instant and the specified instant.
*
* This calculates the duration between this instant and another based on
* the UTC time-scale. Any leap seconds that occur will be included in the duration.
* Adding the duration to this instant using {@link #plus} will always result
* in an instant equal to the specified instant.
*
* @param utcInstant the instant to calculate the duration until, not null
* @return the duration until the specified instant, may be negative, not null
* @throws ArithmeticException if the calculation exceeds the supported range
*/
public Duration durationUntil(UtcInstant utcInstant) {
TaiInstant thisTAI = toTaiInstant();
TaiInstant otherTAI = utcInstant.toTaiInstant();
return thisTAI.durationUntil(otherTAI);
}
//-----------------------------------------------------------------------
/**
* Converts this instant to an {@code Instant}.
*
* Converting a UTC instant to UTC-SLS requires leap second rules.
* This method uses the latest available system rules.
*
* Conversion to an {@code Instant} will not be completely accurate near
* a leap second in accordance with UTC-SLS.
*
* @return an {@code Instant} representing the best approximation of this instant, not null
* @throws DateTimeException if the range of {@code Instant} is exceeded
* @throws ArithmeticException if numeric overflow occurs
*/
public Instant toInstant() {
return UtcRules.system().convertToInstant(this);
}
/**
* Converts this instant to a {@code TaiInstant}.
*
* Converting a UTC instant to TAI requires leap second rules.
* This method uses the latest available system rules.
*
* The {@code TaiInstant} will represent exactly the same point on the
* time-line as per the available leap-second rules.
* If the leap-second rules change then conversion back to UTC may
* result in a different instant.
*
* @return a {@code TaiInstant} representing the same instant, not null
* @throws DateTimeException if the range of {@code TaiInstant} is exceeded
* @throws ArithmeticException if numeric overflow occurs
*/
public TaiInstant toTaiInstant() {
return UtcRules.system().convertToTai(this);
}
//-----------------------------------------------------------------------
/**
* Compares this instant to another based on the time-line.
*
* The comparison is based first on the Modified Julian Day, then on the nano-of-day.
* 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
*/
@Override
public int compareTo(UtcInstant otherInstant) {
int cmp = Long.compare(mjDay, otherInstant.mjDay);
if (cmp != 0) {
return cmp;
}
return Long.compare(nanoOfDay, otherInstant.nanoOfDay);
}
//-----------------------------------------------------------------------
/**
* Checks if this instant is equal to the specified {@code UtcInstant}.
*
* The comparison is based on the Modified Julian Day, then on the nano-of-day.
*
* @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 UtcInstant) {
UtcInstant other = (UtcInstant) otherInstant;
return this.mjDay == other.mjDay &&
this.nanoOfDay == other.nanoOfDay;
}
return false;
}
/**
* Returns a hash code for this instant.
*
* @return a suitable hash code
*/
@Override
public int hashCode() {
return ((int) (mjDay ^ (mjDay >>> 32))) + 51 * ((int) (nanoOfDay ^ (nanoOfDay >>> 32)));
}
//-----------------------------------------------------------------------
/**
* A string representation of this instant.
*
* The string is formatted using ISO-8601.
* The output includes seconds, 9 nanosecond digits and a trailing 'Z'.
* The time-of-day will be 23:59:60 during a positive leap second.
*
* @return a representation of this instant, not null
*/
@Override
@ToString
public String toString() {
LocalDate date = LocalDate.MAX.with(JulianFields.MODIFIED_JULIAN_DAY, mjDay); // TODO: capacity/import issues
StringBuilder buf = new StringBuilder(30);
int sod = (int) (nanoOfDay / NANOS_PER_SECOND);
int hourValue = sod / (60 * 60);
int minuteValue = (sod / 60) % 60;
int secondValue = sod % 60;
int nanoValue = (int) (nanoOfDay % NANOS_PER_SECOND);
if (hourValue == 24) {
hourValue = 23;
minuteValue = 59;
secondValue = 60;
}
buf.append(date).append('T')
.append(hourValue < 10 ? "0" : "").append(hourValue)
.append(minuteValue < 10 ? ":0" : ":").append(minuteValue)
.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));
}
}
buf.append('Z');
return buf.toString();
}
}
threeten-extra-1.5.0/src/main/java/org/threeten/extra/scale/UtcRules.java 0000664 0000000 0000000 00000036457 13434511741 0026362 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2007-present, 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 org.threeten.extra.scale;
import java.time.DateTimeException;
import java.time.Instant;
import java.util.ConcurrentModificationException;
/**
* Rules defining the UTC time-scale, notably when leap seconds occur.
*
* This class defines the UTC time-scale including when leap seconds occur.
* Subclasses obtain the data from a suitable source, such as a file.
*
* The static methods on this class provide access to the system leap second rules.
* These are used by default in {@code UtcInstant} and {@code TaiInstant}.
* Using other rules requires manual use of this class.
*
* The system rules can be updated using a {@code LeapSeconds.txt}} file.
* You can create your own version of this file and place it in on the classpath
* and it will be used. Due to Java 9 module restrictions, the file is located
* under META-INF to avoid module encapsulation problems -
* {@code META-INF/org/threeten/extra/scale/LeapSeconds.txt}.
*
*
* The system default rules are serializable, immutable and thread-safe.
* They will remain up to date as new leap seconds are added.
*
* @return the system rules, not null
*/
public static UtcRules system() {
return SystemUtcRules.INSTANCE;
}
/**
* Adds a new leap second to the system default leap second rules.
*
* This method registers a new leap second with the system leap second rules.
* Once registered, there is no way to deregister the leap second.
*
* Calling this method is thread-safe.
* Its effects are immediately visible in all threads.
* Where possible, only call this method from a single thread to avoid the possibility of
* a {@code ConcurrentModificationException}.
*
* If the leap second being added matches a previous definition, then the method returns normally.
* If the date is before the last registered date and does not match a previous definition,
* then an exception is thrown.
*
* @param mjDay the Modified Julian Day that the leap second occurs at the end of
* @param leapAdjustment the leap seconds to add/remove at the end of the day, either -1 or 1
* @throws IllegalArgumentException if the leap adjustment is invalid
* @throws IllegalArgumentException if the day is before or equal the last known leap second day
* and the definition does not match a previously registered leap
* @throws ConcurrentModificationException if another thread updates the rules at the same time
*/
public static void registerLeapSecond(long mjDay, int leapAdjustment) {
SystemUtcRules.INSTANCE.register(mjDay, leapAdjustment);
}
//-----------------------------------------------------------------------
/**
* Creates an instance of the rules.
*/
protected UtcRules() {
}
//-----------------------------------------------------------------------
/**
* The name of these rules.
*
* @return the name, not null
*/
public abstract String getName();
/**
* Gets the leap second adjustment on the specified date.
*
* The UTC standard restricts the adjustment on any day to {@code -1} or {@code 1}.
*
* Any leap seconds are added to, or removed from, the end of the specified date.
*
* If the UTC specification is altered to allow multiple leap seconds at once, then
* the result of this method would return a number with an absolute value greater than one.
*
* @param mjDay the date as a Modified Julian Day (number of days from the epoch of 1858-11-17)
* @return the number of seconds added, or removed, from the date, either -1 or 1
*/
public abstract int getLeapSecondAdjustment(long mjDay);
/**
* Gets the offset to TAI on the specified date.
*
* The TAI offset starts at 10 in 1972 and varies from then on based on the
* dates of leap seconds.
* The offset will apply for the whole of the date.
*
* @param mjDay the date as a Modified Julian Day (number of days from the epoch of 1858-11-17)
* @return the TAI offset in seconds
*/
public abstract int getTaiOffset(long mjDay);
/**
* Gets all known leap second dates.
*
* The dates are returned as Modified Julian Day values.
* The leap second is added to, or removed from, the end of the specified dates.
* The dates will be sorted from earliest to latest.
*
* @return an array of leap second dates expressed as Modified Julian Day values, not null
*/
public abstract long[] getLeapSecondDates();
//-----------------------------------------------------------------------
/**
* Validates combination of Modified Julian Day and nanosecond-of-day.
*
* Modified Julian Day is a simple incrementing count of days where day 0 is 1858-11-17.
* Nanosecond-of-day is a simple count of nanoseconds from the start of the day
* including any additional leap-second.
* This method validates the nanosecond-of-day value against the Modified Julian Day.
*
* The nanosecond-of-day value has a valid range from {@code 0} to
* {@code 86,400,000,000,000 - 1} on most days, and a larger or smaller range
* on leap-second days.
*
* @param mjDay the date as a Modified Julian Day (number of days from the epoch of 1858-11-17)
* @param nanoOfDay the nanoseconds within the day, including leap seconds
* @throws DateTimeException if nanoOfDay is out of range
*/
public void validateModifiedJulianDay(long mjDay, long nanoOfDay) {
long leapSecs = getLeapSecondAdjustment(mjDay);
long maxNanos = (SECS_PER_DAY + leapSecs) * NANOS_PER_SECOND;
if (nanoOfDay < 0 || nanoOfDay >= maxNanos) {
throw new DateTimeException("Nanosecond-of-day must be between 0 and " + maxNanos + " on date " + mjDay);
}
}
//-----------------------------------------------------------------------
/**
* Converts a {@code UtcInstant} to a {@code TaiInstant}.
*
* This method converts from the UTC to the TAI time-scale using the
* leap-second rules of the implementation.
*
* @param utcInstant the UTC instant to convert, not null
* @return the converted TAI instant, not null
* @throws DateTimeException if the valid range is exceeded
* @throws ArithmeticException if numeric overflow occurs
*/
public TaiInstant convertToTai(UtcInstant utcInstant) {
long mjd = utcInstant.getModifiedJulianDay();
long nod = utcInstant.getNanoOfDay();
long taiUtcDaySeconds = Math.multiplyExact(Math.subtractExact(mjd, OFFSET_MJD_TAI), SECS_PER_DAY);
long taiSecs = Math.addExact(taiUtcDaySeconds, nod / NANOS_PER_SECOND + getTaiOffset(mjd));
int nos = (int) (nod % NANOS_PER_SECOND);
return TaiInstant.ofTaiSeconds(taiSecs, nos);
}
/**
* Converts a {@code TaiInstant} to a {@code UtcInstant}.
*
* This method converts from the TAI to the UTC time-scale using the
* leap-second rules of the implementation.
*
* @param taiInstant the TAI instant to convert, not null
* @return the converted UTC instant, not null
* @throws DateTimeException if the valid range is exceeded
* @throws ArithmeticException if numeric overflow occurs
*/
public abstract UtcInstant convertToUtc(TaiInstant taiInstant);
//-----------------------------------------------------------------------
/**
* Converts a {@code UtcInstant} to an {@code Instant}.
*
* This method converts from the UTC time-scale to one with 86400 subdivisions
* per day using the leap-second rules of the implementation.
*
* The standard implementation uses the UTC-SLS algorithm.
* Overriding this algorithm is possible, however doing so will conflict other parts
* of the specification.
*
* The algorithm calculates the UTC-SLS nanos-of-day {@code US} from the UTC nanos-of day {@code U}.
* This method converts from an instant with 86400 subdivisions per day
* to the UTC time-scale using the leap-second rules of the implementation.
*
* The standard implementation uses the UTC-SLS algorithm.
* Overriding this algorithm is possible, however doing so will conflict other parts
* of the specification.
*
* The algorithm calculates the UTC nanos-of-day {@code U} from the UTC-SLS nanos-of day {@code US}.
* This method converts from the TAI time-scale to one with 86400 subdivisions
* per day using the leap-second rules of the implementation.
*
* The standard implementation uses UTC-SLS. It uses
* {@link #convertToUtc(TaiInstant)} and {@link #convertToInstant(UtcInstant)}.
*
* @param taiInstant the TAI instant to convert, not null
* @return the converted instant, not null
* @throws DateTimeException if the valid range is exceeded
* @throws ArithmeticException if numeric overflow occurs
*/
public Instant convertToInstant(TaiInstant taiInstant) {
return convertToInstant(convertToUtc(taiInstant));
}
/**
* Converts an {@code Instant} to a {@code TaiInstant}.
*
* This method converts from an instant with 86400 subdivisions per day
* to the TAI time-scale using the leap-second rules of the implementation.
*
* The standard implementation uses the UTC-SLS algorithm. It uses
* {@link #convertToUtc(TaiInstant)} and {@link #convertToInstant(UtcInstant)}.
*
* @param instant the instant to convert, not null
* @return the converted instant, not null
* @throws DateTimeException if the valid range is exceeded
* @throws ArithmeticException if numeric overflow occurs
*/
public TaiInstant convertToTai(Instant instant) {
return convertToTai(convertToUtc(instant));
}
//-----------------------------------------------------------------------
/**
* A string representation of these rules.
*
* @return the string representation, not null
*/
@Override
public String toString() {
return "UtcRules[" + getName() + ']';
}
}
threeten-extra-1.5.0/src/main/java/org/threeten/extra/scale/package-info.java 0000664 0000000 0000000 00000003265 13434511741 0027127 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2007-present, 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.
*/
/**
* Support for time scales that extend {@code java.time.*}.
*/
package org.threeten.extra.scale;
threeten-extra-1.5.0/src/main/resources/ 0000775 0000000 0000000 00000000000 13434511741 0020204 5 ustar 00root root 0000000 0000000 threeten-extra-1.5.0/src/main/resources/META-INF/ 0000775 0000000 0000000 00000000000 13434511741 0021344 5 ustar 00root root 0000000 0000000 threeten-extra-1.5.0/src/main/resources/META-INF/services/ 0000775 0000000 0000000 00000000000 13434511741 0023167 5 ustar 00root root 0000000 0000000 threeten-extra-1.5.0/src/main/resources/META-INF/services/java.time.chrono.Chronology 0000664 0000000 0000000 00000000644 13434511741 0030405 0 ustar 00root root 0000000 0000000 org.threeten.extra.chrono.BritishCutoverChronology
org.threeten.extra.chrono.CopticChronology
org.threeten.extra.chrono.DiscordianChronology
org.threeten.extra.chrono.EthiopicChronology
org.threeten.extra.chrono.InternationalFixedChronology
org.threeten.extra.chrono.JulianChronology
org.threeten.extra.chrono.PaxChronology
org.threeten.extra.chrono.Symmetry010Chronology
org.threeten.extra.chrono.Symmetry454Chronology
threeten-extra-1.5.0/src/main/resources/org/ 0000775 0000000 0000000 00000000000 13434511741 0020773 5 ustar 00root root 0000000 0000000 threeten-extra-1.5.0/src/main/resources/org/threeten/ 0000775 0000000 0000000 00000000000 13434511741 0022611 5 ustar 00root root 0000000 0000000 threeten-extra-1.5.0/src/main/resources/org/threeten/extra/ 0000775 0000000 0000000 00000000000 13434511741 0023734 5 ustar 00root root 0000000 0000000 threeten-extra-1.5.0/src/main/resources/org/threeten/extra/scale/ 0000775 0000000 0000000 00000000000 13434511741 0025023 5 ustar 00root root 0000000 0000000 threeten-extra-1.5.0/src/main/resources/org/threeten/extra/scale/LeapSeconds.txt 0000664 0000000 0000000 00000000715 13434511741 0027767 0 ustar 00root root 0000000 0000000 # the ISO date of the leap second and the TAI offset after that date
1972-01-01 10
1972-06-30 11
1972-12-31 12
1973-12-31 13
1974-12-31 14
1975-12-31 15
1976-12-31 16
1977-12-31 17
1978-12-31 18
1979-12-31 19
1981-06-30 20
1982-06-30 21
1983-06-30 22
1985-06-30 23
1987-12-31 24
1989-12-31 25
1990-12-31 26
1992-06-30 27
1993-06-30 28
1994-06-30 29
1995-12-31 30
1997-06-30 31
1998-12-31 32
2005-12-31 33
2008-12-31 34
2012-06-30 35
2015-06-30 36
2016-12-31 37
threeten-extra-1.5.0/src/main/resources/org/threeten/extra/wordbased.properties 0000664 0000000 0000000 00000001062 13434511741 0030023 0 ustar 00root root 0000000 0000000 WordBased.space=\
WordBased.comma=,
WordBased.commandand=,and
WordBased.commaspaceand=, and
WordBased.commaspace=,
WordBased.spaceandspace=\ and
WordBased.year=\ year
WordBased.years=\ years
WordBased.month=\ month
WordBased.months=\ months
WordBased.week=\ week
WordBased.weeks=\ weeks
WordBased.day=\ day
WordBased.days=\ days
WordBased.hour=\ hour
WordBased.hours=\ hours
WordBased.minute=\ minute
WordBased.minutes=\ minutes
WordBased.second=\ second
WordBased.seconds=\ seconds
WordBased.millisecond=\ millisecond
WordBased.milliseconds=\ milliseconds
threeten-extra-1.5.0/src/main/resources/org/threeten/extra/wordbased_ca.properties 0000664 0000000 0000000 00000001045 13434511741 0030467 0 ustar 00root root 0000000 0000000 WordBased.space=\
WordBased.comma=,
WordBased.commandand=,i
WordBased.commaspaceand=, i
WordBased.commaspace=,
WordBased.spaceandspace=\ i
WordBased.year=\ any
WordBased.years=\ anys
WordBased.month=\ mes
WordBased.months=\ mesos
WordBased.week=\ setmana
WordBased.weeks=\ setmanes
WordBased.day=\ dia
WordBased.days=\ dies
WordBased.hour=\ hora
WordBased.hours=\ hores
WordBased.minute=\ minut
WordBased.minutes=\ minuts
WordBased.second=\ segon
WordBased.seconds=\ segons
WordBased.millisecond=\ milisegon
WordBased.milliseconds=\ milisegons
threeten-extra-1.5.0/src/main/resources/org/threeten/extra/wordbased_cs.properties 0000664 0000000 0000000 00000001673 13434511741 0030520 0 ustar 00root root 0000000 0000000 WordBased.space=\
WordBased.comma=,
WordBased.commandand=,a
WordBased.commaspaceand=, a
WordBased.commaspace=,
WordBased.spaceandspace=\ a
WordBased.years.predicates=One|||End234NotTeens
WordBased.years.list=\ rok||| roky||| let
WordBased.months.predicates=One|||End234NotTeens
WordBased.months.list=\ měsíc||| měsíce||| měsíců
WordBased.weeks.predicates=One|||End234NotTeens
WordBased.weeks.list=\ týden||| týdny||| týdnů
WordBased.days.predicates=One|||End234NotTeens
WordBased.days.list=\ den||| dny||| dnů
WordBased.hours.predicates=One|||End234NotTeens
WordBased.hours.list=\ hodina||| hodiny||| hodin
WordBased.minutes.predicates=One|||End234NotTeens
WordBased.minutes.list=\ minuta||| minuty||| minut
WordBased.seconds.predicates=One|||End234NotTeens
WordBased.seconds.list=\ sekunda||| sekundy||| sekund
WordBased.milliseconds.predicates=One|||End234NotTeens
WordBased.milliseconds.list=\ milisekunda||| milisekundy||| milisekund
threeten-extra-1.5.0/src/main/resources/org/threeten/extra/wordbased_da.properties 0000664 0000000 0000000 00000001077 13434511741 0030475 0 ustar 00root root 0000000 0000000 WordBased.space=\
WordBased.comma=,
WordBased.commandand=,og
WordBased.commaspaceand=, og
WordBased.commaspace=,\
WordBased.spaceandspace=\ og\
WordBased.year=\ \u00e5r
WordBased.years=\ \u00e5r
WordBased.month=\ m\u00e5ned
WordBased.months=\ m\u00e5neder
WordBased.week=\ uge
WordBased.weeks=\ uger
WordBased.day=\ dag
WordBased.days=\ dage
WordBased.hour=\ time
WordBased.hours=\ timer
WordBased.minute=\ minut
WordBased.minutes=\ minutter
WordBased.second=\ sekund
WordBased.seconds=\ sekunder
WordBased.millisecond=\ millisekund
WordBased.milliseconds=\ millisekunder
threeten-extra-1.5.0/src/main/resources/org/threeten/extra/wordbased_de.properties 0000664 0000000 0000000 00000001074 13434511741 0030476 0 ustar 00root root 0000000 0000000 WordBased.space=\
WordBased.comma=,
WordBased.commandand=,und
WordBased.commaspaceand=, und
WordBased.commaspace=,
WordBased.spaceandspace=\ und
WordBased.year=\ Jahr
WordBased.years=\ Jahre
WordBased.month=\ Monat
WordBased.months=\ Monate
WordBased.week=\ Woche
WordBased.weeks=\ Wochen
WordBased.day=\ Tag
WordBased.days=\ Tage
WordBased.hour=\ Stunde
WordBased.hours=\ Stunden
WordBased.minute=\ Minute
WordBased.minutes=\ Minuten
WordBased.second=\ Sekunde
WordBased.seconds=\ Sekunden
WordBased.millisecond=\ Millisekunde
WordBased.milliseconds=\ Millisekunden
threeten-extra-1.5.0/src/main/resources/org/threeten/extra/wordbased_en.properties 0000664 0000000 0000000 00000000001 13434511741 0030475 0 ustar 00root root 0000000 0000000
threeten-extra-1.5.0/src/main/resources/org/threeten/extra/wordbased_es.properties 0000664 0000000 0000000 00000001101 13434511741 0030504 0 ustar 00root root 0000000 0000000 WordBased.space=\
WordBased.comma=,
WordBased.commandand=,y
WordBased.commaspaceand=, y
WordBased.commaspace=,
WordBased.spaceandspace=\ y
WordBased.year=\ a\u00f1o
WordBased.years=\ a\u00f1os
WordBased.month=\ mes
WordBased.months=\ meses
WordBased.week=\ semana
WordBased.weeks=\ semanas
WordBased.day=\ d\u00eda
WordBased.days=\ d\u00edas
WordBased.hour=\ hora
WordBased.hours=\ horas
WordBased.minute=\ minuto
WordBased.minutes=\ minutos
WordBased.second=\ segundo
WordBased.seconds=\ segundos
WordBased.millisecond=\ milisegundo
WordBased.milliseconds=\ milisegundos
threeten-extra-1.5.0/src/main/resources/org/threeten/extra/wordbased_fr.properties 0000664 0000000 0000000 00000001106 13434511741 0030511 0 ustar 00root root 0000000 0000000 WordBased.space=\
WordBased.comma=,
WordBased.commandand=,et
WordBased.commaspaceand=, et
WordBased.commaspace=,
WordBased.spaceandspace=\ et
WordBased.year=\ ann\u00e9e
WordBased.years=\ ann\u00e9es
WordBased.month=\ mois
WordBased.months=\ mois
WordBased.week=\ semaine
WordBased.weeks=\ semaines
WordBased.day=\ jour
WordBased.days=\ jours
WordBased.hour=\ heure
WordBased.hours=\ heures
WordBased.minute=\ minute
WordBased.minutes=\ minutes
WordBased.second=\ seconde
WordBased.seconds=\ secondes
WordBased.millisecond=\ milliseconde
WordBased.milliseconds=\ millisecondes
threeten-extra-1.5.0/src/main/resources/org/threeten/extra/wordbased_it.properties 0000664 0000000 0000000 00000001062 13434511741 0030517 0 ustar 00root root 0000000 0000000 WordBased.space=\
WordBased.comma=,
WordBased.commandand=,e
WordBased.commaspaceand=, e
WordBased.commaspace=,
WordBased.spaceandspace=\ e
WordBased.year=\ anno
WordBased.years=\ anni
WordBased.month=\ mese
WordBased.months=\ mesi
WordBased.week=\ settimana
WordBased.weeks=\ settimane
WordBased.day=\ giorno
WordBased.days=\ giorni
WordBased.hour=\ ora
WordBased.hours=\ ore
WordBased.minute=\ minuto
WordBased.minutes=\ minuti
WordBased.second=\ secondo
WordBased.seconds=\ secondi
WordBased.millisecond=\ millisecondo
WordBased.milliseconds=\ millisecondi
threeten-extra-1.5.0/src/main/resources/org/threeten/extra/wordbased_ja.properties 0000664 0000000 0000000 00000001072 13434511741 0030476 0 ustar 00root root 0000000 0000000 WordBased.space=
WordBased.comma=
WordBased.commandand=
WordBased.commaspaceand=
WordBased.commaspace=
WordBased.spaceandspace=
WordBased.year=\u5E74
WordBased.years=\u5E74
WordBased.month=\u304B\u6708
WordBased.months=\u304B\u6708
WordBased.week=\u9031\u9593
WordBased.weeks=\u9031\u9593
WordBased.day=\u65E5
WordBased.days=\u65E5
WordBased.hour=\u6642\u9593
WordBased.hours=\u6642\u9593
WordBased.minute=\u5206
WordBased.minutes=\u5206
WordBased.second=\u79D2
WordBased.seconds=\u79D2
WordBased.millisecond=\u30DF\u30EA\u79D2
WordBased.milliseconds=\u30DF\u30EA\u79D2
threeten-extra-1.5.0/src/main/resources/org/threeten/extra/wordbased_nl.properties 0000664 0000000 0000000 00000001061 13434511741 0030513 0 ustar 00root root 0000000 0000000 WordBased.space=\
WordBased.comma=,
WordBased.commandand=,en
WordBased.commaspaceand=, en
WordBased.commaspace=,
WordBased.spaceandspace=\ en
WordBased.year=\ jaar
WordBased.years=\ jaar
WordBased.month=\ maand
WordBased.months=\ maanden
WordBased.week=\ week
WordBased.weeks=\ weken
WordBased.day=\ dag
WordBased.days=\ dagen
WordBased.hour=\ uur
WordBased.hours=\ uur
WordBased.minute=\ minuut
WordBased.minutes=\ minuten
WordBased.second=\ seconde
WordBased.seconds=\ seconden
WordBased.millisecond=\ milliseconde
WordBased.milliseconds=\ milliseconden
threeten-extra-1.5.0/src/main/resources/org/threeten/extra/wordbased_pl.properties 0000664 0000000 0000000 00000001654 13434511741 0030525 0 ustar 00root root 0000000 0000000 WordBased.space=\
WordBased.comma=,
WordBased.commandand=,i
WordBased.commaspaceand=, i
WordBased.commaspace=,
WordBased.spaceandspace=\ i
WordBased.years.predicates=One|||End234NotTeens
WordBased.years.list=\ rok||| lata||| lat
WordBased.months.predicates=One|||End234NotTeens
WordBased.months.list=\ miesi\u0105c||| miesi\u0105ce||| miesi\u0119cy
WordBased.weeks.predicates=One|||End234NotTeens
WordBased.weeks.list=\ tydzie\u0144||| tygodnie||| tygodni
WordBased.day=\ dzie\u0144
WordBased.days=\ dni
WordBased.hours.predicates=One|||End234NotTeens
WordBased.hours.list=\ godzina||| godziny||| godzin
WordBased.minutes.predicates=One|||End234NotTeens
WordBased.minutes.list=\ minuta||| minuty||| minut
WordBased.seconds.predicates=One|||End234NotTeens
WordBased.seconds.list=\ sekunda||| sekundy||| sekund
WordBased.milliseconds.predicates=One|||End234NotTeens
WordBased.milliseconds.list=\ milisekunda||| milisekundy||| milisekund
threeten-extra-1.5.0/src/main/resources/org/threeten/extra/wordbased_pt.properties 0000664 0000000 0000000 00000001064 13434511741 0030530 0 ustar 00root root 0000000 0000000 WordBased.space=\
WordBased.comma=,
WordBased.commandand=,e
WordBased.commaspaceand=, e
WordBased.commaspace=,
WordBased.spaceandspace=\ e
WordBased.year=\ ano
WordBased.years=\ anos
WordBased.month=\ m\u00eas
WordBased.months=\ meses
WordBased.week=\ semana
WordBased.weeks=\ semanas
WordBased.day=\ dia
WordBased.days=\ dias
WordBased.hour=\ hora
WordBased.hours=\ horas
WordBased.minute=\ minuto
WordBased.minutes=\ minutos
WordBased.second=\ segundo
WordBased.seconds=\ segundos
WordBased.millisecond=\ milissegundo
WordBased.milliseconds=\ milissegundos
threeten-extra-1.5.0/src/main/resources/org/threeten/extra/wordbased_ro.properties 0000664 0000000 0000000 00000001161 13434511741 0030523 0 ustar 00root root 0000000 0000000 WordBased.space=\
WordBased.comma=,
WordBased.commandand=,\u0219i
WordBased.commaspaceand=, \u0219i
WordBased.commaspace=,
WordBased.spaceandspace=\ \u0219i
WordBased.year=\ an
WordBased.years=\ ani
WordBased.month=\ lun\u0103
WordBased.months=\ luni
WordBased.week=\ s\u0103pt\u0103m\u00E2n\u0103
WordBased.weeks=\ s\u0103pt\u0103m\u00E2ni
WordBased.day=\ zi
WordBased.days=\ zile
WordBased.hour=\ or\u0103
WordBased.hours=\ ore
WordBased.minute=\ minut
WordBased.minutes=\ minute
WordBased.second=\ secund\u0103
WordBased.seconds=\ secunde
WordBased.millisecond=\ milisecund\u0103
WordBased.milliseconds=\ milisecunde
threeten-extra-1.5.0/src/main/resources/org/threeten/extra/wordbased_ru.properties 0000664 0000000 0000000 00000003217 13434511741 0030535 0 ustar 00root root 0000000 0000000 WordBased.space=\u0020
WordBased.comma=,
WordBased.commandand=,\u0438
WordBased.commaspaceand=, \u0438
WordBased.commaspace=,\u0020
WordBased.spaceandspace=\ \u0438\u0020
WordBased.years.predicates=One|||End234NotTeens
WordBased.years.list=\ \u0433\u043E\u0434||| \u0433\u043E\u0434\u0430||| \u043B\u0435\u0442
WordBased.months.predicates=One|||End234NotTeens
WordBased.months.list=\ \u043C\u0435\u0441\u044F\u0446||| \u043C\u0435\u0441\u044F\u0446\u0430||| \u043C\u0435\u0441\u044F\u0446\u0435\u0432
WordBased.weeks.predicates=One|||End234NotTeens
WordBased.weeks.list=\ \u043D\u0435\u0434\u0435\u043B\u044F||| \u043D\u0435\u0434\u0435\u043B\u0438||| \u043D\u0435\u0434\u0435\u043B\u044C
WordBased.days.predicates=One|||End234NotTeens
WordBased.days.list=\ \u0434\u0435\u043D\u044C||| \u0434\u043D\u044F||| \u0434\u043D\u0435\u0439
WordBased.hours.predicates=One|||End234NotTeens
WordBased.hours.list=\ \u0447\u0430\u0441||| \u0447\u0430\u0441\u0430||| \u0447\u0430\u0441\u043E\u0432
WordBased.minutes.predicates=One|||End234NotTeens
WordBased.minutes.list=\ \u043C\u0438\u043D\u0443\u0442\u0430||| \u043C\u0438\u043D\u0443\u0442\u044B||| \u043C\u0438\u043D\u0443\u0442
WordBased.seconds.predicates=One|||End234NotTeens
WordBased.seconds.list=\ \u0441\u0435\u043A\u0443\u043D\u0434\u0430||| \u0441\u0435\u043A\u0443\u043D\u0434\u044B||| \u0441\u0435\u043A\u0443\u043D\u0434
WordBased.milliseconds.predicates=One|||End234NotTeens
WordBased.milliseconds.list=\ \u043C\u0438\u043B\u043B\u0438\u0441\u0435\u043A\u0443\u043D\u0434\u0430||| \u043C\u0438\u043B\u043B\u0438\u0441\u0435\u043A\u0443\u043D\u0434\u044B||| \u043C\u0438\u043B\u043B\u0438\u0441\u0435\u043A\u0443\u043D\u0434
threeten-extra-1.5.0/src/main/resources/org/threeten/extra/wordbased_tr.properties 0000664 0000000 0000000 00000001063 13434511741 0030531 0 ustar 00root root 0000000 0000000 WordBased.space=\
WordBased.comma=,
WordBased.commandand=,ve
WordBased.commaspaceand=, ve
WordBased.commaspace=,
WordBased.spaceandspace=\ ve
WordBased.year=\ y\u0131l
WordBased.years=\ y\u0131l
WordBased.month=\ ay
WordBased.months=\ ay
WordBased.week=\ hafta
WordBased.weeks=\ hafta
WordBased.day=\ g\u00fcn
WordBased.days=\ g\u00fcn
WordBased.hour=\ saat
WordBased.hours=\ saat
WordBased.minute=\ dakika
WordBased.minutes=\ dakika
WordBased.second=\ saniye
WordBased.seconds=\ saniye
WordBased.millisecond=\ milisaniye
WordBased.milliseconds=\ milisaniye
threeten-extra-1.5.0/src/site/ 0000775 0000000 0000000 00000000000 13434511741 0016212 5 ustar 00root root 0000000 0000000 threeten-extra-1.5.0/src/site/markdown/ 0000775 0000000 0000000 00000000000 13434511741 0020034 5 ustar 00root root 0000000 0000000 threeten-extra-1.5.0/src/site/markdown/index.md 0000664 0000000 0000000 00000007537 13434511741 0021501 0 ustar 00root root 0000000 0000000 ## About
**ThreeTen-Extra** provides additional date-time classes that complement those in
[Java SE 8](https://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html).
Not every piece of date/time logic is destined for the JDK.
Some concepts are too specialized or too bulky to make it in.
This project provides some of those additional classes as a well-tested and reliable jar.
It is curated by the primary author of the Java 8 date and time library, [Stephen Colebourne](https://www.joda.org/).
ThreeTen-Extra is licensed under the business-friendly [BSD 3-clause license](license.html).
## Features
The following features are included:
* [`DayOfMonth`](apidocs/org.threeten.extra/org/threeten/extra/DayOfMonth.html) - a day-of-month without month or year
* [`DayOfYear`](apidocs/org.threeten.extra/org/threeten/extra/DayOfYear.html) - a day-of-year without year
* [`AmPm`](apidocs/org.threeten.extra/org/threeten/extra/AmPm.html) - before or after midday
* [`Quarter`](apidocs/org.threeten.extra/org/threeten/extra/Quarter.html) - the four quarters, Q1, Q2, Q3 and Q4
* [`YearQuarter`](apidocs/org.threeten.extra/org/threeten/extra/YearQuarter.html) - combines a year and quarter, 2014-Q4
* [`YearWeek`](apidocs/org.threeten.extra/org/threeten/extra/YearWeek.html) - combines a week-based-year and a week, 2014-W06
* [`Days`](apidocs/org.threeten.extra/org/threeten/extra/Days.html),
[`Weeks`](apidocs/org.threeten.extra/org/threeten/extra/Weeks.html),
[`Months`](apidocs/org.threeten.extra/org/threeten/extra/Months.html) and
[`Years`](apidocs/org.threeten.extra/org/threeten/extra/Years.html) - amounts of time
* [`Interval`](apidocs/org.threeten.extra/org/threeten/extra/Interval.html) - an interval between two instants
* [`PeriodDuration`](apidocs/org.threeten.extra/org/threeten/extra/PeriodDuration.html) - combines `Period` and `Duration`
* Weekend adjusters
* [Coptic](apidocs/org.threeten.extra/org/threeten/extra/chrono/CopticChronology.html) calendar system
* [Ethiopic](apidocs/org.threeten.extra/org/threeten/extra/chrono/EthiopicChronology.html) calendar system
* [Julian](apidocs/org.threeten.extra/org/threeten/extra/chrono/JulianChronology.html) calendar system
* [Word-based](apidocs/org.threeten.extra/org/threeten/extra/AmountFormats.html) period and duration formatting
* Support for the TAI and UTC [time-scales](apidocs/org.threeten.extra/org/threeten/extra/scale/package-summary.html)
## Documentation
Various documentation is available:
* The helpful [user guide](userguide.html)
* The [Javadoc](apidocs/org.threeten.extra/module-summary.html)
* The [change notes](changes-report.html) for each release
* The [GitHub](https://github.com/ThreeTen/threeten-extra) source repository
---
## Releases
Release 1.5.0 is the current release.
This release is considered stable and worthy of the 1.x tag.
ThreeTen-Extra requires Java SE 8 or later and has no [dependencies](dependencies.html).
Available in [Maven Central](https://search.maven.org/search?q=g:org.threeten%20AND%20a:threeten-extra&core=gav).
```xml
* // these two lines are equivalent, but the second approach is recommended
* temporal = thisAmPm.adjustInto(temporal);
* temporal = temporal.with(thisAmPm);
*
* Implementation Requirements:
* This class is immutable and thread-safe.
*/
public final class AmountFormats {
/**
* The number of days per week.
*/
private static final int DAYS_PER_WEEK = 7;
/**
* The number of hours per day.
*/
private static final int HOURS_PER_DAY = 24;
/**
* The number of minutes per hour.
*/
private static final int MINUTES_PER_HOUR = 60;
/**
* The number of seconds per minute.
*/
private static final int SECONDS_PER_MINUTE = 60;
/**
* The number of nanosecond per millisecond.
*/
private static final int NANOS_PER_MILLIS = 1000_000;
/**
* The resource bundle name.
*/
private static final String BUNDLE_NAME = "org.threeten.extra.wordbased";
/**
* The pattern to split lists with.
*/
private static final Pattern SPLITTER = Pattern.compile("[|][|][|]");
/**
* The property file key for the separator ", ".
*/
private static final String WORDBASED_COMMASPACE = "WordBased.commaspace";
/**
* The property file key for the separator " and ".
*/
private static final String WORDBASED_SPACEANDSPACE = "WordBased.spaceandspace";
/**
* The property file key for the word "year".
*/
private static final String WORDBASED_YEAR = "WordBased.year";
/**
* The property file key for the word "month".
*/
private static final String WORDBASED_MONTH = "WordBased.month";
/**
* The property file key for the word "week".
*/
private static final String WORDBASED_WEEK = "WordBased.week";
/**
* The property file key for the word "day".
*/
private static final String WORDBASED_DAY = "WordBased.day";
/**
* The property file key for the word "hour".
*/
private static final String WORDBASED_HOUR = "WordBased.hour";
/**
* The property file key for the word "minute".
*/
private static final String WORDBASED_MINUTE = "WordBased.minute";
/**
* The property file key for the word "second".
*/
private static final String WORDBASED_SECOND = "WordBased.second";
/**
* The property file key for the word "millisecond".
*/
private static final String WORDBASED_MILLISECOND = "WordBased.millisecond";
/**
* The predicate that matches 1 or -1.
*/
private static final IntPredicate PREDICATE_1 = value -> value == 1 || value == -1;
/**
* The predicate that matches numbers ending 2, 3 or 4, but not ending 12, 13 or 14.
*/
private static final IntPredicate PREDICATE_END234_NOTTEENS = value -> {
int abs = Math.abs(value);
int last = abs % 10;
int secondLast = (abs % 100) / 10;
return (last >= 2 && last <= 4 && secondLast != 1);
};
//-----------------------------------------------------------------------
/**
* Formats a period and duration to a string in ISO-8601 format.
* Implementation Requirements:
* This class is immutable and thread-safe.
*
*
* All other {@code ChronoField} instances will return false.
*
* // these two lines are equivalent, but the second approach is recommended
* temporal = thisDay.adjustInto(temporal);
* temporal = temporal.with(thisDay);
*
*
* LocalDate date = day.atMonth(month).atYear(year);
*
*
* LocalDate date = day.atMonth(month).atYear(year);
*
* Implementation Requirements:
* This class is immutable and thread-safe.
*
*
* All other {@code ChronoField} instances will return false.
*
* // these two lines are equivalent, but the second approach is recommended
* temporal = thisDay.adjustInto(temporal);
* temporal = temporal.with(thisDay);
*
*
* LocalDate date = day.atYear(year);
*
*
* LocalDate date = day.atYear(year);
*
* Implementation Requirements:
* This class is immutable and thread-safe.
*
* "P2D" -- Days.of(2)
* "P-2D" -- Days.of(-2)
* "-P2D" -- Days.of(-2)
* "-P-2D" -- Days.of(2)
* "P3W" -- Days.of(3 * 7)
* "P3W-2D" -- Days.of(3 * 7 - 2)
*
*
* @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
*/
@FromString
public static Days parse(CharSequence text) {
Objects.requireNonNull(text, "text");
Matcher matcher = PATTERN.matcher(text);
if (matcher.matches()) {
int negate = "-".equals(matcher.group(1)) ? -1 : 1;
String weeksStr = matcher.group(2);
String daysStr = matcher.group(3);
if (weeksStr != null || daysStr != null) {
int days = 0;
if (daysStr != null) {
try {
days = Integer.parseInt(daysStr);
} catch (NumberFormatException ex) {
throw new DateTimeParseException("Text cannot be parsed to a Days, non-numeric days", text, 0, ex);
}
}
if (weeksStr != null) {
try {
int weeks = Math.multiplyExact(Integer.parseInt(weeksStr), DAYS_PER_WEEK);
days = Math.addExact(days, weeks);
} catch (NumberFormatException ex) {
throw new DateTimeParseException("Text cannot be parsed to a Days, non-numeric weeks", text, 0, ex);
}
}
return of(Math.multiplyExact(days, negate));
}
}
throw new DateTimeParseException("Text cannot be parsed to a Days", text, 0);
}
//-----------------------------------------------------------------------
/**
* Obtains a {@code Days} consisting of the number of days between two dates.
*
* // these two lines are equivalent, but the second approach is recommended
* dateTime = thisAmount.addTo(dateTime);
* dateTime = dateTime.plus(thisAmount);
*
*
* // these two lines are equivalent, but the second approach is recommended
* dateTime = thisAmount.subtractFrom(dateTime);
* dateTime = dateTime.minus(thisAmount);
*
* Implementation Requirements:
* This class is immutable and thread-safe.
*
* "PT2H" -- Hours.of(2)
* "PT-HM" -- Hours.of(-2)
* "-PT2H" -- Hours.of(-2)
* "-PT-2H" -- Hours.of(2)
* "P3D" -- Hours.of(3 * 24)
* "P3DT2H" -- Hours.of(3 * 24 + 2)
*
*
* @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
*/
@FromString
public static Hours parse(CharSequence text) {
Objects.requireNonNull(text, "text");
Matcher matcher = PATTERN.matcher(text);
if (matcher.matches()) {
int negate = "-".equals(matcher.group(1)) ? -1 : 1;
String daysStr = matcher.group(2);
String hoursStr = matcher.group(3);
if (daysStr != null || hoursStr != null) {
int hours = 0;
if (hoursStr != null) {
try {
hours = Integer.parseInt(hoursStr);
} catch (NumberFormatException ex) {
throw new DateTimeParseException("Text cannot be parsed to Hours, non-numeric hours", text, 0, ex);
}
}
if (daysStr != null) {
try {
int daysAsHours = Math.multiplyExact(Integer.parseInt(daysStr), HOURS_PER_DAY);
hours = Math.addExact(hours, daysAsHours);
} catch (NumberFormatException ex) {
throw new DateTimeParseException("Text cannot be parsed to Hours, non-numeric days", text, 0, ex);
}
}
return of(Math.multiplyExact(hours, negate));
}
}
throw new DateTimeParseException("Text cannot be parsed to Hours", text, 0);
}
//-----------------------------------------------------------------------
/**
* Obtains a {@code Hours} consisting of the number of hours between two temporals.
*
* // these two lines are equivalent, but the second approach is recommended
* dateTime = thisAmount.addTo(dateTime);
* dateTime = dateTime.plus(thisAmount);
*
*
* // these two lines are equivalent, but the second approach is recommended
* dateTime = thisAmount.subtractFrom(dateTime);
* dateTime = dateTime.minus(thisAmount);
*
* Implementation Requirements:
* This class is immutable and thread-safe.
*
*
* Implementation Requirements:
* This class is immutable and thread-safe.
*
*
*
* @param text the text to parse, not null
* @return the parsed range, not null
* @throws DateTimeParseException if the text cannot be parsed
*/
@FromString
public static LocalDateRange parse(CharSequence text) {
Objects.requireNonNull(text, "text");
for (int i = 0; i < text.length(); i++) {
if (text.charAt(i) == '/') {
char firstChar = text.charAt(0);
if (firstChar == 'P' || firstChar == 'p') {
// period followed by date
Period duration = Period.parse(text.subSequence(0, i));
LocalDate end = LocalDate.parse(text.subSequence(i + 1, text.length()));
return LocalDateRange.of(end.minus(duration), end);
} else {
// date followed by date or period
LocalDate start = LocalDate.parse(text.subSequence(0, i));
if (i + 1 < text.length()) {
char c = text.charAt(i + 1);
if (c == 'P' || c == 'p') {
Period duration = Period.parse(text.subSequence(i + 1, text.length()));
return LocalDateRange.of(start, start.plus(duration));
}
}
LocalDate end = LocalDate.parse(text.subSequence(i + 1, text.length()));
return LocalDateRange.of(start, end);
}
}
}
throw new DateTimeParseException("LocalDateRange cannot be parsed, no forward slash found", text, 0);
}
//-----------------------------------------------------------------------
/**
* Constructor.
*
* @param startInclusive the start date, inclusive, validated not null
* @param endExclusive the end date, exclusive, validated not null
*/
private LocalDateRange(LocalDate startInclusive, LocalDate endExclusive) {
if (endExclusive.isBefore(startInclusive)) {
throw new DateTimeException("End date must be on or after start date");
}
if (startInclusive.equals(MAXM1)) {
throw new DateTimeException("Range must not start at LocalDate.MAX.minusDays(1)");
}
if (endExclusive.equals(MINP1)) {
throw new DateTimeException("Range must not end at LocalDate.MIN.plusDays(1)");
}
if (endExclusive.equals(LocalDate.MIN) || startInclusive.equals(LocalDate.MAX)) {
throw new DateTimeException("Empty range must not be at LocalDate.MIN or LocalDate.MAX");
}
this.start = startInclusive;
this.end = endExclusive;
}
//-----------------------------------------------------------------------
/**
* Gets the start date of this range, inclusive.
*
* range = range.withStart(date -> date.minus(1, ChronoUnit.WEEKS));
*
*
* @param adjuster the adjuster to use, not null
* @return a copy of this range with the start date adjusted
* @throws DateTimeException if the new start date is after the current end date
*/
public LocalDateRange withStart(TemporalAdjuster adjuster) {
return LocalDateRange.of(start.with(adjuster), end);
}
/**
* Returns a copy of this range with the end date adjusted.
*
* range = range.withEnd(date -> date.plus(1, ChronoUnit.WEEKS));
*
*
* @param adjuster the adjuster to use, not null
* @return a copy of this range with the end date adjusted
* @throws DateTimeException if the new end date is before the current start date
*/
public LocalDateRange withEnd(TemporalAdjuster adjuster) {
return LocalDateRange.of(start, end.with(adjuster));
}
//-----------------------------------------------------------------------
/**
* Checks if this range contains the specified date.
* Implementation Requirements:
* This class is immutable and thread-safe.
*
* "PT2M" -- Minutes.of(2)
* "PT-2M" -- Minutes.of(-2)
* "-PT2M" -- Minutes.of(-2)
* "-PT-2M" -- Minutes.of(2)
* "PT3H" -- Minutes.of(3 * 60)
* "PT3H-2M" -- Minutes.of(3 * 60 - 2)
* "P3D" -- Minutes.of(3 * 24 * 60)
* "P3DT2M" -- Minutes.of(3 * 24 * 60 + 2)
*
*
* @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
*/
@FromString
public static Minutes parse(CharSequence text) {
Objects.requireNonNull(text, "text");
Matcher matcher = PATTERN.matcher(text);
if (matcher.matches()) {
int negate = "-".equals(matcher.group(1)) ? -1 : 1;
String daysStr = matcher.group(2);
String hoursStr = matcher.group(3);
String minutesStr = matcher.group(4);
if (daysStr != null || hoursStr != null || minutesStr != null) {
int minutes = 0;
if (minutesStr != null) {
try {
minutes = Integer.parseInt(minutesStr);
} catch (NumberFormatException ex) {
throw new DateTimeParseException("Text cannot be parsed to Minutes, non-numeric minutes", text, 0, ex);
}
}
if (hoursStr != null) {
try {
int hoursAsMins = Math.multiplyExact(Integer.parseInt(hoursStr), MINUTES_PER_HOUR);
minutes = Math.addExact(minutes, hoursAsMins);
} catch (NumberFormatException ex) {
throw new DateTimeParseException("Text cannot be parsed to Minutes, non-numeric hours", text, 0, ex);
}
}
if (daysStr != null) {
try {
int daysAsMins = Math.multiplyExact(Integer.parseInt(daysStr), MINUTES_PER_DAY);
minutes = Math.addExact(minutes, daysAsMins);
} catch (NumberFormatException ex) {
throw new DateTimeParseException("Text cannot be parsed to Minutes, non-numeric days", text, 0, ex);
}
}
return of(Math.multiplyExact(minutes, negate));
}
}
throw new DateTimeParseException("Text cannot be parsed to Minutes", text, 0);
}
//-----------------------------------------------------------------------
/**
* Obtains a {@code Minutes} consisting of the number of minutes between two temporals.
*
* // these two lines are equivalent, but the second approach is recommended
* dateTime = thisAmount.addTo(dateTime);
* dateTime = dateTime.plus(thisAmount);
*
*
* // these two lines are equivalent, but the second approach is recommended
* dateTime = thisAmount.subtractFrom(dateTime);
* dateTime = dateTime.minus(thisAmount);
*
* Implementation Requirements:
* This class is immutable and thread-safe.
*
* "P2M" -- Months.of(2)
* "P-2M" -- Months.of(-2)
* "-P2M" -- Months.of(-2)
* "-P-2M" -- Months.of(2)
* "P3Y" -- Months.of(3 * 12)
* "P3Y-2M" -- Months.of(3 * 12 - 2)
*
*
* @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
*/
@FromString
public static Months parse(CharSequence text) {
Objects.requireNonNull(text, "text");
Matcher matcher = PATTERN.matcher(text);
if (matcher.matches()) {
int negate = "-".equals(matcher.group(1)) ? -1 : 1;
String weeksStr = matcher.group(2);
String daysStr = matcher.group(3);
if (weeksStr != null || daysStr != null) {
int months = 0;
if (daysStr != null) {
try {
months = Integer.parseInt(daysStr);
} catch (NumberFormatException ex) {
throw new DateTimeParseException("Text cannot be parsed to a Months, non-numeric months", text, 0, ex);
}
}
if (weeksStr != null) {
try {
int years = Math.multiplyExact(Integer.parseInt(weeksStr), MONTHS_PER_YEAR);
months = Math.addExact(months, years);
} catch (NumberFormatException ex) {
throw new DateTimeParseException("Text cannot be parsed to a Months, non-numeric years", text, 0, ex);
}
}
return of(Math.multiplyExact(months, negate));
}
}
throw new DateTimeParseException("Text cannot be parsed to a Months", text, 0);
}
//-----------------------------------------------------------------------
/**
* Obtains a {@code Months} consisting of the number of months between two dates.
*
* // these two lines are equivalent, but the second approach is recommended
* dateTime = thisAmount.addTo(dateTime);
* dateTime = dateTime.plus(thisAmount);
*
*
* // these two lines are equivalent, but the second approach is recommended
* dateTime = thisAmount.subtractFrom(dateTime);
* dateTime = dateTime.minus(thisAmount);
*
*
*
*
*
* Implementation Requirements:
* This class is thread-safe. Updates are atomic and synchronized.
*
* ZonedDateTime.now(thisClock)
* .plus(amountToAdd)
* .toInstant()
*
*
* @param amountToAdd the amount to add, not null
* @throws DateTimeException if the addition cannot be made
* @throws ArithmeticException if numeric overflow occurs
* @see ZonedDateTime#plus(TemporalAmount)
*/
public void add(TemporalAmount amountToAdd) {
Objects.requireNonNull(amountToAdd, "amountToAdd");
synchronized (instantHolder) {
ZonedDateTime current = ZonedDateTime.ofInstant(instantHolder.get(), zone);
ZonedDateTime result = current.plus(amountToAdd);
instantHolder.set(result.toInstant());
}
}
/**
* Adds the specified amount to this clock.
*
* ZonedDateTime.now(thisClock)
* .plus(amountToAdd, unit)
* .toInstant()
*
*
* @param amountToAdd the amount of the specified unit to add, may be negative
* @param unit the unit of the amount to add, not null
* @throws DateTimeException if the unit cannot be added
* @throws UnsupportedTemporalTypeException if the unit is not supported
* @throws ArithmeticException if numeric overflow occurs
* @see ZonedDateTime#plus(long, TemporalUnit)
*/
public void add(long amountToAdd, TemporalUnit unit) {
Objects.requireNonNull(unit, "unit");
synchronized (instantHolder) {
ZonedDateTime current = ZonedDateTime.ofInstant(instantHolder.get(), zone);
ZonedDateTime result = current.plus(amountToAdd, unit);
instantHolder.set(result.toInstant());
}
}
/**
* Adjusts this clock.
*
* ZonedDateTime.now(thisClock)
* .with(adjuster)
* .toInstant()
*
*
* @param adjuster the adjuster to use, not null
* @throws DateTimeException if the adjustment cannot be made
* @throws ArithmeticException if numeric overflow occurs
* @see ZonedDateTime#with(TemporalAdjuster)
*/
public void set(TemporalAdjuster adjuster) {
Objects.requireNonNull(adjuster, "adjuster");
synchronized (instantHolder) {
ZonedDateTime current = ZonedDateTime.ofInstant(instantHolder.get(), zone);
ZonedDateTime result = current.with(adjuster);
instantHolder.set(result.toInstant());
}
}
/**
* Alters the specified field of this clock.
*
* ZonedDateTime.now(thisClock)
* .with(field, newValue)
* .toInstant()
*
*
* @param field the field to set, not null
* @param newValue the new value of the field
* @throws DateTimeException if the field cannot be set
* @throws UnsupportedTemporalTypeException if the field is not supported
* @throws ArithmeticException if numeric overflow occurs
* @see ZonedDateTime#with(TemporalField, long)
*/
public void set(TemporalField field, long newValue) {
Objects.requireNonNull(field, "field");
synchronized (instantHolder) {
ZonedDateTime current = ZonedDateTime.ofInstant(instantHolder.get(), zone);
ZonedDateTime result = current.with(field, newValue);
instantHolder.set(result.toInstant());
}
}
@Override
public ZoneId getZone() {
return zone;
}
/**
* Returns a {@code MutableClock} that uses the specified time-zone and that
* has shared updates with this clock.
* Implementation Requirements:
* This class is immutable and thread-safe.
*
* "P2Y" -- PeriodDuration.of(Period.ofYears(2))
* "P3M" -- PeriodDuration.of(Period.ofMonths(3))
* "P4W" -- PeriodDuration.of(Period.ofWeeks(4))
* "P5D" -- PeriodDuration.of(Period.ofDays(5))
* "PT6H" -- PeriodDuration.of(Duration.ofHours(6))
* "P1Y2M3D" -- PeriodDuration.of(Period.of(1, 2, 3))
* "P1Y2M3W4DT8H" -- PeriodDuration.of(Period.of(1, 2, 25), Duration.ofHours(8))
* "P-1Y2M" -- PeriodDuration.of(Period.of(-1, 2, 0))
* "-P1Y2M" -- PeriodDuration.of(Period.of(-1, -2, 0))
*
*
* @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
*/
@FromString
public static PeriodDuration parse(CharSequence text) {
Objects.requireNonNull(text, "text");
String upper = text.toString().toUpperCase(Locale.ENGLISH);
String negate = "";
if (upper.startsWith("+")) {
upper = upper.substring(1);
} else if (upper.startsWith("-")) {
upper = upper.substring(1);
negate = "-";
}
// duration only, parse original text so it does negation
if (upper.startsWith("PT")) {
return PeriodDuration.of(Duration.parse(text));
}
// period only, parse original text so it does negation
int tpos = upper.indexOf('T');
if (tpos < 0) {
return PeriodDuration.of(Period.parse(text));
}
// period and duration
Period period = Period.parse(negate + upper.substring(0, tpos));
Duration duration = Duration.parse(negate + "P" + upper.substring(tpos));
return PeriodDuration.of(period, duration);
}
//-----------------------------------------------------------------------
/**
* Obtains an instance consisting of the amount of time between two temporals.
* Implementation Requirements:
* This is an immutable and thread-safe enum.
*/
public enum Quarter implements TemporalAccessor, TemporalAdjuster {
/**
* The singleton instance for the first quarter-of-year, from January to March.
* This has the numeric value of {@code 1}.
*/
Q1,
/**
* The singleton instance for the second quarter-of-year, from April to June.
* This has the numeric value of {@code 2}.
*/
Q2,
/**
* The singleton instance for the third quarter-of-year, from July to September.
* This has the numeric value of {@code 3}.
*/
Q3,
/**
* The singleton instance for the fourth quarter-of-year, from October to December.
* This has the numeric value of {@code 4}.
*/
Q4;
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code Quarter} from an {@code int} value.
*
* Q2 will return April.
* Q3 will return July.
* Q4 will return October.
*
* // these two lines are equivalent, but the second approach is recommended
* temporal = thisQuarter.adjustInto(temporal);
* temporal = temporal.with(thisQuarter);
*
*
* dateInMay.with(Q1); // three months earlier
* dateInMay.with(Q2); // no change
* dateInMay.with(Q3); // three months later
* dateInMay.with(Q4); // six months later
*
* Implementation Requirements:
* This class is immutable and thread-safe.
*
* "PT2S" -- Seconds.of(2)
* "PT-2S" -- Seconds.of(-2)
* "-PT2S" -- Seconds.of(-2)
* "-PT-2S" -- Seconds.of(2)
* "PT3S" -- Seconds.of(3 * 60)
* "PT3H-2M7S" -- Seconds.of(3 * 3600 - 2 * 60 + 7)
* "P2D" -- Seconds.of(2 * 86400)
* "P2DT3H" -- Seconds.of(2 * 86400 + 3 * 3600)
*
*
* @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
*/
@FromString
public static Seconds parse(CharSequence text) {
Objects.requireNonNull(text, "text");
Matcher matcher = PATTERN.matcher(text);
if (matcher.matches()) {
int negate = "-".equals(matcher.group(1)) ? -1 : 1;
String daysStr = matcher.group(2);
String hoursStr = matcher.group(3);
String minutesStr = matcher.group(4);
String secondsStr = matcher.group(5);
if (daysStr != null || hoursStr != null || minutesStr != null || secondsStr != null) {
int seconds = 0;
if (secondsStr != null) {
try {
seconds = Integer.parseInt(secondsStr);
} catch (NumberFormatException ex) {
throw new DateTimeParseException("Text cannot be parsed to Seconds, non-numeric seconds", text, 0, ex);
}
}
if (minutesStr != null) {
try {
int minutesAsSecs = Math.multiplyExact(Integer.parseInt(minutesStr), SECONDS_PER_MINUTE);
seconds = Math.addExact(seconds, minutesAsSecs);
} catch (NumberFormatException ex) {
throw new DateTimeParseException("Text cannot be parsed to Seconds, non-numeric minutes", text, 0, ex);
}
}
if (hoursStr != null) {
try {
int hoursAsSecs = Math.multiplyExact(Integer.parseInt(hoursStr), SECONDS_PER_HOUR);
seconds = Math.addExact(seconds, hoursAsSecs);
} catch (NumberFormatException ex) {
throw new DateTimeParseException("Text cannot be parsed to Seconds, non-numeric hours", text, 0, ex);
}
}
if (daysStr != null) {
try {
int daysAsSecs = Math.multiplyExact(Integer.parseInt(daysStr), SECONDS_PER_DAY);
seconds = Math.addExact(seconds, daysAsSecs);
} catch (NumberFormatException ex) {
throw new DateTimeParseException("Text cannot be parsed to Seconds, non-numeric days", text, 0, ex);
}
}
return of(Math.multiplyExact(seconds, negate));
}
}
throw new DateTimeParseException("Text cannot be parsed to Seconds", text, 0);
}
//-----------------------------------------------------------------------
/**
* Obtains a {@code Seconds} consisting of the number of seconds between two temporals.
*
* // these two lines are equivalent, but the second approach is recommended
* dateTime = thisAmount.addTo(dateTime);
* dateTime = dateTime.plus(thisAmount);
*
*
* // these two lines are equivalent, but the second approach is recommended
* dateTime = thisAmount.subtractFrom(dateTime);
* dateTime = dateTime.minus(thisAmount);
*
*
*
*
* Implementation Requirements:
* This is a thread-safe utility class.
* All returned classes are immutable and thread-safe.
*/
public final class Temporals {
/**
* Restricted constructor.
*/
private Temporals() {
}
//-------------------------------------------------------------------------
/**
* Returns an adjuster that returns the next working day, ignoring Saturday and Sunday.
*
* LocalDateTime dt = Temporals.parseFirstMatching(str, LocalDateTime::from, fmt1, fm2, fm3);
*
* 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 Implementation Requirements:
* This class is immutable and thread-safe.
*
* "P2W" -- Weeks.of(2)
* "P-2W" -- Weeks.of(-2)
* "-P2W" -- Weeks.of(-2)
* "-P-2W" -- Weeks.of(2)
*
*
* @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
*/
@FromString
public static Weeks parse(CharSequence text) {
Objects.requireNonNull(text, "text");
Matcher matcher = PATTERN.matcher(text);
if (matcher.matches()) {
int negate = "-".equals(matcher.group(1)) ? -1 : 1;
String str = matcher.group(2);
try {
int val = Integer.parseInt(str);
return of(Math.multiplyExact(val, negate));
} catch (NumberFormatException ex) {
throw new DateTimeParseException("Text cannot be parsed to a Weeks", text, 0, ex);
}
}
throw new DateTimeParseException("Text cannot be parsed to a Weeks", text, 0);
}
//-----------------------------------------------------------------------
/**
* Obtains a {@code Weeks} consisting of the number of weeks between two dates.
*
* // these two lines are equivalent, but the second approach is recommended
* dateTime = thisAmount.addTo(dateTime);
* dateTime = dateTime.plus(thisAmount);
*
*
* // these two lines are equivalent, but the second approach is recommended
* dateTime = thisAmount.subtractFrom(dateTime);
* dateTime = dateTime.minus(thisAmount);
*
* Implementation Requirements:
* This class is immutable and thread-safe.
*
*
* All other {@code ChronoField} instances will return false.
*
*
* All other {@code ChronoUnit} instances will return false.
*
*
*
*
*
* // these two lines are equivalent, but the second approach is recommended
* temporal = thisYearQuarter.adjustInto(temporal);
* temporal = temporal.with(thisYearQuarter);
*
*
* // these two lines are equivalent
* amount = start.until(end, QUARTER_YEARS);
* amount = QUARTER_YEARS.between(start, end);
*
* The choice should be made based on which makes the code more readable.
*
* LocalDate date = yearQuarter.atDay(day);
*
*
* @param dayOfQuarter the day-of-quarter to use, from 1 to 92
* @return the date formed from this year-quarter and the specified day, not null
* @throws DateTimeException if the day is invalid for the year-quarter
* @see #isValidDay(int)
*/
public LocalDate atDay(int dayOfQuarter) {
ValueRange.of(1, lengthOfQuarter()).checkValidValue(dayOfQuarter, DAY_OF_QUARTER);
boolean leap = Year.isLeap(year);
Month month = quarter.firstMonth();
while (dayOfQuarter > month.length(leap)) {
dayOfQuarter -= month.length(leap);
month = month.plus(1);
}
return LocalDate.of(year, month, dayOfQuarter);
}
/**
* Returns a {@code LocalDate} at the end of the quarter.
*
* LocalDate date = year.atQuarter(quarter).atEndOfQuarter();
*
*
* @return the last valid date of this year-quarter, not null
*/
public LocalDate atEndOfQuarter() {
Month month = quarter.firstMonth().plus(2);
return LocalDate.of(year, month, month.maxLength());
}
//-----------------------------------------------------------------------
/**
* Compares this year-quarter to another
* Implementation Requirements:
* This class is immutable and thread-safe.
*
*
* All {@code ChronoField} instances will return false.
*
* // these two lines are equivalent, but the second approach is recommended
* temporal = thisYearWeek.adjustInto(temporal);
* temporal = temporal.with(thisYearWeek);
*
*
* LocalDate date = yearWeek.atDay(MONDAY);
*
*
* @param dayOfWeek the day-of-week to use, not null
* @return the date formed from this year-week and the specified day, not null
*/
public LocalDate atDay(DayOfWeek dayOfWeek) {
Objects.requireNonNull(dayOfWeek, "dayOfWeek");
int correction = LocalDate.of(year, 1, 4).getDayOfWeek().getValue() + 3;
int dayOfYear = week * 7 + dayOfWeek.getValue() - correction;
int maxDaysOfYear = Year.isLeap(year) ? 366 : 365;
if (dayOfYear > maxDaysOfYear) {
return LocalDate.ofYearDay(year + 1, dayOfYear - maxDaysOfYear);
}
if (dayOfYear > 0) {
return LocalDate.ofYearDay(year, dayOfYear);
} else {
int daysOfPreviousYear = Year.isLeap(year - 1) ? 366 : 365;
return LocalDate.ofYearDay(year - 1, daysOfPreviousYear + dayOfYear);
}
}
//-----------------------------------------------------------------------
/**
* Compares this year-week to another
* Implementation Requirements:
* This class is immutable and thread-safe.
*
* "P2Y" -- Years.of(2)
* "P-2Y" -- Years.of(-2)
* "-P2Y" -- Years.of(-2)
* "-P-2Y" -- Years.of(2)
*
*
* @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
*/
@FromString
public static Years parse(CharSequence text) {
Objects.requireNonNull(text, "text");
Matcher matcher = PATTERN.matcher(text);
if (matcher.matches()) {
int negate = "-".equals(matcher.group(1)) ? -1 : 1;
String str = matcher.group(2);
try {
int val = Integer.parseInt(str);
return of(Math.multiplyExact(val, negate));
} catch (NumberFormatException ex) {
throw new DateTimeParseException("Text cannot be parsed to a Years", text, 0, ex);
}
}
throw new DateTimeParseException("Text cannot be parsed to a Years", text, 0);
}
//-----------------------------------------------------------------------
/**
* Obtains a {@code Years} consisting of the number of years between two dates.
*
* // these two lines are equivalent, but the second approach is recommended
* dateTime = thisAmount.addTo(dateTime);
* dateTime = dateTime.plus(thisAmount);
*
*
* // these two lines are equivalent, but the second approach is recommended
* dateTime = thisAmount.subtractFrom(dateTime);
* dateTime = dateTime.minus(thisAmount);
*
* Implementation Requirements
* Implementations must be immutable and thread-safe.
*/
abstract class AbstractDate
implements ChronoLocalDate {
/**
* Creates an instance.
*/
AbstractDate() {
}
//-----------------------------------------------------------------------
abstract int getProlepticYear();
abstract int getMonth();
abstract int getDayOfMonth();
abstract int getDayOfYear();
AbstractDate withDayOfYear(int value) {
return plusDays(value - getDayOfYear());
}
int lengthOfWeek() {
return 7;
}
int lengthOfYearInMonths() {
return 12;
}
abstract ValueRange rangeAlignedWeekOfMonth();
abstract AbstractDate resolvePrevious(int newYear, int newMonth, int dayOfMonth);
AbstractDate resolveEpochDay(long epochDay) {
return (AbstractDate) getChronology().dateEpochDay(epochDay);
}
//-----------------------------------------------------------------------
@Override
public ValueRange range(TemporalField field) {
if (field instanceof ChronoField) {
if (isSupported(field)) {
return rangeChrono((ChronoField) field);
}
throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
return field.rangeRefinedBy(this);
}
ValueRange rangeChrono(ChronoField field) {
switch (field) {
case DAY_OF_MONTH:
return ValueRange.of(1, lengthOfMonth());
case DAY_OF_YEAR:
return ValueRange.of(1, lengthOfYear());
case ALIGNED_WEEK_OF_MONTH:
return rangeAlignedWeekOfMonth();
default:
break;
}
return getChronology().range(field);
}
//-----------------------------------------------------------------------
@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 getAlignedDayOfWeekInMonth();
case ALIGNED_DAY_OF_WEEK_IN_YEAR:
return getAlignedDayOfWeekInYear();
case DAY_OF_MONTH:
return getDayOfMonth();
case DAY_OF_YEAR:
return getDayOfYear();
case EPOCH_DAY:
return toEpochDay();
case ALIGNED_WEEK_OF_MONTH:
return getAlignedWeekOfMonth();
case ALIGNED_WEEK_OF_YEAR:
return getAlignedWeekOfYear();
case MONTH_OF_YEAR:
return getMonth();
case PROLEPTIC_MONTH:
return getProlepticMonth();
case YEAR_OF_ERA:
return getYearOfEra();
case YEAR:
return getProlepticYear();
case ERA:
return (getProlepticYear() >= 1 ? 1 : 0);
default:
break;
}
throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
return field.getFrom(this);
}
int getAlignedDayOfWeekInMonth() {
return ((getDayOfMonth() - 1) % lengthOfWeek()) + 1;
}
int getAlignedDayOfWeekInYear() {
return ((getDayOfYear() - 1) % lengthOfWeek()) + 1;
}
int getAlignedWeekOfMonth() {
return ((getDayOfMonth() - 1) / lengthOfWeek()) + 1;
}
int getAlignedWeekOfYear() {
return ((getDayOfYear() - 1) / lengthOfWeek()) + 1;
}
int getDayOfWeek() {
return (int) (Math.floorMod(toEpochDay() + 3, 7) + 1);
}
long getProlepticMonth() {
return getProlepticYear() * lengthOfYearInMonths() + getMonth() - 1;
}
int getYearOfEra() {
return getProlepticYear() >= 1 ? getProlepticYear() : 1 - getProlepticYear();
}
//-------------------------------------------------------------------------
@Override
public AbstractDate with(TemporalField field, long newValue) {
if (field instanceof ChronoField) {
ChronoField f = (ChronoField) field;
getChronology().range(f).checkValidValue(newValue, f);
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 resolvePrevious(getProlepticYear(), getMonth(), nvalue);
case DAY_OF_YEAR:
return withDayOfYear(nvalue);
case EPOCH_DAY:
return resolveEpochDay(newValue);
case ALIGNED_WEEK_OF_MONTH:
return plusDays((newValue - getLong(ALIGNED_WEEK_OF_MONTH)) * lengthOfWeek());
case ALIGNED_WEEK_OF_YEAR:
return plusDays((newValue - getLong(ALIGNED_WEEK_OF_YEAR)) * lengthOfWeek());
case MONTH_OF_YEAR:
return resolvePrevious(getProlepticYear(), nvalue, getDayOfMonth());
case PROLEPTIC_MONTH:
return plusMonths(newValue - getProlepticMonth());
case YEAR_OF_ERA:
return resolvePrevious(getProlepticYear() >= 1 ? nvalue : 1 - nvalue, getMonth(), getDayOfMonth());
case YEAR:
return resolvePrevious(nvalue, getMonth(), getDayOfMonth());
case ERA:
return newValue == getLong(ERA) ? this : resolvePrevious(1 - getProlepticYear(), getMonth(), getDayOfMonth());
default:
break;
}
throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
return field.adjustInto(this, newValue);
}
@Override
public AbstractDate 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));
default:
break;
}
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
}
return unit.addTo(this, amountToAdd);
}
AbstractDate plusYears(long yearsToAdd) {
if (yearsToAdd == 0) {
return this;
}
int newYear = YEAR.checkValidIntValue(Math.addExact(getProlepticYear(), yearsToAdd));
return resolvePrevious(newYear, getMonth(), getDayOfMonth());
}
AbstractDate plusMonths(long months) {
if (months == 0) {
return this;
}
long curEm = getProlepticMonth();
long calcEm = Math.addExact(curEm, months);
int newYear = Math.toIntExact(Math.floorDiv(calcEm, lengthOfYearInMonths()));
int newMonth = (int) (Math.floorMod(calcEm, lengthOfYearInMonths()) + 1);
return resolvePrevious(newYear, newMonth, getDayOfMonth());
}
AbstractDate plusWeeks(long amountToAdd) {
return plusDays(Math.multiplyExact(amountToAdd, lengthOfWeek()));
}
AbstractDate plusDays(long days) {
if (days == 0) {
return this;
}
return resolveEpochDay(Math.addExact(toEpochDay(), days));
}
//-------------------------------------------------------------------------
long until(AbstractDate end, TemporalUnit unit) {
if (unit instanceof ChronoUnit) {
switch ((ChronoUnit) unit) {
case DAYS:
return daysUntil(end);
case WEEKS:
return weeksUntil(end);
case MONTHS:
return monthsUntil(end);
case YEARS:
return monthsUntil(end) / lengthOfYearInMonths();
case DECADES:
return monthsUntil(end) / (lengthOfYearInMonths() * 10);
case CENTURIES:
return monthsUntil(end) / (lengthOfYearInMonths() * 100);
case MILLENNIA:
return monthsUntil(end) / (lengthOfYearInMonths() * 1000);
case ERAS:
return end.getLong(ERA) - getLong(ERA);
default:
break;
}
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
}
return unit.between(this, end);
}
long daysUntil(ChronoLocalDate end) {
return end.toEpochDay() - toEpochDay(); // no overflow
}
long weeksUntil(AbstractDate end) {
return daysUntil(end) / lengthOfWeek();
}
long monthsUntil(AbstractDate end) {
long packed1 = getProlepticMonth() * 256L + getDayOfMonth(); // no overflow
long packed2 = end.getProlepticMonth() * 256L + end.getDayOfMonth(); // no overflow
return (packed2 - packed1) / 256L;
}
ChronoPeriod doUntil(AbstractDate end) {
long totalMonths = end.getProlepticMonth() - this.getProlepticMonth(); // safe
int days = end.getDayOfMonth() - this.getDayOfMonth();
if (totalMonths > 0 && days < 0) {
totalMonths--;
AbstractDate calcDate = this.plusMonths(totalMonths);
days = (int) (end.toEpochDay() - calcDate.toEpochDay()); // safe
} else if (totalMonths < 0 && days > 0) {
totalMonths++;
days -= end.lengthOfMonth();
}
long years = totalMonths / lengthOfYearInMonths(); // safe
int months = (int) (totalMonths % lengthOfYearInMonths()); // safe
return getChronology().period(Math.toIntExact(years), months, days);
}
//-------------------------------------------------------------------------
/**
* Compares this date to another date, including the chronology.
* Implementation Requirements
* Implementations must be immutable and thread-safe.
*/
abstract class AbstractNileChronology
extends AbstractChronology {
/**
* Range of proleptic-year.
*/
static final ValueRange YEAR_RANGE = ValueRange.of(-999_998, 999_999);
/**
* Range of year.
*/
static final ValueRange YOE_RANGE = ValueRange.of(1, 999_999);
/**
* Range of proleptic month.
*/
static final ValueRange PROLEPTIC_MONTH_RANGE = ValueRange.of(-999_998 * 13L, 999_999 * 13L + 12);
/**
* Range of months.
*/
static final ValueRange MOY_RANGE = ValueRange.of(1, 13);
/**
* Range of weeks.
*/
static final ValueRange ALIGNED_WOM_RANGE = ValueRange.of(1, 1, 5);
/**
* Range of days.
*/
static final ValueRange DOM_RANGE = ValueRange.of(1, 5, 30);
/**
* Range of days.
*/
static final ValueRange DOM_RANGE_NONLEAP = ValueRange.of(1, 5);
/**
* Range of days.
*/
static final ValueRange DOM_RANGE_LEAP = ValueRange.of(1, 6);
/**
* Private constructor.
*/
AbstractNileChronology() {
}
//-----------------------------------------------------------------------
/**
* Checks if the specified year is a leap year.
* Implementation Requirements
* Implementations must be immutable and thread-safe.
*/
abstract class AbstractNileDate
extends AbstractDate {
/**
* Creates an instance.
*/
AbstractNileDate() {
}
//-----------------------------------------------------------------------
abstract int getEpochDayDifference();
@Override
int getDayOfYear() {
return (getMonth() - 1) * 30 + getDayOfMonth();
}
@Override
AbstractDate withDayOfYear(int value) {
return resolvePrevious(getProlepticYear(), ((value - 1) / 30) + 1, ((value - 1) % 30) + 1);
}
@Override
int lengthOfYearInMonths() {
return 13;
}
@Override
ValueRange rangeAlignedWeekOfMonth() {
return ValueRange.of(1, getMonth() == 13 ? 1 : 5);
}
//-----------------------------------------------------------------------
/**
* Returns the length of the month represented by this date.
*
*
*
* Implementation Requirements
* This class is immutable and thread-safe.
*/
public final class AccountingChronology extends AbstractChronology implements Serializable {
/**
* Serialization version.
*/
private static final long serialVersionUID = 7291205177830286973L;
/**
* Range of proleptic month for 12-month (period) year.
*/
private static final ValueRange PROLEPTIC_MONTH_RANGE_12 = ValueRange.of(-999_999 * 12L, 999_999 * 12L + 11);
/**
* Range of proleptic month for 13-month (period) year.
*/
private static final ValueRange PROLEPTIC_MONTH_RANGE_13 = ValueRange.of(-999_999 * 13L, 999_999 * 13L + 12);
/**
* Range of weeks in year.
*/
private static final ValueRange ALIGNED_WEEK_OF_YEAR_RANGE = ValueRange.of(1, 52, 53);
/**
* Range of days in year.
*/
static final ValueRange DAY_OF_YEAR_RANGE = ValueRange.of(1, 364, 371);
/**
* The day of the week on which a given Accounting year ends.
*/
private final DayOfWeek endsOn;
/**
* Whether the calendar ends in the last week of a given Gregorian/ISO month,
* or nearest to the last day of the month (will sometimes be in the next month).
*/
private final boolean inLastWeek;
/**
* Which Gregorian/ISO end-of-month the year ends in/is nearest to.
*/
private final Month end;
/**
* How to divide an accounting year.
*/
private final AccountingYearDivision division;
/**
* The month which will have the leap-week added.
*/
private final int leapWeekInMonth;
/**
* Difference in days between accounting year end and ISO month end, in ISO year 0.
*/
private final transient int yearZeroDifference;
/**
* Number of weeks in a month range.
*/
private final transient ValueRange alignedWeekOfMonthRange;
/**
* Number of days in a month range.
*/
private final transient ValueRange dayOfMonthRange;
/**
* Number of days from the start of Accounting year 1 (for this chronology) to the start of ISO 1970
*/
private final transient int days0001ToIso1970;
//-----------------------------------------------------------------------
/**
* Creates an {@code AccountingChronology} validating the input.
* Package private as only meant to be called from the builder.
*
* @param endsOn The day-of-week a given year ends on.
* @param end The month-end the year is based on.
* @param inLastWeek Whether the year ends in the last week of the month, or nearest the end-of-month.
* @param division How the year is divided.
* @param leapWeekInMonth The month in which the leap-week resides.
* @return The created Chronology, not null.
* @throws DateTimeException if the chronology cannot be built.
*/
static AccountingChronology create(DayOfWeek endsOn, Month end, boolean inLastWeek, AccountingYearDivision division, int leapWeekInMonth) {
if (endsOn == null || end == null || division == null || leapWeekInMonth == 0) {
throw new IllegalStateException("AccountingCronology cannot be built: "
+ (endsOn == null ? "| ending day-of-week |" : "")
+ (end == null ? "| month ending in/nearest to |" : "")
+ (division == null ? "| how year divided |" : "")
+ (leapWeekInMonth == 0 ? "| leap-week month |" : "")
+ " not set.");
}
if (!division.getMonthsInYearRange().isValidValue(leapWeekInMonth)) {
throw new IllegalStateException("Leap week cannot not be placed in non-existant month " + leapWeekInMonth
+ ", range is [" + division.getMonthsInYearRange() + "].");
}
// Derive cached information.
LocalDate endingLimit = inLastWeek ? LocalDate.of(0, end, 1).with(TemporalAdjusters.lastDayOfMonth()) :
LocalDate.of(0, end, 1).with(TemporalAdjusters.lastDayOfMonth()).plusDays(3);
LocalDate yearZeroEnd = endingLimit.with(TemporalAdjusters.previousOrSame(endsOn));
int yearZeroDifference = (int) yearZeroEnd.until(endingLimit, ChronoUnit.DAYS);
// Longest/shortest month lengths and related
int longestMonthLength = 0;
int shortestMonthLength = Integer.MAX_VALUE;
for (int month = 1; month <= division.getMonthsInYearRange().getMaximum(); month++) {
int monthLength = division.getWeeksInMonth(month);
shortestMonthLength = Math.min(shortestMonthLength, monthLength);
longestMonthLength = Math.max(longestMonthLength, monthLength + (month == leapWeekInMonth ? 1 : 0));
}
ValueRange alignedWeekOfMonthRange = ValueRange.of(1, shortestMonthLength, longestMonthLength);
ValueRange dayOfMonthRange = ValueRange.of(1, shortestMonthLength * 7, longestMonthLength * 7);
int daysToEpoch = Math.toIntExact(0 - yearZeroEnd.plusDays(1).toEpochDay());
return new AccountingChronology(endsOn, end, inLastWeek, division, leapWeekInMonth, yearZeroDifference, alignedWeekOfMonthRange, dayOfMonthRange, daysToEpoch);
}
//-----------------------------------------------------------------------
/**
* Creates an instance from validated data, and cached data.
*
* @param endsOn The day-of-week a given year ends on.
* @param end The month-end the year is based on.
* @param inLastWeek Whether the year ends in the last week of the month, or nearest the end-of-month.
* @param division How the year is divided.
* @param leapWeekInMonth The month in which the leap-week resides.
* @param yearZeroDifference Difference in days between accounting year end and ISO month end, in ISO year 0.
* @param alignedWeekOfMonthRange Range of weeks in month.
* @param dayOfMonthRange Range of days in month.
* @param daysToEpoch The number of days between the start of Accounting 1 and ISO 1970.
*/
private AccountingChronology(DayOfWeek endsOn, Month end, boolean inLastWeek, AccountingYearDivision division, int leapWeekInMonth, int yearZeroDifference, ValueRange alignedWeekOfMonthRange,
ValueRange dayOfMonthRange, int daysToEpoch) {
this.endsOn = endsOn;
this.end = end;
this.inLastWeek = inLastWeek;
this.division = division;
this.leapWeekInMonth = leapWeekInMonth;
this.yearZeroDifference = yearZeroDifference;
this.alignedWeekOfMonthRange = alignedWeekOfMonthRange;
this.dayOfMonthRange = dayOfMonthRange;
this.days0001ToIso1970 = daysToEpoch;
}
/**
* Resolve stored instances.
*
* @return a built, validated instance.
*/
private Object readResolve() {
return AccountingChronology.create(endsOn, end, inLastWeek, getDivision(), leapWeekInMonth);
}
//-----------------------------------------------------------------------
AccountingYearDivision getDivision() {
return division;
}
int getLeapWeekInMonth() {
return leapWeekInMonth;
}
int getDays0001ToIso1970() {
return days0001ToIso1970;
}
//-----------------------------------------------------------------------
/**
* Gets the ID of the chronology - 'Accounting'.
*
*
* Implementation Requirements
* This class is a mutable builder suitable for use from a single thread.
*/
public final class AccountingChronologyBuilder {
/**
* The day of the week on which a given Accounting year ends.
*/
private DayOfWeek endsOn;
/**
* Whether the calendar ends in the last week of a given Gregorian/ISO month,
* or nearest to the last day of the month (will sometimes be in the next month).
*/
private boolean inLastWeek;
/**
* Which Gregorian/ISO end-of-month the year ends in/is nearest to.
*/
private Month end;
/**
* How to divide an accounting year.
*/
private AccountingYearDivision division;
/**
* The month which will have the leap-week added.
*/
private int leapWeekInMonth;
/**
* Constructs a new instance of the builder.
*/
public AccountingChronologyBuilder() {
// Nothing to setup in the constructor.
}
/**
* Sets the day-of-week on which a given Accounting calendar year ends.
*
* @param endsOn The day-of-week on which a given Accounting calendar year ends.
*
* @return this, for chaining, not null.
*/
public AccountingChronologyBuilder endsOn(DayOfWeek endsOn) {
this.endsOn = endsOn;
return this;
}
/**
* Sets the Gregorian/ISO month-end which a given Accounting calendar year ends nearest to.
* Calendars setup this way will occasionally end in the start of the next month.
* For example, for July, the month ends on any day from July 28th to August 3rd.
*
* @param end The Gregorian/ISO month-end a given Accounting calendar year ends nearest to.
*
* @return this, for chaining, not null.
*/
public AccountingChronologyBuilder nearestEndOf(Month end) {
this.inLastWeek = false;
this.end = end;
return this;
}
/**
* Sets the Gregorian/ISO month-end in which a given Accounting calendar year ends.
* Calendars setup this way will always end in the last week of the given month.
* For example, for July, the month ends on any day from July 25th to July 31st.
*
* @param end The Gregorian/ISO month-end a given Accounting calendar year ends in the last week of.
*
* @return this, for chaining, not null.
*/
public AccountingChronologyBuilder inLastWeekOf(Month end) {
this.inLastWeek = true;
this.end = end;
return this;
}
/**
* Sets how a given Accounting year will be divided.
*
* @param division How to divide a given calendar year.
*
* @return this, for chaining, not null.
*/
public AccountingChronologyBuilder withDivision(AccountingYearDivision division) {
this.division = division;
return this;
}
/**
* Sets the month in which the leap-week occurs.
*
* @param leapWeekInMonth The month in which the leap-week occurs.
*
* @return this, for chaining, not null.
*/
public AccountingChronologyBuilder leapWeekInMonth(int leapWeekInMonth) {
this.leapWeekInMonth = leapWeekInMonth;
return this;
}
/**
* Completes this builder by creating the {@code AccountingChronology}.
*
* @return the created chronology, not null.
* @throws DateTimeException if the chronology cannot be built.
*/
public AccountingChronology toChronology() {
return AccountingChronology.create(endsOn, end, inLastWeek, division, leapWeekInMonth);
}
}
threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/AccountingDate.java 0000664 0000000 0000000 00000053651 13434511741 0027700 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2007-present, 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 org.threeten.extra.chrono;
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 org.threeten.extra.chrono.AccountingChronology.DAY_OF_YEAR_RANGE;
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.chrono.ChronoLocalDate;
import java.time.chrono.ChronoLocalDateTime;
import java.time.chrono.ChronoPeriod;
import java.time.temporal.ChronoField;
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.TemporalQuery;
import java.time.temporal.TemporalUnit;
import java.time.temporal.ValueRange;
import java.util.Objects;
/**
* A date in an Accounting calendar system.
* Implementation Requirements
* This class is immutable and thread-safe.
* Implementation Requirements:
* This is an immutable and thread-safe enum.
*/
public enum AccountingEra 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 AccountingEra} from an {@code int} value.
* Implementation Requirements:
* This is an immutable and thread-safe enum.
*/
public enum AccountingYearDivision {
/**
* The singleton instance for a year divided into 4 quarters,
* each having 3 months with lengths of 4, 4, and 5 weeks, respectively.
*/
QUARTERS_OF_PATTERN_4_4_5_WEEKS(new int[] {4, 4, 5, 4, 4, 5, 4, 4, 5, 4, 4, 5}),
/**
* The singleton instance for a year divided into 4 quarters,
* each having 3 months with lengths of 4, 5, and 4 weeks, respectively.
*/
QUARTERS_OF_PATTERN_4_5_4_WEEKS(new int[] {4, 5, 4, 4, 5, 4, 4, 5, 4, 4, 5, 4}),
/**
* The singleton instance for a year divided into 4 quarters,
* each having 3 months with lengths of 5, 4, and 4 weeks, respectively.
*/
QUARTERS_OF_PATTERN_5_4_4_WEEKS(new int[] {5, 4, 4, 5, 4, 4, 5, 4, 4, 5, 4, 4}),
/**
* The singleton instance for a year divided into 13 even months,
* each having 4 weeks.
*/
THIRTEEN_EVEN_MONTHS_OF_4_WEEKS(new int[] {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4});
/**
* The number of weeks in each month.
*/
private final int[] weeksInMonths;
/**
* The range of months in each year.
*/
private final ValueRange monthsInYearRange;
/**
* The elapsed number of weeks at the start of each month.
*/
private final int[] elapsedWeeks;
//-----------------------------------------------------------------------
/**
* Private constructor for enum, for creating cached info.
*
* @param weeksInMonths The number of weeks in each month (period).
*/
private AccountingYearDivision(int[] weeksInMonths) {
this.weeksInMonths = weeksInMonths;
this.monthsInYearRange = ValueRange.of(1, weeksInMonths.length);
this.elapsedWeeks = new int[weeksInMonths.length];
for (int i = 1; i < weeksInMonths.length; i++) {
elapsedWeeks[i] = elapsedWeeks[i - 1] + weeksInMonths[i - 1];
}
}
//-----------------------------------------------------------------------
/**
* Gets the range of months in a year.
*
*
*
* @return the range of months (periods) in a year.
*/
ValueRange getMonthsInYearRange() {
return monthsInYearRange;
}
/**
* Gets the length of the year in months.
*
* @return The length of the year in months.
*/
int lengthOfYearInMonths() {
return weeksInMonths.length;
}
//-----------------------------------------------------------------------
/**
* Get the number of weeks in the given month (period).
*
* @param month The month for which to get the count of weeks.
* @return The count of weeks in the given month.
* @throws DateTimeException if the month isn't in the valid range of months.
*/
int getWeeksInMonth(int month) {
return getWeeksInMonth(month, 0);
}
/**
* Get the number of weeks in the given month (period), with the leap year in the indicated month.
*
* @param month The month for which to get the count of weeks.
* @param leapWeekInMonth The month in which the leap-week resides
* @return The count of weeks in the given month, including any leap week.
* @throws DateTimeException if the month isn't in the valid range of months.
*/
int getWeeksInMonth(int month, int leapWeekInMonth) {
month = monthsInYearRange.checkValidIntValue(month, ChronoField.MONTH_OF_YEAR);
leapWeekInMonth = (leapWeekInMonth == 0 ? 0 : monthsInYearRange.checkValidIntValue(leapWeekInMonth, ChronoField.MONTH_OF_YEAR));
return weeksInMonths[month - 1] + (month == leapWeekInMonth ? 1 : 0);
}
//-----------------------------------------------------------------------
/**
* Get the number of weeks elapsed before the start of the month.
*
* @param month The month
* @return The number of weeks elapsed before the start of the month.
* @throws DateTimeException if the month isn't in the valid range of months.
*/
int getWeeksAtStartOfMonth(int month) {
return getWeeksAtStartOfMonth(month, 0);
}
/**
* Get the number of weeks elapsed before the start of the month.
*
* @param month The month
* @param leapWeekInMonth The month in which the leap-week resides
* @return The number of weeks elapsed before the start of the month, including any leap week.
* @throws DateTimeException if the month isn't in the valid range of months.
*/
int getWeeksAtStartOfMonth(int month, int leapWeekInMonth) {
month = monthsInYearRange.checkValidIntValue(month, ChronoField.MONTH_OF_YEAR);
leapWeekInMonth = (leapWeekInMonth == 0 ? 0 : monthsInYearRange.checkValidIntValue(leapWeekInMonth, ChronoField.MONTH_OF_YEAR));
return elapsedWeeks[month - 1] + (leapWeekInMonth != 0 && month > leapWeekInMonth ? 1 : 0);
}
/**
* Get the month from a count of elapsed weeks.
*
* @param weeksElapsed The weeks elapsed since the start of the year.
* @return the month
* @throws DateTimeException if the month isn't in the valid range of months,
* or the week isn't in the valid range.
*/
int getMonthFromElapsedWeeks(int weeksElapsed) {
return getMonthFromElapsedWeeks(weeksElapsed, 0);
}
/**
* Get the month from a count of elapsed weeks.
*
* @param weeksElapsed The weeks elapsed since the start of the year.
* @param leapWeekInMonth The month in which the leap-week resides
* @return the month
* @throws DateTimeException if the month isn't in the valid range of months,
* or the week isn't in the valid range.
*/
int getMonthFromElapsedWeeks(int weeksElapsed, int leapWeekInMonth) {
if (weeksElapsed < 0 || weeksElapsed >= (leapWeekInMonth == 0 ? 52 : 53)) {
throw new DateTimeException("Count of '" + elapsedWeeks.length + "' elapsed weeks not valid,"
+ " should be in the range [0, " + (leapWeekInMonth == 0 ? 52 : 53) + ")");
}
leapWeekInMonth = (leapWeekInMonth == 0 ? 0 : monthsInYearRange.checkValidIntValue(leapWeekInMonth, ChronoField.MONTH_OF_YEAR));
int month = Arrays.binarySearch(elapsedWeeks, weeksElapsed);
// Binary search returns 0-indexed if found, negative - 1 for insert position if not.
month = (month >= 0 ? month + 1 : 0 - month - 1);
// Need to move to previous month if there was a leap week and in the first week.
return leapWeekInMonth == 0 || month <= leapWeekInMonth || weeksElapsed > elapsedWeeks[month - 1] ? month : month - 1;
}
}
threeten-extra-1.5.0/src/main/java/org/threeten/extra/chrono/BritishCutoverChronology.java 0000664 0000000 0000000 00000044411 13434511741 0032022 0 ustar 00root root 0000000 0000000 /*
* Copyright (c) 2007-present, 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 org.threeten.extra.chrono;
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.chrono.AbstractChronology;
import java.time.chrono.ChronoLocalDateTime;
import java.time.chrono.ChronoZonedDateTime;
import java.time.chrono.Chronology;
import java.time.chrono.Era;
import java.time.chrono.IsoChronology;
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.Map;
/**
* The British Julian-Gregorian cutover calendar system.
*
*
*
* Implementation Requirements
* This class is immutable and thread-safe.
*/
public final class BritishCutoverChronology
extends AbstractChronology
implements Serializable {
/**
* Singleton instance for the Coptic chronology.
*/
public static final BritishCutoverChronology INSTANCE = new BritishCutoverChronology();
/**
* The cutover date, Thursday 14th September 1752.
*/
public static final LocalDate CUTOVER = LocalDate.of(1752, 9, 14);
/**
* The number of cutover days.
*/
static final int CUTOVER_DAYS = 11;
/**
* The cutover year.
*/
static final int CUTOVER_YEAR = 1752;
/**
* Serialization version.
*/
private static final long serialVersionUID = 87235724675472657L;
/**
* Range of day-of-year.
*/
static final ValueRange DOY_RANGE = ValueRange.of(1, 355, 366);
/**
* Range of aligned-week-of-month.
*/
static final ValueRange ALIGNED_WOM_RANGE = ValueRange.of(1, 3, 5);
/**
* Range of aligned-week-of-year.
*/
static final ValueRange ALIGNED_WOY_RANGE = ValueRange.of(1, 51, 53);
/**
* Range of proleptic-year.
*/
static final ValueRange YEAR_RANGE = ValueRange.of(-999_998, 999_999);
/**
* Range of year.
*/
static final ValueRange YOE_RANGE = ValueRange.of(1, 999_999);
/**
* Range of proleptic month.
*/
static final ValueRange PROLEPTIC_MONTH_RANGE = ValueRange.of(-999_998 * 12L, 999_999 * 12L + 11);
/**
* Private constructor, that is public to satisfy the {@code ServiceLoader}.
* @deprecated Use the singleton {@link #INSTANCE} instead.
*/
@Deprecated
public BritishCutoverChronology() {
}
/**
* Resolve singleton.
*
* @return the singleton instance, not null
*/
private Object readResolve() {
return INSTANCE;
}
//-------------------------------------------------------------------------
/**
* Gets the cutover date between the Julian and Gregorian calendar.
* Implementation Requirements
* This class is immutable and thread-safe.
*
*
*
* Implementation Requirements
* This class is immutable and thread-safe.
*/
public final class CopticChronology
extends AbstractNileChronology
implements Serializable {
/**
* Singleton instance for the Coptic chronology.
*/
public static final CopticChronology INSTANCE = new CopticChronology();
/**
* Serialization version.
*/
private static final long serialVersionUID = 7291205177830286973L;
/**
* Private constructor, that is public to satisfy the {@code ServiceLoader}.
* @deprecated Use the singleton {@link #INSTANCE} instead.
*/
@Deprecated
public CopticChronology() {
}
/**
* Resolve singleton.
*
* @return the singleton instance, not null
*/
private Object readResolve() {
return INSTANCE;
}
//-----------------------------------------------------------------------
/**
* Gets the ID of the chronology - 'Coptic'.
* Implementation Requirements
* This class is immutable and thread-safe.
* Implementation Requirements:
* This is an immutable and thread-safe enum.
*/
public enum CopticEra implements Era {
/**
* The singleton instance for the era before the current one, 'Before Era of the Martyrs',
* which has the numeric value 0.
*/
BEFORE_AM,
/**
* The singleton instance for the current era, 'Era of the Martyrs',
* which has the numeric value 1.
*/
AM;
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code CopticEra} from an {@code int} value.
*
*
*
* Implementation Requirements
* This class is immutable and thread-safe.
*/
public final class DiscordianChronology
extends AbstractChronology
implements Serializable {
/**
* Singleton instance for the Discordian chronology.
*/
public static final DiscordianChronology INSTANCE = new DiscordianChronology();
/**
* Serialization version.
*/
private static final long serialVersionUID = 1075529146344250850L;
/**
* Offset from ISO 0
*/
static final int OFFSET_FROM_ISO_0000 = 1166;
/**
* Days in a regular month.
*/
static final int DAYS_IN_MONTH = 73;
/**
* Days in a regular week.
*/
static final int DAYS_IN_WEEK = 5;
/**
* Months in a regular year.
*/
static final int MONTHS_IN_YEAR = 5;
/**
* Weeks in a regular year.
*/
static final int WEEKS_IN_YEAR = 73;
/**
* Range of proleptic-year.
*/
static final ValueRange YEAR_RANGE = ValueRange.of(1, 999_999);
/**
* Range of month-of-year.
*/
static final ValueRange MONTH_OF_YEAR_RANGE = ValueRange.of(0, 1, MONTHS_IN_YEAR, MONTHS_IN_YEAR);
/**
* Range of day-of-month.
*/
static final ValueRange DAY_OF_MONTH_RANGE = ValueRange.of(0, 1, 0, DAYS_IN_MONTH);
/**
* Range of epoch day.
*/
static final ValueRange EPOCH_DAY_RANGE = ValueRange.of(-1_145_400, 999_999 * 365L + 242_499);
/**
* Range of proleptic month.
*/
private static final ValueRange PROLEPTIC_MONTH_RANGE = ValueRange.of(0, (long) 999_999 * MONTHS_IN_YEAR + MONTHS_IN_YEAR - 1);
/**
* Range of day-of-week.
*/
private static final ValueRange DAY_OF_WEEK_RANGE = ValueRange.of(0, 1, 0, DAYS_IN_WEEK);
/**
* Range of aligned day-of-week of year.
*/
private static final ValueRange ALIGNED_DOW_OF_YEAR_RANGE = ValueRange.of(0, 1, DAYS_IN_WEEK, DAYS_IN_WEEK);
/**
* Range of week-of-month.
*/
private static final ValueRange WEEK_OF_MONTH_RANGE = ValueRange.of(0, 1, 0, 15);
/**
* Range of week-of-year.
*/
private static final ValueRange WEEK_OF_YEAR_RANGE = ValueRange.of(0, 1, WEEKS_IN_YEAR, WEEKS_IN_YEAR);
/**
* Range of eras.
*/
private static final ValueRange ERA_RANGE = ValueRange.of(1, 1);
/**
* Private constructor, that is public to satisfy the {@code ServiceLoader}.
* @deprecated Use the singleton {@link #INSTANCE} instead.
*/
@Deprecated
public DiscordianChronology() {
}
/**
* Resolve singleton.
*
* @return the singleton instance, not null
*/
private Object readResolve() {
return INSTANCE;
}
//-----------------------------------------------------------------------
/**
* Gets the ID of the chronology - 'Discordian'.
* Implementation Requirements
* This class is immutable and thread-safe.
* Implementation Requirements:
* This is an immutable and thread-safe enum.
*/
public enum DiscordianEra implements Era {
/**
* The singleton instance for the current era, 'Year of Our Lady of Discord',
* which has the numeric value 1.
*/
YOLD;
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code DiscordianEra} from an {@code int} value.
*
*
*
* Implementation Requirements
* This class is immutable and thread-safe.
*/
public final class EthiopicChronology
extends AbstractNileChronology
implements Serializable {
/**
* Singleton instance for the Ethiopic chronology.
*/
public static final EthiopicChronology INSTANCE = new EthiopicChronology();
/**
* Serialization version.
*/
private static final long serialVersionUID = 53287687268768L;
/**
* Private constructor, that is public to satisfy the {@code ServiceLoader}.
* @deprecated Use the singleton {@link #INSTANCE} instead.
*/
@Deprecated
public EthiopicChronology() {
}
/**
* Resolve singleton.
*
* @return the singleton instance, not null
*/
private Object readResolve() {
return INSTANCE;
}
//-----------------------------------------------------------------------
/**
* Gets the ID of the chronology - 'Ethiopic'.
* Implementation Requirements
* This class is immutable and thread-safe.
* Implementation Requirements:
* This is an immutable and thread-safe enum.
*/
public enum EthiopicEra implements Era {
/**
* The singleton instance for the era before the current one, 'Before Incarnation Era',
* which has the numeric value 0.
*/
BEFORE_INCARNATION,
/**
* The singleton instance for the current era, 'Incarnation Era',
* which has the numeric value 1.
*/
INCARNATION;
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code EthiopicEra} from an {@code int} value.
*
*
*
* Implementation Requirements
* This class is immutable and thread-safe.
*/
public final class InternationalFixedChronology extends AbstractChronology implements Serializable {
/**
* Singleton instance for the International fixed chronology.
*/
public static final InternationalFixedChronology INSTANCE = new InternationalFixedChronology();
/**
* Serialization version UID.
*/
private static final long serialVersionUID = -8252657100538813526L;
/**
* Standard 7-day week.
*/
static final int DAYS_IN_WEEK = 7;
/**
* In all months, there are 4 complete weeks.
*/
static final int WEEKS_IN_MONTH = 4;
/**
* There are 13 months in a year.
*/
static final int MONTHS_IN_YEAR = 13;
/**
* There are 4 weeks of 7 days, or 28 total days in a month.
*/
static final int DAYS_IN_MONTH = WEEKS_IN_MONTH * DAYS_IN_WEEK;
/**
* There are 29 days in a long month.
*/
static final int DAYS_IN_LONG_MONTH = DAYS_IN_MONTH + 1;
/**
* There are 13 months of 28 days, or 365 days in a (non-leap) year.
*/
static final int DAYS_IN_YEAR = MONTHS_IN_YEAR * DAYS_IN_MONTH + 1;
/**
* There are 52 weeks in a year.
*/
static final int WEEKS_IN_YEAR = DAYS_IN_YEAR / DAYS_IN_WEEK;
/**
* The number of days in a 400 year cycle.
*/
static final int DAYS_PER_CYCLE = 146097;
/**
* The number of days from year zero to year 1970, still the era only allows year 1 and higher.
* 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);
/**
* Range of year.
*/
static final ValueRange YEAR_RANGE = ValueRange.of(1, 1_000_000L);
/**
* Epoch day range.
*/
static final ValueRange EPOCH_DAY_RANGE = ValueRange.of(-DAYS_0000_TO_1970, 1_000_000L * DAYS_IN_YEAR + getLeapYearsBefore(1_000_000L) - DAYS_0000_TO_1970);
/**
* Range of proleptic month.
*/
private static final ValueRange PROLEPTIC_MONTH_RANGE = ValueRange.of(13, 1_000_000 * 13L - 1);
/**
* Range of day of month.
*/
static final ValueRange DAY_OF_MONTH_RANGE = ValueRange.of(1, DAYS_IN_MONTH + 1);
/**
* Range of day of year.
*/
static final ValueRange DAY_OF_YEAR_NORMAL_RANGE = ValueRange.of(1, DAYS_IN_YEAR);
/**
* Range of day of leap year.
*/
static final ValueRange DAY_OF_YEAR_LEAP_RANGE = ValueRange.of(1, DAYS_IN_YEAR + 1);
/**
* Range of month of year.
*/
static final ValueRange MONTH_OF_YEAR_RANGE = ValueRange.of(1, MONTHS_IN_YEAR);
/**
* Range of eras.
*/
static final ValueRange ERA_RANGE = ValueRange.of(1, 1);
/**
* Empty range: [0, 0].
*/
static final ValueRange EMPTY_RANGE = ValueRange.of(0, 0);
/**
* Private constructor, that is public to satisfy the {@code ServiceLoader}.
* @deprecated Use the singleton {@link #INSTANCE} instead.
*/
@Deprecated
public InternationalFixedChronology() {
}
/**
* Resolve singleton.
*
* @return the singleton instance, not null
*/
@SuppressWarnings("static-method")
private Object readResolve() {
return INSTANCE;
}
//-----------------------------------------------------------------------
/**
* Gets the ID of the chronology - 'Ifc'.
* Implementation Requirements
* This class is immutable and thread-safe.
* Implementation Requirements:
* This is an immutable and thread-safe enum.
*/
public enum InternationalFixedEra implements Era {
/**
* The singleton instance for the current era, 'Current Era',
* which has the numeric value 1.
*/
CE;
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code InternationalFixedEra} from an {@code int} value.
*
*
*
* Implementation Requirements
* This class is immutable and thread-safe.
*/
public final class JulianChronology extends AbstractChronology implements Serializable {
/**
* Singleton instance for the Julian chronology.
*/
public static final JulianChronology INSTANCE = new JulianChronology();
/**
* Serialization version.
*/
private static final long serialVersionUID = 7291205177830286973L;
/**
* Range of proleptic-year.
*/
static final ValueRange YEAR_RANGE = ValueRange.of(-999_998, 999_999);
/**
* Range of year.
*/
static final ValueRange YOE_RANGE = ValueRange.of(1, 999_999);
/**
* Range of proleptic month.
*/
static final ValueRange PROLEPTIC_MONTH_RANGE = ValueRange.of(-999_998 * 12L, 999_999 * 12L + 11);
/**
* Private constructor, that is public to satisfy the {@code ServiceLoader}.
* @deprecated Use the singleton {@link #INSTANCE} instead.
*/
@Deprecated
public JulianChronology() {
}
/**
* Resolve singleton.
*
* @return the singleton instance, not null
*/
private Object readResolve() {
return INSTANCE;
}
//-----------------------------------------------------------------------
/**
* Gets the ID of the chronology - 'Julian'.
* Implementation Requirements
* This class is immutable and thread-safe.
* Implementation Requirements:
* This is an immutable and thread-safe enum.
*/
public enum JulianEra implements Era {
/**
* The singleton instance for the era before the current one, 'Before Christ',
* which has the numeric value 0.
*/
BC,
/**
* The singleton instance for the current era, 'Anno Domini',
* which has the numeric value 1.
*/
AD;
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code JulianEra} from an {@code int} value.
*
*
* Implementation Requirements
* This class is immutable and thread-safe.
*/
public final class PaxChronology extends AbstractChronology implements Serializable {
/**
* Singleton instance for the Pax chronology.
*/
public static final PaxChronology INSTANCE = new PaxChronology();
/**
* Serialization version.
*/
private static final long serialVersionUID = -7021464635577802085L;
/**
* The leap-month of Pax is only one week long.
*/
static final int WEEKS_IN_LEAP_MONTH = 1;
/**
* Standard 7-day week.
*/
static final int DAYS_IN_WEEK = 7;
/**
* In all months (except Pax), there are 4 complete weeks.
*/
static final int WEEKS_IN_MONTH = 4;
/**
* There are 13 months in a (non-leap) year.
*/
static final int MONTHS_IN_YEAR = 13;
/**
* There are 4 weeks of 7 days, or 28 total days in a month.
*/
static final int DAYS_IN_MONTH = WEEKS_IN_MONTH * DAYS_IN_WEEK;
/**
* There are 13 months of 28 days, or 364 days in a (non-leap) year.
*/
static final int DAYS_IN_YEAR = MONTHS_IN_YEAR * DAYS_IN_MONTH;
/**
* There are 52 weeks in a (non-leap) year.
*/
static final int WEEKS_IN_YEAR = DAYS_IN_YEAR / DAYS_IN_WEEK;
/**
* Range of aligned week of month.
*/
static final ValueRange ALIGNED_WEEK_OF_MONTH_RANGE = ValueRange.of(1, WEEKS_IN_LEAP_MONTH, WEEKS_IN_MONTH);
/**
* Range of aligned week of year.
*/
static final ValueRange ALIGNED_WEEK_OF_YEAR_RANGE = ValueRange.of(1, WEEKS_IN_YEAR, WEEKS_IN_YEAR + 1);
/**
* Range of day of month.
*/
static final ValueRange DAY_OF_MONTH_RANGE = ValueRange.of(1, DAYS_IN_WEEK, DAYS_IN_MONTH);
/**
* Range of day of year.
*/
static final ValueRange DAY_OF_YEAR_RANGE = ValueRange.of(1, DAYS_IN_YEAR, DAYS_IN_YEAR + DAYS_IN_WEEK);
/**
* Range of month of year.
*/
static final ValueRange MONTH_OF_YEAR_RANGE = ValueRange.of(1, MONTHS_IN_YEAR, MONTHS_IN_YEAR + 1);
/**
* Private constructor, that is public to satisfy the {@code ServiceLoader}.
* @deprecated Use the singleton {@link #INSTANCE} instead.
*/
@Deprecated
public PaxChronology() {
}
/**
* Resolve singleton.
*
* @return the singleton instance, not null
*/
@SuppressWarnings("static-method")
private Object readResolve() {
return INSTANCE;
}
//-----------------------------------------------------------------------
/**
* Gets the ID of the chronology - 'Pax'.
* Implementation Requirements
* This class is immutable and thread-safe.
*
*
*
*
* Implementation Requirements:
* This is an immutable and thread-safe enum.
*/
public enum PaxEra 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 PaxEra} from an {@code int} value.
*
*
*
* Implementation Requirements
* This class is immutable and thread-safe.
*/
public final class Symmetry010Chronology
extends AbstractChronology
implements Serializable {
/**
* Singleton instance for the Symmetry010 chronology.
*/
public static final Symmetry010Chronology INSTANCE = new Symmetry010Chronology();
/**
* Serialization version UID.
*/
private static final long serialVersionUID = -1287766365831162587L;
/**
* Standard 7 day weeks.
*/
static final int DAYS_IN_WEEK = 7;
/**
* Standard 12 month years.
*/
static final int MONTHS_IN_YEAR = 12;
/**
* Normal month is 4 weeks.
*/
static final int WEEKS_IN_MONTH = 4;
/**
* Long month is 5 weeks.
*/
static final int WEEKS_IN_MONTH_LONG = 5;
/**
* Normal month is 30 days.
*/
static final int DAYS_IN_MONTH = 30;
/**
* Long month is 31 days.
*/
static final int DAYS_IN_MONTH_LONG = 31;
/**
* Days in quarter, (30 + 31 + 30) = 91
*/
static final int DAYS_IN_QUARTER = DAYS_IN_MONTH + DAYS_IN_MONTH_LONG + DAYS_IN_MONTH;
/**
* There are 4 quarters of 91 (30 + 31 + 30) days each, or 364 days in a (non-leap) year.
*/
static final int DAYS_IN_YEAR = 4 * DAYS_IN_QUARTER;
/**
* Leap years are 364 + 7 days.
*/
static final int DAYS_IN_YEAR_LONG = DAYS_IN_YEAR + DAYS_IN_WEEK;
/**
* 52 weeks in a normal year.
*/
static final int WEEKS_IN_YEAR = DAYS_IN_YEAR / DAYS_IN_WEEK;
/**
* 53 weeks in a leap year.
*/
static final int WEEKS_IN_YEAR_LONG = DAYS_IN_YEAR_LONG / DAYS_IN_WEEK;
/**
* Number of years in a cycle.
*/
private static final int YEARS_IN_CYCLE = 293;
/**
* Number of days in a cycle.
*/
static final int DAYS_PER_CYCLE = YEARS_IN_CYCLE * DAYS_IN_YEAR + WEEKS_IN_YEAR * DAYS_IN_WEEK; // == 294 full years!
/**
* The number of days from year zero to CE 1970, still the era only allows CE 1 and higher.
* There are 6 full 293-year cycles from CE 1 to 1758, with 6 * 52 leap years, i.e. 312.
* There are 37 leap years from CE 1758 to 1970.
*/
//public static final long DAYS_0001_TO_1970 = (DAYS_PER_CYCLE * 6L) + 211L * DAYS_IN_YEAR + 37 * DAYS_IN_WEEK;
//public static final long DAYS_0001_TO_1970_ISO = IsoChronology.INSTANCE.date(1,1,1).toEpochDay() * -1;
public static final long DAYS_0001_TO_1970 = (146097 * 5L) - (31L * 365L + 7L) - 1;
/**
* Highest year in the range.
*/
private static final long MAX_YEAR = 1_000_000L;
/**
* Range of year.
*/
static final ValueRange YEAR_RANGE = ValueRange.of(-MAX_YEAR, MAX_YEAR);
/**
* Epoch day range.
*/
static final ValueRange EPOCH_DAY_RANGE = ValueRange.of(
-MAX_YEAR * DAYS_IN_YEAR - getLeapYearsBefore(MAX_YEAR) * DAYS_IN_WEEK - DAYS_0001_TO_1970,
MAX_YEAR * DAYS_IN_YEAR + getLeapYearsBefore(MAX_YEAR) * DAYS_IN_WEEK - DAYS_0001_TO_1970);
/**
* Range of proleptic month.
*/
private static final ValueRange PROLEPTIC_MONTH_RANGE = ValueRange.of(-MAX_YEAR * MONTHS_IN_YEAR, MAX_YEAR * MONTHS_IN_YEAR - 1);
/**
* Range of day of month.
*/
static final ValueRange DAY_OF_MONTH_RANGE = ValueRange.of(1, DAYS_IN_MONTH, DAYS_IN_MONTH_LONG + 6);
/**
* Range of day of year.
*/
static final ValueRange DAY_OF_YEAR_RANGE = ValueRange.of(1, DAYS_IN_YEAR, DAYS_IN_YEAR_LONG);
/**
* Range of month of year.
*/
static final ValueRange MONTH_OF_YEAR_RANGE = ValueRange.of(1, MONTHS_IN_YEAR);
/**
* Range of eras.
*/
static final ValueRange ERA_RANGE = ValueRange.of(0, 1);
/**
* Empty range: [0, 0].
*/
static final ValueRange EMPTY_RANGE = ValueRange.of(0, 0);
/**
* Private constructor, that is public to satisfy the {@code ServiceLoader}.
* @deprecated Use the singleton {@link #INSTANCE} instead.
*/
@Deprecated
public Symmetry010Chronology() {
}
/**
* Resolve singleton.
*
* @return the singleton instance, not null
*/
private Object readResolve() {
return INSTANCE;
}
//-----------------------------------------------------------------------
/**
* Gets the ID of the chronology - 'Sym010'.
* Implementation Requirements
* This class is immutable and thread-safe.
*
*
*
* Implementation Requirements
* This class is immutable and thread-safe.
*/
public final class Symmetry454Chronology
extends AbstractChronology
implements Serializable {
/**
* Singleton instance for the Symmetry454 chronology.
*/
public static final Symmetry454Chronology INSTANCE = new Symmetry454Chronology();
/**
* Serialization version.
*/
private static final long serialVersionUID = -1287766365831162587L;
/**
* Standard 7 day weeks.
*/
static final int DAYS_IN_WEEK = 7;
/**
* Standard 12 month years.
*/
static final int MONTHS_IN_YEAR = 12;
/**
* Normal month is 4 weeks.
*/
static final int WEEKS_IN_MONTH = 4;
/**
* Long month is 5 weeks.
*/
static final int WEEKS_IN_MONTH_LONG = 5;
/**
* Days in quarter, (4 + 5 + 4) * 7 = 91
*/
static final int DAYS_IN_QUARTER = (WEEKS_IN_MONTH + WEEKS_IN_MONTH_LONG + WEEKS_IN_MONTH) * DAYS_IN_WEEK;
/**
* Days in year, 8 months of 28 days plus 4 months of 35 days, or 364 days in a normal year.
*/
static final int DAYS_IN_YEAR = 4 * DAYS_IN_QUARTER;
/**
* Leap years are 364 + 7 days.
*/
static final int DAYS_IN_YEAR_LONG = DAYS_IN_YEAR + DAYS_IN_WEEK;
/**
* Days in long month.
*/
static final int DAYS_IN_MONTH_LONG = WEEKS_IN_MONTH_LONG * DAYS_IN_WEEK;
/**
* Days in normal month.
*/
static final int DAYS_IN_MONTH = WEEKS_IN_MONTH * DAYS_IN_WEEK;
/**
* 52 weeks in a normal year.
*/
static final int WEEKS_IN_YEAR = DAYS_IN_YEAR / DAYS_IN_WEEK;
/**
* Number of years in a cycle.
*/
private static final int YEARS_IN_CYCLE = 293;
/**
* Number of days in a cycle.
*/
static final int DAYS_PER_CYCLE = YEARS_IN_CYCLE * DAYS_IN_YEAR + WEEKS_IN_YEAR * DAYS_IN_WEEK; // == 294 full years!
/**
* The number of days from year zero to CE 1970, still the era only allows CE 1 and higher.
* There are 6 full 293-year cycles from CE 1 to 1758, with 6 * 52 leap years, i.e. 312.
* There are 37 leap years from CE 1758 to 1970.
*/
//public static final long DAYS_0001_TO_1970 = (DAYS_PER_CYCLE * 6L) + 211L * DAYS_IN_YEAR + 37 * DAYS_IN_WEEK;
//public static final long DAYS_0001_TO_1970_ISO = IsoChronology.INSTANCE.date(1,1,1).toEpochDay() * -1;
public static final long DAYS_0001_TO_1970 = (146097 * 5L) - (31L * 365L + 7L) - 1;
/**
* Highest year in the range.
*/
private static final long MAX_YEAR = 1_000_000L;
/**
* Range of year.
*/
static final ValueRange YEAR_RANGE = ValueRange.of(-MAX_YEAR, MAX_YEAR);
/**
* Epoch day range.
*/
static final ValueRange EPOCH_DAY_RANGE = ValueRange.of(
-MAX_YEAR * DAYS_IN_YEAR - getLeapYearsBefore(MAX_YEAR) * DAYS_IN_WEEK - DAYS_0001_TO_1970,
MAX_YEAR * DAYS_IN_YEAR + getLeapYearsBefore(MAX_YEAR) * DAYS_IN_WEEK - DAYS_0001_TO_1970);
/**
* Range of proleptic month.
*/
private static final ValueRange PROLEPTIC_MONTH_RANGE = ValueRange.of(-MAX_YEAR * MONTHS_IN_YEAR, MAX_YEAR * MONTHS_IN_YEAR - 1);
/**
* Range of day of month.
*/
static final ValueRange DAY_OF_MONTH_RANGE = ValueRange.of(1, DAYS_IN_MONTH, DAYS_IN_MONTH_LONG);
/**
* Range of day of year.
*/
static final ValueRange DAY_OF_YEAR_RANGE = ValueRange.of(1, DAYS_IN_YEAR, DAYS_IN_YEAR + DAYS_IN_WEEK);
/**
* Range of month of year.
*/
static final ValueRange MONTH_OF_YEAR_RANGE = ValueRange.of(1, MONTHS_IN_YEAR);
/**
* Range of eras.
*/
static final ValueRange ERA_RANGE = ValueRange.of(0, 1);
/**
* Empty range: [0, 0].
*/
static final ValueRange EMPTY_RANGE = ValueRange.of(0, 0);
/**
* Private constructor, that is public to satisfy the {@code ServiceLoader}.
* @deprecated Use the singleton {@link #INSTANCE} instead.
*/
@Deprecated
public Symmetry454Chronology() {
}
/**
* Resolve singleton.
*
* @return the singleton instance, not null
*/
private Object readResolve() {
return INSTANCE;
}
//-----------------------------------------------------------------------
/**
* Gets the ID of the chronology - 'Sym454'.
* Implementation Requirements
* This class is immutable and thread-safe.
* Implementation Requirements:
* This class is immutable and thread-safe.
*/
final class SystemUtcRules extends UtcRules implements Serializable {
/**
* The leap seconds config file.
*/
private static final String LEAP_SECONDS_TXT = "org/threeten/extra/scale/LeapSeconds.txt";
/**
* Leap second file format.
*/
private static final Pattern LEAP_FILE_FORMAT = Pattern.compile("([0-9-]{10})[ ]+([0-9]+)");
/**
* Serialization version.
*/
private static final long serialVersionUID = 7594178360693417218L;
/**
* Singleton.
*/
static final SystemUtcRules INSTANCE = new SystemUtcRules();
/**
* The table of leap second dates.
*/
private AtomicReference dataRef = new AtomicReference(loadLeapSeconds());
/** Data holder. */
private static final class Data implements Serializable {
/** Serialization version. */
private static final long serialVersionUID = -3655687912882817265L;
/** Constructor. */
private Data(long[] dates, int[] offsets, long[] taiSeconds) {
super();
this.dates = dates;
this.offsets = offsets;
this.taiSeconds = taiSeconds;
}
/** The table of leap second date when the leap second occurs. */
private final long[] dates;
/** The table of TAI offset after the leap second. */
private final int[] offsets;
/** The table of TAI second when the new offset starts. */
private final long[] taiSeconds;
/**
* @return The modified Julian Date of the newest leap second
*/
public long getNewestDate() {
return dates[dates.length - 1];
}
}
//-----------------------------------------------------------------------
/**
* Restricted constructor.
*/
private SystemUtcRules() {
}
/**
* Resolves singleton.
*
* @return the resolved instance, not null
*/
private Object readResolve() {
return INSTANCE;
}
//-----------------------------------------------------------------------
/**
* Adds a new leap second to these rules.
*
* @param mjDay the Modified Julian Day that the leap second occurs at the end of
* @param leapAdjustment the leap seconds to add/remove at the end of the day, either -1 or 1
* @throws IllegalArgumentException if the leap adjustment is invalid
* @throws IllegalArgumentException if the day is before or equal the last known leap second day
* and the definition does not match a previously registered leap
* @throws ConcurrentModificationException if another thread updates the rules at the same time
*/
void register(long mjDay, int leapAdjustment) {
if (leapAdjustment != -1 && leapAdjustment != 1) {
throw new IllegalArgumentException("Leap adjustment must be -1 or 1");
}
Data data = dataRef.get();
int pos = Arrays.binarySearch(data.dates, mjDay);
int currentAdj = pos > 0 ? data.offsets[pos] - data.offsets[pos - 1] : 0;
if (currentAdj == leapAdjustment) {
return; // matches previous definition
}
if (mjDay <= data.dates[data.dates.length - 1]) {
throw new IllegalArgumentException("Date must be after the last configured leap second date");
}
long[] dates = Arrays.copyOf(data.dates, data.dates.length + 1);
int[] offsets = Arrays.copyOf(data.offsets, data.offsets.length + 1);
long[] taiSeconds = Arrays.copyOf(data.taiSeconds, data.taiSeconds.length + 1);
int offset = offsets[offsets.length - 2] + leapAdjustment;
dates[dates.length - 1] = mjDay;
offsets[offsets.length - 1] = offset;
taiSeconds[taiSeconds.length - 1] = tai(mjDay, offset);
Data newData = new Data(dates, offsets, taiSeconds);
if (dataRef.compareAndSet(data, newData) == false) {
throw new ConcurrentModificationException("Unable to update leap second rules as they have already been updated");
}
}
//-----------------------------------------------------------------------
@Override
public String getName() {
return "System";
}
@Override
public int getLeapSecondAdjustment(long mjDay) {
Data data = dataRef.get();
int pos = Arrays.binarySearch(data.dates, mjDay);
return pos > 0 ? data.offsets[pos] - data.offsets[pos - 1] : 0;
}
@Override
public int getTaiOffset(long mjDay) {
Data data = dataRef.get();
int pos = Arrays.binarySearch(data.dates, mjDay);
pos = (pos < 0 ? ~pos : pos);
return pos > 0 ? data.offsets[pos - 1] : 10;
}
@Override
public long[] getLeapSecondDates() {
Data data = dataRef.get();
return data.dates.clone();
}
//-----------------------------------------------------------------------
@Override
public UtcInstant convertToUtc(TaiInstant taiInstant) {
Data data = dataRef.get();
long[] mjds = data.dates;
long[] tais = data.taiSeconds;
int pos = Arrays.binarySearch(tais, taiInstant.getTaiSeconds());
pos = (pos >= 0 ? pos : ~pos - 1);
int taiOffset = (pos >= 0 ? data.offsets[pos] : 10);
long adjustedTaiSecs = taiInstant.getTaiSeconds() - taiOffset;
long mjd = Math.floorDiv(adjustedTaiSecs, SECS_PER_DAY) + OFFSET_MJD_TAI;
long nod = Math.floorMod(adjustedTaiSecs, SECS_PER_DAY) * NANOS_PER_SECOND + taiInstant.getNano();
long mjdNextRegionStart = (pos + 1 < mjds.length ? mjds[pos + 1] + 1 : Long.MAX_VALUE);
if (mjd == mjdNextRegionStart) { // in leap second
mjd--;
nod = SECS_PER_DAY * NANOS_PER_SECOND + (nod / NANOS_PER_SECOND) * NANOS_PER_SECOND + nod % NANOS_PER_SECOND;
}
return UtcInstant.ofModifiedJulianDay(mjd, nod);
}
//-----------------------------------------------------------------------
/**
* Loads the rules from files in the class loader, often jar files.
*
* @return the list of loaded rules, not null
* @throws Exception if an error occurs
*/
private static Data loadLeapSeconds() {
Data bestData = null;
URL url = null;
try {
// this is the new location of the file, working on Java 8, Java 9 class path and Java 9 module path
Enumerationjava.time
classes use the Java time-scale for simplicity.
* That scale works on the assumption that the time-line is simple, there are no leap-seconds
* and there are always 24 * 60 * 60 seconds in a day. Unfortunately, the Earth's rotation
* is not straightforward, and a solar day does not match this definition.
* Time-scale
* Implementation Requirements:
* This class is immutable and thread-safe.
*
* TaiInstant.ofTaiSeconds(3, 1);
* TaiInstant.ofTaiSeconds(4, -999999999);
* TaiInstant.ofTaiSeconds(2, 1000000001);
*
*
* @param taiSeconds the number of seconds from the epoch of 1958-01-01T00:00:00(TAI)
* @param nanoAdjustment the nanosecond adjustment to the number of seconds, positive or negative
* @return the TAI instant, not null
* @throws ArithmeticException if numeric overflow occurs
*/
public static TaiInstant ofTaiSeconds(long taiSeconds, long nanoAdjustment) {
long secs = Math.addExact(taiSeconds, Math.floorDiv(nanoAdjustment, NANOS_PER_SECOND));
int nos = (int) Math.floorMod(nanoAdjustment, NANOS_PER_SECOND); // safe cast
return new TaiInstant(secs, nos);
}
/**
* Obtains an instance of {@code TaiInstant} from an {@code Instant}.
*
*
* Implementation Requirements:
* This abstract class must be implemented with care to ensure other classes in
* the framework operate correctly.
* All implementations that can be instantiated must be final, immutable and thread-safe.
* java.time
classes use the Java time-scale for simplicity.
* That scale works on the assumption that the time-line is simple, there are no leap-seconds
* and there are always 24 * 60 * 60 seconds in a day. Unfortunately, the Earth's rotation
* is not straightforward, and a solar day does not match this definition.
* Time-scale
* Implementation Requirements:
* This class is immutable and thread-safe.
* Implementation Requirements:
* This is an abstract class and must be implemented with care
* to ensure other classes in the framework operate correctly.
* All implementations must be final, immutable and thread-safe.
* Subclasses should be {@code Serializable} wherever possible.
*/
public abstract class UtcRules {
/**
* Constant for the offset from MJD day 0 to the Java Epoch of 1970-01-01: 40587.
*/
static final int OFFSET_MJD_EPOCH = 40587;
/**
* Constant for the offset from MJD day 0 to TAI day 0 of 1958-01-01: 36204.
*/
static final int OFFSET_MJD_TAI = 36204;
/**
* Constant for number of seconds per standard day: 86,400.
*/
static final long SECS_PER_DAY = 24L * 60L * 60L;
/**
* Constant for nanos per standard second: 1,000,000,000.
*/
static final long NANOS_PER_SECOND = 1000000000L;
/**
* Gets the system default leap second rules.
*
* Let {@code L = getLeapAdjustment(mjd)}.
* Let {@code B = 86400 + L - 1000}.
* Let {@code US = U - L * (U - B) / 1000}.
* Where the algorithm is applied while {@code U >= B}.
*
* @param utcInstant the UTC instant to convert, not null
* @return the converted instant, not null
* @throws DateTimeException if the valid range is exceeded
* @throws ArithmeticException if numeric overflow occurs
*/
public Instant convertToInstant(UtcInstant utcInstant) {
long mjd = utcInstant.getModifiedJulianDay();
long utcNanos = utcInstant.getNanoOfDay();
long epochDay = Math.subtractExact(mjd, OFFSET_MJD_EPOCH);
long epochSec = Math.multiplyExact(epochDay, SECS_PER_DAY);
int leapAdj = getLeapSecondAdjustment(mjd);
long startSlsNanos = (SECS_PER_DAY + leapAdj - 1000) * NANOS_PER_SECOND;
long slsNanos = utcNanos;
if (leapAdj != 0 && utcNanos >= startSlsNanos) {
slsNanos = utcNanos - leapAdj * (utcNanos - startSlsNanos) / 1000; // apply UTC-SLS mapping
}
return Instant.ofEpochSecond(epochSec + slsNanos / NANOS_PER_SECOND, slsNanos % NANOS_PER_SECOND);
}
/**
* Converts an {@code Instant} to a {@code UtcInstant}.
*
* Let {@code L = getLeapAdjustment(mjd)}.
* Let {@code B = 86400 + L - 1000}.
* Let {@code U = B + ((US - B) * 1000) / (1000 - L)}.
* Where the algorithm is applied while {@code US >= B}.
* (This algorithm has been tuned for integer arithmetic from the UTC-SLS specification.)
*
* @param instant the instant to convert, not null
* @return the converted UTC instant, not null
* @throws DateTimeException if the valid range is exceeded
* @throws ArithmeticException if numeric overflow occurs
*/
public UtcInstant convertToUtc(Instant instant) {
long epochDay = Math.floorDiv(instant.getEpochSecond(), SECS_PER_DAY);
long mjd = epochDay + OFFSET_MJD_EPOCH;
long slsNanos = Math.floorMod(instant.getEpochSecond(), SECS_PER_DAY) * NANOS_PER_SECOND + instant.getNano();
int leapAdj = getLeapSecondAdjustment(mjd);
long startSlsNanos = (SECS_PER_DAY + leapAdj - 1000) * NANOS_PER_SECOND;
long utcNanos = slsNanos;
if (leapAdj != 0 && slsNanos >= startSlsNanos) {
utcNanos = startSlsNanos + ((slsNanos - startSlsNanos) * 1000) / (1000 - leapAdj); // apply UTC-SLS mapping
}
return UtcInstant.ofModifiedJulianDay(mjd, utcNanos);
}
//-------------------------------------------------------------------------
/**
* Converts a {@code TaiInstant} to an {@code Instant}.
* > data_crossCheckGuava() {
List
> list = new ArrayList<>();
for (int i1 = 1; i1 < 5; i1++) {
for (int j1 = i1; j1 < 5; j1++) {
LocalDate date11 = LocalDate.of(2016, 1, i1);
LocalDate date12 = LocalDate.of(2016, 1, j1);
LocalDateRange extraRange1 = LocalDateRange.of(date11, date12);
Range