1 /*
2 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 /*
27 * Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos
28 *
29 * All rights reserved.
30 *
31 * Redistribution and use in source and binary forms, with or without
32 * modification, are permitted provided that the following conditions are met:
33 *
34 * * Redistributions of source code must retain the above copyright notice,
35 * this list of conditions and the following disclaimer.
36 *
37 * * Redistributions in binary form must reproduce the above copyright notice,
38 * this list of conditions and the following disclaimer in the documentation
39 * and/or other materials provided with the distribution.
40 *
41 * * Neither the name of JSR-310 nor the names of its contributors
42 * may be used to endorse or promote products derived from this software
43 * without specific prior written permission.
44 *
45 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
46 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
47 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
48 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
49 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
50 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
51 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
52 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
53 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
54 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
55 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
56 */
57 package java.time.chrono;
58
59 import java.io.InvalidObjectException;
60 import static java.time.temporal.ChronoField.PROLEPTIC_MONTH;
61 import static java.time.temporal.ChronoField.YEAR;
62
63 import java.io.ObjectInputStream;
64 import java.io.Serializable;
65 import java.time.Clock;
66 import java.time.DateTimeException;
67 import java.time.Instant;
68 import java.time.LocalDate;
69 import java.time.ZoneId;
70 import java.time.format.ResolverStyle;
71 import java.time.temporal.ChronoField;
72 import java.time.temporal.TemporalAccessor;
73 import java.time.temporal.TemporalField;
74 import java.time.temporal.ValueRange;
75 import java.util.Arrays;
76 import java.util.HashMap;
77 import java.util.List;
78 import java.util.Locale;
79 import java.util.Map;
80
81 /**
82 * The Thai Buddhist calendar system.
83 * <p>
84 * This chronology defines the rules of the Thai Buddhist calendar system.
85 * This calendar system is primarily used in Thailand.
86 * Dates are aligned such that {@code 2484-01-01 (Buddhist)} is {@code 1941-01-01 (ISO)}.
87 * <p>
88 * The fields are defined as follows:
89 * <ul>
90 * <li>era - There are two eras, the current 'Buddhist' (ERA_BE) and the previous era (ERA_BEFORE_BE).
91 * <li>year-of-era - The year-of-era for the current era increases uniformly from the epoch at year one.
92 * For the previous era the year increases from one as time goes backwards.
93 * The value for the current era is equal to the ISO proleptic-year plus 543.
94 * <li>proleptic-year - The proleptic year is the same as the year-of-era for the
95 * current era. For the previous era, years have zero, then negative values.
96 * The value is equal to the ISO proleptic-year plus 543.
97 * <li>month-of-year - The ThaiBuddhist month-of-year exactly matches ISO.
98 * <li>day-of-month - The ThaiBuddhist day-of-month exactly matches ISO.
99 * <li>day-of-year - The ThaiBuddhist day-of-year exactly matches ISO.
100 * <li>leap-year - The ThaiBuddhist leap-year pattern exactly matches ISO, such that the two calendars
101 * are never out of step.
102 * </ul>
103 *
104 * @implSpec
105 * This class is immutable and thread-safe.
106 *
107 * @since 1.8
108 */
109 public final class ThaiBuddhistChronology extends AbstractChronology implements Serializable {
110
111 /**
112 * Singleton instance of the Buddhist chronology.
113 */
114 public static final ThaiBuddhistChronology INSTANCE = new ThaiBuddhistChronology();
115
116 /**
117 * Serialization version.
118 */
119 private static final long serialVersionUID = 2775954514031616474L;
120 /**
121 * Containing the offset to add to the ISO year.
122 */
123 static final int YEARS_DIFFERENCE = 543;
124 /**
125 * Narrow names for eras.
126 */
127 private static final HashMap<String, String[]> ERA_NARROW_NAMES = new HashMap<>();
128 /**
129 * Short names for eras.
130 */
131 private static final HashMap<String, String[]> ERA_SHORT_NAMES = new HashMap<>();
132 /**
133 * Full names for eras.
134 */
135 private static final HashMap<String, String[]> ERA_FULL_NAMES = new HashMap<>();
136 /**
137 * Fallback language for the era names.
138 */
139 private static final String FALLBACK_LANGUAGE = "en";
140 /**
141 * Language that has the era names.
142 */
143 private static final String TARGET_LANGUAGE = "th";
144 /**
145 * Name data.
146 */
147 static {
148 ERA_NARROW_NAMES.put(FALLBACK_LANGUAGE, new String[]{"BB", "BE"});
149 ERA_NARROW_NAMES.put(TARGET_LANGUAGE, new String[]{"BB", "BE"});
150 ERA_SHORT_NAMES.put(FALLBACK_LANGUAGE, new String[]{"B.B.", "B.E."});
151 ERA_SHORT_NAMES.put(TARGET_LANGUAGE,
152 new String[]{"\u0e1e.\u0e28.",
153 "\u0e1b\u0e35\u0e01\u0e48\u0e2d\u0e19\u0e04\u0e23\u0e34\u0e2a\u0e15\u0e4c\u0e01\u0e32\u0e25\u0e17\u0e35\u0e48"});
154 ERA_FULL_NAMES.put(FALLBACK_LANGUAGE, new String[]{"Before Buddhist", "Budhhist Era"});
155 ERA_FULL_NAMES.put(TARGET_LANGUAGE,
156 new String[]{"\u0e1e\u0e38\u0e17\u0e18\u0e28\u0e31\u0e01\u0e23\u0e32\u0e0a",
157 "\u0e1b\u0e35\u0e01\u0e48\u0e2d\u0e19\u0e04\u0e23\u0e34\u0e2a\u0e15\u0e4c\u0e01\u0e32\u0e25\u0e17\u0e35\u0e48"});
158 }
159
160 /**
161 * Restricted constructor.
162 */
163 private ThaiBuddhistChronology() {
164 }
165
166 //-----------------------------------------------------------------------
167 /**
168 * Gets the ID of the chronology - 'ThaiBuddhist'.
169 * <p>
170 * The ID uniquely identifies the {@code Chronology}.
171 * It can be used to lookup the {@code Chronology} using {@link Chronology#of(String)}.
172 *
173 * @return the chronology ID - 'ThaiBuddhist'
174 * @see #getCalendarType()
175 */
176 @Override
177 public String getId() {
178 return "ThaiBuddhist";
179 }
180
181 /**
182 * Gets the calendar type of the underlying calendar system - 'buddhist'.
183 * <p>
184 * The calendar type is an identifier defined by the
185 * <em>Unicode Locale Data Markup Language (LDML)</em> specification.
186 * It can be used to lookup the {@code Chronology} using {@link Chronology#of(String)}.
187 * It can also be used as part of a locale, accessible via
188 * {@link Locale#getUnicodeLocaleType(String)} with the key 'ca'.
189 *
190 * @return the calendar system type - 'buddhist'
191 * @see #getId()
192 */
193 @Override
194 public String getCalendarType() {
195 return "buddhist";
196 }
197
198 //-----------------------------------------------------------------------
199 /**
200 * Obtains a local date in Thai Buddhist calendar system from the
201 * era, year-of-era, month-of-year and day-of-month fields.
202 *
203 * @param era the Thai Buddhist era, not null
204 * @param yearOfEra the year-of-era
205 * @param month the month-of-year
206 * @param dayOfMonth the day-of-month
207 * @return the Thai Buddhist local date, not null
208 * @throws DateTimeException if unable to create the date
209 * @throws ClassCastException if the {@code era} is not a {@code ThaiBuddhistEra}
210 */
211 @Override
212 public ThaiBuddhistDate date(Era era, int yearOfEra, int month, int dayOfMonth) {
213 return date(prolepticYear(era, yearOfEra), month, dayOfMonth);
214 }
215
216 /**
217 * Obtains a local date in Thai Buddhist calendar system from the
218 * proleptic-year, month-of-year and day-of-month fields.
219 *
220 * @param prolepticYear the proleptic-year
221 * @param month the month-of-year
222 * @param dayOfMonth the day-of-month
223 * @return the Thai Buddhist local date, not null
224 * @throws DateTimeException if unable to create the date
225 */
226 @Override
227 public ThaiBuddhistDate date(int prolepticYear, int month, int dayOfMonth) {
228 return new ThaiBuddhistDate(LocalDate.of(prolepticYear - YEARS_DIFFERENCE, month, dayOfMonth));
229 }
230
231 /**
232 * Obtains a local date in Thai Buddhist calendar system from the
233 * era, year-of-era and day-of-year fields.
234 *
235 * @param era the Thai Buddhist era, not null
236 * @param yearOfEra the year-of-era
237 * @param dayOfYear the day-of-year
238 * @return the Thai Buddhist local date, not null
239 * @throws DateTimeException if unable to create the date
240 * @throws ClassCastException if the {@code era} is not a {@code ThaiBuddhistEra}
241 */
242 @Override
243 public ThaiBuddhistDate dateYearDay(Era era, int yearOfEra, int dayOfYear) {
244 return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear);
245 }
246
247 /**
248 * Obtains a local date in Thai Buddhist calendar system from the
249 * proleptic-year and day-of-year fields.
250 *
251 * @param prolepticYear the proleptic-year
252 * @param dayOfYear the day-of-year
253 * @return the Thai Buddhist local date, not null
254 * @throws DateTimeException if unable to create the date
255 */
256 @Override
257 public ThaiBuddhistDate dateYearDay(int prolepticYear, int dayOfYear) {
258 return new ThaiBuddhistDate(LocalDate.ofYearDay(prolepticYear - YEARS_DIFFERENCE, dayOfYear));
259 }
260
261 /**
262 * Obtains a local date in the Thai Buddhist calendar system from the epoch-day.
263 *
264 * @param epochDay the epoch day
265 * @return the Thai Buddhist local date, not null
266 * @throws DateTimeException if unable to create the date
267 */
268 @Override // override with covariant return type
269 public ThaiBuddhistDate dateEpochDay(long epochDay) {
270 return new ThaiBuddhistDate(LocalDate.ofEpochDay(epochDay));
271 }
272
273 @Override
274 public ThaiBuddhistDate dateNow() {
275 return dateNow(Clock.systemDefaultZone());
276 }
277
278 @Override
279 public ThaiBuddhistDate dateNow(ZoneId zone) {
280 return dateNow(Clock.system(zone));
281 }
282
283 @Override
284 public ThaiBuddhistDate dateNow(Clock clock) {
285 return date(LocalDate.now(clock));
286 }
287
288 @Override
289 public ThaiBuddhistDate date(TemporalAccessor temporal) {
290 if (temporal instanceof ThaiBuddhistDate) {
291 return (ThaiBuddhistDate) temporal;
292 }
293 return new ThaiBuddhistDate(LocalDate.from(temporal));
294 }
295
296 @Override
297 @SuppressWarnings("unchecked")
298 public ChronoLocalDateTime<ThaiBuddhistDate> localDateTime(TemporalAccessor temporal) {
299 return (ChronoLocalDateTime<ThaiBuddhistDate>)super.localDateTime(temporal);
300 }
301
302 @Override
303 @SuppressWarnings("unchecked")
304 public ChronoZonedDateTime<ThaiBuddhistDate> zonedDateTime(TemporalAccessor temporal) {
305 return (ChronoZonedDateTime<ThaiBuddhistDate>)super.zonedDateTime(temporal);
306 }
307
308 @Override
309 @SuppressWarnings("unchecked")
310 public ChronoZonedDateTime<ThaiBuddhistDate> zonedDateTime(Instant instant, ZoneId zone) {
311 return (ChronoZonedDateTime<ThaiBuddhistDate>)super.zonedDateTime(instant, zone);
312 }
313
314 //-----------------------------------------------------------------------
315 /**
316 * Checks if the specified year is a leap year.
317 * <p>
318 * Thai Buddhist leap years occur exactly in line with ISO leap years.
319 * This method does not validate the year passed in, and only has a
320 * well-defined result for years in the supported range.
321 *
322 * @param prolepticYear the proleptic-year to check, not validated for range
323 * @return true if the year is a leap year
324 */
325 @Override
326 public boolean isLeapYear(long prolepticYear) {
327 return IsoChronology.INSTANCE.isLeapYear(prolepticYear - YEARS_DIFFERENCE);
328 }
329
330 @Override
331 public int prolepticYear(Era era, int yearOfEra) {
332 if (era instanceof ThaiBuddhistEra == false) {
333 throw new ClassCastException("Era must be BuddhistEra");
334 }
335 return (era == ThaiBuddhistEra.BE ? yearOfEra : 1 - yearOfEra);
336 }
337
338 @Override
339 public ThaiBuddhistEra eraOf(int eraValue) {
340 return ThaiBuddhistEra.of(eraValue);
341 }
342
343 @Override
344 public List<Era> eras() {
345 return List.of(ThaiBuddhistEra.values());
346 }
347
348 //-----------------------------------------------------------------------
349 @Override
350 public ValueRange range(ChronoField field) {
351 switch (field) {
352 case PROLEPTIC_MONTH: {
353 ValueRange range = PROLEPTIC_MONTH.range();
354 return ValueRange.of(range.getMinimum() + YEARS_DIFFERENCE * 12L, range.getMaximum() + YEARS_DIFFERENCE * 12L);
355 }
356 case YEAR_OF_ERA: {
357 ValueRange range = YEAR.range();
358 return ValueRange.of(1, -(range.getMinimum() + YEARS_DIFFERENCE) + 1, range.getMaximum() + YEARS_DIFFERENCE);
359 }
360 case YEAR: {
361 ValueRange range = YEAR.range();
362 return ValueRange.of(range.getMinimum() + YEARS_DIFFERENCE, range.getMaximum() + YEARS_DIFFERENCE);
363 }
364 }
365 return field.range();
366 }
367
368 //-----------------------------------------------------------------------
369 @Override // override for return type
370 public ThaiBuddhistDate resolveDate(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
371 return (ThaiBuddhistDate) super.resolveDate(fieldValues, resolverStyle);
372 }
373
374 //-----------------------------------------------------------------------
375 /**
376 * Writes the Chronology using a
377 * <a href="../../../serialized-form.html#java.time.chrono.Ser">dedicated serialized form</a>.
378 * @serialData
379 * <pre>
380 * out.writeByte(1); // identifies a Chronology
381 * out.writeUTF(getId());
382 * </pre>
383 *
384 * @return the instance of {@code Ser}, not null
385 */
386 @Override
387 Object writeReplace() {
388 return super.writeReplace();
389 }
390
391 /**
392 * Defend against malicious streams.
393 *
394 * @param s the stream to read
395 * @throws InvalidObjectException always
396 */
397 private void readObject(ObjectInputStream s) throws InvalidObjectException {
398 throw new InvalidObjectException("Deserialization via serialization delegate");
399 }
400 }
401