KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > java > util > JapaneseImperialCalendar


1 /*
2  * @(#)JapaneseImperialCalendar.java 1.8 07/01/18
3  *
4  * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package java.util;
9
10 import java.io.IOException JavaDoc;
11 import java.io.ObjectInputStream JavaDoc;
12 import sun.util.calendar.BaseCalendar;
13 import sun.util.calendar.CalendarDate;
14 import sun.util.calendar.CalendarSystem;
15 import sun.util.calendar.CalendarUtils;
16 import sun.util.calendar.Era;
17 import sun.util.calendar.Gregorian;
18 import sun.util.calendar.LocalGregorianCalendar;
19 import sun.util.calendar.ZoneInfo;
20 import sun.util.resources.LocaleData;
21
22 /**
23  * <code>JapaneseImperialCalendar</code> implements a Japanese
24  * calendar system in which the imperial era-based year numbering is
25  * supported from the Meiji era. The following are the eras supported
26  * by this calendar system.
27  * <pre><tt>
28  * ERA value Era name Since (in Gregorian)
29  * ------------------------------------------------------
30  * 0 N/A N/A
31  * 1 Meiji 1868-01-01 midnight local time
32  * 2 Taisho 1912-07-30 midnight local time
33  * 3 Showa 1926-12-25 midnight local time
34  * 4 Heisei 1989-01-08 midnight local time
35  * ------------------------------------------------------
36  * </tt></pre>
37  *
38  * <p><code>ERA</code> value 0 specifies the years before Meiji and
39  * the Gregorian year values are used. Unlike {@link
40  * GregorianCalendar}, the Julian to Gregorian transition is not
41  * supported because it doesn't make any sense to the Japanese
42  * calendar systems used before Meiji. To represent the years before
43  * Gregorian year 1, 0 and negative values are used. The Japanese
44  * Imperial rescripts and government decrees don't specify how to deal
45  * with time differences for applying the era transitions. This
46  * calendar implementation assumes local time for all transitions.
47  *
48  * @author Masayoshi Okutsu
49  * @since 1.6
50  */

