1 /* 2 * Copyright (c) 2005, 2014, 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 package sun.util.calendar; 27 28 import java.security.AccessController; 29 import java.util.TimeZone; 30 import sun.security.action.GetPropertyAction; 31 32 /** 33 * 34 * @author Masayoshi Okutsu 35 * @since 1.6 36 */ 37 38 public class LocalGregorianCalendar extends BaseCalendar { 39 private static final Era[] JAPANESE_ERAS = { 40 new Era("Meiji", "M", -3218832000000L, true), 41 new Era("Taisho", "T", -1812153600000L, true), 42 new Era("Showa", "S", -1357603200000L, true), 43 new Era("Heisei", "H", 600220800000L, true), 44 }; 45 46 private static boolean isValidEra(Era newEra, Era[] eras) { 47 Era last = eras[eras.length - 1]; 48 if (last.getSinceDate().getYear() >= newEra.getSinceDate().getYear()) { 49 return false; 50 } 51 // The new era name should be unique. Its abbr may not. 52 String newName = newEra.getName(); 53 for (Era era : eras) { 54 if (era.getName().equals(newName)) { 55 return false; 56 } 57 } 58 return true; 59 } 60 61 private String name; 62 private Era[] eras; 63 64 public static class Date extends BaseCalendar.Date { 65 66 protected Date() { 67 super(); 68 } 69 70 protected Date(TimeZone zone) { 71 super(zone); 72 } 73 74 private int gregorianYear = FIELD_UNDEFINED; 75 76 @Override 77 public Date setEra(Era era) { 78 if (getEra() != era) { 79 super.setEra(era); 80 gregorianYear = FIELD_UNDEFINED; 81 } 82 return this; 83 } 84 85 @Override 86 public Date addYear(int localYear) { 87 super.addYear(localYear); 88 gregorianYear += localYear; 89 return this; 90 } 91 92 @Override 93 public Date setYear(int localYear) { 94 if (getYear() != localYear) { 95 super.setYear(localYear); 96 gregorianYear = FIELD_UNDEFINED; 97 } 98 return this; 99 } 100 101 @Override 102 public int getNormalizedYear() { 103 return gregorianYear; 104 } 105 106 @Override 107 public void setNormalizedYear(int normalizedYear) { 108 this.gregorianYear = normalizedYear; 109 } 110 111 void setLocalEra(Era era) { 112 super.setEra(era); 113 } 114 115 void setLocalYear(int year) { 116 super.setYear(year); 117 } 118 119 @Override 120 public String toString() { 121 String time = super.toString(); 122 time = time.substring(time.indexOf('T')); 123 StringBuffer sb = new StringBuffer(); 124 Era era = getEra(); 125 if (era != null) { 126 String abbr = era.getAbbreviation(); 127 if (abbr != null) { 128 sb.append(abbr); 129 } 130 } 131 sb.append(getYear()).append('.'); 132 CalendarUtils.sprintf0d(sb, getMonth(), 2).append('.'); 133 CalendarUtils.sprintf0d(sb, getDayOfMonth(), 2); 134 sb.append(time); 135 return sb.toString(); 136 } 137 } 138 139 static LocalGregorianCalendar getLocalGregorianCalendar(String name) { 140 // Only the Japanese calendar is supported. 141 if (!"japanese".equals(name)) { 142 return null; 143 } 144 145 // Append an era to the predefined eras if it's given by the property. 146 String prop = GetPropertyAction 147 .getProperty("jdk.calendar.japanese.supplemental.era"); 148 if (prop != null) { 149 Era era = parseEraEntry(prop); 150 if (era != null) { 151 if (isValidEra(era, JAPANESE_ERAS)) { 152 int length = JAPANESE_ERAS.length; 153 Era[] eras = new Era[length + 1]; 154 System.arraycopy(JAPANESE_ERAS, 0, eras, 0, length); 155 eras[length] = era; 156 return new LocalGregorianCalendar(name, eras); 157 } 158 } 159 } 160 return new LocalGregorianCalendar(name, JAPANESE_ERAS); 161 } 162 163 private static Era parseEraEntry(String entry) { 164 String[] keyValuePairs = entry.split(","); 165 String eraName = null; 166 boolean localTime = true; 167 long since = 0; 168 String abbr = null; 169 170 for (String item : keyValuePairs) { 171 String[] keyvalue = item.split("="); 172 if (keyvalue.length != 2) { 173 return null; 174 } 175 String key = keyvalue[0].trim(); 176 String value = keyvalue[1].trim(); 177 switch (key) { 178 case "name": 179 eraName = value; 180 break; 181 case "since": 182 if (value.endsWith("u")) { 183 localTime = false; 184 value = value.substring(0, value.length() - 1); 185 } 186 try { 187 since = Long.parseLong(value); 188 } catch (NumberFormatException e) { 189 return null; 190 } 191 break; 192 case "abbr": 193 abbr = value; 194 break; 195 default: 196 return null; 197 } 198 } 199 if (eraName == null || eraName.isEmpty() 200 || abbr == null || abbr.isEmpty()) { 201 return null; 202 } 203 return new Era(eraName, abbr, since, localTime); 204 } 205 206 private LocalGregorianCalendar(String name, Era[] eras) { 207 this.name = name; 208 this.eras = eras; 209 setEras(eras); 210 } 211 212 @Override 213 public String getName() { 214 return name; 215 } 216 217 @Override 218 public Date getCalendarDate() { 219 return getCalendarDate(System.currentTimeMillis(), newCalendarDate()); 220 } 221 222 @Override 223 public Date getCalendarDate(long millis) { 224 return getCalendarDate(millis, newCalendarDate()); 225 } 226 227 @Override 228 public Date getCalendarDate(long millis, TimeZone zone) { 229 return getCalendarDate(millis, newCalendarDate(zone)); 230 } 231 232 @Override 233 public Date getCalendarDate(long millis, CalendarDate date) { 234 Date ldate = (Date) super.getCalendarDate(millis, date); 235 return adjustYear(ldate, millis, ldate.getZoneOffset()); 236 } 237 238 private Date adjustYear(Date ldate, long millis, int zoneOffset) { 239 int i; 240 for (i = eras.length - 1; i >= 0; --i) { 241 Era era = eras[i]; 242 long since = era.getSince(null); 243 if (era.isLocalTime()) { 244 since -= zoneOffset; 245 } 246 if (millis >= since) { 247 ldate.setLocalEra(era); 248 int y = ldate.getNormalizedYear() - era.getSinceDate().getYear() + 1; 249 ldate.setLocalYear(y); 250 break; 251 } 252 } 253 if (i < 0) { 254 ldate.setLocalEra(null); 255 ldate.setLocalYear(ldate.getNormalizedYear()); 256 } 257 ldate.setNormalized(true); 258 return ldate; 259 } 260 261 @Override 262 public Date newCalendarDate() { 263 return new Date(); 264 } 265 266 @Override 267 public Date newCalendarDate(TimeZone zone) { 268 return new Date(zone); 269 } 270 271 @Override 272 public boolean validate(CalendarDate date) { 273 Date ldate = (Date) date; 274 Era era = ldate.getEra(); 275 if (era != null) { 276 if (!validateEra(era)) { 277 return false; 278 } 279 ldate.setNormalizedYear(era.getSinceDate().getYear() + ldate.getYear() - 1); 280 Date tmp = newCalendarDate(date.getZone()); 281 tmp.setEra(era).setDate(date.getYear(), date.getMonth(), date.getDayOfMonth()); 282 normalize(tmp); 283 if (tmp.getEra() != era) { 284 return false; 285 } 286 } else { 287 if (date.getYear() >= eras[0].getSinceDate().getYear()) { 288 return false; 289 } 290 ldate.setNormalizedYear(ldate.getYear()); 291 } 292 return super.validate(ldate); 293 } 294 295 private boolean validateEra(Era era) { 296 for (Era era1 : eras) { 297 if (era == era1) { 298 return true; 299 } 300 } 301 return false; 302 } 303 304 @Override 305 public boolean normalize(CalendarDate date) { 306 if (date.isNormalized()) { 307 return true; 308 } 309 310 normalizeYear(date); 311 Date ldate = (Date) date; 312 313 // Normalize it as a Gregorian date and get its millisecond value 314 super.normalize(ldate); 315 316 boolean hasMillis = false; 317 long millis = 0; 318 int year = ldate.getNormalizedYear(); 319 int i; 320 Era era = null; 321 for (i = eras.length - 1; i >= 0; --i) { 322 era = eras[i]; 323 if (era.isLocalTime()) { 324 CalendarDate sinceDate = era.getSinceDate(); 325 int sinceYear = sinceDate.getYear(); 326 if (year > sinceYear) { 327 break; 328 } 329 if (year == sinceYear) { 330 int month = ldate.getMonth(); 331 int sinceMonth = sinceDate.getMonth(); 332 if (month > sinceMonth) { 333 break; 334 } 335 if (month == sinceMonth) { 336 int day = ldate.getDayOfMonth(); 337 int sinceDay = sinceDate.getDayOfMonth(); 338 if (day > sinceDay) { 339 break; 340 } 341 if (day == sinceDay) { 342 long timeOfDay = ldate.getTimeOfDay(); 343 long sinceTimeOfDay = sinceDate.getTimeOfDay(); 344 if (timeOfDay >= sinceTimeOfDay) { 345 break; 346 } 347 --i; 348 break; 349 } 350 } 351 } 352 } else { 353 if (!hasMillis) { 354 millis = super.getTime(date); 355 hasMillis = true; 356 } 357 358 long since = era.getSince(date.getZone()); 359 if (millis >= since) { 360 break; 361 } 362 } 363 } 364 if (i >= 0) { 365 ldate.setLocalEra(era); 366 @SuppressWarnings("null") 367 int y = ldate.getNormalizedYear() - era.getSinceDate().getYear() + 1; 368 ldate.setLocalYear(y); 369 } else { 370 // Set Gregorian year with no era 371 ldate.setEra(null); 372 ldate.setLocalYear(year); 373 ldate.setNormalizedYear(year); 374 } 375 ldate.setNormalized(true); 376 return true; 377 } 378 379 @Override 380 void normalizeMonth(CalendarDate date) { 381 normalizeYear(date); 382 super.normalizeMonth(date); 383 } 384 385 void normalizeYear(CalendarDate date) { 386 Date ldate = (Date) date; 387 // Set the supposed-to-be-correct Gregorian year first 388 // e.g., Showa 90 becomes 2015 (1926 + 90 - 1). 389 Era era = ldate.getEra(); 390 if (era == null || !validateEra(era)) { 391 ldate.setNormalizedYear(ldate.getYear()); 392 } else { 393 ldate.setNormalizedYear(era.getSinceDate().getYear() + ldate.getYear() - 1); 394 } 395 } 396 397 /** 398 * Returns whether the specified Gregorian year is a leap year. 399 * @see #isLeapYear(Era, int) 400 */ 401 @Override 402 public boolean isLeapYear(int gregorianYear) { 403 return CalendarUtils.isGregorianLeapYear(gregorianYear); 404 } 405 406 public boolean isLeapYear(Era era, int year) { 407 if (era == null) { 408 return isLeapYear(year); 409 } 410 int gyear = era.getSinceDate().getYear() + year - 1; 411 return isLeapYear(gyear); 412 } 413 414 @Override 415 public void getCalendarDateFromFixedDate(CalendarDate date, long fixedDate) { 416 Date ldate = (Date) date; 417 super.getCalendarDateFromFixedDate(ldate, fixedDate); 418 adjustYear(ldate, (fixedDate - EPOCH_OFFSET) * DAY_IN_MILLIS, 0); 419 } 420 }