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