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