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 java.io.Serializable; 035import java.util.Arrays; 036import java.util.Calendar; 037import java.util.HashMap; 038import java.util.List; 039import java.util.Locale; 040import java.util.Map; 041 042import org.threeten.bp.DateTimeException; 043import org.threeten.bp.LocalDate; 044import org.threeten.bp.Year; 045import org.threeten.bp.temporal.Chrono; 046import org.threeten.bp.temporal.ChronoField; 047import org.threeten.bp.temporal.ChronoLocalDate; 048import org.threeten.bp.temporal.Era; 049import org.threeten.bp.temporal.ISOChrono; 050import org.threeten.bp.temporal.TemporalAccessor; 051import org.threeten.bp.temporal.ValueRange; 052 053import sun.util.calendar.CalendarSystem; 054import sun.util.calendar.LocalGregorianCalendar; 055 056/** 057 * The Japanese Imperial calendar system. 058 * <p> 059 * This chronology defines the rules of the Japanese Imperial calendar system. 060 * This calendar system is primarily used in Japan. 061 * The Japanese Imperial calendar system is the same as the ISO calendar system 062 * apart from the era-based year numbering. 063 * <p> 064 * Only Meiji (1865-04-07 - 1868-09-07) and later eras are supported. 065 * Older eras are handled as an unknown era where the year-of-era is the ISO year. 066 * 067 * <h3>Specification for implementors</h3> 068 * This class is immutable and thread-safe. 069 */ 070public final class JapaneseChrono extends Chrono<JapaneseChrono> implements Serializable { 071 // TODO: definition for unknown era may break requirement that year-of-era >= 1 072 073 static final LocalGregorianCalendar JCAL = 074 (LocalGregorianCalendar) CalendarSystem.forName("japanese"); 075 076 // Locale for creating a JapaneseImpericalCalendar. 077 static final Locale LOCALE = Locale.forLanguageTag("ja-JP-u-ca-japanese"); 078 079 /** 080 * Singleton instance for Japanese chronology. 081 */ 082 public static final JapaneseChrono INSTANCE = new JapaneseChrono(); 083 084 /** 085 * The singleton instance for the before Meiji era ( - 1868-09-07) 086 * which has the value -999. 087 */ 088 public static final Era<JapaneseChrono> ERA_SEIREKI = JapaneseEra.SEIREKI; 089 /** 090 * The singleton instance for the Meiji era (1868-09-08 - 1912-07-29) 091 * which has the value -1. 092 */ 093 public static final Era<JapaneseChrono> ERA_MEIJI = JapaneseEra.MEIJI; 094 /** 095 * The singleton instance for the Taisho era (1912-07-30 - 1926-12-24) 096 * which has the value 0. 097 */ 098 public static final Era<JapaneseChrono> ERA_TAISHO = JapaneseEra.TAISHO; 099 /** 100 * The singleton instance for the Showa era (1926-12-25 - 1989-01-07) 101 * which has the value 1. 102 */ 103 public static final Era<JapaneseChrono> ERA_SHOWA = JapaneseEra.SHOWA; 104 /** 105 * The singleton instance for the Heisei era (1989-01-08 - current) 106 * which has the value 2. 107 */ 108 public static final Era<JapaneseChrono> ERA_HEISEI = JapaneseEra.HEISEI; 109 /** 110 * Serialization version. 111 */ 112 private static final long serialVersionUID = 459996390165777884L; 113 114 /** 115 * Narrow names for eras. 116 */ 117 private static final Map<String, String[]> ERA_NARROW_NAMES = new HashMap<>(); 118 /** 119 * Short names for eras. 120 */ 121 private static final Map<String, String[]> ERA_SHORT_NAMES = new HashMap<>(); 122 /** 123 * Full names for eras. 124 */ 125 private static final Map<String, String[]> ERA_FULL_NAMES = new HashMap<>(); 126 /** 127 * Fallback language for the era names. 128 */ 129 private static final String FALLBACK_LANGUAGE = "en"; 130 /** 131 * Language that has the era names. 132 */ 133 private static final String TARGET_LANGUAGE = "ja"; 134 135 /** 136 * Name data. 137 */ 138 // TODO: replace all the hard-coded Maps with locale resources 139 static { 140 ERA_NARROW_NAMES.put(FALLBACK_LANGUAGE, new String[]{"Unknown", "K", "M", "T", "S", "H"}); 141 ERA_NARROW_NAMES.put(TARGET_LANGUAGE, new String[]{"Unknown", "K", "M", "T", "S", "H"}); 142 ERA_SHORT_NAMES.put(FALLBACK_LANGUAGE, new String[]{"Unknown", "K", "M", "T", "S", "H"}); 143 ERA_SHORT_NAMES.put(TARGET_LANGUAGE, new String[]{"Unknown", "\u6176", "\u660e", "\u5927", "\u662d", "\u5e73"}); 144 ERA_FULL_NAMES.put(FALLBACK_LANGUAGE, new String[]{"Unknown", "Keio", "Meiji", "Taisho", "Showa", "Heisei"}); 145 ERA_FULL_NAMES.put(TARGET_LANGUAGE, 146 new String[]{"Unknown", "\u6176\u5fdc", "\u660e\u6cbb", "\u5927\u6b63", "\u662d\u548c", "\u5e73\u6210"}); 147 } 148 149 //----------------------------------------------------------------------- 150 /** 151 * Restricted constructor. 152 */ 153 private JapaneseChrono() { 154 } 155 156 /** 157 * Resolve singleton. 158 * 159 * @return the singleton instance, not null 160 */ 161 private Object readResolve() { 162 return INSTANCE; 163 } 164 165 //----------------------------------------------------------------------- 166 /** 167 * Gets the ID of the chronology - 'Japanese'. 168 * <p> 169 * The ID uniquely identifies the {@code Chrono}. 170 * It can be used to lookup the {@code Chrono} using {@link #of(String)}. 171 * 172 * @return the chronology ID - 'Japanese' 173 * @see #getCalendarType() 174 */ 175 @Override 176 public String getId() { 177 return "Japanese"; 178 } 179 180 /** 181 * Gets the calendar type of the underlying calendar system - 'japanese'. 182 * <p> 183 * The calendar type is an identifier defined by the 184 * <em>Unicode Locale Data Markup Language (LDML)</em> specification. 185 * It can be used to lookup the {@code Chrono} using {@link #of(String)}. 186 * It can also be used as part of a locale, accessible via 187 * {@link Locale#getUnicodeLocaleType(String)} with the key 'ca'. 188 * 189 * @return the calendar system type - 'japanese' 190 * @see #getId() 191 */ 192 @Override 193 public String getCalendarType() { 194 return "japanese"; 195 } 196 197 //----------------------------------------------------------------------- 198 @Override 199 public ChronoLocalDate<JapaneseChrono> date(Era<JapaneseChrono> era, int yearOfEra, int month, int dayOfMonth) { 200 if (era instanceof JapaneseEra == false) { 201 throw new DateTimeException("Era must be JapaneseEra"); 202 } 203 return JapaneseDate.of((JapaneseEra) era, yearOfEra, month, dayOfMonth); 204 } 205 206 @Override 207 public ChronoLocalDate<JapaneseChrono> date(int prolepticYear, int month, int dayOfMonth) { 208 return new JapaneseDate(LocalDate.of(prolepticYear, month, dayOfMonth)); 209 } 210 211 @Override 212 public ChronoLocalDate<JapaneseChrono> dateYearDay(int prolepticYear, int dayOfYear) { 213 LocalDate date = LocalDate.ofYearDay(prolepticYear, dayOfYear); 214 return date(prolepticYear, date.getMonthValue(), date.getDayOfMonth()); 215 } 216 217 @Override 218 public ChronoLocalDate<JapaneseChrono> date(TemporalAccessor temporal) { 219 if (temporal instanceof JapaneseDate) { 220 return (JapaneseDate) temporal; 221 } 222 return new JapaneseDate(LocalDate.from(temporal)); 223 } 224 225 //----------------------------------------------------------------------- 226 /** 227 * Checks if the specified year is a leap year. 228 * <p> 229 * Japanese calendar leap years occur exactly in line with ISO leap years. 230 * This method does not validate the year passed in, and only has a 231 * well-defined result for years in the supported range. 232 * 233 * @param prolepticYear the proleptic-year to check, not validated for range 234 * @return true if the year is a leap year 235 */ 236 @Override 237 public boolean isLeapYear(long prolepticYear) { 238 return ISOChrono.INSTANCE.isLeapYear(prolepticYear); 239 } 240 241 @Override 242 public int prolepticYear(Era<JapaneseChrono> era, int yearOfEra) { 243 if (era instanceof JapaneseEra == false) { 244 throw new DateTimeException("Era must be JapaneseEra"); 245 } 246 JapaneseEra jera = (JapaneseEra) era; 247 int gregorianYear = jera.getPrivateEra().getSinceDate().getYear() + yearOfEra - 1; 248 if (yearOfEra == 1) { 249 return gregorianYear; 250 } 251 LocalGregorianCalendar.Date jdate = JCAL.newCalendarDate(null); 252 jdate.setEra(jera.getPrivateEra()).setDate(yearOfEra, 1, 1); 253 JCAL.normalize(jdate); 254 if (jdate.getNormalizedYear() == gregorianYear) { 255 return gregorianYear; 256 } 257 throw new DateTimeException("invalid yearOfEra value"); 258 } 259 260 /** 261 * Returns the calendar system era object from the given numeric value. 262 * 263 * See the description of each Era for the numeric values of: 264 * {@link #ERA_HEISEI}, {@link #ERA_SHOWA},{@link #ERA_TAISHO}, 265 * {@link #ERA_MEIJI}), only Meiji and later eras are supported. 266 * Prior to Meiji {@link #ERA_SEIREKI} is used. 267 * 268 * @param eraValue the era value 269 * @return the Japanese {@code Era} for the given numeric era value 270 * @throws DateTimeException if {@code eraValue} is invalid 271 */ 272 @Override 273 public Era<JapaneseChrono> eraOf(int eraValue) { 274 return JapaneseEra.of(eraValue); 275 } 276 277 @Override 278 public List<Era<JapaneseChrono>> eras() { 279 return Arrays.<Era<JapaneseChrono>>asList(JapaneseEra.values()); 280 } 281 282 //----------------------------------------------------------------------- 283 @Override 284 public ValueRange range(ChronoField field) { 285 switch (field) { 286 case DAY_OF_MONTH: 287 case DAY_OF_WEEK: 288 case MICRO_OF_DAY: 289 case MICRO_OF_SECOND: 290 case HOUR_OF_DAY: 291 case HOUR_OF_AMPM: 292 case MINUTE_OF_DAY: 293 case MINUTE_OF_HOUR: 294 case SECOND_OF_DAY: 295 case SECOND_OF_MINUTE: 296 case MILLI_OF_DAY: 297 case MILLI_OF_SECOND: 298 case NANO_OF_DAY: 299 case NANO_OF_SECOND: 300 case CLOCK_HOUR_OF_DAY: 301 case CLOCK_HOUR_OF_AMPM: 302 case EPOCH_DAY: 303 case EPOCH_MONTH: 304 return field.range(); 305 } 306 Calendar jcal = Calendar.getInstance(LOCALE); 307 int fieldIndex; 308 switch (field) { 309 case ERA: 310 return ValueRange.of(jcal.getMinimum(Calendar.ERA) - JapaneseEra.ERA_OFFSET, 311 jcal.getMaximum(Calendar.ERA) - JapaneseEra.ERA_OFFSET); 312 case YEAR: 313 case YEAR_OF_ERA: 314 return ValueRange.of(Year.MIN_VALUE, jcal.getGreatestMinimum(Calendar.YEAR), 315 jcal.getLeastMaximum(Calendar.YEAR), Year.MAX_VALUE); 316 case MONTH_OF_YEAR: 317 return ValueRange.of(jcal.getMinimum(Calendar.MONTH) + 1, jcal.getGreatestMinimum(Calendar.MONTH) + 1, 318 jcal.getLeastMaximum(Calendar.MONTH) + 1, jcal.getMaximum(Calendar.MONTH) + 1); 319 case DAY_OF_YEAR: 320 fieldIndex = Calendar.DAY_OF_YEAR; 321 break; 322 default: 323 // TODO: review the remaining fields 324 throw new UnsupportedOperationException("Unimplementable field: " + field); 325 } 326 return ValueRange.of(jcal.getMinimum(fieldIndex), jcal.getGreatestMinimum(fieldIndex), 327 jcal.getLeastMaximum(fieldIndex), jcal.getMaximum(fieldIndex)); 328 } 329 330}