1 /*
   2  * Copyright (c) 2005, 2011, 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.io.File;
  29 import java.io.FileInputStream;
  30 import java.io.IOException;
  31 import java.security.AccessController;
  32 import java.security.PrivilegedAction;
  33 import java.security.PrivilegedActionException;
  34 import java.security.PrivilegedExceptionAction;
  35 import java.util.ArrayList;
  36 import java.util.List;
  37 import java.util.Properties;
  38 import java.util.StringTokenizer;
  39 import java.util.TimeZone;
  40 
  41 /**
  42  *
  43  * @author Masayoshi Okutsu
  44  * @since 1.6
  45  */
  46 
  47 public class LocalGregorianCalendar extends BaseCalendar {
  48     private String name;
  49     private Era[] eras;
  50 
  51     public static class Date extends BaseCalendar.Date {
  52 
  53         protected Date() {
  54             super();
  55         }
  56 
  57         protected Date(TimeZone zone) {
  58             super(zone);
  59         }
  60 
  61         private int gregorianYear = FIELD_UNDEFINED;
  62 
  63         public Date setEra(Era era) {
  64             if (getEra() != era) {
  65                 super.setEra(era);
  66                 gregorianYear = FIELD_UNDEFINED;
  67             }
  68             return this;
  69         }
  70 
  71         public Date addYear(int localYear) {
  72             super.addYear(localYear);
  73             gregorianYear += localYear;
  74             return this;
  75         }
  76 
  77         public Date setYear(int localYear) {
  78             if (getYear() != localYear) {
  79                 super.setYear(localYear);
  80                 gregorianYear = FIELD_UNDEFINED;
  81             }
  82             return this;
  83         }
  84 
  85         public int getNormalizedYear() {
  86             return gregorianYear;
  87         }
  88 
  89         public void setNormalizedYear(int normalizedYear) {
  90             this.gregorianYear = normalizedYear;
  91         }
  92 
  93         void setLocalEra(Era era) {
  94             super.setEra(era);
  95         }
  96 
  97         void setLocalYear(int year) {
  98             super.setYear(year);
  99         }
 100 
 101         public String toString() {
 102             String time = super.toString();
 103             time = time.substring(time.indexOf('T'));
 104             StringBuffer sb = new StringBuffer();
 105             Era era = getEra();
 106             if (era != null) {
 107                 String abbr = era.getAbbreviation();
 108                 if (abbr != null) {
 109                     sb.append(abbr);
 110                 }
 111             }
 112             sb.append(getYear()).append('.');
 113             CalendarUtils.sprintf0d(sb, getMonth(), 2).append('.');
 114             CalendarUtils.sprintf0d(sb, getDayOfMonth(), 2);
 115             sb.append(time);
 116             return sb.toString();
 117         }
 118     }
 119 
 120     static LocalGregorianCalendar getLocalGregorianCalendar(String name) {
 121         Properties calendarProps = null;
 122         try {
 123             String homeDir = AccessController.doPrivileged(
 124                 new sun.security.action.GetPropertyAction("java.home"));
 125             final String fname = homeDir + File.separator + "lib" + File.separator
 126                                  + "calendars.properties";
 127             calendarProps = (Properties) AccessController.doPrivileged(new PrivilegedExceptionAction() {
 128                 public Object run() throws IOException {
 129                     Properties props = new Properties();
 130                     try (FileInputStream fis = new FileInputStream(fname)) {
 131                         props.load(fis);
 132                     }
 133                     return props;
 134                 }
 135             });
 136         } catch (PrivilegedActionException e) {
 137             throw new RuntimeException(e.getException());
 138         }
 139 
 140         // Parse calendar.*.eras
 141         String props = calendarProps.getProperty("calendar." + name + ".eras");
 142         if (props == null) {
 143             return null;
 144         }
 145         List<Era> eras = new ArrayList<Era>();
 146         StringTokenizer eraTokens = new StringTokenizer(props, ";");
 147         while (eraTokens.hasMoreTokens()) {
 148             String items = eraTokens.nextToken().trim();
 149             StringTokenizer itemTokens = new StringTokenizer(items, ",");
 150             String eraName = null;
 151             boolean localTime = true;
 152             long since = 0;
 153             String abbr = null;
 154 
 155             while (itemTokens.hasMoreTokens()) {
 156                 String item = itemTokens.nextToken();
 157                 int index = item.indexOf('=');
 158                 // it must be in the key=value form.
 159                 if (index == -1) {
 160                     return null;
 161                 }
 162                 String key = item.substring(0, index);
 163                 String value = item.substring(index + 1);
 164                 if ("name".equals(key)) {
 165                     eraName = value;
 166                 } else if ("since".equals(key)) {
 167                     if (value.endsWith("u")) {
 168                         localTime = false;
 169                         since = Long.parseLong(value.substring(0, value.length() - 1));
 170                     } else {
 171                         since = Long.parseLong(value);
 172                     }
 173                 } else if ("abbr".equals(key)) {
 174                     abbr = value;
 175                 } else {
 176                     throw new RuntimeException("Unknown key word: " + key);
 177                 }
 178             }
 179             Era era = new Era(eraName, abbr, since, localTime);
 180             eras.add(era);
 181         }
 182         Era[] eraArray = new Era[eras.size()];
 183         eras.toArray(eraArray);
 184 
 185         return new LocalGregorianCalendar(name, eraArray);
 186     }
 187 
 188     private LocalGregorianCalendar(String name, Era[] eras) {
 189         this.name = name;
 190         this.eras = eras;
 191         setEras(eras);
 192     }
 193 
 194     public String getName() {
 195         return name;
 196     }
 197 
 198     public Date getCalendarDate() {
 199         return getCalendarDate(System.currentTimeMillis(), newCalendarDate());
 200     }
 201 
 202     public Date getCalendarDate(long millis) {
 203         return getCalendarDate(millis, newCalendarDate());
 204     }
 205 
 206     public Date getCalendarDate(long millis, TimeZone zone) {
 207         return getCalendarDate(millis, newCalendarDate(zone));
 208     }
 209 
 210     public Date getCalendarDate(long millis, CalendarDate date) {
 211         Date ldate = (Date) super.getCalendarDate(millis, date);
 212         return adjustYear(ldate, millis, ldate.getZoneOffset());
 213     }
 214 
 215     private Date adjustYear(Date ldate, long millis, int zoneOffset) {
 216         int i;
 217         for (i = eras.length - 1; i >= 0; --i) {
 218             Era era = eras[i];
 219             long since = era.getSince(null);
 220             if (era.isLocalTime()) {
 221                 since -= zoneOffset;
 222             }
 223             if (millis >= since) {
 224                 ldate.setLocalEra(era);
 225                 int y = ldate.getNormalizedYear() - era.getSinceDate().getYear() + 1;
 226                 ldate.setLocalYear(y);
 227                 break;
 228             }
 229         }
 230         if (i < 0) {
 231             ldate.setLocalEra(null);
 232             ldate.setLocalYear(ldate.getNormalizedYear());
 233         }
 234         ldate.setNormalized(true);
 235         return ldate;
 236     }
 237 
 238     public Date newCalendarDate() {
 239         return new Date();
 240     }
 241 
 242     public Date newCalendarDate(TimeZone zone) {
 243         return new Date(zone);
 244     }
 245 
 246     public boolean validate(CalendarDate date) {
 247         Date ldate = (Date) date;
 248         Era era = ldate.getEra();
 249         if (era != null) {
 250             if (!validateEra(era)) {
 251                 return false;
 252             }
 253             ldate.setNormalizedYear(era.getSinceDate().getYear() + ldate.getYear());
 254         } else {
 255             ldate.setNormalizedYear(ldate.getYear());
 256         }
 257         return super.validate(ldate);
 258     }
 259 
 260     private boolean validateEra(Era era) {
 261         // Validate the era
 262         for (int i = 0; i < eras.length; i++) {
 263             if (era == eras[i]) {
 264                 return true;
 265             }
 266         }
 267         return false;
 268     }
 269 
 270     public boolean normalize(CalendarDate date) {
 271         if (date.isNormalized()) {
 272             return true;
 273         }
 274 
 275         normalizeYear(date);
 276         Date ldate = (Date) date;
 277 
 278         // Normalize it as a Gregorian date and get its millisecond value
 279         super.normalize(ldate);
 280 
 281         boolean hasMillis = false;
 282         long millis = 0;
 283         int year = ldate.getNormalizedYear();
 284         int i;
 285         Era era = null;
 286         for (i = eras.length - 1; i >= 0; --i) {
 287             era = eras[i];
 288             if (era.isLocalTime()) {
 289                 CalendarDate sinceDate = era.getSinceDate();
 290                 int sinceYear = sinceDate.getYear();
 291                 if (year > sinceYear) {
 292                     break;
 293                 }
 294                 if (year == sinceYear) {
 295                     int month = ldate.getMonth();
 296                     int sinceMonth = sinceDate.getMonth();
 297                     if (month > sinceMonth) {
 298                         break;
 299                     }
 300                     if (month == sinceMonth) {
 301                         int day = ldate.getDayOfMonth();
 302                         int sinceDay = sinceDate.getDayOfMonth();
 303                         if (day > sinceDay) {
 304                             break;
 305                         }
 306                         if (day == sinceDay) {
 307                             long timeOfDay = ldate.getTimeOfDay();
 308                             long sinceTimeOfDay = sinceDate.getTimeOfDay();
 309                             if (timeOfDay >= sinceTimeOfDay) {
 310                                 break;
 311                             }
 312                             --i;
 313                             break;
 314                         }
 315                     }
 316                 }
 317             } else {
 318                 if (!hasMillis) {
 319                     millis  = super.getTime(date);
 320                     hasMillis = true;
 321                 }
 322 
 323                 long since = era.getSince(date.getZone());
 324                 if (millis >= since) {
 325                     break;
 326                 }
 327             }
 328         }
 329         if (i >= 0) {
 330             ldate.setLocalEra(era);
 331             int y = ldate.getNormalizedYear() - era.getSinceDate().getYear() + 1;
 332             ldate.setLocalYear(y);
 333         } else {
 334             // Set Gregorian year with no era
 335             ldate.setEra(null);
 336             ldate.setLocalYear(year);
 337             ldate.setNormalizedYear(year);
 338         }
 339         ldate.setNormalized(true);
 340         return true;
 341     }
 342 
 343     void normalizeMonth(CalendarDate date) {
 344         normalizeYear(date);
 345         super.normalizeMonth(date);
 346     }
 347 
 348     void normalizeYear(CalendarDate date) {
 349         Date ldate = (Date) date;
 350         // Set the supposed-to-be-correct Gregorian year first
 351         // e.g., Showa 90 becomes 2015 (1926 + 90 - 1).
 352         Era era = ldate.getEra();
 353         if (era == null || !validateEra(era)) {
 354             ldate.setNormalizedYear(ldate.getYear());
 355         } else {
 356             ldate.setNormalizedYear(era.getSinceDate().getYear() + ldate.getYear() - 1);
 357         }
 358     }
 359 
 360     /**
 361      * Returns whether the specified Gregorian year is a leap year.
 362      * @see #isLeapYear(Era, int)
 363      */
 364     public boolean isLeapYear(int gregorianYear) {
 365         return CalendarUtils.isGregorianLeapYear(gregorianYear);
 366     }
 367 
 368     public boolean isLeapYear(Era era, int year) {
 369         if (era == null) {
 370             return isLeapYear(year);
 371         }
 372         int gyear = era.getSinceDate().getYear() + year - 1;
 373         return isLeapYear(gyear);
 374     }
 375 
 376     public void getCalendarDateFromFixedDate(CalendarDate date, long fixedDate) {
 377         Date ldate = (Date) date;
 378         super.getCalendarDateFromFixedDate(ldate, fixedDate);
 379         adjustYear(ldate, (fixedDate - EPOCH_OFFSET) * DAY_IN_MILLIS, 0);
 380     }
 381 }