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 }