• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos
3  *
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  *  * Redistributions of source code must retain the above copyright notice,
10  *    this list of conditions and the following disclaimer.
11  *
12  *  * Redistributions in binary form must reproduce the above copyright notice,
13  *    this list of conditions and the following disclaimer in the documentation
14  *    and/or other materials provided with the distribution.
15  *
16  *  * Neither the name of JSR-310 nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 package org.threeten.bp.chrono;
33 
34 import java.io.DataInput;
35 import java.io.DataOutput;
36 import java.io.IOException;
37 import java.io.InvalidObjectException;
38 import java.io.ObjectStreamException;
39 import java.io.Serializable;
40 import java.util.Arrays;
41 import java.util.concurrent.atomic.AtomicReference;
42 
43 import org.threeten.bp.DateTimeException;
44 import org.threeten.bp.LocalDate;
45 import org.threeten.bp.jdk8.DefaultInterfaceEra;
46 import org.threeten.bp.jdk8.Jdk8Methods;
47 import org.threeten.bp.temporal.ChronoField;
48 import org.threeten.bp.temporal.TemporalField;
49 import org.threeten.bp.temporal.ValueRange;
50 
51 /**
52  * An era in the Japanese Imperial calendar system.
53  * <p>
54  * This class defines the valid eras for the Japanese chronology.
55  * Japan introduced the Gregorian calendar starting with Meiji 6.
56  * Only Meiji and later eras are supported;
57  * dates before Meiji 6, January 1 are not supported.
58  * <p>
59  * The four supported eras are hard-coded.
60  * A single additional era may be registered using {@link #registerEra(LocalDate, String)}.
61  *
62  * <h3>Specification for implementors</h3>
63  * This class is immutable and thread-safe.
64  */
65 public final class JapaneseEra
66         extends DefaultInterfaceEra
67         implements Serializable {
68 
69     // The offset value to 0-based index from the era value.
70     // i.e., getValue() + ERA_OFFSET == 0-based index; except that -999 is mapped to zero
71     static final int ERA_OFFSET = 2;
72 
73     /**
74      * The singleton instance for the 'Meiji' era (1868-09-08 - 1912-07-29)
75      * which has the value -1.
76      */
77     public static final JapaneseEra MEIJI = new JapaneseEra(-1, LocalDate.of(1868, 9, 8), "Meiji");
78     /**
79      * The singleton instance for the 'Taisho' era (1912-07-30 - 1926-12-24)
80      * which has the value 0.
81      */
82     public static final JapaneseEra TAISHO = new JapaneseEra(0, LocalDate.of(1912, 7, 30), "Taisho");
83     /**
84      * The singleton instance for the 'Showa' era (1926-12-25 - 1989-01-07)
85      * which has the value 1.
86      */
87     public static final JapaneseEra SHOWA = new JapaneseEra(1, LocalDate.of(1926, 12, 25), "Showa");
88     /**
89      * The singleton instance for the 'Heisei' era (1989-01-08 - 2019-04-30)
90      * which has the value 2.
91      */
92     public static final JapaneseEra HEISEI = new JapaneseEra(2, LocalDate.of(1989, 1, 8), "Heisei");
93     /**
94      * The singleton instance for the 'Reiwa' era (2019-05-01 - current)
95      * which has the value 3.
96      */
97     public static final JapaneseEra REIWA = new JapaneseEra(3, LocalDate.of(2019, 5, 1), "Reiwa");
98     /**
99      * The value of the additional era.
100      */
101     private static final int ADDITIONAL_VALUE = 4;
102 
103     /**
104      * Serialization version.
105      */
106     private static final long serialVersionUID = 1466499369062886794L;
107 
108     // array for the singleton JapaneseEra instances
109     private static final AtomicReference<JapaneseEra[]> KNOWN_ERAS;
110 
111     static {
112         JapaneseEra[] array = new JapaneseEra[5];
113         array[0] = MEIJI;
114         array[1] = TAISHO;
115         array[2] = SHOWA;
116         array[3] = HEISEI;
117         array[4] = REIWA;
118         KNOWN_ERAS = new AtomicReference<JapaneseEra[]>(array);
119     }
120 
121     /**
122      * The era value.
123      * @serial
124      */
125     private final int eraValue;
126 
127     // the first day of the era
128     private final transient LocalDate since;
129     // the name of the era
130     private final transient String name;
131 
132     /**
133      * Creates an instance.
134      *
135      * @param eraValue  the era value, validated
136      * @param since  the date representing the first date of the era, validated not null
137      * @param name  the name
138      */
JapaneseEra(int eraValue, LocalDate since, String name)139     private JapaneseEra(int eraValue, LocalDate since, String name) {
140         this.eraValue = eraValue;
141         this.since = since;
142         this.name = name;
143     }
144 
145     /**
146      * Returns the singleton {@code JapaneseEra} corresponding to this object.
147      * It's possible that this version of {@code JapaneseEra} doesn't support the latest era value.
148      * In that case, this method throws an {@code ObjectStreamException}.
149      *
150      * @return the singleton {@code JapaneseEra} for this object
151      * @throws ObjectStreamException if the deserialized object has any unknown numeric era value.
152      */
readResolve()153     private Object readResolve() throws ObjectStreamException {
154         try {
155             return of(eraValue);
156         } catch (DateTimeException e) {
157             InvalidObjectException ex = new InvalidObjectException("Invalid era");
158             ex.initCause(e);
159             throw ex;
160         }
161     }
162 
163     //-----------------------------------------------------------------------
164     /**
165      * Registers an additional instance of {@code JapaneseEra}.
166      * <p>
167      * A new Japanese era can begin at any time.
168      * This method allows one new era to be registered without the need for a new library version.
169      * If needed, callers should assign the result to a static variable accessible
170      * across the application. This must be done once, in early startup code.
171      * <p>
172      * NOTE: This method does not exist in Java SE 8.
173      *
174      * @param since  the date representing the first date of the era, validated not null
175      * @param name  the name
176      * @return the {@code JapaneseEra} singleton, not null
177      * @throws DateTimeException if an additional era has already been registered
178      */
registerEra(LocalDate since, String name)179     public static JapaneseEra registerEra(LocalDate since, String name) {
180         JapaneseEra[] known = KNOWN_ERAS.get();
181         if (known.length > 5) {
182             throw new DateTimeException("Only one additional Japanese era can be added");
183         }
184         Jdk8Methods.requireNonNull(since, "since");
185         Jdk8Methods.requireNonNull(name, "name");
186         if (!since.isAfter(REIWA.since)) {
187             throw new DateTimeException("Invalid since date for additional Japanese era, must be after Reiwa");
188         }
189         JapaneseEra era = new JapaneseEra(ADDITIONAL_VALUE, since, name);
190         JapaneseEra[] newArray = Arrays.copyOf(known, 6);
191         newArray[5] = era;
192         if (!KNOWN_ERAS.compareAndSet(known, newArray)) {
193             throw new DateTimeException("Only one additional Japanese era can be added");
194         }
195         return era;
196     }
197 
198     /**
199      * Obtains an instance of {@code JapaneseEra} from an {@code int} value.
200      * <p>
201      * The {@link #SHOWA} era that contains 1970-01-01 (ISO calendar system) has the value 1
202      * Later era is numbered 2 ({@link #HEISEI}). Earlier eras are numbered 0 ({@link #TAISHO}),
203      * -1 ({@link #MEIJI}), only Meiji and later eras are supported.
204      *
205      * @param japaneseEra  the era to represent
206      * @return the {@code JapaneseEra} singleton, not null
207      * @throws DateTimeException if the value is invalid
208      */
of(int japaneseEra)209     public static JapaneseEra of(int japaneseEra) {
210         JapaneseEra[] known = KNOWN_ERAS.get();
211         if (japaneseEra < MEIJI.eraValue || japaneseEra > known[known.length - 1].eraValue) {
212             throw new DateTimeException("japaneseEra is invalid");
213         }
214         return known[ordinal(japaneseEra)];
215     }
216 
217     /**
218      * Returns the {@code JapaneseEra} with the name.
219      * <p>
220      * The string must match exactly the name of the era.
221      * (Extraneous whitespace characters are not permitted.)
222      *
223      * @param japaneseEra  the japaneseEra name; non-null
224      * @return the {@code JapaneseEra} singleton, never null
225      * @throws IllegalArgumentException if there is not JapaneseEra with the specified name
226      */
valueOf(String japaneseEra)227     public static JapaneseEra valueOf(String japaneseEra) {
228         Jdk8Methods.requireNonNull(japaneseEra, "japaneseEra");
229         JapaneseEra[] known = KNOWN_ERAS.get();
230         for (JapaneseEra era : known) {
231             if (japaneseEra.equals(era.name)) {
232                 return era;
233             }
234         }
235         throw new IllegalArgumentException("Era not found: " + japaneseEra);
236     }
237 
238     /**
239      * Returns an array of JapaneseEras.
240      * <p>
241      * This method may be used to iterate over the JapaneseEras as follows:
242      * <pre>
243      * for (JapaneseEra c : JapaneseEra.values())
244      *     System.out.println(c);
245      * </pre>
246      *
247      * @return an array of JapaneseEras
248      */
values()249     public static JapaneseEra[] values() {
250         JapaneseEra[] known = KNOWN_ERAS.get();
251         return Arrays.copyOf(known, known.length);
252     }
253 
254     //-----------------------------------------------------------------------
255     /**
256      * Obtains an instance of {@code JapaneseEra} from a date.
257      *
258      * @param date  the date, not null
259      * @return the Era singleton, never null
260      */
from(LocalDate date)261     static JapaneseEra from(LocalDate date) {
262         if (date.isBefore(MEIJI.since)) {
263             throw new DateTimeException("Date too early: " + date);
264         }
265         JapaneseEra[] known = KNOWN_ERAS.get();
266         for (int i = known.length - 1; i >= 0; i--) {
267             JapaneseEra era = known[i];
268             if (date.compareTo(era.since) >= 0) {
269                 return era;
270             }
271         }
272         return null;
273     }
274 
275     /**
276      * Returns the index into the arrays from the Era value.
277      * the eraValue is a valid Era number, -999, -1..2.
278      * @param eraValue the era value to convert to the index
279      * @return the index of the current Era
280      */
ordinal(int eraValue)281     private static int ordinal(int eraValue) {
282         return eraValue + 1;
283     }
284 
285     /**
286      * Returns the start date of the era.
287      * @return the start date
288      */
startDate()289     LocalDate startDate() {
290         return since;
291     }
292 
293     /**
294      * Returns the end date of the era.
295      * @return the end date
296      */
endDate()297     LocalDate endDate() {
298         int ordinal = ordinal(eraValue);
299         JapaneseEra[] eras = values();
300         if (ordinal >= eras.length - 1) {
301             return LocalDate.MAX;
302         }
303         return eras[ordinal + 1].startDate().minusDays(1);
304     }
305 
306     //-----------------------------------------------------------------------
307     /**
308      * Returns the numeric value of this {@code JapaneseEra}.
309      * <p>
310      * The {@link #SHOWA} era that contains 1970-01-01 (ISO calendar system) has the value 1.
311      * Later eras are numbered from 2 ({@link #HEISEI}).
312      * Earlier eras are numbered 0 ({@link #TAISHO}) and -1 ({@link #MEIJI}).
313      *
314      * @return the era value
315      */
316     @Override
getValue()317     public int getValue() {
318         return eraValue;
319     }
320 
321     @Override
range(TemporalField field)322     public ValueRange range(TemporalField field) {
323         if (field == ChronoField.ERA) {
324             return JapaneseChronology.INSTANCE.range(ChronoField.ERA);
325         }
326         return super.range(field);
327     }
328 
329     //-----------------------------------------------------------------------
330     @Override
toString()331     public String toString() {
332         return name;
333     }
334 
335     //-----------------------------------------------------------------------
writeReplace()336     private Object writeReplace() {
337         return new Ser(Ser.JAPANESE_ERA_TYPE, this);
338     }
339 
writeExternal(DataOutput out)340     void writeExternal(DataOutput out) throws IOException {
341         out.writeByte(this.getValue());
342     }
343 
readExternal(DataInput in)344     static JapaneseEra readExternal(DataInput in) throws IOException {
345         byte eraValue = in.readByte();
346         return JapaneseEra.of(eraValue);
347     }
348 
349 }
350