001/* 002 * Copyright (c) 2007-2013, Stephen Colebourne & Michael Nascimento Santos 003 * 004 * All rights reserved. 005 * 006 * Redistribution and use in source and binary forms, with or without 007 * modification, are permitted provided that the following conditions are met: 008 * 009 * * Redistributions of source code must retain the above copyright notice, 010 * this list of conditions and the following disclaimer. 011 * 012 * * Redistributions in binary form must reproduce the above copyright notice, 013 * this list of conditions and the following disclaimer in the documentation 014 * and/or other materials provided with the distribution. 015 * 016 * * Neither the name of JSR-310 nor the names of its contributors 017 * may be used to endorse or promote products derived from this software 018 * without specific prior written permission. 019 * 020 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 021 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 022 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 023 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 024 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 025 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 026 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 027 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 028 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 029 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 030 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 031 */ 032package org.threeten.bp.calendar; 033 034import static org.threeten.bp.temporal.ChronoField.YEAR; 035 036import java.io.Serializable; 037import java.util.Arrays; 038import java.util.HashMap; 039import java.util.List; 040import java.util.Locale; 041 042import org.threeten.bp.DateTimeException; 043import org.threeten.bp.LocalDate; 044import org.threeten.bp.temporal.Chrono; 045import org.threeten.bp.temporal.ChronoField; 046import org.threeten.bp.temporal.ChronoLocalDate; 047import org.threeten.bp.temporal.Era; 048import org.threeten.bp.temporal.ISOChrono; 049import org.threeten.bp.temporal.TemporalAccessor; 050import org.threeten.bp.temporal.ValueRange; 051 052/** 053 * The Thai Buddhist calendar system. 054 * <p> 055 * This chronology defines the rules of the Thai Buddhist calendar system. 056 * This calendar system is primarily used in Thailand. 057 * Dates are aligned such that {@code 2484-01-01 (Buddhist)} is {@code 1941-01-01 (ISO)}. 058 * <p> 059 * The fields are defined as follows: 060 * <p><ul> 061 * <li>era - There are two eras, the current 'Buddhist' (ERA_BE) and the previous era (ERA_BEFORE_BE). 062 * <li>year-of-era - The year-of-era for the current era increases uniformly from the epoch at year one. 063 * For the previous era the year increases from one as time goes backwards. 064 * The value for the current era is equal to the ISO proleptic-year plus 543. 065 * <li>proleptic-year - The proleptic year is the same as the year-of-era for the 066 * current era. For the previous era, years have zero, then negative values. 067 * The value is equal to the ISO proleptic-year plus 543. 068 * <li>month-of-year - The ThaiBuddhist month-of-year exactly matches ISO. 069 * <li>day-of-month - The ThaiBuddhist day-of-month exactly matches ISO. 070 * <li>day-of-year - The ThaiBuddhist day-of-year exactly matches ISO. 071 * <li>leap-year - The ThaiBuddhist leap-year pattern exactly matches ISO, such that the two calendars 072 * are never out of step. 073 * </ul><p> 074 * 075 * <h3>Specification for implementors</h3> 076 * This class is immutable and thread-safe. 077 */ 078public final class ThaiBuddhistChrono extends Chrono<ThaiBuddhistChrono> implements Serializable { 079 080 /** 081 * Singleton instance of the Buddhist chronology. 082 */ 083 public static final ThaiBuddhistChrono INSTANCE = new ThaiBuddhistChrono(); 084 /** 085 * The singleton instance for the era before the current one - Before Buddhist - 086 * which has the value 0. 087 */ 088 public static final Era<ThaiBuddhistChrono> ERA_BEFORE_BE = ThaiBuddhistEra.BEFORE_BE; 089 /** 090 * The singleton instance for the current era - Buddhist - which has the value 1. 091 */ 092 public static final Era<ThaiBuddhistChrono> ERA_BE = ThaiBuddhistEra.BE; 093 094 /** 095 * Serialization version. 096 */ 097 private static final long serialVersionUID = 2775954514031616474L; 098 /** 099 * Containing the offset to add to the ISO year. 100 */ 101 static final int YEARS_DIFFERENCE = 543; 102 /** 103 * Narrow names for eras. 104 */ 105 private static final HashMap<String, String[]> ERA_NARROW_NAMES = new HashMap<>(); 106 /** 107 * Short names for eras. 108 */ 109 private static final HashMap<String, String[]> ERA_SHORT_NAMES = new HashMap<>(); 110 /** 111 * Full names for eras. 112 */ 113 private static final HashMap<String, String[]> ERA_FULL_NAMES = new HashMap<>(); 114 /** 115 * Fallback language for the era names. 116 */ 117 private static final String FALLBACK_LANGUAGE = "en"; 118 /** 119 * Language that has the era names. 120 */ 121 private static final String TARGET_LANGUAGE = "th"; 122 /** 123 * Name data. 124 */ 125 static { 126 ERA_NARROW_NAMES.put(FALLBACK_LANGUAGE, new String[]{"BB", "BE"}); 127 ERA_NARROW_NAMES.put(TARGET_LANGUAGE, new String[]{"BB", "BE"}); 128 ERA_SHORT_NAMES.put(FALLBACK_LANGUAGE, new String[]{"B.B.", "B.E."}); 129 ERA_SHORT_NAMES.put(TARGET_LANGUAGE, 130 new String[]{"\u0e1e.\u0e28.", 131 "\u0e1b\u0e35\u0e01\u0e48\u0e2d\u0e19\u0e04\u0e23\u0e34\u0e2a\u0e15\u0e4c\u0e01\u0e32\u0e25\u0e17\u0e35\u0e48"}); 132 ERA_FULL_NAMES.put(FALLBACK_LANGUAGE, new String[]{"Before Buddhist", "Budhhist Era"}); 133 ERA_FULL_NAMES.put(TARGET_LANGUAGE, 134 new String[]{"\u0e1e\u0e38\u0e17\u0e18\u0e28\u0e31\u0e01\u0e23\u0e32\u0e0a", 135 "\u0e1b\u0e35\u0e01\u0e48\u0e2d\u0e19\u0e04\u0e23\u0e34\u0e2a\u0e15\u0e4c\u0e01\u0e32\u0e25\u0e17\u0e35\u0e48"}); 136 } 137 138 /** 139 * Restricted constructor. 140 */ 141 private ThaiBuddhistChrono() { 142 } 143 144 /** 145 * Resolve singleton. 146 * 147 * @return the singleton instance, not null 148 */ 149 private Object readResolve() { 150 return INSTANCE; 151 } 152 153 //----------------------------------------------------------------------- 154 /** 155 * Gets the ID of the chronology - 'ThaiBuddhist'. 156 * <p> 157 * The ID uniquely identifies the {@code Chrono}. 158 * It can be used to lookup the {@code Chrono} using {@link #of(String)}. 159 * 160 * @return the chronology ID - 'ThaiBuddhist' 161 * @see #getCalendarType() 162 */ 163 @Override 164 public String getId() { 165 return "ThaiBuddhist"; 166 } 167 168 /** 169 * Gets the calendar type of the underlying calendar system - 'buddhist'. 170 * <p> 171 * The calendar type is an identifier defined by the 172 * <em>Unicode Locale Data Markup Language (LDML)</em> specification. 173 * It can be used to lookup the {@code Chrono} using {@link #of(String)}. 174 * It can also be used as part of a locale, accessible via 175 * {@link Locale#getUnicodeLocaleType(String)} with the key 'ca'. 176 * 177 * @return the calendar system type - 'buddhist' 178 * @see #getId() 179 */ 180 @Override 181 public String getCalendarType() { 182 return "buddhist"; 183 } 184 185 //----------------------------------------------------------------------- 186 @Override 187 public ChronoLocalDate<ThaiBuddhistChrono> date(int prolepticYear, int month, int dayOfMonth) { 188 return new ThaiBuddhistDate(LocalDate.of(prolepticYear - YEARS_DIFFERENCE, month, dayOfMonth)); 189 } 190 191 @Override 192 public ChronoLocalDate<ThaiBuddhistChrono> dateYearDay(int prolepticYear, int dayOfYear) { 193 return new ThaiBuddhistDate(LocalDate.ofYearDay(prolepticYear - YEARS_DIFFERENCE, dayOfYear)); 194 } 195 196 @Override 197 public ChronoLocalDate<ThaiBuddhistChrono> date(TemporalAccessor temporal) { 198 if (temporal instanceof ThaiBuddhistDate) { 199 return (ThaiBuddhistDate) temporal; 200 } 201 return new ThaiBuddhistDate(LocalDate.from(temporal)); 202 } 203 204 //----------------------------------------------------------------------- 205 /** 206 * Checks if the specified year is a leap year. 207 * <p> 208 * Thai Buddhist leap years occur exactly in line with ISO leap years. 209 * This method does not validate the year passed in, and only has a 210 * well-defined result for years in the supported range. 211 * 212 * @param prolepticYear the proleptic-year to check, not validated for range 213 * @return true if the year is a leap year 214 */ 215 @Override 216 public boolean isLeapYear(long prolepticYear) { 217 return ISOChrono.INSTANCE.isLeapYear(prolepticYear - YEARS_DIFFERENCE); 218 } 219 220 @Override 221 public int prolepticYear(Era<ThaiBuddhistChrono> era, int yearOfEra) { 222 if (era instanceof ThaiBuddhistEra == false) { 223 throw new DateTimeException("Era must be BuddhistEra"); 224 } 225 return (era == ThaiBuddhistEra.BE ? yearOfEra : 1 - yearOfEra); 226 } 227 228 @Override 229 public Era<ThaiBuddhistChrono> eraOf(int eraValue) { 230 return ThaiBuddhistEra.of(eraValue); 231 } 232 233 @Override 234 public List<Era<ThaiBuddhistChrono>> eras() { 235 return Arrays.<Era<ThaiBuddhistChrono>>asList(ThaiBuddhistEra.values()); 236 } 237 238 //----------------------------------------------------------------------- 239 @Override 240 public ValueRange range(ChronoField field) { 241 switch (field) { 242 case YEAR_OF_ERA: { 243 ValueRange range = YEAR.range(); 244 return ValueRange.of(1, -(range.getMinimum() + YEARS_DIFFERENCE) + 1, range.getMaximum() + YEARS_DIFFERENCE); 245 } 246 case YEAR: { 247 ValueRange range = YEAR.range(); 248 return ValueRange.of(range.getMinimum() + YEARS_DIFFERENCE, range.getMaximum() + YEARS_DIFFERENCE); 249 } 250 } 251 return field.range(); 252 } 253 254}