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