51 class JapaneseImperialCalendar extends Calendar JavaDoc {
52     /*
53      * Implementation Notes
54      *
55      * This implementation uses
56      * sun.util.calendar.LocalGregorianCalendar to perform most of the
57      * calendar calculations. LocalGregorianCalendar is configurable
58      * and reads <JRE_HOME>/lib/calendars.properties at the start-up.
59      */

60
61     /**
62      * The ERA constant designating the era before Meiji.
63      */

64     public static final int BEFORE_MEIJI = 0;
65
66     /**
67      * The ERA constant designating the Meiji era.
68      */

69     public static final int MEIJI = 1;
70
71     /**
72      * The ERA constant designating the Taisho era.
73      */

74     public static final int TAISHO = 2;
75
76     /**
77      * The ERA constant designating the Showa era.
78      */

79     public static final int SHOWA = 3;
80
81     /**
82      * The ERA constant designating the Heisei era.
83      */

84     public static final int HEISEI = 4;
85
86     private static final int EPOCH_OFFSET = 719163; // Fixed date of January 1, 1970 (Gregorian)
87
private static final int EPOCH_YEAR = 1970;
88
89     // Useful millisecond constants. Although ONE_DAY and ONE_WEEK can fit
90
// into ints, they must be longs in order to prevent arithmetic overflow
91
// when performing (bug 4173516).
92
private static final int ONE_SECOND = 1000;
93     private static final int ONE_MINUTE = 60*ONE_SECOND;
94     private static final int ONE_HOUR = 60*ONE_MINUTE;
95     private static final long ONE_DAY = 24*ONE_HOUR;
96     private static final long ONE_WEEK = 7*ONE_DAY;
97
98     // Reference to the sun.util.calendar.LocalGregorianCalendar instance (singleton).
99
private static final LocalGregorianCalendar jcal
100     = (LocalGregorianCalendar) CalendarSystem.forName("japanese");
101
102     // Gregorian calendar instance. This is required because era
103
// transition dates are given in Gregorian dates.
104
private static final Gregorian gcal = CalendarSystem.getGregorianCalendar();
105
106     // The Era instance representing "before Meiji".
107
private static final Era BEFORE_MEIJI_ERA = new Era("BeforeMeiji", "BM", Long.MIN_VALUE, false);
108
109     // Imperial eras. The sun.util.calendar.LocalGregorianCalendar
110
// doesn't have an Era representing before Meiji, which is
111
// inconvenient for a Calendar. So, era[0] is a reference to
112
// BEFORE_MEIJI_ERA.
113
private static final Era[] eras;
114
115     // Fixed date of the first date of each era.
116
private static final long[] sinceFixedDates;
117
118     /*
119      * <pre>
120      * Greatest Least
121      * Field name Minimum Minimum Maximum Maximum
122      * ---------- ------- ------- ------- -------
123      * ERA 0 0 1 1
124      * YEAR -292275055 1 ? ?
125      * MONTH 0 0 11 11
126      * WEEK_OF_YEAR 1 1 52* 53
127      * WEEK_OF_MONTH 0 0 4* 6
128      * DAY_OF_MONTH 1 1 28* 31
129      * DAY_OF_YEAR 1 1 365* 366
130      * DAY_OF_WEEK 1 1 7 7
131      * DAY_OF_WEEK_IN_MONTH -1 -1 4* 6
132      * AM_PM 0 0 1 1
133      * HOUR 0 0 11 11
134      * HOUR_OF_DAY 0 0 23 23
135      * MINUTE 0 0 59 59
136      * SECOND 0 0 59 59
137      * MILLISECOND 0 0 999 999
138      * ZONE_OFFSET -13:00 -13:00 14:00 14:00
139      * DST_OFFSET 0:00 0:00 0:20 2:00
140      * </pre>
141      * *: depends on eras
142      */

143     static final int MIN_VALUES[] = {
144         0, // ERA
145
-292275055, // YEAR
146
JANUARY, // MONTH
147
1, // WEEK_OF_YEAR
148
0, // WEEK_OF_MONTH
149
1, // DAY_OF_MONTH
150
1, // DAY_OF_YEAR
151
SUNDAY, // DAY_OF_WEEK
152
1, // DAY_OF_WEEK_IN_MONTH
153
AM, // AM_PM
154
0, // HOUR
155
0, // HOUR_OF_DAY
156
0, // MINUTE
157
0, // SECOND
158
0, // MILLISECOND
159
-13*ONE_HOUR, // ZONE_OFFSET (UNIX compatibility)
160
0 // DST_OFFSET
161
};
162     static final int LEAST_MAX_VALUES[] = {
163         0, // ERA (initialized later)
164
0, // YEAR (initialized later)
165
JANUARY, // MONTH (Showa 64 ended in January.)
166
0, // WEEK_OF_YEAR (Showa 1 has only 6 days which could be 0 weeks.)
167
4, // WEEK_OF_MONTH
168
28, // DAY_OF_MONTH
169
0, // DAY_OF_YEAR (initialized later)
170
SATURDAY, // DAY_OF_WEEK
171
4, // DAY_OF_WEEK_IN
172
PM, // AM_PM
173
11, // HOUR
174
23, // HOUR_OF_DAY
175
59, // MINUTE
176
59, // SECOND
177
999, // MILLISECOND
178
14*ONE_HOUR, // ZONE_OFFSET
179
20*ONE_MINUTE // DST_OFFSET (historical least maximum)
180
};
181     static final int MAX_VALUES[] = {
182         0, // ERA
183
292278994, // YEAR
184
DECEMBER, // MONTH
185
53, // WEEK_OF_YEAR
186
6, // WEEK_OF_MONTH
187
31, // DAY_OF_MONTH
188
366, // DAY_OF_YEAR
189
SATURDAY, // DAY_OF_WEEK
190
6, // DAY_OF_WEEK_IN
191
PM, // AM_PM
192
11, // HOUR
193
23, // HOUR_OF_DAY
194
59, // MINUTE
195
59, // SECOND
196
999, // MILLISECOND
197
14*ONE_HOUR, // ZONE_OFFSET
198
2*ONE_HOUR // DST_OFFSET (double summer time)
199
};
200
201     // Proclaim serialization compatibility with JDK 1.6
202
private static final long serialVersionUID = -3364572813905467929L;
203
204     static {
205     Era[] es = jcal.getEras();
206     int length = es.length + 1;
207     eras = new Era[length];
208     sinceFixedDates = new long[length];
209
210     // eras[BEFORE_MEIJI] and sinceFixedDate[BEFORE_MEIJI] are the
211
// same as Gregorian.
212
int index = BEFORE_MEIJI;
213     sinceFixedDates[index] = gcal.getFixedDate(BEFORE_MEIJI_ERA.getSinceDate());
214     eras[index++] = BEFORE_MEIJI_ERA;
215     for (Era e : es) {
216         CalendarDate d = e.getSinceDate();
217         sinceFixedDates[index] = gcal.getFixedDate(d);
218         eras[index++] = e;
219     }
220
221     LEAST_MAX_VALUES[ERA] = MAX_VALUES[ERA] = eras.length - 1;
222
223     // Calculate the least maximum year and least day of Year
224
// values. The following code assumes that there's at most one
225
// era transition in a Gregorian year.
226
int year = Integer.MAX_VALUE;
227     int dayOfYear = Integer.MAX_VALUE;
228     CalendarDate date = gcal.newCalendarDate(TimeZone.NO_TIMEZONE);
229     for (int i = 1; i < eras.length; i++) {
230         long fd = sinceFixedDates[i];
231         CalendarDate transitionDate = eras[i].getSinceDate();
232         date.setDate(transitionDate.getYear(), BaseCalendar.JANUARY, 1);
233         long fdd = gcal.getFixedDate(date);
234         dayOfYear = Math.min((int)(fdd - fd), dayOfYear);
235         date.setDate(transitionDate.getYear(), BaseCalendar.DECEMBER, 31);
236         fdd = gcal.getFixedDate(date) + 1;
237         dayOfYear = Math.min((int)(fd - fdd), dayOfYear);
238
239         LocalGregorianCalendar.Date lgd = getCalendarDate(fd - 1);
240         int y = lgd.getYear();
241         // Unless the first year starts from January 1, the actual
242
// max value could be one year short. For example, if it's
243
// Showa 63 January 8, 63 is the actual max value since
244
// Showa 64 January 8 doesn't exist.
245
if (!(lgd.getMonth() == BaseCalendar.JANUARY && lgd.getDayOfMonth() == 1))
246         y--;
247         year = Math.min(y, year);
248     }
249     LEAST_MAX_VALUES[YEAR] = year; // Max year could be smaller than this value.
250
LEAST_MAX_VALUES[DAY_OF_YEAR] = dayOfYear;
251     }
252
253     /**
254      * jdate always has a sun.util.calendar.LocalGregorianCalendar.Date instance to
255      * avoid overhead of creating it for each calculation.
256      */

257     private transient LocalGregorianCalendar.Date jdate;
258
259     /**
260      * Temporary int[2] to get time zone offsets. zoneOffsets[0] gets
261      * the GMT offset value and zoneOffsets[1] gets the daylight saving
262      * value.
263      */

264     private transient int[] zoneOffsets;
265
266     /**
267      * Temporary storage for saving original fields[] values in
268      * non-lenient mode.
269      */

270     private transient int[] originalFields;
271
272     /**
273      * Constructs a <code>JapaneseImperialCalendar</code> based on the current time
274      * in the given time zone with the given locale.
275      *
276      * @param zone the given time zone.
277      * @param aLocale the given locale.
278      */

279     public JapaneseImperialCalendar(TimeZone JavaDoc zone, Locale JavaDoc aLocale) {
280         super(zone, aLocale);
281     jdate = jcal.newCalendarDate(zone);
282         setTimeInMillis(System.currentTimeMillis());
283     }
284
285     /**
286      * Compares this <code>JapaneseImperialCalendar</code> to the specified
287      * <code>Object</code>. The result is <code>true</code> if and
288      * only if the argument is a <code>JapaneseImperialCalendar</code> object
289      * that represents the same time value (millisecond offset from
290      * the <a HREF="Calendar.html#Epoch">Epoch</a>) under the same
291      * <code>Calendar</code> parameters.
292      *
293      * @param obj the object to compare with.
294      * @return <code>true</code> if this object is equal to <code>obj</code>;
295      * <code>false</code> otherwise.
296      * @see Calendar#compareTo(Calendar)
297      */

298     public boolean equals(Object JavaDoc obj) {
299         return obj instanceof JapaneseImperialCalendar JavaDoc &&
300         super.equals(obj);
301     }
302     
303     /**
304      * Generates the hash code for this
305      * <code>JapaneseImperialCalendar</code> object.
306      */

307     public int hashCode() {
308         return super.hashCode() ^ jdate.hashCode();
309     }
310
311     /**
312      * Adds the specified (signed) amount of time to the given calendar field,
313      * based on the calendar's rules.
314      *
315      * <p><em>Add rule 1</em>. The value of <code>field</code>
316      * after the call minus the value of <code>field</code> before the
317      * call is <code>amount</code>, modulo any overflow that has occurred in
318      * <code>field</code>. Overflow occurs when a field value exceeds its
319      * range and, as a result, the next larger field is incremented or
320      * decremented and the field value is adjusted back into its range.</p>
321      *
322      * <p><em>Add rule 2</em>. If a smaller field is expected to be
323      * invariant, but it is impossible for it to be equal to its
324      * prior value because of changes in its minimum or maximum after
325      * <code>field</code> is changed, then its value is adjusted to be as close
326      * as possible to its expected value. A smaller field represents a
327      * smaller unit of time. <code>HOUR</code> is a smaller field than
328      * <code>DAY_OF_MONTH</code>. No adjustment is made to smaller fields
329      * that are not expected to be invariant. The calendar system
330      * determines what fields are expected to be invariant.</p>
331      *
332      * @param field the calendar field.
333      * @param amount the amount of date or time to be added to the field.
334      * @exception IllegalArgumentException if <code>field</code> is
335      * <code>ZONE_OFFSET</code>, <code>DST_OFFSET</code>, or unknown,
336      * or if any calendar fields have out-of-range values in
337      * non-lenient mode.
338      */

339     public void add(int field, int amount) {
340     // If amount == 0, do nothing even the given field is out of
341
// range. This is tested by JCK.
342
if (amount == 0) {
343         return; // Do nothing!
344
}
345
346     if (field < 0 || field >= ZONE_OFFSET) {
347         throw new IllegalArgumentException JavaDoc();
348     }
349
350     // Sync the time and calendar fields.
351
complete();
352
353         if (field == YEAR) {
354         LocalGregorianCalendar.Date d = (LocalGregorianCalendar.Date) jdate.clone();
355         d.addYear(amount);
356             pinDayOfMonth(d);
357         set(ERA, getEraIndex(d));
358         set(YEAR, d.getYear());
359         set(MONTH, d.getMonth() - 1);
360         set(DAY_OF_MONTH, d.getDayOfMonth());
361         } else if (field == MONTH) {
362         LocalGregorianCalendar.Date d = (LocalGregorianCalendar.Date) jdate.clone();
363         d.addMonth(amount);
364             pinDayOfMonth(d);
365         set(ERA, getEraIndex(d));
366         set(YEAR, d.getYear());
367         set(MONTH, d.getMonth() - 1);
368         set(DAY_OF_MONTH, d.getDayOfMonth());
369         } else if (field == ERA) {
370             int era = internalGet(ERA) + amount;
371             if (era < 0) {
372         era = 0;
373         } else if (era > eras.length - 1) {
374         era = eras.length - 1;
375         }
376             set(ERA, era);
377         } else {
378         long delta = amount;
379         long timeOfDay = 0;
380         switch (field) {
381         // Handle the time fields here. Convert the given
382
// amount to milliseconds and call setTimeInMillis.
383
case HOUR:
384             case HOUR_OF_DAY:
385                 delta *= 60 * 60 * 1000; // hours to milliseconds
386
break;
387
388             case MINUTE:
389                 delta *= 60 * 1000; // minutes to milliseconds
390
break;
391
392             case SECOND:
393                 delta *= 1000; // seconds to milliseconds
394
break;
395
396             case MILLISECOND:
397                 break;
398
399         // Handle week, day and AM_PM fields which involves
400
// time zone offset change adjustment. Convert the
401
// given amount to the number of days.
402
case WEEK_OF_YEAR:
403             case WEEK_OF_MONTH:
404             case DAY_OF_WEEK_IN_MONTH:
405                 delta *= 7;
406         break;
407
408             case DAY_OF_MONTH: // synonym of DATE
409
case DAY_OF_YEAR:
410             case DAY_OF_WEEK:
411         break;
412
413         case AM_PM:
414         // Convert the amount to the number of days (delta)
415
// and +12 or -12 hours (timeOfDay).
416
delta = amount / 2;
417         timeOfDay = 12 * (amount % 2);
418         break;
419         }
420
421         // The time fields don't require time zone offset change
422
// adjustment.
423
if (field >= HOUR) {
424         setTimeInMillis(time + delta);
425         return;
426         }
427
428         // The rest of the fields (week, day or AM_PM fields)
429
// require time zone offset (both GMT and DST) change
430
// adjustment.
431

432         // Translate the current time to the fixed date and time
433
// of the day.
434
long fd = cachedFixedDate;
435         timeOfDay += internalGet(HOUR_OF_DAY);
436         timeOfDay *= 60;
437         timeOfDay += internalGet(MINUTE);
438         timeOfDay *= 60;
439         timeOfDay += internalGet(SECOND);
440         timeOfDay *= 1000;
441         timeOfDay += internalGet(MILLISECOND);
442         if (timeOfDay >= ONE_DAY) {
443         fd++;
444         timeOfDay -= ONE_DAY;
445         } else if (timeOfDay < 0) {
446         fd--;
447         timeOfDay += ONE_DAY;
448         }
449
450         fd += delta; // fd is the expected fixed date after the calculation
451
int zoneOffset = internalGet(ZONE_OFFSET) + internalGet(DST_OFFSET);
452         setTimeInMillis((fd - EPOCH_OFFSET) * ONE_DAY + timeOfDay - zoneOffset);
453         zoneOffset -= internalGet(ZONE_OFFSET) + internalGet(DST_OFFSET);
454         // If the time zone offset has changed, then adjust the difference.
455
if (zoneOffset != 0) {
456         setTimeInMillis(time + zoneOffset);
457         long fd2 = cachedFixedDate;
458         // If the adjustment has changed the date, then take
459
// the previous one.
460
if (fd2 != fd) {
461             setTimeInMillis(time - zoneOffset);
462         }
463         }
464     }
465     }
466
467     public void roll(int field, boolean up) {
468         roll(field, up ? +1 : -1);
469     }
470
471     /**
472      * Adds a signed amount to the specified calendar field without changing larger fields.
473      * A negative roll amount means to subtract from field without changing
474      * larger fields. If the specified amount is 0, this method performs nothing.
475      *
476      * <p>This method calls {@link #complete()} before adding the
477      * amount so that all the calendar fields are normalized. If there
478      * is any calendar field having an out-of-range value in non-lenient mode, then an
479      * <code>IllegalArgumentException</code> is thrown.
480      *
481      * @param field the calendar field.
482      * @param amount the signed amount to add to <code>field</code>.
483      * @exception IllegalArgumentException if <code>field</code> is
484      * <code>ZONE_OFFSET</code>, <code>DST_OFFSET</code>, or unknown,
485      * or if any calendar fields have out-of-range values in
486      * non-lenient mode.
487      * @see #roll(int,boolean)
488      * @see #add(int,int)
489      * @see #set(int,int)
490      */

491     public void roll(int field, int amount) {
492     // If amount == 0, do nothing even the given field is out of
493
// range. This is tested by JCK.
494
if (amount == 0) {
495         return;
496     }
497
498     if (field < 0 || field >= ZONE_OFFSET) {
499         throw new IllegalArgumentException JavaDoc();
500     }
501
502     // Sync the time and calendar fields.
503
complete();
504
505     int min = getMinimum(field);
506     int max = getMaximum(field);
507
508         switch (field) {
509     case ERA:
510         case AM_PM:
511         case MINUTE:
512         case SECOND:
513         case MILLISECOND:
514             // These fields are handled simply, since they have fixed
515
// minima and maxima. Other fields are complicated, since
516
// the range within they must roll varies depending on the
517
// date, a time zone and the era transitions.
518
break;
519
520         case HOUR:
521         case HOUR_OF_DAY:
522         {
523         int unit = max + 1; // 12 or 24 hours
524
int h = internalGet(field);
525         int nh = (h + amount) % unit;
526         if (nh < 0) {
527             nh += unit;
528         }
529         time += ONE_HOUR * (nh - h);
530
531         // The day might have changed, which could happen if
532
// the daylight saving time transition brings it to
533
// the next day, although it's very unlikely. But we
534
// have to make sure not to change the larger fields.
535
CalendarDate d = jcal.getCalendarDate(time, getZone());
536         if (internalGet(DAY_OF_MONTH) != d.getDayOfMonth()) {
537             d.setEra(jdate.getEra());
538             d.setDate(internalGet(YEAR),
539                   internalGet(MONTH) + 1,
540                   internalGet(DAY_OF_MONTH));
541             if (field == HOUR) {
542             assert (internalGet(AM_PM) == PM);
543             d.addHours(+12); // restore PM
544
}
545             time = jcal.getTime(d);
546         }
547         int hourOfDay = d.getHours();
548         internalSet(field, hourOfDay % unit);
549         if (field == HOUR) {
550             internalSet(HOUR_OF_DAY, hourOfDay);
551         } else {
552             internalSet(AM_PM, hourOfDay / 12);
553             internalSet(HOUR, hourOfDay % 12);
554         }
555
556         // Time zone offset and/or daylight saving might have changed.
557
int zoneOffset = d.getZoneOffset();
558         int saving = d.getDaylightSaving();
559         internalSet(ZONE_OFFSET, zoneOffset - saving);
560         internalSet(DST_OFFSET, saving);
561         return;
562         }
563
564         case YEAR:
565         min = getActualMinimum(field);
566         max = getActualMaximum(field);
567         break;
568
569         case MONTH:
570             // Rolling the month involves both pinning the final value to [0, 11]
571
// and adjusting the DAY_OF_MONTH if necessary. We only adjust the
572
// DAY_OF_MONTH if, after updating the MONTH field, it is illegal.
573
// E.g., <jan31>.roll(MONTH, 1) -> <feb28> or <feb29>.
574
{
575         if (!isTransitionYear(jdate.getNormalizedYear())) {
576             int year = jdate.getYear();
577             if (year == getMaximum(YEAR)) {
578             CalendarDate jd = jcal.getCalendarDate(time, getZone());
579             CalendarDate d = jcal.getCalendarDate(Long.MAX_VALUE, getZone());
580             max = d.getMonth() - 1;
581             int n = getRolledValue(internalGet(field), amount, min, max);
582             if (n == max) {
583                 // To avoid overflow, use an equivalent year.
584
jd.addYear(-400);
585                 jd.setMonth(n + 1);
586                 if (jd.getDayOfMonth() > d.getDayOfMonth()) {
587                 jd.setDayOfMonth(d.getDayOfMonth());
588                 jcal.normalize(jd);
589                 }
590                 if (jd.getDayOfMonth() == d.getDayOfMonth()
591                 && jd.getTimeOfDay() > d.getTimeOfDay()) {
592                 jd.setMonth(n + 1);
593                 jd.setDayOfMonth(d.getDayOfMonth() - 1);
594                 jcal.normalize(jd);
595                 // Month may have changed by the normalization.
596
n = jd.getMonth() - 1;
597                 }
598                 set(DAY_OF_MONTH, jd.getDayOfMonth());
599             }
600             set(MONTH, n);
601             } else if (year == getMinimum(YEAR)) {
602             CalendarDate jd = jcal.getCalendarDate(time, getZone());
603             CalendarDate d = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
604             min = d.getMonth() - 1;
605             int n = getRolledValue(internalGet(field), amount, min, max);
606             if (n == min) {
607                 // To avoid underflow, use an equivalent year.
608
jd.addYear(+400);
609                 jd.setMonth(n + 1);
610                 if (jd.getDayOfMonth() < d.getDayOfMonth()) {
611                 jd.setDayOfMonth(d.getDayOfMonth());
612                 jcal.normalize(jd);
613                 }
614                 if (jd.getDayOfMonth() == d.getDayOfMonth()
615                 && jd.getTimeOfDay() < d.getTimeOfDay()) {
616                 jd.setMonth(n + 1);
617                 jd.setDayOfMonth(d.getDayOfMonth() + 1);
618                 jcal.normalize(jd);
619                 // Month may have changed by the normalization.
620
n = jd.getMonth() - 1;
621                 }
622                 set(DAY_OF_MONTH, jd.getDayOfMonth());
623             }
624             set(MONTH, n);
625             } else {
626             int mon = (internalGet(MONTH) + amount) % 12;
627             if (mon < 0) {
628                 mon += 12;
629             }
630             set(MONTH, mon);
631                 
632             // Keep the day of month in the range. We
633
// don't want to spill over into the next
634
// month; e.g., we don't want jan31 + 1 mo ->
635
// feb31 -> mar3.
636
int monthLen = monthLength(mon);
637             if (internalGet(DAY_OF_MONTH) > monthLen) {
638                 set(DAY_OF_MONTH, monthLen);
639             }
640             }
641         } else {
642             int eraIndex = getEraIndex(jdate);
643             CalendarDate transition = null;
644             if (jdate.getYear() == 1) {
645             transition = eras[eraIndex].getSinceDate();
646             min = transition.getMonth() - 1;
647             } else {
648             if (eraIndex < eras.length - 1) {
649                 transition = eras[eraIndex + 1].getSinceDate();
650                 if (transition.getYear() == jdate.getNormalizedYear()) {
651                 max = transition.getMonth() - 1;
652                 if (transition.getDayOfMonth() == 1) {
653                     max--;
654                 }
655                 }
656             }
657             }
658
659             if (min == max) {
660             // The year has only one month. No need to
661
// process further. (Showa Gan-nen (year 1)
662
// and the last year have only one month.)
663
return;
664             }
665             int n = getRolledValue(internalGet(field), amount, min, max);
666             set(MONTH, n);
667             if (n == min) {
668             if (!(transition.getMonth() == BaseCalendar.JANUARY
669                   && transition.getDayOfMonth() == 1)) {
670                 if (jdate.getDayOfMonth() < transition.getDayOfMonth()) {
671                 set(DAY_OF_MONTH, transition.getDayOfMonth());
672                 }
673             }
674             } else if (n == max && (transition.getMonth() - 1 == n)) {
675             int dom = transition.getDayOfMonth();
676             if (jdate.getDayOfMonth() >= dom) {
677                 set(DAY_OF_MONTH, dom - 1);
678             }
679             }
680         }
681         return;
682             }
683
684         case WEEK_OF_YEAR:
685         {
686         int y = jdate.getNormalizedYear();
687         max = getActualMaximum(WEEK_OF_YEAR);
688         set(DAY_OF_WEEK, internalGet(DAY_OF_WEEK)); // update stamp[field]
689
int woy = internalGet(WEEK_OF_YEAR);
690         int value = woy + amount;
691         if (!isTransitionYear(jdate.getNormalizedYear())) {
692             int year = jdate.getYear();
693             if (year == getMaximum(YEAR)) {
694             max = getActualMaximum(WEEK_OF_YEAR);
695             } else if (year == getMinimum(YEAR)) {
696             min = getActualMinimum(WEEK_OF_YEAR);
697             max = getActualMaximum(WEEK_OF_YEAR);
698             if (value > min && value < max) {
699                 set(WEEK_OF_YEAR, value);
700                 return;
701             }
702             
703             }
704             // If the new value is in between min and max
705
// (exclusive), then we can use the value.
706
if (value > min && value < max) {
707             set(WEEK_OF_YEAR, value);
708             return;
709             }
710             long fd = cachedFixedDate;
711             // Make sure that the min week has the current DAY_OF_WEEK
712
long day1 = fd - (7 * (woy - min));
713             if (year != getMinimum(YEAR)) {
714             if (gcal.getYearFromFixedDate(day1) != y) {
715                 min++;
716             }
717             } else {
718             CalendarDate d = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
719             if (day1 < jcal.getFixedDate(d)) {
720                 min++;
721             }
722             }
723
724             // Make sure the same thing for the max week
725
fd += 7 * (max - internalGet(WEEK_OF_YEAR));
726             if (gcal.getYearFromFixedDate(fd) != y) {
727             max--;
728             }
729             break;
730         }
731
732         // Handle transition here.
733
long fd = cachedFixedDate;
734         long day1 = fd - (7 * (woy - min));
735         // Make sure that the min week has the current DAY_OF_WEEK
736
LocalGregorianCalendar.Date d = getCalendarDate(day1);
737         if (!(d.getEra() == jdate.getEra() && d.getYear() == jdate.getYear())) {
738             min++;
739         }
740
741         // Make sure the same thing for the max week
742
fd += 7 * (max - woy);
743         jcal.getCalendarDateFromFixedDate(d, fd);
744         if (!(d.getEra() == jdate.getEra() && d.getYear() == jdate.getYear())) {
745             max--;
746         }
747         // value: the new WEEK_OF_YEAR which must be converted
748
// to month and day of month.
749
value = getRolledValue(woy, amount, min, max) - 1;
750         d = getCalendarDate(day1 + value * 7);
751         set(MONTH, d.getMonth() - 1);
752         set(DAY_OF_MONTH, d.getDayOfMonth());
753         return;
754         }
755
756         case WEEK_OF_MONTH:
757         {
758         boolean isTransitionYear = isTransitionYear(jdate.getNormalizedYear());
759         // dow: relative day of week from the first day of week
760
int dow = internalGet(DAY_OF_WEEK) - getFirstDayOfWeek();
761         if (dow < 0) {
762             dow += 7;
763         }
764
765         long fd = cachedFixedDate;
766         long month1; // fixed date of the first day (usually 1) of the month
767
int monthLength; // actual month length
768
if (isTransitionYear) {
769             month1 = getFixedDateMonth1(jdate, fd);
770             monthLength = actualMonthLength();
771         } else {
772             month1 = fd - internalGet(DAY_OF_MONTH) + 1;
773             monthLength = jcal.getMonthLength(jdate);
774         }
775
776         // the first day of week of the month.
777
long monthDay1st = jcal.getDayOfWeekDateOnOrBefore(month1 + 6,
778                                    getFirstDayOfWeek());
779         // if the week has enough days to form a week, the
780
// week starts from the previous month.
781
if ((int)(monthDay1st - month1) >= getMinimalDaysInFirstWeek()) {
782             monthDay1st -= 7;
783         }
784         max = getActualMaximum(field);
785
786         // value: the new WEEK_OF_MONTH value
787
int value = getRolledValue(internalGet(field), amount, 1, max) - 1;
788
789         // nfd: fixed date of the rolled date
790
long nfd = monthDay1st + value * 7 + dow;
791
792         // Unlike WEEK_OF_YEAR, we need to change day of week if the
793
// nfd is out of the month.
794
if (nfd < month1) {
795             nfd = month1;
796         } else if (nfd >= (month1 + monthLength)) {
797             nfd = month1 + monthLength - 1;
798         }
799         set(DAY_OF_MONTH, (int)(nfd - month1) + 1);
800         return;
801         }
802
803         case DAY_OF_MONTH:
804         {
805         if (!isTransitionYear(jdate.getNormalizedYear())) {
806             max = jcal.getMonthLength(jdate);
807             break;
808         }
809
810         // TODO: Need to change the spec to be usable DAY_OF_MONTH rolling...
811

812         // Transition handling. We can't change year and era
813
// values here due to the Calendar roll spec!
814
long month1 = getFixedDateMonth1(jdate, cachedFixedDate);
815
816         // It may not be a regular month. Convert the date and range to
817
// the relative values, perform the roll, and
818
// convert the result back to the rolled date.
819
int value = getRolledValue((int)(cachedFixedDate - month1), amount,
820                        0, actualMonthLength() - 1);
821         LocalGregorianCalendar.Date d = getCalendarDate(month1 + value);
822         assert getEraIndex(d) == internalGetEra()
823             && d.getYear() == internalGet(YEAR) && d.getMonth()-1 == internalGet(MONTH);
824         set(DAY_OF_MONTH, d.getDayOfMonth());
825         return;
826         }
827
828         case DAY_OF_YEAR:
829         {
830         max = getActualMaximum(field);
831         if (!isTransitionYear(jdate.getNormalizedYear())) {
832             break;
833         }
834
835         // Handle transition. We can't change year and era values
836
// here due to the Calendar roll spec.
837
int value = getRolledValue(internalGet(DAY_OF_YEAR), amount, min, max);
838         long jan0 = cachedFixedDate - internalGet(DAY_OF_YEAR);
839         LocalGregorianCalendar.Date d = getCalendarDate(jan0 + value);
840         assert getEraIndex(d) == internalGetEra() && d.getYear() == internalGet(YEAR);
841         set(MONTH, d.getMonth() - 1);
842         set(DAY_OF_MONTH, d.getDayOfMonth());
843         return;
844         }
845
846         case DAY_OF_WEEK:
847         {
848         int normalizedYear = jdate.getNormalizedYear();
849         if (!isTransitionYear(normalizedYear) && !isTransitionYear(normalizedYear - 1)) {
850             // If the week of year is in the same year, we can
851
// just change DAY_OF_WEEK.
852
int weekOfYear = internalGet(WEEK_OF_YEAR);
853             if (weekOfYear > 1 && weekOfYear < 52) {
854             set(WEEK_OF_YEAR, internalGet(WEEK_OF_YEAR));
855             max = SATURDAY;
856             break;
857             }
858         }
859
860         // We need to handle it in a different way around year
861
// boundaries and in the transition year. Note that
862
// changing era and year values violates the roll
863
// rule: not changing larger calendar fields...
864
amount %= 7;
865         if (amount == 0) {
866             return;
867         }
868         long fd = cachedFixedDate;
869         long dowFirst = jcal.getDayOfWeekDateOnOrBefore(fd, getFirstDayOfWeek());
870         fd += amount;
871         if (fd < dowFirst) {
872             fd += 7;
873         } else if (fd >= dowFirst + 7) {
874             fd -= 7;
875         }
876         LocalGregorianCalendar.Date d = getCalendarDate(fd);
877         set(ERA, getEraIndex(d));
878         set(d.getYear(), d.getMonth() - 1, d.getDayOfMonth());
879         return;
880         }
881
882         case DAY_OF_WEEK_IN_MONTH:
883             {
884         min = 1; // after having normalized, min should be 1.
885
if (!isTransitionYear(jdate.getNormalizedYear())) {
886             int dom = internalGet(DAY_OF_MONTH);
887             int monthLength = jcal.getMonthLength(jdate);
888             int lastDays = monthLength % 7;
889             max = monthLength / 7;
890             int x = (dom - 1) % 7;
891             if (x < lastDays) {
892             max++;
893             }
894             set(DAY_OF_WEEK, internalGet(DAY_OF_WEEK));
895             break;
896         }
897
898         // Transition year handling.
899
long fd = cachedFixedDate;
900         long month1 = getFixedDateMonth1(jdate, fd);
901         int monthLength = actualMonthLength();
902         int lastDays = monthLength % 7;
903         max = monthLength / 7;
904         int x = (int)(fd - month1) % 7;
905         if (x < lastDays) {
906             max++;
907         }
908         int value = getRolledValue(internalGet(field), amount, min, max) - 1;
909         fd = month1 + value * 7 + x;
910         LocalGregorianCalendar.Date d = getCalendarDate(fd);
911         set(DAY_OF_MONTH, d.getDayOfMonth());
912         return;
913             }
914     }
915
916     set(field, getRolledValue(internalGet(field), amount, min, max));
917     }
918
919     public String JavaDoc getDisplayName(int field, int style, Locale JavaDoc locale) {
920     if (!checkDisplayNameParams(field, style, SHORT, LONG, locale,
921                     ERA_MASK|YEAR_MASK|MONTH_MASK|DAY_OF_WEEK_MASK|AM_PM_MASK)) {
922         return null;
923     }
924
925     // "GanNen" is supported only in the LONG style.
926
if (field == YEAR
927         && (style == SHORT || get(YEAR) != 1 || get(ERA) == 0)) {
928         return null;
929     }
930
931     ResourceBundle JavaDoc rb = LocaleData.getDateFormatData(locale);
932     String JavaDoc name = null;
933     String JavaDoc key = getKey(field, style);
934     if (key != null) {
935         String JavaDoc[] strings = rb.getStringArray(key);
936         if (field == YEAR) {
937         if (strings.length > 0) {
938             name = strings[0];
939         }
940         } else {
941         int index = get(field);
942         // If the ERA value is out of range for strings, then
943
// try to get its name or abbreviation from the Era instance.
944
if (field == ERA && index >= strings.length && index < eras.length) {
945             Era era = eras[index];
946             name = (style == SHORT) ? era.getAbbreviation() : era.getName();
947         } else {
948             if (field == DAY_OF_WEEK)
949             --index;
950             name = strings[index];
951         }
952         }
953     }
954     return name;
955     }
956
957     public Map JavaDoc<String JavaDoc,Integer JavaDoc> getDisplayNames(int field, int style, Locale JavaDoc locale) {
958     if (!checkDisplayNameParams(field, style, ALL_STYLES, LONG, locale,
959                     ERA_MASK|YEAR_MASK|MONTH_MASK|DAY_OF_WEEK_MASK|AM_PM_MASK)) {
960         return null;
961     }
962
963     if (style == ALL_STYLES) {
964         Map JavaDoc<String JavaDoc,Integer JavaDoc> shortNames = getDisplayNamesImpl(field, SHORT, locale);
965         if (field == AM_PM) {
966         return shortNames;
967         }
968         Map JavaDoc<String JavaDoc,Integer JavaDoc> longNames = getDisplayNamesImpl(field, LONG, locale);
969         if (shortNames == null) {
970         return longNames;
971         }
972         if (longNames != null) {
973         shortNames.putAll(longNames);
974         }
975         return shortNames;
976     }
977
978     // SHORT or LONG
979
return getDisplayNamesImpl(field, style, locale);
980     }
981
982     private Map JavaDoc<String JavaDoc,Integer JavaDoc> getDisplayNamesImpl(int field, int style, Locale JavaDoc locale) {
983     ResourceBundle JavaDoc rb = LocaleData.getDateFormatData(locale);
984     String JavaDoc key = getKey(field, style);
985     Map JavaDoc<String JavaDoc,Integer JavaDoc> map = new HashMap JavaDoc<String JavaDoc,Integer JavaDoc>();
986     if (key != null) {
987         String JavaDoc[] strings = rb.getStringArray(key);
988         if (field == YEAR) {
989         if (strings.length > 0) {
990             map.put(strings[0], 1);
991         }
992         } else {
993         int base = (field == DAY_OF_WEEK) ? 1 : 0;
994         for (int i = 0; i < strings.length; i++) {
995             map.put(strings[i], base + i);
996         }
997         // If strings[] has fewer than eras[], get more names from eras[].
998
if (field == ERA && strings.length < eras.length) {
999             for (int i = strings.length; i < eras.length; i++) {
1000            Era era = eras[i];
1001            String JavaDoc name = (style == SHORT) ? era.getAbbreviation() : era.getName();
1002            map.put(name, i);
1003            }
1004        }
1005        }
1006    }
1007    return map.size() > 0 ? map : null;
1008    }
1009
1010    private String JavaDoc getKey(int field, int style) {
1011    String JavaDoc className = JapaneseImperialCalendar JavaDoc.class.getName();
1012    StringBuilder JavaDoc key = new StringBuilder JavaDoc();
1013    switch (field) {
1014    case ERA:
1015        key.append(className);
1016        if (style == SHORT) {
1017        key.append(".short");
1018        }
1019        key.append(".Eras");
1020        break;
1021
1022    case YEAR:
1023        key.append(className).append(".FirstYear");
1024        break;
1025
1026    case MONTH:
1027        key.append(style == SHORT ? "MonthAbbreviations" : "MonthNames");
1028        break;
1029
1030    case DAY_OF_WEEK:
1031        key.append(style == SHORT ? "DayAbbreviations" : "DayNames");
1032        break;
1033
1034    case AM_PM:
1035        key.append("AmPmMarkers");
1036        break;
1037    }
1038    return key.length() > 0 ? key.toString() : null;
1039    }
1040
1041    /**
1042     * Returns the minimum value for the given calendar field of this
1043     * <code>Calendar</code> instance. The minimum value is
1044     * defined as the smallest value returned by the {@link
1045     * Calendar#get(int) get} method for any possible time value,
1046     * taking into consideration the current values of the
1047     * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
1048     * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
1049     * and {@link Calendar#getTimeZone() getTimeZone} methods.
1050     *
1051     * @param field the calendar field.
1052     * @return the minimum value for the given calendar field.
1053     * @see #getMaximum(int)
1054     * @see #getGreatestMinimum(int)
1055     * @see #getLeastMaximum(int)
1056     * @see #getActualMinimum(int)
1057     * @see #getActualMaximum(int)
1058     */

1059    public int getMinimum(int field) {
1060        return MIN_VALUES[field];
1061    }
1062
1063    /**
1064     * Returns the maximum value for the given calendar field of this
1065     * <code>GregorianCalendar</code> instance. The maximum value is
1066     * defined as the largest value returned by the {@link
1067     * Calendar#get(int) get} method for any possible time value,
1068     * taking into consideration the current values of the
1069     * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
1070     * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
1071     * and {@link Calendar#getTimeZone() getTimeZone} methods.
1072     *
1073     * @param field the calendar field.
1074     * @return the maximum value for the given calendar field.
1075     * @see #getMinimum(int)
1076     * @see #getGreatestMinimum(int)
1077     * @see #getLeastMaximum(int)
1078     * @see #getActualMinimum(int)
1079     * @see #getActualMaximum(int)
1080     */

1081    public int getMaximum(int field) {
1082    switch (field) {
1083    case YEAR:
1084        {
1085        // The value should depend on the time zone of this calendar.
1086
LocalGregorianCalendar.Date d = jcal.getCalendarDate(Long.MAX_VALUE,
1087                                     getZone());
1088        return Math.max(LEAST_MAX_VALUES[YEAR], d.getYear());
1089        }
1090    }
1091        return MAX_VALUES[field];
1092    }
1093
1094    /**
1095     * Returns the highest minimum value for the given calendar field
1096     * of this <code>GregorianCalendar</code> instance. The highest
1097     * minimum value is defined as the largest value returned by
1098     * {@link #getActualMinimum(int)} for any possible time value,
1099     * taking into consideration the current values of the
1100     * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
1101     * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
1102     * and {@link Calendar#getTimeZone() getTimeZone} methods.
1103     *
1104     * @param field the calendar field.
1105     * @return the highest minimum value for the given calendar field.
1106     * @see #getMinimum(int)
1107     * @see #getMaximum(int)
1108     * @see #getLeastMaximum(int)
1109     * @see #getActualMinimum(int)
1110     * @see #getActualMaximum(int)
1111     */

1112    public int getGreatestMinimum(int field) {
1113        return field == YEAR ? 1 : MIN_VALUES[field];
1114    }
1115
1116    /**
1117     * Returns the lowest maximum value for the given calendar field
1118     * of this <code>GregorianCalendar</code> instance. The lowest
1119     * maximum value is defined as the smallest value returned by
1120     * {@link #getActualMaximum(int)} for any possible time value,
1121     * taking into consideration the current values of the
1122     * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
1123     * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
1124     * and {@link Calendar#getTimeZone() getTimeZone} methods.
1125     *
1126     * @param field the calendar field
1127     * @return the lowest maximum value for the given calendar field.
1128     * @see #getMinimum(int)
1129     * @see #getMaximum(int)
1130     * @see #getGreatestMinimum(int)
1131     * @see #getActualMinimum(int)
1132     * @see #getActualMaximum(int)
1133     */

1134    public int getLeastMaximum(int field) {
1135    switch (field) {
1136    case YEAR:
1137        {
1138        return Math.min(LEAST_MAX_VALUES[YEAR], getMaximum(YEAR));
1139        }
1140    }
1141        return LEAST_MAX_VALUES[field];
1142    }
1143
1144    /**
1145     * Returns the minimum value that this calendar field could have,
1146     * taking into consideration the given time value and the current
1147     * values of the
1148     * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
1149     * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
1150     * and {@link Calendar#getTimeZone() getTimeZone} methods.
1151     *
1152     * @param field the calendar field
1153     * @return the minimum of the given field for the time value of
1154     * this <code>JapaneseImperialCalendar</code>
1155     * @see #getMinimum(int)
1156     * @see #getMaximum(int)
1157     * @see #getGreatestMinimum(int)
1158     * @see #getLeastMaximum(int)
1159     * @see #getActualMaximum(int)
1160     */

1161    public int getActualMinimum(int field) {
1162    if (!isFieldSet(YEAR_MASK|MONTH_MASK|WEEK_OF_YEAR_MASK, field)) {
1163        return getMinimum(field);
1164    }
1165
1166    int value = 0;
1167    JapaneseImperialCalendar JavaDoc jc = getNormalizedCalendar();
1168    // Get a local date which includes time of day and time zone,
1169
// which are missing in jc.jdate.
1170
LocalGregorianCalendar.Date jd = jcal.getCalendarDate(jc.getTimeInMillis(),
1171                                  getZone());
1172    int eraIndex = getEraIndex(jd);
1173    switch (field) {
1174    case YEAR:
1175        {
1176        if (eraIndex > BEFORE_MEIJI) {
1177            value = 1;
1178            long since = eras[eraIndex].getSince(getZone());
1179            CalendarDate d = jcal.getCalendarDate(since, getZone());
1180            // Use the same year in jd to take care of leap
1181
// years. i.e., both jd and d must agree on leap
1182
// or common years.
1183
jd.setYear(d.getYear());
1184            jcal.normalize(jd);
1185            assert jd.isLeapYear() == d.isLeapYear();
1186            if (getYearOffsetInMillis(jd) < getYearOffsetInMillis(d)) {
1187            value++;
1188            }
1189        } else {
1190            value = getMinimum(field);
1191            CalendarDate d = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
1192            // Use an equvalent year of d.getYear() if
1193
// possible. Otherwise, ignore the leap year and
1194
// common year difference.
1195
int y = d.getYear();
1196            if (y > 400) {
1197            y -= 400;
1198            }
1199            jd.setYear(y);
1200            jcal.normalize(jd);
1201            if (getYearOffsetInMillis(jd) < getYearOffsetInMillis(d)) {
1202            value++;
1203            }
1204        }
1205        }
1206        break;
1207
1208    case MONTH:
1209        {
1210        // In Before Meiji and Meiji, January is the first month.
1211
if (eraIndex > MEIJI && jd.getYear() == 1) {
1212            long since = eras[eraIndex].getSince(getZone());
1213            CalendarDate d = jcal.getCalendarDate(since, getZone());
1214            value = d.getMonth() - 1;
1215            if (jd.getDayOfMonth() < d.getDayOfMonth()) {
1216            value++;
1217            }
1218        }
1219        }
1220        break;
1221
1222    case WEEK_OF_YEAR:
1223        {
1224        value = 1;
1225        CalendarDate d = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
1226        // shift 400 years to avoid underflow
1227
d.addYear(+400);
1228        jcal.normalize(d);
1229        jd.setEra(d.getEra());
1230        jd.setYear(d.getYear());
1231        jcal.normalize(jd);
1232
1233        long jan1 = jcal.getFixedDate(d);
1234        long fd = jcal.getFixedDate(jd);
1235        int woy = getWeekNumber(jan1, fd);
1236        long day1 = fd - (7 * (woy - 1));
1237        if ((day1 < jan1) ||
1238            (day1 == jan1 &&
1239             jd.getTimeOfDay() < d.getTimeOfDay())) {
1240            value++;
1241        }
1242        }
1243        break;
1244    }
1245    return value;
1246    }
1247
1248    /**
1249     * Returns the maximum value that this calendar field could have,
1250     * taking into consideration the given time value and the current
1251     * values of the
1252     * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
1253     * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
1254     * and
1255     * {@link Calendar#getTimeZone() getTimeZone} methods.
1256     * For example, if the date of this instance is Heisei 16February 1,
1257     * the actual maximum value of the <code>DAY_OF_MONTH</code> field
1258     * is 29 because Heisei 16 is a leap year, and if the date of this
1259     * instance is Heisei 17 February 1, it's 28.
1260     *
1261     * @param field the calendar field
1262     * @return the maximum of the given field for the time value of
1263     * this <code>JapaneseImperialCalendar</code>
1264     * @see #getMinimum(int)
1265     * @see #getMaximum(int)
1266     * @see #getGreatestMinimum(int)
1267     * @see #getLeastMaximum(int)
1268     * @see #getActualMinimum(int)
1269     */

1270    public int getActualMaximum(int field) {
1271    final int fieldsForFixedMax = ERA_MASK|DAY_OF_WEEK_MASK|HOUR_MASK|AM_PM_MASK|
1272        HOUR_OF_DAY_MASK|MINUTE_MASK|SECOND_MASK|MILLISECOND_MASK|
1273        ZONE_OFFSET_MASK|DST_OFFSET_MASK;
1274    if ((fieldsForFixedMax & (1<<field)) != 0) {
1275        return getMaximum(field);
1276    }
1277
1278    JapaneseImperialCalendar JavaDoc jc = getNormalizedCalendar();
1279    LocalGregorianCalendar.Date date = jc.jdate;
1280    int normalizedYear = date.getNormalizedYear();
1281
1282    int value = -1;
1283        switch (field) {
1284    case MONTH:
1285        {
1286        value = DECEMBER;
1287        if (isTransitionYear(date.getNormalizedYear())) {
1288            // TODO: there may be multiple transitions in a year.
1289
int eraIndex = getEraIndex(date);
1290            if (date.getYear() != 1) {
1291            eraIndex++;
1292            assert eraIndex < eras.length;
1293            }
1294            long transition = sinceFixedDates[eraIndex];
1295            long fd = jc.cachedFixedDate;
1296            if (fd < transition) {
1297            LocalGregorianCalendar.Date ldate
1298                = (LocalGregorianCalendar.Date) date.clone();
1299            jcal.getCalendarDateFromFixedDate(ldate, transition - 1);
1300            value = ldate.getMonth() - 1;
1301            }
1302        } else {
1303            LocalGregorianCalendar.Date d = jcal.getCalendarDate(Long.MAX_VALUE,
1304                                     getZone());
1305            if (date.getEra() == d.getEra() && date.getYear() == d.getYear()) {
1306            value = d.getMonth() - 1;
1307            }
1308        }
1309        }
1310        break;
1311
1312        case DAY_OF_MONTH:
1313        value = jcal.getMonthLength(date);
1314        break;
1315
1316        case DAY_OF_YEAR:
1317        {
1318        if (isTransitionYear(date.getNormalizedYear())) {
1319            // Handle transition year.
1320
// TODO: there may be multiple transitions in a year.
1321
int eraIndex = getEraIndex(date);
1322            if (date.getYear() != 1) {
1323            eraIndex++;
1324            assert eraIndex < eras.length;
1325            }
1326            long transition = sinceFixedDates[eraIndex];
1327            long fd = jc.cachedFixedDate;
1328            CalendarDate d = gcal.newCalendarDate(TimeZone.NO_TIMEZONE);
1329            d.setDate(date.getNormalizedYear(), BaseCalendar.JANUARY, 1);
1330            if (fd < transition) {
1331            value = (int)(transition - gcal.getFixedDate(d));
1332            } else {
1333            d.addYear(+1);
1334            value = (int)(gcal.getFixedDate(d) - transition);
1335            }
1336        } else {
1337            LocalGregorianCalendar.Date d = jcal.getCalendarDate(Long.MAX_VALUE,
1338                                     getZone());
1339            if (date.getEra() == d.getEra() && date.getYear() == d.getYear()) {
1340            long fd = jcal.getFixedDate(d);
1341            long jan1 = getFixedDateJan1(d, fd);
1342            value = (int)(fd - jan1) + 1;
1343            } else if (date.getYear() == getMinimum(YEAR)) {
1344            CalendarDate d1 = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
1345            long fd1 = jcal.getFixedDate(d1);
1346            d1.addYear(1);
1347            d1.setMonth(BaseCalendar.JANUARY).setDayOfMonth(1);
1348            jcal.normalize(d1);
1349            long fd2 = jcal.getFixedDate(d1);
1350            value = (int)(fd2 - fd1);
1351            } else {
1352            value = jcal.getYearLength(date);
1353            }
1354        }
1355        }
1356        break;
1357
1358        case WEEK_OF_YEAR:
1359        {
1360        if (!isTransitionYear(date.getNormalizedYear())) {
1361            LocalGregorianCalendar.Date jd = jcal.getCalendarDate(Long.MAX_VALUE,
1362                                      getZone());
1363            if (date.getEra() == jd.getEra() && date.getYear() == jd.getYear()) {
1364            long fd = jcal.getFixedDate(jd);
1365            long jan1 = getFixedDateJan1(jd, fd);
1366            value = getWeekNumber(jan1, fd);
1367            } else if (date.getEra() == null && date.getYear() == getMinimum(YEAR)) {
1368            CalendarDate d = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
1369            // shift 400 years to avoid underflow
1370
d.addYear(+400);
1371            jcal.normalize(d);
1372            jd.setEra(d.getEra());
1373            jd.setDate(d.getYear() + 1, BaseCalendar.JANUARY, 1);
1374            jcal.normalize(jd);
1375            long jan1 = jcal.getFixedDate(d);
1376            long nextJan1 = jcal.getFixedDate(jd);
1377            long nextJan1st = jcal.getDayOfWeekDateOnOrBefore(nextJan1 + 6,
1378                                      getFirstDayOfWeek());
1379            int ndays = (int)(nextJan1st - nextJan1);
1380            if (ndays >= getMinimalDaysInFirstWeek()) {
1381                nextJan1st -= 7;
1382            }
1383            value = getWeekNumber(jan1, nextJan1st);
1384            } else {
1385            // Get the day of week of January 1 of the year
1386
CalendarDate d = gcal.newCalendarDate(TimeZone.NO_TIMEZONE);
1387            d.setDate(date.getNormalizedYear(), BaseCalendar.JANUARY, 1);
1388            int dayOfWeek = gcal.getDayOfWeek(d);
1389            // Normalize the day of week with the firstDayOfWeek value
1390
dayOfWeek -= getFirstDayOfWeek();
1391            if (dayOfWeek < 0) {
1392                dayOfWeek += 7;
1393            }
1394            value = 52;
1395            int magic = dayOfWeek + getMinimalDaysInFirstWeek() - 1;
1396            if ((magic == 6) ||
1397                (date.isLeapYear() && (magic == 5 || magic == 12))) {
1398                value++;
1399            }
1400            }
1401            break;
1402        }
1403
1404        if (jc == this) {
1405            jc = (JapaneseImperialCalendar JavaDoc) jc.clone();
1406        }
1407        int max = getActualMaximum(DAY_OF_YEAR);
1408        jc.set(DAY_OF_YEAR, max);
1409        value = jc.get(WEEK_OF_YEAR);
1410        if (value == 1 && max > 7) {
1411            jc.add(WEEK_OF_YEAR, -1);
1412            value = jc.get(WEEK_OF_YEAR);
1413        }
1414        }
1415        break;
1416
1417        case WEEK_OF_MONTH:
1418        {
1419        LocalGregorianCalendar.Date jd = jcal.getCalendarDate(Long.MAX_VALUE,
1420                                      getZone());
1421        if (!(date.getEra() == jd.getEra() && date.getYear() == jd.getYear())) {
1422            CalendarDate d = gcal.newCalendarDate(TimeZone.NO_TIMEZONE);
1423            d.setDate(date.getNormalizedYear(), date.getMonth(), 1);
1424            int dayOfWeek = gcal.getDayOfWeek(d);
1425            int monthLength = gcal.getMonthLength(d);
1426            dayOfWeek -= getFirstDayOfWeek();
1427            if (dayOfWeek < 0) {
1428            dayOfWeek += 7;
1429            }
1430            int nDaysFirstWeek = 7 - dayOfWeek; // # of days in the first week
1431
value = 3;
1432            if (nDaysFirstWeek >= getMinimalDaysInFirstWeek()) {
1433            value++;
1434            }
1435            monthLength -= nDaysFirstWeek + 7 * 3;
1436            if (monthLength > 0) {
1437            value++;
1438            if (monthLength > 7) {
1439                value++;
1440            }
1441            }
1442        } else {
1443            long fd = jcal.getFixedDate(jd);
1444            long month1 = fd - jd.getDayOfMonth() + 1;
1445            value = getWeekNumber(month1, fd);
1446        }
1447        }
1448        break;
1449
1450        case DAY_OF_WEEK_IN_MONTH:
1451        {
1452        int ndays, dow1;
1453        int dow = date.getDayOfWeek();
1454        BaseCalendar.Date d = (BaseCalendar.Date) date.clone();
1455        ndays = jcal.getMonthLength(d);
1456        d.setDayOfMonth(1);
1457        jcal.normalize(d);
1458        dow1 = d.getDayOfWeek();
1459        int x = dow - dow1;
1460        if (x < 0) {
1461            x += 7;
1462        }
1463        ndays -= x;
1464        value = (ndays + 6) / 7;
1465        }
1466        break;
1467
1468    case YEAR:
1469        {
1470        CalendarDate jd = jcal.getCalendarDate(jc.getTimeInMillis(), getZone());
1471        CalendarDate d;
1472        int eraIndex = getEraIndex(date);
1473        if (eraIndex == eras.length - 1) {
1474            d = jcal.getCalendarDate(Long.MAX_VALUE, getZone());
1475            value = d.getYear();
1476            // Use an equivalent year for the
1477
// getYearOffsetInMillis call to avoid overflow.
1478
if (value > 400) {
1479            jd.setYear(value - 400);
1480            }
1481        } else {
1482            d = jcal.getCalendarDate(eras[eraIndex + 1].getSince(getZone()) - 1,
1483                         getZone());
1484            value = d.getYear();
1485            // Use the same year as d.getYear() to be
1486
// consistent with leap and common years.
1487
jd.setYear(value);
1488        }
1489        jcal.normalize(jd);
1490        if (getYearOffsetInMillis(jd) > getYearOffsetInMillis(d)) {
1491            value--;
1492        }
1493        }
1494        break;
1495
1496    default:
1497        throw new ArrayIndexOutOfBoundsException JavaDoc(field);
1498    }
1499    return value;
1500    }
1501
1502    /**
1503     * Returns the millisecond offset from the beginning of the
1504     * year. In the year for Long.MIN_VALUE, it's a pseudo value
1505     * beyond the limit. The given CalendarDate object must have been
1506     * normalized before calling this method.
1507     */

1508    private final long getYearOffsetInMillis(CalendarDate date) {
1509    long t = (jcal.getDayOfYear(date) - 1) * ONE_DAY;
1510    return t + date.getTimeOfDay() - date.getZoneOffset();
1511    }
1512
1513    public Object JavaDoc clone() {
1514    JapaneseImperialCalendar JavaDoc other = (JapaneseImperialCalendar JavaDoc) super.clone();
1515
1516    other.jdate = (LocalGregorianCalendar.Date) jdate.clone();
1517    other.originalFields = null;
1518    other.zoneOffsets = null;
1519    return other;
1520    }
1521
1522    public TimeZone JavaDoc getTimeZone() {
1523    TimeZone JavaDoc zone = super.getTimeZone();
1524    // To share the zone by the CalendarDate
1525
jdate.setZone(zone);
1526    return zone;
1527    }
1528
1529    public void setTimeZone(TimeZone JavaDoc zone) {
1530    super.setTimeZone(zone);
1531    // To share the zone by the CalendarDate
1532
jdate.setZone(zone);
1533    }
1534
1535    /**
1536     * The fixed date corresponding to jdate. If the value is
1537     * Long.MIN_VALUE, the fixed date value is unknown.
1538     */

1539    transient private long cachedFixedDate = Long.MIN_VALUE;
1540
1541    /**
1542     * Converts the time value (millisecond offset from the <a
1543     * HREF="Calendar.html#Epoch">Epoch</a>) to calendar field values.
1544     * The time is <em>not</em>
1545     * recomputed first; to recompute the time, then the fields, call the
1546     * <code>complete</code> method.
1547     *
1548     * @see Calendar#complete
1549     */

1550    protected void computeFields() {
1551    int mask = 0;
1552    if (isPartiallyNormalized()) {
1553        // Determine which calendar fields need to be computed.
1554
mask = getSetStateFields();
1555        int fieldMask = ~mask & ALL_FIELDS;
1556        if (fieldMask != 0 || cachedFixedDate == Long.MIN_VALUE) {
1557        mask |= computeFields(fieldMask,
1558                      mask & (ZONE_OFFSET_MASK|DST_OFFSET_MASK));
1559        assert mask == ALL_FIELDS;
1560        }
1561    } else {
1562        // Specify all fields
1563
mask = ALL_FIELDS;
1564        computeFields(mask, 0);
1565    }
1566    // After computing all the fields, set the field state to `COMPUTED'.
1567
setFieldsComputed(mask);
1568    }
1569
1570    /**
1571     * This computeFields implements the conversion from UTC
1572     * (millisecond offset from the Epoch) to calendar
1573     * field values. fieldMask specifies which fields to change the
1574     * setting state to COMPUTED, although all fields are set to
1575     * the correct values. This is required to fix 4685354.
1576     *
1577     * @param fieldMask a bit mask to specify which fields to change
1578     * the setting state.
1579     * @param tzMask a bit mask to specify which time zone offset
1580     * fields to be used for time calculations
1581     * @return a new field mask that indicates what field values have
1582     * actually been set.
1583     */

1584    private int computeFields(int fieldMask, int tzMask) {
1585    int zoneOffset = 0;
1586    TimeZone JavaDoc tz = getZone();
1587    if (zoneOffsets == null) {
1588        zoneOffsets = new int[2];
1589    }
1590    if (tzMask != (ZONE_OFFSET_MASK|DST_OFFSET_MASK)) {
1591        if (tz instanceof ZoneInfo) {
1592        zoneOffset = ((ZoneInfo)tz).getOffsets(time, zoneOffsets);
1593        } else {
1594        zoneOffset = tz.getOffset(time);
1595        zoneOffsets[0] = tz.getRawOffset();
1596        zoneOffsets[1] = zoneOffset - zoneOffsets[0];
1597        }
1598    }
1599    if (tzMask != 0) {
1600        if (isFieldSet(tzMask, ZONE_OFFSET)) {
1601        zoneOffsets[0] = internalGet(ZONE_OFFSET);
1602        }
1603        if (isFieldSet(tzMask, DST_OFFSET)) {
1604        zoneOffsets[1] = internalGet(DST_OFFSET);
1605        }
1606        zoneOffset = zoneOffsets[0] + zoneOffsets[1];
1607    }
1608
1609    // By computing time and zoneOffset separately, we can take
1610
// the wider range of time+zoneOffset than the previous
1611
// implementation.
1612
long fixedDate = zoneOffset / ONE_DAY;
1613    int timeOfDay = zoneOffset % (int)ONE_DAY;
1614    fixedDate += time / ONE_DAY;
1615    timeOfDay += (int) (time % ONE_DAY);
1616    if (timeOfDay >= ONE_DAY) {
1617        timeOfDay -= ONE_DAY;
1618        ++fixedDate;
1619    } else {
1620        while (timeOfDay < 0) {
1621        timeOfDay += ONE_DAY;
1622        --fixedDate;
1623        }
1624    }
1625    fixedDate += EPOCH_OFFSET;
1626
1627    // See if we can use jdate to avoid date calculation.
1628
if (fixedDate != cachedFixedDate || fixedDate < 0) {
1629        jcal.getCalendarDateFromFixedDate(jdate, fixedDate);
1630        cachedFixedDate = fixedDate;
1631    }
1632    int era = getEraIndex(jdate);
1633    int year = jdate.getYear();
1634
1635    // Always set the ERA and YEAR values.
1636
internalSet(ERA, era);
1637    internalSet(YEAR, year);
1638    int mask = fieldMask | (ERA_MASK|YEAR_MASK);
1639
1640    int month = jdate.getMonth() - 1; // 0-based
1641
int dayOfMonth = jdate.getDayOfMonth();
1642
1643    // Set the basic date fields.
1644
if ((fieldMask & (MONTH_MASK|DAY_OF_MONTH_MASK|DAY_OF_WEEK_MASK))
1645        != 0) {
1646        internalSet(MONTH, month);
1647        internalSet(DAY_OF_MONTH, dayOfMonth);
1648        internalSet(DAY_OF_WEEK, jdate.getDayOfWeek());
1649        mask |= MONTH_MASK|DAY_OF_MONTH_MASK|DAY_OF_WEEK_MASK;
1650    }
1651
1652    if ((fieldMask & (HOUR_OF_DAY_MASK|AM_PM_MASK|HOUR_MASK
1653              |MINUTE_MASK|SECOND_MASK|MILLISECOND_MASK)) != 0) {
1654        if (timeOfDay != 0) {
1655        int hours = timeOfDay / ONE_HOUR;
1656        internalSet(HOUR_OF_DAY, hours);
1657        internalSet(AM_PM, hours / 12); // Assume AM == 0
1658
internalSet(HOUR, hours % 12);
1659        int r = timeOfDay % ONE_HOUR;
1660        internalSet(MINUTE, r / ONE_MINUTE);
1661        r %= ONE_MINUTE;
1662        internalSet(SECOND, r / ONE_SECOND);
1663        internalSet(MILLISECOND, r % ONE_SECOND);
1664        } else {
1665        internalSet(HOUR_OF_DAY, 0);
1666        internalSet(AM_PM, AM);
1667        internalSet(HOUR, 0);
1668        internalSet(MINUTE, 0);
1669        internalSet(SECOND, 0);
1670        internalSet(MILLISECOND, 0);
1671        }
1672        mask |= (HOUR_OF_DAY_MASK|AM_PM_MASK|HOUR_MASK
1673             |MINUTE_MASK|SECOND_MASK|MILLISECOND_MASK);
1674    }
1675
1676    if ((fieldMask & (ZONE_OFFSET_MASK|DST_OFFSET_MASK)) != 0) {
1677        internalSet(ZONE_OFFSET, zoneOffsets[0]);
1678        internalSet(DST_OFFSET, zoneOffsets[1]);
1679        mask |= (ZONE_OFFSET_MASK|DST_OFFSET_MASK);
1680    }
1681
1682    if ((fieldMask & (DAY_OF_YEAR_MASK|WEEK_OF_YEAR_MASK
1683              |WEEK_OF_MONTH_MASK|DAY_OF_WEEK_IN_MONTH_MASK)) != 0) {
1684        int normalizedYear = jdate.getNormalizedYear();
1685        // If it's a year of an era transition, we need to handle
1686
// irregular year boundaries.
1687
boolean transitionYear = isTransitionYear(jdate.getNormalizedYear());
1688        int dayOfYear;
1689        long fixedDateJan1;
1690        if (transitionYear) {
1691        fixedDateJan1 = getFixedDateJan1(jdate, fixedDate);
1692        dayOfYear = (int)(fixedDate - fixedDateJan1) + 1;
1693        } else if (normalizedYear == MIN_VALUES[YEAR]) {
1694        CalendarDate dx = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
1695        fixedDateJan1 = jcal.getFixedDate(dx);
1696        dayOfYear = (int)(fixedDate - fixedDateJan1) + 1;
1697        } else {
1698        dayOfYear = (int) jcal.getDayOfYear(jdate);
1699        fixedDateJan1 = fixedDate - dayOfYear + 1;
1700        }
1701        long fixedDateMonth1 = transitionYear ?
1702        getFixedDateMonth1(jdate, fixedDate) : fixedDate - dayOfMonth + 1;
1703
1704        internalSet(DAY_OF_YEAR, dayOfYear);
1705        internalSet(DAY_OF_WEEK_IN_MONTH, (dayOfMonth - 1) / 7 + 1);
1706
1707        int weekOfYear = getWeekNumber(fixedDateJan1, fixedDate);
1708
1709        // The spec is to calculate WEEK_OF_YEAR in the
1710
// ISO8601-style. This creates problems, though.
1711
if (weekOfYear == 0) {
1712        // If the date belongs to the last week of the
1713
// previous year, use the week number of "12/31" of
1714
// the "previous" year. Again, if the previous year is
1715
// a transition year, we need to take care of it.
1716
// Usually the previous day of the first day of a year
1717
// is December 31, which is not always true in the
1718
// Japanese imperial calendar system.
1719
long fixedDec31 = fixedDateJan1 - 1;
1720        long prevJan1;
1721        LocalGregorianCalendar.Date d = getCalendarDate(fixedDec31);
1722        if (!(transitionYear || isTransitionYear(d.getNormalizedYear()))) {
1723            prevJan1 = fixedDateJan1 - 365;
1724            if (d.isLeapYear()) {
1725            --prevJan1;
1726            }
1727        } else if (transitionYear) {
1728            if (jdate.getYear() == 1) {
1729            // As of Heisei (since Meiji) there's no case
1730
// that there are multiple transitions in a
1731
// year. Historically there was such
1732
// case. There might be such case again in the
1733
// future.
1734
if (era > HEISEI) {
1735                CalendarDate pd = eras[era - 1].getSinceDate();
1736                if (normalizedYear == pd.getYear()) {
1737                d.setMonth(pd.getMonth()).setDayOfMonth(pd.getDayOfMonth());
1738                }
1739            } else {
1740                d.setMonth(jcal.JANUARY).setDayOfMonth(1);
1741            }
1742            jcal.normalize(d);
1743            prevJan1 = jcal.getFixedDate(d);
1744            } else {
1745            prevJan1 = fixedDateJan1 - 365;
1746            if (d.isLeapYear()) {
1747                --prevJan1;
1748            }
1749            }
1750        } else {
1751            CalendarDate cd = eras[getEraIndex(jdate)].getSinceDate();
1752            d.setMonth(cd.getMonth()).setDayOfMonth(cd.getDayOfMonth());
1753            jcal.normalize(d);
1754            prevJan1 = jcal.getFixedDate(d);
1755        }
1756        weekOfYear = getWeekNumber(prevJan1, fixedDec31);
1757        } else {
1758        if (!transitionYear) {
1759            // Regular years
1760
if (weekOfYear >= 52) {
1761            long nextJan1 = fixedDateJan1 + 365;
1762            if (jdate.isLeapYear()) {
1763                nextJan1++;
1764            }
1765            long nextJan1st = jcal.getDayOfWeekDateOnOrBefore(nextJan1 + 6,
1766                                      getFirstDayOfWeek());
1767            int ndays = (int)(nextJan1st - nextJan1);
1768            if (ndays >= getMinimalDaysInFirstWeek() && fixedDate >= (nextJan1st - 7)) {
1769                // The first days forms a week in which the date is included.
1770
weekOfYear = 1;
1771            }
1772            }
1773        } else {
1774            LocalGregorianCalendar.Date d = (LocalGregorianCalendar.Date) jdate.clone();
1775            long nextJan1;
1776            if (jdate.getYear() == 1) {
1777            d.addYear(+1);
1778            d.setMonth(jcal.JANUARY).setDayOfMonth(1);
1779            nextJan1 = jcal.getFixedDate(d);
1780            } else {
1781            int nextEraIndex = getEraIndex(d) + 1;
1782            CalendarDate cd = eras[nextEraIndex].getSinceDate();
1783            d.setEra(eras[nextEraIndex]);
1784            d.setDate(1, cd.getMonth(), cd.getDayOfMonth());
1785            jcal.normalize(d);
1786            nextJan1 = jcal.getFixedDate(d);
1787            }
1788            long nextJan1st = jcal.getDayOfWeekDateOnOrBefore(nextJan1 + 6,
1789                                      getFirstDayOfWeek());
1790            int ndays = (int)(nextJan1st - nextJan1);
1791            if (ndays >= getMinimalDaysInFirstWeek() && fixedDate >= (nextJan1st - 7)) {
1792            // The first days forms a week in which the date is included.
1793
weekOfYear = 1;
1794            }
1795        }
1796        }
1797        internalSet(WEEK_OF_YEAR, weekOfYear);
1798        internalSet(WEEK_OF_MONTH, getWeekNumber(fixedDateMonth1, fixedDate));
1799        mask |= (DAY_OF_YEAR_MASK|WEEK_OF_YEAR_MASK|WEEK_OF_MONTH_MASK|DAY_OF_WEEK_IN_MONTH_MASK);
1800    }
1801    return mask;
1802    }
1803
1804    /**
1805     * Returns the number of weeks in a period between fixedDay1 and
1806     * fixedDate. The getFirstDayOfWeek-getMinimalDaysInFirstWeek rule
1807     * is applied to calculate the number of weeks.
1808     *
1809     * @param fixedDay1 the fixed date of the first day of the period
1810     * @param fixedDate the fixed date of the last day of the period
1811     * @return the number of weeks of the given period
1812     */

1813    private final int getWeekNumber(long fixedDay1, long fixedDate) {
1814    // We can always use `jcal' since Julian and Gregorian are the
1815
// same thing for this calculation.
1816
long fixedDay1st = jcal.getDayOfWeekDateOnOrBefore(fixedDay1 + 6,
1817                               getFirstDayOfWeek());
1818    int ndays = (int)(fixedDay1st - fixedDay1);
1819    assert ndays <= 7;
1820    if (ndays >= getMinimalDaysInFirstWeek()) {
1821        fixedDay1st -= 7;
1822    }
1823    int normalizedDayOfPeriod = (int)(fixedDate - fixedDay1st);
1824    if (normalizedDayOfPeriod >= 0) {
1825        return normalizedDayOfPeriod / 7 + 1;
1826    }
1827    return CalendarUtils.floorDivide(normalizedDayOfPeriod, 7) + 1;
1828    }
1829
1830    /**
1831     * Converts calendar field values to the time value (millisecond
1832     * offset from the <a HREF="Calendar.html#Epoch">Epoch</a>).
1833     *
1834     * @exception IllegalArgumentException if any calendar fields are invalid.
1835     */

1836    protected void computeTime() {
1837    // In non-lenient mode, perform brief checking of calendar
1838
// fields which have been set externally. Through this
1839
// checking, the field values are stored in originalFields[]
1840
// to see if any of them are normalized later.
1841
if (!isLenient()) {
1842        if (originalFields == null) {
1843        originalFields = new int[FIELD_COUNT];
1844        }
1845        for (int field = 0; field < FIELD_COUNT; field++) {
1846        int value = internalGet(field);
1847        if (isExternallySet(field)) {
1848            // Quick validation for any out of range values
1849
if (value < getMinimum(field) || value > getMaximum(field)) {
1850            throw new IllegalArgumentException JavaDoc(getFieldName(field));
1851            }
1852        }
1853        originalFields[field] = value;
1854        }
1855    }
1856
1857    // Let the super class determine which calendar fields to be
1858
// used to calculate the time.
1859
int fieldMask = selectFields();
1860
1861        int year;
1862        int era;
1863
1864    if (isSet(ERA)) {
1865        era = internalGet(ERA);
1866        year = isSet(YEAR) ? internalGet(YEAR) : 1;
1867    } else {
1868        if (isSet(YEAR)) {
1869        era = eras.length - 1;
1870        year = internalGet(YEAR);
1871        } else {
1872        // Equivalent to 1970 (Gregorian)
1873
era = SHOWA;
1874        year = 45;
1875        }
1876    }
1877
1878        // Calculate the time of day. We rely on the convention that
1879
// an UNSET field has 0.
1880
long timeOfDay = 0;
1881    if (isFieldSet(fieldMask, HOUR_OF_DAY)) {
1882        timeOfDay += (long) internalGet(HOUR_OF_DAY);
1883    } else {
1884        timeOfDay += internalGet(HOUR);
1885        // The default value of AM_PM is 0 which designates AM.
1886
if (isFieldSet(fieldMask, AM_PM)) {
1887        timeOfDay += 12 * internalGet(AM_PM);
1888        }
1889        }
1890        timeOfDay *= 60;
1891    timeOfDay += internalGet(MINUTE);
1892        timeOfDay *= 60;
1893    timeOfDay += internalGet(SECOND);
1894        timeOfDay *= 1000;
1895    timeOfDay += internalGet(MILLISECOND);
1896
1897    // Convert the time of day to the number of days and the
1898
// millisecond offset from midnight.
1899
long fixedDate = timeOfDay / ONE_DAY;
1900    timeOfDay %= ONE_DAY;
1901    while (timeOfDay < 0) {
1902        timeOfDay += ONE_DAY;
1903        --fixedDate;
1904    }
1905    
1906    // Calculate the fixed date since January 1, 1 (Gregorian).
1907
fixedDate += getFixedDate(era, year, fieldMask);
1908
1909        // millis represents local wall-clock time in milliseconds.
1910
long millis = (fixedDate - EPOCH_OFFSET) * ONE_DAY + timeOfDay;
1911
1912        // Compute the time zone offset and DST offset. There are two potential
1913
// ambiguities here. We'll assume a 2:00 am (wall time) switchover time
1914
// for discussion purposes here.
1915
// 1. The transition into DST. Here, a designated time of 2:00 am - 2:59 am
1916
// can be in standard or in DST depending. However, 2:00 am is an invalid
1917
// representation (the representation jumps from 1:59:59 am Std to 3:00:00 am DST).
1918
// We assume standard time.
1919
// 2. The transition out of DST. Here, a designated time of 1:00 am - 1:59 am
1920
// can be in standard or DST. Both are valid representations (the rep
1921
// jumps from 1:59:59 DST to 1:00:00 Std).
1922
// Again, we assume standard time.
1923
// We use the TimeZone object, unless the user has explicitly set the ZONE_OFFSET
1924
// or DST_OFFSET fields; then we use those fields.
1925
TimeZone JavaDoc zone = getZone();
1926    if (zoneOffsets == null) {
1927        zoneOffsets = new int[2];
1928    }
1929    int tzMask = fieldMask & (ZONE_OFFSET_MASK|DST_OFFSET_MASK);
1930    if (tzMask != (ZONE_OFFSET_MASK|DST_OFFSET_MASK)) {
1931        if (zone instanceof ZoneInfo) {
1932        ((ZoneInfo)zone).getOffsetsByWall(millis, zoneOffsets);
1933        } else {
1934        zone.getOffsets(millis - zone.getRawOffset(), zoneOffsets);
1935        }
1936    }
1937    if (tzMask != 0) {
1938        if (isFieldSet(tzMask, ZONE_OFFSET)) {
1939        zoneOffsets[0] = internalGet(ZONE_OFFSET);
1940        }
1941        if (isFieldSet(tzMask, DST_OFFSET)) {
1942        zoneOffsets[1] = internalGet(DST_OFFSET);
1943        }
1944    }
1945
1946    // Adjust the time zone offset values to get the UTC time.
1947
millis -= zoneOffsets[0] + zoneOffsets[1];
1948
1949    // Set this calendar's time in milliseconds
1950
time = millis;
1951
1952    int mask = computeFields(fieldMask | getSetStateFields(), tzMask);
1953
1954    if (!isLenient()) {
1955        for (int field = 0; field < FIELD_COUNT; field++) {
1956        if (!isExternallySet(field)) {
1957            continue;
1958        }
1959        if (originalFields[field] != internalGet(field)) {
1960            int wrongValue = internalGet(field);
1961            // Restore the original field values
1962
System.arraycopy(originalFields, 0, fields, 0, fields.length);
1963            throw new IllegalArgumentException JavaDoc(getFieldName(field) + "=" + wrongValue
1964                               + ", expected " + originalFields[field]);
1965        }
1966        }
1967    }
1968    setFieldsNormalized(mask);
1969    }
1970
1971    /**
1972     * Computes the fixed date under either the Gregorian or the
1973     * Julian calendar, using the given year and the specified calendar fields.
1974     *
1975     * @param cal the CalendarSystem to be used for the date calculation
1976     * @param year the normalized year number, with 0 indicating the
1977     * year 1 BCE, -1 indicating 2 BCE, etc.
1978     * @param fieldMask the calendar fields to be used for the date calculation
1979     * @return the fixed date
1980     * @see Calendar#selectFields
1981     */

1982    private long getFixedDate(int era, int year, int fieldMask) {
1983    int month = JANUARY;
1984    int firstDayOfMonth = 1;
1985    if (isFieldSet(fieldMask, MONTH)) {
1986            // No need to check if MONTH has been set (no isSet(MONTH)
1987
// call) since its unset value happens to be JANUARY (0).
1988
month = internalGet(MONTH);
1989
1990            // If the month is out of range, adjust it into range.
1991
if (month > DECEMBER) {
1992        year += month / 12;
1993        month %= 12;
1994        } else if (month < JANUARY) {
1995                int[] rem = new int[1];
1996                year += CalendarUtils.floorDivide(month, 12, rem);
1997                month = rem[0];
1998            }
1999    } else {
2000        if (year == 1 && era != 0) {
2001        CalendarDate d = eras[era].getSinceDate();
2002        month = d.getMonth() - 1;
2003        firstDayOfMonth = d.getDayOfMonth();
2004        }
2005    }
2006
2007    // Adjust the base date if year is the minimum value.
2008
if (year == MIN_VALUES[YEAR]) {
2009        CalendarDate dx = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
2010        int m = dx.getMonth() - 1;
2011        if (month < m)
2012        month = m;
2013        if (month == m)
2014        firstDayOfMonth = dx.getDayOfMonth();
2015    }
2016
2017    LocalGregorianCalendar.Date date = jcal.newCalendarDate(TimeZone.NO_TIMEZONE);
2018    date.setEra(era > 0 ? eras[era] : null);
2019    date.setDate(year, month + 1, firstDayOfMonth);
2020    jcal.normalize(date);
2021
2022    // Get the fixed date since Jan 1, 1 (Gregorian). We are on
2023
// the first day of either `month' or January in 'year'.
2024
long fixedDate = jcal.getFixedDate(date);
2025
2026    if (isFieldSet(fieldMask, MONTH)) {
2027        // Month-based calculations
2028
if (isFieldSet(fieldMask, DAY_OF_MONTH)) {
2029        // We are on the "first day" of the month (which may
2030
// not be 1). Just add the offset if DAY_OF_MONTH is
2031
// set. If the isSet call returns false, that means
2032
// DAY_OF_MONTH has been selected just because of the
2033
// selected combination. We don't need to add any
2034
// since the default value is the "first day".
2035
if (isSet(DAY_OF_MONTH)) {
2036            // To avoid underflow with DAY_OF_MONTH-firstDayOfMonth, add
2037
// DAY_OF_MONTH, then subtract firstDayOfMonth.
2038
fixedDate += internalGet(DAY_OF_MONTH);
2039            fixedDate -= firstDayOfMonth;
2040        }
2041            } else {
2042                if (isFieldSet(fieldMask, WEEK_OF_MONTH)) {
2043            long firstDayOfWeek = jcal.getDayOfWeekDateOnOrBefore(fixedDate + 6,
2044                                      getFirstDayOfWeek());
2045                    // If we have enough days in the first week, then
2046
// move to the previous week.
2047
if ((firstDayOfWeek - fixedDate) >= getMinimalDaysInFirstWeek()) {
2048            firstDayOfWeek -= 7;
2049            }
2050            if (isFieldSet(fieldMask, DAY_OF_WEEK)) {
2051            firstDayOfWeek = jcal.getDayOfWeekDateOnOrBefore(firstDayOfWeek + 6,
2052                                     internalGet(DAY_OF_WEEK));
2053            }
2054            // In lenient mode, we treat days of the previous
2055
// months as a part of the specified
2056
// WEEK_OF_MONTH. See 4633646.
2057
fixedDate = firstDayOfWeek + 7 * (internalGet(WEEK_OF_MONTH) - 1);
2058                } else {
2059            int dayOfWeek;
2060            if (isFieldSet(fieldMask, DAY_OF_WEEK)) {
2061            dayOfWeek = internalGet(DAY_OF_WEEK);
2062            } else {
2063            dayOfWeek = getFirstDayOfWeek();
2064            }
2065                    // We are basing this on the day-of-week-in-month. The only
2066
// trickiness occurs if the day-of-week-in-month is
2067
// negative.
2068
int dowim;
2069            if (isFieldSet(fieldMask, DAY_OF_WEEK_IN_MONTH)) {
2070            dowim = internalGet(DAY_OF_WEEK_IN_MONTH);
2071            } else {
2072            dowim = 1;
2073            }
2074            if (dowim >= 0) {
2075            fixedDate = jcal.getDayOfWeekDateOnOrBefore(fixedDate + (7 * dowim) - 1,
2076                                    dayOfWeek);
2077            } else {
2078            // Go to the first day of the next week of
2079
// the specified week boundary.
2080
int lastDate = monthLength(month, year) + (7 * (dowim + 1));
2081            // Then, get the day of week date on or before the last date.
2082
fixedDate = jcal.getDayOfWeekDateOnOrBefore(fixedDate + lastDate - 1,
2083                                    dayOfWeek);
2084                    }
2085                }
2086            }
2087        } else {
2088        // We are on the first day of the year.
2089
if (isFieldSet(fieldMask, DAY_OF_YEAR)) {
2090        if (isTransitionYear(date.getNormalizedYear())) {
2091            fixedDate = getFixedDateJan1(date, fixedDate);
2092        }
2093        // Add the offset, then subtract 1. (Make sure to avoid underflow.)
2094
fixedDate += internalGet(DAY_OF_YEAR);
2095        fixedDate--;
2096            } else {
2097        long firstDayOfWeek = jcal.getDayOfWeekDateOnOrBefore(fixedDate + 6,
2098                                      getFirstDayOfWeek());
2099        // If we have enough days in the first week, then move
2100
// to the previous week.
2101
if ((firstDayOfWeek - fixedDate) >= getMinimalDaysInFirstWeek()) {
2102            firstDayOfWeek -= 7;
2103        }
2104        if (isFieldSet(fieldMask, DAY_OF_WEEK)) {
2105            int dayOfWeek = internalGet(DAY_OF_WEEK);
2106            if (dayOfWeek != getFirstDayOfWeek()) {
2107            firstDayOfWeek = jcal.getDayOfWeekDateOnOrBefore(firstDayOfWeek + 6,
2108                                     dayOfWeek);
2109            }
2110        }
2111        fixedDate = firstDayOfWeek + 7 * ((long)internalGet(WEEK_OF_YEAR) - 1);
2112            }
2113        }
2114        return fixedDate;
2115    }
2116
2117    /**
2118     * Returns the fixed date of the first day of the year (usually
2119     * January 1) before the specified date.
2120     *
2121     * @param date the date for which the first day of the year is
2122     * calculated. The date has to be in the cut-over year.
2123     * @param fixedDate the fixed date representation of the date
2124     */

2125    private final long getFixedDateJan1(LocalGregorianCalendar.Date date, long fixedDate) {
2126    Era era = date.getEra();
2127    if (date.getEra() != null && date.getYear() == 1) {
2128        for (int eraIndex = getEraIndex(date); eraIndex > 0; eraIndex--) {
2129        CalendarDate d = eras[eraIndex].getSinceDate();
2130        long fd = gcal.getFixedDate(d);
2131        // There might be multiple era transitions in a year.
2132
if (fd > fixedDate) {
2133            continue;
2134        }
2135        return fd;
2136        }
2137    }
2138    CalendarDate d = gcal.newCalendarDate(TimeZone.NO_TIMEZONE);
2139    d.setDate(date.getNormalizedYear(), gcal.JANUARY, 1);
2140    return gcal.getFixedDate(d);
2141    }
2142
2143    /**
2144     * Returns the fixed date of the first date of the month (usually
2145     * the 1st of the month) before the specified date.
2146     *
2147     * @param date the date for which the first day of the month is
2148     * calculated. The date must be in the era transition year.
2149     * @param fixedDate the fixed date representation of the date
2150     */

2151    private final long getFixedDateMonth1(LocalGregorianCalendar.Date date,
2152                      long fixedDate) {
2153    int eraIndex = getTransitionEraIndex(date);
2154    if (eraIndex != -1) {
2155        long transition = sinceFixedDates[eraIndex];
2156        // If the given date is on or after the transition date, then
2157
// return the transition date.
2158
if (transition <= fixedDate) {
2159        return transition;
2160        }
2161    }
2162
2163    // Otherwise, we can use the 1st day of the month.
2164
return fixedDate - date.getDayOfMonth() + 1;
2165    }
2166
2167    /**
2168     * Returns a LocalGregorianCalendar.Date produced from the specified fixed date.
2169     *
2170     * @param fd the fixed date
2171     */

2172    private static final LocalGregorianCalendar.Date getCalendarDate(long fd) {
2173    LocalGregorianCalendar.Date d = jcal.newCalendarDate(TimeZone.NO_TIMEZONE);
2174    jcal.getCalendarDateFromFixedDate(d, fd);
2175    return d;
2176    }
2177
2178    /**
2179     * Returns the length of the specified month in the specified
2180     * Gregorian year. The year number must be normalized.
2181     *
2182     * @see #isLeapYear(int)
2183     */

2184    private final int monthLength(int month, int gregorianYear) {
2185        return CalendarUtils.isGregorianLeapYear(gregorianYear) ?
2186        GregorianCalendar.LEAP_MONTH_LENGTH[month] : GregorianCalendar.MONTH_LENGTH[month];
2187    }
2188
2189    /**
2190     * Returns the length of the specified month in the year provided
2191     * by internalGet(YEAR).
2192     *
2193     * @see #isLeapYear(int)
2194     */

2195    private final int monthLength(int month) {
2196    assert jdate.isNormalized();
2197        return jdate.isLeapYear() ?
2198        GregorianCalendar.LEAP_MONTH_LENGTH[month] : GregorianCalendar.MONTH_LENGTH[month];
2199    }
2200
2201    private final int actualMonthLength() {
2202    int length = jcal.getMonthLength(jdate);
2203    int eraIndex = getTransitionEraIndex(jdate);
2204    if (eraIndex == -1) {
2205        long transitionFixedDate = sinceFixedDates[eraIndex];
2206        CalendarDate d = eras[eraIndex].getSinceDate();
2207        if (transitionFixedDate <= cachedFixedDate) {
2208        length -= d.getDayOfMonth() - 1;
2209        } else {
2210        length = d.getDayOfMonth() - 1;
2211        }
2212    }
2213    return length;
2214    }
2215
2216    /**
2217     * Returns the index to the new era if the given date is in a
2218     * transition month. For example, if the give date is Heisei 1
2219     * (1989) January 20, then the era index for Heisei is
2220     * returned. Likewise, if the given date is Showa 64 (1989)
2221     * January 3, then the era index for Heisei is returned. If the
2222     * given date is not in any transition month, then -1 is returned.
2223     */

2224    private static final int getTransitionEraIndex(LocalGregorianCalendar.Date date) {
2225    int eraIndex = getEraIndex(date);
2226    CalendarDate transitionDate = eras[eraIndex].getSinceDate();
2227    if (transitionDate.getYear() == date.getNormalizedYear() &&
2228        transitionDate.getMonth() == date.getMonth()) {
2229        return eraIndex;
2230    }
2231    if (eraIndex < eras.length - 1) {
2232        transitionDate = eras[++eraIndex].getSinceDate();
2233        if (transitionDate.getYear() == date.getNormalizedYear() &&
2234        transitionDate.getMonth() == date.getMonth()) {
2235        return eraIndex;
2236        }
2237    }
2238    return -1;
2239    }
2240
2241    private final boolean isTransitionYear(int normalizedYear) {
2242    for (int i = eras.length - 1; i > 0; i--) {
2243        int transitionYear = eras[i].getSinceDate().getYear();
2244        if (normalizedYear == transitionYear) {
2245        return true;
2246        }
2247        if (normalizedYear > transitionYear) {
2248        break;
2249        }
2250    }
2251    return false;
2252    }
2253
2254    private static final int getEraIndex(LocalGregorianCalendar.Date date) {
2255    Era era = date.getEra();
2256    for (int i = eras.length - 1; i > 0; i--) {
2257        if (eras[i] == era) {
2258        return i;
2259        }
2260    }
2261    return 0;
2262    }
2263
2264    /**
2265     * Returns this object if it's normalized (all fields and time are
2266     * in sync). Otherwise, a cloned object is returned after calling
2267     * complete() in lenient mode.
2268     */

2269    private final JapaneseImperialCalendar JavaDoc getNormalizedCalendar() {
2270    JapaneseImperialCalendar JavaDoc jc;
2271    if (isFullyNormalized()) {
2272        jc = this;
2273    } else {
2274        // Create a clone and normalize the calendar fields
2275
jc = (JapaneseImperialCalendar JavaDoc) this.clone();
2276        jc.setLenient(true);
2277        jc.complete();
2278    }
2279    return jc;
2280    }
2281
2282    /**
2283     * After adjustments such as add(MONTH), add(YEAR), we don't want the
2284     * month to jump around. E.g., we don't want Jan 31 + 1 month to go to Mar
2285     * 3, we want it to go to Feb 28. Adjustments which might run into this
2286     * problem call this method to retain the proper month.
2287     */

2288    private final void pinDayOfMonth(LocalGregorianCalendar.Date date) {
2289    int year = date.getYear();
2290    int dom = date.getDayOfMonth();
2291    if (year != getMinimum(YEAR)) {
2292        date.setDayOfMonth(1);
2293        jcal.normalize(date);
2294        int monthLength = jcal.getMonthLength(date);
2295        if (dom > monthLength) {
2296        date.setDayOfMonth(monthLength);
2297        } else {
2298        date.setDayOfMonth(dom);
2299        }
2300        jcal.normalize(date);
2301    } else {
2302        LocalGregorianCalendar.Date d = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
2303        LocalGregorianCalendar.Date realDate = jcal.getCalendarDate(time, getZone());
2304        long tod = realDate.getTimeOfDay();
2305        // Use an equivalent year.
2306
realDate.addYear(+400);
2307        realDate.setMonth(date.getMonth());
2308        realDate.setDayOfMonth(1);
2309        jcal.normalize(realDate);
2310        int monthLength = jcal.getMonthLength(realDate);
2311        if (dom > monthLength) {
2312        realDate.setDayOfMonth(monthLength);
2313        } else {
2314        if (dom < d.getDayOfMonth()) {
2315            realDate.setDayOfMonth(d.getDayOfMonth());
2316        } else {
2317            realDate.setDayOfMonth(dom);
2318        }
2319        }
2320        if (realDate.getDayOfMonth() == d.getDayOfMonth() && tod < d.getTimeOfDay()) {
2321        realDate.setDayOfMonth(Math.min(dom + 1, monthLength));
2322        }
2323        // restore the year.
2324
date.setDate(year, realDate.getMonth(), realDate.getDayOfMonth());
2325        // Don't normalize date here so as not to cause underflow.
2326
}
2327    }
2328
2329    /**
2330     * Returns the new value after 'roll'ing the specified value and amount.
2331     */

2332    private static final int getRolledValue(int value, int amount, int min, int max) {
2333    assert value >= min && value <= max;
2334    int range = max - min + 1;
2335    amount %= range;
2336    int n = value + amount;
2337    if (n > max) {
2338        n -= range;
2339    } else if (n < min) {
2340        n += range;
2341    }
2342    assert n >= min && n <= max;
2343    return n;
2344    }
2345
2346    /**
2347     * Returns the ERA. We need a special method for this because the
2348     * default ERA is the current era, but a zero (unset) ERA means before Meiji.
2349     */

2350    private final int internalGetEra() {
2351        return isSet(ERA) ? internalGet(ERA) : eras.length - 1;
2352    }
2353
2354    /**
2355     * Updates internal state.
2356     */

2357    private void readObject(ObjectInputStream JavaDoc stream)
2358        throws IOException JavaDoc, ClassNotFoundException JavaDoc {
2359    stream.defaultReadObject();
2360    if (jdate == null) {
2361        jdate = jcal.newCalendarDate(getZone());
2362        cachedFixedDate = Long.MIN_VALUE;
2363    }
2364    }
2365}
2366
Popular Tags