8187443: Forest Consolidation: Move files to unified layout

Reviewed-by: darcy, ihse
This commit is contained in:
Erik Joelsson 2017-09-12 19:03:39 +02:00
parent 270fe13182
commit 3789983e89
56923 changed files with 3 additions and 15727 deletions

View file

@ -0,0 +1,755 @@
/*
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* This file is available under and governed by the GNU General Public
* License version 2 only, as published by the Free Software Foundation.
* However, the following notice accompanied the original version of this
* file:
*
* Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of JSR-310 nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package java.time.chrono;
import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH;
import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR;
import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH;
import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR;
import static java.time.temporal.ChronoField.DAY_OF_MONTH;
import static java.time.temporal.ChronoField.DAY_OF_WEEK;
import static java.time.temporal.ChronoField.DAY_OF_YEAR;
import static java.time.temporal.ChronoField.EPOCH_DAY;
import static java.time.temporal.ChronoField.ERA;
import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
import static java.time.temporal.ChronoField.PROLEPTIC_MONTH;
import static java.time.temporal.ChronoField.YEAR;
import static java.time.temporal.ChronoField.YEAR_OF_ERA;
import static java.time.temporal.ChronoUnit.DAYS;
import static java.time.temporal.ChronoUnit.MONTHS;
import static java.time.temporal.ChronoUnit.WEEKS;
import static java.time.temporal.TemporalAdjusters.nextOrSame;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.time.DateTimeException;
import java.time.DayOfWeek;
import java.time.format.ResolverStyle;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAdjusters;
import java.time.temporal.TemporalField;
import java.time.temporal.ValueRange;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import sun.util.logging.PlatformLogger;
/**
* An abstract implementation of a calendar system, used to organize and identify dates.
* <p>
* The main date and time API is built on the ISO calendar system.
* The chronology operates behind the scenes to represent the general concept of a calendar system.
* <p>
* See {@link Chronology} for more details.
*
* @implSpec
* This class is separated from the {@code Chronology} interface so that the static methods
* are not inherited. While {@code Chronology} can be implemented directly, it is strongly
* recommended to extend this abstract class instead.
* <p>
* This class must be implemented with care to ensure other classes operate correctly.
* All implementations that can be instantiated must be final, immutable and thread-safe.
* Subclasses should be Serializable wherever possible.
*
* @since 1.8
*/
public abstract class AbstractChronology implements Chronology {
/**
* Map of available calendars by ID.
*/
private static final ConcurrentHashMap<String, Chronology> CHRONOS_BY_ID = new ConcurrentHashMap<>();
/**
* Map of available calendars by calendar type.
*/
private static final ConcurrentHashMap<String, Chronology> CHRONOS_BY_TYPE = new ConcurrentHashMap<>();
/**
* Register a Chronology by its ID and type for lookup by {@link #of(String)}.
* Chronologies must not be registered until they are completely constructed.
* Specifically, not in the constructor of Chronology.
*
* @param chrono the chronology to register; not null
* @return the already registered Chronology if any, may be null
*/
static Chronology registerChrono(Chronology chrono) {
return registerChrono(chrono, chrono.getId());
}
/**
* Register a Chronology by ID and type for lookup by {@link #of(String)}.
* Chronos must not be registered until they are completely constructed.
* Specifically, not in the constructor of Chronology.
*
* @param chrono the chronology to register; not null
* @param id the ID to register the chronology; not null
* @return the already registered Chronology if any, may be null
*/
static Chronology registerChrono(Chronology chrono, String id) {
Chronology prev = CHRONOS_BY_ID.putIfAbsent(id, chrono);
if (prev == null) {
String type = chrono.getCalendarType();
if (type != null) {
CHRONOS_BY_TYPE.putIfAbsent(type, chrono);
}
}
return prev;
}
/**
* Initialization of the maps from id and type to Chronology.
* The ServiceLoader is used to find and register any implementations
* of {@link java.time.chrono.AbstractChronology} found in the bootclass loader.
* The built-in chronologies are registered explicitly.
* Calendars configured via the Thread's context classloader are local
* to that thread and are ignored.
* <p>
* The initialization is done only once using the registration
* of the IsoChronology as the test and the final step.
* Multiple threads may perform the initialization concurrently.
* Only the first registration of each Chronology is retained by the
* ConcurrentHashMap.
* @return true if the cache was initialized
*/
private static boolean initCache() {
if (CHRONOS_BY_ID.get("ISO") == null) {
// Initialization is incomplete
// Register built-in Chronologies
registerChrono(HijrahChronology.INSTANCE);
registerChrono(JapaneseChronology.INSTANCE);
registerChrono(MinguoChronology.INSTANCE);
registerChrono(ThaiBuddhistChronology.INSTANCE);
// Register Chronologies from the ServiceLoader
@SuppressWarnings("rawtypes")
ServiceLoader<AbstractChronology> loader = ServiceLoader.load(AbstractChronology.class, null);
for (AbstractChronology chrono : loader) {
String id = chrono.getId();
if (id.equals("ISO") || registerChrono(chrono) != null) {
// Log the attempt to replace an existing Chronology
PlatformLogger logger = PlatformLogger.getLogger("java.time.chrono");
logger.warning("Ignoring duplicate Chronology, from ServiceLoader configuration " + id);
}
}
// finally, register IsoChronology to mark initialization is complete
registerChrono(IsoChronology.INSTANCE);
return true;
}
return false;
}
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code Chronology} from a locale.
* <p>
* See {@link Chronology#ofLocale(Locale)}.
*
* @param locale the locale to use to obtain the calendar system, not null
* @return the calendar system associated with the locale, not null
* @throws java.time.DateTimeException if the locale-specified calendar cannot be found
*/
static Chronology ofLocale(Locale locale) {
Objects.requireNonNull(locale, "locale");
String type = locale.getUnicodeLocaleType("ca");
if (type == null || "iso".equals(type) || "iso8601".equals(type)) {
return IsoChronology.INSTANCE;
}
// Not pre-defined; lookup by the type
do {
Chronology chrono = CHRONOS_BY_TYPE.get(type);
if (chrono != null) {
return chrono;
}
// If not found, do the initialization (once) and repeat the lookup
} while (initCache());
// Look for a Chronology using ServiceLoader of the Thread's ContextClassLoader
// Application provided Chronologies must not be cached
@SuppressWarnings("rawtypes")
ServiceLoader<Chronology> loader = ServiceLoader.load(Chronology.class);
for (Chronology chrono : loader) {
if (type.equals(chrono.getCalendarType())) {
return chrono;
}
}
throw new DateTimeException("Unknown calendar system: " + type);
}
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code Chronology} from a chronology ID or
* calendar system type.
* <p>
* See {@link Chronology#of(String)}.
*
* @param id the chronology ID or calendar system type, not null
* @return the chronology with the identifier requested, not null
* @throws java.time.DateTimeException if the chronology cannot be found
*/
static Chronology of(String id) {
Objects.requireNonNull(id, "id");
do {
Chronology chrono = of0(id);
if (chrono != null) {
return chrono;
}
// If not found, do the initialization (once) and repeat the lookup
} while (initCache());
// Look for a Chronology using ServiceLoader of the Thread's ContextClassLoader
// Application provided Chronologies must not be cached
@SuppressWarnings("rawtypes")
ServiceLoader<Chronology> loader = ServiceLoader.load(Chronology.class);
for (Chronology chrono : loader) {
if (id.equals(chrono.getId()) || id.equals(chrono.getCalendarType())) {
return chrono;
}
}
throw new DateTimeException("Unknown chronology: " + id);
}
/**
* Obtains an instance of {@code Chronology} from a chronology ID or
* calendar system type.
*
* @param id the chronology ID or calendar system type, not null
* @return the chronology with the identifier requested, or {@code null} if not found
*/
private static Chronology of0(String id) {
Chronology chrono = CHRONOS_BY_ID.get(id);
if (chrono == null) {
chrono = CHRONOS_BY_TYPE.get(id);
}
return chrono;
}
/**
* Returns the available chronologies.
* <p>
* Each returned {@code Chronology} is available for use in the system.
* The set of chronologies includes the system chronologies and
* any chronologies provided by the application via ServiceLoader
* configuration.
*
* @return the independent, modifiable set of the available chronology IDs, not null
*/
static Set<Chronology> getAvailableChronologies() {
initCache(); // force initialization
HashSet<Chronology> chronos = new HashSet<>(CHRONOS_BY_ID.values());
/// Add in Chronologies from the ServiceLoader configuration
@SuppressWarnings("rawtypes")
ServiceLoader<Chronology> loader = ServiceLoader.load(Chronology.class);
for (Chronology chrono : loader) {
chronos.add(chrono);
}
return chronos;
}
//-----------------------------------------------------------------------
/**
* Creates an instance.
*/
protected AbstractChronology() {
}
//-----------------------------------------------------------------------
/**
* Resolves parsed {@code ChronoField} values into a date during parsing.
* <p>
* Most {@code TemporalField} implementations are resolved using the
* resolve method on the field. By contrast, the {@code ChronoField} class
* defines fields that only have meaning relative to the chronology.
* As such, {@code ChronoField} date fields are resolved here in the
* context of a specific chronology.
* <p>
* {@code ChronoField} instances are resolved by this method, which may
* be overridden in subclasses.
* <ul>
* <li>{@code EPOCH_DAY} - If present, this is converted to a date and
* all other date fields are then cross-checked against the date.
* <li>{@code PROLEPTIC_MONTH} - If present, then it is split into the
* {@code YEAR} and {@code MONTH_OF_YEAR}. If the mode is strict or smart
* then the field is validated.
* <li>{@code YEAR_OF_ERA} and {@code ERA} - If both are present, then they
* are combined to form a {@code YEAR}. In lenient mode, the {@code YEAR_OF_ERA}
* range is not validated, in smart and strict mode it is. The {@code ERA} is
* validated for range in all three modes. If only the {@code YEAR_OF_ERA} is
* present, and the mode is smart or lenient, then the last available era
* is assumed. In strict mode, no era is assumed and the {@code YEAR_OF_ERA} is
* left untouched. If only the {@code ERA} is present, then it is left untouched.
* <li>{@code YEAR}, {@code MONTH_OF_YEAR} and {@code DAY_OF_MONTH} -
* If all three are present, then they are combined to form a date.
* In all three modes, the {@code YEAR} is validated.
* If the mode is smart or strict, then the month and day are validated.
* If the mode is lenient, then the date is combined in a manner equivalent to
* creating a date on the first day of the first month in the requested year,
* then adding the difference in months, then the difference in days.
* If the mode is smart, and the day-of-month is greater than the maximum for
* the year-month, then the day-of-month is adjusted to the last day-of-month.
* If the mode is strict, then the three fields must form a valid date.
* <li>{@code YEAR} and {@code DAY_OF_YEAR} -
* If both are present, then they are combined to form a date.
* In all three modes, the {@code YEAR} is validated.
* If the mode is lenient, then the date is combined in a manner equivalent to
* creating a date on the first day of the requested year, then adding
* the difference in days.
* If the mode is smart or strict, then the two fields must form a valid date.
* <li>{@code YEAR}, {@code MONTH_OF_YEAR}, {@code ALIGNED_WEEK_OF_MONTH} and
* {@code ALIGNED_DAY_OF_WEEK_IN_MONTH} -
* If all four are present, then they are combined to form a date.
* In all three modes, the {@code YEAR} is validated.
* If the mode is lenient, then the date is combined in a manner equivalent to
* creating a date on the first day of the first month in the requested year, then adding
* the difference in months, then the difference in weeks, then in days.
* If the mode is smart or strict, then the all four fields are validated to
* their outer ranges. The date is then combined in a manner equivalent to
* creating a date on the first day of the requested year and month, then adding
* the amount in weeks and days to reach their values. If the mode is strict,
* the date is additionally validated to check that the day and week adjustment
* did not change the month.
* <li>{@code YEAR}, {@code MONTH_OF_YEAR}, {@code ALIGNED_WEEK_OF_MONTH} and
* {@code DAY_OF_WEEK} - If all four are present, then they are combined to
* form a date. The approach is the same as described above for
* years, months and weeks in {@code ALIGNED_DAY_OF_WEEK_IN_MONTH}.
* The day-of-week is adjusted as the next or same matching day-of-week once
* the years, months and weeks have been handled.
* <li>{@code YEAR}, {@code ALIGNED_WEEK_OF_YEAR} and {@code ALIGNED_DAY_OF_WEEK_IN_YEAR} -
* If all three are present, then they are combined to form a date.
* In all three modes, the {@code YEAR} is validated.
* If the mode is lenient, then the date is combined in a manner equivalent to
* creating a date on the first day of the requested year, then adding
* the difference in weeks, then in days.
* If the mode is smart or strict, then the all three fields are validated to
* their outer ranges. The date is then combined in a manner equivalent to
* creating a date on the first day of the requested year, then adding
* the amount in weeks and days to reach their values. If the mode is strict,
* the date is additionally validated to check that the day and week adjustment
* did not change the year.
* <li>{@code YEAR}, {@code ALIGNED_WEEK_OF_YEAR} and {@code DAY_OF_WEEK} -
* If all three are present, then they are combined to form a date.
* The approach is the same as described above for years and weeks in
* {@code ALIGNED_DAY_OF_WEEK_IN_YEAR}. The day-of-week is adjusted as the
* next or same matching day-of-week once the years and weeks have been handled.
* </ul>
* <p>
* The default implementation is suitable for most calendar systems.
* If {@link java.time.temporal.ChronoField#YEAR_OF_ERA} is found without an {@link java.time.temporal.ChronoField#ERA}
* then the last era in {@link #eras()} is used.
* The implementation assumes a 7 day week, that the first day-of-month
* has the value 1, that first day-of-year has the value 1, and that the
* first of the month and year always exists.
*
* @param fieldValues the map of fields to values, which can be updated, not null
* @param resolverStyle the requested type of resolve, not null
* @return the resolved date, null if insufficient information to create a date
* @throws java.time.DateTimeException if the date cannot be resolved, typically
* because of a conflict in the input data
*/
@Override
public ChronoLocalDate resolveDate(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
// check epoch-day before inventing era
if (fieldValues.containsKey(EPOCH_DAY)) {
return dateEpochDay(fieldValues.remove(EPOCH_DAY));
}
// fix proleptic month before inventing era
resolveProlepticMonth(fieldValues, resolverStyle);
// invent era if necessary to resolve year-of-era
ChronoLocalDate resolved = resolveYearOfEra(fieldValues, resolverStyle);
if (resolved != null) {
return resolved;
}
// build date
if (fieldValues.containsKey(YEAR)) {
if (fieldValues.containsKey(MONTH_OF_YEAR)) {
if (fieldValues.containsKey(DAY_OF_MONTH)) {
return resolveYMD(fieldValues, resolverStyle);
}
if (fieldValues.containsKey(ALIGNED_WEEK_OF_MONTH)) {
if (fieldValues.containsKey(ALIGNED_DAY_OF_WEEK_IN_MONTH)) {
return resolveYMAA(fieldValues, resolverStyle);
}
if (fieldValues.containsKey(DAY_OF_WEEK)) {
return resolveYMAD(fieldValues, resolverStyle);
}
}
}
if (fieldValues.containsKey(DAY_OF_YEAR)) {
return resolveYD(fieldValues, resolverStyle);
}
if (fieldValues.containsKey(ALIGNED_WEEK_OF_YEAR)) {
if (fieldValues.containsKey(ALIGNED_DAY_OF_WEEK_IN_YEAR)) {
return resolveYAA(fieldValues, resolverStyle);
}
if (fieldValues.containsKey(DAY_OF_WEEK)) {
return resolveYAD(fieldValues, resolverStyle);
}
}
}
return null;
}
void resolveProlepticMonth(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
Long pMonth = fieldValues.remove(PROLEPTIC_MONTH);
if (pMonth != null) {
if (resolverStyle != ResolverStyle.LENIENT) {
PROLEPTIC_MONTH.checkValidValue(pMonth);
}
// first day-of-month is likely to be safest for setting proleptic-month
// cannot add to year zero, as not all chronologies have a year zero
ChronoLocalDate chronoDate = dateNow()
.with(DAY_OF_MONTH, 1).with(PROLEPTIC_MONTH, pMonth);
addFieldValue(fieldValues, MONTH_OF_YEAR, chronoDate.get(MONTH_OF_YEAR));
addFieldValue(fieldValues, YEAR, chronoDate.get(YEAR));
}
}
ChronoLocalDate resolveYearOfEra(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
Long yoeLong = fieldValues.remove(YEAR_OF_ERA);
if (yoeLong != null) {
Long eraLong = fieldValues.remove(ERA);
int yoe;
if (resolverStyle != ResolverStyle.LENIENT) {
yoe = range(YEAR_OF_ERA).checkValidIntValue(yoeLong, YEAR_OF_ERA);
} else {
yoe = Math.toIntExact(yoeLong);
}
if (eraLong != null) {
Era eraObj = eraOf(range(ERA).checkValidIntValue(eraLong, ERA));
addFieldValue(fieldValues, YEAR, prolepticYear(eraObj, yoe));
} else {
if (fieldValues.containsKey(YEAR)) {
int year = range(YEAR).checkValidIntValue(fieldValues.get(YEAR), YEAR);
ChronoLocalDate chronoDate = dateYearDay(year, 1);
addFieldValue(fieldValues, YEAR, prolepticYear(chronoDate.getEra(), yoe));
} else if (resolverStyle == ResolverStyle.STRICT) {
// do not invent era if strict
// reinstate the field removed earlier, no cross-check issues
fieldValues.put(YEAR_OF_ERA, yoeLong);
} else {
List<Era> eras = eras();
if (eras.isEmpty()) {
addFieldValue(fieldValues, YEAR, yoe);
} else {
Era eraObj = eras.get(eras.size() - 1);
addFieldValue(fieldValues, YEAR, prolepticYear(eraObj, yoe));
}
}
}
} else if (fieldValues.containsKey(ERA)) {
range(ERA).checkValidValue(fieldValues.get(ERA), ERA); // always validated
}
return null;
}
ChronoLocalDate resolveYMD(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
if (resolverStyle == ResolverStyle.LENIENT) {
long months = Math.subtractExact(fieldValues.remove(MONTH_OF_YEAR), 1);
long days = Math.subtractExact(fieldValues.remove(DAY_OF_MONTH), 1);
return date(y, 1, 1).plus(months, MONTHS).plus(days, DAYS);
}
int moy = range(MONTH_OF_YEAR).checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR), MONTH_OF_YEAR);
ValueRange domRange = range(DAY_OF_MONTH);
int dom = domRange.checkValidIntValue(fieldValues.remove(DAY_OF_MONTH), DAY_OF_MONTH);
if (resolverStyle == ResolverStyle.SMART) { // previous valid
try {
return date(y, moy, dom);
} catch (DateTimeException ex) {
return date(y, moy, 1).with(TemporalAdjusters.lastDayOfMonth());
}
}
return date(y, moy, dom);
}
ChronoLocalDate resolveYD(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
if (resolverStyle == ResolverStyle.LENIENT) {
long days = Math.subtractExact(fieldValues.remove(DAY_OF_YEAR), 1);
return dateYearDay(y, 1).plus(days, DAYS);
}
int doy = range(DAY_OF_YEAR).checkValidIntValue(fieldValues.remove(DAY_OF_YEAR), DAY_OF_YEAR);
return dateYearDay(y, doy); // smart is same as strict
}
ChronoLocalDate resolveYMAA(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
if (resolverStyle == ResolverStyle.LENIENT) {
long months = Math.subtractExact(fieldValues.remove(MONTH_OF_YEAR), 1);
long weeks = Math.subtractExact(fieldValues.remove(ALIGNED_WEEK_OF_MONTH), 1);
long days = Math.subtractExact(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_MONTH), 1);
return date(y, 1, 1).plus(months, MONTHS).plus(weeks, WEEKS).plus(days, DAYS);
}
int moy = range(MONTH_OF_YEAR).checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR), MONTH_OF_YEAR);
int aw = range(ALIGNED_WEEK_OF_MONTH).checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_MONTH), ALIGNED_WEEK_OF_MONTH);
int ad = range(ALIGNED_DAY_OF_WEEK_IN_MONTH).checkValidIntValue(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_MONTH), ALIGNED_DAY_OF_WEEK_IN_MONTH);
ChronoLocalDate date = date(y, moy, 1).plus((aw - 1) * 7 + (ad - 1), DAYS);
if (resolverStyle == ResolverStyle.STRICT && date.get(MONTH_OF_YEAR) != moy) {
throw new DateTimeException("Strict mode rejected resolved date as it is in a different month");
}
return date;
}
ChronoLocalDate resolveYMAD(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
if (resolverStyle == ResolverStyle.LENIENT) {
long months = Math.subtractExact(fieldValues.remove(MONTH_OF_YEAR), 1);
long weeks = Math.subtractExact(fieldValues.remove(ALIGNED_WEEK_OF_MONTH), 1);
long dow = Math.subtractExact(fieldValues.remove(DAY_OF_WEEK), 1);
return resolveAligned(date(y, 1, 1), months, weeks, dow);
}
int moy = range(MONTH_OF_YEAR).checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR), MONTH_OF_YEAR);
int aw = range(ALIGNED_WEEK_OF_MONTH).checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_MONTH), ALIGNED_WEEK_OF_MONTH);
int dow = range(DAY_OF_WEEK).checkValidIntValue(fieldValues.remove(DAY_OF_WEEK), DAY_OF_WEEK);
ChronoLocalDate date = date(y, moy, 1).plus((aw - 1) * 7, DAYS).with(nextOrSame(DayOfWeek.of(dow)));
if (resolverStyle == ResolverStyle.STRICT && date.get(MONTH_OF_YEAR) != moy) {
throw new DateTimeException("Strict mode rejected resolved date as it is in a different month");
}
return date;
}
ChronoLocalDate resolveYAA(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
if (resolverStyle == ResolverStyle.LENIENT) {
long weeks = Math.subtractExact(fieldValues.remove(ALIGNED_WEEK_OF_YEAR), 1);
long days = Math.subtractExact(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_YEAR), 1);
return dateYearDay(y, 1).plus(weeks, WEEKS).plus(days, DAYS);
}
int aw = range(ALIGNED_WEEK_OF_YEAR).checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_YEAR), ALIGNED_WEEK_OF_YEAR);
int ad = range(ALIGNED_DAY_OF_WEEK_IN_YEAR).checkValidIntValue(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_YEAR), ALIGNED_DAY_OF_WEEK_IN_YEAR);
ChronoLocalDate date = dateYearDay(y, 1).plus((aw - 1) * 7 + (ad - 1), DAYS);
if (resolverStyle == ResolverStyle.STRICT && date.get(YEAR) != y) {
throw new DateTimeException("Strict mode rejected resolved date as it is in a different year");
}
return date;
}
ChronoLocalDate resolveYAD(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
if (resolverStyle == ResolverStyle.LENIENT) {
long weeks = Math.subtractExact(fieldValues.remove(ALIGNED_WEEK_OF_YEAR), 1);
long dow = Math.subtractExact(fieldValues.remove(DAY_OF_WEEK), 1);
return resolveAligned(dateYearDay(y, 1), 0, weeks, dow);
}
int aw = range(ALIGNED_WEEK_OF_YEAR).checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_YEAR), ALIGNED_WEEK_OF_YEAR);
int dow = range(DAY_OF_WEEK).checkValidIntValue(fieldValues.remove(DAY_OF_WEEK), DAY_OF_WEEK);
ChronoLocalDate date = dateYearDay(y, 1).plus((aw - 1) * 7, DAYS).with(nextOrSame(DayOfWeek.of(dow)));
if (resolverStyle == ResolverStyle.STRICT && date.get(YEAR) != y) {
throw new DateTimeException("Strict mode rejected resolved date as it is in a different year");
}
return date;
}
ChronoLocalDate resolveAligned(ChronoLocalDate base, long months, long weeks, long dow) {
ChronoLocalDate date = base.plus(months, MONTHS).plus(weeks, WEEKS);
if (dow > 7) {
date = date.plus((dow - 1) / 7, WEEKS);
dow = ((dow - 1) % 7) + 1;
} else if (dow < 1) {
date = date.plus(Math.subtractExact(dow, 7) / 7, WEEKS);
dow = ((dow + 6) % 7) + 1;
}
return date.with(nextOrSame(DayOfWeek.of((int) dow)));
}
/**
* Adds a field-value pair to the map, checking for conflicts.
* <p>
* If the field is not already present, then the field-value pair is added to the map.
* If the field is already present and it has the same value as that specified, no action occurs.
* If the field is already present and it has a different value to that specified, then
* an exception is thrown.
*
* @param field the field to add, not null
* @param value the value to add, not null
* @throws java.time.DateTimeException if the field is already present with a different value
*/
void addFieldValue(Map<TemporalField, Long> fieldValues, ChronoField field, long value) {
Long old = fieldValues.get(field); // check first for better error message
if (old != null && old.longValue() != value) {
throw new DateTimeException("Conflict found: " + field + " " + old + " differs from " + field + " " + value);
}
fieldValues.put(field, value);
}
//-----------------------------------------------------------------------
/**
* Compares this chronology to another chronology.
* <p>
* The comparison order first by the chronology ID string, then by any
* additional information specific to the subclass.
* It is "consistent with equals", as defined by {@link Comparable}.
*
* @implSpec
* This implementation compares the chronology ID.
* Subclasses must compare any additional state that they store.
*
* @param other the other chronology to compare to, not null
* @return the comparator value, negative if less, positive if greater
*/
@Override
public int compareTo(Chronology other) {
return getId().compareTo(other.getId());
}
/**
* Checks if this chronology is equal to another chronology.
* <p>
* The comparison is based on the entire state of the object.
*
* @implSpec
* This implementation checks the type and calls
* {@link #compareTo(java.time.chrono.Chronology)}.
*
* @param obj the object to check, null returns false
* @return true if this is equal to the other chronology
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof AbstractChronology) {
return compareTo((AbstractChronology) obj) == 0;
}
return false;
}
/**
* A hash code for this chronology.
* <p>
* The hash code should be based on the entire state of the object.
*
* @implSpec
* This implementation is based on the chronology ID and class.
* Subclasses should add any additional state that they store.
*
* @return a suitable hash code
*/
@Override
public int hashCode() {
return getClass().hashCode() ^ getId().hashCode();
}
//-----------------------------------------------------------------------
/**
* Outputs this chronology as a {@code String}, using the chronology ID.
*
* @return a string representation of this chronology, not null
*/
@Override
public String toString() {
return getId();
}
//-----------------------------------------------------------------------
/**
* Writes the Chronology using a
* <a href="../../../serialized-form.html#java.time.chrono.Ser">dedicated serialized form</a>.
* <pre>
* out.writeByte(1); // identifies this as a Chronology
* out.writeUTF(getId());
* </pre>
*
* @return the instance of {@code Ser}, not null
*/
Object writeReplace() {
return new Ser(Ser.CHRONO_TYPE, this);
}
/**
* Defend against malicious streams.
*
* @param s the stream to read
* @throws java.io.InvalidObjectException always
*/
private void readObject(ObjectInputStream s) throws ObjectStreamException {
throw new InvalidObjectException("Deserialization via serialization delegate");
}
void writeExternal(DataOutput out) throws IOException {
out.writeUTF(getId());
}
static Chronology readExternal(DataInput in) throws IOException {
String id = in.readUTF();
return Chronology.of(id);
}
}

View file

@ -0,0 +1,802 @@
/*
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* This file is available under and governed by the GNU General Public
* License version 2 only, as published by the Free Software Foundation.
* However, the following notice accompanied the original version of this
* file:
*
* Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of JSR-310 nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package java.time.chrono;
import static java.time.temporal.ChronoField.EPOCH_DAY;
import static java.time.temporal.ChronoField.ERA;
import static java.time.temporal.ChronoField.YEAR;
import static java.time.temporal.ChronoUnit.DAYS;
import java.io.Serializable;
import java.time.DateTimeException;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalAdjuster;
import java.time.temporal.TemporalAmount;
import java.time.temporal.TemporalField;
import java.time.temporal.TemporalQueries;
import java.time.temporal.TemporalQuery;
import java.time.temporal.TemporalUnit;
import java.time.temporal.UnsupportedTemporalTypeException;
import java.util.Comparator;
import java.util.Objects;
/**
* A date without time-of-day or time-zone in an arbitrary chronology, intended
* for advanced globalization use cases.
* <p>
* <b>Most applications should declare method signatures, fields and variables
* as {@link LocalDate}, not this interface.</b>
* <p>
* A {@code ChronoLocalDate} is the abstract representation of a date where the
* {@code Chronology chronology}, or calendar system, is pluggable.
* The date is defined in terms of fields expressed by {@link TemporalField},
* where most common implementations are defined in {@link ChronoField}.
* The chronology defines how the calendar system operates and the meaning of
* the standard fields.
*
* <h3>When to use this interface</h3>
* The design of the API encourages the use of {@code LocalDate} rather than this
* interface, even in the case where the application needs to deal with multiple
* calendar systems.
* <p>
* This concept can seem surprising at first, as the natural way to globalize an
* application might initially appear to be to abstract the calendar system.
* However, as explored below, abstracting the calendar system is usually the wrong
* approach, resulting in logic errors and hard to find bugs.
* As such, it should be considered an application-wide architectural decision to choose
* to use this interface as opposed to {@code LocalDate}.
*
* <h3>Architectural issues to consider</h3>
* These are some of the points that must be considered before using this interface
* throughout an application.
* <p>
* 1) Applications using this interface, as opposed to using just {@code LocalDate},
* face a significantly higher probability of bugs. This is because the calendar system
* in use is not known at development time. A key cause of bugs is where the developer
* applies assumptions from their day-to-day knowledge of the ISO calendar system
* to code that is intended to deal with any arbitrary calendar system.
* The section below outlines how those assumptions can cause problems
* The primary mechanism for reducing this increased risk of bugs is a strong code review process.
* This should also be considered a extra cost in maintenance for the lifetime of the code.
* <p>
* 2) This interface does not enforce immutability of implementations.
* While the implementation notes indicate that all implementations must be immutable
* there is nothing in the code or type system to enforce this. Any method declared
* to accept a {@code ChronoLocalDate} could therefore be passed a poorly or
* maliciously written mutable implementation.
* <p>
* 3) Applications using this interface must consider the impact of eras.
* {@code LocalDate} shields users from the concept of eras, by ensuring that {@code getYear()}
* returns the proleptic year. That decision ensures that developers can think of
* {@code LocalDate} instances as consisting of three fields - year, month-of-year and day-of-month.
* By contrast, users of this interface must think of dates as consisting of four fields -
* era, year-of-era, month-of-year and day-of-month. The extra era field is frequently
* forgotten, yet it is of vital importance to dates in an arbitrary calendar system.
* For example, in the Japanese calendar system, the era represents the reign of an Emperor.
* Whenever one reign ends and another starts, the year-of-era is reset to one.
* <p>
* 4) The only agreed international standard for passing a date between two systems
* is the ISO-8601 standard which requires the ISO calendar system. Using this interface
* throughout the application will inevitably lead to the requirement to pass the date
* across a network or component boundary, requiring an application specific protocol or format.
* <p>
* 5) Long term persistence, such as a database, will almost always only accept dates in the
* ISO-8601 calendar system (or the related Julian-Gregorian). Passing around dates in other
* calendar systems increases the complications of interacting with persistence.
* <p>
* 6) Most of the time, passing a {@code ChronoLocalDate} throughout an application
* is unnecessary, as discussed in the last section below.
*
* <h3>False assumptions causing bugs in multi-calendar system code</h3>
* As indicated above, there are many issues to consider when try to use and manipulate a
* date in an arbitrary calendar system. These are some of the key issues.
* <p>
* Code that queries the day-of-month and assumes that the value will never be more than
* 31 is invalid. Some calendar systems have more than 31 days in some months.
* <p>
* Code that adds 12 months to a date and assumes that a year has been added is invalid.
* Some calendar systems have a different number of months, such as 13 in the Coptic or Ethiopic.
* <p>
* Code that adds one month to a date and assumes that the month-of-year value will increase
* by one or wrap to the next year is invalid. Some calendar systems have a variable number
* of months in a year, such as the Hebrew.
* <p>
* Code that adds one month, then adds a second one month and assumes that the day-of-month
* will remain close to its original value is invalid. Some calendar systems have a large difference
* between the length of the longest month and the length of the shortest month.
* For example, the Coptic or Ethiopic have 12 months of 30 days and 1 month of 5 days.
* <p>
* Code that adds seven days and assumes that a week has been added is invalid.
* Some calendar systems have weeks of other than seven days, such as the French Revolutionary.
* <p>
* Code that assumes that because the year of {@code date1} is greater than the year of {@code date2}
* then {@code date1} is after {@code date2} is invalid. This is invalid for all calendar systems
* when referring to the year-of-era, and especially untrue of the Japanese calendar system
* where the year-of-era restarts with the reign of every new Emperor.
* <p>
* Code that treats month-of-year one and day-of-month one as the start of the year is invalid.
* Not all calendar systems start the year when the month value is one.
* <p>
* In general, manipulating a date, and even querying a date, is wide open to bugs when the
* calendar system is unknown at development time. This is why it is essential that code using
* this interface is subjected to additional code reviews. It is also why an architectural
* decision to avoid this interface type is usually the correct one.
*
* <h3>Using LocalDate instead</h3>
* The primary alternative to using this interface throughout your application is as follows.
* <ul>
* <li>Declare all method signatures referring to dates in terms of {@code LocalDate}.
* <li>Either store the chronology (calendar system) in the user profile or lookup
* the chronology from the user locale
* <li>Convert the ISO {@code LocalDate} to and from the user's preferred calendar system during
* printing and parsing
* </ul>
* This approach treats the problem of globalized calendar systems as a localization issue
* and confines it to the UI layer. This approach is in keeping with other localization
* issues in the java platform.
* <p>
* As discussed above, performing calculations on a date where the rules of the calendar system
* are pluggable requires skill and is not recommended.
* Fortunately, the need to perform calculations on a date in an arbitrary calendar system
* is extremely rare. For example, it is highly unlikely that the business rules of a library
* book rental scheme will allow rentals to be for one month, where meaning of the month
* is dependent on the user's preferred calendar system.
* <p>
* A key use case for calculations on a date in an arbitrary calendar system is producing
* a month-by-month calendar for display and user interaction. Again, this is a UI issue,
* and use of this interface solely within a few methods of the UI layer may be justified.
* <p>
* In any other part of the system, where a date must be manipulated in a calendar system
* other than ISO, the use case will generally specify the calendar system to use.
* For example, an application may need to calculate the next Islamic or Hebrew holiday
* which may require manipulating the date.
* This kind of use case can be handled as follows:
* <ul>
* <li>start from the ISO {@code LocalDate} being passed to the method
* <li>convert the date to the alternate calendar system, which for this use case is known
* rather than arbitrary
* <li>perform the calculation
* <li>convert back to {@code LocalDate}
* </ul>
* Developers writing low-level frameworks or libraries should also avoid this interface.
* Instead, one of the two general purpose access interfaces should be used.
* Use {@link TemporalAccessor} if read-only access is required, or use {@link Temporal}
* if read-write access is required.
*
* @implSpec
* This interface must be implemented with care to ensure other classes operate correctly.
* All implementations that can be instantiated must be final, immutable and thread-safe.
* Subclasses should be Serializable wherever possible.
* <p>
* Additional calendar systems may be added to the system.
* See {@link Chronology} for more details.
*
* @since 1.8
*/
public interface ChronoLocalDate
extends Temporal, TemporalAdjuster, Comparable<ChronoLocalDate> {
/**
* Gets a comparator that compares {@code ChronoLocalDate} in
* time-line order ignoring the chronology.
* <p>
* This comparator differs from the comparison in {@link #compareTo} in that it
* only compares the underlying date and not the chronology.
* This allows dates in different calendar systems to be compared based
* on the position of the date on the local time-line.
* The underlying comparison is equivalent to comparing the epoch-day.
*
* @return a comparator that compares in time-line order ignoring the chronology
* @see #isAfter
* @see #isBefore
* @see #isEqual
*/
static Comparator<ChronoLocalDate> timeLineOrder() {
return (Comparator<ChronoLocalDate> & Serializable) (date1, date2) -> {
return Long.compare(date1.toEpochDay(), date2.toEpochDay());
};
}
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code ChronoLocalDate} from a temporal object.
* <p>
* This obtains a local date based on the specified temporal.
* A {@code TemporalAccessor} represents an arbitrary set of date and time information,
* which this factory converts to an instance of {@code ChronoLocalDate}.
* <p>
* The conversion extracts and combines the chronology and the date
* from the temporal object. The behavior is equivalent to using
* {@link Chronology#date(TemporalAccessor)} with the extracted chronology.
* Implementations are permitted to perform optimizations such as accessing
* those fields that are equivalent to the relevant objects.
* <p>
* This method matches the signature of the functional interface {@link TemporalQuery}
* allowing it to be used as a query via method reference, {@code ChronoLocalDate::from}.
*
* @param temporal the temporal object to convert, not null
* @return the date, not null
* @throws DateTimeException if unable to convert to a {@code ChronoLocalDate}
* @see Chronology#date(TemporalAccessor)
*/
static ChronoLocalDate from(TemporalAccessor temporal) {
if (temporal instanceof ChronoLocalDate) {
return (ChronoLocalDate) temporal;
}
Objects.requireNonNull(temporal, "temporal");
Chronology chrono = temporal.query(TemporalQueries.chronology());
if (chrono == null) {
throw new DateTimeException("Unable to obtain ChronoLocalDate from TemporalAccessor: " + temporal.getClass());
}
return chrono.date(temporal);
}
//-----------------------------------------------------------------------
/**
* Gets the chronology of this date.
* <p>
* The {@code Chronology} represents the calendar system in use.
* The era and other fields in {@link ChronoField} are defined by the chronology.
*
* @return the chronology, not null
*/
Chronology getChronology();
/**
* Gets the era, as defined by the chronology.
* <p>
* The era is, conceptually, the largest division of the time-line.
* Most calendar systems have a single epoch dividing the time-line into two eras.
* However, some have multiple eras, such as one for the reign of each leader.
* The exact meaning is determined by the {@code Chronology}.
* <p>
* All correctly implemented {@code Era} classes are singletons, thus it
* is valid code to write {@code date.getEra() == SomeChrono.ERA_NAME)}.
* <p>
* This default implementation uses {@link Chronology#eraOf(int)}.
*
* @return the chronology specific era constant applicable at this date, not null
*/
default Era getEra() {
return getChronology().eraOf(get(ERA));
}
/**
* Checks if the year is a leap year, as defined by the calendar system.
* <p>
* A leap-year is a year of a longer length than normal.
* The exact meaning is determined by the chronology with the constraint that
* a leap-year must imply a year-length longer than a non leap-year.
* <p>
* This default implementation uses {@link Chronology#isLeapYear(long)}.
*
* @return true if this date is in a leap year, false otherwise
*/
default boolean isLeapYear() {
return getChronology().isLeapYear(getLong(YEAR));
}
/**
* Returns the length of the month represented by this date, as defined by the calendar system.
* <p>
* This returns the length of the month in days.
*
* @return the length of the month in days
*/
int lengthOfMonth();
/**
* Returns the length of the year represented by this date, as defined by the calendar system.
* <p>
* This returns the length of the year in days.
* <p>
* The default implementation uses {@link #isLeapYear()} and returns 365 or 366.
*
* @return the length of the year in days
*/
default int lengthOfYear() {
return (isLeapYear() ? 366 : 365);
}
/**
* Checks if the specified field is supported.
* <p>
* This checks if the specified field can be queried on this date.
* If false, then calling the {@link #range(TemporalField) range},
* {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}
* methods will throw an exception.
* <p>
* The set of supported fields is defined by the chronology and normally includes
* all {@code ChronoField} date fields.
* <p>
* If the field is not a {@code ChronoField}, then the result of this method
* is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}
* passing {@code this} as the argument.
* Whether the field is supported is determined by the field.
*
* @param field the field to check, null returns false
* @return true if the field can be queried, false if not
*/
@Override
default boolean isSupported(TemporalField field) {
if (field instanceof ChronoField) {
return field.isDateBased();
}
return field != null && field.isSupportedBy(this);
}
/**
* Checks if the specified unit is supported.
* <p>
* This checks if the specified unit can be added to or subtracted from this date.
* If false, then calling the {@link #plus(long, TemporalUnit)} and
* {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
* <p>
* The set of supported units is defined by the chronology and normally includes
* all {@code ChronoUnit} date units except {@code FOREVER}.
* <p>
* If the unit is not a {@code ChronoUnit}, then the result of this method
* is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
* passing {@code this} as the argument.
* Whether the unit is supported is determined by the unit.
*
* @param unit the unit to check, null returns false
* @return true if the unit can be added/subtracted, false if not
*/
@Override
default boolean isSupported(TemporalUnit unit) {
if (unit instanceof ChronoUnit) {
return unit.isDateBased();
}
return unit != null && unit.isSupportedBy(this);
}
//-----------------------------------------------------------------------
// override for covariant return type
/**
* {@inheritDoc}
* @throws DateTimeException {@inheritDoc}
* @throws ArithmeticException {@inheritDoc}
*/
@Override
default ChronoLocalDate with(TemporalAdjuster adjuster) {
return ChronoLocalDateImpl.ensureValid(getChronology(), Temporal.super.with(adjuster));
}
/**
* {@inheritDoc}
* @throws DateTimeException {@inheritDoc}
* @throws UnsupportedTemporalTypeException {@inheritDoc}
* @throws ArithmeticException {@inheritDoc}
*/
@Override
default ChronoLocalDate with(TemporalField field, long newValue) {
if (field instanceof ChronoField) {
throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
return ChronoLocalDateImpl.ensureValid(getChronology(), field.adjustInto(this, newValue));
}
/**
* {@inheritDoc}
* @throws DateTimeException {@inheritDoc}
* @throws ArithmeticException {@inheritDoc}
*/
@Override
default ChronoLocalDate plus(TemporalAmount amount) {
return ChronoLocalDateImpl.ensureValid(getChronology(), Temporal.super.plus(amount));
}
/**
* {@inheritDoc}
* @throws DateTimeException {@inheritDoc}
* @throws ArithmeticException {@inheritDoc}
*/
@Override
default ChronoLocalDate plus(long amountToAdd, TemporalUnit unit) {
if (unit instanceof ChronoUnit) {
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
}
return ChronoLocalDateImpl.ensureValid(getChronology(), unit.addTo(this, amountToAdd));
}
/**
* {@inheritDoc}
* @throws DateTimeException {@inheritDoc}
* @throws ArithmeticException {@inheritDoc}
*/
@Override
default ChronoLocalDate minus(TemporalAmount amount) {
return ChronoLocalDateImpl.ensureValid(getChronology(), Temporal.super.minus(amount));
}
/**
* {@inheritDoc}
* @throws DateTimeException {@inheritDoc}
* @throws UnsupportedTemporalTypeException {@inheritDoc}
* @throws ArithmeticException {@inheritDoc}
*/
@Override
default ChronoLocalDate minus(long amountToSubtract, TemporalUnit unit) {
return ChronoLocalDateImpl.ensureValid(getChronology(), Temporal.super.minus(amountToSubtract, unit));
}
//-----------------------------------------------------------------------
/**
* Queries this date using the specified query.
* <p>
* This queries this date using the specified query strategy object.
* The {@code TemporalQuery} object defines the logic to be used to
* obtain the result. Read the documentation of the query to understand
* what the result of this method will be.
* <p>
* The result of this method is obtained by invoking the
* {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the
* specified query passing {@code this} as the argument.
*
* @param <R> the type of the result
* @param query the query to invoke, not null
* @return the query result, null may be returned (defined by the query)
* @throws DateTimeException if unable to query (defined by the query)
* @throws ArithmeticException if numeric overflow occurs (defined by the query)
*/
@SuppressWarnings("unchecked")
@Override
default <R> R query(TemporalQuery<R> query) {
if (query == TemporalQueries.zoneId() || query == TemporalQueries.zone() || query == TemporalQueries.offset()) {
return null;
} else if (query == TemporalQueries.localTime()) {
return null;
} else if (query == TemporalQueries.chronology()) {
return (R) getChronology();
} else if (query == TemporalQueries.precision()) {
return (R) DAYS;
}
// inline TemporalAccessor.super.query(query) as an optimization
// non-JDK classes are not permitted to make this optimization
return query.queryFrom(this);
}
/**
* Adjusts the specified temporal object to have the same date as this object.
* <p>
* This returns a temporal object of the same observable type as the input
* with the date changed to be the same as this.
* <p>
* The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)}
* passing {@link ChronoField#EPOCH_DAY} as the field.
* <p>
* In most cases, it is clearer to reverse the calling pattern by using
* {@link Temporal#with(TemporalAdjuster)}:
* <pre>
* // these two lines are equivalent, but the second approach is recommended
* temporal = thisLocalDate.adjustInto(temporal);
* temporal = temporal.with(thisLocalDate);
* </pre>
* <p>
* This instance is immutable and unaffected by this method call.
*
* @param temporal the target object to be adjusted, not null
* @return the adjusted object, not null
* @throws DateTimeException if unable to make the adjustment
* @throws ArithmeticException if numeric overflow occurs
*/
@Override
default Temporal adjustInto(Temporal temporal) {
return temporal.with(EPOCH_DAY, toEpochDay());
}
/**
* Calculates the amount of time until another date in terms of the specified unit.
* <p>
* This calculates the amount of time between two {@code ChronoLocalDate}
* objects in terms of a single {@code TemporalUnit}.
* The start and end points are {@code this} and the specified date.
* The result will be negative if the end is before the start.
* The {@code Temporal} passed to this method is converted to a
* {@code ChronoLocalDate} using {@link Chronology#date(TemporalAccessor)}.
* The calculation returns a whole number, representing the number of
* complete units between the two dates.
* For example, the amount in days between two dates can be calculated
* using {@code startDate.until(endDate, DAYS)}.
* <p>
* There are two equivalent ways of using this method.
* The first is to invoke this method.
* The second is to use {@link TemporalUnit#between(Temporal, Temporal)}:
* <pre>
* // these two lines are equivalent
* amount = start.until(end, MONTHS);
* amount = MONTHS.between(start, end);
* </pre>
* The choice should be made based on which makes the code more readable.
* <p>
* The calculation is implemented in this method for {@link ChronoUnit}.
* The units {@code DAYS}, {@code WEEKS}, {@code MONTHS}, {@code YEARS},
* {@code DECADES}, {@code CENTURIES}, {@code MILLENNIA} and {@code ERAS}
* should be supported by all implementations.
* Other {@code ChronoUnit} values will throw an exception.
* <p>
* If the unit is not a {@code ChronoUnit}, then the result of this method
* is obtained by invoking {@code TemporalUnit.between(Temporal, Temporal)}
* passing {@code this} as the first argument and the converted input temporal as
* the second argument.
* <p>
* This instance is immutable and unaffected by this method call.
*
* @param endExclusive the end date, exclusive, which is converted to a
* {@code ChronoLocalDate} in the same chronology, not null
* @param unit the unit to measure the amount in, not null
* @return the amount of time between this date and the end date
* @throws DateTimeException if the amount cannot be calculated, or the end
* temporal cannot be converted to a {@code ChronoLocalDate}
* @throws UnsupportedTemporalTypeException if the unit is not supported
* @throws ArithmeticException if numeric overflow occurs
*/
@Override // override for Javadoc
long until(Temporal endExclusive, TemporalUnit unit);
/**
* Calculates the period between this date and another date as a {@code ChronoPeriod}.
* <p>
* This calculates the period between two dates. All supplied chronologies
* calculate the period using years, months and days, however the
* {@code ChronoPeriod} API allows the period to be represented using other units.
* <p>
* The start and end points are {@code this} and the specified date.
* The result will be negative if the end is before the start.
* The negative sign will be the same in each of year, month and day.
* <p>
* The calculation is performed using the chronology of this date.
* If necessary, the input date will be converted to match.
* <p>
* This instance is immutable and unaffected by this method call.
*
* @param endDateExclusive the end date, exclusive, which may be in any chronology, not null
* @return the period between this date and the end date, not null
* @throws DateTimeException if the period cannot be calculated
* @throws ArithmeticException if numeric overflow occurs
*/
ChronoPeriod until(ChronoLocalDate endDateExclusive);
/**
* Formats this date using the specified formatter.
* <p>
* This date will be passed to the formatter to produce a string.
* <p>
* The default implementation must behave as follows:
* <pre>
* return formatter.format(this);
* </pre>
*
* @param formatter the formatter to use, not null
* @return the formatted date string, not null
* @throws DateTimeException if an error occurs during printing
*/
default String format(DateTimeFormatter formatter) {
Objects.requireNonNull(formatter, "formatter");
return formatter.format(this);
}
//-----------------------------------------------------------------------
/**
* Combines this date with a time to create a {@code ChronoLocalDateTime}.
* <p>
* This returns a {@code ChronoLocalDateTime} formed from this date at the specified time.
* All possible combinations of date and time are valid.
*
* @param localTime the local time to use, not null
* @return the local date-time formed from this date and the specified time, not null
*/
@SuppressWarnings("unchecked")
default ChronoLocalDateTime<?> atTime(LocalTime localTime) {
return ChronoLocalDateTimeImpl.of(this, localTime);
}
//-----------------------------------------------------------------------
/**
* Converts this date to the Epoch Day.
* <p>
* The {@link ChronoField#EPOCH_DAY Epoch Day count} is a simple
* incrementing count of days where day 0 is 1970-01-01 (ISO).
* This definition is the same for all chronologies, enabling conversion.
* <p>
* This default implementation queries the {@code EPOCH_DAY} field.
*
* @return the Epoch Day equivalent to this date
*/
default long toEpochDay() {
return getLong(EPOCH_DAY);
}
//-----------------------------------------------------------------------
/**
* Compares this date to another date, including the chronology.
* <p>
* The comparison is based first on the underlying time-line date, then
* on the chronology.
* It is "consistent with equals", as defined by {@link Comparable}.
* <p>
* For example, the following is the comparator order:
* <ol>
* <li>{@code 2012-12-03 (ISO)}</li>
* <li>{@code 2012-12-04 (ISO)}</li>
* <li>{@code 2555-12-04 (ThaiBuddhist)}</li>
* <li>{@code 2012-12-05 (ISO)}</li>
* </ol>
* Values #2 and #3 represent the same date on the time-line.
* When two values represent the same date, the chronology ID is compared to distinguish them.
* This step is needed to make the ordering "consistent with equals".
* <p>
* If all the date objects being compared are in the same chronology, then the
* additional chronology stage is not required and only the local date is used.
* To compare the dates of two {@code TemporalAccessor} instances, including dates
* in two different chronologies, use {@link ChronoField#EPOCH_DAY} as a comparator.
* <p>
* This default implementation performs the comparison defined above.
*
* @param other the other date to compare to, not null
* @return the comparator value, negative if less, positive if greater
*/
@Override
default int compareTo(ChronoLocalDate other) {
int cmp = Long.compare(toEpochDay(), other.toEpochDay());
if (cmp == 0) {
cmp = getChronology().compareTo(other.getChronology());
}
return cmp;
}
/**
* Checks if this date is after the specified date ignoring the chronology.
* <p>
* This method differs from the comparison in {@link #compareTo} in that it
* only compares the underlying date and not the chronology.
* This allows dates in different calendar systems to be compared based
* on the time-line position.
* This is equivalent to using {@code date1.toEpochDay() > date2.toEpochDay()}.
* <p>
* This default implementation performs the comparison based on the epoch-day.
*
* @param other the other date to compare to, not null
* @return true if this is after the specified date
*/
default boolean isAfter(ChronoLocalDate other) {
return this.toEpochDay() > other.toEpochDay();
}
/**
* Checks if this date is before the specified date ignoring the chronology.
* <p>
* This method differs from the comparison in {@link #compareTo} in that it
* only compares the underlying date and not the chronology.
* This allows dates in different calendar systems to be compared based
* on the time-line position.
* This is equivalent to using {@code date1.toEpochDay() < date2.toEpochDay()}.
* <p>
* This default implementation performs the comparison based on the epoch-day.
*
* @param other the other date to compare to, not null
* @return true if this is before the specified date
*/
default boolean isBefore(ChronoLocalDate other) {
return this.toEpochDay() < other.toEpochDay();
}
/**
* Checks if this date is equal to the specified date ignoring the chronology.
* <p>
* This method differs from the comparison in {@link #compareTo} in that it
* only compares the underlying date and not the chronology.
* This allows dates in different calendar systems to be compared based
* on the time-line position.
* This is equivalent to using {@code date1.toEpochDay() == date2.toEpochDay()}.
* <p>
* This default implementation performs the comparison based on the epoch-day.
*
* @param other the other date to compare to, not null
* @return true if the underlying date is equal to the specified date
*/
default boolean isEqual(ChronoLocalDate other) {
return this.toEpochDay() == other.toEpochDay();
}
//-----------------------------------------------------------------------
/**
* Checks if this date is equal to another date, including the chronology.
* <p>
* Compares this date with another ensuring that the date and chronology are the same.
* <p>
* To compare the dates of two {@code TemporalAccessor} instances, including dates
* in two different chronologies, use {@link ChronoField#EPOCH_DAY} as a comparator.
*
* @param obj the object to check, null returns false
* @return true if this is equal to the other date
*/
@Override
boolean equals(Object obj);
/**
* A hash code for this date.
*
* @return a suitable hash code
*/
@Override
int hashCode();
//-----------------------------------------------------------------------
/**
* Outputs this date as a {@code String}.
* <p>
* The output will include the full local date.
*
* @return the formatted date, not null
*/
@Override
String toString();
}

View file

@ -0,0 +1,444 @@
/*
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of JSR-310 nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package java.time.chrono;
import static java.time.temporal.ChronoField.DAY_OF_MONTH;
import static java.time.temporal.ChronoField.ERA;
import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
import static java.time.temporal.ChronoField.PROLEPTIC_MONTH;
import static java.time.temporal.ChronoField.YEAR_OF_ERA;
import java.io.Serializable;
import java.time.DateTimeException;
import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAdjuster;
import java.time.temporal.TemporalAmount;
import java.time.temporal.TemporalField;
import java.time.temporal.TemporalUnit;
import java.time.temporal.UnsupportedTemporalTypeException;
import java.time.temporal.ValueRange;
import java.util.Objects;
/**
* A date expressed in terms of a standard year-month-day calendar system.
* <p>
* This class is used by applications seeking to handle dates in non-ISO calendar systems.
* For example, the Japanese, Minguo, Thai Buddhist and others.
* <p>
* {@code ChronoLocalDate} is built on the generic concepts of year, month and day.
* The calendar system, represented by a {@link java.time.chrono.Chronology}, expresses the relationship between
* the fields and this class allows the resulting date to be manipulated.
* <p>
* Note that not all calendar systems are suitable for use with this class.
* For example, the Mayan calendar uses a system that bears no relation to years, months and days.
* <p>
* The API design encourages the use of {@code LocalDate} for the majority of the application.
* This includes code to read and write from a persistent data store, such as a database,
* and to send dates and times across a network. The {@code ChronoLocalDate} instance is then used
* at the user interface level to deal with localized input/output.
*
* <P>Example: </p>
* <pre>
* System.out.printf("Example()%n");
* // Enumerate the list of available calendars and print today for each
* Set&lt;Chronology&gt; chronos = Chronology.getAvailableChronologies();
* for (Chronology chrono : chronos) {
* ChronoLocalDate date = chrono.dateNow();
* System.out.printf(" %20s: %s%n", chrono.getID(), date.toString());
* }
*
* // Print the Hijrah date and calendar
* ChronoLocalDate date = Chronology.of("Hijrah").dateNow();
* int day = date.get(ChronoField.DAY_OF_MONTH);
* int dow = date.get(ChronoField.DAY_OF_WEEK);
* int month = date.get(ChronoField.MONTH_OF_YEAR);
* int year = date.get(ChronoField.YEAR);
* System.out.printf(" Today is %s %s %d-%s-%d%n", date.getChronology().getID(),
* dow, day, month, year);
* // Print today's date and the last day of the year
* ChronoLocalDate now1 = Chronology.of("Hijrah").dateNow();
* ChronoLocalDate first = now1.with(ChronoField.DAY_OF_MONTH, 1)
* .with(ChronoField.MONTH_OF_YEAR, 1);
* ChronoLocalDate last = first.plus(1, ChronoUnit.YEARS)
* .minus(1, ChronoUnit.DAYS);
* System.out.printf(" Today is %s: start: %s; end: %s%n", last.getChronology().getID(),
* first, last);
* </pre>
*
* <h3>Adding Calendars</h3>
* <p> The set of calendars is extensible by defining a subclass of {@link ChronoLocalDate}
* to represent a date instance and an implementation of {@code Chronology}
* to be the factory for the ChronoLocalDate subclass.
* </p>
* <p> To permit the discovery of the additional calendar types the implementation of
* {@code Chronology} must be registered as a Service implementing the {@code Chronology} interface
* in the {@code META-INF/Services} file as per the specification of {@link java.util.ServiceLoader}.
* The subclass must function according to the {@code Chronology} class description and must provide its
* {@link java.time.chrono.Chronology#getId() chronlogy ID} and {@link Chronology#getCalendarType() calendar type}. </p>
*
* @implSpec
* This abstract class must be implemented with care to ensure other classes operate correctly.
* All implementations that can be instantiated must be final, immutable and thread-safe.
* Subclasses should be Serializable wherever possible.
*
* @param <D> the ChronoLocalDate of this date-time
* @since 1.8
*/
abstract class ChronoLocalDateImpl<D extends ChronoLocalDate>
implements ChronoLocalDate, Temporal, TemporalAdjuster, Serializable {
/**
* Serialization version.
*/
private static final long serialVersionUID = 6282433883239719096L;
/**
* Casts the {@code Temporal} to {@code ChronoLocalDate} ensuring it bas the specified chronology.
*
* @param chrono the chronology to check for, not null
* @param temporal a date-time to cast, not null
* @return the date-time checked and cast to {@code ChronoLocalDate}, not null
* @throws ClassCastException if the date-time cannot be cast to ChronoLocalDate
* or the chronology is not equal this Chronology
*/
static <D extends ChronoLocalDate> D ensureValid(Chronology chrono, Temporal temporal) {
@SuppressWarnings("unchecked")
D other = (D) temporal;
if (chrono.equals(other.getChronology()) == false) {
throw new ClassCastException("Chronology mismatch, expected: " + chrono.getId() + ", actual: " + other.getChronology().getId());
}
return other;
}
//-----------------------------------------------------------------------
/**
* Creates an instance.
*/
ChronoLocalDateImpl() {
}
@Override
@SuppressWarnings("unchecked")
public D with(TemporalAdjuster adjuster) {
return (D) ChronoLocalDate.super.with(adjuster);
}
@Override
@SuppressWarnings("unchecked")
public D with(TemporalField field, long value) {
return (D) ChronoLocalDate.super.with(field, value);
}
//-----------------------------------------------------------------------
@Override
@SuppressWarnings("unchecked")
public D plus(TemporalAmount amount) {
return (D) ChronoLocalDate.super.plus(amount);
}
//-----------------------------------------------------------------------
@Override
@SuppressWarnings("unchecked")
public D plus(long amountToAdd, TemporalUnit unit) {
if (unit instanceof ChronoUnit) {
ChronoUnit f = (ChronoUnit) unit;
switch (f) {
case DAYS: return plusDays(amountToAdd);
case WEEKS: return plusDays(Math.multiplyExact(amountToAdd, 7));
case MONTHS: return plusMonths(amountToAdd);
case YEARS: return plusYears(amountToAdd);
case DECADES: return plusYears(Math.multiplyExact(amountToAdd, 10));
case CENTURIES: return plusYears(Math.multiplyExact(amountToAdd, 100));
case MILLENNIA: return plusYears(Math.multiplyExact(amountToAdd, 1000));
case ERAS: return with(ERA, Math.addExact(getLong(ERA), amountToAdd));
}
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
}
return (D) ChronoLocalDate.super.plus(amountToAdd, unit);
}
@Override
@SuppressWarnings("unchecked")
public D minus(TemporalAmount amount) {
return (D) ChronoLocalDate.super.minus(amount);
}
@Override
@SuppressWarnings("unchecked")
public D minus(long amountToSubtract, TemporalUnit unit) {
return (D) ChronoLocalDate.super.minus(amountToSubtract, unit);
}
//-----------------------------------------------------------------------
/**
* Returns a copy of this date with the specified number of years added.
* <p>
* This adds the specified period in years to the date.
* In some cases, adding years can cause the resulting date to become invalid.
* If this occurs, then other fields, typically the day-of-month, will be adjusted to ensure
* that the result is valid. Typically this will select the last valid day of the month.
* <p>
* This instance is immutable and unaffected by this method call.
*
* @param yearsToAdd the years to add, may be negative
* @return a date based on this one with the years added, not null
* @throws DateTimeException if the result exceeds the supported date range
*/
abstract D plusYears(long yearsToAdd);
/**
* Returns a copy of this date with the specified number of months added.
* <p>
* This adds the specified period in months to the date.
* In some cases, adding months can cause the resulting date to become invalid.
* If this occurs, then other fields, typically the day-of-month, will be adjusted to ensure
* that the result is valid. Typically this will select the last valid day of the month.
* <p>
* This instance is immutable and unaffected by this method call.
*
* @param monthsToAdd the months to add, may be negative
* @return a date based on this one with the months added, not null
* @throws DateTimeException if the result exceeds the supported date range
*/
abstract D plusMonths(long monthsToAdd);
/**
* Returns a copy of this date with the specified number of weeks added.
* <p>
* This adds the specified period in weeks to the date.
* In some cases, adding weeks can cause the resulting date to become invalid.
* If this occurs, then other fields will be adjusted to ensure that the result is valid.
* <p>
* The default implementation uses {@link #plusDays(long)} using a 7 day week.
* <p>
* This instance is immutable and unaffected by this method call.
*
* @param weeksToAdd the weeks to add, may be negative
* @return a date based on this one with the weeks added, not null
* @throws DateTimeException if the result exceeds the supported date range
*/
D plusWeeks(long weeksToAdd) {
return plusDays(Math.multiplyExact(weeksToAdd, 7));
}
/**
* Returns a copy of this date with the specified number of days added.
* <p>
* This adds the specified period in days to the date.
* <p>
* This instance is immutable and unaffected by this method call.
*
* @param daysToAdd the days to add, may be negative
* @return a date based on this one with the days added, not null
* @throws DateTimeException if the result exceeds the supported date range
*/
abstract D plusDays(long daysToAdd);
//-----------------------------------------------------------------------
/**
* Returns a copy of this date with the specified number of years subtracted.
* <p>
* This subtracts the specified period in years to the date.
* In some cases, subtracting years can cause the resulting date to become invalid.
* If this occurs, then other fields, typically the day-of-month, will be adjusted to ensure
* that the result is valid. Typically this will select the last valid day of the month.
* <p>
* The default implementation uses {@link #plusYears(long)}.
* <p>
* This instance is immutable and unaffected by this method call.
*
* @param yearsToSubtract the years to subtract, may be negative
* @return a date based on this one with the years subtracted, not null
* @throws DateTimeException if the result exceeds the supported date range
*/
@SuppressWarnings("unchecked")
D minusYears(long yearsToSubtract) {
return (yearsToSubtract == Long.MIN_VALUE ? ((ChronoLocalDateImpl<D>)plusYears(Long.MAX_VALUE)).plusYears(1) : plusYears(-yearsToSubtract));
}
/**
* Returns a copy of this date with the specified number of months subtracted.
* <p>
* This subtracts the specified period in months to the date.
* In some cases, subtracting months can cause the resulting date to become invalid.
* If this occurs, then other fields, typically the day-of-month, will be adjusted to ensure
* that the result is valid. Typically this will select the last valid day of the month.
* <p>
* The default implementation uses {@link #plusMonths(long)}.
* <p>
* This instance is immutable and unaffected by this method call.
*
* @param monthsToSubtract the months to subtract, may be negative
* @return a date based on this one with the months subtracted, not null
* @throws DateTimeException if the result exceeds the supported date range
*/
@SuppressWarnings("unchecked")
D minusMonths(long monthsToSubtract) {
return (monthsToSubtract == Long.MIN_VALUE ? ((ChronoLocalDateImpl<D>)plusMonths(Long.MAX_VALUE)).plusMonths(1) : plusMonths(-monthsToSubtract));
}
/**
* Returns a copy of this date with the specified number of weeks subtracted.
* <p>
* This subtracts the specified period in weeks to the date.
* In some cases, subtracting weeks can cause the resulting date to become invalid.
* If this occurs, then other fields will be adjusted to ensure that the result is valid.
* <p>
* The default implementation uses {@link #plusWeeks(long)}.
* <p>
* This instance is immutable and unaffected by this method call.
*
* @param weeksToSubtract the weeks to subtract, may be negative
* @return a date based on this one with the weeks subtracted, not null
* @throws DateTimeException if the result exceeds the supported date range
*/
@SuppressWarnings("unchecked")
D minusWeeks(long weeksToSubtract) {
return (weeksToSubtract == Long.MIN_VALUE ? ((ChronoLocalDateImpl<D>)plusWeeks(Long.MAX_VALUE)).plusWeeks(1) : plusWeeks(-weeksToSubtract));
}
/**
* Returns a copy of this date with the specified number of days subtracted.
* <p>
* This subtracts the specified period in days to the date.
* <p>
* The default implementation uses {@link #plusDays(long)}.
* <p>
* This instance is immutable and unaffected by this method call.
*
* @param daysToSubtract the days to subtract, may be negative
* @return a date based on this one with the days subtracted, not null
* @throws DateTimeException if the result exceeds the supported date range
*/
@SuppressWarnings("unchecked")
D minusDays(long daysToSubtract) {
return (daysToSubtract == Long.MIN_VALUE ? ((ChronoLocalDateImpl<D>)plusDays(Long.MAX_VALUE)).plusDays(1) : plusDays(-daysToSubtract));
}
//-----------------------------------------------------------------------
@Override
public long until(Temporal endExclusive, TemporalUnit unit) {
Objects.requireNonNull(endExclusive, "endExclusive");
ChronoLocalDate end = getChronology().date(endExclusive);
if (unit instanceof ChronoUnit) {
switch ((ChronoUnit) unit) {
case DAYS: return daysUntil(end);
case WEEKS: return daysUntil(end) / 7;
case MONTHS: return monthsUntil(end);
case YEARS: return monthsUntil(end) / 12;
case DECADES: return monthsUntil(end) / 120;
case CENTURIES: return monthsUntil(end) / 1200;
case MILLENNIA: return monthsUntil(end) / 12000;
case ERAS: return end.getLong(ERA) - getLong(ERA);
}
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
}
Objects.requireNonNull(unit, "unit");
return unit.between(this, end);
}
private long daysUntil(ChronoLocalDate end) {
return end.toEpochDay() - toEpochDay(); // no overflow
}
private long monthsUntil(ChronoLocalDate end) {
ValueRange range = getChronology().range(MONTH_OF_YEAR);
if (range.getMaximum() != 12) {
throw new IllegalStateException("ChronoLocalDateImpl only supports Chronologies with 12 months per year");
}
long packed1 = getLong(PROLEPTIC_MONTH) * 32L + get(DAY_OF_MONTH); // no overflow
long packed2 = end.getLong(PROLEPTIC_MONTH) * 32L + end.get(DAY_OF_MONTH); // no overflow
return (packed2 - packed1) / 32;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof ChronoLocalDate) {
return compareTo((ChronoLocalDate) obj) == 0;
}
return false;
}
@Override
public int hashCode() {
long epDay = toEpochDay();
return getChronology().hashCode() ^ ((int) (epDay ^ (epDay >>> 32)));
}
@Override
public String toString() {
// getLong() reduces chances of exceptions in toString()
long yoe = getLong(YEAR_OF_ERA);
long moy = getLong(MONTH_OF_YEAR);
long dom = getLong(DAY_OF_MONTH);
StringBuilder buf = new StringBuilder(30);
buf.append(getChronology().toString())
.append(" ")
.append(getEra())
.append(" ")
.append(yoe)
.append(moy < 10 ? "-0" : "-").append(moy)
.append(dom < 10 ? "-0" : "-").append(dom);
return buf.toString();
}
}

View file

@ -0,0 +1,609 @@
/*
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* This file is available under and governed by the GNU General Public
* License version 2 only, as published by the Free Software Foundation.
* However, the following notice accompanied the original version of this
* file:
*
* Copyright (c) 2007-2012, Stephen Colebourne & Michael Nascimento Santos
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of JSR-310 nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package java.time.chrono;
import static java.time.temporal.ChronoField.EPOCH_DAY;
import static java.time.temporal.ChronoField.NANO_OF_DAY;
import static java.time.temporal.ChronoUnit.FOREVER;
import static java.time.temporal.ChronoUnit.NANOS;
import java.io.Serializable;
import java.time.DateTimeException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalAdjuster;
import java.time.temporal.TemporalAmount;
import java.time.temporal.TemporalField;
import java.time.temporal.TemporalQueries;
import java.time.temporal.TemporalQuery;
import java.time.temporal.TemporalUnit;
import java.time.zone.ZoneRules;
import java.util.Comparator;
import java.util.Objects;
/**
* A date-time without a time-zone in an arbitrary chronology, intended
* for advanced globalization use cases.
* <p>
* <b>Most applications should declare method signatures, fields and variables
* as {@link LocalDateTime}, not this interface.</b>
* <p>
* A {@code ChronoLocalDateTime} is the abstract representation of a local date-time
* where the {@code Chronology chronology}, or calendar system, is pluggable.
* The date-time is defined in terms of fields expressed by {@link TemporalField},
* where most common implementations are defined in {@link ChronoField}.
* The chronology defines how the calendar system operates and the meaning of
* the standard fields.
*
* <h3>When to use this interface</h3>
* The design of the API encourages the use of {@code LocalDateTime} rather than this
* interface, even in the case where the application needs to deal with multiple
* calendar systems. The rationale for this is explored in detail in {@link ChronoLocalDate}.
* <p>
* Ensure that the discussion in {@code ChronoLocalDate} has been read and understood
* before using this interface.
*
* @implSpec
* This interface must be implemented with care to ensure other classes operate correctly.
* All implementations that can be instantiated must be final, immutable and thread-safe.
* Subclasses should be Serializable wherever possible.
*
* @param <D> the concrete type for the date of this date-time
* @since 1.8
*/
public interface ChronoLocalDateTime<D extends ChronoLocalDate>
extends Temporal, TemporalAdjuster, Comparable<ChronoLocalDateTime<?>> {
/**
* Gets a comparator that compares {@code ChronoLocalDateTime} in
* time-line order ignoring the chronology.
* <p>
* This comparator differs from the comparison in {@link #compareTo} in that it
* only compares the underlying date-time and not the chronology.
* This allows dates in different calendar systems to be compared based
* on the position of the date-time on the local time-line.
* The underlying comparison is equivalent to comparing the epoch-day and nano-of-day.
*
* @return a comparator that compares in time-line order ignoring the chronology
* @see #isAfter
* @see #isBefore
* @see #isEqual
*/
static Comparator<ChronoLocalDateTime<?>> timeLineOrder() {
return (Comparator<ChronoLocalDateTime<? extends ChronoLocalDate>> & Serializable) (dateTime1, dateTime2) -> {
int cmp = Long.compare(dateTime1.toLocalDate().toEpochDay(), dateTime2.toLocalDate().toEpochDay());
if (cmp == 0) {
cmp = Long.compare(dateTime1.toLocalTime().toNanoOfDay(), dateTime2.toLocalTime().toNanoOfDay());
}
return cmp;
};
}
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code ChronoLocalDateTime} from a temporal object.
* <p>
* This obtains a local date-time based on the specified temporal.
* A {@code TemporalAccessor} represents an arbitrary set of date and time information,
* which this factory converts to an instance of {@code ChronoLocalDateTime}.
* <p>
* The conversion extracts and combines the chronology and the date-time
* from the temporal object. The behavior is equivalent to using
* {@link Chronology#localDateTime(TemporalAccessor)} with the extracted chronology.
* Implementations are permitted to perform optimizations such as accessing
* those fields that are equivalent to the relevant objects.
* <p>
* This method matches the signature of the functional interface {@link TemporalQuery}
* allowing it to be used as a query via method reference, {@code ChronoLocalDateTime::from}.
*
* @param temporal the temporal object to convert, not null
* @return the date-time, not null
* @throws DateTimeException if unable to convert to a {@code ChronoLocalDateTime}
* @see Chronology#localDateTime(TemporalAccessor)
*/
static ChronoLocalDateTime<?> from(TemporalAccessor temporal) {
if (temporal instanceof ChronoLocalDateTime) {
return (ChronoLocalDateTime<?>) temporal;
}
Objects.requireNonNull(temporal, "temporal");
Chronology chrono = temporal.query(TemporalQueries.chronology());
if (chrono == null) {
throw new DateTimeException("Unable to obtain ChronoLocalDateTime from TemporalAccessor: " + temporal.getClass());
}
return chrono.localDateTime(temporal);
}
//-----------------------------------------------------------------------
/**
* Gets the chronology of this date-time.
* <p>
* The {@code Chronology} represents the calendar system in use.
* The era and other fields in {@link ChronoField} are defined by the chronology.
*
* @return the chronology, not null
*/
default Chronology getChronology() {
return toLocalDate().getChronology();
}
/**
* Gets the local date part of this date-time.
* <p>
* This returns a local date with the same year, month and day
* as this date-time.
*
* @return the date part of this date-time, not null
*/
D toLocalDate() ;
/**
* Gets the local time part of this date-time.
* <p>
* This returns a local time with the same hour, minute, second and
* nanosecond as this date-time.
*
* @return the time part of this date-time, not null
*/
LocalTime toLocalTime();
/**
* Checks if the specified field is supported.
* <p>
* This checks if the specified field can be queried on this date-time.
* If false, then calling the {@link #range(TemporalField) range},
* {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}
* methods will throw an exception.
* <p>
* The set of supported fields is defined by the chronology and normally includes
* all {@code ChronoField} date and time fields.
* <p>
* If the field is not a {@code ChronoField}, then the result of this method
* is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}
* passing {@code this} as the argument.
* Whether the field is supported is determined by the field.
*
* @param field the field to check, null returns false
* @return true if the field can be queried, false if not
*/
@Override
boolean isSupported(TemporalField field);
/**
* Checks if the specified unit is supported.
* <p>
* This checks if the specified unit can be added to or subtracted from this date-time.
* If false, then calling the {@link #plus(long, TemporalUnit)} and
* {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
* <p>
* The set of supported units is defined by the chronology and normally includes
* all {@code ChronoUnit} units except {@code FOREVER}.
* <p>
* If the unit is not a {@code ChronoUnit}, then the result of this method
* is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
* passing {@code this} as the argument.
* Whether the unit is supported is determined by the unit.
*
* @param unit the unit to check, null returns false
* @return true if the unit can be added/subtracted, false if not
*/
@Override
default boolean isSupported(TemporalUnit unit) {
if (unit instanceof ChronoUnit) {
return unit != FOREVER;
}
return unit != null && unit.isSupportedBy(this);
}
//-----------------------------------------------------------------------
// override for covariant return type
/**
* {@inheritDoc}
* @throws DateTimeException {@inheritDoc}
* @throws ArithmeticException {@inheritDoc}
*/
@Override
default ChronoLocalDateTime<D> with(TemporalAdjuster adjuster) {
return ChronoLocalDateTimeImpl.ensureValid(getChronology(), Temporal.super.with(adjuster));
}
/**
* {@inheritDoc}
* @throws DateTimeException {@inheritDoc}
* @throws ArithmeticException {@inheritDoc}
*/
@Override
ChronoLocalDateTime<D> with(TemporalField field, long newValue);
/**
* {@inheritDoc}
* @throws DateTimeException {@inheritDoc}
* @throws ArithmeticException {@inheritDoc}
*/
@Override
default ChronoLocalDateTime<D> plus(TemporalAmount amount) {
return ChronoLocalDateTimeImpl.ensureValid(getChronology(), Temporal.super.plus(amount));
}
/**
* {@inheritDoc}
* @throws DateTimeException {@inheritDoc}
* @throws ArithmeticException {@inheritDoc}
*/
@Override
ChronoLocalDateTime<D> plus(long amountToAdd, TemporalUnit unit);
/**
* {@inheritDoc}
* @throws DateTimeException {@inheritDoc}
* @throws ArithmeticException {@inheritDoc}
*/
@Override
default ChronoLocalDateTime<D> minus(TemporalAmount amount) {
return ChronoLocalDateTimeImpl.ensureValid(getChronology(), Temporal.super.minus(amount));
}
/**
* {@inheritDoc}
* @throws DateTimeException {@inheritDoc}
* @throws ArithmeticException {@inheritDoc}
*/
@Override
default ChronoLocalDateTime<D> minus(long amountToSubtract, TemporalUnit unit) {
return ChronoLocalDateTimeImpl.ensureValid(getChronology(), Temporal.super.minus(amountToSubtract, unit));
}
//-----------------------------------------------------------------------
/**
* Queries this date-time using the specified query.
* <p>
* This queries this date-time using the specified query strategy object.
* The {@code TemporalQuery} object defines the logic to be used to
* obtain the result. Read the documentation of the query to understand
* what the result of this method will be.
* <p>
* The result of this method is obtained by invoking the
* {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the
* specified query passing {@code this} as the argument.
*
* @param <R> the type of the result
* @param query the query to invoke, not null
* @return the query result, null may be returned (defined by the query)
* @throws DateTimeException if unable to query (defined by the query)
* @throws ArithmeticException if numeric overflow occurs (defined by the query)
*/
@SuppressWarnings("unchecked")
@Override
default <R> R query(TemporalQuery<R> query) {
if (query == TemporalQueries.zoneId() || query == TemporalQueries.zone() || query == TemporalQueries.offset()) {
return null;
} else if (query == TemporalQueries.localTime()) {
return (R) toLocalTime();
} else if (query == TemporalQueries.chronology()) {
return (R) getChronology();
} else if (query == TemporalQueries.precision()) {
return (R) NANOS;
}
// inline TemporalAccessor.super.query(query) as an optimization
// non-JDK classes are not permitted to make this optimization
return query.queryFrom(this);
}
/**
* Adjusts the specified temporal object to have the same date and time as this object.
* <p>
* This returns a temporal object of the same observable type as the input
* with the date and time changed to be the same as this.
* <p>
* The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)}
* twice, passing {@link ChronoField#EPOCH_DAY} and
* {@link ChronoField#NANO_OF_DAY} as the fields.
* <p>
* In most cases, it is clearer to reverse the calling pattern by using
* {@link Temporal#with(TemporalAdjuster)}:
* <pre>
* // these two lines are equivalent, but the second approach is recommended
* temporal = thisLocalDateTime.adjustInto(temporal);
* temporal = temporal.with(thisLocalDateTime);
* </pre>
* <p>
* This instance is immutable and unaffected by this method call.
*
* @param temporal the target object to be adjusted, not null
* @return the adjusted object, not null
* @throws DateTimeException if unable to make the adjustment
* @throws ArithmeticException if numeric overflow occurs
*/
@Override
default Temporal adjustInto(Temporal temporal) {
return temporal
.with(EPOCH_DAY, toLocalDate().toEpochDay())
.with(NANO_OF_DAY, toLocalTime().toNanoOfDay());
}
/**
* Formats this date-time using the specified formatter.
* <p>
* This date-time will be passed to the formatter to produce a string.
* <p>
* The default implementation must behave as follows:
* <pre>
* return formatter.format(this);
* </pre>
*
* @param formatter the formatter to use, not null
* @return the formatted date-time string, not null
* @throws DateTimeException if an error occurs during printing
*/
default String format(DateTimeFormatter formatter) {
Objects.requireNonNull(formatter, "formatter");
return formatter.format(this);
}
//-----------------------------------------------------------------------
/**
* Combines this time with a time-zone to create a {@code ChronoZonedDateTime}.
* <p>
* This returns a {@code ChronoZonedDateTime} formed from this date-time at the
* specified time-zone. The result will match this date-time as closely as possible.
* Time-zone rules, such as daylight savings, mean that not every local date-time
* is valid for the specified zone, thus the local date-time may be adjusted.
* <p>
* The local date-time is resolved to a single instant on the time-line.
* This is achieved by finding a valid offset from UTC/Greenwich for the local
* date-time as defined by the {@link ZoneRules rules} of the zone ID.
*<p>
* In most cases, there is only one valid offset for a local date-time.
* In the case of an overlap, where clocks are set back, there are two valid offsets.
* This method uses the earlier offset typically corresponding to "summer".
* <p>
* In the case of a gap, where clocks jump forward, there is no valid offset.
* Instead, the local date-time is adjusted to be later by the length of the gap.
* For a typical one hour daylight savings change, the local date-time will be
* moved one hour later into the offset typically corresponding to "summer".
* <p>
* To obtain the later offset during an overlap, call
* {@link ChronoZonedDateTime#withLaterOffsetAtOverlap()} on the result of this method.
*
* @param zone the time-zone to use, not null
* @return the zoned date-time formed from this date-time, not null
*/
ChronoZonedDateTime<D> atZone(ZoneId zone);
//-----------------------------------------------------------------------
/**
* Converts this date-time to an {@code Instant}.
* <p>
* This combines this local date-time and the specified offset to form
* an {@code Instant}.
* <p>
* This default implementation calculates from the epoch-day of the date and the
* second-of-day of the time.
*
* @param offset the offset to use for the conversion, not null
* @return an {@code Instant} representing the same instant, not null
*/
default Instant toInstant(ZoneOffset offset) {
return Instant.ofEpochSecond(toEpochSecond(offset), toLocalTime().getNano());
}
/**
* Converts this date-time to the number of seconds from the epoch
* of 1970-01-01T00:00:00Z.
* <p>
* This combines this local date-time and the specified offset to calculate the
* epoch-second value, which is the number of elapsed seconds from 1970-01-01T00:00:00Z.
* Instants on the time-line after the epoch are positive, earlier are negative.
* <p>
* This default implementation calculates from the epoch-day of the date and the
* second-of-day of the time.
*
* @param offset the offset to use for the conversion, not null
* @return the number of seconds from the epoch of 1970-01-01T00:00:00Z
*/
default long toEpochSecond(ZoneOffset offset) {
Objects.requireNonNull(offset, "offset");
long epochDay = toLocalDate().toEpochDay();
long secs = epochDay * 86400 + toLocalTime().toSecondOfDay();
secs -= offset.getTotalSeconds();
return secs;
}
//-----------------------------------------------------------------------
/**
* Compares this date-time to another date-time, including the chronology.
* <p>
* The comparison is based first on the underlying time-line date-time, then
* on the chronology.
* It is "consistent with equals", as defined by {@link Comparable}.
* <p>
* For example, the following is the comparator order:
* <ol>
* <li>{@code 2012-12-03T12:00 (ISO)}</li>
* <li>{@code 2012-12-04T12:00 (ISO)}</li>
* <li>{@code 2555-12-04T12:00 (ThaiBuddhist)}</li>
* <li>{@code 2012-12-05T12:00 (ISO)}</li>
* </ol>
* Values #2 and #3 represent the same date-time on the time-line.
* When two values represent the same date-time, the chronology ID is compared to distinguish them.
* This step is needed to make the ordering "consistent with equals".
* <p>
* If all the date-time objects being compared are in the same chronology, then the
* additional chronology stage is not required and only the local date-time is used.
* <p>
* This default implementation performs the comparison defined above.
*
* @param other the other date-time to compare to, not null
* @return the comparator value, negative if less, positive if greater
*/
@Override
default int compareTo(ChronoLocalDateTime<?> other) {
int cmp = toLocalDate().compareTo(other.toLocalDate());
if (cmp == 0) {
cmp = toLocalTime().compareTo(other.toLocalTime());
if (cmp == 0) {
cmp = getChronology().compareTo(other.getChronology());
}
}
return cmp;
}
/**
* Checks if this date-time is after the specified date-time ignoring the chronology.
* <p>
* This method differs from the comparison in {@link #compareTo} in that it
* only compares the underlying date-time and not the chronology.
* This allows dates in different calendar systems to be compared based
* on the time-line position.
* <p>
* This default implementation performs the comparison based on the epoch-day
* and nano-of-day.
*
* @param other the other date-time to compare to, not null
* @return true if this is after the specified date-time
*/
default boolean isAfter(ChronoLocalDateTime<?> other) {
long thisEpDay = this.toLocalDate().toEpochDay();
long otherEpDay = other.toLocalDate().toEpochDay();
return thisEpDay > otherEpDay ||
(thisEpDay == otherEpDay && this.toLocalTime().toNanoOfDay() > other.toLocalTime().toNanoOfDay());
}
/**
* Checks if this date-time is before the specified date-time ignoring the chronology.
* <p>
* This method differs from the comparison in {@link #compareTo} in that it
* only compares the underlying date-time and not the chronology.
* This allows dates in different calendar systems to be compared based
* on the time-line position.
* <p>
* This default implementation performs the comparison based on the epoch-day
* and nano-of-day.
*
* @param other the other date-time to compare to, not null
* @return true if this is before the specified date-time
*/
default boolean isBefore(ChronoLocalDateTime<?> other) {
long thisEpDay = this.toLocalDate().toEpochDay();
long otherEpDay = other.toLocalDate().toEpochDay();
return thisEpDay < otherEpDay ||
(thisEpDay == otherEpDay && this.toLocalTime().toNanoOfDay() < other.toLocalTime().toNanoOfDay());
}
/**
* Checks if this date-time is equal to the specified date-time ignoring the chronology.
* <p>
* This method differs from the comparison in {@link #compareTo} in that it
* only compares the underlying date and time and not the chronology.
* This allows date-times in different calendar systems to be compared based
* on the time-line position.
* <p>
* This default implementation performs the comparison based on the epoch-day
* and nano-of-day.
*
* @param other the other date-time to compare to, not null
* @return true if the underlying date-time is equal to the specified date-time on the timeline
*/
default boolean isEqual(ChronoLocalDateTime<?> other) {
// Do the time check first, it is cheaper than computing EPOCH day.
return this.toLocalTime().toNanoOfDay() == other.toLocalTime().toNanoOfDay() &&
this.toLocalDate().toEpochDay() == other.toLocalDate().toEpochDay();
}
/**
* Checks if this date-time is equal to another date-time, including the chronology.
* <p>
* Compares this date-time with another ensuring that the date-time and chronology are the same.
*
* @param obj the object to check, null returns false
* @return true if this is equal to the other date
*/
@Override
boolean equals(Object obj);
/**
* A hash code for this date-time.
*
* @return a suitable hash code
*/
@Override
int hashCode();
//-----------------------------------------------------------------------
/**
* Outputs this date-time as a {@code String}.
* <p>
* The output will include the full local date-time.
*
* @return a string representation of this date-time, not null
*/
@Override
String toString();
}

View file

@ -0,0 +1,459 @@
/*
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* This file is available under and governed by the GNU General Public
* License version 2 only, as published by the Free Software Foundation.
* However, the following notice accompanied the original version of this
* file:
*
* Copyright (c) 2007-2012, Stephen Colebourne & Michael Nascimento Santos
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of JSR-310 nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package java.time.chrono;
import static java.time.temporal.ChronoField.EPOCH_DAY;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.Serializable;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAdjuster;
import java.time.temporal.TemporalField;
import java.time.temporal.TemporalUnit;
import java.time.temporal.ValueRange;
import java.util.Objects;
/**
* A date-time without a time-zone for the calendar neutral API.
* <p>
* {@code ChronoLocalDateTime} is an immutable date-time object that represents a date-time, often
* viewed as year-month-day-hour-minute-second. This object can also access other
* fields such as day-of-year, day-of-week and week-of-year.
* <p>
* This class stores all date and time fields, to a precision of nanoseconds.
* It does not store or represent a time-zone. For example, the value
* "2nd October 2007 at 13:45.30.123456789" can be stored in an {@code ChronoLocalDateTime}.
*
* @implSpec
* This class is immutable and thread-safe.
* @serial
* @param <D> the concrete type for the date of this date-time
* @since 1.8
*/
final class ChronoLocalDateTimeImpl<D extends ChronoLocalDate>
implements ChronoLocalDateTime<D>, Temporal, TemporalAdjuster, Serializable {
/**
* Serialization version.
*/
private static final long serialVersionUID = 4556003607393004514L;
/**
* Hours per day.
*/
static final int HOURS_PER_DAY = 24;
/**
* Minutes per hour.
*/
static final int MINUTES_PER_HOUR = 60;
/**
* Minutes per day.
*/
static final int MINUTES_PER_DAY = MINUTES_PER_HOUR * HOURS_PER_DAY;
/**
* Seconds per minute.
*/
static final int SECONDS_PER_MINUTE = 60;
/**
* Seconds per hour.
*/
static final int SECONDS_PER_HOUR = SECONDS_PER_MINUTE * MINUTES_PER_HOUR;
/**
* Seconds per day.
*/
static final int SECONDS_PER_DAY = SECONDS_PER_HOUR * HOURS_PER_DAY;
/**
* Milliseconds per day.
*/
static final long MILLIS_PER_DAY = SECONDS_PER_DAY * 1000L;
/**
* Microseconds per day.
*/
static final long MICROS_PER_DAY = SECONDS_PER_DAY * 1000_000L;
/**
* Nanos per second.
*/
static final long NANOS_PER_SECOND = 1000_000_000L;
/**
* Nanos per minute.
*/
static final long NANOS_PER_MINUTE = NANOS_PER_SECOND * SECONDS_PER_MINUTE;
/**
* Nanos per hour.
*/
static final long NANOS_PER_HOUR = NANOS_PER_MINUTE * MINUTES_PER_HOUR;
/**
* Nanos per day.
*/
static final long NANOS_PER_DAY = NANOS_PER_HOUR * HOURS_PER_DAY;
/**
* The date part.
*/
private final transient D date;
/**
* The time part.
*/
private final transient LocalTime time;
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code ChronoLocalDateTime} from a date and time.
*
* @param date the local date, not null
* @param time the local time, not null
* @return the local date-time, not null
*/
static <R extends ChronoLocalDate> ChronoLocalDateTimeImpl<R> of(R date, LocalTime time) {
return new ChronoLocalDateTimeImpl<>(date, time);
}
/**
* Casts the {@code Temporal} to {@code ChronoLocalDateTime} ensuring it bas the specified chronology.
*
* @param chrono the chronology to check for, not null
* @param temporal a date-time to cast, not null
* @return the date-time checked and cast to {@code ChronoLocalDateTime}, not null
* @throws ClassCastException if the date-time cannot be cast to ChronoLocalDateTimeImpl
* or the chronology is not equal this Chronology
*/
static <R extends ChronoLocalDate> ChronoLocalDateTimeImpl<R> ensureValid(Chronology chrono, Temporal temporal) {
@SuppressWarnings("unchecked")
ChronoLocalDateTimeImpl<R> other = (ChronoLocalDateTimeImpl<R>) temporal;
if (chrono.equals(other.getChronology()) == false) {
throw new ClassCastException("Chronology mismatch, required: " + chrono.getId()
+ ", actual: " + other.getChronology().getId());
}
return other;
}
/**
* Constructor.
*
* @param date the date part of the date-time, not null
* @param time the time part of the date-time, not null
*/
private ChronoLocalDateTimeImpl(D date, LocalTime time) {
Objects.requireNonNull(date, "date");
Objects.requireNonNull(time, "time");
this.date = date;
this.time = time;
}
/**
* Returns a copy of this date-time with the new date and time, checking
* to see if a new object is in fact required.
*
* @param newDate the date of the new date-time, not null
* @param newTime the time of the new date-time, not null
* @return the date-time, not null
*/
private ChronoLocalDateTimeImpl<D> with(Temporal newDate, LocalTime newTime) {
if (date == newDate && time == newTime) {
return this;
}
// Validate that the new Temporal is a ChronoLocalDate (and not something else)
D cd = ChronoLocalDateImpl.ensureValid(date.getChronology(), newDate);
return new ChronoLocalDateTimeImpl<>(cd, newTime);
}
//-----------------------------------------------------------------------
@Override
public D toLocalDate() {
return date;
}
@Override
public LocalTime toLocalTime() {
return time;
}
//-----------------------------------------------------------------------
@Override
public boolean isSupported(TemporalField field) {
if (field instanceof ChronoField) {
ChronoField f = (ChronoField) field;
return f.isDateBased() || f.isTimeBased();
}
return field != null && field.isSupportedBy(this);
}
@Override
public ValueRange range(TemporalField field) {
if (field instanceof ChronoField) {
ChronoField f = (ChronoField) field;
return (f.isTimeBased() ? time.range(field) : date.range(field));
}
return field.rangeRefinedBy(this);
}
@Override
public int get(TemporalField field) {
if (field instanceof ChronoField) {
ChronoField f = (ChronoField) field;
return (f.isTimeBased() ? time.get(field) : date.get(field));
}
return range(field).checkValidIntValue(getLong(field), field);
}
@Override
public long getLong(TemporalField field) {
if (field instanceof ChronoField) {
ChronoField f = (ChronoField) field;
return (f.isTimeBased() ? time.getLong(field) : date.getLong(field));
}
return field.getFrom(this);
}
//-----------------------------------------------------------------------
@SuppressWarnings("unchecked")
@Override
public ChronoLocalDateTimeImpl<D> with(TemporalAdjuster adjuster) {
if (adjuster instanceof ChronoLocalDate) {
// The Chronology is checked in with(date,time)
return with((ChronoLocalDate) adjuster, time);
} else if (adjuster instanceof LocalTime) {
return with(date, (LocalTime) adjuster);
} else if (adjuster instanceof ChronoLocalDateTimeImpl) {
return ChronoLocalDateTimeImpl.ensureValid(date.getChronology(), (ChronoLocalDateTimeImpl<?>) adjuster);
}
return ChronoLocalDateTimeImpl.ensureValid(date.getChronology(), (ChronoLocalDateTimeImpl<?>) adjuster.adjustInto(this));
}
@Override
public ChronoLocalDateTimeImpl<D> with(TemporalField field, long newValue) {
if (field instanceof ChronoField) {
ChronoField f = (ChronoField) field;
if (f.isTimeBased()) {
return with(date, time.with(field, newValue));
} else {
return with(date.with(field, newValue), time);
}
}
return ChronoLocalDateTimeImpl.ensureValid(date.getChronology(), field.adjustInto(this, newValue));
}
//-----------------------------------------------------------------------
@Override
public ChronoLocalDateTimeImpl<D> plus(long amountToAdd, TemporalUnit unit) {
if (unit instanceof ChronoUnit) {
ChronoUnit f = (ChronoUnit) unit;
switch (f) {
case NANOS: return plusNanos(amountToAdd);
case MICROS: return plusDays(amountToAdd / MICROS_PER_DAY).plusNanos((amountToAdd % MICROS_PER_DAY) * 1000);
case MILLIS: return plusDays(amountToAdd / MILLIS_PER_DAY).plusNanos((amountToAdd % MILLIS_PER_DAY) * 1000000);
case SECONDS: return plusSeconds(amountToAdd);
case MINUTES: return plusMinutes(amountToAdd);
case HOURS: return plusHours(amountToAdd);
case HALF_DAYS: return plusDays(amountToAdd / 256).plusHours((amountToAdd % 256) * 12); // no overflow (256 is multiple of 2)
}
return with(date.plus(amountToAdd, unit), time);
}
return ChronoLocalDateTimeImpl.ensureValid(date.getChronology(), unit.addTo(this, amountToAdd));
}
private ChronoLocalDateTimeImpl<D> plusDays(long days) {
return with(date.plus(days, ChronoUnit.DAYS), time);
}
private ChronoLocalDateTimeImpl<D> plusHours(long hours) {
return plusWithOverflow(date, hours, 0, 0, 0);
}
private ChronoLocalDateTimeImpl<D> plusMinutes(long minutes) {
return plusWithOverflow(date, 0, minutes, 0, 0);
}
ChronoLocalDateTimeImpl<D> plusSeconds(long seconds) {
return plusWithOverflow(date, 0, 0, seconds, 0);
}
private ChronoLocalDateTimeImpl<D> plusNanos(long nanos) {
return plusWithOverflow(date, 0, 0, 0, nanos);
}
//-----------------------------------------------------------------------
private ChronoLocalDateTimeImpl<D> plusWithOverflow(D newDate, long hours, long minutes, long seconds, long nanos) {
// 9223372036854775808 long, 2147483648 int
if ((hours | minutes | seconds | nanos) == 0) {
return with(newDate, time);
}
long totDays = nanos / NANOS_PER_DAY + // max/24*60*60*1B
seconds / SECONDS_PER_DAY + // max/24*60*60
minutes / MINUTES_PER_DAY + // max/24*60
hours / HOURS_PER_DAY; // max/24
long totNanos = nanos % NANOS_PER_DAY + // max 86400000000000
(seconds % SECONDS_PER_DAY) * NANOS_PER_SECOND + // max 86400000000000
(minutes % MINUTES_PER_DAY) * NANOS_PER_MINUTE + // max 86400000000000
(hours % HOURS_PER_DAY) * NANOS_PER_HOUR; // max 86400000000000
long curNoD = time.toNanoOfDay(); // max 86400000000000
totNanos = totNanos + curNoD; // total 432000000000000
totDays += Math.floorDiv(totNanos, NANOS_PER_DAY);
long newNoD = Math.floorMod(totNanos, NANOS_PER_DAY);
LocalTime newTime = (newNoD == curNoD ? time : LocalTime.ofNanoOfDay(newNoD));
return with(newDate.plus(totDays, ChronoUnit.DAYS), newTime);
}
//-----------------------------------------------------------------------
@Override
public ChronoZonedDateTime<D> atZone(ZoneId zone) {
return ChronoZonedDateTimeImpl.ofBest(this, zone, null);
}
//-----------------------------------------------------------------------
@Override
public long until(Temporal endExclusive, TemporalUnit unit) {
Objects.requireNonNull(endExclusive, "endExclusive");
@SuppressWarnings("unchecked")
ChronoLocalDateTime<D> end = (ChronoLocalDateTime<D>) getChronology().localDateTime(endExclusive);
if (unit instanceof ChronoUnit) {
if (unit.isTimeBased()) {
long amount = end.getLong(EPOCH_DAY) - date.getLong(EPOCH_DAY);
switch ((ChronoUnit) unit) {
case NANOS: amount = Math.multiplyExact(amount, NANOS_PER_DAY); break;
case MICROS: amount = Math.multiplyExact(amount, MICROS_PER_DAY); break;
case MILLIS: amount = Math.multiplyExact(amount, MILLIS_PER_DAY); break;
case SECONDS: amount = Math.multiplyExact(amount, SECONDS_PER_DAY); break;
case MINUTES: amount = Math.multiplyExact(amount, MINUTES_PER_DAY); break;
case HOURS: amount = Math.multiplyExact(amount, HOURS_PER_DAY); break;
case HALF_DAYS: amount = Math.multiplyExact(amount, 2); break;
}
return Math.addExact(amount, time.until(end.toLocalTime(), unit));
}
ChronoLocalDate endDate = end.toLocalDate();
if (end.toLocalTime().isBefore(time)) {
endDate = endDate.minus(1, ChronoUnit.DAYS);
}
return date.until(endDate, unit);
}
Objects.requireNonNull(unit, "unit");
return unit.between(this, end);
}
//-----------------------------------------------------------------------
/**
* Writes the ChronoLocalDateTime using a
* <a href="../../../serialized-form.html#java.time.chrono.Ser">dedicated serialized form</a>.
* @serialData
* <pre>
* out.writeByte(2); // identifies a ChronoLocalDateTime
* out.writeObject(toLocalDate());
* out.witeObject(toLocalTime());
* </pre>
*
* @return the instance of {@code Ser}, not null
*/
private Object writeReplace() {
return new Ser(Ser.CHRONO_LOCAL_DATE_TIME_TYPE, this);
}
/**
* Defend against malicious streams.
*
* @param s the stream to read
* @throws InvalidObjectException always
*/
private void readObject(ObjectInputStream s) throws InvalidObjectException {
throw new InvalidObjectException("Deserialization via serialization delegate");
}
void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(date);
out.writeObject(time);
}
static ChronoLocalDateTime<?> readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
ChronoLocalDate date = (ChronoLocalDate) in.readObject();
LocalTime time = (LocalTime) in.readObject();
return date.atTime(time);
}
//-----------------------------------------------------------------------
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof ChronoLocalDateTime) {
return compareTo((ChronoLocalDateTime<?>) obj) == 0;
}
return false;
}
@Override
public int hashCode() {
return toLocalDate().hashCode() ^ toLocalTime().hashCode();
}
@Override
public String toString() {
return toLocalDate().toString() + 'T' + toLocalTime().toString();
}
}

View file

@ -0,0 +1,365 @@
/*
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* This file is available under and governed by the GNU General Public
* License version 2 only, as published by the Free Software Foundation.
* However, the following notice accompanied the original version of this
* file:
*
* Copyright (c) 2013, Stephen Colebourne & Michael Nascimento Santos
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of JSR-310 nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package java.time.chrono;
import java.time.DateTimeException;
import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAmount;
import java.time.temporal.TemporalUnit;
import java.time.temporal.UnsupportedTemporalTypeException;
import java.util.List;
import java.util.Objects;
/**
* A date-based amount of time, such as '3 years, 4 months and 5 days' in an
* arbitrary chronology, intended for advanced globalization use cases.
* <p>
* This interface models a date-based amount of time in a calendar system.
* While most calendar systems use years, months and days, some do not.
* Therefore, this interface operates solely in terms of a set of supported
* units that are defined by the {@code Chronology}.
* The set of supported units is fixed for a given chronology.
* The amount of a supported unit may be set to zero.
* <p>
* The period is modeled as a directed amount of time, meaning that individual
* parts of the period may be negative.
*
* @implSpec
* This interface must be implemented with care to ensure other classes operate correctly.
* All implementations that can be instantiated must be final, immutable and thread-safe.
* Subclasses should be Serializable wherever possible.
*
* @since 1.8
*/
public interface ChronoPeriod
extends TemporalAmount {
/**
* Obtains a {@code ChronoPeriod} consisting of amount of time between two dates.
* <p>
* The start date is included, but the end date is not.
* The period is calculated using {@link ChronoLocalDate#until(ChronoLocalDate)}.
* As such, the calculation is chronology specific.
* <p>
* The chronology of the first date is used.
* The chronology of the second date is ignored, with the date being converted
* to the target chronology system before the calculation starts.
* <p>
* The result of this method can be a negative period if the end is before the start.
* In most cases, the positive/negative sign will be the same in each of the supported fields.
*
* @param startDateInclusive the start date, inclusive, specifying the chronology of the calculation, not null
* @param endDateExclusive the end date, exclusive, in any chronology, not null
* @return the period between this date and the end date, not null
* @see ChronoLocalDate#until(ChronoLocalDate)
*/
public static ChronoPeriod between(ChronoLocalDate startDateInclusive, ChronoLocalDate endDateExclusive) {
Objects.requireNonNull(startDateInclusive, "startDateInclusive");
Objects.requireNonNull(endDateExclusive, "endDateExclusive");
return startDateInclusive.until(endDateExclusive);
}
//-----------------------------------------------------------------------
/**
* Gets the value of the requested unit.
* <p>
* The supported units are chronology specific.
* They will typically be {@link ChronoUnit#YEARS YEARS},
* {@link ChronoUnit#MONTHS MONTHS} and {@link ChronoUnit#DAYS DAYS}.
* Requesting an unsupported unit will throw an exception.
*
* @param unit the {@code TemporalUnit} for which to return the value
* @return the long value of the unit
* @throws DateTimeException if the unit is not supported
* @throws UnsupportedTemporalTypeException if the unit is not supported
*/
@Override
long get(TemporalUnit unit);
/**
* Gets the set of units supported by this period.
* <p>
* The supported units are chronology specific.
* They will typically be {@link ChronoUnit#YEARS YEARS},
* {@link ChronoUnit#MONTHS MONTHS} and {@link ChronoUnit#DAYS DAYS}.
* They are returned in order from largest to smallest.
* <p>
* This set can be used in conjunction with {@link #get(TemporalUnit)}
* to access the entire state of the period.
*
* @return a list containing the supported units, not null
*/
@Override
List<TemporalUnit> getUnits();
/**
* Gets the chronology that defines the meaning of the supported units.
* <p>
* The period is defined by the chronology.
* It controls the supported units and restricts addition/subtraction
* to {@code ChronoLocalDate} instances of the same chronology.
*
* @return the chronology defining the period, not null
*/
Chronology getChronology();
//-----------------------------------------------------------------------
/**
* Checks if all the supported units of this period are zero.
*
* @return true if this period is zero-length
*/
default boolean isZero() {
for (TemporalUnit unit : getUnits()) {
if (get(unit) != 0) {
return false;
}
}
return true;
}
/**
* Checks if any of the supported units of this period are negative.
*
* @return true if any unit of this period is negative
*/
default boolean isNegative() {
for (TemporalUnit unit : getUnits()) {
if (get(unit) < 0) {
return true;
}
}
return false;
}
//-----------------------------------------------------------------------
/**
* Returns a copy of this period with the specified period added.
* <p>
* If the specified amount is a {@code ChronoPeriod} then it must have
* the same chronology as this period. Implementations may choose to
* accept or reject other {@code TemporalAmount} implementations.
* <p>
* This instance is immutable and unaffected by this method call.
*
* @param amountToAdd the period to add, not null
* @return a {@code ChronoPeriod} based on this period with the requested period added, not null
* @throws ArithmeticException if numeric overflow occurs
*/
ChronoPeriod plus(TemporalAmount amountToAdd);
/**
* Returns a copy of this period with the specified period subtracted.
* <p>
* If the specified amount is a {@code ChronoPeriod} then it must have
* the same chronology as this period. Implementations may choose to
* accept or reject other {@code TemporalAmount} implementations.
* <p>
* This instance is immutable and unaffected by this method call.
*
* @param amountToSubtract the period to subtract, not null
* @return a {@code ChronoPeriod} based on this period with the requested period subtracted, not null
* @throws ArithmeticException if numeric overflow occurs
*/
ChronoPeriod minus(TemporalAmount amountToSubtract);
//-----------------------------------------------------------------------
/**
* Returns a new instance with each amount in this period in this period
* multiplied by the specified scalar.
* <p>
* This returns a period with each supported unit individually multiplied.
* For example, a period of "2 years, -3 months and 4 days" multiplied by
* 3 will return "6 years, -9 months and 12 days".
* No normalization is performed.
*
* @param scalar the scalar to multiply by, not null
* @return a {@code ChronoPeriod} based on this period with the amounts multiplied
* by the scalar, not null
* @throws ArithmeticException if numeric overflow occurs
*/
ChronoPeriod multipliedBy(int scalar);
/**
* Returns a new instance with each amount in this period negated.
* <p>
* This returns a period with each supported unit individually negated.
* For example, a period of "2 years, -3 months and 4 days" will be
* negated to "-2 years, 3 months and -4 days".
* No normalization is performed.
*
* @return a {@code ChronoPeriod} based on this period with the amounts negated, not null
* @throws ArithmeticException if numeric overflow occurs, which only happens if
* one of the units has the value {@code Long.MIN_VALUE}
*/
default ChronoPeriod negated() {
return multipliedBy(-1);
}
//-----------------------------------------------------------------------
/**
* Returns a copy of this period with the amounts of each unit normalized.
* <p>
* The process of normalization is specific to each calendar system.
* For example, in the ISO calendar system, the years and months are
* normalized but the days are not, such that "15 months" would be
* normalized to "1 year and 3 months".
* <p>
* This instance is immutable and unaffected by this method call.
*
* @return a {@code ChronoPeriod} based on this period with the amounts of each
* unit normalized, not null
* @throws ArithmeticException if numeric overflow occurs
*/
ChronoPeriod normalized();
//-------------------------------------------------------------------------
/**
* Adds this period to the specified temporal object.
* <p>
* This returns a temporal object of the same observable type as the input
* with this period added.
* <p>
* In most cases, it is clearer to reverse the calling pattern by using
* {@link Temporal#plus(TemporalAmount)}.
* <pre>
* // these two lines are equivalent, but the second approach is recommended
* dateTime = thisPeriod.addTo(dateTime);
* dateTime = dateTime.plus(thisPeriod);
* </pre>
* <p>
* The specified temporal must have the same chronology as this period.
* This returns a temporal with the non-zero supported units added.
* <p>
* This instance is immutable and unaffected by this method call.
*
* @param temporal the temporal object to adjust, not null
* @return an object of the same type with the adjustment made, not null
* @throws DateTimeException if unable to add
* @throws ArithmeticException if numeric overflow occurs
*/
@Override
Temporal addTo(Temporal temporal);
/**
* Subtracts this period from the specified temporal object.
* <p>
* This returns a temporal object of the same observable type as the input
* with this period subtracted.
* <p>
* In most cases, it is clearer to reverse the calling pattern by using
* {@link Temporal#minus(TemporalAmount)}.
* <pre>
* // these two lines are equivalent, but the second approach is recommended
* dateTime = thisPeriod.subtractFrom(dateTime);
* dateTime = dateTime.minus(thisPeriod);
* </pre>
* <p>
* The specified temporal must have the same chronology as this period.
* This returns a temporal with the non-zero supported units subtracted.
* <p>
* This instance is immutable and unaffected by this method call.
*
* @param temporal the temporal object to adjust, not null
* @return an object of the same type with the adjustment made, not null
* @throws DateTimeException if unable to subtract
* @throws ArithmeticException if numeric overflow occurs
*/
@Override
Temporal subtractFrom(Temporal temporal);
//-----------------------------------------------------------------------
/**
* Checks if this period is equal to another period, including the chronology.
* <p>
* Compares this period with another ensuring that the type, each amount and
* the chronology are the same.
* Note that this means that a period of "15 Months" is not equal to a period
* of "1 Year and 3 Months".
*
* @param obj the object to check, null returns false
* @return true if this is equal to the other period
*/
@Override
boolean equals(Object obj);
/**
* A hash code for this period.
*
* @return a suitable hash code
*/
@Override
int hashCode();
//-----------------------------------------------------------------------
/**
* Outputs this period as a {@code String}.
* <p>
* The output will include the period amounts and chronology.
*
* @return a string representation of this period, not null
*/
@Override
String toString();
}

View file

@ -0,0 +1,398 @@
/*
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Copyright (c) 2013, Stephen Colebourne & Michael Nascimento Santos
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of JSR-310 nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package java.time.chrono;
import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
import static java.time.temporal.ChronoUnit.DAYS;
import static java.time.temporal.ChronoUnit.MONTHS;
import static java.time.temporal.ChronoUnit.YEARS;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.time.DateTimeException;
import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalAmount;
import java.time.temporal.TemporalQueries;
import java.time.temporal.TemporalUnit;
import java.time.temporal.UnsupportedTemporalTypeException;
import java.time.temporal.ValueRange;
import java.util.List;
import java.util.Objects;
/**
* A period expressed in terms of a standard year-month-day calendar system.
* <p>
* This class is used by applications seeking to handle dates in non-ISO calendar systems.
* For example, the Japanese, Minguo, Thai Buddhist and others.
*
* @implSpec
* This class is immutable nad thread-safe.
*
* @since 1.8
*/
final class ChronoPeriodImpl
implements ChronoPeriod, Serializable {
// this class is only used by JDK chronology implementations and makes assumptions based on that fact
/**
* Serialization version.
*/
private static final long serialVersionUID = 57387258289L;
/**
* The set of supported units.
*/
private static final List<TemporalUnit> SUPPORTED_UNITS = List.of(YEARS, MONTHS, DAYS);
/**
* The chronology.
*/
private final Chronology chrono;
/**
* The number of years.
*/
final int years;
/**
* The number of months.
*/
final int months;
/**
* The number of days.
*/
final int days;
/**
* Creates an instance.
*/
ChronoPeriodImpl(Chronology chrono, int years, int months, int days) {
Objects.requireNonNull(chrono, "chrono");
this.chrono = chrono;
this.years = years;
this.months = months;
this.days = days;
}
//-----------------------------------------------------------------------
@Override
public long get(TemporalUnit unit) {
if (unit == ChronoUnit.YEARS) {
return years;
} else if (unit == ChronoUnit.MONTHS) {
return months;
} else if (unit == ChronoUnit.DAYS) {
return days;
} else {
throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
}
}
@Override
public List<TemporalUnit> getUnits() {
return ChronoPeriodImpl.SUPPORTED_UNITS;
}
@Override
public Chronology getChronology() {
return chrono;
}
//-----------------------------------------------------------------------
@Override
public boolean isZero() {
return years == 0 && months == 0 && days == 0;
}
@Override
public boolean isNegative() {
return years < 0 || months < 0 || days < 0;
}
//-----------------------------------------------------------------------
@Override
public ChronoPeriod plus(TemporalAmount amountToAdd) {
ChronoPeriodImpl amount = validateAmount(amountToAdd);
return new ChronoPeriodImpl(
chrono,
Math.addExact(years, amount.years),
Math.addExact(months, amount.months),
Math.addExact(days, amount.days));
}
@Override
public ChronoPeriod minus(TemporalAmount amountToSubtract) {
ChronoPeriodImpl amount = validateAmount(amountToSubtract);
return new ChronoPeriodImpl(
chrono,
Math.subtractExact(years, amount.years),
Math.subtractExact(months, amount.months),
Math.subtractExact(days, amount.days));
}
/**
* Obtains an instance of {@code ChronoPeriodImpl} from a temporal amount.
*
* @param amount the temporal amount to convert, not null
* @return the period, not null
*/
private ChronoPeriodImpl validateAmount(TemporalAmount amount) {
Objects.requireNonNull(amount, "amount");
if (amount instanceof ChronoPeriodImpl == false) {
throw new DateTimeException("Unable to obtain ChronoPeriod from TemporalAmount: " + amount.getClass());
}
ChronoPeriodImpl period = (ChronoPeriodImpl) amount;
if (chrono.equals(period.getChronology()) == false) {
throw new ClassCastException("Chronology mismatch, expected: " + chrono.getId() + ", actual: " + period.getChronology().getId());
}
return period;
}
//-----------------------------------------------------------------------
@Override
public ChronoPeriod multipliedBy(int scalar) {
if (this.isZero() || scalar == 1) {
return this;
}
return new ChronoPeriodImpl(
chrono,
Math.multiplyExact(years, scalar),
Math.multiplyExact(months, scalar),
Math.multiplyExact(days, scalar));
}
//-----------------------------------------------------------------------
@Override
public ChronoPeriod normalized() {
long monthRange = monthRange();
if (monthRange > 0) {
long totalMonths = years * monthRange + months;
long splitYears = totalMonths / monthRange;
int splitMonths = (int) (totalMonths % monthRange); // no overflow
if (splitYears == years && splitMonths == months) {
return this;
}
return new ChronoPeriodImpl(chrono, Math.toIntExact(splitYears), splitMonths, days);
}
return this;
}
/**
* Calculates the range of months.
*
* @return the month range, -1 if not fixed range
*/
private long monthRange() {
ValueRange startRange = chrono.range(MONTH_OF_YEAR);
if (startRange.isFixed() && startRange.isIntValue()) {
return startRange.getMaximum() - startRange.getMinimum() + 1;
}
return -1;
}
//-------------------------------------------------------------------------
@Override
public Temporal addTo(Temporal temporal) {
validateChrono(temporal);
if (months == 0) {
if (years != 0) {
temporal = temporal.plus(years, YEARS);
}
} else {
long monthRange = monthRange();
if (monthRange > 0) {
temporal = temporal.plus(years * monthRange + months, MONTHS);
} else {
if (years != 0) {
temporal = temporal.plus(years, YEARS);
}
temporal = temporal.plus(months, MONTHS);
}
}
if (days != 0) {
temporal = temporal.plus(days, DAYS);
}
return temporal;
}
@Override
public Temporal subtractFrom(Temporal temporal) {
validateChrono(temporal);
if (months == 0) {
if (years != 0) {
temporal = temporal.minus(years, YEARS);
}
} else {
long monthRange = monthRange();
if (monthRange > 0) {
temporal = temporal.minus(years * monthRange + months, MONTHS);
} else {
if (years != 0) {
temporal = temporal.minus(years, YEARS);
}
temporal = temporal.minus(months, MONTHS);
}
}
if (days != 0) {
temporal = temporal.minus(days, DAYS);
}
return temporal;
}
/**
* Validates that the temporal has the correct chronology.
*/
private void validateChrono(TemporalAccessor temporal) {
Objects.requireNonNull(temporal, "temporal");
Chronology temporalChrono = temporal.query(TemporalQueries.chronology());
if (temporalChrono != null && chrono.equals(temporalChrono) == false) {
throw new DateTimeException("Chronology mismatch, expected: " + chrono.getId() + ", actual: " + temporalChrono.getId());
}
}
//-----------------------------------------------------------------------
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof ChronoPeriodImpl) {
ChronoPeriodImpl other = (ChronoPeriodImpl) obj;
return years == other.years && months == other.months &&
days == other.days && chrono.equals(other.chrono);
}
return false;
}
@Override
public int hashCode() {
return (years + Integer.rotateLeft(months, 8) + Integer.rotateLeft(days, 16)) ^ chrono.hashCode();
}
//-----------------------------------------------------------------------
@Override
public String toString() {
if (isZero()) {
return getChronology().toString() + " P0D";
} else {
StringBuilder buf = new StringBuilder();
buf.append(getChronology().toString()).append(' ').append('P');
if (years != 0) {
buf.append(years).append('Y');
}
if (months != 0) {
buf.append(months).append('M');
}
if (days != 0) {
buf.append(days).append('D');
}
return buf.toString();
}
}
//-----------------------------------------------------------------------
/**
* Writes the Chronology using a
* <a href="../../../serialized-form.html#java.time.chrono.Ser">dedicated serialized form</a>.
* <pre>
* out.writeByte(12); // identifies this as a ChronoPeriodImpl
* out.writeUTF(getId()); // the chronology
* out.writeInt(years);
* out.writeInt(months);
* out.writeInt(days);
* </pre>
*
* @return the instance of {@code Ser}, not null
*/
protected Object writeReplace() {
return new Ser(Ser.CHRONO_PERIOD_TYPE, this);
}
/**
* Defend against malicious streams.
*
* @param s the stream to read
* @throws InvalidObjectException always
*/
private void readObject(ObjectInputStream s) throws ObjectStreamException {
throw new InvalidObjectException("Deserialization via serialization delegate");
}
void writeExternal(DataOutput out) throws IOException {
out.writeUTF(chrono.getId());
out.writeInt(years);
out.writeInt(months);
out.writeInt(days);
}
static ChronoPeriodImpl readExternal(DataInput in) throws IOException {
Chronology chrono = Chronology.of(in.readUTF());
int years = in.readInt();
int months = in.readInt();
int days = in.readInt();
return new ChronoPeriodImpl(chrono, years, months, days);
}
}

View file

@ -0,0 +1,684 @@
/*
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* This file is available under and governed by the GNU General Public
* License version 2 only, as published by the Free Software Foundation.
* However, the following notice accompanied the original version of this
* file:
*
* Copyright (c) 2007-2012, Stephen Colebourne & Michael Nascimento Santos
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of JSR-310 nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package java.time.chrono;
import static java.time.temporal.ChronoField.INSTANT_SECONDS;
import static java.time.temporal.ChronoField.OFFSET_SECONDS;
import static java.time.temporal.ChronoUnit.FOREVER;
import static java.time.temporal.ChronoUnit.NANOS;
import java.io.Serializable;
import java.time.DateTimeException;
import java.time.Instant;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalAdjuster;
import java.time.temporal.TemporalAmount;
import java.time.temporal.TemporalField;
import java.time.temporal.TemporalQueries;
import java.time.temporal.TemporalQuery;
import java.time.temporal.TemporalUnit;
import java.time.temporal.UnsupportedTemporalTypeException;
import java.time.temporal.ValueRange;
import java.util.Comparator;
import java.util.Objects;
/**
* A date-time with a time-zone in an arbitrary chronology,
* intended for advanced globalization use cases.
* <p>
* <b>Most applications should declare method signatures, fields and variables
* as {@link ZonedDateTime}, not this interface.</b>
* <p>
* A {@code ChronoZonedDateTime} is the abstract representation of an offset date-time
* where the {@code Chronology chronology}, or calendar system, is pluggable.
* The date-time is defined in terms of fields expressed by {@link TemporalField},
* where most common implementations are defined in {@link ChronoField}.
* The chronology defines how the calendar system operates and the meaning of
* the standard fields.
*
* <h3>When to use this interface</h3>
* The design of the API encourages the use of {@code ZonedDateTime} rather than this
* interface, even in the case where the application needs to deal with multiple
* calendar systems. The rationale for this is explored in detail in {@link ChronoLocalDate}.
* <p>
* Ensure that the discussion in {@code ChronoLocalDate} has been read and understood
* before using this interface.
*
* @implSpec
* This interface must be implemented with care to ensure other classes operate correctly.
* All implementations that can be instantiated must be final, immutable and thread-safe.
* Subclasses should be Serializable wherever possible.
*
* @param <D> the concrete type for the date of this date-time
* @since 1.8
*/
public interface ChronoZonedDateTime<D extends ChronoLocalDate>
extends Temporal, Comparable<ChronoZonedDateTime<?>> {
/**
* Gets a comparator that compares {@code ChronoZonedDateTime} in
* time-line order ignoring the chronology.
* <p>
* This comparator differs from the comparison in {@link #compareTo} in that it
* only compares the underlying instant and not the chronology.
* This allows dates in different calendar systems to be compared based
* on the position of the date-time on the instant time-line.
* The underlying comparison is equivalent to comparing the epoch-second and nano-of-second.
*
* @return a comparator that compares in time-line order ignoring the chronology
* @see #isAfter
* @see #isBefore
* @see #isEqual
*/
static Comparator<ChronoZonedDateTime<?>> timeLineOrder() {
return (Comparator<ChronoZonedDateTime<?>> & Serializable) (dateTime1, dateTime2) -> {
int cmp = Long.compare(dateTime1.toEpochSecond(), dateTime2.toEpochSecond());
if (cmp == 0) {
cmp = Long.compare(dateTime1.toLocalTime().getNano(), dateTime2.toLocalTime().getNano());
}
return cmp;
};
}
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code ChronoZonedDateTime} from a temporal object.
* <p>
* This creates a zoned date-time based on the specified temporal.
* A {@code TemporalAccessor} represents an arbitrary set of date and time information,
* which this factory converts to an instance of {@code ChronoZonedDateTime}.
* <p>
* The conversion extracts and combines the chronology, date, time and zone
* from the temporal object. The behavior is equivalent to using
* {@link Chronology#zonedDateTime(TemporalAccessor)} with the extracted chronology.
* Implementations are permitted to perform optimizations such as accessing
* those fields that are equivalent to the relevant objects.
* <p>
* This method matches the signature of the functional interface {@link TemporalQuery}
* allowing it to be used as a query via method reference, {@code ChronoZonedDateTime::from}.
*
* @param temporal the temporal object to convert, not null
* @return the date-time, not null
* @throws DateTimeException if unable to convert to a {@code ChronoZonedDateTime}
* @see Chronology#zonedDateTime(TemporalAccessor)
*/
static ChronoZonedDateTime<?> from(TemporalAccessor temporal) {
if (temporal instanceof ChronoZonedDateTime) {
return (ChronoZonedDateTime<?>) temporal;
}
Objects.requireNonNull(temporal, "temporal");
Chronology chrono = temporal.query(TemporalQueries.chronology());
if (chrono == null) {
throw new DateTimeException("Unable to obtain ChronoZonedDateTime from TemporalAccessor: " + temporal.getClass());
}
return chrono.zonedDateTime(temporal);
}
//-----------------------------------------------------------------------
@Override
default ValueRange range(TemporalField field) {
if (field instanceof ChronoField) {
if (field == INSTANT_SECONDS || field == OFFSET_SECONDS) {
return field.range();
}
return toLocalDateTime().range(field);
}
return field.rangeRefinedBy(this);
}
@Override
default int get(TemporalField field) {
if (field instanceof ChronoField) {
switch ((ChronoField) field) {
case INSTANT_SECONDS:
throw new UnsupportedTemporalTypeException("Invalid field 'InstantSeconds' for get() method, use getLong() instead");
case OFFSET_SECONDS:
return getOffset().getTotalSeconds();
}
return toLocalDateTime().get(field);
}
return Temporal.super.get(field);
}
@Override
default long getLong(TemporalField field) {
if (field instanceof ChronoField) {
switch ((ChronoField) field) {
case INSTANT_SECONDS: return toEpochSecond();
case OFFSET_SECONDS: return getOffset().getTotalSeconds();
}
return toLocalDateTime().getLong(field);
}
return field.getFrom(this);
}
/**
* Gets the local date part of this date-time.
* <p>
* This returns a local date with the same year, month and day
* as this date-time.
*
* @return the date part of this date-time, not null
*/
default D toLocalDate() {
return toLocalDateTime().toLocalDate();
}
/**
* Gets the local time part of this date-time.
* <p>
* This returns a local time with the same hour, minute, second and
* nanosecond as this date-time.
*
* @return the time part of this date-time, not null
*/
default LocalTime toLocalTime() {
return toLocalDateTime().toLocalTime();
}
/**
* Gets the local date-time part of this date-time.
* <p>
* This returns a local date with the same year, month and day
* as this date-time.
*
* @return the local date-time part of this date-time, not null
*/
ChronoLocalDateTime<D> toLocalDateTime();
/**
* Gets the chronology of this date-time.
* <p>
* The {@code Chronology} represents the calendar system in use.
* The era and other fields in {@link ChronoField} are defined by the chronology.
*
* @return the chronology, not null
*/
default Chronology getChronology() {
return toLocalDate().getChronology();
}
/**
* Gets the zone offset, such as '+01:00'.
* <p>
* This is the offset of the local date-time from UTC/Greenwich.
*
* @return the zone offset, not null
*/
ZoneOffset getOffset();
/**
* Gets the zone ID, such as 'Europe/Paris'.
* <p>
* This returns the stored time-zone id used to determine the time-zone rules.
*
* @return the zone ID, not null
*/
ZoneId getZone();
//-----------------------------------------------------------------------
/**
* Returns a copy of this date-time changing the zone offset to the
* earlier of the two valid offsets at a local time-line overlap.
* <p>
* This method only has any effect when the local time-line overlaps, such as
* at an autumn daylight savings cutover. In this scenario, there are two
* valid offsets for the local date-time. Calling this method will return
* a zoned date-time with the earlier of the two selected.
* <p>
* If this method is called when it is not an overlap, {@code this}
* is returned.
* <p>
* This instance is immutable and unaffected by this method call.
*
* @return a {@code ChronoZonedDateTime} based on this date-time with the earlier offset, not null
* @throws DateTimeException if no rules can be found for the zone
* @throws DateTimeException if no rules are valid for this date-time
*/
ChronoZonedDateTime<D> withEarlierOffsetAtOverlap();
/**
* Returns a copy of this date-time changing the zone offset to the
* later of the two valid offsets at a local time-line overlap.
* <p>
* This method only has any effect when the local time-line overlaps, such as
* at an autumn daylight savings cutover. In this scenario, there are two
* valid offsets for the local date-time. Calling this method will return
* a zoned date-time with the later of the two selected.
* <p>
* If this method is called when it is not an overlap, {@code this}
* is returned.
* <p>
* This instance is immutable and unaffected by this method call.
*
* @return a {@code ChronoZonedDateTime} based on this date-time with the later offset, not null
* @throws DateTimeException if no rules can be found for the zone
* @throws DateTimeException if no rules are valid for this date-time
*/
ChronoZonedDateTime<D> withLaterOffsetAtOverlap();
/**
* Returns a copy of this date-time with a different time-zone,
* retaining the local date-time if possible.
* <p>
* This method changes the time-zone and retains the local date-time.
* The local date-time is only changed if it is invalid for the new zone.
* <p>
* To change the zone and adjust the local date-time,
* use {@link #withZoneSameInstant(ZoneId)}.
* <p>
* This instance is immutable and unaffected by this method call.
*
* @param zone the time-zone to change to, not null
* @return a {@code ChronoZonedDateTime} based on this date-time with the requested zone, not null
*/
ChronoZonedDateTime<D> withZoneSameLocal(ZoneId zone);
/**
* Returns a copy of this date-time with a different time-zone,
* retaining the instant.
* <p>
* This method changes the time-zone and retains the instant.
* This normally results in a change to the local date-time.
* <p>
* This method is based on retaining the same instant, thus gaps and overlaps
* in the local time-line have no effect on the result.
* <p>
* To change the offset while keeping the local time,
* use {@link #withZoneSameLocal(ZoneId)}.
*
* @param zone the time-zone to change to, not null
* @return a {@code ChronoZonedDateTime} based on this date-time with the requested zone, not null
* @throws DateTimeException if the result exceeds the supported date range
*/
ChronoZonedDateTime<D> withZoneSameInstant(ZoneId zone);
/**
* Checks if the specified field is supported.
* <p>
* This checks if the specified field can be queried on this date-time.
* If false, then calling the {@link #range(TemporalField) range},
* {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}
* methods will throw an exception.
* <p>
* The set of supported fields is defined by the chronology and normally includes
* all {@code ChronoField} fields.
* <p>
* If the field is not a {@code ChronoField}, then the result of this method
* is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}
* passing {@code this} as the argument.
* Whether the field is supported is determined by the field.
*
* @param field the field to check, null returns false
* @return true if the field can be queried, false if not
*/
@Override
boolean isSupported(TemporalField field);
/**
* Checks if the specified unit is supported.
* <p>
* This checks if the specified unit can be added to or subtracted from this date-time.
* If false, then calling the {@link #plus(long, TemporalUnit)} and
* {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
* <p>
* The set of supported units is defined by the chronology and normally includes
* all {@code ChronoUnit} units except {@code FOREVER}.
* <p>
* If the unit is not a {@code ChronoUnit}, then the result of this method
* is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
* passing {@code this} as the argument.
* Whether the unit is supported is determined by the unit.
*
* @param unit the unit to check, null returns false
* @return true if the unit can be added/subtracted, false if not
*/
@Override
default boolean isSupported(TemporalUnit unit) {
if (unit instanceof ChronoUnit) {
return unit != FOREVER;
}
return unit != null && unit.isSupportedBy(this);
}
//-----------------------------------------------------------------------
// override for covariant return type
/**
* {@inheritDoc}
* @throws DateTimeException {@inheritDoc}
* @throws ArithmeticException {@inheritDoc}
*/
@Override
default ChronoZonedDateTime<D> with(TemporalAdjuster adjuster) {
return ChronoZonedDateTimeImpl.ensureValid(getChronology(), Temporal.super.with(adjuster));
}
/**
* {@inheritDoc}
* @throws DateTimeException {@inheritDoc}
* @throws ArithmeticException {@inheritDoc}
*/
@Override
ChronoZonedDateTime<D> with(TemporalField field, long newValue);
/**
* {@inheritDoc}
* @throws DateTimeException {@inheritDoc}
* @throws ArithmeticException {@inheritDoc}
*/
@Override
default ChronoZonedDateTime<D> plus(TemporalAmount amount) {
return ChronoZonedDateTimeImpl.ensureValid(getChronology(), Temporal.super.plus(amount));
}
/**
* {@inheritDoc}
* @throws DateTimeException {@inheritDoc}
* @throws ArithmeticException {@inheritDoc}
*/
@Override
ChronoZonedDateTime<D> plus(long amountToAdd, TemporalUnit unit);
/**
* {@inheritDoc}
* @throws DateTimeException {@inheritDoc}
* @throws ArithmeticException {@inheritDoc}
*/
@Override
default ChronoZonedDateTime<D> minus(TemporalAmount amount) {
return ChronoZonedDateTimeImpl.ensureValid(getChronology(), Temporal.super.minus(amount));
}
/**
* {@inheritDoc}
* @throws DateTimeException {@inheritDoc}
* @throws ArithmeticException {@inheritDoc}
*/
@Override
default ChronoZonedDateTime<D> minus(long amountToSubtract, TemporalUnit unit) {
return ChronoZonedDateTimeImpl.ensureValid(getChronology(), Temporal.super.minus(amountToSubtract, unit));
}
//-----------------------------------------------------------------------
/**
* Queries this date-time using the specified query.
* <p>
* This queries this date-time using the specified query strategy object.
* The {@code TemporalQuery} object defines the logic to be used to
* obtain the result. Read the documentation of the query to understand
* what the result of this method will be.
* <p>
* The result of this method is obtained by invoking the
* {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the
* specified query passing {@code this} as the argument.
*
* @param <R> the type of the result
* @param query the query to invoke, not null
* @return the query result, null may be returned (defined by the query)
* @throws DateTimeException if unable to query (defined by the query)
* @throws ArithmeticException if numeric overflow occurs (defined by the query)
*/
@SuppressWarnings("unchecked")
@Override
default <R> R query(TemporalQuery<R> query) {
if (query == TemporalQueries.zone() || query == TemporalQueries.zoneId()) {
return (R) getZone();
} else if (query == TemporalQueries.offset()) {
return (R) getOffset();
} else if (query == TemporalQueries.localTime()) {
return (R) toLocalTime();
} else if (query == TemporalQueries.chronology()) {
return (R) getChronology();
} else if (query == TemporalQueries.precision()) {
return (R) NANOS;
}
// inline TemporalAccessor.super.query(query) as an optimization
// non-JDK classes are not permitted to make this optimization
return query.queryFrom(this);
}
/**
* Formats this date-time using the specified formatter.
* <p>
* This date-time will be passed to the formatter to produce a string.
* <p>
* The default implementation must behave as follows:
* <pre>
* return formatter.format(this);
* </pre>
*
* @param formatter the formatter to use, not null
* @return the formatted date-time string, not null
* @throws DateTimeException if an error occurs during printing
*/
default String format(DateTimeFormatter formatter) {
Objects.requireNonNull(formatter, "formatter");
return formatter.format(this);
}
//-----------------------------------------------------------------------
/**
* Converts this date-time to an {@code Instant}.
* <p>
* This returns an {@code Instant} representing the same point on the
* time-line as this date-time. The calculation combines the
* {@linkplain #toLocalDateTime() local date-time} and
* {@linkplain #getOffset() offset}.
*
* @return an {@code Instant} representing the same instant, not null
*/
default Instant toInstant() {
return Instant.ofEpochSecond(toEpochSecond(), toLocalTime().getNano());
}
/**
* Converts this date-time to the number of seconds from the epoch
* of 1970-01-01T00:00:00Z.
* <p>
* This uses the {@linkplain #toLocalDateTime() local date-time} and
* {@linkplain #getOffset() offset} to calculate the epoch-second value,
* which is the number of elapsed seconds from 1970-01-01T00:00:00Z.
* Instants on the time-line after the epoch are positive, earlier are negative.
*
* @return the number of seconds from the epoch of 1970-01-01T00:00:00Z
*/
default long toEpochSecond() {
long epochDay = toLocalDate().toEpochDay();
long secs = epochDay * 86400 + toLocalTime().toSecondOfDay();
secs -= getOffset().getTotalSeconds();
return secs;
}
//-----------------------------------------------------------------------
/**
* Compares this date-time to another date-time, including the chronology.
* <p>
* The comparison is based first on the instant, then on the local date-time,
* then on the zone ID, then on the chronology.
* It is "consistent with equals", as defined by {@link Comparable}.
* <p>
* If all the date-time objects being compared are in the same chronology, then the
* additional chronology stage is not required.
* <p>
* This default implementation performs the comparison defined above.
*
* @param other the other date-time to compare to, not null
* @return the comparator value, negative if less, positive if greater
*/
@Override
default int compareTo(ChronoZonedDateTime<?> other) {
int cmp = Long.compare(toEpochSecond(), other.toEpochSecond());
if (cmp == 0) {
cmp = toLocalTime().getNano() - other.toLocalTime().getNano();
if (cmp == 0) {
cmp = toLocalDateTime().compareTo(other.toLocalDateTime());
if (cmp == 0) {
cmp = getZone().getId().compareTo(other.getZone().getId());
if (cmp == 0) {
cmp = getChronology().compareTo(other.getChronology());
}
}
}
}
return cmp;
}
/**
* Checks if the instant of this date-time is before that of the specified date-time.
* <p>
* This method differs from the comparison in {@link #compareTo} in that it
* only compares the instant of the date-time. This is equivalent to using
* {@code dateTime1.toInstant().isBefore(dateTime2.toInstant());}.
* <p>
* This default implementation performs the comparison based on the epoch-second
* and nano-of-second.
*
* @param other the other date-time to compare to, not null
* @return true if this point is before the specified date-time
*/
default boolean isBefore(ChronoZonedDateTime<?> other) {
long thisEpochSec = toEpochSecond();
long otherEpochSec = other.toEpochSecond();
return thisEpochSec < otherEpochSec ||
(thisEpochSec == otherEpochSec && toLocalTime().getNano() < other.toLocalTime().getNano());
}
/**
* Checks if the instant of this date-time is after that of the specified date-time.
* <p>
* This method differs from the comparison in {@link #compareTo} in that it
* only compares the instant of the date-time. This is equivalent to using
* {@code dateTime1.toInstant().isAfter(dateTime2.toInstant());}.
* <p>
* This default implementation performs the comparison based on the epoch-second
* and nano-of-second.
*
* @param other the other date-time to compare to, not null
* @return true if this is after the specified date-time
*/
default boolean isAfter(ChronoZonedDateTime<?> other) {
long thisEpochSec = toEpochSecond();
long otherEpochSec = other.toEpochSecond();
return thisEpochSec > otherEpochSec ||
(thisEpochSec == otherEpochSec && toLocalTime().getNano() > other.toLocalTime().getNano());
}
/**
* Checks if the instant of this date-time is equal to that of the specified date-time.
* <p>
* This method differs from the comparison in {@link #compareTo} and {@link #equals}
* in that it only compares the instant of the date-time. This is equivalent to using
* {@code dateTime1.toInstant().equals(dateTime2.toInstant());}.
* <p>
* This default implementation performs the comparison based on the epoch-second
* and nano-of-second.
*
* @param other the other date-time to compare to, not null
* @return true if the instant equals the instant of the specified date-time
*/
default boolean isEqual(ChronoZonedDateTime<?> other) {
return toEpochSecond() == other.toEpochSecond() &&
toLocalTime().getNano() == other.toLocalTime().getNano();
}
//-----------------------------------------------------------------------
/**
* Checks if this date-time is equal to another date-time.
* <p>
* The comparison is based on the offset date-time and the zone.
* To compare for the same instant on the time-line, use {@link #compareTo}.
* Only objects of type {@code ChronoZonedDateTime} are compared, other types return false.
*
* @param obj the object to check, null returns false
* @return true if this is equal to the other date-time
*/
@Override
boolean equals(Object obj);
/**
* A hash code for this date-time.
*
* @return a suitable hash code
*/
@Override
int hashCode();
//-----------------------------------------------------------------------
/**
* Outputs this date-time as a {@code String}.
* <p>
* The output will include the full zoned date-time.
*
* @return a string representation of this date-time, not null
*/
@Override
String toString();
}

View file

@ -0,0 +1,391 @@
/*
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* This file is available under and governed by the GNU General Public
* License version 2 only, as published by the Free Software Foundation.
* However, the following notice accompanied the original version of this
* file:
*
* Copyright (c) 2007-2012, Stephen Colebourne & Michael Nascimento Santos
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of JSR-310 nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package java.time.chrono;
import static java.time.temporal.ChronoUnit.SECONDS;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.Serializable;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalField;
import java.time.temporal.TemporalUnit;
import java.time.zone.ZoneOffsetTransition;
import java.time.zone.ZoneRules;
import java.util.List;
import java.util.Objects;
/**
* A date-time with a time-zone in the calendar neutral API.
* <p>
* {@code ZoneChronoDateTime} is an immutable representation of a date-time with a time-zone.
* This class stores all date and time fields, to a precision of nanoseconds,
* as well as a time-zone and zone offset.
* <p>
* The purpose of storing the time-zone is to distinguish the ambiguous case where
* the local time-line overlaps, typically as a result of the end of daylight time.
* Information about the local-time can be obtained using methods on the time-zone.
*
* @implSpec
* This class is immutable and thread-safe.
*
* @serial Document the delegation of this class in the serialized-form specification.
* @param <D> the concrete type for the date of this date-time
* @since 1.8
*/
final class ChronoZonedDateTimeImpl<D extends ChronoLocalDate>
implements ChronoZonedDateTime<D>, Serializable {
/**
* Serialization version.
*/
private static final long serialVersionUID = -5261813987200935591L;
/**
* The local date-time.
*/
private final transient ChronoLocalDateTimeImpl<D> dateTime;
/**
* The zone offset.
*/
private final transient ZoneOffset offset;
/**
* The zone ID.
*/
private final transient ZoneId zone;
//-----------------------------------------------------------------------
/**
* Obtains an instance from a local date-time using the preferred offset if possible.
*
* @param localDateTime the local date-time, not null
* @param zone the zone identifier, not null
* @param preferredOffset the zone offset, null if no preference
* @return the zoned date-time, not null
*/
static <R extends ChronoLocalDate> ChronoZonedDateTime<R> ofBest(
ChronoLocalDateTimeImpl<R> localDateTime, ZoneId zone, ZoneOffset preferredOffset) {
Objects.requireNonNull(localDateTime, "localDateTime");
Objects.requireNonNull(zone, "zone");
if (zone instanceof ZoneOffset) {
return new ChronoZonedDateTimeImpl<>(localDateTime, (ZoneOffset) zone, zone);
}
ZoneRules rules = zone.getRules();
LocalDateTime isoLDT = LocalDateTime.from(localDateTime);
List<ZoneOffset> validOffsets = rules.getValidOffsets(isoLDT);
ZoneOffset offset;
if (validOffsets.size() == 1) {
offset = validOffsets.get(0);
} else if (validOffsets.size() == 0) {
ZoneOffsetTransition trans = rules.getTransition(isoLDT);
localDateTime = localDateTime.plusSeconds(trans.getDuration().getSeconds());
offset = trans.getOffsetAfter();
} else {
if (preferredOffset != null && validOffsets.contains(preferredOffset)) {
offset = preferredOffset;
} else {
offset = validOffsets.get(0);
}
}
Objects.requireNonNull(offset, "offset"); // protect against bad ZoneRules
return new ChronoZonedDateTimeImpl<>(localDateTime, offset, zone);
}
/**
* Obtains an instance from an instant using the specified time-zone.
*
* @param chrono the chronology, not null
* @param instant the instant, not null
* @param zone the zone identifier, not null
* @return the zoned date-time, not null
*/
static ChronoZonedDateTimeImpl<?> ofInstant(Chronology chrono, Instant instant, ZoneId zone) {
ZoneRules rules = zone.getRules();
ZoneOffset offset = rules.getOffset(instant);
Objects.requireNonNull(offset, "offset"); // protect against bad ZoneRules
LocalDateTime ldt = LocalDateTime.ofEpochSecond(instant.getEpochSecond(), instant.getNano(), offset);
ChronoLocalDateTimeImpl<?> cldt = (ChronoLocalDateTimeImpl<?>)chrono.localDateTime(ldt);
return new ChronoZonedDateTimeImpl<>(cldt, offset, zone);
}
/**
* Obtains an instance from an {@code Instant}.
*
* @param instant the instant to create the date-time from, not null
* @param zone the time-zone to use, validated not null
* @return the zoned date-time, validated not null
*/
@SuppressWarnings("unchecked")
private ChronoZonedDateTimeImpl<D> create(Instant instant, ZoneId zone) {
return (ChronoZonedDateTimeImpl<D>)ofInstant(getChronology(), instant, zone);
}
/**
* Casts the {@code Temporal} to {@code ChronoZonedDateTimeImpl} ensuring it bas the specified chronology.
*
* @param chrono the chronology to check for, not null
* @param temporal a date-time to cast, not null
* @return the date-time checked and cast to {@code ChronoZonedDateTimeImpl}, not null
* @throws ClassCastException if the date-time cannot be cast to ChronoZonedDateTimeImpl
* or the chronology is not equal this Chronology
*/
static <R extends ChronoLocalDate> ChronoZonedDateTimeImpl<R> ensureValid(Chronology chrono, Temporal temporal) {
@SuppressWarnings("unchecked")
ChronoZonedDateTimeImpl<R> other = (ChronoZonedDateTimeImpl<R>) temporal;
if (chrono.equals(other.getChronology()) == false) {
throw new ClassCastException("Chronology mismatch, required: " + chrono.getId()
+ ", actual: " + other.getChronology().getId());
}
return other;
}
//-----------------------------------------------------------------------
/**
* Constructor.
*
* @param dateTime the date-time, not null
* @param offset the zone offset, not null
* @param zone the zone ID, not null
*/
private ChronoZonedDateTimeImpl(ChronoLocalDateTimeImpl<D> dateTime, ZoneOffset offset, ZoneId zone) {
this.dateTime = Objects.requireNonNull(dateTime, "dateTime");
this.offset = Objects.requireNonNull(offset, "offset");
this.zone = Objects.requireNonNull(zone, "zone");
}
//-----------------------------------------------------------------------
@Override
public ZoneOffset getOffset() {
return offset;
}
@Override
public ChronoZonedDateTime<D> withEarlierOffsetAtOverlap() {
ZoneOffsetTransition trans = getZone().getRules().getTransition(LocalDateTime.from(this));
if (trans != null && trans.isOverlap()) {
ZoneOffset earlierOffset = trans.getOffsetBefore();
if (earlierOffset.equals(offset) == false) {
return new ChronoZonedDateTimeImpl<>(dateTime, earlierOffset, zone);
}
}
return this;
}
@Override
public ChronoZonedDateTime<D> withLaterOffsetAtOverlap() {
ZoneOffsetTransition trans = getZone().getRules().getTransition(LocalDateTime.from(this));
if (trans != null) {
ZoneOffset offset = trans.getOffsetAfter();
if (offset.equals(getOffset()) == false) {
return new ChronoZonedDateTimeImpl<>(dateTime, offset, zone);
}
}
return this;
}
//-----------------------------------------------------------------------
@Override
public ChronoLocalDateTime<D> toLocalDateTime() {
return dateTime;
}
@Override
public ZoneId getZone() {
return zone;
}
@Override
public ChronoZonedDateTime<D> withZoneSameLocal(ZoneId zone) {
return ofBest(dateTime, zone, offset);
}
@Override
public ChronoZonedDateTime<D> withZoneSameInstant(ZoneId zone) {
Objects.requireNonNull(zone, "zone");
return this.zone.equals(zone) ? this : create(dateTime.toInstant(offset), zone);
}
//-----------------------------------------------------------------------
@Override
public boolean isSupported(TemporalField field) {
return field instanceof ChronoField || (field != null && field.isSupportedBy(this));
}
//-----------------------------------------------------------------------
@Override
public ChronoZonedDateTime<D> with(TemporalField field, long newValue) {
if (field instanceof ChronoField) {
ChronoField f = (ChronoField) field;
switch (f) {
case INSTANT_SECONDS: return plus(newValue - toEpochSecond(), SECONDS);
case OFFSET_SECONDS: {
ZoneOffset offset = ZoneOffset.ofTotalSeconds(f.checkValidIntValue(newValue));
return create(dateTime.toInstant(offset), zone);
}
}
return ofBest(dateTime.with(field, newValue), zone, offset);
}
return ChronoZonedDateTimeImpl.ensureValid(getChronology(), field.adjustInto(this, newValue));
}
//-----------------------------------------------------------------------
@Override
public ChronoZonedDateTime<D> plus(long amountToAdd, TemporalUnit unit) {
if (unit instanceof ChronoUnit) {
return with(dateTime.plus(amountToAdd, unit));
}
return ChronoZonedDateTimeImpl.ensureValid(getChronology(), unit.addTo(this, amountToAdd)); /// TODO: Generics replacement Risk!
}
//-----------------------------------------------------------------------
@Override
public long until(Temporal endExclusive, TemporalUnit unit) {
Objects.requireNonNull(endExclusive, "endExclusive");
@SuppressWarnings("unchecked")
ChronoZonedDateTime<D> end = (ChronoZonedDateTime<D>) getChronology().zonedDateTime(endExclusive);
if (unit instanceof ChronoUnit) {
end = end.withZoneSameInstant(offset);
return dateTime.until(end.toLocalDateTime(), unit);
}
Objects.requireNonNull(unit, "unit");
return unit.between(this, end);
}
//-----------------------------------------------------------------------
/**
* Writes the ChronoZonedDateTime using a
* <a href="../../../serialized-form.html#java.time.chrono.Ser">dedicated serialized form</a>.
* @serialData
* <pre>
* out.writeByte(3); // identifies a ChronoZonedDateTime
* out.writeObject(toLocalDateTime());
* out.writeObject(getOffset());
* out.writeObject(getZone());
* </pre>
*
* @return the instance of {@code Ser}, not null
*/
private Object writeReplace() {
return new Ser(Ser.CHRONO_ZONE_DATE_TIME_TYPE, this);
}
/**
* Defend against malicious streams.
*
* @param s the stream to read
* @throws InvalidObjectException always
*/
private void readObject(ObjectInputStream s) throws InvalidObjectException {
throw new InvalidObjectException("Deserialization via serialization delegate");
}
void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(dateTime);
out.writeObject(offset);
out.writeObject(zone);
}
static ChronoZonedDateTime<?> readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
ChronoLocalDateTime<?> dateTime = (ChronoLocalDateTime<?>) in.readObject();
ZoneOffset offset = (ZoneOffset) in.readObject();
ZoneId zone = (ZoneId) in.readObject();
return dateTime.atZone(offset).withZoneSameLocal(zone);
// TODO: ZDT uses ofLenient()
}
//-------------------------------------------------------------------------
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof ChronoZonedDateTime) {
return compareTo((ChronoZonedDateTime<?>) obj) == 0;
}
return false;
}
@Override
public int hashCode() {
return toLocalDateTime().hashCode() ^ getOffset().hashCode() ^ Integer.rotateLeft(getZone().hashCode(), 3);
}
@Override
public String toString() {
String str = toLocalDateTime().toString() + getOffset().toString();
if (getOffset() != getZone()) {
str += '[' + getZone().toString() + ']';
}
return str;
}
}

View file

@ -0,0 +1,819 @@
/*
* Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* This file is available under and governed by the GNU General Public
* License version 2 only, as published by the Free Software Foundation.
* However, the following notice accompanied the original version of this
* file:
*
* Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of JSR-310 nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package java.time.chrono;
import static java.time.temporal.ChronoField.HOUR_OF_DAY;
import static java.time.temporal.ChronoField.MINUTE_OF_HOUR;
import static java.time.temporal.ChronoField.SECOND_OF_MINUTE;
import java.time.Clock;
import java.time.DateTimeException;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.ResolverStyle;
import java.time.format.TextStyle;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalField;
import java.time.temporal.TemporalQueries;
import java.time.temporal.TemporalQuery;
import java.time.temporal.UnsupportedTemporalTypeException;
import java.time.temporal.ValueRange;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
/**
* A calendar system, used to organize and identify dates.
* <p>
* The main date and time API is built on the ISO calendar system.
* The chronology operates behind the scenes to represent the general concept of a calendar system.
* For example, the Japanese, Minguo, Thai Buddhist and others.
* <p>
* Most other calendar systems also operate on the shared concepts of year, month and day,
* linked to the cycles of the Earth around the Sun, and the Moon around the Earth.
* These shared concepts are defined by {@link ChronoField} and are available
* for use by any {@code Chronology} implementation:
* <pre>
* LocalDate isoDate = ...
* ThaiBuddhistDate thaiDate = ...
* int isoYear = isoDate.get(ChronoField.YEAR);
* int thaiYear = thaiDate.get(ChronoField.YEAR);
* </pre>
* As shown, although the date objects are in different calendar systems, represented by different
* {@code Chronology} instances, both can be queried using the same constant on {@code ChronoField}.
* For a full discussion of the implications of this, see {@link ChronoLocalDate}.
* In general, the advice is to use the known ISO-based {@code LocalDate}, rather than
* {@code ChronoLocalDate}.
* <p>
* While a {@code Chronology} object typically uses {@code ChronoField} and is based on
* an era, year-of-era, month-of-year, day-of-month model of a date, this is not required.
* A {@code Chronology} instance may represent a totally different kind of calendar system,
* such as the Mayan.
* <p>
* In practical terms, the {@code Chronology} instance also acts as a factory.
* The {@link #of(String)} method allows an instance to be looked up by identifier,
* while the {@link #ofLocale(Locale)} method allows lookup by locale.
* <p>
* The {@code Chronology} instance provides a set of methods to create {@code ChronoLocalDate} instances.
* The date classes are used to manipulate specific dates.
* <ul>
* <li> {@link #dateNow() dateNow()}
* <li> {@link #dateNow(Clock) dateNow(clock)}
* <li> {@link #dateNow(ZoneId) dateNow(zone)}
* <li> {@link #date(int, int, int) date(yearProleptic, month, day)}
* <li> {@link #date(Era, int, int, int) date(era, yearOfEra, month, day)}
* <li> {@link #dateYearDay(int, int) dateYearDay(yearProleptic, dayOfYear)}
* <li> {@link #dateYearDay(Era, int, int) dateYearDay(era, yearOfEra, dayOfYear)}
* <li> {@link #date(TemporalAccessor) date(TemporalAccessor)}
* </ul>
*
* <h3 id="addcalendars">Adding New Calendars</h3>
* The set of available chronologies can be extended by applications.
* Adding a new calendar system requires the writing of an implementation of
* {@code Chronology}, {@code ChronoLocalDate} and {@code Era}.
* The majority of the logic specific to the calendar system will be in the
* {@code ChronoLocalDate} implementation.
* The {@code Chronology} implementation acts as a factory.
* <p>
* To permit the discovery of additional chronologies, the {@link java.util.ServiceLoader ServiceLoader}
* is used. A file must be added to the {@code META-INF/services} directory with the
* name 'java.time.chrono.Chronology' listing the implementation classes.
* See the ServiceLoader for more details on service loading.
* For lookup by id or calendarType, the system provided calendars are found
* first followed by application provided calendars.
* <p>
* Each chronology must define a chronology ID that is unique within the system.
* If the chronology represents a calendar system defined by the
* CLDR specification then the calendar type is the concatenation of the
* CLDR type and, if applicable, the CLDR variant.
*
* @implSpec
* This interface must be implemented with care to ensure other classes operate correctly.
* All implementations that can be instantiated must be final, immutable and thread-safe.
* Subclasses should be Serializable wherever possible.
*
* @since 1.8
*/
public interface Chronology extends Comparable<Chronology> {
/**
* Obtains an instance of {@code Chronology} from a temporal object.
* <p>
* This obtains a chronology based on the specified temporal.
* A {@code TemporalAccessor} represents an arbitrary set of date and time information,
* which this factory converts to an instance of {@code Chronology}.
* <p>
* The conversion will obtain the chronology using {@link TemporalQueries#chronology()}.
* If the specified temporal object does not have a chronology, {@link IsoChronology} is returned.
* <p>
* This method matches the signature of the functional interface {@link TemporalQuery}
* allowing it to be used as a query via method reference, {@code Chronology::from}.
*
* @param temporal the temporal to convert, not null
* @return the chronology, not null
* @throws DateTimeException if unable to convert to a {@code Chronology}
*/
static Chronology from(TemporalAccessor temporal) {
Objects.requireNonNull(temporal, "temporal");
Chronology obj = temporal.query(TemporalQueries.chronology());
return Objects.requireNonNullElse(obj, IsoChronology.INSTANCE);
}
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code Chronology} from a locale.
* <p>
* This returns a {@code Chronology} based on the specified locale,
* typically returning {@code IsoChronology}. Other calendar systems
* are only returned if they are explicitly selected within the locale.
* <p>
* The {@link Locale} class provide access to a range of information useful
* for localizing an application. This includes the language and region,
* such as "en-GB" for English as used in Great Britain.
* <p>
* The {@code Locale} class also supports an extension mechanism that
* can be used to identify a calendar system. The mechanism is a form
* of key-value pairs, where the calendar system has the key "ca".
* For example, the locale "en-JP-u-ca-japanese" represents the English
* language as used in Japan with the Japanese calendar system.
* <p>
* This method finds the desired calendar system in a manner equivalent
* to passing "ca" to {@link Locale#getUnicodeLocaleType(String)}.
* If the "ca" key is not present, then {@code IsoChronology} is returned.
* <p>
* Note that the behavior of this method differs from the older
* {@link java.util.Calendar#getInstance(Locale)} method.
* If that method receives a locale of "th_TH" it will return {@code BuddhistCalendar}.
* By contrast, this method will return {@code IsoChronology}.
* Passing the locale "th-TH-u-ca-buddhist" into either method will
* result in the Thai Buddhist calendar system and is therefore the
* recommended approach going forward for Thai calendar system localization.
* <p>
* A similar, but simpler, situation occurs for the Japanese calendar system.
* The locale "jp_JP_JP" has previously been used to access the calendar.
* However, unlike the Thai locale, "ja_JP_JP" is automatically converted by
* {@code Locale} to the modern and recommended form of "ja-JP-u-ca-japanese".
* Thus, there is no difference in behavior between this method and
* {@code Calendar#getInstance(Locale)}.
*
* @param locale the locale to use to obtain the calendar system, not null
* @return the calendar system associated with the locale, not null
* @throws DateTimeException if the locale-specified calendar cannot be found
*/
static Chronology ofLocale(Locale locale) {
return AbstractChronology.ofLocale(locale);
}
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code Chronology} from a chronology ID or
* calendar system type.
* <p>
* This returns a chronology based on either the ID or the type.
* The {@link #getId() chronology ID} uniquely identifies the chronology.
* The {@link #getCalendarType() calendar system type} is defined by the
* CLDR specification.
* <p>
* The chronology may be a system chronology or a chronology
* provided by the application via ServiceLoader configuration.
* <p>
* Since some calendars can be customized, the ID or type typically refers
* to the default customization. For example, the Gregorian calendar can have multiple
* cutover dates from the Julian, but the lookup only provides the default cutover date.
*
* @param id the chronology ID or calendar system type, not null
* @return the chronology with the identifier requested, not null
* @throws DateTimeException if the chronology cannot be found
*/
static Chronology of(String id) {
return AbstractChronology.of(id);
}
/**
* Returns the available chronologies.
* <p>
* Each returned {@code Chronology} is available for use in the system.
* The set of chronologies includes the system chronologies and
* any chronologies provided by the application via ServiceLoader
* configuration.
*
* @return the independent, modifiable set of the available chronology IDs, not null
*/
static Set<Chronology> getAvailableChronologies() {
return AbstractChronology.getAvailableChronologies();
}
//-----------------------------------------------------------------------
/**
* Gets the ID of the chronology.
* <p>
* The ID uniquely identifies the {@code Chronology}.
* It can be used to lookup the {@code Chronology} using {@link #of(String)}.
*
* @return the chronology ID, not null
* @see #getCalendarType()
*/
String getId();
/**
* Gets the calendar type of the calendar system.
* <p>
* The calendar type is an identifier defined by the CLDR and
* <em>Unicode Locale Data Markup Language (LDML)</em> specifications
* to uniquely identify a calendar.
* The {@code getCalendarType} is the concatenation of the CLDR calendar type
* and the variant, if applicable, is appended separated by "-".
* The calendar type is used to lookup the {@code Chronology} using {@link #of(String)}.
*
* @return the calendar system type, null if the calendar is not defined by CLDR/LDML
* @see #getId()
*/
String getCalendarType();
//-----------------------------------------------------------------------
/**
* Obtains a local date in this chronology from the era, year-of-era,
* month-of-year and day-of-month fields.
*
* @implSpec
* The default implementation combines the era and year-of-era into a proleptic
* year before calling {@link #date(int, int, int)}.
*
* @param era the era of the correct type for the chronology, not null
* @param yearOfEra the chronology year-of-era
* @param month the chronology month-of-year
* @param dayOfMonth the chronology day-of-month
* @return the local date in this chronology, not null
* @throws DateTimeException if unable to create the date
* @throws ClassCastException if the {@code era} is not of the correct type for the chronology
*/
default ChronoLocalDate date(Era era, int yearOfEra, int month, int dayOfMonth) {
return date(prolepticYear(era, yearOfEra), month, dayOfMonth);
}
/**
* Obtains a local date in this chronology from the proleptic-year,
* month-of-year and day-of-month fields.
*
* @param prolepticYear the chronology proleptic-year
* @param month the chronology month-of-year
* @param dayOfMonth the chronology day-of-month
* @return the local date in this chronology, not null
* @throws DateTimeException if unable to create the date
*/
ChronoLocalDate date(int prolepticYear, int month, int dayOfMonth);
/**
* Obtains a local date in this chronology from the era, year-of-era and
* day-of-year fields.
*
* @implSpec
* The default implementation combines the era and year-of-era into a proleptic
* year before calling {@link #dateYearDay(int, int)}.
*
* @param era the era of the correct type for the chronology, not null
* @param yearOfEra the chronology year-of-era
* @param dayOfYear the chronology day-of-year
* @return the local date in this chronology, not null
* @throws DateTimeException if unable to create the date
* @throws ClassCastException if the {@code era} is not of the correct type for the chronology
*/
default ChronoLocalDate dateYearDay(Era era, int yearOfEra, int dayOfYear) {
return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear);
}
/**
* Obtains a local date in this chronology from the proleptic-year and
* day-of-year fields.
*
* @param prolepticYear the chronology proleptic-year
* @param dayOfYear the chronology day-of-year
* @return the local date in this chronology, not null
* @throws DateTimeException if unable to create the date
*/
ChronoLocalDate dateYearDay(int prolepticYear, int dayOfYear);
/**
* Obtains a local date in this chronology from the epoch-day.
* <p>
* The definition of {@link ChronoField#EPOCH_DAY EPOCH_DAY} is the same
* for all calendar systems, thus it can be used for conversion.
*
* @param epochDay the epoch day
* @return the local date in this chronology, not null
* @throws DateTimeException if unable to create the date
*/
ChronoLocalDate dateEpochDay(long epochDay);
//-----------------------------------------------------------------------
/**
* Obtains the current local date in this chronology from the system clock in the default time-zone.
* <p>
* This will query the {@link Clock#systemDefaultZone() system clock} in the default
* time-zone to obtain the current date.
* <p>
* Using this method will prevent the ability to use an alternate clock for testing
* because the clock is hard-coded.
*
* @implSpec
* The default implementation invokes {@link #dateNow(Clock)}.
*
* @return the current local date using the system clock and default time-zone, not null
* @throws DateTimeException if unable to create the date
*/
default ChronoLocalDate dateNow() {
return dateNow(Clock.systemDefaultZone());
}
/**
* Obtains the current local date in this chronology from the system clock in the specified time-zone.
* <p>
* This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date.
* Specifying the time-zone avoids dependence on the default time-zone.
* <p>
* Using this method will prevent the ability to use an alternate clock for testing
* because the clock is hard-coded.
*
* @implSpec
* The default implementation invokes {@link #dateNow(Clock)}.
*
* @param zone the zone ID to use, not null
* @return the current local date using the system clock, not null
* @throws DateTimeException if unable to create the date
*/
default ChronoLocalDate dateNow(ZoneId zone) {
return dateNow(Clock.system(zone));
}
/**
* Obtains the current local date in this chronology from the specified clock.
* <p>
* This will query the specified clock to obtain the current date - today.
* Using this method allows the use of an alternate clock for testing.
* The alternate clock may be introduced using {@link Clock dependency injection}.
*
* @implSpec
* The default implementation invokes {@link #date(TemporalAccessor)}.
*
* @param clock the clock to use, not null
* @return the current local date, not null
* @throws DateTimeException if unable to create the date
*/
default ChronoLocalDate dateNow(Clock clock) {
Objects.requireNonNull(clock, "clock");
return date(LocalDate.now(clock));
}
//-----------------------------------------------------------------------
/**
* Obtains a local date in this chronology from another temporal object.
* <p>
* This obtains a date in this chronology based on the specified temporal.
* A {@code TemporalAccessor} represents an arbitrary set of date and time information,
* which this factory converts to an instance of {@code ChronoLocalDate}.
* <p>
* The conversion typically uses the {@link ChronoField#EPOCH_DAY EPOCH_DAY}
* field, which is standardized across calendar systems.
* <p>
* This method matches the signature of the functional interface {@link TemporalQuery}
* allowing it to be used as a query via method reference, {@code aChronology::date}.
*
* @param temporal the temporal object to convert, not null
* @return the local date in this chronology, not null
* @throws DateTimeException if unable to create the date
* @see ChronoLocalDate#from(TemporalAccessor)
*/
ChronoLocalDate date(TemporalAccessor temporal);
/**
* Obtains a local date-time in this chronology from another temporal object.
* <p>
* This obtains a date-time in this chronology based on the specified temporal.
* A {@code TemporalAccessor} represents an arbitrary set of date and time information,
* which this factory converts to an instance of {@code ChronoLocalDateTime}.
* <p>
* The conversion extracts and combines the {@code ChronoLocalDate} and the
* {@code LocalTime} from the temporal object.
* Implementations are permitted to perform optimizations such as accessing
* those fields that are equivalent to the relevant objects.
* The result uses this chronology.
* <p>
* This method matches the signature of the functional interface {@link TemporalQuery}
* allowing it to be used as a query via method reference, {@code aChronology::localDateTime}.
*
* @param temporal the temporal object to convert, not null
* @return the local date-time in this chronology, not null
* @throws DateTimeException if unable to create the date-time
* @see ChronoLocalDateTime#from(TemporalAccessor)
*/
default ChronoLocalDateTime<? extends ChronoLocalDate> localDateTime(TemporalAccessor temporal) {
try {
return date(temporal).atTime(LocalTime.from(temporal));
} catch (DateTimeException ex) {
throw new DateTimeException("Unable to obtain ChronoLocalDateTime from TemporalAccessor: " + temporal.getClass(), ex);
}
}
/**
* Obtains a {@code ChronoZonedDateTime} in this chronology from another temporal object.
* <p>
* This obtains a zoned date-time in this chronology based on the specified temporal.
* A {@code TemporalAccessor} represents an arbitrary set of date and time information,
* which this factory converts to an instance of {@code ChronoZonedDateTime}.
* <p>
* The conversion will first obtain a {@code ZoneId} from the temporal object,
* falling back to a {@code ZoneOffset} if necessary. It will then try to obtain
* an {@code Instant}, falling back to a {@code ChronoLocalDateTime} if necessary.
* The result will be either the combination of {@code ZoneId} or {@code ZoneOffset}
* with {@code Instant} or {@code ChronoLocalDateTime}.
* Implementations are permitted to perform optimizations such as accessing
* those fields that are equivalent to the relevant objects.
* The result uses this chronology.
* <p>
* This method matches the signature of the functional interface {@link TemporalQuery}
* allowing it to be used as a query via method reference, {@code aChronology::zonedDateTime}.
*
* @param temporal the temporal object to convert, not null
* @return the zoned date-time in this chronology, not null
* @throws DateTimeException if unable to create the date-time
* @see ChronoZonedDateTime#from(TemporalAccessor)
*/
default ChronoZonedDateTime<? extends ChronoLocalDate> zonedDateTime(TemporalAccessor temporal) {
try {
ZoneId zone = ZoneId.from(temporal);
try {
Instant instant = Instant.from(temporal);
return zonedDateTime(instant, zone);
} catch (DateTimeException ex1) {
ChronoLocalDateTimeImpl<?> cldt = ChronoLocalDateTimeImpl.ensureValid(this, localDateTime(temporal));
return ChronoZonedDateTimeImpl.ofBest(cldt, zone, null);
}
} catch (DateTimeException ex) {
throw new DateTimeException("Unable to obtain ChronoZonedDateTime from TemporalAccessor: " + temporal.getClass(), ex);
}
}
/**
* Obtains a {@code ChronoZonedDateTime} in this chronology from an {@code Instant}.
* <p>
* This obtains a zoned date-time with the same instant as that specified.
*
* @param instant the instant to create the date-time from, not null
* @param zone the time-zone, not null
* @return the zoned date-time, not null
* @throws DateTimeException if the result exceeds the supported range
*/
default ChronoZonedDateTime<? extends ChronoLocalDate> zonedDateTime(Instant instant, ZoneId zone) {
return ChronoZonedDateTimeImpl.ofInstant(this, instant, zone);
}
//-----------------------------------------------------------------------
/**
* Checks if the specified year is a leap year.
* <p>
* A leap-year is a year of a longer length than normal.
* The exact meaning is determined by the chronology according to the following constraints.
* <ul>
* <li>a leap-year must imply a year-length longer than a non leap-year.
* <li>a chronology that does not support the concept of a year must return false.
* <li>the correct result must be returned for all years within the
* valid range of years for the chronology.
* </ul>
* <p>
* Outside the range of valid years an implementation is free to return
* either a best guess or false.
* An implementation must not throw an exception, even if the year is
* outside the range of valid years.
*
* @param prolepticYear the proleptic-year to check, not validated for range
* @return true if the year is a leap year
*/
boolean isLeapYear(long prolepticYear);
/**
* Calculates the proleptic-year given the era and year-of-era.
* <p>
* This combines the era and year-of-era into the single proleptic-year field.
* <p>
* If the chronology makes active use of eras, such as {@code JapaneseChronology}
* then the year-of-era will be validated against the era.
* For other chronologies, validation is optional.
*
* @param era the era of the correct type for the chronology, not null
* @param yearOfEra the chronology year-of-era
* @return the proleptic-year
* @throws DateTimeException if unable to convert to a proleptic-year,
* such as if the year is invalid for the era
* @throws ClassCastException if the {@code era} is not of the correct type for the chronology
*/
int prolepticYear(Era era, int yearOfEra);
/**
* Creates the chronology era object from the numeric value.
* <p>
* The era is, conceptually, the largest division of the time-line.
* Most calendar systems have a single epoch dividing the time-line into two eras.
* However, some have multiple eras, such as one for the reign of each leader.
* The exact meaning is determined by the chronology according to the following constraints.
* <p>
* The era in use at 1970-01-01 must have the value 1.
* Later eras must have sequentially higher values.
* Earlier eras must have sequentially lower values.
* Each chronology must refer to an enum or similar singleton to provide the era values.
* <p>
* This method returns the singleton era of the correct type for the specified era value.
*
* @param eraValue the era value
* @return the calendar system era, not null
* @throws DateTimeException if unable to create the era
*/
Era eraOf(int eraValue);
/**
* Gets the list of eras for the chronology.
* <p>
* Most calendar systems have an era, within which the year has meaning.
* If the calendar system does not support the concept of eras, an empty
* list must be returned.
*
* @return the list of eras for the chronology, may be immutable, not null
*/
List<Era> eras();
//-----------------------------------------------------------------------
/**
* Gets the range of valid values for the specified field.
* <p>
* All fields can be expressed as a {@code long} integer.
* This method returns an object that describes the valid range for that value.
* <p>
* Note that the result only describes the minimum and maximum valid values
* and it is important not to read too much into them. For example, there
* could be values within the range that are invalid for the field.
* <p>
* This method will return a result whether or not the chronology supports the field.
*
* @param field the field to get the range for, not null
* @return the range of valid values for the field, not null
* @throws DateTimeException if the range for the field cannot be obtained
*/
ValueRange range(ChronoField field);
//-----------------------------------------------------------------------
/**
* Gets the textual representation of this chronology.
* <p>
* This returns the textual name used to identify the chronology,
* suitable for presentation to the user.
* The parameters control the style of the returned text and the locale.
*
* @implSpec
* The default implementation behaves as though the formatter was used to
* format the chronology textual name.
*
* @param style the style of the text required, not null
* @param locale the locale to use, not null
* @return the text value of the chronology, not null
*/
default String getDisplayName(TextStyle style, Locale locale) {
TemporalAccessor temporal = new TemporalAccessor() {
@Override
public boolean isSupported(TemporalField field) {
return false;
}
@Override
public long getLong(TemporalField field) {
throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
@SuppressWarnings("unchecked")
@Override
public <R> R query(TemporalQuery<R> query) {
if (query == TemporalQueries.chronology()) {
return (R) Chronology.this;
}
return TemporalAccessor.super.query(query);
}
};
return new DateTimeFormatterBuilder().appendChronologyText(style).toFormatter(locale).format(temporal);
}
//-----------------------------------------------------------------------
/**
* Resolves parsed {@code ChronoField} values into a date during parsing.
* <p>
* Most {@code TemporalField} implementations are resolved using the
* resolve method on the field. By contrast, the {@code ChronoField} class
* defines fields that only have meaning relative to the chronology.
* As such, {@code ChronoField} date fields are resolved here in the
* context of a specific chronology.
* <p>
* The default implementation, which explains typical resolve behaviour,
* is provided in {@link AbstractChronology}.
*
* @param fieldValues the map of fields to values, which can be updated, not null
* @param resolverStyle the requested type of resolve, not null
* @return the resolved date, null if insufficient information to create a date
* @throws DateTimeException if the date cannot be resolved, typically
* because of a conflict in the input data
*/
ChronoLocalDate resolveDate(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle);
//-----------------------------------------------------------------------
/**
* Obtains a period for this chronology based on years, months and days.
* <p>
* This returns a period tied to this chronology using the specified
* years, months and days. All supplied chronologies use periods
* based on years, months and days, however the {@code ChronoPeriod} API
* allows the period to be represented using other units.
*
* @implSpec
* The default implementation returns an implementation class suitable
* for most calendar systems. It is based solely on the three units.
* Normalization, addition and subtraction derive the number of months
* in a year from the {@link #range(ChronoField)}. If the number of
* months within a year is fixed, then the calculation approach for
* addition, subtraction and normalization is slightly different.
* <p>
* If implementing an unusual calendar system that is not based on
* years, months and days, or where you want direct control, then
* the {@code ChronoPeriod} interface must be directly implemented.
* <p>
* The returned period is immutable and thread-safe.
*
* @param years the number of years, may be negative
* @param months the number of years, may be negative
* @param days the number of years, may be negative
* @return the period in terms of this chronology, not null
*/
default ChronoPeriod period(int years, int months, int days) {
return new ChronoPeriodImpl(this, years, months, days);
}
//---------------------------------------------------------------------
/**
* Gets the number of seconds from the epoch of 1970-01-01T00:00:00Z.
* <p>
* The number of seconds is calculated using the proleptic-year,
* month, day-of-month, hour, minute, second, and zoneOffset.
*
* @param prolepticYear the chronology proleptic-year
* @param month the chronology month-of-year
* @param dayOfMonth the chronology day-of-month
* @param hour the hour-of-day, from 0 to 23
* @param minute the minute-of-hour, from 0 to 59
* @param second the second-of-minute, from 0 to 59
* @param zoneOffset the zone offset, not null
* @return the number of seconds relative to 1970-01-01T00:00:00Z, may be negative
* @throws DateTimeException if any of the values are out of range
* @since 9
*/
public default long epochSecond(int prolepticYear, int month, int dayOfMonth,
int hour, int minute, int second, ZoneOffset zoneOffset) {
Objects.requireNonNull(zoneOffset, "zoneOffset");
HOUR_OF_DAY.checkValidValue(hour);
MINUTE_OF_HOUR.checkValidValue(minute);
SECOND_OF_MINUTE.checkValidValue(second);
long daysInSec = Math.multiplyExact(date(prolepticYear, month, dayOfMonth).toEpochDay(), 86400);
long timeinSec = (hour * 60 + minute) * 60 + second;
return Math.addExact(daysInSec, timeinSec - zoneOffset.getTotalSeconds());
}
/**
* Gets the number of seconds from the epoch of 1970-01-01T00:00:00Z.
* <p>
* The number of seconds is calculated using the era, year-of-era,
* month, day-of-month, hour, minute, second, and zoneOffset.
*
* @param era the era of the correct type for the chronology, not null
* @param yearOfEra the chronology year-of-era
* @param month the chronology month-of-year
* @param dayOfMonth the chronology day-of-month
* @param hour the hour-of-day, from 0 to 23
* @param minute the minute-of-hour, from 0 to 59
* @param second the second-of-minute, from 0 to 59
* @param zoneOffset the zone offset, not null
* @return the number of seconds relative to 1970-01-01T00:00:00Z, may be negative
* @throws DateTimeException if any of the values are out of range
* @since 9
*/
public default long epochSecond(Era era, int yearOfEra, int month, int dayOfMonth,
int hour, int minute, int second, ZoneOffset zoneOffset) {
Objects.requireNonNull(era, "era");
return epochSecond(prolepticYear(era, yearOfEra), month, dayOfMonth, hour, minute, second, zoneOffset);
}
//-----------------------------------------------------------------------
/**
* Compares this chronology to another chronology.
* <p>
* The comparison order first by the chronology ID string, then by any
* additional information specific to the subclass.
* It is "consistent with equals", as defined by {@link Comparable}.
*
* @param other the other chronology to compare to, not null
* @return the comparator value, negative if less, positive if greater
*/
@Override
int compareTo(Chronology other);
/**
* Checks if this chronology is equal to another chronology.
* <p>
* The comparison is based on the entire state of the object.
*
* @param obj the object to check, null returns false
* @return true if this is equal to the other chronology
*/
@Override
boolean equals(Object obj);
/**
* A hash code for this chronology.
* <p>
* The hash code should be based on the entire state of the object.
*
* @return a suitable hash code
*/
@Override
int hashCode();
//-----------------------------------------------------------------------
/**
* Outputs this chronology as a {@code String}.
* <p>
* The format should include the entire state of the object.
*
* @return a string representation of this chronology, not null
*/
@Override
String toString();
}

View file

@ -0,0 +1,325 @@
/*
* Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* This file is available under and governed by the GNU General Public
* License version 2 only, as published by the Free Software Foundation.
* However, the following notice accompanied the original version of this
* file:
*
* Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of JSR-310 nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package java.time.chrono;
import static java.time.temporal.ChronoField.ERA;
import static java.time.temporal.ChronoUnit.ERAS;
import java.time.DateTimeException;
import java.time.temporal.UnsupportedTemporalTypeException;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.TextStyle;
import java.time.temporal.ChronoField;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalAdjuster;
import java.time.temporal.TemporalField;
import java.time.temporal.TemporalQueries;
import java.time.temporal.TemporalQuery;
import java.time.temporal.ValueRange;
import java.util.Locale;
/**
* An era of the time-line.
* <p>
* Most calendar systems have a single epoch dividing the time-line into two eras.
* However, some calendar systems, have multiple eras, such as one for the reign
* of each leader.
* In all cases, the era is conceptually the largest division of the time-line.
* Each chronology defines the Era's that are known Eras and a
* {@link Chronology#eras Chronology.eras} to get the valid eras.
* <p>
* For example, the Thai Buddhist calendar system divides time into two eras,
* before and after a single date. By contrast, the Japanese calendar system
* has one era for the reign of each Emperor.
* <p>
* Instances of {@code Era} may be compared using the {@code ==} operator.
*
* @implSpec
* This interface must be implemented with care to ensure other classes operate correctly.
* All implementations must be singletons - final, immutable and thread-safe.
* It is recommended to use an enum whenever possible.
*
* @since 1.8
*/
public interface Era extends TemporalAccessor, TemporalAdjuster {
/**
* Gets the numeric value associated with the era as defined by the chronology.
* Each chronology defines the predefined Eras and methods to list the Eras
* of the chronology.
* <p>
* All fields, including eras, have an associated numeric value.
* The meaning of the numeric value for era is determined by the chronology
* according to these principles:
* <ul>
* <li>The era in use at the epoch 1970-01-01 (ISO) has the value 1.
* <li>Later eras have sequentially higher values.
* <li>Earlier eras have sequentially lower values, which may be negative.
* </ul>
*
* @return the numeric era value
*/
int getValue();
//-----------------------------------------------------------------------
/**
* Checks if the specified field is supported.
* <p>
* This checks if this era can be queried for the specified field.
* If false, then calling the {@link #range(TemporalField) range} and
* {@link #get(TemporalField) get} methods will throw an exception.
* <p>
* If the field is a {@link ChronoField} then the query is implemented here.
* The {@code ERA} field returns true.
* All other {@code ChronoField} instances will return false.
* <p>
* If the field is not a {@code ChronoField}, then the result of this method
* is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}
* passing {@code this} as the argument.
* Whether the field is supported is determined by the field.
*
* @param field the field to check, null returns false
* @return true if the field is supported on this era, false if not
*/
@Override
default boolean isSupported(TemporalField field) {
if (field instanceof ChronoField) {
return field == ERA;
}
return field != null && field.isSupportedBy(this);
}
/**
* Gets the range of valid values for the specified field.
* <p>
* The range object expresses the minimum and maximum valid values for a field.
* This era is used to enhance the accuracy of the returned range.
* If it is not possible to return the range, because the field is not supported
* or for some other reason, an exception is thrown.
* <p>
* If the field is a {@link ChronoField} then the query is implemented here.
* The {@code ERA} field returns the range.
* All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
* <p>
* If the field is not a {@code ChronoField}, then the result of this method
* is obtained by invoking {@code TemporalField.rangeRefinedBy(TemporalAccessor)}
* passing {@code this} as the argument.
* Whether the range can be obtained is determined by the field.
* <p>
* The default implementation must return a range for {@code ERA} from
* zero to one, suitable for two era calendar systems such as ISO.
*
* @param field the field to query the range for, not null
* @return the range of valid values for the field, not null
* @throws DateTimeException if the range for the field cannot be obtained
* @throws UnsupportedTemporalTypeException if the unit is not supported
*/
@Override // override for Javadoc
default ValueRange range(TemporalField field) {
return TemporalAccessor.super.range(field);
}
/**
* Gets the value of the specified field from this era as an {@code int}.
* <p>
* This queries this era for the value of the specified field.
* The returned value will always be within the valid range of values for the field.
* If it is not possible to return the value, because the field is not supported
* or for some other reason, an exception is thrown.
* <p>
* If the field is a {@link ChronoField} then the query is implemented here.
* The {@code ERA} field returns the value of the era.
* All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
* <p>
* If the field is not a {@code ChronoField}, then the result of this method
* is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
* passing {@code this} as the argument. Whether the value can be obtained,
* and what the value represents, is determined by the field.
*
* @param field the field to get, not null
* @return the value for the field
* @throws DateTimeException if a value for the field cannot be obtained or
* the value is outside the range of valid values for the field
* @throws UnsupportedTemporalTypeException if the field is not supported or
* the range of values exceeds an {@code int}
* @throws ArithmeticException if numeric overflow occurs
*/
@Override // override for Javadoc and performance
default int get(TemporalField field) {
if (field == ERA) {
return getValue();
}
return TemporalAccessor.super.get(field);
}
/**
* Gets the value of the specified field from this era as a {@code long}.
* <p>
* This queries this era for the value of the specified field.
* If it is not possible to return the value, because the field is not supported
* or for some other reason, an exception is thrown.
* <p>
* If the field is a {@link ChronoField} then the query is implemented here.
* The {@code ERA} field returns the value of the era.
* All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
* <p>
* If the field is not a {@code ChronoField}, then the result of this method
* is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
* passing {@code this} as the argument. Whether the value can be obtained,
* and what the value represents, is determined by the field.
*
* @param field the field to get, not null
* @return the value for the field
* @throws DateTimeException if a value for the field cannot be obtained
* @throws UnsupportedTemporalTypeException if the field is not supported
* @throws ArithmeticException if numeric overflow occurs
*/
@Override
default long getLong(TemporalField field) {
if (field == ERA) {
return getValue();
} else if (field instanceof ChronoField) {
throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
return field.getFrom(this);
}
//-----------------------------------------------------------------------
/**
* Queries this era using the specified query.
* <p>
* This queries this era using the specified query strategy object.
* The {@code TemporalQuery} object defines the logic to be used to
* obtain the result. Read the documentation of the query to understand
* what the result of this method will be.
* <p>
* The result of this method is obtained by invoking the
* {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the
* specified query passing {@code this} as the argument.
*
* @param <R> the type of the result
* @param query the query to invoke, not null
* @return the query result, null may be returned (defined by the query)
* @throws DateTimeException if unable to query (defined by the query)
* @throws ArithmeticException if numeric overflow occurs (defined by the query)
*/
@SuppressWarnings("unchecked")
@Override
default <R> R query(TemporalQuery<R> query) {
if (query == TemporalQueries.precision()) {
return (R) ERAS;
}
return TemporalAccessor.super.query(query);
}
/**
* Adjusts the specified temporal object to have the same era as this object.
* <p>
* This returns a temporal object of the same observable type as the input
* with the era changed to be the same as this.
* <p>
* The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)}
* passing {@link ChronoField#ERA} as the field.
* <p>
* In most cases, it is clearer to reverse the calling pattern by using
* {@link Temporal#with(TemporalAdjuster)}:
* <pre>
* // these two lines are equivalent, but the second approach is recommended
* temporal = thisEra.adjustInto(temporal);
* temporal = temporal.with(thisEra);
* </pre>
* <p>
* This instance is immutable and unaffected by this method call.
*
* @param temporal the target object to be adjusted, not null
* @return the adjusted object, not null
* @throws DateTimeException if unable to make the adjustment
* @throws ArithmeticException if numeric overflow occurs
*/
@Override
default Temporal adjustInto(Temporal temporal) {
return temporal.with(ERA, getValue());
}
//-----------------------------------------------------------------------
/**
* Gets the textual representation of this era.
* <p>
* This returns the textual name used to identify the era,
* suitable for presentation to the user.
* The parameters control the style of the returned text and the locale.
* <p>
* If no textual mapping is found then the {@link #getValue() numeric value} is returned.
*
* @apiNote This default implementation is suitable for most implementations.
*
* @param style the style of the text required, not null
* @param locale the locale to use, not null
* @return the text value of the era, not null
*/
default String getDisplayName(TextStyle style, Locale locale) {
return new DateTimeFormatterBuilder().appendText(ERA, style).toFormatter(locale).format(this);
}
// NOTE: methods to convert year-of-era/proleptic-year cannot be here as they may depend on month/day (Japanese)
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,698 @@
/*
* Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of JSR-310 nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package java.time.chrono;
import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH;
import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR;
import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH;
import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR;
import static java.time.temporal.ChronoField.DAY_OF_MONTH;
import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
import static java.time.temporal.ChronoField.YEAR;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.Serializable;
import java.time.Clock;
import java.time.DateTimeException;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalAdjuster;
import java.time.temporal.TemporalAmount;
import java.time.temporal.TemporalField;
import java.time.temporal.TemporalQuery;
import java.time.temporal.TemporalUnit;
import java.time.temporal.UnsupportedTemporalTypeException;
import java.time.temporal.ValueRange;
/**
* A date in the Hijrah calendar system.
* <p>
* This date operates using one of several variants of the
* {@linkplain HijrahChronology Hijrah calendar}.
* <p>
* The Hijrah calendar has a different total of days in a year than
* Gregorian calendar, and the length of each month is based on the period
* of a complete revolution of the moon around the earth
* (as between successive new moons).
* Refer to the {@link HijrahChronology} for details of supported variants.
* <p>
* Each HijrahDate is created bound to a particular HijrahChronology,
* The same chronology is propagated to each HijrahDate computed from the date.
* To use a different Hijrah variant, its HijrahChronology can be used
* to create new HijrahDate instances.
* Alternatively, the {@link #withVariant} method can be used to convert
* to a new HijrahChronology.
*
* <p>
* This is a <a href="{@docRoot}/java/lang/doc-files/ValueBased.html">value-based</a>
* class; use of identity-sensitive operations (including reference equality
* ({@code ==}), identity hash code, or synchronization) on instances of
* {@code HijrahDate} may have unpredictable results and should be avoided.
* The {@code equals} method should be used for comparisons.
*
* @implSpec
* This class is immutable and thread-safe.
*
* @since 1.8
*/
public final class HijrahDate
extends ChronoLocalDateImpl<HijrahDate>
implements ChronoLocalDate, Serializable {
/**
* Serialization version.
*/
private static final long serialVersionUID = -5207853542612002020L;
/**
* The Chronology of this HijrahDate.
*/
private final transient HijrahChronology chrono;
/**
* The proleptic year.
*/
private final transient int prolepticYear;
/**
* The month-of-year.
*/
private final transient int monthOfYear;
/**
* The day-of-month.
*/
private final transient int dayOfMonth;
//-------------------------------------------------------------------------
/**
* Obtains an instance of {@code HijrahDate} from the Hijrah proleptic year,
* month-of-year and day-of-month.
*
* @param prolepticYear the proleptic year to represent in the Hijrah calendar
* @param monthOfYear the month-of-year to represent, from 1 to 12
* @param dayOfMonth the day-of-month to represent, from 1 to 30
* @return the Hijrah date, never null
* @throws DateTimeException if the value of any field is out of range
*/
static HijrahDate of(HijrahChronology chrono, int prolepticYear, int monthOfYear, int dayOfMonth) {
return new HijrahDate(chrono, prolepticYear, monthOfYear, dayOfMonth);
}
/**
* Returns a HijrahDate for the chronology and epochDay.
* @param chrono The Hijrah chronology
* @param epochDay the epoch day
* @return a HijrahDate for the epoch day; non-null
*/
static HijrahDate ofEpochDay(HijrahChronology chrono, long epochDay) {
return new HijrahDate(chrono, epochDay);
}
//-----------------------------------------------------------------------
/**
* Obtains the current {@code HijrahDate} of the Islamic Umm Al-Qura calendar
* in the default time-zone.
* <p>
* This will query the {@link Clock#systemDefaultZone() system clock} in the default
* time-zone to obtain the current date.
* <p>
* Using this method will prevent the ability to use an alternate clock for testing
* because the clock is hard-coded.
*
* @return the current date using the system clock and default time-zone, not null
*/
public static HijrahDate now() {
return now(Clock.systemDefaultZone());
}
/**
* Obtains the current {@code HijrahDate} of the Islamic Umm Al-Qura calendar
* in the specified time-zone.
* <p>
* This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date.
* Specifying the time-zone avoids dependence on the default time-zone.
* <p>
* Using this method will prevent the ability to use an alternate clock for testing
* because the clock is hard-coded.
*
* @param zone the zone ID to use, not null
* @return the current date using the system clock, not null
*/
public static HijrahDate now(ZoneId zone) {
return now(Clock.system(zone));
}
/**
* Obtains the current {@code HijrahDate} of the Islamic Umm Al-Qura calendar
* from the specified clock.
* <p>
* This will query the specified clock to obtain the current date - today.
* Using this method allows the use of an alternate clock for testing.
* The alternate clock may be introduced using {@linkplain Clock dependency injection}.
*
* @param clock the clock to use, not null
* @return the current date, not null
* @throws DateTimeException if the current date cannot be obtained
*/
public static HijrahDate now(Clock clock) {
return HijrahDate.ofEpochDay(HijrahChronology.INSTANCE, LocalDate.now(clock).toEpochDay());
}
/**
* Obtains a {@code HijrahDate} of the Islamic Umm Al-Qura calendar
* from the proleptic-year, month-of-year and day-of-month fields.
* <p>
* This returns a {@code HijrahDate} with the specified fields.
* The day must be valid for the year and month, otherwise an exception will be thrown.
*
* @param prolepticYear the Hijrah proleptic-year
* @param month the Hijrah month-of-year, from 1 to 12
* @param dayOfMonth the Hijrah day-of-month, from 1 to 30
* @return the date in Hijrah calendar system, not null
* @throws DateTimeException if the value of any field is out of range,
* or if the day-of-month is invalid for the month-year
*/
public static HijrahDate of(int prolepticYear, int month, int dayOfMonth) {
return HijrahChronology.INSTANCE.date(prolepticYear, month, dayOfMonth);
}
/**
* Obtains a {@code HijrahDate} of the Islamic Umm Al-Qura calendar from a temporal object.
* <p>
* This obtains a date in the Hijrah calendar system based on the specified temporal.
* A {@code TemporalAccessor} represents an arbitrary set of date and time information,
* which this factory converts to an instance of {@code HijrahDate}.
* <p>
* The conversion typically uses the {@link ChronoField#EPOCH_DAY EPOCH_DAY}
* field, which is standardized across calendar systems.
* <p>
* This method matches the signature of the functional interface {@link TemporalQuery}
* allowing it to be used as a query via method reference, {@code HijrahDate::from}.
*
* @param temporal the temporal object to convert, not null
* @return the date in Hijrah calendar system, not null
* @throws DateTimeException if unable to convert to a {@code HijrahDate}
*/
public static HijrahDate from(TemporalAccessor temporal) {
return HijrahChronology.INSTANCE.date(temporal);
}
//-----------------------------------------------------------------------
/**
* Constructs an {@code HijrahDate} with the proleptic-year, month-of-year and
* day-of-month fields.
*
* @param chrono The chronology to create the date with
* @param prolepticYear the proleptic year
* @param monthOfYear the month of year
* @param dayOfMonth the day of month
*/
private HijrahDate(HijrahChronology chrono, int prolepticYear, int monthOfYear, int dayOfMonth) {
// Computing the Gregorian day checks the valid ranges
chrono.getEpochDay(prolepticYear, monthOfYear, dayOfMonth);
this.chrono = chrono;
this.prolepticYear = prolepticYear;
this.monthOfYear = monthOfYear;
this.dayOfMonth = dayOfMonth;
}
/**
* Constructs an instance with the Epoch Day.
*
* @param epochDay the epochDay
*/
private HijrahDate(HijrahChronology chrono, long epochDay) {
int[] dateInfo = chrono.getHijrahDateInfo((int)epochDay);
this.chrono = chrono;
this.prolepticYear = dateInfo[0];
this.monthOfYear = dateInfo[1];
this.dayOfMonth = dateInfo[2];
}
//-----------------------------------------------------------------------
/**
* Gets the chronology of this date, which is the Hijrah calendar system.
* <p>
* The {@code Chronology} represents the calendar system in use.
* The era and other fields in {@link ChronoField} are defined by the chronology.
*
* @return the Hijrah chronology, not null
*/
@Override
public HijrahChronology getChronology() {
return chrono;
}
/**
* Gets the era applicable at this date.
* <p>
* The Hijrah calendar system has one era, 'AH',
* defined by {@link HijrahEra}.
*
* @return the era applicable at this date, not null
*/
@Override
public HijrahEra getEra() {
return HijrahEra.AH;
}
/**
* Returns the length of the month represented by this date.
* <p>
* This returns the length of the month in days.
* Month lengths in the Hijrah calendar system vary between 29 and 30 days.
*
* @return the length of the month in days
*/
@Override
public int lengthOfMonth() {
return chrono.getMonthLength(prolepticYear, monthOfYear);
}
/**
* Returns the length of the year represented by this date.
* <p>
* This returns the length of the year in days.
* A Hijrah calendar system year is typically shorter than
* that of the ISO calendar system.
*
* @return the length of the year in days
*/
@Override
public int lengthOfYear() {
return chrono.getYearLength(prolepticYear);
}
//-----------------------------------------------------------------------
@Override
public ValueRange range(TemporalField field) {
if (field instanceof ChronoField) {
if (isSupported(field)) {
ChronoField f = (ChronoField) field;
switch (f) {
case DAY_OF_MONTH: return ValueRange.of(1, lengthOfMonth());
case DAY_OF_YEAR: return ValueRange.of(1, lengthOfYear());
case ALIGNED_WEEK_OF_MONTH: return ValueRange.of(1, 5); // TODO
// TODO does the limited range of valid years cause years to
// start/end part way through? that would affect range
}
return getChronology().range(f);
}
throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
return field.rangeRefinedBy(this);
}
@Override
public long getLong(TemporalField field) {
if (field instanceof ChronoField) {
switch ((ChronoField) field) {
case DAY_OF_WEEK: return getDayOfWeek();
case ALIGNED_DAY_OF_WEEK_IN_MONTH: return ((dayOfMonth - 1) % 7) + 1;
case ALIGNED_DAY_OF_WEEK_IN_YEAR: return ((getDayOfYear() - 1) % 7) + 1;
case DAY_OF_MONTH: return this.dayOfMonth;
case DAY_OF_YEAR: return this.getDayOfYear();
case EPOCH_DAY: return toEpochDay();
case ALIGNED_WEEK_OF_MONTH: return ((dayOfMonth - 1) / 7) + 1;
case ALIGNED_WEEK_OF_YEAR: return ((getDayOfYear() - 1) / 7) + 1;
case MONTH_OF_YEAR: return monthOfYear;
case PROLEPTIC_MONTH: return getProlepticMonth();
case YEAR_OF_ERA: return prolepticYear;
case YEAR: return prolepticYear;
case ERA: return getEraValue();
}
throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
return field.getFrom(this);
}
private long getProlepticMonth() {
return prolepticYear * 12L + monthOfYear - 1;
}
@Override
public HijrahDate with(TemporalField field, long newValue) {
if (field instanceof ChronoField) {
ChronoField f = (ChronoField) field;
// not using checkValidIntValue so EPOCH_DAY and PROLEPTIC_MONTH work
chrono.range(f).checkValidValue(newValue, f); // TODO: validate value
int nvalue = (int) newValue;
switch (f) {
case DAY_OF_WEEK: return plusDays(newValue - getDayOfWeek());
case ALIGNED_DAY_OF_WEEK_IN_MONTH: return plusDays(newValue - getLong(ALIGNED_DAY_OF_WEEK_IN_MONTH));
case ALIGNED_DAY_OF_WEEK_IN_YEAR: return plusDays(newValue - getLong(ALIGNED_DAY_OF_WEEK_IN_YEAR));
case DAY_OF_MONTH: return resolvePreviousValid(prolepticYear, monthOfYear, nvalue);
case DAY_OF_YEAR: return plusDays(Math.min(nvalue, lengthOfYear()) - getDayOfYear());
case EPOCH_DAY: return new HijrahDate(chrono, newValue);
case ALIGNED_WEEK_OF_MONTH: return plusDays((newValue - getLong(ALIGNED_WEEK_OF_MONTH)) * 7);
case ALIGNED_WEEK_OF_YEAR: return plusDays((newValue - getLong(ALIGNED_WEEK_OF_YEAR)) * 7);
case MONTH_OF_YEAR: return resolvePreviousValid(prolepticYear, nvalue, dayOfMonth);
case PROLEPTIC_MONTH: return plusMonths(newValue - getProlepticMonth());
case YEAR_OF_ERA: return resolvePreviousValid(prolepticYear >= 1 ? nvalue : 1 - nvalue, monthOfYear, dayOfMonth);
case YEAR: return resolvePreviousValid(nvalue, monthOfYear, dayOfMonth);
case ERA: return resolvePreviousValid(1 - prolepticYear, monthOfYear, dayOfMonth);
}
throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
return super.with(field, newValue);
}
private HijrahDate resolvePreviousValid(int prolepticYear, int month, int day) {
int monthDays = chrono.getMonthLength(prolepticYear, month);
if (day > monthDays) {
day = monthDays;
}
return HijrahDate.of(chrono, prolepticYear, month, day);
}
/**
* {@inheritDoc}
* @throws DateTimeException if unable to make the adjustment.
* For example, if the adjuster requires an ISO chronology
* @throws ArithmeticException {@inheritDoc}
*/
@Override
public HijrahDate with(TemporalAdjuster adjuster) {
return super.with(adjuster);
}
/**
* Returns a {@code HijrahDate} with the Chronology requested.
* <p>
* The year, month, and day are checked against the new requested
* HijrahChronology. If the chronology has a shorter month length
* for the month, the day is reduced to be the last day of the month.
*
* @param chronology the new HijrahChonology, non-null
* @return a HijrahDate with the requested HijrahChronology, non-null
*/
public HijrahDate withVariant(HijrahChronology chronology) {
if (chrono == chronology) {
return this;
}
// Like resolvePreviousValid the day is constrained to stay in the same month
int monthDays = chronology.getDayOfYear(prolepticYear, monthOfYear);
return HijrahDate.of(chronology, prolepticYear, monthOfYear,(dayOfMonth > monthDays) ? monthDays : dayOfMonth );
}
/**
* {@inheritDoc}
* @throws DateTimeException {@inheritDoc}
* @throws ArithmeticException {@inheritDoc}
*/
@Override
public HijrahDate plus(TemporalAmount amount) {
return super.plus(amount);
}
/**
* {@inheritDoc}
* @throws DateTimeException {@inheritDoc}
* @throws ArithmeticException {@inheritDoc}
*/
@Override
public HijrahDate minus(TemporalAmount amount) {
return super.minus(amount);
}
@Override
public long toEpochDay() {
return chrono.getEpochDay(prolepticYear, monthOfYear, dayOfMonth);
}
/**
* Gets the day-of-year field.
* <p>
* This method returns the primitive {@code int} value for the day-of-year.
*
* @return the day-of-year
*/
private int getDayOfYear() {
return chrono.getDayOfYear(prolepticYear, monthOfYear) + dayOfMonth;
}
/**
* Gets the day-of-week value.
*
* @return the day-of-week; computed from the epochday
*/
private int getDayOfWeek() {
int dow0 = Math.floorMod(toEpochDay() + 3, 7);
return dow0 + 1;
}
/**
* Gets the Era of this date.
*
* @return the Era of this date; computed from epochDay
*/
private int getEraValue() {
return (prolepticYear > 1 ? 1 : 0);
}
//-----------------------------------------------------------------------
/**
* Checks if the year is a leap year, according to the Hijrah calendar system rules.
*
* @return true if this date is in a leap year
*/
@Override
public boolean isLeapYear() {
return chrono.isLeapYear(prolepticYear);
}
//-----------------------------------------------------------------------
@Override
HijrahDate plusYears(long years) {
if (years == 0) {
return this;
}
int newYear = Math.addExact(this.prolepticYear, (int)years);
return resolvePreviousValid(newYear, monthOfYear, dayOfMonth);
}
@Override
HijrahDate plusMonths(long monthsToAdd) {
if (monthsToAdd == 0) {
return this;
}
long monthCount = prolepticYear * 12L + (monthOfYear - 1);
long calcMonths = monthCount + monthsToAdd; // safe overflow
int newYear = chrono.checkValidYear(Math.floorDiv(calcMonths, 12L));
int newMonth = (int)Math.floorMod(calcMonths, 12L) + 1;
return resolvePreviousValid(newYear, newMonth, dayOfMonth);
}
@Override
HijrahDate plusWeeks(long weeksToAdd) {
return super.plusWeeks(weeksToAdd);
}
@Override
HijrahDate plusDays(long days) {
return new HijrahDate(chrono, toEpochDay() + days);
}
@Override
public HijrahDate plus(long amountToAdd, TemporalUnit unit) {
return super.plus(amountToAdd, unit);
}
@Override
public HijrahDate minus(long amountToSubtract, TemporalUnit unit) {
return super.minus(amountToSubtract, unit);
}
@Override
HijrahDate minusYears(long yearsToSubtract) {
return super.minusYears(yearsToSubtract);
}
@Override
HijrahDate minusMonths(long monthsToSubtract) {
return super.minusMonths(monthsToSubtract);
}
@Override
HijrahDate minusWeeks(long weeksToSubtract) {
return super.minusWeeks(weeksToSubtract);
}
@Override
HijrahDate minusDays(long daysToSubtract) {
return super.minusDays(daysToSubtract);
}
@Override // for javadoc and covariant return type
@SuppressWarnings("unchecked")
public final ChronoLocalDateTime<HijrahDate> atTime(LocalTime localTime) {
return (ChronoLocalDateTime<HijrahDate>)super.atTime(localTime);
}
@Override
public ChronoPeriod until(ChronoLocalDate endDate) {
// TODO: untested
HijrahDate end = getChronology().date(endDate);
long totalMonths = (end.prolepticYear - this.prolepticYear) * 12 + (end.monthOfYear - this.monthOfYear); // safe
int days = end.dayOfMonth - this.dayOfMonth;
if (totalMonths > 0 && days < 0) {
totalMonths--;
HijrahDate calcDate = this.plusMonths(totalMonths);
days = (int) (end.toEpochDay() - calcDate.toEpochDay()); // safe
} else if (totalMonths < 0 && days > 0) {
totalMonths++;
days -= end.lengthOfMonth();
}
long years = totalMonths / 12; // safe
int months = (int) (totalMonths % 12); // safe
return getChronology().period(Math.toIntExact(years), months, days);
}
//-------------------------------------------------------------------------
/**
* Compares this date to another date, including the chronology.
* <p>
* Compares this {@code HijrahDate} with another ensuring that the date is the same.
* <p>
* Only objects of type {@code HijrahDate} are compared, other types return false.
* To compare the dates of two {@code TemporalAccessor} instances, including dates
* in two different chronologies, use {@link ChronoField#EPOCH_DAY} as a comparator.
*
* @param obj the object to check, null returns false
* @return true if this is equal to the other date and the Chronologies are equal
*/
@Override // override for performance
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof HijrahDate) {
HijrahDate otherDate = (HijrahDate) obj;
return prolepticYear == otherDate.prolepticYear
&& this.monthOfYear == otherDate.monthOfYear
&& this.dayOfMonth == otherDate.dayOfMonth
&& getChronology().equals(otherDate.getChronology());
}
return false;
}
/**
* A hash code for this date.
*
* @return a suitable hash code based only on the Chronology and the date
*/
@Override // override for performance
public int hashCode() {
int yearValue = prolepticYear;
int monthValue = monthOfYear;
int dayValue = dayOfMonth;
return getChronology().getId().hashCode() ^ (yearValue & 0xFFFFF800)
^ ((yearValue << 11) + (monthValue << 6) + (dayValue));
}
//-----------------------------------------------------------------------
/**
* Defend against malicious streams.
*
* @param s the stream to read
* @throws InvalidObjectException always
*/
private void readObject(ObjectInputStream s) throws InvalidObjectException {
throw new InvalidObjectException("Deserialization via serialization delegate");
}
/**
* Writes the object using a
* <a href="../../../serialized-form.html#java.time.chrono.Ser">dedicated serialized form</a>.
* @serialData
* <pre>
* out.writeByte(6); // identifies a HijrahDate
* out.writeObject(chrono); // the HijrahChronology variant
* out.writeInt(get(YEAR));
* out.writeByte(get(MONTH_OF_YEAR));
* out.writeByte(get(DAY_OF_MONTH));
* </pre>
*
* @return the instance of {@code Ser}, not null
*/
private Object writeReplace() {
return new Ser(Ser.HIJRAH_DATE_TYPE, this);
}
void writeExternal(ObjectOutput out) throws IOException {
// HijrahChronology is implicit in the Hijrah_DATE_TYPE
out.writeObject(getChronology());
out.writeInt(get(YEAR));
out.writeByte(get(MONTH_OF_YEAR));
out.writeByte(get(DAY_OF_MONTH));
}
static HijrahDate readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
HijrahChronology chrono = (HijrahChronology) in.readObject();
int year = in.readInt();
int month = in.readByte();
int dayOfMonth = in.readByte();
return chrono.date(year, month, dayOfMonth);
}
}

View file

@ -0,0 +1,175 @@
/*
* Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* This file is available under and governed by the GNU General Public
* License version 2 only, as published by the Free Software Foundation.
* However, the following notice accompanied the original version of this
* file:
*
* Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of JSR-310 nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package java.time.chrono;
import static java.time.temporal.ChronoField.ERA;
import java.time.DateTimeException;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.TextStyle;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalField;
import java.time.temporal.UnsupportedTemporalTypeException;
import java.time.temporal.ValueRange;
import java.util.Locale;
/**
* An era in the Hijrah calendar system.
* <p>
* The Hijrah calendar system has only one era covering the
* proleptic years greater than zero.
* <p>
* <b>Do not use {@code ordinal()} to obtain the numeric representation of {@code HijrahEra}.
* Use {@code getValue()} instead.</b>
*
* @implSpec
* This is an immutable and thread-safe enum.
*
* @since 1.8
*/
public enum HijrahEra implements Era {
/**
* The singleton instance for the current era, 'Anno Hegirae',
* which has the numeric value 1.
*/
AH;
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code HijrahEra} from an {@code int} value.
* <p>
* The current era, which is the only accepted value, has the value 1
*
* @param hijrahEra the era to represent, only 1 supported
* @return the HijrahEra.AH singleton, not null
* @throws DateTimeException if the value is invalid
*/
public static HijrahEra of(int hijrahEra) {
if (hijrahEra == 1 ) {
return AH;
} else {
throw new DateTimeException("Invalid era: " + hijrahEra);
}
}
//-----------------------------------------------------------------------
/**
* Gets the numeric era {@code int} value.
* <p>
* The era AH has the value 1.
*
* @return the era value, 1 (AH)
*/
@Override
public int getValue() {
return 1;
}
//-----------------------------------------------------------------------
/**
* Gets the range of valid values for the specified field.
* <p>
* The range object expresses the minimum and maximum valid values for a field.
* This era is used to enhance the accuracy of the returned range.
* If it is not possible to return the range, because the field is not supported
* or for some other reason, an exception is thrown.
* <p>
* If the field is a {@link ChronoField} then the query is implemented here.
* The {@code ERA} field returns the range.
* All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
* <p>
* If the field is not a {@code ChronoField}, then the result of this method
* is obtained by invoking {@code TemporalField.rangeRefinedBy(TemporalAccessor)}
* passing {@code this} as the argument.
* Whether the range can be obtained is determined by the field.
* <p>
* The {@code ERA} field returns a range for the one valid Hijrah era.
*
* @param field the field to query the range for, not null
* @return the range of valid values for the field, not null
* @throws DateTimeException if the range for the field cannot be obtained
* @throws UnsupportedTemporalTypeException if the unit is not supported
*/
@Override // override as super would return range from 0 to 1
public ValueRange range(TemporalField field) {
if (field == ERA) {
return ValueRange.of(1, 1);
}
return Era.super.range(field);
}
/**
* {@inheritDoc}
*
* @param style {@inheritDoc}
* @param locale {@inheritDoc}
*/
@Override
public String getDisplayName(TextStyle style, Locale locale) {
return new DateTimeFormatterBuilder()
.appendText(ERA, style)
.toFormatter(locale)
.withChronology(HijrahChronology.INSTANCE)
.format(HijrahDate.now());
}
}

View file

@ -0,0 +1,706 @@
/*
* Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* This file is available under and governed by the GNU General Public
* License version 2 only, as published by the Free Software Foundation.
* However, the following notice accompanied the original version of this
* file:
*
* Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of JSR-310 nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package java.time.chrono;
import static java.time.temporal.ChronoField.DAY_OF_MONTH;
import static java.time.temporal.ChronoField.ERA;
import static java.time.temporal.ChronoField.HOUR_OF_DAY;
import static java.time.temporal.ChronoField.MINUTE_OF_HOUR;
import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
import static java.time.temporal.ChronoField.PROLEPTIC_MONTH;
import static java.time.temporal.ChronoField.SECOND_OF_MINUTE;
import static java.time.temporal.ChronoField.YEAR;
import static java.time.temporal.ChronoField.YEAR_OF_ERA;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.time.Clock;
import java.time.DateTimeException;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.Period;
import java.time.Year;
import java.time.ZonedDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
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.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
/**
* The ISO calendar system.
* <p>
* This chronology defines the rules of the ISO calendar system.
* This calendar system is based on the ISO-8601 standard, which is the
* <i>de facto</i> world calendar.
* <p>
* The fields are defined as follows:
* <ul>
* <li>era - There are two eras, 'Current Era' (CE) and 'Before Current Era' (BCE).
* <li>year-of-era - The year-of-era is the same as the proleptic-year for the current CE era.
* For the BCE era before the ISO epoch the year increases from 1 upwards as time goes backwards.
* <li>proleptic-year - The proleptic year is the same as the year-of-era for the
* current era. For the previous era, years have zero, then negative values.
* <li>month-of-year - There are 12 months in an ISO year, numbered from 1 to 12.
* <li>day-of-month - There are between 28 and 31 days in each of the ISO month, numbered from 1 to 31.
* Months 4, 6, 9 and 11 have 30 days, Months 1, 3, 5, 7, 8, 10 and 12 have 31 days.
* Month 2 has 28 days, or 29 in a leap year.
* <li>day-of-year - There are 365 days in a standard ISO year and 366 in a leap year.
* The days are numbered from 1 to 365 or 1 to 366.
* <li>leap-year - Leap years occur every 4 years, except where the year is divisble by 100 and not divisble by 400.
* </ul>
*
* @implSpec
* This class is immutable and thread-safe.
*
* @since 1.8
*/
public final class IsoChronology extends AbstractChronology implements Serializable {
/**
* Singleton instance of the ISO chronology.
*/
public static final IsoChronology INSTANCE = new IsoChronology();
/**
* Serialization version.
*/
private static final long serialVersionUID = -1440403870442975015L;
private static final long DAYS_0000_TO_1970 = (146097 * 5L) - (30L * 365L + 7L); // taken from LocalDate
/**
* Restricted constructor.
*/
private IsoChronology() {
}
//-----------------------------------------------------------------------
/**
* Gets the ID of the chronology - 'ISO'.
* <p>
* The ID uniquely identifies the {@code Chronology}.
* It can be used to lookup the {@code Chronology} using {@link Chronology#of(String)}.
*
* @return the chronology ID - 'ISO'
* @see #getCalendarType()
*/
@Override
public String getId() {
return "ISO";
}
/**
* Gets the calendar type of the underlying calendar system - 'iso8601'.
* <p>
* The calendar type is an identifier defined by the
* <em>Unicode Locale Data Markup Language (LDML)</em> specification.
* It can be used to lookup the {@code Chronology} using {@link Chronology#of(String)}.
* It can also be used as part of a locale, accessible via
* {@link Locale#getUnicodeLocaleType(String)} with the key 'ca'.
*
* @return the calendar system type - 'iso8601'
* @see #getId()
*/
@Override
public String getCalendarType() {
return "iso8601";
}
//-----------------------------------------------------------------------
/**
* Obtains an ISO local date from the era, year-of-era, month-of-year
* and day-of-month fields.
*
* @param era the ISO era, not null
* @param yearOfEra the ISO year-of-era
* @param month the ISO month-of-year
* @param dayOfMonth the ISO day-of-month
* @return the ISO local date, not null
* @throws DateTimeException if unable to create the date
* @throws ClassCastException if the type of {@code era} is not {@code IsoEra}
*/
@Override // override with covariant return type
public LocalDate date(Era era, int yearOfEra, int month, int dayOfMonth) {
return date(prolepticYear(era, yearOfEra), month, dayOfMonth);
}
/**
* Obtains an ISO local date from the proleptic-year, month-of-year
* and day-of-month fields.
* <p>
* This is equivalent to {@link LocalDate#of(int, int, int)}.
*
* @param prolepticYear the ISO proleptic-year
* @param month the ISO month-of-year
* @param dayOfMonth the ISO day-of-month
* @return the ISO local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override // override with covariant return type
public LocalDate date(int prolepticYear, int month, int dayOfMonth) {
return LocalDate.of(prolepticYear, month, dayOfMonth);
}
/**
* Obtains an ISO local date from the era, year-of-era and day-of-year fields.
*
* @param era the ISO era, not null
* @param yearOfEra the ISO year-of-era
* @param dayOfYear the ISO day-of-year
* @return the ISO local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override // override with covariant return type
public LocalDate dateYearDay(Era era, int yearOfEra, int dayOfYear) {
return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear);
}
/**
* Obtains an ISO local date from the proleptic-year and day-of-year fields.
* <p>
* This is equivalent to {@link LocalDate#ofYearDay(int, int)}.
*
* @param prolepticYear the ISO proleptic-year
* @param dayOfYear the ISO day-of-year
* @return the ISO local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override // override with covariant return type
public LocalDate dateYearDay(int prolepticYear, int dayOfYear) {
return LocalDate.ofYearDay(prolepticYear, dayOfYear);
}
/**
* Obtains an ISO local date from the epoch-day.
* <p>
* This is equivalent to {@link LocalDate#ofEpochDay(long)}.
*
* @param epochDay the epoch day
* @return the ISO local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override // override with covariant return type
public LocalDate dateEpochDay(long epochDay) {
return LocalDate.ofEpochDay(epochDay);
}
//-----------------------------------------------------------------------
/**
* Obtains an ISO local date from another date-time object.
* <p>
* This is equivalent to {@link LocalDate#from(TemporalAccessor)}.
*
* @param temporal the date-time object to convert, not null
* @return the ISO local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override // override with covariant return type
public LocalDate date(TemporalAccessor temporal) {
return LocalDate.from(temporal);
}
//-----------------------------------------------------------------------
/**
* Gets the number of seconds from the epoch of 1970-01-01T00:00:00Z.
* <p>
* The number of seconds is calculated using the year,
* month, day-of-month, hour, minute, second, and zoneOffset.
*
* @param prolepticYear the year, from MIN_YEAR to MAX_YEAR
* @param month the month-of-year, from 1 to 12
* @param dayOfMonth the day-of-month, from 1 to 31
* @param hour the hour-of-day, from 0 to 23
* @param minute the minute-of-hour, from 0 to 59
* @param second the second-of-minute, from 0 to 59
* @param zoneOffset the zone offset, not null
* @return the number of seconds relative to 1970-01-01T00:00:00Z, may be negative
* @throws DateTimeException if the value of any argument is out of range,
* or if the day-of-month is invalid for the month-of-year
* @since 9
*/
@Override
public long epochSecond(int prolepticYear, int month, int dayOfMonth,
int hour, int minute, int second, ZoneOffset zoneOffset) {
YEAR.checkValidValue(prolepticYear);
MONTH_OF_YEAR.checkValidValue(month);
DAY_OF_MONTH.checkValidValue(dayOfMonth);
HOUR_OF_DAY.checkValidValue(hour);
MINUTE_OF_HOUR.checkValidValue(minute);
SECOND_OF_MINUTE.checkValidValue(second);
Objects.requireNonNull(zoneOffset, "zoneOffset");
if (dayOfMonth > 28) {
int dom = numberOfDaysOfMonth(prolepticYear, month);
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 + "'");
}
}
}
long totalDays = 0;
int timeinSec = 0;
totalDays += 365L * prolepticYear;
if (prolepticYear >= 0) {
totalDays += (prolepticYear + 3L) / 4 - (prolepticYear + 99L) / 100 + (prolepticYear + 399L) / 400;
} else {
totalDays -= prolepticYear / -4 - prolepticYear / -100 + prolepticYear / -400;
}
totalDays += (367 * month - 362) / 12;
totalDays += dayOfMonth - 1;
if (month > 2) {
totalDays--;
if (IsoChronology.INSTANCE.isLeapYear(prolepticYear) == false) {
totalDays--;
}
}
totalDays -= DAYS_0000_TO_1970;
timeinSec = (hour * 60 + minute ) * 60 + second;
return Math.addExact(Math.multiplyExact(totalDays, 86400L), timeinSec - zoneOffset.getTotalSeconds());
}
/**
* Gets the number of days for the given month in the given year.
*
* @param year the year to represent, from MIN_YEAR to MAX_YEAR
* @param month the month-of-year to represent, from 1 to 12
* @return the number of days for the given month in the given year
*/
private int numberOfDaysOfMonth(int year, int month) {
int dom;
switch (month) {
case 2:
dom = (IsoChronology.INSTANCE.isLeapYear(year) ? 29 : 28);
break;
case 4:
case 6:
case 9:
case 11:
dom = 30;
break;
default:
dom = 31;
break;
}
return dom;
}
/**
* Obtains an ISO local date-time from another date-time object.
* <p>
* This is equivalent to {@link LocalDateTime#from(TemporalAccessor)}.
*
* @param temporal the date-time object to convert, not null
* @return the ISO local date-time, not null
* @throws DateTimeException if unable to create the date-time
*/
@Override // override with covariant return type
public LocalDateTime localDateTime(TemporalAccessor temporal) {
return LocalDateTime.from(temporal);
}
/**
* Obtains an ISO zoned date-time from another date-time object.
* <p>
* This is equivalent to {@link ZonedDateTime#from(TemporalAccessor)}.
*
* @param temporal the date-time object to convert, not null
* @return the ISO zoned date-time, not null
* @throws DateTimeException if unable to create the date-time
*/
@Override // override with covariant return type
public ZonedDateTime zonedDateTime(TemporalAccessor temporal) {
return ZonedDateTime.from(temporal);
}
/**
* Obtains an ISO zoned date-time in this chronology from an {@code Instant}.
* <p>
* This is equivalent to {@link ZonedDateTime#ofInstant(Instant, ZoneId)}.
*
* @param instant the instant to create the date-time from, not null
* @param zone the time-zone, not null
* @return the zoned date-time, not null
* @throws DateTimeException if the result exceeds the supported range
*/
@Override
public ZonedDateTime zonedDateTime(Instant instant, ZoneId zone) {
return ZonedDateTime.ofInstant(instant, zone);
}
//-----------------------------------------------------------------------
/**
* Obtains the current ISO local date from the system clock in the default time-zone.
* <p>
* This will query the {@link Clock#systemDefaultZone() system clock} in the default
* time-zone to obtain the current date.
* <p>
* Using this method will prevent the ability to use an alternate clock for testing
* because the clock is hard-coded.
*
* @return the current ISO local date using the system clock and default time-zone, not null
* @throws DateTimeException if unable to create the date
*/
@Override // override with covariant return type
public LocalDate dateNow() {
return dateNow(Clock.systemDefaultZone());
}
/**
* Obtains the current ISO local date from the system clock in the specified time-zone.
* <p>
* This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date.
* Specifying the time-zone avoids dependence on the default time-zone.
* <p>
* Using this method will prevent the ability to use an alternate clock for testing
* because the clock is hard-coded.
*
* @return the current ISO local date using the system clock, not null
* @throws DateTimeException if unable to create the date
*/
@Override // override with covariant return type
public LocalDate dateNow(ZoneId zone) {
return dateNow(Clock.system(zone));
}
/**
* Obtains the current ISO local date from the specified clock.
* <p>
* This will query the specified clock to obtain the current date - today.
* Using this method allows the use of an alternate clock for testing.
* The alternate clock may be introduced using {@link Clock dependency injection}.
*
* @param clock the clock to use, not null
* @return the current ISO local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override // override with covariant return type
public LocalDate dateNow(Clock clock) {
Objects.requireNonNull(clock, "clock");
return date(LocalDate.now(clock));
}
//-----------------------------------------------------------------------
/**
* Checks if the year is a leap year, according to the ISO proleptic
* calendar system rules.
* <p>
* This method applies the current rules for leap years across the whole time-line.
* In general, a year is a leap year if it is divisible by four without
* remainder. However, years divisible by 100, are not leap years, with
* the exception of years divisible by 400 which are.
* <p>
* For example, 1904 is a leap year it is divisible by 4.
* 1900 was not a leap year as it is divisible by 100, however 2000 was a
* leap year as it is divisible by 400.
* <p>
* The calculation is proleptic - applying the same rules into the far future and far past.
* This is historically inaccurate, but is correct for the ISO-8601 standard.
*
* @param prolepticYear the ISO proleptic year to check
* @return true if the year is leap, false otherwise
*/
@Override
public boolean isLeapYear(long prolepticYear) {
return ((prolepticYear & 3) == 0) && ((prolepticYear % 100) != 0 || (prolepticYear % 400) == 0);
}
@Override
public int prolepticYear(Era era, int yearOfEra) {
if (era instanceof IsoEra == false) {
throw new ClassCastException("Era must be IsoEra");
}
return (era == IsoEra.CE ? yearOfEra : 1 - yearOfEra);
}
@Override
public IsoEra eraOf(int eraValue) {
return IsoEra.of(eraValue);
}
@Override
public List<Era> eras() {
return List.of(IsoEra.values());
}
//-----------------------------------------------------------------------
/**
* Resolves parsed {@code ChronoField} values into a date during parsing.
* <p>
* Most {@code TemporalField} implementations are resolved using the
* resolve method on the field. By contrast, the {@code ChronoField} class
* defines fields that only have meaning relative to the chronology.
* As such, {@code ChronoField} date fields are resolved here in the
* context of a specific chronology.
* <p>
* {@code ChronoField} instances on the ISO calendar system are resolved
* as follows.
* <ul>
* <li>{@code EPOCH_DAY} - If present, this is converted to a {@code LocalDate}
* and all other date fields are then cross-checked against the date.
* <li>{@code PROLEPTIC_MONTH} - If present, then it is split into the
* {@code YEAR} and {@code MONTH_OF_YEAR}. If the mode is strict or smart
* then the field is validated.
* <li>{@code YEAR_OF_ERA} and {@code ERA} - If both are present, then they
* are combined to form a {@code YEAR}. In lenient mode, the {@code YEAR_OF_ERA}
* range is not validated, in smart and strict mode it is. The {@code ERA} is
* validated for range in all three modes. If only the {@code YEAR_OF_ERA} is
* present, and the mode is smart or lenient, then the current era (CE/AD)
* is assumed. In strict mode, no era is assumed and the {@code YEAR_OF_ERA} is
* left untouched. If only the {@code ERA} is present, then it is left untouched.
* <li>{@code YEAR}, {@code MONTH_OF_YEAR} and {@code DAY_OF_MONTH} -
* If all three are present, then they are combined to form a {@code LocalDate}.
* In all three modes, the {@code YEAR} is validated. If the mode is smart or strict,
* then the month and day are validated, with the day validated from 1 to 31.
* If the mode is lenient, then the date is combined in a manner equivalent to
* creating a date on the first of January in the requested year, then adding
* the difference in months, then the difference in days.
* If the mode is smart, and the day-of-month is greater than the maximum for
* the year-month, then the day-of-month is adjusted to the last day-of-month.
* If the mode is strict, then the three fields must form a valid date.
* <li>{@code YEAR} and {@code DAY_OF_YEAR} -
* If both are present, then they are combined to form a {@code LocalDate}.
* In all three modes, the {@code YEAR} is validated.
* If the mode is lenient, then the date is combined in a manner equivalent to
* creating a date on the first of January in the requested year, then adding
* the difference in days.
* If the mode is smart or strict, then the two fields must form a valid date.
* <li>{@code YEAR}, {@code MONTH_OF_YEAR}, {@code ALIGNED_WEEK_OF_MONTH} and
* {@code ALIGNED_DAY_OF_WEEK_IN_MONTH} -
* If all four are present, then they are combined to form a {@code LocalDate}.
* In all three modes, the {@code YEAR} is validated.
* If the mode is lenient, then the date is combined in a manner equivalent to
* creating a date on the first of January in the requested year, then adding
* the difference in months, then the difference in weeks, then in days.
* If the mode is smart or strict, then the all four fields are validated to
* their outer ranges. The date is then combined in a manner equivalent to
* creating a date on the first day of the requested year and month, then adding
* the amount in weeks and days to reach their values. If the mode is strict,
* the date is additionally validated to check that the day and week adjustment
* did not change the month.
* <li>{@code YEAR}, {@code MONTH_OF_YEAR}, {@code ALIGNED_WEEK_OF_MONTH} and
* {@code DAY_OF_WEEK} - If all four are present, then they are combined to
* form a {@code LocalDate}. The approach is the same as described above for
* years, months and weeks in {@code ALIGNED_DAY_OF_WEEK_IN_MONTH}.
* The day-of-week is adjusted as the next or same matching day-of-week once
* the years, months and weeks have been handled.
* <li>{@code YEAR}, {@code ALIGNED_WEEK_OF_YEAR} and {@code ALIGNED_DAY_OF_WEEK_IN_YEAR} -
* If all three are present, then they are combined to form a {@code LocalDate}.
* In all three modes, the {@code YEAR} is validated.
* If the mode is lenient, then the date is combined in a manner equivalent to
* creating a date on the first of January in the requested year, then adding
* the difference in weeks, then in days.
* If the mode is smart or strict, then the all three fields are validated to
* their outer ranges. The date is then combined in a manner equivalent to
* creating a date on the first day of the requested year, then adding
* the amount in weeks and days to reach their values. If the mode is strict,
* the date is additionally validated to check that the day and week adjustment
* did not change the year.
* <li>{@code YEAR}, {@code ALIGNED_WEEK_OF_YEAR} and {@code DAY_OF_WEEK} -
* If all three are present, then they are combined to form a {@code LocalDate}.
* The approach is the same as described above for years and weeks in
* {@code ALIGNED_DAY_OF_WEEK_IN_YEAR}. The day-of-week is adjusted as the
* next or same matching day-of-week once the years and weeks have been handled.
* </ul>
*
* @param fieldValues the map of fields to values, which can be updated, not null
* @param resolverStyle the requested type of resolve, not null
* @return the resolved date, null if insufficient information to create a date
* @throws DateTimeException if the date cannot be resolved, typically
* because of a conflict in the input data
*/
@Override // override for performance
public LocalDate resolveDate(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
return (LocalDate) super.resolveDate(fieldValues, resolverStyle);
}
@Override // override for better proleptic algorithm
void resolveProlepticMonth(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
Long pMonth = fieldValues.remove(PROLEPTIC_MONTH);
if (pMonth != null) {
if (resolverStyle != ResolverStyle.LENIENT) {
PROLEPTIC_MONTH.checkValidValue(pMonth);
}
addFieldValue(fieldValues, MONTH_OF_YEAR, Math.floorMod(pMonth, 12) + 1);
addFieldValue(fieldValues, YEAR, Math.floorDiv(pMonth, 12));
}
}
@Override // override for enhanced behaviour
LocalDate resolveYearOfEra(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
Long yoeLong = fieldValues.remove(YEAR_OF_ERA);
if (yoeLong != null) {
if (resolverStyle != ResolverStyle.LENIENT) {
YEAR_OF_ERA.checkValidValue(yoeLong);
}
Long era = fieldValues.remove(ERA);
if (era == null) {
Long year = fieldValues.get(YEAR);
if (resolverStyle == ResolverStyle.STRICT) {
// do not invent era if strict, but do cross-check with year
if (year != null) {
addFieldValue(fieldValues, YEAR, (year > 0 ? yoeLong: Math.subtractExact(1, yoeLong)));
} else {
// reinstate the field removed earlier, no cross-check issues
fieldValues.put(YEAR_OF_ERA, yoeLong);
}
} else {
// invent era
addFieldValue(fieldValues, YEAR, (year == null || year > 0 ? yoeLong: Math.subtractExact(1, yoeLong)));
}
} else if (era.longValue() == 1L) {
addFieldValue(fieldValues, YEAR, yoeLong);
} else if (era.longValue() == 0L) {
addFieldValue(fieldValues, YEAR, Math.subtractExact(1, yoeLong));
} else {
throw new DateTimeException("Invalid value for era: " + era);
}
} else if (fieldValues.containsKey(ERA)) {
ERA.checkValidValue(fieldValues.get(ERA)); // always validated
}
return null;
}
@Override // override for performance
LocalDate resolveYMD(Map <TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
int y = YEAR.checkValidIntValue(fieldValues.remove(YEAR));
if (resolverStyle == ResolverStyle.LENIENT) {
long months = Math.subtractExact(fieldValues.remove(MONTH_OF_YEAR), 1);
long days = Math.subtractExact(fieldValues.remove(DAY_OF_MONTH), 1);
return LocalDate.of(y, 1, 1).plusMonths(months).plusDays(days);
}
int moy = MONTH_OF_YEAR.checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR));
int dom = DAY_OF_MONTH.checkValidIntValue(fieldValues.remove(DAY_OF_MONTH));
if (resolverStyle == ResolverStyle.SMART) { // previous valid
if (moy == 4 || moy == 6 || moy == 9 || moy == 11) {
dom = Math.min(dom, 30);
} else if (moy == 2) {
dom = Math.min(dom, Month.FEBRUARY.length(Year.isLeap(y)));
}
}
return LocalDate.of(y, moy, dom);
}
//-----------------------------------------------------------------------
@Override
public ValueRange range(ChronoField field) {
return field.range();
}
//-----------------------------------------------------------------------
/**
* Obtains a period for this chronology based on years, months and days.
* <p>
* This returns a period tied to the ISO chronology using the specified
* years, months and days. See {@link Period} for further details.
*
* @param years the number of years, may be negative
* @param months the number of years, may be negative
* @param days the number of years, may be negative
* @return the period in terms of this chronology, not null
* @return the ISO period, not null
*/
@Override // override with covariant return type
public Period period(int years, int months, int days) {
return Period.of(years, months, days);
}
//-----------------------------------------------------------------------
/**
* Writes the Chronology using a
* <a href="../../../serialized-form.html#java.time.chrono.Ser">dedicated serialized form</a>.
* @serialData
* <pre>
* out.writeByte(1); // identifies a Chronology
* out.writeUTF(getId());
* </pre>
*
* @return the instance of {@code Ser}, not null
*/
@Override
Object writeReplace() {
return super.writeReplace();
}
/**
* Defend against malicious streams.
*
* @param s the stream to read
* @throws InvalidObjectException always
*/
private void readObject(ObjectInputStream s) throws InvalidObjectException {
throw new InvalidObjectException("Deserialization via serialization delegate");
}
}

View file

@ -0,0 +1,154 @@
/*
* Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* This file is available under and governed by the GNU General Public
* License version 2 only, as published by the Free Software Foundation.
* However, the following notice accompanied the original version of this
* file:
*
* Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of JSR-310 nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package java.time.chrono;
import java.time.DateTimeException;
/**
* An era in the ISO calendar system.
* <p>
* The ISO-8601 standard does not define eras.
* A definition has therefore been created with two eras - 'Current era' (CE) for
* years on or after 0001-01-01 (ISO), and 'Before current era' (BCE) for years before that.
*
* <table class="striped" style="text-align:left">
* <caption style="display:none">ISO years and eras</caption>
* <thead>
* <tr>
* <th scope="col">year-of-era</th>
* <th scope="col">era</th>
* <th scope="col">proleptic-year</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td>2</td><td>CE</td><th scope="row">2</th>
* </tr>
* <tr>
* <td>1</td><td>CE</td><th scope="row">1</th>
* </tr>
* <tr>
* <td>1</td><td>BCE</td><th scope="row">0</th>
* </tr>
* <tr>
* <td>2</td><td>BCE</td><th scope="row">-1</th>
* </tr>
* </tbody>
* </table>
* <p>
* <b>Do not use {@code ordinal()} to obtain the numeric representation of {@code IsoEra}.
* Use {@code getValue()} instead.</b>
*
* @implSpec
* This is an immutable and thread-safe enum.
*
* @since 1.8
*/
public enum IsoEra implements Era {
/**
* The singleton instance for the era before the current one, 'Before Current Era',
* which has the numeric value 0.
*/
BCE,
/**
* The singleton instance for the current era, 'Current Era',
* which has the numeric value 1.
*/
CE;
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code IsoEra} from an {@code int} value.
* <p>
* {@code IsoEra} is an enum representing the ISO eras of BCE/CE.
* This factory allows the enum to be obtained from the {@code int} value.
*
* @param isoEra the BCE/CE value to represent, from 0 (BCE) to 1 (CE)
* @return the era singleton, not null
* @throws DateTimeException if the value is invalid
*/
public static IsoEra of(int isoEra) {
switch (isoEra) {
case 0:
return BCE;
case 1:
return CE;
default:
throw new DateTimeException("Invalid era: " + isoEra);
}
}
//-----------------------------------------------------------------------
/**
* Gets the numeric era {@code int} value.
* <p>
* The era BCE has the value 0, while the era CE has the value 1.
*
* @return the era value, from 0 (BCE) to 1 (CE)
*/
@Override
public int getValue() {
return ordinal();
}
}

View file

@ -0,0 +1,533 @@
/*
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of JSR-310 nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package java.time.chrono;
import static java.time.temporal.ChronoField.DAY_OF_MONTH;
import static java.time.temporal.ChronoField.DAY_OF_YEAR;
import static java.time.temporal.ChronoField.ERA;
import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
import static java.time.temporal.ChronoField.YEAR;
import static java.time.temporal.ChronoField.YEAR_OF_ERA;
import static java.time.temporal.ChronoUnit.DAYS;
import static java.time.temporal.ChronoUnit.MONTHS;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.time.Clock;
import java.time.DateTimeException;
import java.time.Instant;
import java.time.LocalDate;
import java.time.Year;
import java.time.ZoneId;
import java.time.format.ResolverStyle;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalAdjusters;
import java.time.temporal.TemporalField;
import java.time.temporal.UnsupportedTemporalTypeException;
import java.time.temporal.ValueRange;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import sun.util.calendar.CalendarSystem;
import sun.util.calendar.LocalGregorianCalendar;
/**
* The Japanese Imperial calendar system.
* <p>
* This chronology defines the rules of the Japanese Imperial calendar system.
* This calendar system is primarily used in Japan.
* The Japanese Imperial calendar system is the same as the ISO calendar system
* apart from the era-based year numbering.
* <p>
* Japan introduced the Gregorian calendar starting with Meiji 6.
* Only Meiji and later eras are supported;
* dates before Meiji 6, January 1 are not supported.
* <p>
* The supported {@code ChronoField} instances are:
* <ul>
* <li>{@code DAY_OF_WEEK}
* <li>{@code DAY_OF_MONTH}
* <li>{@code DAY_OF_YEAR}
* <li>{@code EPOCH_DAY}
* <li>{@code MONTH_OF_YEAR}
* <li>{@code PROLEPTIC_MONTH}
* <li>{@code YEAR_OF_ERA}
* <li>{@code YEAR}
* <li>{@code ERA}
* </ul>
*
* @implSpec
* This class is immutable and thread-safe.
*
* @since 1.8
*/
public final class JapaneseChronology extends AbstractChronology implements Serializable {
static final LocalGregorianCalendar JCAL =
(LocalGregorianCalendar) CalendarSystem.forName("japanese");
// Locale for creating a JapaneseImpericalCalendar.
static final Locale LOCALE = Locale.forLanguageTag("ja-JP-u-ca-japanese");
/**
* Singleton instance for Japanese chronology.
*/
public static final JapaneseChronology INSTANCE = new JapaneseChronology();
/**
* Serialization version.
*/
private static final long serialVersionUID = 459996390165777884L;
//-----------------------------------------------------------------------
/**
* Restricted constructor.
*/
private JapaneseChronology() {
}
//-----------------------------------------------------------------------
/**
* Gets the ID of the chronology - 'Japanese'.
* <p>
* The ID uniquely identifies the {@code Chronology}.
* It can be used to lookup the {@code Chronology} using {@link Chronology#of(String)}.
*
* @return the chronology ID - 'Japanese'
* @see #getCalendarType()
*/
@Override
public String getId() {
return "Japanese";
}
/**
* Gets the calendar type of the underlying calendar system - 'japanese'.
* <p>
* The calendar type is an identifier defined by the
* <em>Unicode Locale Data Markup Language (LDML)</em> specification.
* It can be used to lookup the {@code Chronology} using {@link Chronology#of(String)}.
* It can also be used as part of a locale, accessible via
* {@link Locale#getUnicodeLocaleType(String)} with the key 'ca'.
*
* @return the calendar system type - 'japanese'
* @see #getId()
*/
@Override
public String getCalendarType() {
return "japanese";
}
//-----------------------------------------------------------------------
/**
* Obtains a local date in Japanese calendar system from the
* era, year-of-era, month-of-year and day-of-month fields.
* <p>
* The Japanese month and day-of-month are the same as those in the
* ISO calendar system. They are not reset when the era changes.
* For example:
* <pre>
* 6th Jan Showa 64 = ISO 1989-01-06
* 7th Jan Showa 64 = ISO 1989-01-07
* 8th Jan Heisei 1 = ISO 1989-01-08
* 9th Jan Heisei 1 = ISO 1989-01-09
* </pre>
*
* @param era the Japanese era, not null
* @param yearOfEra the year-of-era
* @param month the month-of-year
* @param dayOfMonth the day-of-month
* @return the Japanese local date, not null
* @throws DateTimeException if unable to create the date
* @throws ClassCastException if the {@code era} is not a {@code JapaneseEra}
*/
@Override
public JapaneseDate date(Era era, int yearOfEra, int month, int dayOfMonth) {
if (era instanceof JapaneseEra == false) {
throw new ClassCastException("Era must be JapaneseEra");
}
return JapaneseDate.of((JapaneseEra) era, yearOfEra, month, dayOfMonth);
}
/**
* Obtains a local date in Japanese calendar system from the
* proleptic-year, month-of-year and day-of-month fields.
* <p>
* The Japanese proleptic year, month and day-of-month are the same as those
* in the ISO calendar system. They are not reset when the era changes.
*
* @param prolepticYear the proleptic-year
* @param month the month-of-year
* @param dayOfMonth the day-of-month
* @return the Japanese local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override
public JapaneseDate date(int prolepticYear, int month, int dayOfMonth) {
return new JapaneseDate(LocalDate.of(prolepticYear, month, dayOfMonth));
}
/**
* Obtains a local date in Japanese calendar system from the
* era, year-of-era and day-of-year fields.
* <p>
* The day-of-year in this factory is expressed relative to the start of the year-of-era.
* This definition changes the normal meaning of day-of-year only in those years
* where the year-of-era is reset to one due to a change in the era.
* For example:
* <pre>
* 6th Jan Showa 64 = day-of-year 6
* 7th Jan Showa 64 = day-of-year 7
* 8th Jan Heisei 1 = day-of-year 1
* 9th Jan Heisei 1 = day-of-year 2
* </pre>
*
* @param era the Japanese era, not null
* @param yearOfEra the year-of-era
* @param dayOfYear the day-of-year
* @return the Japanese local date, not null
* @throws DateTimeException if unable to create the date
* @throws ClassCastException if the {@code era} is not a {@code JapaneseEra}
*/
@Override
public JapaneseDate dateYearDay(Era era, int yearOfEra, int dayOfYear) {
return JapaneseDate.ofYearDay((JapaneseEra) era, yearOfEra, dayOfYear);
}
/**
* Obtains a local date in Japanese calendar system from the
* proleptic-year and day-of-year fields.
* <p>
* The day-of-year in this factory is expressed relative to the start of the proleptic year.
* The Japanese proleptic year and day-of-year are the same as those in the ISO calendar system.
* They are not reset when the era changes.
*
* @param prolepticYear the proleptic-year
* @param dayOfYear the day-of-year
* @return the Japanese local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override
public JapaneseDate dateYearDay(int prolepticYear, int dayOfYear) {
return new JapaneseDate(LocalDate.ofYearDay(prolepticYear, dayOfYear));
}
/**
* Obtains a local date in the Japanese calendar system from the epoch-day.
*
* @param epochDay the epoch day
* @return the Japanese local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override // override with covariant return type
public JapaneseDate dateEpochDay(long epochDay) {
return new JapaneseDate(LocalDate.ofEpochDay(epochDay));
}
@Override
public JapaneseDate dateNow() {
return dateNow(Clock.systemDefaultZone());
}
@Override
public JapaneseDate dateNow(ZoneId zone) {
return dateNow(Clock.system(zone));
}
@Override
public JapaneseDate dateNow(Clock clock) {
return date(LocalDate.now(clock));
}
@Override
public JapaneseDate date(TemporalAccessor temporal) {
if (temporal instanceof JapaneseDate) {
return (JapaneseDate) temporal;
}
return new JapaneseDate(LocalDate.from(temporal));
}
@Override
@SuppressWarnings("unchecked")
public ChronoLocalDateTime<JapaneseDate> localDateTime(TemporalAccessor temporal) {
return (ChronoLocalDateTime<JapaneseDate>)super.localDateTime(temporal);
}
@Override
@SuppressWarnings("unchecked")
public ChronoZonedDateTime<JapaneseDate> zonedDateTime(TemporalAccessor temporal) {
return (ChronoZonedDateTime<JapaneseDate>)super.zonedDateTime(temporal);
}
@Override
@SuppressWarnings("unchecked")
public ChronoZonedDateTime<JapaneseDate> zonedDateTime(Instant instant, ZoneId zone) {
return (ChronoZonedDateTime<JapaneseDate>)super.zonedDateTime(instant, zone);
}
//-----------------------------------------------------------------------
/**
* Checks if the specified year is a leap year.
* <p>
* Japanese calendar leap years occur exactly in line with ISO leap years.
* This method does not validate the year passed in, and only has a
* well-defined result for years in the supported range.
*
* @param prolepticYear the proleptic-year to check, not validated for range
* @return true if the year is a leap year
*/
@Override
public boolean isLeapYear(long prolepticYear) {
return IsoChronology.INSTANCE.isLeapYear(prolepticYear);
}
@Override
public int prolepticYear(Era era, int yearOfEra) {
if (era instanceof JapaneseEra == false) {
throw new ClassCastException("Era must be JapaneseEra");
}
JapaneseEra jera = (JapaneseEra) era;
int gregorianYear = jera.getPrivateEra().getSinceDate().getYear() + yearOfEra - 1;
if (yearOfEra == 1) {
return gregorianYear;
}
if (gregorianYear >= Year.MIN_VALUE && gregorianYear <= Year.MAX_VALUE) {
LocalGregorianCalendar.Date jdate = JCAL.newCalendarDate(null);
jdate.setEra(jera.getPrivateEra()).setDate(yearOfEra, 1, 1);
if (JapaneseChronology.JCAL.validate(jdate)) {
return gregorianYear;
}
}
throw new DateTimeException("Invalid yearOfEra value");
}
/**
* Returns the calendar system era object from the given numeric value.
*
* See the description of each Era for the numeric values of:
* {@link JapaneseEra#HEISEI}, {@link JapaneseEra#SHOWA},{@link JapaneseEra#TAISHO},
* {@link JapaneseEra#MEIJI}), only Meiji and later eras are supported.
*
* @param eraValue the era value
* @return the Japanese {@code Era} for the given numeric era value
* @throws DateTimeException if {@code eraValue} is invalid
*/
@Override
public JapaneseEra eraOf(int eraValue) {
return JapaneseEra.of(eraValue);
}
@Override
public List<Era> eras() {
return List.of(JapaneseEra.values());
}
JapaneseEra getCurrentEra() {
// Assume that the last JapaneseEra is the current one.
JapaneseEra[] eras = JapaneseEra.values();
return eras[eras.length - 1];
}
//-----------------------------------------------------------------------
@Override
public ValueRange range(ChronoField field) {
switch (field) {
case ALIGNED_DAY_OF_WEEK_IN_MONTH:
case ALIGNED_DAY_OF_WEEK_IN_YEAR:
case ALIGNED_WEEK_OF_MONTH:
case ALIGNED_WEEK_OF_YEAR:
throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
case YEAR_OF_ERA: {
Calendar jcal = Calendar.getInstance(LOCALE);
int startYear = getCurrentEra().getPrivateEra().getSinceDate().getYear();
return ValueRange.of(1, jcal.getGreatestMinimum(Calendar.YEAR),
jcal.getLeastMaximum(Calendar.YEAR) + 1, // +1 due to the different definitions
Year.MAX_VALUE - startYear);
}
case DAY_OF_YEAR: {
Calendar jcal = Calendar.getInstance(LOCALE);
int fieldIndex = Calendar.DAY_OF_YEAR;
return ValueRange.of(jcal.getMinimum(fieldIndex), jcal.getGreatestMinimum(fieldIndex),
jcal.getLeastMaximum(fieldIndex), jcal.getMaximum(fieldIndex));
}
case YEAR:
return ValueRange.of(JapaneseDate.MEIJI_6_ISODATE.getYear(), Year.MAX_VALUE);
case ERA:
return ValueRange.of(JapaneseEra.MEIJI.getValue(), getCurrentEra().getValue());
default:
return field.range();
}
}
//-----------------------------------------------------------------------
@Override // override for return type
public JapaneseDate resolveDate(Map <TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
return (JapaneseDate) super.resolveDate(fieldValues, resolverStyle);
}
@Override // override for special Japanese behavior
ChronoLocalDate resolveYearOfEra(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
// validate era and year-of-era
Long eraLong = fieldValues.get(ERA);
JapaneseEra era = null;
if (eraLong != null) {
era = eraOf(range(ERA).checkValidIntValue(eraLong, ERA)); // always validated
}
Long yoeLong = fieldValues.get(YEAR_OF_ERA);
int yoe = 0;
if (yoeLong != null) {
yoe = range(YEAR_OF_ERA).checkValidIntValue(yoeLong, YEAR_OF_ERA); // always validated
}
// if only year-of-era and no year then invent era unless strict
if (era == null && yoeLong != null && fieldValues.containsKey(YEAR) == false && resolverStyle != ResolverStyle.STRICT) {
era = JapaneseEra.values()[JapaneseEra.values().length - 1];
}
// if both present, then try to create date
if (yoeLong != null && era != null) {
if (fieldValues.containsKey(MONTH_OF_YEAR)) {
if (fieldValues.containsKey(DAY_OF_MONTH)) {
return resolveYMD(era, yoe, fieldValues, resolverStyle);
}
}
if (fieldValues.containsKey(DAY_OF_YEAR)) {
return resolveYD(era, yoe, fieldValues, resolverStyle);
}
}
return null;
}
private int prolepticYearLenient(JapaneseEra era, int yearOfEra) {
return era.getPrivateEra().getSinceDate().getYear() + yearOfEra - 1;
}
private ChronoLocalDate resolveYMD(JapaneseEra era, int yoe, Map<TemporalField,Long> fieldValues, ResolverStyle resolverStyle) {
fieldValues.remove(ERA);
fieldValues.remove(YEAR_OF_ERA);
if (resolverStyle == ResolverStyle.LENIENT) {
int y = prolepticYearLenient(era, yoe);
long months = Math.subtractExact(fieldValues.remove(MONTH_OF_YEAR), 1);
long days = Math.subtractExact(fieldValues.remove(DAY_OF_MONTH), 1);
return date(y, 1, 1).plus(months, MONTHS).plus(days, DAYS);
}
int moy = range(MONTH_OF_YEAR).checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR), MONTH_OF_YEAR);
int dom = range(DAY_OF_MONTH).checkValidIntValue(fieldValues.remove(DAY_OF_MONTH), DAY_OF_MONTH);
if (resolverStyle == ResolverStyle.SMART) { // previous valid
if (yoe < 1) {
throw new DateTimeException("Invalid YearOfEra: " + yoe);
}
int y = prolepticYearLenient(era, yoe);
JapaneseDate result;
try {
result = date(y, moy, dom);
} catch (DateTimeException ex) {
result = date(y, moy, 1).with(TemporalAdjusters.lastDayOfMonth());
}
// handle the era being changed
// only allow if the new date is in the same Jan-Dec as the era change
// determine by ensuring either original yoe or result yoe is 1
if (result.getEra() != era && result.get(YEAR_OF_ERA) > 1 && yoe > 1) {
throw new DateTimeException("Invalid YearOfEra for Era: " + era + " " + yoe);
}
return result;
}
return date(era, yoe, moy, dom);
}
private ChronoLocalDate resolveYD(JapaneseEra era, int yoe, Map <TemporalField,Long> fieldValues, ResolverStyle resolverStyle) {
fieldValues.remove(ERA);
fieldValues.remove(YEAR_OF_ERA);
if (resolverStyle == ResolverStyle.LENIENT) {
int y = prolepticYearLenient(era, yoe);
long days = Math.subtractExact(fieldValues.remove(DAY_OF_YEAR), 1);
return dateYearDay(y, 1).plus(days, DAYS);
}
int doy = range(DAY_OF_YEAR).checkValidIntValue(fieldValues.remove(DAY_OF_YEAR), DAY_OF_YEAR);
return dateYearDay(era, yoe, doy); // smart is same as strict
}
//-----------------------------------------------------------------------
/**
* Writes the Chronology using a
* <a href="../../../serialized-form.html#java.time.chrono.Ser">dedicated serialized form</a>.
* @serialData
* <pre>
* out.writeByte(1); // identifies a Chronology
* out.writeUTF(getId());
* </pre>
*
* @return the instance of {@code Ser}, not null
*/
@Override
Object writeReplace() {
return super.writeReplace();
}
/**
* Defend against malicious streams.
*
* @param s the stream to read
* @throws InvalidObjectException always
*/
private void readObject(ObjectInputStream s) throws InvalidObjectException {
throw new InvalidObjectException("Deserialization via serialization delegate");
}
}

View file

@ -0,0 +1,757 @@
/*
* Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of JSR-310 nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package java.time.chrono;
import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH;
import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR;
import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH;
import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR;
import static java.time.temporal.ChronoField.DAY_OF_MONTH;
import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
import static java.time.temporal.ChronoField.YEAR;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.time.Clock;
import java.time.DateTimeException;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.Period;
import java.time.ZoneId;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalAdjuster;
import java.time.temporal.TemporalAmount;
import java.time.temporal.TemporalField;
import java.time.temporal.TemporalQuery;
import java.time.temporal.TemporalUnit;
import java.time.temporal.UnsupportedTemporalTypeException;
import java.time.temporal.ValueRange;
import java.util.Calendar;
import java.util.Objects;
import sun.util.calendar.CalendarDate;
import sun.util.calendar.LocalGregorianCalendar;
/**
* A date in the Japanese Imperial calendar system.
* <p>
* This date operates using the {@linkplain JapaneseChronology Japanese Imperial calendar}.
* This calendar system is primarily used in Japan.
* <p>
* The Japanese Imperial calendar system is the same as the ISO calendar system
* apart from the era-based year numbering. The proleptic-year is defined to be
* equal to the ISO proleptic-year.
* <p>
* Japan introduced the Gregorian calendar starting with Meiji 6.
* Only Meiji and later eras are supported;
* dates before Meiji 6, January 1 are not supported.
* <p>
* For example, the Japanese year "Heisei 24" corresponds to ISO year "2012".<br>
* Calling {@code japaneseDate.get(YEAR_OF_ERA)} will return 24.<br>
* Calling {@code japaneseDate.get(YEAR)} will return 2012.<br>
* Calling {@code japaneseDate.get(ERA)} will return 2, corresponding to
* {@code JapaneseChronology.ERA_HEISEI}.<br>
*
* <p>
* This is a <a href="{@docRoot}/java/lang/doc-files/ValueBased.html">value-based</a>
* class; use of identity-sensitive operations (including reference equality
* ({@code ==}), identity hash code, or synchronization) on instances of
* {@code JapaneseDate} may have unpredictable results and should be avoided.
* The {@code equals} method should be used for comparisons.
*
* @implSpec
* This class is immutable and thread-safe.
*
* @since 1.8
*/
public final class JapaneseDate
extends ChronoLocalDateImpl<JapaneseDate>
implements ChronoLocalDate, Serializable {
/**
* Serialization version.
*/
private static final long serialVersionUID = -305327627230580483L;
/**
* The underlying ISO local date.
*/
private final transient LocalDate isoDate;
/**
* The JapaneseEra of this date.
*/
private transient JapaneseEra era;
/**
* The Japanese imperial calendar year of this date.
*/
private transient int yearOfEra;
/**
* The first day supported by the JapaneseChronology is Meiji 6, January 1st.
*/
static final LocalDate MEIJI_6_ISODATE = LocalDate.of(1873, 1, 1);
//-----------------------------------------------------------------------
/**
* Obtains the current {@code JapaneseDate} from the system clock in the default time-zone.
* <p>
* This will query the {@link Clock#systemDefaultZone() system clock} in the default
* time-zone to obtain the current date.
* <p>
* Using this method will prevent the ability to use an alternate clock for testing
* because the clock is hard-coded.
*
* @return the current date using the system clock and default time-zone, not null
*/
public static JapaneseDate now() {
return now(Clock.systemDefaultZone());
}
/**
* Obtains the current {@code JapaneseDate} from the system clock in the specified time-zone.
* <p>
* This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date.
* Specifying the time-zone avoids dependence on the default time-zone.
* <p>
* Using this method will prevent the ability to use an alternate clock for testing
* because the clock is hard-coded.
*
* @param zone the zone ID to use, not null
* @return the current date using the system clock, not null
*/
public static JapaneseDate now(ZoneId zone) {
return now(Clock.system(zone));
}
/**
* Obtains the current {@code JapaneseDate} from the specified clock.
* <p>
* This will query the specified clock to obtain the current date - today.
* Using this method allows the use of an alternate clock for testing.
* The alternate clock may be introduced using {@linkplain Clock dependency injection}.
*
* @param clock the clock to use, not null
* @return the current date, not null
* @throws DateTimeException if the current date cannot be obtained
*/
public static JapaneseDate now(Clock clock) {
return new JapaneseDate(LocalDate.now(clock));
}
/**
* Obtains a {@code JapaneseDate} representing a date in the Japanese calendar
* system from the era, year-of-era, month-of-year and day-of-month fields.
* <p>
* This returns a {@code JapaneseDate} with the specified fields.
* The day must be valid for the year and month, otherwise an exception will be thrown.
* <p>
* The Japanese month and day-of-month are the same as those in the
* ISO calendar system. They are not reset when the era changes.
* For example:
* <pre>
* 6th Jan Showa 64 = ISO 1989-01-06
* 7th Jan Showa 64 = ISO 1989-01-07
* 8th Jan Heisei 1 = ISO 1989-01-08
* 9th Jan Heisei 1 = ISO 1989-01-09
* </pre>
*
* @param era the Japanese era, not null
* @param yearOfEra the Japanese year-of-era
* @param month the Japanese month-of-year, from 1 to 12
* @param dayOfMonth the Japanese day-of-month, from 1 to 31
* @return the date in Japanese calendar system, not null
* @throws DateTimeException if the value of any field is out of range,
* or if the day-of-month is invalid for the month-year,
* or if the date is not a Japanese era
*/
public static JapaneseDate of(JapaneseEra era, int yearOfEra, int month, int dayOfMonth) {
Objects.requireNonNull(era, "era");
LocalGregorianCalendar.Date jdate = JapaneseChronology.JCAL.newCalendarDate(null);
jdate.setEra(era.getPrivateEra()).setDate(yearOfEra, month, dayOfMonth);
if (!JapaneseChronology.JCAL.validate(jdate)) {
throw new DateTimeException("year, month, and day not valid for Era");
}
LocalDate date = LocalDate.of(jdate.getNormalizedYear(), month, dayOfMonth);
return new JapaneseDate(era, yearOfEra, date);
}
/**
* Obtains a {@code JapaneseDate} representing a date in the Japanese calendar
* system from the proleptic-year, month-of-year and day-of-month fields.
* <p>
* This returns a {@code JapaneseDate} with the specified fields.
* The day must be valid for the year and month, otherwise an exception will be thrown.
* <p>
* The Japanese proleptic year, month and day-of-month are the same as those
* in the ISO calendar system. They are not reset when the era changes.
*
* @param prolepticYear the Japanese proleptic-year
* @param month the Japanese month-of-year, from 1 to 12
* @param dayOfMonth the Japanese day-of-month, from 1 to 31
* @return the date in Japanese calendar system, not null
* @throws DateTimeException if the value of any field is out of range,
* or if the day-of-month is invalid for the month-year
*/
public static JapaneseDate of(int prolepticYear, int month, int dayOfMonth) {
return new JapaneseDate(LocalDate.of(prolepticYear, month, dayOfMonth));
}
/**
* Obtains a {@code JapaneseDate} representing a date in the Japanese calendar
* system from the era, year-of-era and day-of-year fields.
* <p>
* This returns a {@code JapaneseDate} with the specified fields.
* The day must be valid for the year, otherwise an exception will be thrown.
* <p>
* The day-of-year in this factory is expressed relative to the start of the year-of-era.
* This definition changes the normal meaning of day-of-year only in those years
* where the year-of-era is reset to one due to a change in the era.
* For example:
* <pre>
* 6th Jan Showa 64 = day-of-year 6
* 7th Jan Showa 64 = day-of-year 7
* 8th Jan Heisei 1 = day-of-year 1
* 9th Jan Heisei 1 = day-of-year 2
* </pre>
*
* @param era the Japanese era, not null
* @param yearOfEra the Japanese year-of-era
* @param dayOfYear the chronology day-of-year, from 1 to 366
* @return the date in Japanese calendar system, not null
* @throws DateTimeException if the value of any field is out of range,
* or if the day-of-year is invalid for the year
*/
static JapaneseDate ofYearDay(JapaneseEra era, int yearOfEra, int dayOfYear) {
Objects.requireNonNull(era, "era");
CalendarDate firstDay = era.getPrivateEra().getSinceDate();
LocalGregorianCalendar.Date jdate = JapaneseChronology.JCAL.newCalendarDate(null);
jdate.setEra(era.getPrivateEra());
if (yearOfEra == 1) {
jdate.setDate(yearOfEra, firstDay.getMonth(), firstDay.getDayOfMonth() + dayOfYear - 1);
} else {
jdate.setDate(yearOfEra, 1, dayOfYear);
}
JapaneseChronology.JCAL.normalize(jdate);
if (era.getPrivateEra() != jdate.getEra() || yearOfEra != jdate.getYear()) {
throw new DateTimeException("Invalid parameters");
}
LocalDate localdate = LocalDate.of(jdate.getNormalizedYear(),
jdate.getMonth(), jdate.getDayOfMonth());
return new JapaneseDate(era, yearOfEra, localdate);
}
/**
* Obtains a {@code JapaneseDate} from a temporal object.
* <p>
* This obtains a date in the Japanese calendar system based on the specified temporal.
* A {@code TemporalAccessor} represents an arbitrary set of date and time information,
* which this factory converts to an instance of {@code JapaneseDate}.
* <p>
* The conversion typically uses the {@link ChronoField#EPOCH_DAY EPOCH_DAY}
* field, which is standardized across calendar systems.
* <p>
* This method matches the signature of the functional interface {@link TemporalQuery}
* allowing it to be used as a query via method reference, {@code JapaneseDate::from}.
*
* @param temporal the temporal object to convert, not null
* @return the date in Japanese calendar system, not null
* @throws DateTimeException if unable to convert to a {@code JapaneseDate}
*/
public static JapaneseDate from(TemporalAccessor temporal) {
return JapaneseChronology.INSTANCE.date(temporal);
}
//-----------------------------------------------------------------------
/**
* Creates an instance from an ISO date.
*
* @param isoDate the standard local date, validated not null
*/
JapaneseDate(LocalDate isoDate) {
if (isoDate.isBefore(MEIJI_6_ISODATE)) {
throw new DateTimeException("JapaneseDate before Meiji 6 is not supported");
}
LocalGregorianCalendar.Date jdate = toPrivateJapaneseDate(isoDate);
this.era = JapaneseEra.toJapaneseEra(jdate.getEra());
this.yearOfEra = jdate.getYear();
this.isoDate = isoDate;
}
/**
* Constructs a {@code JapaneseDate}. This constructor does NOT validate the given parameters,
* and {@code era} and {@code year} must agree with {@code isoDate}.
*
* @param era the era, validated not null
* @param year the year-of-era, validated
* @param isoDate the standard local date, validated not null
*/
JapaneseDate(JapaneseEra era, int year, LocalDate isoDate) {
if (isoDate.isBefore(MEIJI_6_ISODATE)) {
throw new DateTimeException("JapaneseDate before Meiji 6 is not supported");
}
this.era = era;
this.yearOfEra = year;
this.isoDate = isoDate;
}
//-----------------------------------------------------------------------
/**
* Gets the chronology of this date, which is the Japanese calendar system.
* <p>
* The {@code Chronology} represents the calendar system in use.
* The era and other fields in {@link ChronoField} are defined by the chronology.
*
* @return the Japanese chronology, not null
*/
@Override
public JapaneseChronology getChronology() {
return JapaneseChronology.INSTANCE;
}
/**
* Gets the era applicable at this date.
* <p>
* The Japanese calendar system has multiple eras defined by {@link JapaneseEra}.
*
* @return the era applicable at this date, not null
*/
@Override
public JapaneseEra getEra() {
return era;
}
/**
* Returns the length of the month represented by this date.
* <p>
* This returns the length of the month in days.
* Month lengths match those of the ISO calendar system.
*
* @return the length of the month in days
*/
@Override
public int lengthOfMonth() {
return isoDate.lengthOfMonth();
}
@Override
public int lengthOfYear() {
Calendar jcal = Calendar.getInstance(JapaneseChronology.LOCALE);
jcal.set(Calendar.ERA, era.getValue() + JapaneseEra.ERA_OFFSET);
jcal.set(yearOfEra, isoDate.getMonthValue() - 1, isoDate.getDayOfMonth());
return jcal.getActualMaximum(Calendar.DAY_OF_YEAR);
}
//-----------------------------------------------------------------------
/**
* Checks if the specified field is supported.
* <p>
* This checks if this date can be queried for the specified field.
* If false, then calling the {@link #range(TemporalField) range} and
* {@link #get(TemporalField) get} methods will throw an exception.
* <p>
* If the field is a {@link ChronoField} then the query is implemented here.
* The supported fields are:
* <ul>
* <li>{@code DAY_OF_WEEK}
* <li>{@code DAY_OF_MONTH}
* <li>{@code DAY_OF_YEAR}
* <li>{@code EPOCH_DAY}
* <li>{@code MONTH_OF_YEAR}
* <li>{@code PROLEPTIC_MONTH}
* <li>{@code YEAR_OF_ERA}
* <li>{@code YEAR}
* <li>{@code ERA}
* </ul>
* All other {@code ChronoField} instances will return false.
* <p>
* If the field is not a {@code ChronoField}, then the result of this method
* is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}
* passing {@code this} as the argument.
* Whether the field is supported is determined by the field.
*
* @param field the field to check, null returns false
* @return true if the field is supported on this date, false if not
*/
@Override
public boolean isSupported(TemporalField field) {
if (field == ALIGNED_DAY_OF_WEEK_IN_MONTH || field == ALIGNED_DAY_OF_WEEK_IN_YEAR ||
field == ALIGNED_WEEK_OF_MONTH || field == ALIGNED_WEEK_OF_YEAR) {
return false;
}
return super.isSupported(field);
}
@Override
public ValueRange range(TemporalField field) {
if (field instanceof ChronoField) {
if (isSupported(field)) {
ChronoField f = (ChronoField) field;
switch (f) {
case DAY_OF_MONTH: return ValueRange.of(1, lengthOfMonth());
case DAY_OF_YEAR: return ValueRange.of(1, lengthOfYear());
case YEAR_OF_ERA: {
Calendar jcal = Calendar.getInstance(JapaneseChronology.LOCALE);
jcal.set(Calendar.ERA, era.getValue() + JapaneseEra.ERA_OFFSET);
jcal.set(yearOfEra, isoDate.getMonthValue() - 1, isoDate.getDayOfMonth());
return ValueRange.of(1, jcal.getActualMaximum(Calendar.YEAR));
}
}
return getChronology().range(f);
}
throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
return field.rangeRefinedBy(this);
}
@Override
public long getLong(TemporalField field) {
if (field instanceof ChronoField) {
// same as ISO:
// DAY_OF_WEEK, DAY_OF_MONTH, EPOCH_DAY, MONTH_OF_YEAR, PROLEPTIC_MONTH, YEAR
//
// calendar specific fields
// DAY_OF_YEAR, YEAR_OF_ERA, ERA
switch ((ChronoField) field) {
case ALIGNED_DAY_OF_WEEK_IN_MONTH:
case ALIGNED_DAY_OF_WEEK_IN_YEAR:
case ALIGNED_WEEK_OF_MONTH:
case ALIGNED_WEEK_OF_YEAR:
throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
case YEAR_OF_ERA:
return yearOfEra;
case ERA:
return era.getValue();
case DAY_OF_YEAR:
Calendar jcal = Calendar.getInstance(JapaneseChronology.LOCALE);
jcal.set(Calendar.ERA, era.getValue() + JapaneseEra.ERA_OFFSET);
jcal.set(yearOfEra, isoDate.getMonthValue() - 1, isoDate.getDayOfMonth());
return jcal.get(Calendar.DAY_OF_YEAR);
}
return isoDate.getLong(field);
}
return field.getFrom(this);
}
/**
* Returns a {@code LocalGregorianCalendar.Date} converted from the given {@code isoDate}.
*
* @param isoDate the local date, not null
* @return a {@code LocalGregorianCalendar.Date}, not null
*/
private static LocalGregorianCalendar.Date toPrivateJapaneseDate(LocalDate isoDate) {
LocalGregorianCalendar.Date jdate = JapaneseChronology.JCAL.newCalendarDate(null);
sun.util.calendar.Era sunEra = JapaneseEra.privateEraFrom(isoDate);
int year = isoDate.getYear();
if (sunEra != null) {
year -= sunEra.getSinceDate().getYear() - 1;
}
jdate.setEra(sunEra).setYear(year).setMonth(isoDate.getMonthValue()).setDayOfMonth(isoDate.getDayOfMonth());
JapaneseChronology.JCAL.normalize(jdate);
return jdate;
}
//-----------------------------------------------------------------------
@Override
public JapaneseDate with(TemporalField field, long newValue) {
if (field instanceof ChronoField) {
ChronoField f = (ChronoField) field;
if (getLong(f) == newValue) { // getLong() validates for supported fields
return this;
}
switch (f) {
case YEAR_OF_ERA:
case YEAR:
case ERA: {
int nvalue = getChronology().range(f).checkValidIntValue(newValue, f);
switch (f) {
case YEAR_OF_ERA:
return this.withYear(nvalue);
case YEAR:
return with(isoDate.withYear(nvalue));
case ERA: {
return this.withYear(JapaneseEra.of(nvalue), yearOfEra);
}
}
}
}
// YEAR, PROLEPTIC_MONTH and others are same as ISO
return with(isoDate.with(field, newValue));
}
return super.with(field, newValue);
}
/**
* {@inheritDoc}
* @throws DateTimeException {@inheritDoc}
* @throws ArithmeticException {@inheritDoc}
*/
@Override
public JapaneseDate with(TemporalAdjuster adjuster) {
return super.with(adjuster);
}
/**
* {@inheritDoc}
* @throws DateTimeException {@inheritDoc}
* @throws ArithmeticException {@inheritDoc}
*/
@Override
public JapaneseDate plus(TemporalAmount amount) {
return super.plus(amount);
}
/**
* {@inheritDoc}
* @throws DateTimeException {@inheritDoc}
* @throws ArithmeticException {@inheritDoc}
*/
@Override
public JapaneseDate minus(TemporalAmount amount) {
return super.minus(amount);
}
//-----------------------------------------------------------------------
/**
* Returns a copy of this date with the year altered.
* <p>
* This method changes the year of the date.
* If the month-day is invalid for the year, then the previous valid day
* will be selected instead.
* <p>
* This instance is immutable and unaffected by this method call.
*
* @param era the era to set in the result, not null
* @param yearOfEra the year-of-era to set in the returned date
* @return a {@code JapaneseDate} based on this date with the requested year, never null
* @throws DateTimeException if {@code year} is invalid
*/
private JapaneseDate withYear(JapaneseEra era, int yearOfEra) {
int year = JapaneseChronology.INSTANCE.prolepticYear(era, yearOfEra);
return with(isoDate.withYear(year));
}
/**
* Returns a copy of this date with the year-of-era altered.
* <p>
* This method changes the year-of-era of the date.
* If the month-day is invalid for the year, then the previous valid day
* will be selected instead.
* <p>
* This instance is immutable and unaffected by this method call.
*
* @param year the year to set in the returned date
* @return a {@code JapaneseDate} based on this date with the requested year-of-era, never null
* @throws DateTimeException if {@code year} is invalid
*/
private JapaneseDate withYear(int year) {
return withYear(getEra(), year);
}
//-----------------------------------------------------------------------
@Override
JapaneseDate plusYears(long years) {
return with(isoDate.plusYears(years));
}
@Override
JapaneseDate plusMonths(long months) {
return with(isoDate.plusMonths(months));
}
@Override
JapaneseDate plusWeeks(long weeksToAdd) {
return with(isoDate.plusWeeks(weeksToAdd));
}
@Override
JapaneseDate plusDays(long days) {
return with(isoDate.plusDays(days));
}
@Override
public JapaneseDate plus(long amountToAdd, TemporalUnit unit) {
return super.plus(amountToAdd, unit);
}
@Override
public JapaneseDate minus(long amountToAdd, TemporalUnit unit) {
return super.minus(amountToAdd, unit);
}
@Override
JapaneseDate minusYears(long yearsToSubtract) {
return super.minusYears(yearsToSubtract);
}
@Override
JapaneseDate minusMonths(long monthsToSubtract) {
return super.minusMonths(monthsToSubtract);
}
@Override
JapaneseDate minusWeeks(long weeksToSubtract) {
return super.minusWeeks(weeksToSubtract);
}
@Override
JapaneseDate minusDays(long daysToSubtract) {
return super.minusDays(daysToSubtract);
}
private JapaneseDate with(LocalDate newDate) {
return (newDate.equals(isoDate) ? this : new JapaneseDate(newDate));
}
@Override // for javadoc and covariant return type
@SuppressWarnings("unchecked")
public final ChronoLocalDateTime<JapaneseDate> atTime(LocalTime localTime) {
return (ChronoLocalDateTime<JapaneseDate>)super.atTime(localTime);
}
@Override
public ChronoPeriod until(ChronoLocalDate endDate) {
Period period = isoDate.until(endDate);
return getChronology().period(period.getYears(), period.getMonths(), period.getDays());
}
@Override // override for performance
public long toEpochDay() {
return isoDate.toEpochDay();
}
//-------------------------------------------------------------------------
/**
* Compares this date to another date, including the chronology.
* <p>
* Compares this {@code JapaneseDate} with another ensuring that the date is the same.
* <p>
* Only objects of type {@code JapaneseDate} are compared, other types return false.
* To compare the dates of two {@code TemporalAccessor} instances, including dates
* in two different chronologies, use {@link ChronoField#EPOCH_DAY} as a comparator.
*
* @param obj the object to check, null returns false
* @return true if this is equal to the other date
*/
@Override // override for performance
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof JapaneseDate) {
JapaneseDate otherDate = (JapaneseDate) obj;
return this.isoDate.equals(otherDate.isoDate);
}
return false;
}
/**
* A hash code for this date.
*
* @return a suitable hash code based only on the Chronology and the date
*/
@Override // override for performance
public int hashCode() {
return getChronology().getId().hashCode() ^ isoDate.hashCode();
}
//-----------------------------------------------------------------------
/**
* Defend against malicious streams.
*
* @param s the stream to read
* @throws InvalidObjectException always
*/
private void readObject(ObjectInputStream s) throws InvalidObjectException {
throw new InvalidObjectException("Deserialization via serialization delegate");
}
/**
* Writes the object using a
* <a href="../../../serialized-form.html#java.time.chrono.Ser">dedicated serialized form</a>.
* @serialData
* <pre>
* out.writeByte(4); // identifies a JapaneseDate
* out.writeInt(get(YEAR));
* out.writeByte(get(MONTH_OF_YEAR));
* out.writeByte(get(DAY_OF_MONTH));
* </pre>
*
* @return the instance of {@code Ser}, not null
*/
private Object writeReplace() {
return new Ser(Ser.JAPANESE_DATE_TYPE, this);
}
void writeExternal(DataOutput out) throws IOException {
// JapaneseChronology is implicit in the JAPANESE_DATE_TYPE
out.writeInt(get(YEAR));
out.writeByte(get(MONTH_OF_YEAR));
out.writeByte(get(DAY_OF_MONTH));
}
static JapaneseDate readExternal(DataInput in) throws IOException {
int year = in.readInt();
int month = in.readByte();
int dayOfMonth = in.readByte();
return JapaneseChronology.INSTANCE.date(year, month, dayOfMonth);
}
}

View file

@ -0,0 +1,414 @@
/*
* Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* This file is available under and governed by the GNU General Public
* License version 2 only, as published by the Free Software Foundation.
* However, the following notice accompanied the original version of this
* file:
*
* Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of JSR-310 nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package java.time.chrono;
import static java.time.chrono.JapaneseDate.MEIJI_6_ISODATE;
import static java.time.temporal.ChronoField.ERA;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.time.DateTimeException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.TextStyle;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalField;
import java.time.temporal.UnsupportedTemporalTypeException;
import java.time.temporal.ValueRange;
import java.util.Arrays;
import java.util.Locale;
import java.util.Objects;
import sun.util.calendar.CalendarDate;
/**
* An era in the Japanese Imperial calendar system.
* <p>
* This class defines the valid eras for the Japanese chronology.
* Japan introduced the Gregorian calendar starting with Meiji 6.
* Only Meiji and later eras are supported;
* dates before Meiji 6, January 1 are not supported.
*
* @implSpec
* This class is immutable and thread-safe.
*
* @since 1.8
*/
public final class JapaneseEra
implements Era, Serializable {
// The offset value to 0-based index from the era value.
// i.e., getValue() + ERA_OFFSET == 0-based index
static final int ERA_OFFSET = 2;
static final sun.util.calendar.Era[] ERA_CONFIG;
/**
* The singleton instance for the 'Meiji' era (1868-01-01 - 1912-07-29)
* which has the value -1.
*/
public static final JapaneseEra MEIJI = new JapaneseEra(-1, LocalDate.of(1868, 1, 1));
/**
* The singleton instance for the 'Taisho' era (1912-07-30 - 1926-12-24)
* which has the value 0.
*/
public static final JapaneseEra TAISHO = new JapaneseEra(0, LocalDate.of(1912, 7, 30));
/**
* The singleton instance for the 'Showa' era (1926-12-25 - 1989-01-07)
* which has the value 1.
*/
public static final JapaneseEra SHOWA = new JapaneseEra(1, LocalDate.of(1926, 12, 25));
/**
* The singleton instance for the 'Heisei' era (1989-01-08 - current)
* which has the value 2.
*/
public static final JapaneseEra HEISEI = new JapaneseEra(2, LocalDate.of(1989, 1, 8));
// The number of predefined JapaneseEra constants.
// There may be a supplemental era defined by the property.
private static final int N_ERA_CONSTANTS = HEISEI.getValue() + ERA_OFFSET;
/**
* Serialization version.
*/
private static final long serialVersionUID = 1466499369062886794L;
// array for the singleton JapaneseEra instances
private static final JapaneseEra[] KNOWN_ERAS;
static {
ERA_CONFIG = JapaneseChronology.JCAL.getEras();
KNOWN_ERAS = new JapaneseEra[ERA_CONFIG.length];
KNOWN_ERAS[0] = MEIJI;
KNOWN_ERAS[1] = TAISHO;
KNOWN_ERAS[2] = SHOWA;
KNOWN_ERAS[3] = HEISEI;
for (int i = N_ERA_CONSTANTS; i < ERA_CONFIG.length; i++) {
CalendarDate date = ERA_CONFIG[i].getSinceDate();
LocalDate isoDate = LocalDate.of(date.getYear(), date.getMonth(), date.getDayOfMonth());
KNOWN_ERAS[i] = new JapaneseEra(i - ERA_OFFSET + 1, isoDate);
}
};
/**
* The era value.
* @serial
*/
private final transient int eraValue;
// the first day of the era
private final transient LocalDate since;
/**
* Creates an instance.
*
* @param eraValue the era value, validated
* @param since the date representing the first date of the era, validated not null
*/
private JapaneseEra(int eraValue, LocalDate since) {
this.eraValue = eraValue;
this.since = since;
}
//-----------------------------------------------------------------------
/**
* Returns the Sun private Era instance corresponding to this {@code JapaneseEra}.
*
* @return the Sun private Era instance for this {@code JapaneseEra}.
*/
sun.util.calendar.Era getPrivateEra() {
return ERA_CONFIG[ordinal(eraValue)];
}
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code JapaneseEra} from an {@code int} value.
* <p>
* The {@link #SHOWA} era that contains 1970-01-01 (ISO calendar system) has the value 1
* Later era is numbered 2 ({@link #HEISEI}). Earlier eras are numbered 0 ({@link #TAISHO}),
* -1 ({@link #MEIJI}), only Meiji and later eras are supported.
*
* @param japaneseEra the era to represent
* @return the {@code JapaneseEra} singleton, not null
* @throws DateTimeException if the value is invalid
*/
public static JapaneseEra of(int japaneseEra) {
int i = ordinal(japaneseEra);
if (i < 0 || i >= KNOWN_ERAS.length) {
throw new DateTimeException("Invalid era: " + japaneseEra);
}
return KNOWN_ERAS[i];
}
/**
* Returns the {@code JapaneseEra} with the name.
* <p>
* The string must match exactly the name of the era.
* (Extraneous whitespace characters are not permitted.)
*
* @param japaneseEra the japaneseEra name; non-null
* @return the {@code JapaneseEra} singleton, never null
* @throws IllegalArgumentException if there is not JapaneseEra with the specified name
*/
public static JapaneseEra valueOf(String japaneseEra) {
Objects.requireNonNull(japaneseEra, "japaneseEra");
for (JapaneseEra era : KNOWN_ERAS) {
if (era.getName().equals(japaneseEra)) {
return era;
}
}
throw new IllegalArgumentException("japaneseEra is invalid");
}
/**
* Returns an array of JapaneseEras.
* <p>
* This method may be used to iterate over the JapaneseEras as follows:
* <pre>
* for (JapaneseEra c : JapaneseEra.values())
* System.out.println(c);
* </pre>
*
* @return an array of JapaneseEras
*/
public static JapaneseEra[] values() {
return Arrays.copyOf(KNOWN_ERAS, KNOWN_ERAS.length);
}
/**
* {@inheritDoc}
*
* @param style {@inheritDoc}
* @param locale {@inheritDoc}
*/
@Override
public String getDisplayName(TextStyle style, Locale locale) {
// If this JapaneseEra is a supplemental one, obtain the name from
// the era definition.
if (getValue() > N_ERA_CONSTANTS - ERA_OFFSET) {
Objects.requireNonNull(locale, "locale");
return style.asNormal() == TextStyle.NARROW ? getAbbreviation() : getName();
}
return new DateTimeFormatterBuilder()
.appendText(ERA, style)
.toFormatter(locale)
.withChronology(JapaneseChronology.INSTANCE)
.format(this == MEIJI ? MEIJI_6_ISODATE : since);
}
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code JapaneseEra} from a date.
*
* @param date the date, not null
* @return the Era singleton, never null
*/
static JapaneseEra from(LocalDate date) {
if (date.isBefore(MEIJI_6_ISODATE)) {
throw new DateTimeException("JapaneseDate before Meiji 6 are not supported");
}
for (int i = KNOWN_ERAS.length - 1; i > 0; i--) {
JapaneseEra era = KNOWN_ERAS[i];
if (date.compareTo(era.since) >= 0) {
return era;
}
}
return null;
}
static JapaneseEra toJapaneseEra(sun.util.calendar.Era privateEra) {
for (int i = ERA_CONFIG.length - 1; i >= 0; i--) {
if (ERA_CONFIG[i].equals(privateEra)) {
return KNOWN_ERAS[i];
}
}
return null;
}
static sun.util.calendar.Era privateEraFrom(LocalDate isoDate) {
for (int i = KNOWN_ERAS.length - 1; i > 0; i--) {
JapaneseEra era = KNOWN_ERAS[i];
if (isoDate.compareTo(era.since) >= 0) {
return ERA_CONFIG[i];
}
}
return null;
}
/**
* Returns the index into the arrays from the Era value.
* the eraValue is a valid Era number, -1..2.
*
* @param eraValue the era value to convert to the index
* @return the index of the current Era
*/
private static int ordinal(int eraValue) {
return eraValue + ERA_OFFSET - 1;
}
//-----------------------------------------------------------------------
/**
* Gets the numeric era {@code int} value.
* <p>
* The {@link #SHOWA} era that contains 1970-01-01 (ISO calendar system) has the value 1.
* Later eras are numbered from 2 ({@link #HEISEI}).
* Earlier eras are numbered 0 ({@link #TAISHO}), -1 ({@link #MEIJI})).
*
* @return the era value
*/
@Override
public int getValue() {
return eraValue;
}
//-----------------------------------------------------------------------
/**
* Gets the range of valid values for the specified field.
* <p>
* The range object expresses the minimum and maximum valid values for a field.
* This era is used to enhance the accuracy of the returned range.
* If it is not possible to return the range, because the field is not supported
* or for some other reason, an exception is thrown.
* <p>
* If the field is a {@link ChronoField} then the query is implemented here.
* The {@code ERA} field returns the range.
* All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
* <p>
* If the field is not a {@code ChronoField}, then the result of this method
* is obtained by invoking {@code TemporalField.rangeRefinedBy(TemporalAccessor)}
* passing {@code this} as the argument.
* Whether the range can be obtained is determined by the field.
* <p>
* The range of valid Japanese eras can change over time due to the nature
* of the Japanese calendar system.
*
* @param field the field to query the range for, not null
* @return the range of valid values for the field, not null
* @throws DateTimeException if the range for the field cannot be obtained
* @throws UnsupportedTemporalTypeException if the unit is not supported
*/
@Override // override as super would return range from 0 to 1
public ValueRange range(TemporalField field) {
if (field == ERA) {
return JapaneseChronology.INSTANCE.range(ERA);
}
return Era.super.range(field);
}
//-----------------------------------------------------------------------
String getAbbreviation() {
return ERA_CONFIG[ordinal(getValue())].getAbbreviation();
}
String getName() {
return ERA_CONFIG[ordinal(getValue())].getName();
}
@Override
public String toString() {
return getName();
}
//-----------------------------------------------------------------------
/**
* Defend against malicious streams.
*
* @param s the stream to read
* @throws InvalidObjectException always
*/
private void readObject(ObjectInputStream s) throws InvalidObjectException {
throw new InvalidObjectException("Deserialization via serialization delegate");
}
//-----------------------------------------------------------------------
/**
* Writes the object using a
* <a href="../../../serialized-form.html#java.time.chrono.Ser">dedicated serialized form</a>.
* @serialData
* <pre>
* out.writeByte(5); // identifies a JapaneseEra
* out.writeInt(getValue());
* </pre>
*
* @return the instance of {@code Ser}, not null
*/
private Object writeReplace() {
return new Ser(Ser.JAPANESE_ERA_TYPE, this);
}
void writeExternal(DataOutput out) throws IOException {
out.writeByte(this.getValue());
}
static JapaneseEra readExternal(DataInput in) throws IOException {
byte eraValue = in.readByte();
return JapaneseEra.of(eraValue);
}
}

View file

@ -0,0 +1,363 @@
/*
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of JSR-310 nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package java.time.chrono;
import java.io.InvalidObjectException;
import static java.time.temporal.ChronoField.PROLEPTIC_MONTH;
import static java.time.temporal.ChronoField.YEAR;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.time.Clock;
import java.time.DateTimeException;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.ResolverStyle;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalField;
import java.time.temporal.ValueRange;
import java.util.List;
import java.util.Locale;
import java.util.Map;
/**
* The Minguo calendar system.
* <p>
* This chronology defines the rules of the Minguo calendar system.
* This calendar system is primarily used in the Republic of China, often known as Taiwan.
* Dates are aligned such that {@code 0001-01-01 (Minguo)} is {@code 1912-01-01 (ISO)}.
* <p>
* The fields are defined as follows:
* <ul>
* <li>era - There are two eras, the current 'Republic' (ERA_ROC) and the previous era (ERA_BEFORE_ROC).
* <li>year-of-era - The year-of-era for the current era increases uniformly from the epoch at year one.
* For the previous era the year increases from one as time goes backwards.
* The value for the current era is equal to the ISO proleptic-year minus 1911.
* <li>proleptic-year - The proleptic year is the same as the year-of-era for the
* current era. For the previous era, years have zero, then negative values.
* The value is equal to the ISO proleptic-year minus 1911.
* <li>month-of-year - The Minguo month-of-year exactly matches ISO.
* <li>day-of-month - The Minguo day-of-month exactly matches ISO.
* <li>day-of-year - The Minguo day-of-year exactly matches ISO.
* <li>leap-year - The Minguo leap-year pattern exactly matches ISO, such that the two calendars
* are never out of step.
* </ul>
*
* @implSpec
* This class is immutable and thread-safe.
*
* @since 1.8
*/
public final class MinguoChronology extends AbstractChronology implements Serializable {
/**
* Singleton instance for the Minguo chronology.
*/
public static final MinguoChronology INSTANCE = new MinguoChronology();
/**
* Serialization version.
*/
private static final long serialVersionUID = 1039765215346859963L;
/**
* The difference in years between ISO and Minguo.
*/
static final int YEARS_DIFFERENCE = 1911;
/**
* Restricted constructor.
*/
private MinguoChronology() {
}
//-----------------------------------------------------------------------
/**
* Gets the ID of the chronology - 'Minguo'.
* <p>
* The ID uniquely identifies the {@code Chronology}.
* It can be used to lookup the {@code Chronology} using {@link Chronology#of(String)}.
*
* @return the chronology ID - 'Minguo'
* @see #getCalendarType()
*/
@Override
public String getId() {
return "Minguo";
}
/**
* Gets the calendar type of the underlying calendar system - 'roc'.
* <p>
* The calendar type is an identifier defined by the
* <em>Unicode Locale Data Markup Language (LDML)</em> specification.
* It can be used to lookup the {@code Chronology} using {@link Chronology#of(String)}.
* It can also be used as part of a locale, accessible via
* {@link Locale#getUnicodeLocaleType(String)} with the key 'ca'.
*
* @return the calendar system type - 'roc'
* @see #getId()
*/
@Override
public String getCalendarType() {
return "roc";
}
//-----------------------------------------------------------------------
/**
* Obtains a local date in Minguo calendar system from the
* era, year-of-era, month-of-year and day-of-month fields.
*
* @param era the Minguo era, not null
* @param yearOfEra the year-of-era
* @param month the month-of-year
* @param dayOfMonth the day-of-month
* @return the Minguo local date, not null
* @throws DateTimeException if unable to create the date
* @throws ClassCastException if the {@code era} is not a {@code MinguoEra}
*/
@Override
public MinguoDate date(Era era, int yearOfEra, int month, int dayOfMonth) {
return date(prolepticYear(era, yearOfEra), month, dayOfMonth);
}
/**
* Obtains a local date in Minguo calendar system from the
* proleptic-year, month-of-year and day-of-month fields.
*
* @param prolepticYear the proleptic-year
* @param month the month-of-year
* @param dayOfMonth the day-of-month
* @return the Minguo local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override
public MinguoDate date(int prolepticYear, int month, int dayOfMonth) {
return new MinguoDate(LocalDate.of(prolepticYear + YEARS_DIFFERENCE, month, dayOfMonth));
}
/**
* Obtains a local date in Minguo calendar system from the
* era, year-of-era and day-of-year fields.
*
* @param era the Minguo era, not null
* @param yearOfEra the year-of-era
* @param dayOfYear the day-of-year
* @return the Minguo local date, not null
* @throws DateTimeException if unable to create the date
* @throws ClassCastException if the {@code era} is not a {@code MinguoEra}
*/
@Override
public MinguoDate dateYearDay(Era era, int yearOfEra, int dayOfYear) {
return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear);
}
/**
* Obtains a local date in Minguo calendar system from the
* proleptic-year and day-of-year fields.
*
* @param prolepticYear the proleptic-year
* @param dayOfYear the day-of-year
* @return the Minguo local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override
public MinguoDate dateYearDay(int prolepticYear, int dayOfYear) {
return new MinguoDate(LocalDate.ofYearDay(prolepticYear + YEARS_DIFFERENCE, dayOfYear));
}
/**
* Obtains a local date in the Minguo calendar system from the epoch-day.
*
* @param epochDay the epoch day
* @return the Minguo local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override // override with covariant return type
public MinguoDate dateEpochDay(long epochDay) {
return new MinguoDate(LocalDate.ofEpochDay(epochDay));
}
@Override
public MinguoDate dateNow() {
return dateNow(Clock.systemDefaultZone());
}
@Override
public MinguoDate dateNow(ZoneId zone) {
return dateNow(Clock.system(zone));
}
@Override
public MinguoDate dateNow(Clock clock) {
return date(LocalDate.now(clock));
}
@Override
public MinguoDate date(TemporalAccessor temporal) {
if (temporal instanceof MinguoDate) {
return (MinguoDate) temporal;
}
return new MinguoDate(LocalDate.from(temporal));
}
@Override
@SuppressWarnings("unchecked")
public ChronoLocalDateTime<MinguoDate> localDateTime(TemporalAccessor temporal) {
return (ChronoLocalDateTime<MinguoDate>)super.localDateTime(temporal);
}
@Override
@SuppressWarnings("unchecked")
public ChronoZonedDateTime<MinguoDate> zonedDateTime(TemporalAccessor temporal) {
return (ChronoZonedDateTime<MinguoDate>)super.zonedDateTime(temporal);
}
@Override
@SuppressWarnings("unchecked")
public ChronoZonedDateTime<MinguoDate> zonedDateTime(Instant instant, ZoneId zone) {
return (ChronoZonedDateTime<MinguoDate>)super.zonedDateTime(instant, zone);
}
//-----------------------------------------------------------------------
/**
* Checks if the specified year is a leap year.
* <p>
* Minguo leap years occur exactly in line with ISO leap years.
* This method does not validate the year passed in, and only has a
* well-defined result for years in the supported range.
*
* @param prolepticYear the proleptic-year to check, not validated for range
* @return true if the year is a leap year
*/
@Override
public boolean isLeapYear(long prolepticYear) {
return IsoChronology.INSTANCE.isLeapYear(prolepticYear + YEARS_DIFFERENCE);
}
@Override
public int prolepticYear(Era era, int yearOfEra) {
if (era instanceof MinguoEra == false) {
throw new ClassCastException("Era must be MinguoEra");
}
return (era == MinguoEra.ROC ? yearOfEra : 1 - yearOfEra);
}
@Override
public MinguoEra eraOf(int eraValue) {
return MinguoEra.of(eraValue);
}
@Override
public List<Era> eras() {
return List.of(MinguoEra.values());
}
//-----------------------------------------------------------------------
@Override
public ValueRange range(ChronoField field) {
switch (field) {
case PROLEPTIC_MONTH: {
ValueRange range = PROLEPTIC_MONTH.range();
return ValueRange.of(range.getMinimum() - YEARS_DIFFERENCE * 12L, range.getMaximum() - YEARS_DIFFERENCE * 12L);
}
case YEAR_OF_ERA: {
ValueRange range = YEAR.range();
return ValueRange.of(1, range.getMaximum() - YEARS_DIFFERENCE, -range.getMinimum() + 1 + YEARS_DIFFERENCE);
}
case YEAR: {
ValueRange range = YEAR.range();
return ValueRange.of(range.getMinimum() - YEARS_DIFFERENCE, range.getMaximum() - YEARS_DIFFERENCE);
}
}
return field.range();
}
//-----------------------------------------------------------------------
@Override // override for return type
public MinguoDate resolveDate(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
return (MinguoDate) super.resolveDate(fieldValues, resolverStyle);
}
//-----------------------------------------------------------------------
/**
* Writes the Chronology using a
* <a href="../../../serialized-form.html#java.time.chrono.Ser">dedicated serialized form</a>.
* @serialData
* <pre>
* out.writeByte(1); // identifies a Chronology
* out.writeUTF(getId());
* </pre>
*
* @return the instance of {@code Ser}, not null
*/
@Override
Object writeReplace() {
return super.writeReplace();
}
/**
* Defend against malicious streams.
*
* @param s the stream to read
* @throws InvalidObjectException always
*/
private void readObject(ObjectInputStream s) throws InvalidObjectException {
throw new InvalidObjectException("Deserialization via serialization delegate");
}
}

View file

@ -0,0 +1,519 @@
/*
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of JSR-310 nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package java.time.chrono;
import static java.time.chrono.MinguoChronology.YEARS_DIFFERENCE;
import static java.time.temporal.ChronoField.DAY_OF_MONTH;
import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
import static java.time.temporal.ChronoField.YEAR;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.time.Clock;
import java.time.DateTimeException;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.Period;
import java.time.ZoneId;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalAdjuster;
import java.time.temporal.TemporalAmount;
import java.time.temporal.TemporalField;
import java.time.temporal.TemporalQuery;
import java.time.temporal.TemporalUnit;
import java.time.temporal.UnsupportedTemporalTypeException;
import java.time.temporal.ValueRange;
import java.util.Objects;
/**
* A date in the Minguo calendar system.
* <p>
* This date operates using the {@linkplain MinguoChronology Minguo calendar}.
* This calendar system is primarily used in the Republic of China, often known as Taiwan.
* Dates are aligned such that {@code 0001-01-01 (Minguo)} is {@code 1912-01-01 (ISO)}.
*
* <p>
* This is a <a href="{@docRoot}/java/lang/doc-files/ValueBased.html">value-based</a>
* class; use of identity-sensitive operations (including reference equality
* ({@code ==}), identity hash code, or synchronization) on instances of
* {@code MinguoDate} may have unpredictable results and should be avoided.
* The {@code equals} method should be used for comparisons.
*
* @implSpec
* This class is immutable and thread-safe.
*
* @since 1.8
*/
public final class MinguoDate
extends ChronoLocalDateImpl<MinguoDate>
implements ChronoLocalDate, Serializable {
/**
* Serialization version.
*/
private static final long serialVersionUID = 1300372329181994526L;
/**
* The underlying date.
*/
private final transient LocalDate isoDate;
//-----------------------------------------------------------------------
/**
* Obtains the current {@code MinguoDate} from the system clock in the default time-zone.
* <p>
* This will query the {@link Clock#systemDefaultZone() system clock} in the default
* time-zone to obtain the current date.
* <p>
* Using this method will prevent the ability to use an alternate clock for testing
* because the clock is hard-coded.
*
* @return the current date using the system clock and default time-zone, not null
*/
public static MinguoDate now() {
return now(Clock.systemDefaultZone());
}
/**
* Obtains the current {@code MinguoDate} from the system clock in the specified time-zone.
* <p>
* This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date.
* Specifying the time-zone avoids dependence on the default time-zone.
* <p>
* Using this method will prevent the ability to use an alternate clock for testing
* because the clock is hard-coded.
*
* @param zone the zone ID to use, not null
* @return the current date using the system clock, not null
*/
public static MinguoDate now(ZoneId zone) {
return now(Clock.system(zone));
}
/**
* Obtains the current {@code MinguoDate} from the specified clock.
* <p>
* This will query the specified clock to obtain the current date - today.
* Using this method allows the use of an alternate clock for testing.
* The alternate clock may be introduced using {@linkplain Clock dependency injection}.
*
* @param clock the clock to use, not null
* @return the current date, not null
* @throws DateTimeException if the current date cannot be obtained
*/
public static MinguoDate now(Clock clock) {
return new MinguoDate(LocalDate.now(clock));
}
/**
* Obtains a {@code MinguoDate} representing a date in the Minguo calendar
* system from the proleptic-year, month-of-year and day-of-month fields.
* <p>
* This returns a {@code MinguoDate} with the specified fields.
* The day must be valid for the year and month, otherwise an exception will be thrown.
*
* @param prolepticYear the Minguo proleptic-year
* @param month the Minguo month-of-year, from 1 to 12
* @param dayOfMonth the Minguo day-of-month, from 1 to 31
* @return the date in Minguo calendar system, not null
* @throws DateTimeException if the value of any field is out of range,
* or if the day-of-month is invalid for the month-year
*/
public static MinguoDate of(int prolepticYear, int month, int dayOfMonth) {
return new MinguoDate(LocalDate.of(prolepticYear + YEARS_DIFFERENCE, month, dayOfMonth));
}
/**
* Obtains a {@code MinguoDate} from a temporal object.
* <p>
* This obtains a date in the Minguo calendar system based on the specified temporal.
* A {@code TemporalAccessor} represents an arbitrary set of date and time information,
* which this factory converts to an instance of {@code MinguoDate}.
* <p>
* The conversion typically uses the {@link ChronoField#EPOCH_DAY EPOCH_DAY}
* field, which is standardized across calendar systems.
* <p>
* This method matches the signature of the functional interface {@link TemporalQuery}
* allowing it to be used as a query via method reference, {@code MinguoDate::from}.
*
* @param temporal the temporal object to convert, not null
* @return the date in Minguo calendar system, not null
* @throws DateTimeException if unable to convert to a {@code MinguoDate}
*/
public static MinguoDate from(TemporalAccessor temporal) {
return MinguoChronology.INSTANCE.date(temporal);
}
//-----------------------------------------------------------------------
/**
* Creates an instance from an ISO date.
*
* @param isoDate the standard local date, validated not null
*/
MinguoDate(LocalDate isoDate) {
Objects.requireNonNull(isoDate, "isoDate");
this.isoDate = isoDate;
}
//-----------------------------------------------------------------------
/**
* Gets the chronology of this date, which is the Minguo calendar system.
* <p>
* The {@code Chronology} represents the calendar system in use.
* The era and other fields in {@link ChronoField} are defined by the chronology.
*
* @return the Minguo chronology, not null
*/
@Override
public MinguoChronology getChronology() {
return MinguoChronology.INSTANCE;
}
/**
* Gets the era applicable at this date.
* <p>
* The Minguo calendar system has two eras, 'ROC' and 'BEFORE_ROC',
* defined by {@link MinguoEra}.
*
* @return the era applicable at this date, not null
*/
@Override
public MinguoEra getEra() {
return (getProlepticYear() >= 1 ? MinguoEra.ROC : MinguoEra.BEFORE_ROC);
}
/**
* Returns the length of the month represented by this date.
* <p>
* This returns the length of the month in days.
* Month lengths match those of the ISO calendar system.
*
* @return the length of the month in days
*/
@Override
public int lengthOfMonth() {
return isoDate.lengthOfMonth();
}
//-----------------------------------------------------------------------
@Override
public ValueRange range(TemporalField field) {
if (field instanceof ChronoField) {
if (isSupported(field)) {
ChronoField f = (ChronoField) field;
switch (f) {
case DAY_OF_MONTH:
case DAY_OF_YEAR:
case ALIGNED_WEEK_OF_MONTH:
return isoDate.range(field);
case YEAR_OF_ERA: {
ValueRange range = YEAR.range();
long max = (getProlepticYear() <= 0 ? -range.getMinimum() + 1 + YEARS_DIFFERENCE : range.getMaximum() - YEARS_DIFFERENCE);
return ValueRange.of(1, max);
}
}
return getChronology().range(f);
}
throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
return field.rangeRefinedBy(this);
}
@Override
public long getLong(TemporalField field) {
if (field instanceof ChronoField) {
switch ((ChronoField) field) {
case PROLEPTIC_MONTH:
return getProlepticMonth();
case YEAR_OF_ERA: {
int prolepticYear = getProlepticYear();
return (prolepticYear >= 1 ? prolepticYear : 1 - prolepticYear);
}
case YEAR:
return getProlepticYear();
case ERA:
return (getProlepticYear() >= 1 ? 1 : 0);
}
return isoDate.getLong(field);
}
return field.getFrom(this);
}
private long getProlepticMonth() {
return getProlepticYear() * 12L + isoDate.getMonthValue() - 1;
}
private int getProlepticYear() {
return isoDate.getYear() - YEARS_DIFFERENCE;
}
//-----------------------------------------------------------------------
@Override
public MinguoDate with(TemporalField field, long newValue) {
if (field instanceof ChronoField) {
ChronoField f = (ChronoField) field;
if (getLong(f) == newValue) {
return this;
}
switch (f) {
case PROLEPTIC_MONTH:
getChronology().range(f).checkValidValue(newValue, f);
return plusMonths(newValue - getProlepticMonth());
case YEAR_OF_ERA:
case YEAR:
case ERA: {
int nvalue = getChronology().range(f).checkValidIntValue(newValue, f);
switch (f) {
case YEAR_OF_ERA:
return with(isoDate.withYear(getProlepticYear() >= 1 ? nvalue + YEARS_DIFFERENCE : (1 - nvalue) + YEARS_DIFFERENCE));
case YEAR:
return with(isoDate.withYear(nvalue + YEARS_DIFFERENCE));
case ERA:
return with(isoDate.withYear((1 - getProlepticYear()) + YEARS_DIFFERENCE));
}
}
}
return with(isoDate.with(field, newValue));
}
return super.with(field, newValue);
}
/**
* {@inheritDoc}
* @throws DateTimeException {@inheritDoc}
* @throws ArithmeticException {@inheritDoc}
*/
@Override
public MinguoDate with(TemporalAdjuster adjuster) {
return super.with(adjuster);
}
/**
* {@inheritDoc}
* @throws DateTimeException {@inheritDoc}
* @throws ArithmeticException {@inheritDoc}
*/
@Override
public MinguoDate plus(TemporalAmount amount) {
return super.plus(amount);
}
/**
* {@inheritDoc}
* @throws DateTimeException {@inheritDoc}
* @throws ArithmeticException {@inheritDoc}
*/
@Override
public MinguoDate minus(TemporalAmount amount) {
return super.minus(amount);
}
//-----------------------------------------------------------------------
@Override
MinguoDate plusYears(long years) {
return with(isoDate.plusYears(years));
}
@Override
MinguoDate plusMonths(long months) {
return with(isoDate.plusMonths(months));
}
@Override
MinguoDate plusWeeks(long weeksToAdd) {
return super.plusWeeks(weeksToAdd);
}
@Override
MinguoDate plusDays(long days) {
return with(isoDate.plusDays(days));
}
@Override
public MinguoDate plus(long amountToAdd, TemporalUnit unit) {
return super.plus(amountToAdd, unit);
}
@Override
public MinguoDate minus(long amountToAdd, TemporalUnit unit) {
return super.minus(amountToAdd, unit);
}
@Override
MinguoDate minusYears(long yearsToSubtract) {
return super.minusYears(yearsToSubtract);
}
@Override
MinguoDate minusMonths(long monthsToSubtract) {
return super.minusMonths(monthsToSubtract);
}
@Override
MinguoDate minusWeeks(long weeksToSubtract) {
return super.minusWeeks(weeksToSubtract);
}
@Override
MinguoDate minusDays(long daysToSubtract) {
return super.minusDays(daysToSubtract);
}
private MinguoDate with(LocalDate newDate) {
return (newDate.equals(isoDate) ? this : new MinguoDate(newDate));
}
@Override // for javadoc and covariant return type
@SuppressWarnings("unchecked")
public final ChronoLocalDateTime<MinguoDate> atTime(LocalTime localTime) {
return (ChronoLocalDateTime<MinguoDate>)super.atTime(localTime);
}
@Override
public ChronoPeriod until(ChronoLocalDate endDate) {
Period period = isoDate.until(endDate);
return getChronology().period(period.getYears(), period.getMonths(), period.getDays());
}
@Override // override for performance
public long toEpochDay() {
return isoDate.toEpochDay();
}
//-------------------------------------------------------------------------
/**
* Compares this date to another date, including the chronology.
* <p>
* Compares this {@code MinguoDate} with another ensuring that the date is the same.
* <p>
* Only objects of type {@code MinguoDate} are compared, other types return false.
* To compare the dates of two {@code TemporalAccessor} instances, including dates
* in two different chronologies, use {@link ChronoField#EPOCH_DAY} as a comparator.
*
* @param obj the object to check, null returns false
* @return true if this is equal to the other date
*/
@Override // override for performance
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof MinguoDate) {
MinguoDate otherDate = (MinguoDate) obj;
return this.isoDate.equals(otherDate.isoDate);
}
return false;
}
/**
* A hash code for this date.
*
* @return a suitable hash code based only on the Chronology and the date
*/
@Override // override for performance
public int hashCode() {
return getChronology().getId().hashCode() ^ isoDate.hashCode();
}
//-----------------------------------------------------------------------
/**
* Defend against malicious streams.
*
* @param s the stream to read
* @throws InvalidObjectException always
*/
private void readObject(ObjectInputStream s) throws InvalidObjectException {
throw new InvalidObjectException("Deserialization via serialization delegate");
}
/**
* Writes the object using a
* <a href="../../../serialized-form.html#java.time.chrono.Ser">dedicated serialized form</a>.
* @serialData
* <pre>
* out.writeByte(8); // identifies a MinguoDate
* out.writeInt(get(YEAR));
* out.writeByte(get(MONTH_OF_YEAR));
* out.writeByte(get(DAY_OF_MONTH));
* </pre>
*
* @return the instance of {@code Ser}, not null
*/
private Object writeReplace() {
return new Ser(Ser.MINGUO_DATE_TYPE, this);
}
void writeExternal(DataOutput out) throws IOException {
// MinguoChronology is implicit in the MINGUO_DATE_TYPE
out.writeInt(get(YEAR));
out.writeByte(get(MONTH_OF_YEAR));
out.writeByte(get(DAY_OF_MONTH));
}
static MinguoDate readExternal(DataInput in) throws IOException {
int year = in.readInt();
int month = in.readByte();
int dayOfMonth = in.readByte();
return MinguoChronology.INSTANCE.date(year, month, dayOfMonth);
}
}

View file

@ -0,0 +1,176 @@
/*
* Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* This file is available under and governed by the GNU General Public
* License version 2 only, as published by the Free Software Foundation.
* However, the following notice accompanied the original version of this
* file:
*
* Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of JSR-310 nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package java.time.chrono;
import static java.time.temporal.ChronoField.ERA;
import java.time.DateTimeException;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.TextStyle;
import java.util.Locale;
/**
* An era in the Minguo calendar system.
* <p>
* The Minguo calendar system has two eras.
* The current era, for years from 1 onwards, is known as the 'Republic of China' era.
* All previous years, zero or earlier in the proleptic count or one and greater
* in the year-of-era count, are part of the 'Before Republic of China' era.
*
* <table class="striped" style="text-align:left">
* <caption style="display:none">Minguo years and eras</caption>
* <thead>
* <tr>
* <th>year-of-era</th>
* <th>era</th>
* <th>proleptic-year</th>
* <th>ISO proleptic-year</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td>2</td><td>ROC</td><th scope="row">2</th><td>1913</td>
* </tr>
* <tr>
* <td>1</td><td>ROC</td><th scope="row">1</th><td>1912</td>
* </tr>
* <tr>
* <td>1</td><td>BEFORE_ROC</td><th scope="row">0</th><td>1911</td>
* </tr>
* <tr>
* <td>2</td><td>BEFORE_ROC</td><th scope="row">-1</th><td>1910</td>
* </tr>
* </tbody>
* </table>
* <p>
* <b>Do not use {@code ordinal()} to obtain the numeric representation of {@code MinguoEra}.
* Use {@code getValue()} instead.</b>
*
* @implSpec
* This is an immutable and thread-safe enum.
*
* @since 1.8
*/
public enum MinguoEra implements Era {
/**
* The singleton instance for the era before the current one, 'Before Republic of China Era',
* which has the numeric value 0.
*/
BEFORE_ROC,
/**
* The singleton instance for the current era, 'Republic of China Era',
* which has the numeric value 1.
*/
ROC;
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code MinguoEra} from an {@code int} value.
* <p>
* {@code MinguoEra} is an enum representing the Minguo eras of BEFORE_ROC/ROC.
* This factory allows the enum to be obtained from the {@code int} value.
*
* @param minguoEra the BEFORE_ROC/ROC value to represent, from 0 (BEFORE_ROC) to 1 (ROC)
* @return the era singleton, not null
* @throws DateTimeException if the value is invalid
*/
public static MinguoEra of(int minguoEra) {
switch (minguoEra) {
case 0:
return BEFORE_ROC;
case 1:
return ROC;
default:
throw new DateTimeException("Invalid era: " + minguoEra);
}
}
//-----------------------------------------------------------------------
/**
* Gets the numeric era {@code int} value.
* <p>
* The era BEFORE_ROC has the value 0, while the era ROC has the value 1.
*
* @return the era value, from 0 (BEFORE_ROC) to 1 (ROC)
*/
@Override
public int getValue() {
return ordinal();
}
/**
* {@inheritDoc}
*
* @param style {@inheritDoc}
* @param locale {@inheritDoc}
*/
@Override
public String getDisplayName(TextStyle style, Locale locale) {
return new DateTimeFormatterBuilder()
.appendText(ERA, style)
.toFormatter(locale)
.withChronology(MinguoChronology.INSTANCE)
.format(this == ROC ? MinguoDate.of(1, 1, 1) : MinguoDate.of(0, 1, 1));
}
}

View file

@ -0,0 +1,256 @@
/*
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Copyright (c) 2011-2012, Stephen Colebourne & Michael Nascimento Santos
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of JSR-310 nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package java.time.chrono;
import java.io.Externalizable;
import java.io.IOException;
import java.io.InvalidClassException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.StreamCorruptedException;
import java.time.LocalDate;
import java.time.LocalDateTime;
/**
* The shared serialization delegate for this package.
*
* @implNote
* This class wraps the object being serialized, and takes a byte representing the type of the class to
* be serialized. This byte can also be used for versioning the serialization format. In this case another
* byte flag would be used in order to specify an alternative version of the type format.
* For example {@code CHRONO_TYPE_VERSION_2 = 21}
* <p>
* In order to serialize the object it writes its byte and then calls back to the appropriate class where
* the serialization is performed. In order to deserialize the object it read in the type byte, switching
* in order to select which class to call back into.
* <p>
* The serialization format is determined on a per class basis. In the case of field based classes each
* of the fields is written out with an appropriate size format in descending order of the field's size. For
* example in the case of {@link LocalDate} year is written before month. Composite classes, such as
* {@link LocalDateTime} are serialized as one object. Enum classes are serialized using the index of their
* element.
* <p>
* This class is mutable and should be created once per serialization.
*
* @serial include
* @since 1.8
*/
final class Ser implements Externalizable {
/**
* Serialization version.
*/
private static final long serialVersionUID = -6103370247208168577L;
static final byte CHRONO_TYPE = 1;
static final byte CHRONO_LOCAL_DATE_TIME_TYPE = 2;
static final byte CHRONO_ZONE_DATE_TIME_TYPE = 3;
static final byte JAPANESE_DATE_TYPE = 4;
static final byte JAPANESE_ERA_TYPE = 5;
static final byte HIJRAH_DATE_TYPE = 6;
static final byte MINGUO_DATE_TYPE = 7;
static final byte THAIBUDDHIST_DATE_TYPE = 8;
static final byte CHRONO_PERIOD_TYPE = 9;
/** The type being serialized. */
private byte type;
/** The object being serialized. */
private Object object;
/**
* Constructor for deserialization.
*/
public Ser() {
}
/**
* Creates an instance for serialization.
*
* @param type the type
* @param object the object
*/
Ser(byte type, Object object) {
this.type = type;
this.object = object;
}
//-----------------------------------------------------------------------
/**
* Implements the {@code Externalizable} interface to write the object.
* @serialData
* Each serializable class is mapped to a type that is the first byte
* in the stream. Refer to each class {@code writeReplace}
* serialized form for the value of the type and sequence of values for the type.
* <ul>
* <li><a href="../../../serialized-form.html#java.time.chrono.HijrahChronology">HijrahChronology.writeReplace</a>
* <li><a href="../../../serialized-form.html#java.time.chrono.IsoChronology">IsoChronology.writeReplace</a>
* <li><a href="../../../serialized-form.html#java.time.chrono.JapaneseChronology">JapaneseChronology.writeReplace</a>
* <li><a href="../../../serialized-form.html#java.time.chrono.MinguoChronology">MinguoChronology.writeReplace</a>
* <li><a href="../../../serialized-form.html#java.time.chrono.ThaiBuddhistChronology">ThaiBuddhistChronology.writeReplace</a>
* <li><a href="../../../serialized-form.html#java.time.chrono.ChronoLocalDateTimeImpl">ChronoLocalDateTime.writeReplace</a>
* <li><a href="../../../serialized-form.html#java.time.chrono.ChronoZonedDateTimeImpl">ChronoZonedDateTime.writeReplace</a>
* <li><a href="../../../serialized-form.html#java.time.chrono.JapaneseDate">JapaneseDate.writeReplace</a>
* <li><a href="../../../serialized-form.html#java.time.chrono.JapaneseEra">JapaneseEra.writeReplace</a>
* <li><a href="../../../serialized-form.html#java.time.chrono.HijrahDate">HijrahDate.writeReplace</a>
* <li><a href="../../../serialized-form.html#java.time.chrono.MinguoDate">MinguoDate.writeReplace</a>
* <li><a href="../../../serialized-form.html#java.time.chrono.ThaiBuddhistDate">ThaiBuddhistDate.writeReplace</a>
* </ul>
*
* @param out the data stream to write to, not null
*/
@Override
public void writeExternal(ObjectOutput out) throws IOException {
writeInternal(type, object, out);
}
private static void writeInternal(byte type, Object object, ObjectOutput out) throws IOException {
out.writeByte(type);
switch (type) {
case CHRONO_TYPE:
((AbstractChronology) object).writeExternal(out);
break;
case CHRONO_LOCAL_DATE_TIME_TYPE:
((ChronoLocalDateTimeImpl<?>) object).writeExternal(out);
break;
case CHRONO_ZONE_DATE_TIME_TYPE:
((ChronoZonedDateTimeImpl<?>) object).writeExternal(out);
break;
case JAPANESE_DATE_TYPE:
((JapaneseDate) object).writeExternal(out);
break;
case JAPANESE_ERA_TYPE:
((JapaneseEra) object).writeExternal(out);
break;
case HIJRAH_DATE_TYPE:
((HijrahDate) object).writeExternal(out);
break;
case MINGUO_DATE_TYPE:
((MinguoDate) object).writeExternal(out);
break;
case THAIBUDDHIST_DATE_TYPE:
((ThaiBuddhistDate) object).writeExternal(out);
break;
case CHRONO_PERIOD_TYPE:
((ChronoPeriodImpl) object).writeExternal(out);
break;
default:
throw new InvalidClassException("Unknown serialized type");
}
}
//-----------------------------------------------------------------------
/**
* Implements the {@code Externalizable} interface to read the object.
* @serialData
* The streamed type and parameters defined by the type's {@code writeReplace}
* method are read and passed to the corresponding static factory for the type
* to create a new instance. That instance is returned as the de-serialized
* {@code Ser} object.
*
* <ul>
* <li><a href="../../../serialized-form.html#java.time.chrono.HijrahChronology">HijrahChronology</a> - Chronology.of(id)
* <li><a href="../../../serialized-form.html#java.time.chrono.IsoChronology">IsoChronology</a> - Chronology.of(id)
* <li><a href="../../../serialized-form.html#java.time.chrono.JapaneseChronology">JapaneseChronology</a> - Chronology.of(id)
* <li><a href="../../../serialized-form.html#java.time.chrono.MinguoChronology">MinguoChronology</a> - Chronology.of(id)
* <li><a href="../../../serialized-form.html#java.time.chrono.ThaiBuddhistChronology">ThaiBuddhistChronology</a> - Chronology.of(id)
* <li><a href="../../../serialized-form.html#java.time.chrono.ChronoLocalDateTimeImpl">ChronoLocalDateTime</a> - date.atTime(time)
* <li><a href="../../../serialized-form.html#java.time.chrono.ChronoZonedDateTimeImpl">ChronoZonedDateTime</a> - dateTime.atZone(offset).withZoneSameLocal(zone)
* <li><a href="../../../serialized-form.html#java.time.chrono.JapaneseDate">JapaneseDate</a> - JapaneseChronology.INSTANCE.date(year, month, dayOfMonth)
* <li><a href="../../../serialized-form.html#java.time.chrono.JapaneseEra">JapaneseEra</a> - JapaneseEra.of(eraValue)
* <li><a href="../../../serialized-form.html#java.time.chrono.HijrahDate">HijrahDate</a> - HijrahChronology chrono.date(year, month, dayOfMonth)
* <li><a href="../../../serialized-form.html#java.time.chrono.MinguoDate">MinguoDate</a> - MinguoChronology.INSTANCE.date(year, month, dayOfMonth)
* <li><a href="../../../serialized-form.html#java.time.chrono.ThaiBuddhistDate">ThaiBuddhistDate</a> - ThaiBuddhistChronology.INSTANCE.date(year, month, dayOfMonth)
* </ul>
*
* @param in the data stream to read from, not null
*/
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
type = in.readByte();
object = readInternal(type, in);
}
static Object read(ObjectInput in) throws IOException, ClassNotFoundException {
byte type = in.readByte();
return readInternal(type, in);
}
private static Object readInternal(byte type, ObjectInput in) throws IOException, ClassNotFoundException {
switch (type) {
case CHRONO_TYPE: return AbstractChronology.readExternal(in);
case CHRONO_LOCAL_DATE_TIME_TYPE: return ChronoLocalDateTimeImpl.readExternal(in);
case CHRONO_ZONE_DATE_TIME_TYPE: return ChronoZonedDateTimeImpl.readExternal(in);
case JAPANESE_DATE_TYPE: return JapaneseDate.readExternal(in);
case JAPANESE_ERA_TYPE: return JapaneseEra.readExternal(in);
case HIJRAH_DATE_TYPE: return HijrahDate.readExternal(in);
case MINGUO_DATE_TYPE: return MinguoDate.readExternal(in);
case THAIBUDDHIST_DATE_TYPE: return ThaiBuddhistDate.readExternal(in);
case CHRONO_PERIOD_TYPE: return ChronoPeriodImpl.readExternal(in);
default: throw new StreamCorruptedException("Unknown serialized type");
}
}
/**
* Returns the object that will replace this one.
*
* @return the read object, should never be null
*/
private Object readResolve() {
return object;
}
}

View file

@ -0,0 +1,400 @@
/*
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of JSR-310 nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package java.time.chrono;
import java.io.InvalidObjectException;
import static java.time.temporal.ChronoField.PROLEPTIC_MONTH;
import static java.time.temporal.ChronoField.YEAR;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.time.Clock;
import java.time.DateTimeException;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.ResolverStyle;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalField;
import java.time.temporal.ValueRange;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
/**
* The Thai Buddhist calendar system.
* <p>
* This chronology defines the rules of the Thai Buddhist calendar system.
* This calendar system is primarily used in Thailand.
* Dates are aligned such that {@code 2484-01-01 (Buddhist)} is {@code 1941-01-01 (ISO)}.
* <p>
* The fields are defined as follows:
* <ul>
* <li>era - There are two eras, the current 'Buddhist' (ERA_BE) and the previous era (ERA_BEFORE_BE).
* <li>year-of-era - The year-of-era for the current era increases uniformly from the epoch at year one.
* For the previous era the year increases from one as time goes backwards.
* The value for the current era is equal to the ISO proleptic-year plus 543.
* <li>proleptic-year - The proleptic year is the same as the year-of-era for the
* current era. For the previous era, years have zero, then negative values.
* The value is equal to the ISO proleptic-year plus 543.
* <li>month-of-year - The ThaiBuddhist month-of-year exactly matches ISO.
* <li>day-of-month - The ThaiBuddhist day-of-month exactly matches ISO.
* <li>day-of-year - The ThaiBuddhist day-of-year exactly matches ISO.
* <li>leap-year - The ThaiBuddhist leap-year pattern exactly matches ISO, such that the two calendars
* are never out of step.
* </ul>
*
* @implSpec
* This class is immutable and thread-safe.
*
* @since 1.8
*/
public final class ThaiBuddhistChronology extends AbstractChronology implements Serializable {
/**
* Singleton instance of the Buddhist chronology.
*/
public static final ThaiBuddhistChronology INSTANCE = new ThaiBuddhistChronology();
/**
* Serialization version.
*/
private static final long serialVersionUID = 2775954514031616474L;
/**
* Containing the offset to add to the ISO year.
*/
static final int YEARS_DIFFERENCE = 543;
/**
* Narrow names for eras.
*/
private static final HashMap<String, String[]> ERA_NARROW_NAMES = new HashMap<>();
/**
* Short names for eras.
*/
private static final HashMap<String, String[]> ERA_SHORT_NAMES = new HashMap<>();
/**
* Full names for eras.
*/
private static final HashMap<String, String[]> ERA_FULL_NAMES = new HashMap<>();
/**
* Fallback language for the era names.
*/
private static final String FALLBACK_LANGUAGE = "en";
/**
* Language that has the era names.
*/
private static final String TARGET_LANGUAGE = "th";
/**
* Name data.
*/
static {
ERA_NARROW_NAMES.put(FALLBACK_LANGUAGE, new String[]{"BB", "BE"});
ERA_NARROW_NAMES.put(TARGET_LANGUAGE, new String[]{"BB", "BE"});
ERA_SHORT_NAMES.put(FALLBACK_LANGUAGE, new String[]{"B.B.", "B.E."});
ERA_SHORT_NAMES.put(TARGET_LANGUAGE,
new String[]{"\u0e1e.\u0e28.",
"\u0e1b\u0e35\u0e01\u0e48\u0e2d\u0e19\u0e04\u0e23\u0e34\u0e2a\u0e15\u0e4c\u0e01\u0e32\u0e25\u0e17\u0e35\u0e48"});
ERA_FULL_NAMES.put(FALLBACK_LANGUAGE, new String[]{"Before Buddhist", "Budhhist Era"});
ERA_FULL_NAMES.put(TARGET_LANGUAGE,
new String[]{"\u0e1e\u0e38\u0e17\u0e18\u0e28\u0e31\u0e01\u0e23\u0e32\u0e0a",
"\u0e1b\u0e35\u0e01\u0e48\u0e2d\u0e19\u0e04\u0e23\u0e34\u0e2a\u0e15\u0e4c\u0e01\u0e32\u0e25\u0e17\u0e35\u0e48"});
}
/**
* Restricted constructor.
*/
private ThaiBuddhistChronology() {
}
//-----------------------------------------------------------------------
/**
* Gets the ID of the chronology - 'ThaiBuddhist'.
* <p>
* The ID uniquely identifies the {@code Chronology}.
* It can be used to lookup the {@code Chronology} using {@link Chronology#of(String)}.
*
* @return the chronology ID - 'ThaiBuddhist'
* @see #getCalendarType()
*/
@Override
public String getId() {
return "ThaiBuddhist";
}
/**
* Gets the calendar type of the underlying calendar system - 'buddhist'.
* <p>
* The calendar type is an identifier defined by the
* <em>Unicode Locale Data Markup Language (LDML)</em> specification.
* It can be used to lookup the {@code Chronology} using {@link Chronology#of(String)}.
* It can also be used as part of a locale, accessible via
* {@link Locale#getUnicodeLocaleType(String)} with the key 'ca'.
*
* @return the calendar system type - 'buddhist'
* @see #getId()
*/
@Override
public String getCalendarType() {
return "buddhist";
}
//-----------------------------------------------------------------------
/**
* Obtains a local date in Thai Buddhist calendar system from the
* era, year-of-era, month-of-year and day-of-month fields.
*
* @param era the Thai Buddhist era, not null
* @param yearOfEra the year-of-era
* @param month the month-of-year
* @param dayOfMonth the day-of-month
* @return the Thai Buddhist local date, not null
* @throws DateTimeException if unable to create the date
* @throws ClassCastException if the {@code era} is not a {@code ThaiBuddhistEra}
*/
@Override
public ThaiBuddhistDate date(Era era, int yearOfEra, int month, int dayOfMonth) {
return date(prolepticYear(era, yearOfEra), month, dayOfMonth);
}
/**
* Obtains a local date in Thai Buddhist calendar system from the
* proleptic-year, month-of-year and day-of-month fields.
*
* @param prolepticYear the proleptic-year
* @param month the month-of-year
* @param dayOfMonth the day-of-month
* @return the Thai Buddhist local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override
public ThaiBuddhistDate date(int prolepticYear, int month, int dayOfMonth) {
return new ThaiBuddhistDate(LocalDate.of(prolepticYear - YEARS_DIFFERENCE, month, dayOfMonth));
}
/**
* Obtains a local date in Thai Buddhist calendar system from the
* era, year-of-era and day-of-year fields.
*
* @param era the Thai Buddhist era, not null
* @param yearOfEra the year-of-era
* @param dayOfYear the day-of-year
* @return the Thai Buddhist local date, not null
* @throws DateTimeException if unable to create the date
* @throws ClassCastException if the {@code era} is not a {@code ThaiBuddhistEra}
*/
@Override
public ThaiBuddhistDate dateYearDay(Era era, int yearOfEra, int dayOfYear) {
return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear);
}
/**
* Obtains a local date in Thai Buddhist calendar system from the
* proleptic-year and day-of-year fields.
*
* @param prolepticYear the proleptic-year
* @param dayOfYear the day-of-year
* @return the Thai Buddhist local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override
public ThaiBuddhistDate dateYearDay(int prolepticYear, int dayOfYear) {
return new ThaiBuddhistDate(LocalDate.ofYearDay(prolepticYear - YEARS_DIFFERENCE, dayOfYear));
}
/**
* Obtains a local date in the Thai Buddhist calendar system from the epoch-day.
*
* @param epochDay the epoch day
* @return the Thai Buddhist local date, not null
* @throws DateTimeException if unable to create the date
*/
@Override // override with covariant return type
public ThaiBuddhistDate dateEpochDay(long epochDay) {
return new ThaiBuddhistDate(LocalDate.ofEpochDay(epochDay));
}
@Override
public ThaiBuddhistDate dateNow() {
return dateNow(Clock.systemDefaultZone());
}
@Override
public ThaiBuddhistDate dateNow(ZoneId zone) {
return dateNow(Clock.system(zone));
}
@Override
public ThaiBuddhistDate dateNow(Clock clock) {
return date(LocalDate.now(clock));
}
@Override
public ThaiBuddhistDate date(TemporalAccessor temporal) {
if (temporal instanceof ThaiBuddhistDate) {
return (ThaiBuddhistDate) temporal;
}
return new ThaiBuddhistDate(LocalDate.from(temporal));
}
@Override
@SuppressWarnings("unchecked")
public ChronoLocalDateTime<ThaiBuddhistDate> localDateTime(TemporalAccessor temporal) {
return (ChronoLocalDateTime<ThaiBuddhistDate>)super.localDateTime(temporal);
}
@Override
@SuppressWarnings("unchecked")
public ChronoZonedDateTime<ThaiBuddhistDate> zonedDateTime(TemporalAccessor temporal) {
return (ChronoZonedDateTime<ThaiBuddhistDate>)super.zonedDateTime(temporal);
}
@Override
@SuppressWarnings("unchecked")
public ChronoZonedDateTime<ThaiBuddhistDate> zonedDateTime(Instant instant, ZoneId zone) {
return (ChronoZonedDateTime<ThaiBuddhistDate>)super.zonedDateTime(instant, zone);
}
//-----------------------------------------------------------------------
/**
* Checks if the specified year is a leap year.
* <p>
* Thai Buddhist leap years occur exactly in line with ISO leap years.
* This method does not validate the year passed in, and only has a
* well-defined result for years in the supported range.
*
* @param prolepticYear the proleptic-year to check, not validated for range
* @return true if the year is a leap year
*/
@Override
public boolean isLeapYear(long prolepticYear) {
return IsoChronology.INSTANCE.isLeapYear(prolepticYear - YEARS_DIFFERENCE);
}
@Override
public int prolepticYear(Era era, int yearOfEra) {
if (era instanceof ThaiBuddhistEra == false) {
throw new ClassCastException("Era must be BuddhistEra");
}
return (era == ThaiBuddhistEra.BE ? yearOfEra : 1 - yearOfEra);
}
@Override
public ThaiBuddhistEra eraOf(int eraValue) {
return ThaiBuddhistEra.of(eraValue);
}
@Override
public List<Era> eras() {
return List.of(ThaiBuddhistEra.values());
}
//-----------------------------------------------------------------------
@Override
public ValueRange range(ChronoField field) {
switch (field) {
case PROLEPTIC_MONTH: {
ValueRange range = PROLEPTIC_MONTH.range();
return ValueRange.of(range.getMinimum() + YEARS_DIFFERENCE * 12L, range.getMaximum() + YEARS_DIFFERENCE * 12L);
}
case YEAR_OF_ERA: {
ValueRange range = YEAR.range();
return ValueRange.of(1, -(range.getMinimum() + YEARS_DIFFERENCE) + 1, range.getMaximum() + YEARS_DIFFERENCE);
}
case YEAR: {
ValueRange range = YEAR.range();
return ValueRange.of(range.getMinimum() + YEARS_DIFFERENCE, range.getMaximum() + YEARS_DIFFERENCE);
}
}
return field.range();
}
//-----------------------------------------------------------------------
@Override // override for return type
public ThaiBuddhistDate resolveDate(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
return (ThaiBuddhistDate) super.resolveDate(fieldValues, resolverStyle);
}
//-----------------------------------------------------------------------
/**
* Writes the Chronology using a
* <a href="../../../serialized-form.html#java.time.chrono.Ser">dedicated serialized form</a>.
* @serialData
* <pre>
* out.writeByte(1); // identifies a Chronology
* out.writeUTF(getId());
* </pre>
*
* @return the instance of {@code Ser}, not null
*/
@Override
Object writeReplace() {
return super.writeReplace();
}
/**
* Defend against malicious streams.
*
* @param s the stream to read
* @throws InvalidObjectException always
*/
private void readObject(ObjectInputStream s) throws InvalidObjectException {
throw new InvalidObjectException("Deserialization via serialization delegate");
}
}

View file

@ -0,0 +1,519 @@
/*
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of JSR-310 nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package java.time.chrono;
import static java.time.chrono.ThaiBuddhistChronology.YEARS_DIFFERENCE;
import static java.time.temporal.ChronoField.DAY_OF_MONTH;
import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
import static java.time.temporal.ChronoField.YEAR;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.time.Clock;
import java.time.DateTimeException;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.Period;
import java.time.ZoneId;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalAdjuster;
import java.time.temporal.TemporalAmount;
import java.time.temporal.TemporalField;
import java.time.temporal.TemporalQuery;
import java.time.temporal.TemporalUnit;
import java.time.temporal.UnsupportedTemporalTypeException;
import java.time.temporal.ValueRange;
import java.util.Objects;
/**
* A date in the Thai Buddhist calendar system.
* <p>
* This date operates using the {@linkplain ThaiBuddhistChronology Thai Buddhist calendar}.
* This calendar system is primarily used in Thailand.
* Dates are aligned such that {@code 2484-01-01 (Buddhist)} is {@code 1941-01-01 (ISO)}.
*
* <p>
* This is a <a href="{@docRoot}/java/lang/doc-files/ValueBased.html">value-based</a>
* class; use of identity-sensitive operations (including reference equality
* ({@code ==}), identity hash code, or synchronization) on instances of
* {@code ThaiBuddhistDate} may have unpredictable results and should be avoided.
* The {@code equals} method should be used for comparisons.
*
* @implSpec
* This class is immutable and thread-safe.
*
* @since 1.8
*/
public final class ThaiBuddhistDate
extends ChronoLocalDateImpl<ThaiBuddhistDate>
implements ChronoLocalDate, Serializable {
/**
* Serialization version.
*/
private static final long serialVersionUID = -8722293800195731463L;
/**
* The underlying date.
*/
private final transient LocalDate isoDate;
//-----------------------------------------------------------------------
/**
* Obtains the current {@code ThaiBuddhistDate} from the system clock in the default time-zone.
* <p>
* This will query the {@link Clock#systemDefaultZone() system clock} in the default
* time-zone to obtain the current date.
* <p>
* Using this method will prevent the ability to use an alternate clock for testing
* because the clock is hard-coded.
*
* @return the current date using the system clock and default time-zone, not null
*/
public static ThaiBuddhistDate now() {
return now(Clock.systemDefaultZone());
}
/**
* Obtains the current {@code ThaiBuddhistDate} from the system clock in the specified time-zone.
* <p>
* This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date.
* Specifying the time-zone avoids dependence on the default time-zone.
* <p>
* Using this method will prevent the ability to use an alternate clock for testing
* because the clock is hard-coded.
*
* @param zone the zone ID to use, not null
* @return the current date using the system clock, not null
*/
public static ThaiBuddhistDate now(ZoneId zone) {
return now(Clock.system(zone));
}
/**
* Obtains the current {@code ThaiBuddhistDate} from the specified clock.
* <p>
* This will query the specified clock to obtain the current date - today.
* Using this method allows the use of an alternate clock for testing.
* The alternate clock may be introduced using {@linkplain Clock dependency injection}.
*
* @param clock the clock to use, not null
* @return the current date, not null
* @throws DateTimeException if the current date cannot be obtained
*/
public static ThaiBuddhistDate now(Clock clock) {
return new ThaiBuddhistDate(LocalDate.now(clock));
}
/**
* Obtains a {@code ThaiBuddhistDate} representing a date in the Thai Buddhist calendar
* system from the proleptic-year, month-of-year and day-of-month fields.
* <p>
* This returns a {@code ThaiBuddhistDate} with the specified fields.
* The day must be valid for the year and month, otherwise an exception will be thrown.
*
* @param prolepticYear the Thai Buddhist proleptic-year
* @param month the Thai Buddhist month-of-year, from 1 to 12
* @param dayOfMonth the Thai Buddhist day-of-month, from 1 to 31
* @return the date in Thai Buddhist calendar system, not null
* @throws DateTimeException if the value of any field is out of range,
* or if the day-of-month is invalid for the month-year
*/
public static ThaiBuddhistDate of(int prolepticYear, int month, int dayOfMonth) {
return new ThaiBuddhistDate(LocalDate.of(prolepticYear - YEARS_DIFFERENCE, month, dayOfMonth));
}
/**
* Obtains a {@code ThaiBuddhistDate} from a temporal object.
* <p>
* This obtains a date in the Thai Buddhist calendar system based on the specified temporal.
* A {@code TemporalAccessor} represents an arbitrary set of date and time information,
* which this factory converts to an instance of {@code ThaiBuddhistDate}.
* <p>
* The conversion typically uses the {@link ChronoField#EPOCH_DAY EPOCH_DAY}
* field, which is standardized across calendar systems.
* <p>
* This method matches the signature of the functional interface {@link TemporalQuery}
* allowing it to be used as a query via method reference, {@code ThaiBuddhistDate::from}.
*
* @param temporal the temporal object to convert, not null
* @return the date in Thai Buddhist calendar system, not null
* @throws DateTimeException if unable to convert to a {@code ThaiBuddhistDate}
*/
public static ThaiBuddhistDate from(TemporalAccessor temporal) {
return ThaiBuddhistChronology.INSTANCE.date(temporal);
}
//-----------------------------------------------------------------------
/**
* Creates an instance from an ISO date.
*
* @param isoDate the standard local date, validated not null
*/
ThaiBuddhistDate(LocalDate isoDate) {
Objects.requireNonNull(isoDate, "isoDate");
this.isoDate = isoDate;
}
//-----------------------------------------------------------------------
/**
* Gets the chronology of this date, which is the Thai Buddhist calendar system.
* <p>
* The {@code Chronology} represents the calendar system in use.
* The era and other fields in {@link ChronoField} are defined by the chronology.
*
* @return the Thai Buddhist chronology, not null
*/
@Override
public ThaiBuddhistChronology getChronology() {
return ThaiBuddhistChronology.INSTANCE;
}
/**
* Gets the era applicable at this date.
* <p>
* The Thai Buddhist calendar system has two eras, 'BE' and 'BEFORE_BE',
* defined by {@link ThaiBuddhistEra}.
*
* @return the era applicable at this date, not null
*/
@Override
public ThaiBuddhistEra getEra() {
return (getProlepticYear() >= 1 ? ThaiBuddhistEra.BE : ThaiBuddhistEra.BEFORE_BE);
}
/**
* Returns the length of the month represented by this date.
* <p>
* This returns the length of the month in days.
* Month lengths match those of the ISO calendar system.
*
* @return the length of the month in days
*/
@Override
public int lengthOfMonth() {
return isoDate.lengthOfMonth();
}
//-----------------------------------------------------------------------
@Override
public ValueRange range(TemporalField field) {
if (field instanceof ChronoField) {
if (isSupported(field)) {
ChronoField f = (ChronoField) field;
switch (f) {
case DAY_OF_MONTH:
case DAY_OF_YEAR:
case ALIGNED_WEEK_OF_MONTH:
return isoDate.range(field);
case YEAR_OF_ERA: {
ValueRange range = YEAR.range();
long max = (getProlepticYear() <= 0 ? -(range.getMinimum() + YEARS_DIFFERENCE) + 1 : range.getMaximum() + YEARS_DIFFERENCE);
return ValueRange.of(1, max);
}
}
return getChronology().range(f);
}
throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
return field.rangeRefinedBy(this);
}
@Override
public long getLong(TemporalField field) {
if (field instanceof ChronoField) {
switch ((ChronoField) field) {
case PROLEPTIC_MONTH:
return getProlepticMonth();
case YEAR_OF_ERA: {
int prolepticYear = getProlepticYear();
return (prolepticYear >= 1 ? prolepticYear : 1 - prolepticYear);
}
case YEAR:
return getProlepticYear();
case ERA:
return (getProlepticYear() >= 1 ? 1 : 0);
}
return isoDate.getLong(field);
}
return field.getFrom(this);
}
private long getProlepticMonth() {
return getProlepticYear() * 12L + isoDate.getMonthValue() - 1;
}
private int getProlepticYear() {
return isoDate.getYear() + YEARS_DIFFERENCE;
}
//-----------------------------------------------------------------------
@Override
public ThaiBuddhistDate with(TemporalField field, long newValue) {
if (field instanceof ChronoField) {
ChronoField f = (ChronoField) field;
if (getLong(f) == newValue) {
return this;
}
switch (f) {
case PROLEPTIC_MONTH:
getChronology().range(f).checkValidValue(newValue, f);
return plusMonths(newValue - getProlepticMonth());
case YEAR_OF_ERA:
case YEAR:
case ERA: {
int nvalue = getChronology().range(f).checkValidIntValue(newValue, f);
switch (f) {
case YEAR_OF_ERA:
return with(isoDate.withYear((getProlepticYear() >= 1 ? nvalue : 1 - nvalue) - YEARS_DIFFERENCE));
case YEAR:
return with(isoDate.withYear(nvalue - YEARS_DIFFERENCE));
case ERA:
return with(isoDate.withYear((1 - getProlepticYear()) - YEARS_DIFFERENCE));
}
}
}
return with(isoDate.with(field, newValue));
}
return super.with(field, newValue);
}
/**
* {@inheritDoc}
* @throws DateTimeException {@inheritDoc}
* @throws ArithmeticException {@inheritDoc}
*/
@Override
public ThaiBuddhistDate with(TemporalAdjuster adjuster) {
return super.with(adjuster);
}
/**
* {@inheritDoc}
* @throws DateTimeException {@inheritDoc}
* @throws ArithmeticException {@inheritDoc}
*/
@Override
public ThaiBuddhistDate plus(TemporalAmount amount) {
return super.plus(amount);
}
/**
* {@inheritDoc}
* @throws DateTimeException {@inheritDoc}
* @throws ArithmeticException {@inheritDoc}
*/
@Override
public ThaiBuddhistDate minus(TemporalAmount amount) {
return super.minus(amount);
}
//-----------------------------------------------------------------------
@Override
ThaiBuddhistDate plusYears(long years) {
return with(isoDate.plusYears(years));
}
@Override
ThaiBuddhistDate plusMonths(long months) {
return with(isoDate.plusMonths(months));
}
@Override
ThaiBuddhistDate plusWeeks(long weeksToAdd) {
return super.plusWeeks(weeksToAdd);
}
@Override
ThaiBuddhistDate plusDays(long days) {
return with(isoDate.plusDays(days));
}
@Override
public ThaiBuddhistDate plus(long amountToAdd, TemporalUnit unit) {
return super.plus(amountToAdd, unit);
}
@Override
public ThaiBuddhistDate minus(long amountToAdd, TemporalUnit unit) {
return super.minus(amountToAdd, unit);
}
@Override
ThaiBuddhistDate minusYears(long yearsToSubtract) {
return super.minusYears(yearsToSubtract);
}
@Override
ThaiBuddhistDate minusMonths(long monthsToSubtract) {
return super.minusMonths(monthsToSubtract);
}
@Override
ThaiBuddhistDate minusWeeks(long weeksToSubtract) {
return super.minusWeeks(weeksToSubtract);
}
@Override
ThaiBuddhistDate minusDays(long daysToSubtract) {
return super.minusDays(daysToSubtract);
}
private ThaiBuddhistDate with(LocalDate newDate) {
return (newDate.equals(isoDate) ? this : new ThaiBuddhistDate(newDate));
}
@Override // for javadoc and covariant return type
@SuppressWarnings("unchecked")
public final ChronoLocalDateTime<ThaiBuddhistDate> atTime(LocalTime localTime) {
return (ChronoLocalDateTime<ThaiBuddhistDate>) super.atTime(localTime);
}
@Override
public ChronoPeriod until(ChronoLocalDate endDate) {
Period period = isoDate.until(endDate);
return getChronology().period(period.getYears(), period.getMonths(), period.getDays());
}
@Override // override for performance
public long toEpochDay() {
return isoDate.toEpochDay();
}
//-------------------------------------------------------------------------
/**
* Compares this date to another date, including the chronology.
* <p>
* Compares this {@code ThaiBuddhistDate} with another ensuring that the date is the same.
* <p>
* Only objects of type {@code ThaiBuddhistDate} are compared, other types return false.
* To compare the dates of two {@code TemporalAccessor} instances, including dates
* in two different chronologies, use {@link ChronoField#EPOCH_DAY} as a comparator.
*
* @param obj the object to check, null returns false
* @return true if this is equal to the other date
*/
@Override // override for performance
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof ThaiBuddhistDate) {
ThaiBuddhistDate otherDate = (ThaiBuddhistDate) obj;
return this.isoDate.equals(otherDate.isoDate);
}
return false;
}
/**
* A hash code for this date.
*
* @return a suitable hash code based only on the Chronology and the date
*/
@Override // override for performance
public int hashCode() {
return getChronology().getId().hashCode() ^ isoDate.hashCode();
}
//-----------------------------------------------------------------------
/**
* Defend against malicious streams.
*
* @param s the stream to read
* @throws InvalidObjectException always
*/
private void readObject(ObjectInputStream s) throws InvalidObjectException {
throw new InvalidObjectException("Deserialization via serialization delegate");
}
/**
* Writes the object using a
* <a href="../../../serialized-form.html#java.time.chrono.Ser">dedicated serialized form</a>.
* @serialData
* <pre>
* out.writeByte(10); // identifies a ThaiBuddhistDate
* out.writeInt(get(YEAR));
* out.writeByte(get(MONTH_OF_YEAR));
* out.writeByte(get(DAY_OF_MONTH));
* </pre>
*
* @return the instance of {@code Ser}, not null
*/
private Object writeReplace() {
return new Ser(Ser.THAIBUDDHIST_DATE_TYPE, this);
}
void writeExternal(DataOutput out) throws IOException {
// ThaiBuddhistChronology is implicit in the THAIBUDDHIST_DATE_TYPE
out.writeInt(this.get(YEAR));
out.writeByte(this.get(MONTH_OF_YEAR));
out.writeByte(this.get(DAY_OF_MONTH));
}
static ThaiBuddhistDate readExternal(DataInput in) throws IOException {
int year = in.readInt();
int month = in.readByte();
int dayOfMonth = in.readByte();
return ThaiBuddhistChronology.INSTANCE.date(year, month, dayOfMonth);
}
}

View file

@ -0,0 +1,176 @@
/*
* Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* This file is available under and governed by the GNU General Public
* License version 2 only, as published by the Free Software Foundation.
* However, the following notice accompanied the original version of this
* file:
*
* Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of JSR-310 nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package java.time.chrono;
import static java.time.temporal.ChronoField.ERA;
import java.time.DateTimeException;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.TextStyle;
import java.util.Locale;
/**
* An era in the Thai Buddhist calendar system.
* <p>
* The Thai Buddhist calendar system has two eras.
* The current era, for years from 1 onwards, is known as the 'Buddhist' era.
* All previous years, zero or earlier in the proleptic count or one and greater
* in the year-of-era count, are part of the 'Before Buddhist' era.
*
* <table class="striped" style="text-align:left">
* <caption style="display:none">Buddhist years and eras</caption>
* <thead>
* <tr>
* <th scope="col">year-of-era</th>
* <th scope="col">era</th>
* <th scope="col">proleptic-year</th>
* <th scope="col">ISO proleptic-year</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td>2</td><td>BE</td><th scope="row">2</th><td>-542</td>
* </tr>
* <tr>
* <td>1</td><td>BE</td><th scope="row">1</th><td>-543</td>
* </tr>
* <tr>
* <td>1</td><td>BEFORE_BE</td><th scope="row">0</th><td>-544</td>
* </tr>
* <tr>
* <td>2</td><td>BEFORE_BE</td><th scope="row">-1</th><td>-545</td>
* </tr>
* </tbody>
* </table>
* <p>
* <b>Do not use {@code ordinal()} to obtain the numeric representation of {@code ThaiBuddhistEra}.
* Use {@code getValue()} instead.</b>
*
* @implSpec
* This is an immutable and thread-safe enum.
*
* @since 1.8
*/
public enum ThaiBuddhistEra implements Era {
/**
* The singleton instance for the era before the current one, 'Before Buddhist Era',
* which has the numeric value 0.
*/
BEFORE_BE,
/**
* The singleton instance for the current era, 'Buddhist Era',
* which has the numeric value 1.
*/
BE;
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code ThaiBuddhistEra} from an {@code int} value.
* <p>
* {@code ThaiBuddhistEra} is an enum representing the Thai Buddhist eras of BEFORE_BE/BE.
* This factory allows the enum to be obtained from the {@code int} value.
*
* @param thaiBuddhistEra the era to represent, from 0 to 1
* @return the BuddhistEra singleton, never null
* @throws DateTimeException if the era is invalid
*/
public static ThaiBuddhistEra of(int thaiBuddhistEra) {
switch (thaiBuddhistEra) {
case 0:
return BEFORE_BE;
case 1:
return BE;
default:
throw new DateTimeException("Invalid era: " + thaiBuddhistEra);
}
}
//-----------------------------------------------------------------------
/**
* Gets the numeric era {@code int} value.
* <p>
* The era BEFORE_BE has the value 0, while the era BE has the value 1.
*
* @return the era value, from 0 (BEFORE_BE) to 1 (BE)
*/
@Override
public int getValue() {
return ordinal();
}
/**
* {@inheritDoc}
*
* @param style {@inheritDoc}
* @param locale {@inheritDoc}
*/
@Override
public String getDisplayName(TextStyle style, Locale locale) {
return new DateTimeFormatterBuilder()
.appendText(ERA, style)
.toFormatter(locale)
.withChronology(ThaiBuddhistChronology.INSTANCE)
.format(this == BE ? ThaiBuddhistDate.of(1, 1, 1) : ThaiBuddhistDate.of(0, 1, 1));
}
}

View file

@ -0,0 +1,369 @@
# Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation. Oracle designates this
# particular file as subject to the "Classpath" exception as provided
# by Oracle in the LICENSE file that accompanied this code.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
# or visit www.oracle.com if you need additional information or have any
# questions.
#
# This properties file defines a Hijrah calendar variant.
#
# Fields:
#
# <version> ::= 'version' '=' <version string>
# <id> ::= 'id' '=' <id string>
# <type> ::= 'type' '=' <type string>
# <iso-start> ::= 'iso-start' '=' <start date in the ISO calendar>
# <year> ::= <yyyy> '=' <nn nn nn nn nn nn nn nn nn nn nn nn>
#
# version ... (Required)
#
# id ... (Required)
# Identifies the Java Chronology
#
# type ... (Required)
# Identifies the type of calendar in the standard calendar ID scheme
# iso-start ... (Required)
# Specifies the corresponding ISO date to the first Hijrah day
# in the defined range of dates
#
# year ... (Required)
# Number of days for each month of a Hijrah year
# * Each line defines a year. The years must be in chronological
# order and no gap is allowed.
# * Each line is in the form indicated above. <yyyy> is a Hijrah year and
# nn is the number of days for a month listed in the order of the months.
# * Each year must have 12 months.
# * Each month should be 29 or 30 days long.
# * There must be one or more space characters between the months.
#
# Version of this definition
version=1.8.0_1
# Java chronology ID
id=Hijrah-umalqura
# Standard calendar type specification
type=islamic-umalqura
# defines the corresponding ISO date to the earliest Hijrah date
iso-start=1882-11-12
# 1 2 3 4 5 6 7 8 9 10 11 12
1300=30 29 30 29 30 29 30 29 30 29 30 29
1301=30 30 29 30 29 30 29 30 29 30 29 29
1302=30 30 30 29 30 30 29 29 30 29 29 30
1303=29 30 30 29 30 30 29 30 29 30 29 29
1304=29 30 30 29 30 30 30 29 30 29 30 29
1305=29 29 30 30 29 30 30 29 30 30 29 29
1306=30 29 30 29 30 29 30 29 30 30 29 30
1307=29 30 29 30 29 30 29 30 29 30 29 30
1308=29 30 30 29 30 29 30 29 30 29 29 30
1309=29 30 30 30 30 29 29 30 29 29 30 29
1310=30 29 30 30 30 29 30 29 30 29 29 30
1311=29 30 29 30 30 30 29 30 29 30 29 29
1312=30 29 30 29 30 30 29 30 30 29 30 29
1313=29 30 29 30 29 30 29 30 30 30 29 29
1314=30 30 29 30 29 29 30 29 30 30 29 30
1315=29 30 30 29 30 29 29 30 29 30 29 30
1316=29 30 30 30 29 30 29 29 30 29 30 29
1317=30 29 30 30 29 30 29 30 29 30 29 29
1318=30 29 30 30 29 30 30 29 30 29 30 29
1319=29 30 29 30 30 29 30 29 30 30 29 30
1320=29 30 29 29 30 29 30 29 30 30 30 29
1321=30 29 30 29 29 30 29 29 30 30 30 30
1322=29 30 29 30 29 29 29 30 29 30 30 30
1323=29 30 30 29 30 29 29 29 30 29 30 30
1324=29 30 30 29 30 29 30 29 29 30 29 30
1325=30 29 30 29 30 30 29 30 29 30 29 30
1326=29 29 30 29 30 30 29 30 29 30 30 29
1327=30 29 29 30 29 30 29 30 30 29 30 30
1328=29 30 29 29 30 29 29 30 30 30 29 30
1329=30 29 30 29 29 30 29 29 30 30 29 30
1330=30 30 29 30 29 29 30 29 29 30 30 29
1331=30 30 29 30 30 29 29 30 29 30 29 30
1332=29 30 29 30 30 29 30 29 30 30 29 29
1333=30 29 29 30 30 29 30 30 29 30 30 29
1334=29 29 30 29 30 29 30 30 30 29 30 29
1335=30 29 30 29 29 30 29 30 30 29 30 30
1336=29 30 29 30 29 29 30 29 30 29 30 30
1337=30 29 30 29 30 29 29 30 29 30 29 30
1338=29 30 30 29 30 30 29 29 30 29 30 29
1339=30 29 30 29 30 30 30 29 30 29 29 30
1340=29 29 30 29 30 30 30 30 29 30 29 29
1341=30 29 29 30 29 30 30 30 29 30 30 29
1342=29 29 30 29 30 29 30 30 29 30 30 29
1343=30 29 29 30 29 30 29 30 29 30 30 29
1344=30 29 30 29 30 30 29 29 30 29 30 29
1345=30 29 30 30 30 29 30 29 29 30 29 29
1346=30 29 30 30 30 30 29 30 29 29 30 29
1347=29 30 29 30 30 30 29 30 30 29 29 30
1348=29 29 30 29 30 30 29 30 30 30 29 29
1349=30 29 29 30 29 30 30 29 30 30 29 30
1350=29 30 29 30 29 30 29 29 30 30 29 30
1351=30 29 30 29 30 29 30 29 29 30 29 30
1352=30 29 30 30 29 30 29 30 29 29 30 29
1353=30 29 30 30 30 29 30 29 29 30 29 30
1354=29 30 29 30 30 29 30 30 29 30 29 29
1355=30 29 29 30 30 29 30 30 29 30 30 29
1356=29 30 29 30 29 30 29 30 29 30 30 30
1357=29 29 30 29 30 29 29 30 29 30 30 30
1358=29 30 29 30 29 30 29 29 30 29 30 30
1359=29 30 30 29 30 29 30 29 29 29 30 30
1360=29 30 30 30 29 30 29 30 29 29 30 29
1361=30 29 30 30 29 30 30 29 29 30 29 30
1362=29 30 29 30 29 30 30 29 30 29 30 29
1363=30 29 30 29 30 29 30 29 30 29 30 30
1364=29 30 29 30 29 29 30 29 30 29 30 30
1365=30 30 29 29 30 29 29 30 29 30 29 30
1366=30 30 29 30 29 30 29 29 30 29 30 29
1367=30 30 29 30 30 29 30 29 29 30 29 30
1368=29 30 29 30 30 30 29 29 30 29 30 29
1369=30 29 30 29 30 30 29 30 29 30 30 29
1370=30 29 29 30 29 30 29 30 29 30 30 30
1371=29 30 29 29 30 29 30 29 30 29 30 30
1372=30 29 29 30 29 30 29 29 30 29 30 30
1373=30 29 30 29 30 29 30 29 29 30 29 30
1374=30 29 30 30 29 30 29 30 29 29 30 29
1375=30 29 30 30 29 30 30 29 30 29 30 29
1376=29 30 29 30 29 30 30 30 29 30 29 30
1377=29 29 30 29 29 30 30 30 29 30 30 29
1378=30 29 29 29 30 29 30 30 29 30 30 30
1379=29 30 29 29 29 30 29 30 30 29 30 30
1380=29 30 29 30 29 30 29 30 29 30 29 30
1381=29 30 29 30 30 29 30 29 30 29 29 30
1382=29 30 29 30 30 29 30 30 29 30 29 29
1383=30 29 29 30 30 30 29 30 30 29 30 29
1384=29 30 29 29 30 30 29 30 30 30 29 30
1385=29 29 30 29 29 30 30 29 30 30 30 29
1386=30 29 29 30 29 29 30 30 29 30 30 29
1387=30 29 30 29 30 29 30 29 30 29 30 29
1388=30 30 29 30 29 30 29 30 29 30 29 29
1389=30 30 29 30 30 29 30 30 29 29 30 29
1390=29 30 29 30 30 30 29 30 29 30 29 30
1391=29 29 30 29 30 30 29 30 30 29 30 29
1392=30 29 29 30 29 30 29 30 30 29 30 30
1393=29 30 29 29 30 29 30 29 30 29 30 30
1394=30 29 30 29 29 30 29 30 29 30 29 30
1395=30 29 30 30 29 30 29 29 30 29 29 30
1396=30 29 30 30 29 30 30 29 29 30 29 29
1397=30 29 30 30 29 30 30 30 29 29 29 30
1398=29 30 29 30 30 29 30 30 29 30 29 29
1399=30 29 30 29 30 29 30 30 29 30 29 30
1400=30 29 30 29 29 30 29 30 29 30 29 30
1401=30 30 29 30 29 29 30 29 29 30 29 30
1402=30 30 30 29 30 29 29 30 29 29 30 29
1403=30 30 30 29 30 30 29 29 30 29 29 30
1404=29 30 30 29 30 30 29 30 29 30 29 29
1405=30 29 30 29 30 30 30 29 30 29 29 30
1406=30 29 29 30 29 30 30 29 30 29 30 30
1407=29 30 29 29 30 29 30 29 30 29 30 30
1408=30 29 30 29 30 29 29 30 29 29 30 30
1409=30 30 29 30 29 30 29 29 30 29 29 30
1410=30 30 29 30 30 29 30 29 29 30 29 29
1411=30 30 29 30 30 29 30 30 29 29 30 29
1412=30 29 30 29 30 29 30 30 30 29 29 30
1413=29 30 29 29 30 29 30 30 30 29 30 29
1414=30 29 30 29 29 30 29 30 30 29 30 30
1415=29 30 29 30 29 29 30 29 30 29 30 30
1416=30 29 30 29 30 29 29 30 29 30 29 30
1417=30 29 30 30 29 29 30 29 30 29 30 29
1418=30 29 30 30 29 30 29 30 29 30 29 30
1419=29 30 29 30 29 30 29 30 30 30 29 29
1420=29 30 29 29 30 29 30 30 30 30 29 30
1421=29 29 30 29 29 29 30 30 30 30 29 30
1422=30 29 29 30 29 29 29 30 30 30 29 30
1423=30 29 30 29 30 29 29 30 29 30 29 30
1424=30 29 30 30 29 30 29 29 30 29 30 29
1425=30 29 30 30 29 30 29 30 30 29 30 29
1426=29 30 29 30 29 30 30 29 30 30 29 30
1427=29 29 30 29 30 29 30 30 29 30 30 29
1428=30 29 29 30 29 29 30 30 30 29 30 30
1429=29 30 29 29 30 29 29 30 30 29 30 30
1430=29 30 30 29 29 30 29 30 29 30 29 30
1431=29 30 30 29 30 29 30 29 30 29 29 30
1432=29 30 30 30 29 30 29 30 29 30 29 29
1433=30 29 30 30 29 30 30 29 30 29 30 29
1434=29 30 29 30 29 30 30 29 30 30 29 29
1435=30 29 30 29 30 29 30 29 30 30 29 30
1436=29 30 29 30 29 30 29 30 29 30 29 30
1437=30 29 30 30 29 29 30 29 30 29 29 30
1438=30 29 30 30 30 29 29 30 29 29 30 29
1439=30 29 30 30 30 29 30 29 30 29 29 30
1440=29 30 29 30 30 30 29 30 29 30 29 29
1441=30 29 30 29 30 30 29 30 30 29 30 29
1442=29 30 29 30 29 30 29 30 30 29 30 29
1443=30 29 30 29 30 29 30 29 30 29 30 30
1444=29 30 29 30 30 29 29 30 29 30 29 30
1445=29 30 30 30 29 30 29 29 30 29 29 30
1446=29 30 30 30 29 30 30 29 29 30 29 29
1447=30 29 30 30 30 29 30 29 30 29 30 29
1448=29 30 29 30 30 29 30 30 29 30 29 30
1449=29 29 30 29 30 29 30 30 29 30 30 29
1450=30 29 30 29 29 30 29 30 29 30 30 29
1451=30 30 30 29 29 30 29 29 30 30 29 30
1452=30 29 30 30 29 29 30 29 29 30 29 30
1453=30 29 30 30 29 30 29 30 29 29 30 29
1454=30 29 30 30 29 30 30 29 30 29 30 29
1455=29 30 29 30 30 29 30 29 30 30 29 30
1456=29 29 30 29 30 29 30 29 30 30 30 29
1457=30 29 29 30 29 29 30 29 30 30 30 30
1458=29 30 29 29 30 29 29 30 29 30 30 30
1459=29 30 30 29 29 30 29 29 30 29 30 30
1460=29 30 30 29 30 29 30 29 29 30 29 30
1461=29 30 30 29 30 29 30 29 30 30 29 29
1462=30 29 30 29 30 30 29 30 29 30 30 29
1463=29 30 29 30 29 30 29 30 30 30 29 30
1464=29 30 29 29 30 29 29 30 30 30 29 30
1465=30 29 30 29 29 30 29 29 30 30 29 30
1466=30 30 29 30 29 29 29 30 29 30 30 29
1467=30 30 29 30 30 29 29 30 29 30 29 30
1468=29 30 29 30 30 29 30 29 30 29 30 29
1469=29 30 29 30 30 29 30 30 29 30 29 30
1470=29 29 30 29 30 30 29 30 30 29 30 29
1471=30 29 29 30 29 30 29 30 30 29 30 30
1472=29 30 29 29 30 29 30 29 30 30 29 30
1473=29 30 29 30 30 29 29 30 29 30 29 30
1474=29 30 30 29 30 30 29 29 30 29 30 29
1475=29 30 30 29 30 30 30 29 29 30 29 29
1476=30 29 30 29 30 30 30 29 30 29 30 29
1477=29 30 29 29 30 30 30 30 29 30 29 30
1478=29 29 30 29 30 29 30 30 29 30 30 29
1479=30 29 29 30 29 30 29 30 29 30 30 29
1480=30 29 30 29 30 29 30 29 30 29 30 29
1481=30 29 30 30 29 30 29 30 29 30 29 29
1482=30 29 30 30 30 30 29 30 29 29 30 29
1483=29 30 29 30 30 30 29 30 30 29 29 30
1484=29 29 30 29 30 30 30 29 30 29 30 29
1485=30 29 29 30 29 30 30 29 30 30 29 30
1486=29 30 29 29 30 29 30 29 30 30 29 30
1487=30 29 30 29 30 29 29 30 29 30 29 30
1488=30 29 30 30 29 30 29 29 30 29 30 29
1489=30 29 30 30 30 29 30 29 29 30 29 30
1490=29 30 29 30 30 29 30 30 29 29 30 29
1491=30 29 29 30 30 29 30 30 29 30 29 30
1492=29 30 29 29 30 30 29 30 29 30 30 29
1493=30 29 30 29 30 29 29 30 29 30 30 30
1494=29 30 29 30 29 30 29 29 29 30 30 30
1495=29 30 30 29 30 29 29 30 29 29 30 30
1496=29 30 30 30 29 30 29 29 30 29 29 30
1497=30 29 30 30 29 30 29 30 29 30 29 30
1498=29 30 29 30 29 30 30 29 30 29 30 29
1499=30 29 30 29 29 30 30 29 30 29 30 30
1500=29 30 29 30 29 29 30 29 30 29 30 30
1501=30 29 30 29 30 29 29 29 30 29 30 30
1502=30 30 29 30 29 30 29 29 29 30 30 29
1503=30 30 29 30 30 29 30 29 29 29 30 30
1504=29 30 29 30 30 30 29 29 30 29 30 29
1505=30 29 30 29 30 30 29 30 29 30 30 29
1506=29 30 29 29 30 30 29 30 30 29 30 30
1507=29 29 30 29 29 30 30 29 30 29 30 30
1508=30 29 29 30 29 30 29 29 30 29 30 30
1509=30 29 30 29 30 29 30 29 29 30 29 30
1510=30 29 30 30 29 30 29 30 29 29 30 29
1511=30 29 30 30 29 30 30 29 30 29 29 30
1512=29 30 29 30 29 30 30 30 29 30 29 30
1513=29 29 29 30 29 30 30 30 29 30 30 29
1514=30 29 29 29 30 29 30 30 29 30 30 30
1515=29 29 30 29 29 30 29 30 30 29 30 30
1516=29 30 29 30 29 29 30 29 30 29 30 30
1517=29 30 29 30 29 30 30 29 29 30 29 30
1518=29 30 29 30 30 29 30 30 29 30 29 29
1519=30 29 29 30 30 30 29 30 30 29 30 29
1520=29 30 29 29 30 30 30 29 30 30 29 30
1521=29 29 29 30 29 30 30 29 30 30 29 30
1522=30 29 29 29 30 29 30 30 29 30 30 29
1523=30 29 30 29 30 29 30 29 29 30 30 29
1524=30 30 29 30 29 30 29 30 29 29 30 29
1525=30 30 29 30 30 29 30 29 30 29 29 30
1526=29 30 29 30 30 30 29 30 29 30 29 29
1527=30 29 30 29 30 30 29 30 30 29 30 29
1528=30 29 29 30 29 30 29 30 30 29 30 30
1529=29 30 29 29 30 29 30 29 30 29 30 30
1530=29 30 30 29 29 30 29 30 29 29 30 30
1531=29 30 30 30 29 29 30 29 30 29 29 30
1532=29 30 30 30 29 30 30 29 29 29 30 29
1533=30 29 30 30 30 29 30 29 30 29 29 30
1534=29 30 29 30 30 29 30 30 29 29 30 29
1535=30 29 30 29 30 29 30 30 29 30 29 30
1536=29 30 29 30 29 30 29 30 29 30 29 30
1537=30 29 30 30 29 29 30 29 29 30 29 30
1538=30 30 29 30 30 29 29 30 29 29 30 29
1539=30 30 30 29 30 30 29 29 30 29 29 30
1540=29 30 30 29 30 30 29 30 29 29 30 29
1541=30 29 30 29 30 30 30 29 30 29 29 30
1542=29 30 29 30 29 30 30 29 30 29 30 30
1543=29 30 29 29 30 29 30 29 30 29 30 30
1544=30 29 30 29 29 30 29 30 29 30 29 30
1545=30 30 29 30 29 29 30 29 30 29 29 30
1546=30 30 29 30 29 30 29 30 29 30 29 29
1547=30 30 29 30 30 29 30 29 30 29 30 29
1548=30 29 29 30 30 29 30 30 29 30 29 30
1549=29 30 29 29 30 29 30 30 30 29 30 29
1550=30 29 30 29 29 29 30 30 30 29 30 30
1551=29 30 29 29 30 29 29 30 30 29 30 30
1552=30 29 30 29 29 30 29 29 30 30 29 30
1553=30 29 30 29 30 29 30 29 30 29 30 29
1554=30 29 30 29 30 30 29 30 29 30 29 30
1555=29 29 30 29 30 30 29 30 30 29 30 29
1556=30 29 29 30 29 30 29 30 30 30 29 30
1557=29 30 29 29 29 30 29 30 30 30 30 29
1558=30 29 30 29 29 29 30 29 30 30 30 29
1559=30 30 29 29 30 29 29 30 30 29 30 29
1560=30 30 29 30 29 30 29 30 29 30 29 30
1561=29 30 30 29 30 29 30 30 29 29 30 29
1562=29 30 30 29 30 29 30 30 30 29 29 30
1563=29 30 29 29 30 29 30 30 30 29 30 29
1564=30 29 30 29 29 30 29 30 30 30 29 30
1565=29 30 29 30 29 29 30 29 30 30 29 30
1566=30 29 30 29 30 29 29 30 29 30 29 30
1567=30 29 30 30 29 30 29 30 29 29 30 29
1568=30 29 30 30 30 29 30 29 30 29 29 29
1569=30 29 30 30 30 29 30 30 29 30 29 29
1570=29 30 29 30 30 29 30 30 30 29 29 30
1571=29 29 30 29 30 30 29 30 30 29 30 29
1572=30 29 29 30 29 30 29 30 30 29 30 29
1573=30 29 30 30 29 30 29 29 30 29 30 29
1574=30 30 29 30 30 29 30 29 29 30 29 29
1575=30 30 30 29 30 30 29 30 29 29 29 30
1576=29 30 30 29 30 30 30 29 30 29 29 29
1577=30 29 30 30 29 30 30 29 30 29 30 29
1578=29 30 29 30 29 30 30 29 30 30 29 30
1579=29 30 29 30 29 29 30 30 29 30 29 30
1580=29 30 30 29 30 29 29 30 29 30 29 30
1581=30 30 29 30 29 30 29 29 30 29 30 29
1582=30 30 29 30 30 29 30 29 30 29 29 29
1583=30 30 29 30 30 30 29 30 29 30 29 29
1584=29 30 30 29 30 30 29 30 30 29 30 29
1585=29 30 29 30 29 30 29 30 30 29 30 30
1586=29 29 30 29 30 29 29 30 30 30 29 30
1587=29 30 30 29 29 29 30 29 30 29 30 30
1588=30 29 30 30 29 29 29 30 29 30 29 30
1589=30 29 30 30 29 30 29 29 30 29 30 29
1590=30 29 30 30 30 29 29 30 29 30 29 30
1591=29 30 29 30 30 29 30 29 30 29 30 29
1592=30 29 30 29 30 29 30 29 30 30 30 29
1593=30 29 29 30 29 29 30 29 30 30 30 29
1594=30 30 29 29 30 29 29 29 30 30 30 30
1595=29 30 29 30 29 29 30 29 29 30 30 30
1596=29 30 30 29 30 29 29 30 29 30 29 30
1597=29 30 30 29 30 29 30 29 30 29 30 29
1598=30 29 30 29 30 30 29 30 29 30 30 29
1599=29 30 29 30 29 30 29 30 30 30 29 30
1600=29 29 30 29 30 29 29 30 30 30 29 30

View file

@ -0,0 +1,171 @@
/*
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* This file is available under and governed by the GNU General Public
* License version 2 only, as published by the Free Software Foundation.
* However, the following notice accompanied the original version of this
* file:
*
* Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of JSR-310 nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* <p>
* Generic API for calendar systems other than the default ISO.
* </p>
* <p>
* The main API is based around the calendar system defined in ISO-8601.
* However, there are other calendar systems, and this package provides basic support for them.
* The alternate calendars are provided in the {@link java.time.chrono} package.
* </p>
* <p>
* A calendar system is defined by the {@link java.time.chrono.Chronology} interface,
* while a date in a calendar system is defined by the {@link java.time.chrono.ChronoLocalDate} interface.
* </p>
* <p>
* It is intended that applications use the main API whenever possible, including code to read and write
* from a persistent data store, such as a database, and to send dates and times across a network.
* The "chrono" classes are then used at the user interface level to deal with localized input/output.
* See {@link java.time.chrono.ChronoLocalDate ChronoLocalDate}
* for a full discussion of the issues.
* </p>
* <p>
* Using non-ISO calendar systems in an application introduces significant extra complexity.
* Ensure that the warnings and recommendations in {@code ChronoLocalDate} have been read before
* working with the "chrono" interfaces.
* </p>
* <p>
* The supported calendar systems includes:
* </p>
* <ul>
* <li>{@link java.time.chrono.HijrahChronology Hijrah calendar}</li>
* <li>{@link java.time.chrono.JapaneseChronology Japanese calendar}</li>
* <li>{@link java.time.chrono.MinguoChronology Minguo calendar}</li>
* <li>{@link java.time.chrono.ThaiBuddhistChronology Thai Buddhist calendar}</li>
* </ul>
*
* <h3>Example</h3>
* <p>
* This example lists todays date for all of the available calendars.
* </p>
* <pre>
* // Enumerate the list of available calendars and print todays date for each.
* Set&lt;Chronology&gt; chronos = Chronology.getAvailableChronologies();
* for (Chronology chrono : chronos) {
* ChronoLocalDate date = chrono.dateNow();
* System.out.printf(" %20s: %s%n", chrono.getId(), date.toString());
* }
* </pre>
*
* <p>
* This example creates and uses a date in a named non-ISO calendar system.
* </p>
* <pre>
* // Print the Thai Buddhist date
* ChronoLocalDate now1 = Chronology.of("ThaiBuddhist").dateNow();
* int day = now1.get(ChronoField.DAY_OF_MONTH);
* int dow = now1.get(ChronoField.DAY_OF_WEEK);
* int month = now1.get(ChronoField.MONTH_OF_YEAR);
* int year = now1.get(ChronoField.YEAR);
* System.out.printf(" Today is %s %s %d-%s-%d%n", now1.getChronology().getId(),
* dow, day, month, year);
* // Print today's date and the last day of the year for the Thai Buddhist Calendar.
* ChronoLocalDate first = now1
* .with(ChronoField.DAY_OF_MONTH, 1)
* .with(ChronoField.MONTH_OF_YEAR, 1);
* ChronoLocalDate last = first
* .plus(1, ChronoUnit.YEARS)
* .minus(1, ChronoUnit.DAYS);
* System.out.printf(" %s: 1st of year: %s; end of year: %s%n", last.getChronology().getId(),
* first, last);
* </pre>
*
* <p>
* This example creates and uses a date in a specific ThaiBuddhist calendar system.
* </p>
* <pre>
* // Print the Thai Buddhist date
* ThaiBuddhistDate now1 = ThaiBuddhistDate.now();
* int day = now1.get(ChronoField.DAY_OF_MONTH);
* int dow = now1.get(ChronoField.DAY_OF_WEEK);
* int month = now1.get(ChronoField.MONTH_OF_YEAR);
* int year = now1.get(ChronoField.YEAR);
* System.out.printf(" Today is %s %s %d-%s-%d%n", now1.getChronology().getId(),
* dow, day, month, year);
*
* // Print today's date and the last day of the year for the Thai Buddhist Calendar.
* ThaiBuddhistDate first = now1
* .with(ChronoField.DAY_OF_MONTH, 1)
* .with(ChronoField.MONTH_OF_YEAR, 1);
* ThaiBuddhistDate last = first
* .plus(1, ChronoUnit.YEARS)
* .minus(1, ChronoUnit.DAYS);
* System.out.printf(" %s: 1st of year: %s; end of year: %s%n", last.getChronology().getId(),
* first, last);
* </pre>
*
* <h3>Package specification</h3>
* <p>
* Unless otherwise noted, passing a null argument to a constructor or method in any class or interface
* in this package will cause a {@link java.lang.NullPointerException NullPointerException} to be thrown.
* The Javadoc "@param" definition is used to summarise the null-behavior.
* The "@throws {@link java.lang.NullPointerException}" is not explicitly documented in each method.
* </p>
* <p>
* All calculations should check for numeric overflow and throw either an {@link java.lang.ArithmeticException}
* or a {@link java.time.DateTimeException}.
* </p>
* @since 1.8
*/
package java.time.chrono;