1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * This code is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 only, as 8 * published by the Free Software Foundation. Oracle designates this 9 * particular file as subject to the "Classpath" exception as provided 10 * by Oracle in the LICENSE file that accompanied this code. 11 * 12 * This code is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 * version 2 for more details (a copy is included in the LICENSE file that 16 * accompanied this code). 17 * 18 * You should have received a copy of the GNU General Public License version 19 * 2 along with this work; if not, write to the Free Software Foundation, 20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 21 * 22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 23 * or visit www.oracle.com if you need additional information or have any 24 * questions. 25 */ 26 27 package sun.util.calendar; 28 29 import java.io.IOException; 30 import java.io.InputStream; 31 import java.util.HashMap; 32 import java.util.Map; 33 import java.util.Properties; 34 import java.util.TimeZone; 35 import java.util.concurrent.ConcurrentHashMap; 36 import java.util.concurrent.ConcurrentMap; 37 38 /** 39 * <code>CalendarSystem</code> is an abstract class that defines the 40 * programming interface to deal with calendar date and time. 41 * 42 * <p><code>CalendarSystem</code> instances are singletons. For 43 * example, there exists only one Gregorian calendar instance in the 44 * Java runtime environment. A singleton instance can be obtained 45 * calling one of the static factory methods. 46 * 47 * <h4>CalendarDate</h4> 48 * 49 * <p>For the methods in a <code>CalendarSystem</code> that manipulate 50 * a <code>CalendarDate</code>, <code>CalendarDate</code>s that have 51 * been created by the <code>CalendarSystem</code> must be 52 * specified. Otherwise, the methods throw an exception. This is 53 * because, for example, a Chinese calendar date can't be understood 54 * by the Hebrew calendar system. 55 * 56 * <h4>Calendar names</h4> 57 * 58 * Each calendar system has a unique name to be identified. The Java 59 * runtime in this release supports the following calendar systems. 60 * 61 * <pre> 62 * Name Calendar System 63 * --------------------------------------- 64 * gregorian Gregorian Calendar 65 * julian Julian Calendar 66 * japanese Japanese Imperial Calendar 67 * </pre> 68 * 69 * @see CalendarDate 70 * @author Masayoshi Okutsu 71 * @since 1.5 72 */ 73 74 public abstract class CalendarSystem { 75 76 /////////////////////// Calendar Factory Methods ///////////////////////// 77 78 // Map of calendar names and calendar classes; 79 private static final Map<String, Class<?>> names; 80 81 // Android-changed: Don't use reflection for Class.forName every time. 82 83 static { 84 names = new HashMap<>(); 85 names.put("gregorian", Gregorian.class); 86 names.put("japanese", LocalGregorianCalendar.class); 87 names.put("julian", JulianCalendar.class); 88 // names.put("hebrew", "HebrewCalendar"); 89 // names.put("iso8601", "ISOCalendar"); 90 // names.put("taiwanese", "LocalGregorianCalendar"); 91 // names.put("thaibuddhist", "LocalGregorianCalendar"); 92 } 93 94 // Map of calendar names and CalendarSystem instances 95 private static final ConcurrentMap<String, CalendarSystem> calendars = 96 new ConcurrentHashMap<>(); 97 98 private final static Gregorian GREGORIAN_INSTANCE = new Gregorian(); 99 100 /** 101 * Returns the singleton instance of the <code>Gregorian</code> 102 * calendar system. 103 * 104 * @return the <code>Gregorian</code> instance 105 */ getGregorianCalendar()106 public static Gregorian getGregorianCalendar() { 107 return GREGORIAN_INSTANCE; 108 } 109 110 /** 111 * Returns a <code>CalendarSystem</code> specified by the calendar 112 * name. The calendar name has to be one of the supported calendar 113 * names. 114 * 115 * @param calendarName the calendar name 116 * @return the <code>CalendarSystem</code> specified by 117 * <code>calendarName</code>, or null if there is no 118 * <code>CalendarSystem</code> associated with the given calendar name. 119 */ forName(String calendarName)120 public static CalendarSystem forName(String calendarName) { 121 if ("gregorian".equals(calendarName)) { 122 return GREGORIAN_INSTANCE; 123 } 124 125 //Android-changed: remove lazy initialization, use classes instead of class names. 126 127 CalendarSystem cal = calendars.get(calendarName); 128 if (cal != null) { 129 return cal; 130 } 131 132 Class<?> calendarClass = names.get(calendarName); 133 if (calendarClass == null) { 134 return null; // Unknown calendar name 135 } 136 137 if (calendarClass.isAssignableFrom(LocalGregorianCalendar.class)) { 138 // Create the specific kind of local Gregorian calendar system 139 cal = LocalGregorianCalendar.getLocalGregorianCalendar(calendarName); 140 } else { 141 try { 142 cal = (CalendarSystem) calendarClass.newInstance(); 143 } catch (Exception e) { 144 throw new RuntimeException("internal error", e); 145 } 146 } 147 if (cal == null) { 148 return null; 149 } 150 151 CalendarSystem cs = calendars.putIfAbsent(calendarName, cal); 152 return (cs == null) ? cal : cs; 153 } 154 155 /** 156 * Returns a {@link Properties} loaded from lib/calendars.properties. 157 * 158 * @return a {@link Properties} loaded from lib/calendars.properties 159 * @throws IOException if an error occurred when reading from the input stream 160 * @throws IllegalArgumentException if the input stream contains any malformed 161 * Unicode escape sequences 162 */ getCalendarProperties()163 public static Properties getCalendarProperties() throws IOException { 164 // Android-changed: load from resources. 165 Properties calendarProps = new Properties(); 166 try (InputStream is = ClassLoader.getSystemResourceAsStream("calendars.properties")) { 167 calendarProps.load(is); 168 } 169 return calendarProps; 170 } 171 172 //////////////////////////////// Calendar API ////////////////////////////////// 173 174 /** 175 * Returns the name of this calendar system. 176 */ getName()177 public abstract String getName(); 178 getCalendarDate()179 public abstract CalendarDate getCalendarDate(); 180 181 /** 182 * Calculates calendar fields from the specified number of 183 * milliseconds since the Epoch, January 1, 1970 00:00:00 UTC 184 * (Gregorian). This method doesn't check overflow or underflow 185 * when adjusting the millisecond value (representing UTC) with 186 * the time zone offsets (i.e., the GMT offset and amount of 187 * daylight saving). 188 * 189 * @param millis the offset value in milliseconds from January 1, 190 * 1970 00:00:00 UTC (Gregorian). 191 * @return a <code>CalendarDate</code> instance that contains the 192 * calculated calendar field values. 193 */ getCalendarDate(long millis)194 public abstract CalendarDate getCalendarDate(long millis); 195 getCalendarDate(long millis, CalendarDate date)196 public abstract CalendarDate getCalendarDate(long millis, CalendarDate date); 197 getCalendarDate(long millis, TimeZone zone)198 public abstract CalendarDate getCalendarDate(long millis, TimeZone zone); 199 200 /** 201 * Constructs a <code>CalendarDate</code> that is specific to this 202 * calendar system. All calendar fields have their initial 203 * values. The {@link TimeZone#getDefault() default time zone} is 204 * set to the instance. 205 * 206 * @return a <code>CalendarDate</code> instance that contains the initial 207 * calendar field values. 208 */ newCalendarDate()209 public abstract CalendarDate newCalendarDate(); 210 newCalendarDate(TimeZone zone)211 public abstract CalendarDate newCalendarDate(TimeZone zone); 212 213 /** 214 * Returns the number of milliseconds since the Epoch, January 1, 215 * 1970 00:00:00 UTC (Gregorian), represented by the specified 216 * <code>CalendarDate</code>. 217 * 218 * @param date the <code>CalendarDate</code> from which the time 219 * value is calculated 220 * @return the number of milliseconds since the Epoch. 221 */ getTime(CalendarDate date)222 public abstract long getTime(CalendarDate date); 223 224 /** 225 * Returns the length in days of the specified year by 226 * <code>date</code>. This method does not perform the 227 * normalization with the specified <code>CalendarDate</code>. The 228 * <code>CalendarDate</code> must be normalized to get a correct 229 * value. 230 */ getYearLength(CalendarDate date)231 public abstract int getYearLength(CalendarDate date); 232 233 /** 234 * Returns the number of months of the specified year. This method 235 * does not perform the normalization with the specified 236 * <code>CalendarDate</code>. The <code>CalendarDate</code> must 237 * be normalized to get a correct value. 238 */ getYearLengthInMonths(CalendarDate date)239 public abstract int getYearLengthInMonths(CalendarDate date); 240 241 /** 242 * Returns the length in days of the month specified by the calendar 243 * date. This method does not perform the normalization with the 244 * specified calendar date. The <code>CalendarDate</code> must 245 * be normalized to get a correct value. 246 * 247 * @param date the date from which the month value is obtained 248 * @return the number of days in the month 249 * @exception IllegalArgumentException if the specified calendar date 250 * doesn't have a valid month value in this calendar system. 251 */ getMonthLength(CalendarDate date)252 public abstract int getMonthLength(CalendarDate date); // no setter 253 254 /** 255 * Returns the length in days of a week in this calendar 256 * system. If this calendar system has multiple radix weeks, this 257 * method returns only one of them. 258 */ getWeekLength()259 public abstract int getWeekLength(); 260 261 /** 262 * Returns the <code>Era</code> designated by the era name that 263 * has to be known to this calendar system. If no Era is 264 * applicable to this calendar system, null is returned. 265 * 266 * @param eraName the name of the era 267 * @return the <code>Era</code> designated by 268 * <code>eraName</code>, or <code>null</code> if no Era is 269 * applicable to this calendar system or the specified era name is 270 * not known to this calendar system. 271 */ getEra(String eraName)272 public abstract Era getEra(String eraName); 273 274 /** 275 * Returns valid <code>Era</code>s of this calendar system. The 276 * return value is sorted in the descendant order. (i.e., the first 277 * element of the returned array is the oldest era.) If no era is 278 * applicable to this calendar system, <code>null</code> is returned. 279 * 280 * @return an array of valid <code>Era</code>s, or 281 * <code>null</code> if no era is applicable to this calendar 282 * system. 283 */ getEras()284 public abstract Era[] getEras(); 285 286 /** 287 * @throws IllegalArgumentException if the specified era name is 288 * unknown to this calendar system. 289 * @see Era 290 */ setEra(CalendarDate date, String eraName)291 public abstract void setEra(CalendarDate date, String eraName); 292 293 /** 294 * Returns a <code>CalendarDate</code> of the n-th day of week 295 * which is on, after or before the specified date. For example, the 296 * first Sunday in April 2002 (Gregorian) can be obtained as 297 * below: 298 * 299 * <pre><code> 300 * Gregorian cal = CalendarSystem.getGregorianCalendar(); 301 * CalendarDate date = cal.newCalendarDate(); 302 * date.setDate(2004, cal.APRIL, 1); 303 * CalendarDate firstSun = cal.getNthDayOfWeek(1, cal.SUNDAY, date); 304 * // firstSun represents April 4, 2004. 305 * </code></pre> 306 * 307 * This method returns a new <code>CalendarDate</code> instance 308 * and doesn't modify the original date. 309 * 310 * @param nth specifies the n-th one. A positive number specifies 311 * <em>on or after</em> the <code>date</code>. A non-positive number 312 * specifies <em>on or before</em> the <code>date</code>. 313 * @param dayOfWeek the day of week 314 * @param date the date 315 * @return the date of the nth <code>dayOfWeek</code> after 316 * or before the specified <code>CalendarDate</code> 317 */ getNthDayOfWeek(int nth, int dayOfWeek, CalendarDate date)318 public abstract CalendarDate getNthDayOfWeek(int nth, int dayOfWeek, 319 CalendarDate date); 320 setTimeOfDay(CalendarDate date, int timeOfDay)321 public abstract CalendarDate setTimeOfDay(CalendarDate date, int timeOfDay); 322 323 /** 324 * Checks whether the calendar fields specified by <code>date</code> 325 * represents a valid date and time in this calendar system. If the 326 * given date is valid, <code>date</code> is marked as <em>normalized</em>. 327 * 328 * @param date the <code>CalendarDate</code> to be validated 329 * @return <code>true</code> if all the calendar fields are consistent, 330 * otherwise, <code>false</code> is returned. 331 * @exception NullPointerException if the specified 332 * <code>date</code> is <code>null</code> 333 */ validate(CalendarDate date)334 public abstract boolean validate(CalendarDate date); 335 336 /** 337 * Normalizes calendar fields in the specified 338 * <code>date</code>. Also all {@link CalendarDate#FIELD_UNDEFINED 339 * undefined} fields are set to correct values. The actual 340 * normalization process is calendar system dependent. 341 * 342 * @param date the calendar date to be validated 343 * @return <code>true</code> if all fields have been normalized; 344 * <code>false</code> otherwise. 345 * @exception NullPointerException if the specified 346 * <code>date</code> is <code>null</code> 347 */ normalize(CalendarDate date)348 public abstract boolean normalize(CalendarDate date); 349 } 350