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