/*
* Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
/*
*
*
*
*
*
* Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of JSR-310 nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package java.time.chrono;
import static java.time.chrono.
JapaneseDate.
MEIJI_6_ISODATE;
import static java.time.temporal.
ChronoField.
ERA;
import java.io.
DataInput;
import java.io.
DataOutput;
import java.io.
IOException;
import java.io.
InvalidObjectException;
import java.io.
ObjectInputStream;
import java.io.
ObjectStreamException;
import java.io.
Serializable;
import java.time.
DateTimeException;
import java.time.
LocalDate;
import java.time.format.
DateTimeFormatterBuilder;
import java.time.format.
TextStyle;
import java.time.temporal.
ChronoField;
import java.time.temporal.
TemporalField;
import java.time.temporal.
UnsupportedTemporalTypeException;
import java.time.temporal.
ValueRange;
import java.util.
Arrays;
import java.util.
Locale;
import java.util.
Objects;
import sun.util.calendar.
CalendarDate;
/**
* An era in the Japanese Imperial calendar system.
* <p>
* The Japanese government defines the official name and start date of
* each era. Eras are consecutive and their date ranges do not overlap,
* so the end date of one era is always the day before the start date
* of the next era.
* <p>
* The Java SE Platform supports all eras defined by the Japanese government,
* beginning with the Meiji era. Each era is identified in the Platform by an
* integer value and a name. The {@link #of(int)} and {@link #valueOf(String)}
* methods may be used to obtain a singleton instance of JapaneseEra for each
* era. The {@link #values()} method returns the singleton instances of all
* supported eras.
* <p>
* For convenience, this class declares a number of public static final fields
* that refer to singleton instances returned by the values() method.
*
* @apiNote
* The fields declared in this class may evolve over time, in line with the
* results of the {@link #values()} method. However, there is not necessarily
* a 1:1 correspondence between the fields and the singleton instances.
*
* @apiNote
* The Japanese government may announce a new era and define its start
* date but not its official name. In this scenario, the singleton instance
* that represents the new era may return a name that is not stable until
* the official name is defined. Developers should exercise caution when
* relying on the name returned by any singleton instance that does not
* correspond to a public static final field.
*
* @implSpec
* This class is immutable and thread-safe.
*
* @since 1.8
*/
public final class
JapaneseEra
implements
Era,
Serializable {
// The offset value to 0-based index from the era value.
// i.e., getValue() + ERA_OFFSET == 0-based index
static final int
ERA_OFFSET = 2;
static final sun.util.calendar.
Era[]
ERA_CONFIG;
/**
* The singleton instance for the 'Meiji' era (1868-01-01 - 1912-07-29)
* which has the value -1.
*/
public static final
JapaneseEra MEIJI = new
JapaneseEra(-1,
LocalDate.
of(1868, 1, 1));
/**
* The singleton instance for the 'Taisho' era (1912-07-30 - 1926-12-24)
* which has the value 0.
*/
public static final
JapaneseEra TAISHO = new
JapaneseEra(0,
LocalDate.
of(1912, 7, 30));
/**
* The singleton instance for the 'Showa' era (1926-12-25 - 1989-01-07)
* which has the value 1.
*/
public static final
JapaneseEra SHOWA = new
JapaneseEra(1,
LocalDate.
of(1926, 12, 25));
/**
* The singleton instance for the 'Heisei' era (1989-01-08 - 2019-04-30)
* which has the value 2.
*/
public static final
JapaneseEra HEISEI = new
JapaneseEra(2,
LocalDate.
of(1989, 1, 8));
/**
* The singleton instance for the 'Reiwa' era (2019-05-01 - current)
* which has the value 3.
*/
private static final
JapaneseEra REIWA = new
JapaneseEra(3,
LocalDate.
of(2019, 5, 1));
// The number of predefined JapaneseEra constants.
// There may be a supplemental era defined by the property.
private static final int
N_ERA_CONSTANTS =
REIWA.
getValue() +
ERA_OFFSET;
/**
* Serialization version.
*/
private static final long
serialVersionUID = 1466499369062886794L;
// array for the singleton JapaneseEra instances
private static final
JapaneseEra[]
KNOWN_ERAS;
static {
ERA_CONFIG =
JapaneseChronology.
JCAL.
getEras();
KNOWN_ERAS = new
JapaneseEra[
ERA_CONFIG.length];
KNOWN_ERAS[0] =
MEIJI;
KNOWN_ERAS[1] =
TAISHO;
KNOWN_ERAS[2] =
SHOWA;
KNOWN_ERAS[3] =
HEISEI;
KNOWN_ERAS[4] =
REIWA;
for (int
i =
N_ERA_CONSTANTS;
i <
ERA_CONFIG.length;
i++) {
CalendarDate date =
ERA_CONFIG[
i].
getSinceDate();
LocalDate isoDate =
LocalDate.
of(
date.
getYear(),
date.
getMonth(),
date.
getDayOfMonth());
KNOWN_ERAS[
i] = new
JapaneseEra(
i -
ERA_OFFSET + 1,
isoDate);
}
};
/**
* The era value.
* @serial
*/
private final transient int
eraValue;
// the first day of the era
private final transient
LocalDate since;
/**
* Creates an instance.
*
* @param eraValue the era value, validated
* @param since the date representing the first date of the era, validated not null
*/
private
JapaneseEra(int
eraValue,
LocalDate since) {
this.
eraValue =
eraValue;
this.
since =
since;
}
//-----------------------------------------------------------------------
/**
* Returns the Sun private Era instance corresponding to this {@code JapaneseEra}.
*
* @return the Sun private Era instance for this {@code JapaneseEra}.
*/
sun.util.calendar.
Era getPrivateEra() {
return
ERA_CONFIG[
ordinal(
eraValue)];
}
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code JapaneseEra} from an {@code int} value.
* <ul>
* <li>The value {@code 1} is associated with the 'Showa' era, because
* it contains 1970-01-01 (ISO calendar system).</li>
* <li>The values {@code -1} and {@code 0} are associated with two earlier
* eras, Meiji and Taisho, respectively.</li>
* <li>A value greater than {@code 1} is associated with a later era,
* beginning with Heisei ({@code 2}).</li>
* </ul>
* <p>
* Every instance of {@code JapaneseEra} that is returned from the {@link values()}
* method has an int value (available via {@link Era#getValue()} which is
* accepted by this method.
*
* @param japaneseEra the era to represent
* @return the {@code JapaneseEra} singleton, not null
* @throws DateTimeException if the value is invalid
*/
public static
JapaneseEra of(int
japaneseEra) {
if (
japaneseEra <
MEIJI.
eraValue ||
japaneseEra +
ERA_OFFSET >
KNOWN_ERAS.length) {
throw new
DateTimeException("Invalid era: " +
japaneseEra);
}
return
KNOWN_ERAS[
ordinal(
japaneseEra)];
}
/**
* Returns the {@code JapaneseEra} with the name.
* <p>
* The string must match exactly the name of the era.
* (Extraneous whitespace characters are not permitted.)
*
* @param japaneseEra the japaneseEra name; non-null
* @return the {@code JapaneseEra} singleton, never null
* @throws IllegalArgumentException if there is not JapaneseEra with the specified name
*/
public static
JapaneseEra valueOf(
String japaneseEra) {
Objects.
requireNonNull(
japaneseEra, "japaneseEra");
for (
JapaneseEra era :
KNOWN_ERAS) {
if (
era.
getName().
equals(
japaneseEra)) {
return
era;
}
}
throw new
IllegalArgumentException("japaneseEra is invalid");
}
/**
* Returns an array of JapaneseEras.
* <p>
* This method may be used to iterate over the JapaneseEras as follows:
* <pre>
* for (JapaneseEra c : JapaneseEra.values())
* System.out.println(c);
* </pre>
*
* @return an array of JapaneseEras
*/
public static
JapaneseEra[]
values() {
return
Arrays.
copyOf(
KNOWN_ERAS,
KNOWN_ERAS.length);
}
/**
* {@inheritDoc}
*
* @param style {@inheritDoc}
* @param locale {@inheritDoc}
*/
@
Override
public
String getDisplayName(
TextStyle style,
Locale locale) {
// If this JapaneseEra is a supplemental one, obtain the name from
// the era definition.
if (
getValue() >
N_ERA_CONSTANTS -
ERA_OFFSET) {
Objects.
requireNonNull(
locale, "locale");
return
style.
asNormal() ==
TextStyle.
NARROW ?
getAbbreviation() :
getName();
}
return new
DateTimeFormatterBuilder()
.
appendText(
ERA,
style)
.
toFormatter(
locale)
.
withChronology(
JapaneseChronology.
INSTANCE)
.
format(this ==
MEIJI ?
MEIJI_6_ISODATE :
since);
}
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code JapaneseEra} from a date.
*
* @param date the date, not null
* @return the Era singleton, never null
*/
static
JapaneseEra from(
LocalDate date) {
if (
date.
isBefore(
MEIJI_6_ISODATE)) {
throw new
DateTimeException("JapaneseDate before Meiji 6 are not supported");
}
for (int
i =
KNOWN_ERAS.length - 1;
i > 0;
i--) {
JapaneseEra era =
KNOWN_ERAS[
i];
if (
date.
compareTo(
era.
since) >= 0) {
return
era;
}
}
return null;
}
static
JapaneseEra toJapaneseEra(sun.util.calendar.
Era privateEra) {
for (int
i =
ERA_CONFIG.length - 1;
i >= 0;
i--) {
if (
ERA_CONFIG[
i].
equals(
privateEra)) {
return
KNOWN_ERAS[
i];
}
}
return null;
}
static sun.util.calendar.
Era privateEraFrom(
LocalDate isoDate) {
for (int
i =
KNOWN_ERAS.length - 1;
i > 0;
i--) {
JapaneseEra era =
KNOWN_ERAS[
i];
if (
isoDate.
compareTo(
era.
since) >= 0) {
return
ERA_CONFIG[
i];
}
}
return null;
}
/**
* Returns the index into the arrays from the Era value.
* the eraValue is a valid Era number, -1..2.
*
* @param eraValue the era value to convert to the index
* @return the index of the current Era
*/
private static int
ordinal(int
eraValue) {
return
eraValue +
ERA_OFFSET - 1;
}
//-----------------------------------------------------------------------
/**
* Gets the numeric era {@code int} value.
* <p>
* The {@link #SHOWA} era that contains 1970-01-01 (ISO calendar system) has the value 1.
* Later eras are numbered from 2 ({@link #HEISEI}).
* Earlier eras are numbered 0 ({@link #TAISHO}), -1 ({@link #MEIJI})).
*
* @return the era value
*/
@
Override
public int
getValue() {
return
eraValue;
}
//-----------------------------------------------------------------------
/**
* Gets the range of valid values for the specified field.
* <p>
* The range object expresses the minimum and maximum valid values for a field.
* This era is used to enhance the accuracy of the returned range.
* If it is not possible to return the range, because the field is not supported
* or for some other reason, an exception is thrown.
* <p>
* If the field is a {@link ChronoField} then the query is implemented here.
* The {@code ERA} field returns the range.
* All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
* <p>
* If the field is not a {@code ChronoField}, then the result of this method
* is obtained by invoking {@code TemporalField.rangeRefinedBy(TemporalAccessor)}
* passing {@code this} as the argument.
* Whether the range can be obtained is determined by the field.
* <p>
* The range of valid Japanese eras can change over time due to the nature
* of the Japanese calendar system.
*
* @param field the field to query the range for, not null
* @return the range of valid values for the field, not null
* @throws DateTimeException if the range for the field cannot be obtained
* @throws UnsupportedTemporalTypeException if the unit is not supported
*/
@
Override // override as super would return range from 0 to 1
public
ValueRange range(
TemporalField field) {
if (
field ==
ERA) {
return
JapaneseChronology.
INSTANCE.
range(
ERA);
}
return
Era.super.range(
field);
}
//-----------------------------------------------------------------------
String getAbbreviation() {
return
ERA_CONFIG[
ordinal(
getValue())].
getAbbreviation();
}
String getName() {
return
ERA_CONFIG[
ordinal(
getValue())].
getName();
}
@
Override
public
String toString() {
return
getName();
}
//-----------------------------------------------------------------------
/**
* Defend against malicious streams.
*
* @param s the stream to read
* @throws InvalidObjectException always
*/
private void
readObject(
ObjectInputStream s) throws
InvalidObjectException {
throw new
InvalidObjectException("Deserialization via serialization delegate");
}
//-----------------------------------------------------------------------
/**
* Writes the object using a
* <a href="../../../serialized-form.html#java.time.chrono.Ser">dedicated serialized form</a>.
* @serialData
* <pre>
* out.writeByte(5); // identifies a JapaneseEra
* out.writeInt(getValue());
* </pre>
*
* @return the instance of {@code Ser}, not null
*/
private
Object writeReplace() {
return new
Ser(
Ser.
JAPANESE_ERA_TYPE, this);
}
void
writeExternal(
DataOutput out) throws
IOException {
out.
writeByte(this.
getValue());
}
static
JapaneseEra readExternal(
DataInput in) throws
IOException {
byte
eraValue =
in.
readByte();
return
JapaneseEra.
of(
eraValue);
}
}