Print this page
rev 5696 : 6336885: RFE: Locale Data Deployment Enhancements
4609153: Provide locale data for Indic locales
5104387: Support for gl_ES locale (galician language)
6337471: desktop/system locale preferences support
7056139: (cal) SPI support for locale-dependent Calendar parameters
7058206: Provide CalendarData SPI for week params and display field value names
7073852: Support multiple scripts for digits and decimal symbols per locale
7079560: [Fmt-Da] Context dependent month names support in SimpleDateFormat
7171324: getAvailableLocales() of locale sensitive services should return the actual availability of locales
7151414: (cal) Support calendar type identification
7168528: LocaleServiceProvider needs to be aware of Locale extensions
7171372: (cal) locale's default Calendar should be created if unknown calendar is specified
Summary: JEP 127: Improve Locale Data Packaging and Adopt Unicode CLDR Data (part 1 w/o packaging changes. by Naoto Sato and Masayoshi Okutsu)
Split |
Close |
Expand all |
Collapse all |
--- old/src/share/classes/java/util/JapaneseImperialCalendar.java
+++ new/src/share/classes/java/util/JapaneseImperialCalendar.java
1 1 /*
2 - * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
2 + * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
3 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 4 *
5 5 * This code is free software; you can redistribute it and/or modify it
6 6 * under the terms of the GNU General Public License version 2 only, as
7 7 * published by the Free Software Foundation. Oracle designates this
8 8 * particular file as subject to the "Classpath" exception as provided
9 9 * by Oracle in the LICENSE file that accompanied this code.
10 10 *
11 11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 14 * version 2 for more details (a copy is included in the LICENSE file that
15 15 * accompanied this code).
16 16 *
17 17 * You should have received a copy of the GNU General Public License version
18 18 * 2 along with this work; if not, write to the Free Software Foundation,
19 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
↓ open down ↓ |
7 lines elided |
↑ open up ↑ |
20 20 *
21 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 22 * or visit www.oracle.com if you need additional information or have any
23 23 * questions.
24 24 */
25 25
26 26 package java.util;
27 27
28 28 import java.io.IOException;
29 29 import java.io.ObjectInputStream;
30 +import sun.util.locale.provider.CalendarDataUtility;
30 31 import sun.util.calendar.BaseCalendar;
31 32 import sun.util.calendar.CalendarDate;
32 33 import sun.util.calendar.CalendarSystem;
33 34 import sun.util.calendar.CalendarUtils;
34 35 import sun.util.calendar.Era;
35 36 import sun.util.calendar.Gregorian;
36 37 import sun.util.calendar.LocalGregorianCalendar;
37 38 import sun.util.calendar.ZoneInfo;
38 -import sun.util.resources.LocaleData;
39 39
40 40 /**
41 41 * <code>JapaneseImperialCalendar</code> implements a Japanese
42 42 * calendar system in which the imperial era-based year numbering is
43 43 * supported from the Meiji era. The following are the eras supported
44 44 * by this calendar system.
45 45 * <pre><tt>
46 46 * ERA value Era name Since (in Gregorian)
47 47 * ------------------------------------------------------
48 48 * 0 N/A N/A
49 49 * 1 Meiji 1868-01-01 midnight local time
50 50 * 2 Taisho 1912-07-30 midnight local time
51 51 * 3 Showa 1926-12-25 midnight local time
52 52 * 4 Heisei 1989-01-08 midnight local time
53 53 * ------------------------------------------------------
54 54 * </tt></pre>
55 55 *
56 56 * <p><code>ERA</code> value 0 specifies the years before Meiji and
57 57 * the Gregorian year values are used. Unlike {@link
58 58 * GregorianCalendar}, the Julian to Gregorian transition is not
59 59 * supported because it doesn't make any sense to the Japanese
60 60 * calendar systems used before Meiji. To represent the years before
61 61 * Gregorian year 1, 0 and negative values are used. The Japanese
62 62 * Imperial rescripts and government decrees don't specify how to deal
63 63 * with time differences for applying the era transitions. This
64 64 * calendar implementation assumes local time for all transitions.
65 65 *
66 66 * @author Masayoshi Okutsu
67 67 * @since 1.6
68 68 */
69 69 class JapaneseImperialCalendar extends Calendar {
70 70 /*
71 71 * Implementation Notes
72 72 *
73 73 * This implementation uses
74 74 * sun.util.calendar.LocalGregorianCalendar to perform most of the
75 75 * calendar calculations. LocalGregorianCalendar is configurable
76 76 * and reads <JRE_HOME>/lib/calendars.properties at the start-up.
77 77 */
78 78
79 79 /**
80 80 * The ERA constant designating the era before Meiji.
81 81 */
82 82 public static final int BEFORE_MEIJI = 0;
83 83
84 84 /**
85 85 * The ERA constant designating the Meiji era.
86 86 */
87 87 public static final int MEIJI = 1;
88 88
89 89 /**
90 90 * The ERA constant designating the Taisho era.
91 91 */
92 92 public static final int TAISHO = 2;
93 93
94 94 /**
95 95 * The ERA constant designating the Showa era.
96 96 */
97 97 public static final int SHOWA = 3;
98 98
99 99 /**
100 100 * The ERA constant designating the Heisei era.
101 101 */
102 102 public static final int HEISEI = 4;
103 103
104 104 private static final int EPOCH_OFFSET = 719163; // Fixed date of January 1, 1970 (Gregorian)
105 105 private static final int EPOCH_YEAR = 1970;
106 106
107 107 // Useful millisecond constants. Although ONE_DAY and ONE_WEEK can fit
108 108 // into ints, they must be longs in order to prevent arithmetic overflow
109 109 // when performing (bug 4173516).
110 110 private static final int ONE_SECOND = 1000;
111 111 private static final int ONE_MINUTE = 60*ONE_SECOND;
112 112 private static final int ONE_HOUR = 60*ONE_MINUTE;
113 113 private static final long ONE_DAY = 24*ONE_HOUR;
114 114 private static final long ONE_WEEK = 7*ONE_DAY;
115 115
116 116 // Reference to the sun.util.calendar.LocalGregorianCalendar instance (singleton).
117 117 private static final LocalGregorianCalendar jcal
118 118 = (LocalGregorianCalendar) CalendarSystem.forName("japanese");
119 119
120 120 // Gregorian calendar instance. This is required because era
121 121 // transition dates are given in Gregorian dates.
122 122 private static final Gregorian gcal = CalendarSystem.getGregorianCalendar();
123 123
124 124 // The Era instance representing "before Meiji".
125 125 private static final Era BEFORE_MEIJI_ERA = new Era("BeforeMeiji", "BM", Long.MIN_VALUE, false);
126 126
127 127 // Imperial eras. The sun.util.calendar.LocalGregorianCalendar
128 128 // doesn't have an Era representing before Meiji, which is
129 129 // inconvenient for a Calendar. So, era[0] is a reference to
130 130 // BEFORE_MEIJI_ERA.
131 131 private static final Era[] eras;
132 132
133 133 // Fixed date of the first date of each era.
134 134 private static final long[] sinceFixedDates;
135 135
136 136 /*
137 137 * <pre>
138 138 * Greatest Least
139 139 * Field name Minimum Minimum Maximum Maximum
140 140 * ---------- ------- ------- ------- -------
141 141 * ERA 0 0 1 1
142 142 * YEAR -292275055 1 ? ?
143 143 * MONTH 0 0 11 11
144 144 * WEEK_OF_YEAR 1 1 52* 53
145 145 * WEEK_OF_MONTH 0 0 4* 6
146 146 * DAY_OF_MONTH 1 1 28* 31
147 147 * DAY_OF_YEAR 1 1 365* 366
148 148 * DAY_OF_WEEK 1 1 7 7
149 149 * DAY_OF_WEEK_IN_MONTH -1 -1 4* 6
150 150 * AM_PM 0 0 1 1
151 151 * HOUR 0 0 11 11
152 152 * HOUR_OF_DAY 0 0 23 23
153 153 * MINUTE 0 0 59 59
154 154 * SECOND 0 0 59 59
155 155 * MILLISECOND 0 0 999 999
156 156 * ZONE_OFFSET -13:00 -13:00 14:00 14:00
157 157 * DST_OFFSET 0:00 0:00 0:20 2:00
158 158 * </pre>
159 159 * *: depends on eras
160 160 */
161 161 static final int MIN_VALUES[] = {
162 162 0, // ERA
163 163 -292275055, // YEAR
164 164 JANUARY, // MONTH
165 165 1, // WEEK_OF_YEAR
166 166 0, // WEEK_OF_MONTH
167 167 1, // DAY_OF_MONTH
168 168 1, // DAY_OF_YEAR
169 169 SUNDAY, // DAY_OF_WEEK
170 170 1, // DAY_OF_WEEK_IN_MONTH
171 171 AM, // AM_PM
172 172 0, // HOUR
173 173 0, // HOUR_OF_DAY
174 174 0, // MINUTE
175 175 0, // SECOND
176 176 0, // MILLISECOND
177 177 -13*ONE_HOUR, // ZONE_OFFSET (UNIX compatibility)
178 178 0 // DST_OFFSET
179 179 };
180 180 static final int LEAST_MAX_VALUES[] = {
181 181 0, // ERA (initialized later)
182 182 0, // YEAR (initialized later)
183 183 JANUARY, // MONTH (Showa 64 ended in January.)
184 184 0, // WEEK_OF_YEAR (Showa 1 has only 6 days which could be 0 weeks.)
185 185 4, // WEEK_OF_MONTH
186 186 28, // DAY_OF_MONTH
187 187 0, // DAY_OF_YEAR (initialized later)
188 188 SATURDAY, // DAY_OF_WEEK
189 189 4, // DAY_OF_WEEK_IN
190 190 PM, // AM_PM
191 191 11, // HOUR
192 192 23, // HOUR_OF_DAY
193 193 59, // MINUTE
194 194 59, // SECOND
195 195 999, // MILLISECOND
196 196 14*ONE_HOUR, // ZONE_OFFSET
197 197 20*ONE_MINUTE // DST_OFFSET (historical least maximum)
198 198 };
199 199 static final int MAX_VALUES[] = {
200 200 0, // ERA
201 201 292278994, // YEAR
202 202 DECEMBER, // MONTH
203 203 53, // WEEK_OF_YEAR
204 204 6, // WEEK_OF_MONTH
205 205 31, // DAY_OF_MONTH
206 206 366, // DAY_OF_YEAR
207 207 SATURDAY, // DAY_OF_WEEK
208 208 6, // DAY_OF_WEEK_IN
209 209 PM, // AM_PM
210 210 11, // HOUR
211 211 23, // HOUR_OF_DAY
212 212 59, // MINUTE
213 213 59, // SECOND
214 214 999, // MILLISECOND
215 215 14*ONE_HOUR, // ZONE_OFFSET
216 216 2*ONE_HOUR // DST_OFFSET (double summer time)
217 217 };
218 218
219 219 // Proclaim serialization compatibility with JDK 1.6
220 220 private static final long serialVersionUID = -3364572813905467929L;
221 221
222 222 static {
223 223 Era[] es = jcal.getEras();
224 224 int length = es.length + 1;
225 225 eras = new Era[length];
226 226 sinceFixedDates = new long[length];
227 227
228 228 // eras[BEFORE_MEIJI] and sinceFixedDate[BEFORE_MEIJI] are the
229 229 // same as Gregorian.
230 230 int index = BEFORE_MEIJI;
231 231 sinceFixedDates[index] = gcal.getFixedDate(BEFORE_MEIJI_ERA.getSinceDate());
232 232 eras[index++] = BEFORE_MEIJI_ERA;
233 233 for (Era e : es) {
234 234 CalendarDate d = e.getSinceDate();
235 235 sinceFixedDates[index] = gcal.getFixedDate(d);
236 236 eras[index++] = e;
237 237 }
238 238
239 239 LEAST_MAX_VALUES[ERA] = MAX_VALUES[ERA] = eras.length - 1;
240 240
241 241 // Calculate the least maximum year and least day of Year
242 242 // values. The following code assumes that there's at most one
243 243 // era transition in a Gregorian year.
244 244 int year = Integer.MAX_VALUE;
245 245 int dayOfYear = Integer.MAX_VALUE;
246 246 CalendarDate date = gcal.newCalendarDate(TimeZone.NO_TIMEZONE);
247 247 for (int i = 1; i < eras.length; i++) {
248 248 long fd = sinceFixedDates[i];
249 249 CalendarDate transitionDate = eras[i].getSinceDate();
250 250 date.setDate(transitionDate.getYear(), BaseCalendar.JANUARY, 1);
251 251 long fdd = gcal.getFixedDate(date);
252 252 dayOfYear = Math.min((int)(fdd - fd), dayOfYear);
253 253 date.setDate(transitionDate.getYear(), BaseCalendar.DECEMBER, 31);
254 254 fdd = gcal.getFixedDate(date) + 1;
255 255 dayOfYear = Math.min((int)(fd - fdd), dayOfYear);
256 256
257 257 LocalGregorianCalendar.Date lgd = getCalendarDate(fd - 1);
258 258 int y = lgd.getYear();
259 259 // Unless the first year starts from January 1, the actual
260 260 // max value could be one year short. For example, if it's
261 261 // Showa 63 January 8, 63 is the actual max value since
262 262 // Showa 64 January 8 doesn't exist.
263 263 if (!(lgd.getMonth() == BaseCalendar.JANUARY && lgd.getDayOfMonth() == 1)) {
264 264 y--;
265 265 }
266 266 year = Math.min(y, year);
267 267 }
268 268 LEAST_MAX_VALUES[YEAR] = year; // Max year could be smaller than this value.
269 269 LEAST_MAX_VALUES[DAY_OF_YEAR] = dayOfYear;
270 270 }
271 271
272 272 /**
273 273 * jdate always has a sun.util.calendar.LocalGregorianCalendar.Date instance to
274 274 * avoid overhead of creating it for each calculation.
275 275 */
276 276 private transient LocalGregorianCalendar.Date jdate;
277 277
278 278 /**
279 279 * Temporary int[2] to get time zone offsets. zoneOffsets[0] gets
280 280 * the GMT offset value and zoneOffsets[1] gets the daylight saving
281 281 * value.
282 282 */
283 283 private transient int[] zoneOffsets;
284 284
285 285 /**
286 286 * Temporary storage for saving original fields[] values in
287 287 * non-lenient mode.
288 288 */
289 289 private transient int[] originalFields;
290 290
291 291 /**
292 292 * Constructs a <code>JapaneseImperialCalendar</code> based on the current time
293 293 * in the given time zone with the given locale.
294 294 *
↓ open down ↓ |
246 lines elided |
↑ open up ↑ |
295 295 * @param zone the given time zone.
296 296 * @param aLocale the given locale.
297 297 */
298 298 JapaneseImperialCalendar(TimeZone zone, Locale aLocale) {
299 299 super(zone, aLocale);
300 300 jdate = jcal.newCalendarDate(zone);
301 301 setTimeInMillis(System.currentTimeMillis());
302 302 }
303 303
304 304 /**
305 + * Returns {@code "japanese"} as the calendar type of this {@code
306 + * JapaneseImperialCalendar}.
307 + *
308 + * @return {@code "japanese"}
309 + */
310 + @Override
311 + public String getCalendarType() {
312 + return "japanese";
313 + }
314 +
315 + /**
305 316 * Compares this <code>JapaneseImperialCalendar</code> to the specified
306 317 * <code>Object</code>. The result is <code>true</code> if and
307 318 * only if the argument is a <code>JapaneseImperialCalendar</code> object
308 319 * that represents the same time value (millisecond offset from
309 320 * the <a href="Calendar.html#Epoch">Epoch</a>) under the same
310 321 * <code>Calendar</code> parameters.
311 322 *
312 323 * @param obj the object to compare with.
313 324 * @return <code>true</code> if this object is equal to <code>obj</code>;
314 325 * <code>false</code> otherwise.
315 326 * @see Calendar#compareTo(Calendar)
316 327 */
317 328 public boolean equals(Object obj) {
318 329 return obj instanceof JapaneseImperialCalendar &&
319 330 super.equals(obj);
320 331 }
321 332
322 333 /**
323 334 * Generates the hash code for this
324 335 * <code>JapaneseImperialCalendar</code> object.
325 336 */
326 337 public int hashCode() {
327 338 return super.hashCode() ^ jdate.hashCode();
328 339 }
329 340
330 341 /**
331 342 * Adds the specified (signed) amount of time to the given calendar field,
332 343 * based on the calendar's rules.
333 344 *
334 345 * <p><em>Add rule 1</em>. The value of <code>field</code>
335 346 * after the call minus the value of <code>field</code> before the
336 347 * call is <code>amount</code>, modulo any overflow that has occurred in
337 348 * <code>field</code>. Overflow occurs when a field value exceeds its
338 349 * range and, as a result, the next larger field is incremented or
339 350 * decremented and the field value is adjusted back into its range.</p>
340 351 *
341 352 * <p><em>Add rule 2</em>. If a smaller field is expected to be
342 353 * invariant, but it is impossible for it to be equal to its
343 354 * prior value because of changes in its minimum or maximum after
344 355 * <code>field</code> is changed, then its value is adjusted to be as close
345 356 * as possible to its expected value. A smaller field represents a
346 357 * smaller unit of time. <code>HOUR</code> is a smaller field than
347 358 * <code>DAY_OF_MONTH</code>. No adjustment is made to smaller fields
348 359 * that are not expected to be invariant. The calendar system
349 360 * determines what fields are expected to be invariant.</p>
350 361 *
351 362 * @param field the calendar field.
352 363 * @param amount the amount of date or time to be added to the field.
353 364 * @exception IllegalArgumentException if <code>field</code> is
354 365 * <code>ZONE_OFFSET</code>, <code>DST_OFFSET</code>, or unknown,
355 366 * or if any calendar fields have out-of-range values in
356 367 * non-lenient mode.
357 368 */
358 369 public void add(int field, int amount) {
359 370 // If amount == 0, do nothing even the given field is out of
360 371 // range. This is tested by JCK.
361 372 if (amount == 0) {
362 373 return; // Do nothing!
363 374 }
364 375
365 376 if (field < 0 || field >= ZONE_OFFSET) {
366 377 throw new IllegalArgumentException();
367 378 }
368 379
369 380 // Sync the time and calendar fields.
370 381 complete();
371 382
372 383 if (field == YEAR) {
373 384 LocalGregorianCalendar.Date d = (LocalGregorianCalendar.Date) jdate.clone();
374 385 d.addYear(amount);
375 386 pinDayOfMonth(d);
376 387 set(ERA, getEraIndex(d));
377 388 set(YEAR, d.getYear());
378 389 set(MONTH, d.getMonth() - 1);
379 390 set(DAY_OF_MONTH, d.getDayOfMonth());
380 391 } else if (field == MONTH) {
381 392 LocalGregorianCalendar.Date d = (LocalGregorianCalendar.Date) jdate.clone();
382 393 d.addMonth(amount);
383 394 pinDayOfMonth(d);
384 395 set(ERA, getEraIndex(d));
385 396 set(YEAR, d.getYear());
386 397 set(MONTH, d.getMonth() - 1);
387 398 set(DAY_OF_MONTH, d.getDayOfMonth());
388 399 } else if (field == ERA) {
389 400 int era = internalGet(ERA) + amount;
390 401 if (era < 0) {
391 402 era = 0;
392 403 } else if (era > eras.length - 1) {
393 404 era = eras.length - 1;
394 405 }
395 406 set(ERA, era);
396 407 } else {
397 408 long delta = amount;
398 409 long timeOfDay = 0;
399 410 switch (field) {
400 411 // Handle the time fields here. Convert the given
401 412 // amount to milliseconds and call setTimeInMillis.
402 413 case HOUR:
403 414 case HOUR_OF_DAY:
404 415 delta *= 60 * 60 * 1000; // hours to milliseconds
405 416 break;
406 417
407 418 case MINUTE:
408 419 delta *= 60 * 1000; // minutes to milliseconds
409 420 break;
410 421
411 422 case SECOND:
412 423 delta *= 1000; // seconds to milliseconds
413 424 break;
414 425
415 426 case MILLISECOND:
416 427 break;
417 428
418 429 // Handle week, day and AM_PM fields which involves
419 430 // time zone offset change adjustment. Convert the
420 431 // given amount to the number of days.
421 432 case WEEK_OF_YEAR:
422 433 case WEEK_OF_MONTH:
423 434 case DAY_OF_WEEK_IN_MONTH:
424 435 delta *= 7;
425 436 break;
426 437
427 438 case DAY_OF_MONTH: // synonym of DATE
428 439 case DAY_OF_YEAR:
429 440 case DAY_OF_WEEK:
430 441 break;
431 442
432 443 case AM_PM:
433 444 // Convert the amount to the number of days (delta)
434 445 // and +12 or -12 hours (timeOfDay).
435 446 delta = amount / 2;
436 447 timeOfDay = 12 * (amount % 2);
437 448 break;
438 449 }
439 450
440 451 // The time fields don't require time zone offset change
441 452 // adjustment.
442 453 if (field >= HOUR) {
443 454 setTimeInMillis(time + delta);
444 455 return;
445 456 }
446 457
447 458 // The rest of the fields (week, day or AM_PM fields)
448 459 // require time zone offset (both GMT and DST) change
449 460 // adjustment.
450 461
451 462 // Translate the current time to the fixed date and time
452 463 // of the day.
453 464 long fd = cachedFixedDate;
454 465 timeOfDay += internalGet(HOUR_OF_DAY);
455 466 timeOfDay *= 60;
456 467 timeOfDay += internalGet(MINUTE);
457 468 timeOfDay *= 60;
458 469 timeOfDay += internalGet(SECOND);
459 470 timeOfDay *= 1000;
460 471 timeOfDay += internalGet(MILLISECOND);
461 472 if (timeOfDay >= ONE_DAY) {
462 473 fd++;
463 474 timeOfDay -= ONE_DAY;
464 475 } else if (timeOfDay < 0) {
465 476 fd--;
466 477 timeOfDay += ONE_DAY;
467 478 }
468 479
469 480 fd += delta; // fd is the expected fixed date after the calculation
470 481 int zoneOffset = internalGet(ZONE_OFFSET) + internalGet(DST_OFFSET);
471 482 setTimeInMillis((fd - EPOCH_OFFSET) * ONE_DAY + timeOfDay - zoneOffset);
472 483 zoneOffset -= internalGet(ZONE_OFFSET) + internalGet(DST_OFFSET);
473 484 // If the time zone offset has changed, then adjust the difference.
474 485 if (zoneOffset != 0) {
475 486 setTimeInMillis(time + zoneOffset);
476 487 long fd2 = cachedFixedDate;
477 488 // If the adjustment has changed the date, then take
478 489 // the previous one.
479 490 if (fd2 != fd) {
480 491 setTimeInMillis(time - zoneOffset);
481 492 }
482 493 }
483 494 }
484 495 }
485 496
486 497 public void roll(int field, boolean up) {
487 498 roll(field, up ? +1 : -1);
488 499 }
489 500
490 501 /**
491 502 * Adds a signed amount to the specified calendar field without changing larger fields.
492 503 * A negative roll amount means to subtract from field without changing
493 504 * larger fields. If the specified amount is 0, this method performs nothing.
494 505 *
495 506 * <p>This method calls {@link #complete()} before adding the
496 507 * amount so that all the calendar fields are normalized. If there
497 508 * is any calendar field having an out-of-range value in non-lenient mode, then an
498 509 * <code>IllegalArgumentException</code> is thrown.
499 510 *
500 511 * @param field the calendar field.
501 512 * @param amount the signed amount to add to <code>field</code>.
502 513 * @exception IllegalArgumentException if <code>field</code> is
503 514 * <code>ZONE_OFFSET</code>, <code>DST_OFFSET</code>, or unknown,
504 515 * or if any calendar fields have out-of-range values in
505 516 * non-lenient mode.
506 517 * @see #roll(int,boolean)
507 518 * @see #add(int,int)
508 519 * @see #set(int,int)
509 520 */
510 521 public void roll(int field, int amount) {
511 522 // If amount == 0, do nothing even the given field is out of
512 523 // range. This is tested by JCK.
513 524 if (amount == 0) {
514 525 return;
515 526 }
516 527
517 528 if (field < 0 || field >= ZONE_OFFSET) {
518 529 throw new IllegalArgumentException();
519 530 }
520 531
521 532 // Sync the time and calendar fields.
522 533 complete();
523 534
524 535 int min = getMinimum(field);
525 536 int max = getMaximum(field);
526 537
527 538 switch (field) {
528 539 case ERA:
529 540 case AM_PM:
530 541 case MINUTE:
531 542 case SECOND:
532 543 case MILLISECOND:
533 544 // These fields are handled simply, since they have fixed
534 545 // minima and maxima. Other fields are complicated, since
535 546 // the range within they must roll varies depending on the
536 547 // date, a time zone and the era transitions.
537 548 break;
538 549
539 550 case HOUR:
540 551 case HOUR_OF_DAY:
541 552 {
542 553 int unit = max + 1; // 12 or 24 hours
543 554 int h = internalGet(field);
544 555 int nh = (h + amount) % unit;
545 556 if (nh < 0) {
546 557 nh += unit;
547 558 }
548 559 time += ONE_HOUR * (nh - h);
549 560
550 561 // The day might have changed, which could happen if
551 562 // the daylight saving time transition brings it to
552 563 // the next day, although it's very unlikely. But we
553 564 // have to make sure not to change the larger fields.
554 565 CalendarDate d = jcal.getCalendarDate(time, getZone());
555 566 if (internalGet(DAY_OF_MONTH) != d.getDayOfMonth()) {
556 567 d.setEra(jdate.getEra());
557 568 d.setDate(internalGet(YEAR),
558 569 internalGet(MONTH) + 1,
559 570 internalGet(DAY_OF_MONTH));
560 571 if (field == HOUR) {
561 572 assert (internalGet(AM_PM) == PM);
562 573 d.addHours(+12); // restore PM
563 574 }
564 575 time = jcal.getTime(d);
565 576 }
566 577 int hourOfDay = d.getHours();
567 578 internalSet(field, hourOfDay % unit);
568 579 if (field == HOUR) {
569 580 internalSet(HOUR_OF_DAY, hourOfDay);
570 581 } else {
571 582 internalSet(AM_PM, hourOfDay / 12);
572 583 internalSet(HOUR, hourOfDay % 12);
573 584 }
574 585
575 586 // Time zone offset and/or daylight saving might have changed.
576 587 int zoneOffset = d.getZoneOffset();
577 588 int saving = d.getDaylightSaving();
578 589 internalSet(ZONE_OFFSET, zoneOffset - saving);
579 590 internalSet(DST_OFFSET, saving);
580 591 return;
581 592 }
582 593
583 594 case YEAR:
584 595 min = getActualMinimum(field);
585 596 max = getActualMaximum(field);
586 597 break;
587 598
588 599 case MONTH:
589 600 // Rolling the month involves both pinning the final value to [0, 11]
590 601 // and adjusting the DAY_OF_MONTH if necessary. We only adjust the
591 602 // DAY_OF_MONTH if, after updating the MONTH field, it is illegal.
592 603 // E.g., <jan31>.roll(MONTH, 1) -> <feb28> or <feb29>.
593 604 {
594 605 if (!isTransitionYear(jdate.getNormalizedYear())) {
595 606 int year = jdate.getYear();
596 607 if (year == getMaximum(YEAR)) {
597 608 CalendarDate jd = jcal.getCalendarDate(time, getZone());
598 609 CalendarDate d = jcal.getCalendarDate(Long.MAX_VALUE, getZone());
599 610 max = d.getMonth() - 1;
600 611 int n = getRolledValue(internalGet(field), amount, min, max);
601 612 if (n == max) {
602 613 // To avoid overflow, use an equivalent year.
603 614 jd.addYear(-400);
604 615 jd.setMonth(n + 1);
605 616 if (jd.getDayOfMonth() > d.getDayOfMonth()) {
606 617 jd.setDayOfMonth(d.getDayOfMonth());
607 618 jcal.normalize(jd);
608 619 }
609 620 if (jd.getDayOfMonth() == d.getDayOfMonth()
610 621 && jd.getTimeOfDay() > d.getTimeOfDay()) {
611 622 jd.setMonth(n + 1);
612 623 jd.setDayOfMonth(d.getDayOfMonth() - 1);
613 624 jcal.normalize(jd);
614 625 // Month may have changed by the normalization.
615 626 n = jd.getMonth() - 1;
616 627 }
617 628 set(DAY_OF_MONTH, jd.getDayOfMonth());
618 629 }
619 630 set(MONTH, n);
620 631 } else if (year == getMinimum(YEAR)) {
621 632 CalendarDate jd = jcal.getCalendarDate(time, getZone());
622 633 CalendarDate d = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
623 634 min = d.getMonth() - 1;
624 635 int n = getRolledValue(internalGet(field), amount, min, max);
625 636 if (n == min) {
626 637 // To avoid underflow, use an equivalent year.
627 638 jd.addYear(+400);
628 639 jd.setMonth(n + 1);
629 640 if (jd.getDayOfMonth() < d.getDayOfMonth()) {
630 641 jd.setDayOfMonth(d.getDayOfMonth());
631 642 jcal.normalize(jd);
632 643 }
633 644 if (jd.getDayOfMonth() == d.getDayOfMonth()
634 645 && jd.getTimeOfDay() < d.getTimeOfDay()) {
635 646 jd.setMonth(n + 1);
636 647 jd.setDayOfMonth(d.getDayOfMonth() + 1);
637 648 jcal.normalize(jd);
638 649 // Month may have changed by the normalization.
639 650 n = jd.getMonth() - 1;
640 651 }
641 652 set(DAY_OF_MONTH, jd.getDayOfMonth());
642 653 }
643 654 set(MONTH, n);
644 655 } else {
645 656 int mon = (internalGet(MONTH) + amount) % 12;
646 657 if (mon < 0) {
647 658 mon += 12;
648 659 }
649 660 set(MONTH, mon);
650 661
651 662 // Keep the day of month in the range. We
652 663 // don't want to spill over into the next
653 664 // month; e.g., we don't want jan31 + 1 mo ->
654 665 // feb31 -> mar3.
655 666 int monthLen = monthLength(mon);
656 667 if (internalGet(DAY_OF_MONTH) > monthLen) {
657 668 set(DAY_OF_MONTH, monthLen);
658 669 }
659 670 }
660 671 } else {
661 672 int eraIndex = getEraIndex(jdate);
662 673 CalendarDate transition = null;
663 674 if (jdate.getYear() == 1) {
664 675 transition = eras[eraIndex].getSinceDate();
665 676 min = transition.getMonth() - 1;
666 677 } else {
667 678 if (eraIndex < eras.length - 1) {
668 679 transition = eras[eraIndex + 1].getSinceDate();
669 680 if (transition.getYear() == jdate.getNormalizedYear()) {
670 681 max = transition.getMonth() - 1;
671 682 if (transition.getDayOfMonth() == 1) {
672 683 max--;
673 684 }
674 685 }
675 686 }
676 687 }
677 688
678 689 if (min == max) {
679 690 // The year has only one month. No need to
680 691 // process further. (Showa Gan-nen (year 1)
681 692 // and the last year have only one month.)
682 693 return;
683 694 }
684 695 int n = getRolledValue(internalGet(field), amount, min, max);
685 696 set(MONTH, n);
686 697 if (n == min) {
687 698 if (!(transition.getMonth() == BaseCalendar.JANUARY
688 699 && transition.getDayOfMonth() == 1)) {
689 700 if (jdate.getDayOfMonth() < transition.getDayOfMonth()) {
690 701 set(DAY_OF_MONTH, transition.getDayOfMonth());
691 702 }
692 703 }
693 704 } else if (n == max && (transition.getMonth() - 1 == n)) {
694 705 int dom = transition.getDayOfMonth();
695 706 if (jdate.getDayOfMonth() >= dom) {
696 707 set(DAY_OF_MONTH, dom - 1);
697 708 }
698 709 }
699 710 }
700 711 return;
701 712 }
702 713
703 714 case WEEK_OF_YEAR:
704 715 {
705 716 int y = jdate.getNormalizedYear();
706 717 max = getActualMaximum(WEEK_OF_YEAR);
707 718 set(DAY_OF_WEEK, internalGet(DAY_OF_WEEK)); // update stamp[field]
708 719 int woy = internalGet(WEEK_OF_YEAR);
709 720 int value = woy + amount;
710 721 if (!isTransitionYear(jdate.getNormalizedYear())) {
711 722 int year = jdate.getYear();
712 723 if (year == getMaximum(YEAR)) {
713 724 max = getActualMaximum(WEEK_OF_YEAR);
714 725 } else if (year == getMinimum(YEAR)) {
715 726 min = getActualMinimum(WEEK_OF_YEAR);
716 727 max = getActualMaximum(WEEK_OF_YEAR);
717 728 if (value > min && value < max) {
718 729 set(WEEK_OF_YEAR, value);
719 730 return;
720 731 }
721 732
722 733 }
723 734 // If the new value is in between min and max
724 735 // (exclusive), then we can use the value.
725 736 if (value > min && value < max) {
726 737 set(WEEK_OF_YEAR, value);
727 738 return;
728 739 }
729 740 long fd = cachedFixedDate;
730 741 // Make sure that the min week has the current DAY_OF_WEEK
731 742 long day1 = fd - (7 * (woy - min));
732 743 if (year != getMinimum(YEAR)) {
733 744 if (gcal.getYearFromFixedDate(day1) != y) {
734 745 min++;
735 746 }
736 747 } else {
737 748 CalendarDate d = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
738 749 if (day1 < jcal.getFixedDate(d)) {
739 750 min++;
740 751 }
741 752 }
742 753
743 754 // Make sure the same thing for the max week
744 755 fd += 7 * (max - internalGet(WEEK_OF_YEAR));
745 756 if (gcal.getYearFromFixedDate(fd) != y) {
746 757 max--;
747 758 }
748 759 break;
749 760 }
750 761
751 762 // Handle transition here.
752 763 long fd = cachedFixedDate;
753 764 long day1 = fd - (7 * (woy - min));
754 765 // Make sure that the min week has the current DAY_OF_WEEK
755 766 LocalGregorianCalendar.Date d = getCalendarDate(day1);
756 767 if (!(d.getEra() == jdate.getEra() && d.getYear() == jdate.getYear())) {
757 768 min++;
758 769 }
759 770
760 771 // Make sure the same thing for the max week
761 772 fd += 7 * (max - woy);
762 773 jcal.getCalendarDateFromFixedDate(d, fd);
763 774 if (!(d.getEra() == jdate.getEra() && d.getYear() == jdate.getYear())) {
764 775 max--;
765 776 }
766 777 // value: the new WEEK_OF_YEAR which must be converted
767 778 // to month and day of month.
768 779 value = getRolledValue(woy, amount, min, max) - 1;
769 780 d = getCalendarDate(day1 + value * 7);
770 781 set(MONTH, d.getMonth() - 1);
771 782 set(DAY_OF_MONTH, d.getDayOfMonth());
772 783 return;
773 784 }
774 785
775 786 case WEEK_OF_MONTH:
776 787 {
777 788 boolean isTransitionYear = isTransitionYear(jdate.getNormalizedYear());
778 789 // dow: relative day of week from the first day of week
779 790 int dow = internalGet(DAY_OF_WEEK) - getFirstDayOfWeek();
780 791 if (dow < 0) {
781 792 dow += 7;
782 793 }
783 794
784 795 long fd = cachedFixedDate;
785 796 long month1; // fixed date of the first day (usually 1) of the month
786 797 int monthLength; // actual month length
787 798 if (isTransitionYear) {
788 799 month1 = getFixedDateMonth1(jdate, fd);
789 800 monthLength = actualMonthLength();
790 801 } else {
791 802 month1 = fd - internalGet(DAY_OF_MONTH) + 1;
792 803 monthLength = jcal.getMonthLength(jdate);
793 804 }
794 805
795 806 // the first day of week of the month.
796 807 long monthDay1st = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(month1 + 6,
797 808 getFirstDayOfWeek());
798 809 // if the week has enough days to form a week, the
799 810 // week starts from the previous month.
800 811 if ((int)(monthDay1st - month1) >= getMinimalDaysInFirstWeek()) {
801 812 monthDay1st -= 7;
802 813 }
803 814 max = getActualMaximum(field);
804 815
805 816 // value: the new WEEK_OF_MONTH value
806 817 int value = getRolledValue(internalGet(field), amount, 1, max) - 1;
807 818
808 819 // nfd: fixed date of the rolled date
809 820 long nfd = monthDay1st + value * 7 + dow;
810 821
811 822 // Unlike WEEK_OF_YEAR, we need to change day of week if the
812 823 // nfd is out of the month.
813 824 if (nfd < month1) {
814 825 nfd = month1;
815 826 } else if (nfd >= (month1 + monthLength)) {
816 827 nfd = month1 + monthLength - 1;
817 828 }
818 829 set(DAY_OF_MONTH, (int)(nfd - month1) + 1);
819 830 return;
820 831 }
821 832
822 833 case DAY_OF_MONTH:
823 834 {
824 835 if (!isTransitionYear(jdate.getNormalizedYear())) {
825 836 max = jcal.getMonthLength(jdate);
826 837 break;
827 838 }
828 839
829 840 // TODO: Need to change the spec to be usable DAY_OF_MONTH rolling...
830 841
831 842 // Transition handling. We can't change year and era
832 843 // values here due to the Calendar roll spec!
833 844 long month1 = getFixedDateMonth1(jdate, cachedFixedDate);
834 845
835 846 // It may not be a regular month. Convert the date and range to
836 847 // the relative values, perform the roll, and
837 848 // convert the result back to the rolled date.
838 849 int value = getRolledValue((int)(cachedFixedDate - month1), amount,
839 850 0, actualMonthLength() - 1);
840 851 LocalGregorianCalendar.Date d = getCalendarDate(month1 + value);
841 852 assert getEraIndex(d) == internalGetEra()
842 853 && d.getYear() == internalGet(YEAR) && d.getMonth()-1 == internalGet(MONTH);
843 854 set(DAY_OF_MONTH, d.getDayOfMonth());
844 855 return;
845 856 }
846 857
847 858 case DAY_OF_YEAR:
848 859 {
849 860 max = getActualMaximum(field);
850 861 if (!isTransitionYear(jdate.getNormalizedYear())) {
851 862 break;
852 863 }
853 864
854 865 // Handle transition. We can't change year and era values
855 866 // here due to the Calendar roll spec.
856 867 int value = getRolledValue(internalGet(DAY_OF_YEAR), amount, min, max);
857 868 long jan0 = cachedFixedDate - internalGet(DAY_OF_YEAR);
858 869 LocalGregorianCalendar.Date d = getCalendarDate(jan0 + value);
859 870 assert getEraIndex(d) == internalGetEra() && d.getYear() == internalGet(YEAR);
860 871 set(MONTH, d.getMonth() - 1);
861 872 set(DAY_OF_MONTH, d.getDayOfMonth());
862 873 return;
863 874 }
864 875
865 876 case DAY_OF_WEEK:
866 877 {
867 878 int normalizedYear = jdate.getNormalizedYear();
868 879 if (!isTransitionYear(normalizedYear) && !isTransitionYear(normalizedYear - 1)) {
869 880 // If the week of year is in the same year, we can
870 881 // just change DAY_OF_WEEK.
871 882 int weekOfYear = internalGet(WEEK_OF_YEAR);
872 883 if (weekOfYear > 1 && weekOfYear < 52) {
873 884 set(WEEK_OF_YEAR, internalGet(WEEK_OF_YEAR));
874 885 max = SATURDAY;
875 886 break;
876 887 }
877 888 }
878 889
879 890 // We need to handle it in a different way around year
880 891 // boundaries and in the transition year. Note that
881 892 // changing era and year values violates the roll
882 893 // rule: not changing larger calendar fields...
883 894 amount %= 7;
884 895 if (amount == 0) {
885 896 return;
886 897 }
887 898 long fd = cachedFixedDate;
888 899 long dowFirst = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(fd, getFirstDayOfWeek());
889 900 fd += amount;
890 901 if (fd < dowFirst) {
891 902 fd += 7;
892 903 } else if (fd >= dowFirst + 7) {
893 904 fd -= 7;
894 905 }
895 906 LocalGregorianCalendar.Date d = getCalendarDate(fd);
896 907 set(ERA, getEraIndex(d));
897 908 set(d.getYear(), d.getMonth() - 1, d.getDayOfMonth());
898 909 return;
899 910 }
900 911
901 912 case DAY_OF_WEEK_IN_MONTH:
902 913 {
903 914 min = 1; // after having normalized, min should be 1.
904 915 if (!isTransitionYear(jdate.getNormalizedYear())) {
905 916 int dom = internalGet(DAY_OF_MONTH);
906 917 int monthLength = jcal.getMonthLength(jdate);
907 918 int lastDays = monthLength % 7;
908 919 max = monthLength / 7;
909 920 int x = (dom - 1) % 7;
910 921 if (x < lastDays) {
911 922 max++;
912 923 }
913 924 set(DAY_OF_WEEK, internalGet(DAY_OF_WEEK));
914 925 break;
915 926 }
916 927
917 928 // Transition year handling.
918 929 long fd = cachedFixedDate;
919 930 long month1 = getFixedDateMonth1(jdate, fd);
920 931 int monthLength = actualMonthLength();
921 932 int lastDays = monthLength % 7;
922 933 max = monthLength / 7;
923 934 int x = (int)(fd - month1) % 7;
924 935 if (x < lastDays) {
925 936 max++;
926 937 }
927 938 int value = getRolledValue(internalGet(field), amount, min, max) - 1;
928 939 fd = month1 + value * 7 + x;
929 940 LocalGregorianCalendar.Date d = getCalendarDate(fd);
930 941 set(DAY_OF_MONTH, d.getDayOfMonth());
931 942 return;
932 943 }
933 944 }
↓ open down ↓ |
619 lines elided |
↑ open up ↑ |
934 945
935 946 set(field, getRolledValue(internalGet(field), amount, min, max));
936 947 }
937 948
938 949 public String getDisplayName(int field, int style, Locale locale) {
939 950 if (!checkDisplayNameParams(field, style, SHORT, LONG, locale,
940 951 ERA_MASK|YEAR_MASK|MONTH_MASK|DAY_OF_WEEK_MASK|AM_PM_MASK)) {
941 952 return null;
942 953 }
943 954
955 + int fieldValue = get(field);
956 +
944 957 // "GanNen" is supported only in the LONG style.
945 958 if (field == YEAR
946 - && (style == SHORT || get(YEAR) != 1 || get(ERA) == 0)) {
959 + && (getBaseStyle(style) == SHORT || fieldValue != 1 || get(ERA) == 0)) {
947 960 return null;
948 961 }
949 962
950 - ResourceBundle rb = LocaleData.getDateFormatData(locale);
951 - String name = null;
952 - String key = getKey(field, style);
953 - if (key != null) {
954 - String[] strings = rb.getStringArray(key);
955 - if (field == YEAR) {
956 - if (strings.length > 0) {
957 - name = strings[0];
958 - }
959 - } else {
960 - int index = get(field);
961 - // If the ERA value is out of range for strings, then
962 - // try to get its name or abbreviation from the Era instance.
963 - if (field == ERA && index >= strings.length && index < eras.length) {
964 - Era era = eras[index];
965 - name = (style == SHORT) ? era.getAbbreviation() : era.getName();
966 - } else {
967 - if (field == DAY_OF_WEEK) {
968 - --index;
969 - }
970 - name = strings[index];
971 - }
972 - }
963 + String name = CalendarDataUtility.retrieveFieldValueName("japanese", field, fieldValue, style, locale);
964 + // If the ERA value is null, then
965 + // try to get its name or abbreviation from the Era instance.
966 + if (name == null && field == ERA && fieldValue < eras.length) {
967 + Era era = eras[fieldValue];
968 + name = (style == SHORT) ? era.getAbbreviation() : era.getName();
973 969 }
974 970 return name;
975 971 }
976 972
977 973 public Map<String,Integer> getDisplayNames(int field, int style, Locale locale) {
978 974 if (!checkDisplayNameParams(field, style, ALL_STYLES, LONG, locale,
979 975 ERA_MASK|YEAR_MASK|MONTH_MASK|DAY_OF_WEEK_MASK|AM_PM_MASK)) {
980 976 return null;
981 977 }
982 -
983 - if (style == ALL_STYLES) {
984 - Map<String,Integer> shortNames = getDisplayNamesImpl(field, SHORT, locale);
985 - if (field == AM_PM) {
986 - return shortNames;
978 + Map<String, Integer> names = CalendarDataUtility.retrieveFieldValueNames("japanese", field, style, locale);
979 + // If strings[] has fewer than eras[], get more names from eras[].
980 + if (field == ERA) {
981 + int size = names.size();
982 + if (style == ALL_STYLES) {
983 + size /= 2; // SHORT and LONG
987 984 }
988 - Map<String,Integer> longNames = getDisplayNamesImpl(field, LONG, locale);
989 - if (shortNames == null) {
990 - return longNames;
991 - }
992 - if (longNames != null) {
993 - shortNames.putAll(longNames);
994 - }
995 - return shortNames;
996 - }
997 -
998 - // SHORT or LONG
999 - return getDisplayNamesImpl(field, style, locale);
1000 - }
1001 -
1002 - private Map<String,Integer> getDisplayNamesImpl(int field, int style, Locale locale) {
1003 - ResourceBundle rb = LocaleData.getDateFormatData(locale);
1004 - String key = getKey(field, style);
1005 - Map<String,Integer> map = new HashMap<>();
1006 - if (key != null) {
1007 - String[] strings = rb.getStringArray(key);
1008 - if (field == YEAR) {
1009 - if (strings.length > 0) {
1010 - map.put(strings[0], 1);
1011 - }
1012 - } else {
1013 - int base = (field == DAY_OF_WEEK) ? 1 : 0;
1014 - for (int i = 0; i < strings.length; i++) {
1015 - map.put(strings[i], base + i);
1016 - }
1017 - // If strings[] has fewer than eras[], get more names from eras[].
1018 - if (field == ERA && strings.length < eras.length) {
1019 - for (int i = strings.length; i < eras.length; i++) {
1020 - Era era = eras[i];
1021 - String name = (style == SHORT) ? era.getAbbreviation() : era.getName();
1022 - map.put(name, i);
985 + if (size < eras.length) {
986 + int baseStyle = getBaseStyle(style);
987 + for (int i = size; i < eras.length; i++) {
988 + Era era = eras[i];
989 + if (baseStyle == ALL_STYLES || baseStyle == SHORT) {
990 + names.put(era.getAbbreviation(), i);
1023 991 }
992 + if (baseStyle == ALL_STYLES || baseStyle == LONG) {
993 + names.put(era.getName(), i);
994 + }
1024 995 }
1025 996 }
1026 997 }
1027 - return map.size() > 0 ? map : null;
998 + return names;
1028 999 }
1029 1000
1030 - private String getKey(int field, int style) {
1031 - String className = JapaneseImperialCalendar.class.getName();
1032 - StringBuilder key = new StringBuilder();
1033 - switch (field) {
1034 - case ERA:
1035 - key.append(className);
1036 - if (style == SHORT) {
1037 - key.append(".short");
1038 - }
1039 - key.append(".Eras");
1040 - break;
1041 -
1042 - case YEAR:
1043 - key.append(className).append(".FirstYear");
1044 - break;
1045 -
1046 - case MONTH:
1047 - key.append(style == SHORT ? "MonthAbbreviations" : "MonthNames");
1048 - break;
1049 -
1050 - case DAY_OF_WEEK:
1051 - key.append(style == SHORT ? "DayAbbreviations" : "DayNames");
1052 - break;
1053 -
1054 - case AM_PM:
1055 - key.append("AmPmMarkers");
1056 - break;
1057 - }
1058 - return key.length() > 0 ? key.toString() : null;
1059 - }
1060 -
1061 1001 /**
1062 1002 * Returns the minimum value for the given calendar field of this
1063 1003 * <code>Calendar</code> instance. The minimum value is
1064 1004 * defined as the smallest value returned by the {@link
1065 1005 * Calendar#get(int) get} method for any possible time value,
1066 1006 * taking into consideration the current values of the
1067 1007 * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
1068 1008 * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
1069 1009 * and {@link Calendar#getTimeZone() getTimeZone} methods.
1070 1010 *
1071 1011 * @param field the calendar field.
1072 1012 * @return the minimum value for the given calendar field.
1073 1013 * @see #getMaximum(int)
1074 1014 * @see #getGreatestMinimum(int)
1075 1015 * @see #getLeastMaximum(int)
1076 1016 * @see #getActualMinimum(int)
1077 1017 * @see #getActualMaximum(int)
1078 1018 */
1079 1019 public int getMinimum(int field) {
1080 1020 return MIN_VALUES[field];
1081 1021 }
1082 1022
1083 1023 /**
1084 1024 * Returns the maximum value for the given calendar field of this
1085 1025 * <code>GregorianCalendar</code> instance. The maximum value is
1086 1026 * defined as the largest value returned by the {@link
1087 1027 * Calendar#get(int) get} method for any possible time value,
1088 1028 * taking into consideration the current values of the
1089 1029 * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
1090 1030 * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
1091 1031 * and {@link Calendar#getTimeZone() getTimeZone} methods.
1092 1032 *
1093 1033 * @param field the calendar field.
1094 1034 * @return the maximum value for the given calendar field.
1095 1035 * @see #getMinimum(int)
1096 1036 * @see #getGreatestMinimum(int)
1097 1037 * @see #getLeastMaximum(int)
1098 1038 * @see #getActualMinimum(int)
1099 1039 * @see #getActualMaximum(int)
1100 1040 */
1101 1041 public int getMaximum(int field) {
1102 1042 switch (field) {
1103 1043 case YEAR:
1104 1044 {
1105 1045 // The value should depend on the time zone of this calendar.
1106 1046 LocalGregorianCalendar.Date d = jcal.getCalendarDate(Long.MAX_VALUE,
1107 1047 getZone());
1108 1048 return Math.max(LEAST_MAX_VALUES[YEAR], d.getYear());
1109 1049 }
1110 1050 }
1111 1051 return MAX_VALUES[field];
1112 1052 }
1113 1053
1114 1054 /**
1115 1055 * Returns the highest minimum value for the given calendar field
1116 1056 * of this <code>GregorianCalendar</code> instance. The highest
1117 1057 * minimum value is defined as the largest value returned by
1118 1058 * {@link #getActualMinimum(int)} for any possible time value,
1119 1059 * taking into consideration the current values of the
1120 1060 * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
1121 1061 * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
1122 1062 * and {@link Calendar#getTimeZone() getTimeZone} methods.
1123 1063 *
1124 1064 * @param field the calendar field.
1125 1065 * @return the highest minimum value for the given calendar field.
1126 1066 * @see #getMinimum(int)
1127 1067 * @see #getMaximum(int)
1128 1068 * @see #getLeastMaximum(int)
1129 1069 * @see #getActualMinimum(int)
1130 1070 * @see #getActualMaximum(int)
1131 1071 */
1132 1072 public int getGreatestMinimum(int field) {
1133 1073 return field == YEAR ? 1 : MIN_VALUES[field];
1134 1074 }
1135 1075
1136 1076 /**
1137 1077 * Returns the lowest maximum value for the given calendar field
1138 1078 * of this <code>GregorianCalendar</code> instance. The lowest
1139 1079 * maximum value is defined as the smallest value returned by
1140 1080 * {@link #getActualMaximum(int)} for any possible time value,
1141 1081 * taking into consideration the current values of the
1142 1082 * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
1143 1083 * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
1144 1084 * and {@link Calendar#getTimeZone() getTimeZone} methods.
1145 1085 *
1146 1086 * @param field the calendar field
1147 1087 * @return the lowest maximum value for the given calendar field.
1148 1088 * @see #getMinimum(int)
1149 1089 * @see #getMaximum(int)
1150 1090 * @see #getGreatestMinimum(int)
1151 1091 * @see #getActualMinimum(int)
1152 1092 * @see #getActualMaximum(int)
1153 1093 */
1154 1094 public int getLeastMaximum(int field) {
1155 1095 switch (field) {
1156 1096 case YEAR:
1157 1097 {
1158 1098 return Math.min(LEAST_MAX_VALUES[YEAR], getMaximum(YEAR));
1159 1099 }
1160 1100 }
1161 1101 return LEAST_MAX_VALUES[field];
1162 1102 }
1163 1103
1164 1104 /**
1165 1105 * Returns the minimum value that this calendar field could have,
1166 1106 * taking into consideration the given time value and the current
1167 1107 * values of the
1168 1108 * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
1169 1109 * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
1170 1110 * and {@link Calendar#getTimeZone() getTimeZone} methods.
1171 1111 *
1172 1112 * @param field the calendar field
1173 1113 * @return the minimum of the given field for the time value of
1174 1114 * this <code>JapaneseImperialCalendar</code>
1175 1115 * @see #getMinimum(int)
1176 1116 * @see #getMaximum(int)
1177 1117 * @see #getGreatestMinimum(int)
1178 1118 * @see #getLeastMaximum(int)
1179 1119 * @see #getActualMaximum(int)
1180 1120 */
1181 1121 public int getActualMinimum(int field) {
1182 1122 if (!isFieldSet(YEAR_MASK|MONTH_MASK|WEEK_OF_YEAR_MASK, field)) {
1183 1123 return getMinimum(field);
1184 1124 }
1185 1125
1186 1126 int value = 0;
1187 1127 JapaneseImperialCalendar jc = getNormalizedCalendar();
1188 1128 // Get a local date which includes time of day and time zone,
1189 1129 // which are missing in jc.jdate.
1190 1130 LocalGregorianCalendar.Date jd = jcal.getCalendarDate(jc.getTimeInMillis(),
1191 1131 getZone());
1192 1132 int eraIndex = getEraIndex(jd);
1193 1133 switch (field) {
1194 1134 case YEAR:
1195 1135 {
1196 1136 if (eraIndex > BEFORE_MEIJI) {
1197 1137 value = 1;
1198 1138 long since = eras[eraIndex].getSince(getZone());
1199 1139 CalendarDate d = jcal.getCalendarDate(since, getZone());
1200 1140 // Use the same year in jd to take care of leap
1201 1141 // years. i.e., both jd and d must agree on leap
1202 1142 // or common years.
1203 1143 jd.setYear(d.getYear());
1204 1144 jcal.normalize(jd);
1205 1145 assert jd.isLeapYear() == d.isLeapYear();
1206 1146 if (getYearOffsetInMillis(jd) < getYearOffsetInMillis(d)) {
1207 1147 value++;
1208 1148 }
1209 1149 } else {
1210 1150 value = getMinimum(field);
1211 1151 CalendarDate d = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
1212 1152 // Use an equvalent year of d.getYear() if
1213 1153 // possible. Otherwise, ignore the leap year and
1214 1154 // common year difference.
1215 1155 int y = d.getYear();
1216 1156 if (y > 400) {
1217 1157 y -= 400;
1218 1158 }
1219 1159 jd.setYear(y);
1220 1160 jcal.normalize(jd);
1221 1161 if (getYearOffsetInMillis(jd) < getYearOffsetInMillis(d)) {
1222 1162 value++;
1223 1163 }
1224 1164 }
1225 1165 }
1226 1166 break;
1227 1167
1228 1168 case MONTH:
1229 1169 {
1230 1170 // In Before Meiji and Meiji, January is the first month.
1231 1171 if (eraIndex > MEIJI && jd.getYear() == 1) {
1232 1172 long since = eras[eraIndex].getSince(getZone());
1233 1173 CalendarDate d = jcal.getCalendarDate(since, getZone());
1234 1174 value = d.getMonth() - 1;
1235 1175 if (jd.getDayOfMonth() < d.getDayOfMonth()) {
1236 1176 value++;
1237 1177 }
1238 1178 }
1239 1179 }
1240 1180 break;
1241 1181
1242 1182 case WEEK_OF_YEAR:
1243 1183 {
1244 1184 value = 1;
1245 1185 CalendarDate d = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
1246 1186 // shift 400 years to avoid underflow
1247 1187 d.addYear(+400);
1248 1188 jcal.normalize(d);
1249 1189 jd.setEra(d.getEra());
1250 1190 jd.setYear(d.getYear());
1251 1191 jcal.normalize(jd);
1252 1192
1253 1193 long jan1 = jcal.getFixedDate(d);
1254 1194 long fd = jcal.getFixedDate(jd);
1255 1195 int woy = getWeekNumber(jan1, fd);
1256 1196 long day1 = fd - (7 * (woy - 1));
1257 1197 if ((day1 < jan1) ||
1258 1198 (day1 == jan1 &&
1259 1199 jd.getTimeOfDay() < d.getTimeOfDay())) {
1260 1200 value++;
1261 1201 }
1262 1202 }
1263 1203 break;
1264 1204 }
1265 1205 return value;
1266 1206 }
1267 1207
1268 1208 /**
1269 1209 * Returns the maximum value that this calendar field could have,
1270 1210 * taking into consideration the given time value and the current
1271 1211 * values of the
1272 1212 * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
1273 1213 * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
1274 1214 * and
1275 1215 * {@link Calendar#getTimeZone() getTimeZone} methods.
1276 1216 * For example, if the date of this instance is Heisei 16February 1,
1277 1217 * the actual maximum value of the <code>DAY_OF_MONTH</code> field
1278 1218 * is 29 because Heisei 16 is a leap year, and if the date of this
1279 1219 * instance is Heisei 17 February 1, it's 28.
1280 1220 *
1281 1221 * @param field the calendar field
1282 1222 * @return the maximum of the given field for the time value of
1283 1223 * this <code>JapaneseImperialCalendar</code>
1284 1224 * @see #getMinimum(int)
1285 1225 * @see #getMaximum(int)
1286 1226 * @see #getGreatestMinimum(int)
1287 1227 * @see #getLeastMaximum(int)
1288 1228 * @see #getActualMinimum(int)
1289 1229 */
1290 1230 public int getActualMaximum(int field) {
1291 1231 final int fieldsForFixedMax = ERA_MASK|DAY_OF_WEEK_MASK|HOUR_MASK|AM_PM_MASK|
1292 1232 HOUR_OF_DAY_MASK|MINUTE_MASK|SECOND_MASK|MILLISECOND_MASK|
1293 1233 ZONE_OFFSET_MASK|DST_OFFSET_MASK;
1294 1234 if ((fieldsForFixedMax & (1<<field)) != 0) {
1295 1235 return getMaximum(field);
1296 1236 }
1297 1237
1298 1238 JapaneseImperialCalendar jc = getNormalizedCalendar();
1299 1239 LocalGregorianCalendar.Date date = jc.jdate;
1300 1240 int normalizedYear = date.getNormalizedYear();
1301 1241
1302 1242 int value = -1;
1303 1243 switch (field) {
1304 1244 case MONTH:
1305 1245 {
1306 1246 value = DECEMBER;
1307 1247 if (isTransitionYear(date.getNormalizedYear())) {
1308 1248 // TODO: there may be multiple transitions in a year.
1309 1249 int eraIndex = getEraIndex(date);
1310 1250 if (date.getYear() != 1) {
1311 1251 eraIndex++;
1312 1252 assert eraIndex < eras.length;
1313 1253 }
1314 1254 long transition = sinceFixedDates[eraIndex];
1315 1255 long fd = jc.cachedFixedDate;
1316 1256 if (fd < transition) {
1317 1257 LocalGregorianCalendar.Date ldate
1318 1258 = (LocalGregorianCalendar.Date) date.clone();
1319 1259 jcal.getCalendarDateFromFixedDate(ldate, transition - 1);
1320 1260 value = ldate.getMonth() - 1;
1321 1261 }
1322 1262 } else {
1323 1263 LocalGregorianCalendar.Date d = jcal.getCalendarDate(Long.MAX_VALUE,
1324 1264 getZone());
1325 1265 if (date.getEra() == d.getEra() && date.getYear() == d.getYear()) {
1326 1266 value = d.getMonth() - 1;
1327 1267 }
1328 1268 }
1329 1269 }
1330 1270 break;
1331 1271
1332 1272 case DAY_OF_MONTH:
1333 1273 value = jcal.getMonthLength(date);
1334 1274 break;
1335 1275
1336 1276 case DAY_OF_YEAR:
1337 1277 {
1338 1278 if (isTransitionYear(date.getNormalizedYear())) {
1339 1279 // Handle transition year.
1340 1280 // TODO: there may be multiple transitions in a year.
1341 1281 int eraIndex = getEraIndex(date);
1342 1282 if (date.getYear() != 1) {
1343 1283 eraIndex++;
1344 1284 assert eraIndex < eras.length;
1345 1285 }
1346 1286 long transition = sinceFixedDates[eraIndex];
1347 1287 long fd = jc.cachedFixedDate;
1348 1288 CalendarDate d = gcal.newCalendarDate(TimeZone.NO_TIMEZONE);
1349 1289 d.setDate(date.getNormalizedYear(), BaseCalendar.JANUARY, 1);
1350 1290 if (fd < transition) {
1351 1291 value = (int)(transition - gcal.getFixedDate(d));
1352 1292 } else {
1353 1293 d.addYear(+1);
1354 1294 value = (int)(gcal.getFixedDate(d) - transition);
1355 1295 }
1356 1296 } else {
1357 1297 LocalGregorianCalendar.Date d = jcal.getCalendarDate(Long.MAX_VALUE,
1358 1298 getZone());
1359 1299 if (date.getEra() == d.getEra() && date.getYear() == d.getYear()) {
1360 1300 long fd = jcal.getFixedDate(d);
1361 1301 long jan1 = getFixedDateJan1(d, fd);
1362 1302 value = (int)(fd - jan1) + 1;
1363 1303 } else if (date.getYear() == getMinimum(YEAR)) {
1364 1304 CalendarDate d1 = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
1365 1305 long fd1 = jcal.getFixedDate(d1);
1366 1306 d1.addYear(1);
1367 1307 d1.setMonth(BaseCalendar.JANUARY).setDayOfMonth(1);
1368 1308 jcal.normalize(d1);
1369 1309 long fd2 = jcal.getFixedDate(d1);
1370 1310 value = (int)(fd2 - fd1);
1371 1311 } else {
1372 1312 value = jcal.getYearLength(date);
1373 1313 }
1374 1314 }
1375 1315 }
1376 1316 break;
1377 1317
1378 1318 case WEEK_OF_YEAR:
1379 1319 {
1380 1320 if (!isTransitionYear(date.getNormalizedYear())) {
1381 1321 LocalGregorianCalendar.Date jd = jcal.getCalendarDate(Long.MAX_VALUE,
1382 1322 getZone());
1383 1323 if (date.getEra() == jd.getEra() && date.getYear() == jd.getYear()) {
1384 1324 long fd = jcal.getFixedDate(jd);
1385 1325 long jan1 = getFixedDateJan1(jd, fd);
1386 1326 value = getWeekNumber(jan1, fd);
1387 1327 } else if (date.getEra() == null && date.getYear() == getMinimum(YEAR)) {
1388 1328 CalendarDate d = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
1389 1329 // shift 400 years to avoid underflow
1390 1330 d.addYear(+400);
1391 1331 jcal.normalize(d);
1392 1332 jd.setEra(d.getEra());
1393 1333 jd.setDate(d.getYear() + 1, BaseCalendar.JANUARY, 1);
1394 1334 jcal.normalize(jd);
1395 1335 long jan1 = jcal.getFixedDate(d);
1396 1336 long nextJan1 = jcal.getFixedDate(jd);
1397 1337 long nextJan1st = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(nextJan1 + 6,
1398 1338 getFirstDayOfWeek());
1399 1339 int ndays = (int)(nextJan1st - nextJan1);
1400 1340 if (ndays >= getMinimalDaysInFirstWeek()) {
1401 1341 nextJan1st -= 7;
1402 1342 }
1403 1343 value = getWeekNumber(jan1, nextJan1st);
1404 1344 } else {
1405 1345 // Get the day of week of January 1 of the year
1406 1346 CalendarDate d = gcal.newCalendarDate(TimeZone.NO_TIMEZONE);
1407 1347 d.setDate(date.getNormalizedYear(), BaseCalendar.JANUARY, 1);
1408 1348 int dayOfWeek = gcal.getDayOfWeek(d);
1409 1349 // Normalize the day of week with the firstDayOfWeek value
1410 1350 dayOfWeek -= getFirstDayOfWeek();
1411 1351 if (dayOfWeek < 0) {
1412 1352 dayOfWeek += 7;
1413 1353 }
1414 1354 value = 52;
1415 1355 int magic = dayOfWeek + getMinimalDaysInFirstWeek() - 1;
1416 1356 if ((magic == 6) ||
1417 1357 (date.isLeapYear() && (magic == 5 || magic == 12))) {
1418 1358 value++;
1419 1359 }
1420 1360 }
1421 1361 break;
1422 1362 }
1423 1363
1424 1364 if (jc == this) {
1425 1365 jc = (JapaneseImperialCalendar) jc.clone();
1426 1366 }
1427 1367 int max = getActualMaximum(DAY_OF_YEAR);
1428 1368 jc.set(DAY_OF_YEAR, max);
1429 1369 value = jc.get(WEEK_OF_YEAR);
1430 1370 if (value == 1 && max > 7) {
1431 1371 jc.add(WEEK_OF_YEAR, -1);
1432 1372 value = jc.get(WEEK_OF_YEAR);
1433 1373 }
1434 1374 }
1435 1375 break;
1436 1376
1437 1377 case WEEK_OF_MONTH:
1438 1378 {
1439 1379 LocalGregorianCalendar.Date jd = jcal.getCalendarDate(Long.MAX_VALUE,
1440 1380 getZone());
1441 1381 if (!(date.getEra() == jd.getEra() && date.getYear() == jd.getYear())) {
1442 1382 CalendarDate d = gcal.newCalendarDate(TimeZone.NO_TIMEZONE);
1443 1383 d.setDate(date.getNormalizedYear(), date.getMonth(), 1);
1444 1384 int dayOfWeek = gcal.getDayOfWeek(d);
1445 1385 int monthLength = gcal.getMonthLength(d);
1446 1386 dayOfWeek -= getFirstDayOfWeek();
1447 1387 if (dayOfWeek < 0) {
1448 1388 dayOfWeek += 7;
1449 1389 }
1450 1390 int nDaysFirstWeek = 7 - dayOfWeek; // # of days in the first week
1451 1391 value = 3;
1452 1392 if (nDaysFirstWeek >= getMinimalDaysInFirstWeek()) {
1453 1393 value++;
1454 1394 }
1455 1395 monthLength -= nDaysFirstWeek + 7 * 3;
1456 1396 if (monthLength > 0) {
1457 1397 value++;
1458 1398 if (monthLength > 7) {
1459 1399 value++;
1460 1400 }
1461 1401 }
1462 1402 } else {
1463 1403 long fd = jcal.getFixedDate(jd);
1464 1404 long month1 = fd - jd.getDayOfMonth() + 1;
1465 1405 value = getWeekNumber(month1, fd);
1466 1406 }
1467 1407 }
1468 1408 break;
1469 1409
1470 1410 case DAY_OF_WEEK_IN_MONTH:
1471 1411 {
1472 1412 int ndays, dow1;
1473 1413 int dow = date.getDayOfWeek();
1474 1414 BaseCalendar.Date d = (BaseCalendar.Date) date.clone();
1475 1415 ndays = jcal.getMonthLength(d);
1476 1416 d.setDayOfMonth(1);
1477 1417 jcal.normalize(d);
1478 1418 dow1 = d.getDayOfWeek();
1479 1419 int x = dow - dow1;
1480 1420 if (x < 0) {
1481 1421 x += 7;
1482 1422 }
1483 1423 ndays -= x;
1484 1424 value = (ndays + 6) / 7;
1485 1425 }
1486 1426 break;
1487 1427
1488 1428 case YEAR:
1489 1429 {
1490 1430 CalendarDate jd = jcal.getCalendarDate(jc.getTimeInMillis(), getZone());
1491 1431 CalendarDate d;
1492 1432 int eraIndex = getEraIndex(date);
1493 1433 if (eraIndex == eras.length - 1) {
1494 1434 d = jcal.getCalendarDate(Long.MAX_VALUE, getZone());
1495 1435 value = d.getYear();
1496 1436 // Use an equivalent year for the
1497 1437 // getYearOffsetInMillis call to avoid overflow.
1498 1438 if (value > 400) {
1499 1439 jd.setYear(value - 400);
1500 1440 }
1501 1441 } else {
1502 1442 d = jcal.getCalendarDate(eras[eraIndex + 1].getSince(getZone()) - 1,
1503 1443 getZone());
1504 1444 value = d.getYear();
1505 1445 // Use the same year as d.getYear() to be
1506 1446 // consistent with leap and common years.
1507 1447 jd.setYear(value);
1508 1448 }
1509 1449 jcal.normalize(jd);
1510 1450 if (getYearOffsetInMillis(jd) > getYearOffsetInMillis(d)) {
1511 1451 value--;
1512 1452 }
1513 1453 }
1514 1454 break;
1515 1455
1516 1456 default:
1517 1457 throw new ArrayIndexOutOfBoundsException(field);
1518 1458 }
1519 1459 return value;
1520 1460 }
1521 1461
1522 1462 /**
1523 1463 * Returns the millisecond offset from the beginning of the
1524 1464 * year. In the year for Long.MIN_VALUE, it's a pseudo value
1525 1465 * beyond the limit. The given CalendarDate object must have been
1526 1466 * normalized before calling this method.
1527 1467 */
1528 1468 private long getYearOffsetInMillis(CalendarDate date) {
1529 1469 long t = (jcal.getDayOfYear(date) - 1) * ONE_DAY;
1530 1470 return t + date.getTimeOfDay() - date.getZoneOffset();
1531 1471 }
1532 1472
1533 1473 public Object clone() {
1534 1474 JapaneseImperialCalendar other = (JapaneseImperialCalendar) super.clone();
1535 1475
1536 1476 other.jdate = (LocalGregorianCalendar.Date) jdate.clone();
1537 1477 other.originalFields = null;
1538 1478 other.zoneOffsets = null;
1539 1479 return other;
1540 1480 }
1541 1481
1542 1482 public TimeZone getTimeZone() {
1543 1483 TimeZone zone = super.getTimeZone();
1544 1484 // To share the zone by the CalendarDate
1545 1485 jdate.setZone(zone);
1546 1486 return zone;
1547 1487 }
1548 1488
1549 1489 public void setTimeZone(TimeZone zone) {
1550 1490 super.setTimeZone(zone);
1551 1491 // To share the zone by the CalendarDate
1552 1492 jdate.setZone(zone);
1553 1493 }
1554 1494
1555 1495 /**
1556 1496 * The fixed date corresponding to jdate. If the value is
1557 1497 * Long.MIN_VALUE, the fixed date value is unknown.
1558 1498 */
1559 1499 transient private long cachedFixedDate = Long.MIN_VALUE;
1560 1500
1561 1501 /**
1562 1502 * Converts the time value (millisecond offset from the <a
1563 1503 * href="Calendar.html#Epoch">Epoch</a>) to calendar field values.
1564 1504 * The time is <em>not</em>
1565 1505 * recomputed first; to recompute the time, then the fields, call the
1566 1506 * <code>complete</code> method.
1567 1507 *
1568 1508 * @see Calendar#complete
1569 1509 */
1570 1510 protected void computeFields() {
1571 1511 int mask = 0;
1572 1512 if (isPartiallyNormalized()) {
1573 1513 // Determine which calendar fields need to be computed.
1574 1514 mask = getSetStateFields();
1575 1515 int fieldMask = ~mask & ALL_FIELDS;
1576 1516 if (fieldMask != 0 || cachedFixedDate == Long.MIN_VALUE) {
1577 1517 mask |= computeFields(fieldMask,
1578 1518 mask & (ZONE_OFFSET_MASK|DST_OFFSET_MASK));
1579 1519 assert mask == ALL_FIELDS;
1580 1520 }
1581 1521 } else {
1582 1522 // Specify all fields
1583 1523 mask = ALL_FIELDS;
1584 1524 computeFields(mask, 0);
1585 1525 }
1586 1526 // After computing all the fields, set the field state to `COMPUTED'.
1587 1527 setFieldsComputed(mask);
1588 1528 }
1589 1529
1590 1530 /**
1591 1531 * This computeFields implements the conversion from UTC
1592 1532 * (millisecond offset from the Epoch) to calendar
1593 1533 * field values. fieldMask specifies which fields to change the
1594 1534 * setting state to COMPUTED, although all fields are set to
1595 1535 * the correct values. This is required to fix 4685354.
1596 1536 *
1597 1537 * @param fieldMask a bit mask to specify which fields to change
1598 1538 * the setting state.
1599 1539 * @param tzMask a bit mask to specify which time zone offset
1600 1540 * fields to be used for time calculations
1601 1541 * @return a new field mask that indicates what field values have
1602 1542 * actually been set.
1603 1543 */
1604 1544 private int computeFields(int fieldMask, int tzMask) {
1605 1545 int zoneOffset = 0;
1606 1546 TimeZone tz = getZone();
1607 1547 if (zoneOffsets == null) {
1608 1548 zoneOffsets = new int[2];
1609 1549 }
1610 1550 if (tzMask != (ZONE_OFFSET_MASK|DST_OFFSET_MASK)) {
1611 1551 if (tz instanceof ZoneInfo) {
1612 1552 zoneOffset = ((ZoneInfo)tz).getOffsets(time, zoneOffsets);
1613 1553 } else {
1614 1554 zoneOffset = tz.getOffset(time);
1615 1555 zoneOffsets[0] = tz.getRawOffset();
1616 1556 zoneOffsets[1] = zoneOffset - zoneOffsets[0];
1617 1557 }
1618 1558 }
1619 1559 if (tzMask != 0) {
1620 1560 if (isFieldSet(tzMask, ZONE_OFFSET)) {
1621 1561 zoneOffsets[0] = internalGet(ZONE_OFFSET);
1622 1562 }
1623 1563 if (isFieldSet(tzMask, DST_OFFSET)) {
1624 1564 zoneOffsets[1] = internalGet(DST_OFFSET);
1625 1565 }
1626 1566 zoneOffset = zoneOffsets[0] + zoneOffsets[1];
1627 1567 }
1628 1568
1629 1569 // By computing time and zoneOffset separately, we can take
1630 1570 // the wider range of time+zoneOffset than the previous
1631 1571 // implementation.
1632 1572 long fixedDate = zoneOffset / ONE_DAY;
1633 1573 int timeOfDay = zoneOffset % (int)ONE_DAY;
1634 1574 fixedDate += time / ONE_DAY;
1635 1575 timeOfDay += (int) (time % ONE_DAY);
1636 1576 if (timeOfDay >= ONE_DAY) {
1637 1577 timeOfDay -= ONE_DAY;
1638 1578 ++fixedDate;
1639 1579 } else {
1640 1580 while (timeOfDay < 0) {
1641 1581 timeOfDay += ONE_DAY;
1642 1582 --fixedDate;
1643 1583 }
1644 1584 }
1645 1585 fixedDate += EPOCH_OFFSET;
1646 1586
1647 1587 // See if we can use jdate to avoid date calculation.
1648 1588 if (fixedDate != cachedFixedDate || fixedDate < 0) {
1649 1589 jcal.getCalendarDateFromFixedDate(jdate, fixedDate);
1650 1590 cachedFixedDate = fixedDate;
1651 1591 }
1652 1592 int era = getEraIndex(jdate);
1653 1593 int year = jdate.getYear();
1654 1594
1655 1595 // Always set the ERA and YEAR values.
1656 1596 internalSet(ERA, era);
1657 1597 internalSet(YEAR, year);
1658 1598 int mask = fieldMask | (ERA_MASK|YEAR_MASK);
1659 1599
1660 1600 int month = jdate.getMonth() - 1; // 0-based
1661 1601 int dayOfMonth = jdate.getDayOfMonth();
1662 1602
1663 1603 // Set the basic date fields.
1664 1604 if ((fieldMask & (MONTH_MASK|DAY_OF_MONTH_MASK|DAY_OF_WEEK_MASK))
1665 1605 != 0) {
1666 1606 internalSet(MONTH, month);
1667 1607 internalSet(DAY_OF_MONTH, dayOfMonth);
1668 1608 internalSet(DAY_OF_WEEK, jdate.getDayOfWeek());
1669 1609 mask |= MONTH_MASK|DAY_OF_MONTH_MASK|DAY_OF_WEEK_MASK;
1670 1610 }
1671 1611
1672 1612 if ((fieldMask & (HOUR_OF_DAY_MASK|AM_PM_MASK|HOUR_MASK
1673 1613 |MINUTE_MASK|SECOND_MASK|MILLISECOND_MASK)) != 0) {
1674 1614 if (timeOfDay != 0) {
1675 1615 int hours = timeOfDay / ONE_HOUR;
1676 1616 internalSet(HOUR_OF_DAY, hours);
1677 1617 internalSet(AM_PM, hours / 12); // Assume AM == 0
1678 1618 internalSet(HOUR, hours % 12);
1679 1619 int r = timeOfDay % ONE_HOUR;
1680 1620 internalSet(MINUTE, r / ONE_MINUTE);
1681 1621 r %= ONE_MINUTE;
1682 1622 internalSet(SECOND, r / ONE_SECOND);
1683 1623 internalSet(MILLISECOND, r % ONE_SECOND);
1684 1624 } else {
1685 1625 internalSet(HOUR_OF_DAY, 0);
1686 1626 internalSet(AM_PM, AM);
1687 1627 internalSet(HOUR, 0);
1688 1628 internalSet(MINUTE, 0);
1689 1629 internalSet(SECOND, 0);
1690 1630 internalSet(MILLISECOND, 0);
1691 1631 }
1692 1632 mask |= (HOUR_OF_DAY_MASK|AM_PM_MASK|HOUR_MASK
1693 1633 |MINUTE_MASK|SECOND_MASK|MILLISECOND_MASK);
1694 1634 }
1695 1635
1696 1636 if ((fieldMask & (ZONE_OFFSET_MASK|DST_OFFSET_MASK)) != 0) {
1697 1637 internalSet(ZONE_OFFSET, zoneOffsets[0]);
1698 1638 internalSet(DST_OFFSET, zoneOffsets[1]);
1699 1639 mask |= (ZONE_OFFSET_MASK|DST_OFFSET_MASK);
1700 1640 }
1701 1641
1702 1642 if ((fieldMask & (DAY_OF_YEAR_MASK|WEEK_OF_YEAR_MASK
1703 1643 |WEEK_OF_MONTH_MASK|DAY_OF_WEEK_IN_MONTH_MASK)) != 0) {
1704 1644 int normalizedYear = jdate.getNormalizedYear();
1705 1645 // If it's a year of an era transition, we need to handle
1706 1646 // irregular year boundaries.
1707 1647 boolean transitionYear = isTransitionYear(jdate.getNormalizedYear());
1708 1648 int dayOfYear;
1709 1649 long fixedDateJan1;
1710 1650 if (transitionYear) {
1711 1651 fixedDateJan1 = getFixedDateJan1(jdate, fixedDate);
1712 1652 dayOfYear = (int)(fixedDate - fixedDateJan1) + 1;
1713 1653 } else if (normalizedYear == MIN_VALUES[YEAR]) {
1714 1654 CalendarDate dx = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
1715 1655 fixedDateJan1 = jcal.getFixedDate(dx);
1716 1656 dayOfYear = (int)(fixedDate - fixedDateJan1) + 1;
1717 1657 } else {
1718 1658 dayOfYear = (int) jcal.getDayOfYear(jdate);
1719 1659 fixedDateJan1 = fixedDate - dayOfYear + 1;
1720 1660 }
1721 1661 long fixedDateMonth1 = transitionYear ?
1722 1662 getFixedDateMonth1(jdate, fixedDate) : fixedDate - dayOfMonth + 1;
1723 1663
1724 1664 internalSet(DAY_OF_YEAR, dayOfYear);
1725 1665 internalSet(DAY_OF_WEEK_IN_MONTH, (dayOfMonth - 1) / 7 + 1);
1726 1666
1727 1667 int weekOfYear = getWeekNumber(fixedDateJan1, fixedDate);
1728 1668
1729 1669 // The spec is to calculate WEEK_OF_YEAR in the
1730 1670 // ISO8601-style. This creates problems, though.
1731 1671 if (weekOfYear == 0) {
1732 1672 // If the date belongs to the last week of the
1733 1673 // previous year, use the week number of "12/31" of
1734 1674 // the "previous" year. Again, if the previous year is
1735 1675 // a transition year, we need to take care of it.
1736 1676 // Usually the previous day of the first day of a year
1737 1677 // is December 31, which is not always true in the
1738 1678 // Japanese imperial calendar system.
1739 1679 long fixedDec31 = fixedDateJan1 - 1;
1740 1680 long prevJan1;
1741 1681 LocalGregorianCalendar.Date d = getCalendarDate(fixedDec31);
1742 1682 if (!(transitionYear || isTransitionYear(d.getNormalizedYear()))) {
1743 1683 prevJan1 = fixedDateJan1 - 365;
1744 1684 if (d.isLeapYear()) {
1745 1685 --prevJan1;
1746 1686 }
1747 1687 } else if (transitionYear) {
1748 1688 if (jdate.getYear() == 1) {
1749 1689 // As of Heisei (since Meiji) there's no case
1750 1690 // that there are multiple transitions in a
1751 1691 // year. Historically there was such
1752 1692 // case. There might be such case again in the
1753 1693 // future.
1754 1694 if (era > HEISEI) {
1755 1695 CalendarDate pd = eras[era - 1].getSinceDate();
1756 1696 if (normalizedYear == pd.getYear()) {
1757 1697 d.setMonth(pd.getMonth()).setDayOfMonth(pd.getDayOfMonth());
1758 1698 }
1759 1699 } else {
1760 1700 d.setMonth(LocalGregorianCalendar.JANUARY).setDayOfMonth(1);
1761 1701 }
1762 1702 jcal.normalize(d);
1763 1703 prevJan1 = jcal.getFixedDate(d);
1764 1704 } else {
1765 1705 prevJan1 = fixedDateJan1 - 365;
1766 1706 if (d.isLeapYear()) {
1767 1707 --prevJan1;
1768 1708 }
1769 1709 }
1770 1710 } else {
1771 1711 CalendarDate cd = eras[getEraIndex(jdate)].getSinceDate();
1772 1712 d.setMonth(cd.getMonth()).setDayOfMonth(cd.getDayOfMonth());
1773 1713 jcal.normalize(d);
1774 1714 prevJan1 = jcal.getFixedDate(d);
1775 1715 }
1776 1716 weekOfYear = getWeekNumber(prevJan1, fixedDec31);
1777 1717 } else {
1778 1718 if (!transitionYear) {
1779 1719 // Regular years
1780 1720 if (weekOfYear >= 52) {
1781 1721 long nextJan1 = fixedDateJan1 + 365;
1782 1722 if (jdate.isLeapYear()) {
1783 1723 nextJan1++;
1784 1724 }
1785 1725 long nextJan1st = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(nextJan1 + 6,
1786 1726 getFirstDayOfWeek());
1787 1727 int ndays = (int)(nextJan1st - nextJan1);
1788 1728 if (ndays >= getMinimalDaysInFirstWeek() && fixedDate >= (nextJan1st - 7)) {
1789 1729 // The first days forms a week in which the date is included.
1790 1730 weekOfYear = 1;
1791 1731 }
1792 1732 }
1793 1733 } else {
1794 1734 LocalGregorianCalendar.Date d = (LocalGregorianCalendar.Date) jdate.clone();
1795 1735 long nextJan1;
1796 1736 if (jdate.getYear() == 1) {
1797 1737 d.addYear(+1);
1798 1738 d.setMonth(LocalGregorianCalendar.JANUARY).setDayOfMonth(1);
1799 1739 nextJan1 = jcal.getFixedDate(d);
1800 1740 } else {
1801 1741 int nextEraIndex = getEraIndex(d) + 1;
1802 1742 CalendarDate cd = eras[nextEraIndex].getSinceDate();
1803 1743 d.setEra(eras[nextEraIndex]);
1804 1744 d.setDate(1, cd.getMonth(), cd.getDayOfMonth());
1805 1745 jcal.normalize(d);
1806 1746 nextJan1 = jcal.getFixedDate(d);
1807 1747 }
1808 1748 long nextJan1st = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(nextJan1 + 6,
1809 1749 getFirstDayOfWeek());
1810 1750 int ndays = (int)(nextJan1st - nextJan1);
1811 1751 if (ndays >= getMinimalDaysInFirstWeek() && fixedDate >= (nextJan1st - 7)) {
1812 1752 // The first days forms a week in which the date is included.
1813 1753 weekOfYear = 1;
1814 1754 }
1815 1755 }
1816 1756 }
1817 1757 internalSet(WEEK_OF_YEAR, weekOfYear);
1818 1758 internalSet(WEEK_OF_MONTH, getWeekNumber(fixedDateMonth1, fixedDate));
1819 1759 mask |= (DAY_OF_YEAR_MASK|WEEK_OF_YEAR_MASK|WEEK_OF_MONTH_MASK|DAY_OF_WEEK_IN_MONTH_MASK);
1820 1760 }
1821 1761 return mask;
1822 1762 }
1823 1763
1824 1764 /**
1825 1765 * Returns the number of weeks in a period between fixedDay1 and
1826 1766 * fixedDate. The getFirstDayOfWeek-getMinimalDaysInFirstWeek rule
1827 1767 * is applied to calculate the number of weeks.
1828 1768 *
1829 1769 * @param fixedDay1 the fixed date of the first day of the period
1830 1770 * @param fixedDate the fixed date of the last day of the period
1831 1771 * @return the number of weeks of the given period
1832 1772 */
1833 1773 private int getWeekNumber(long fixedDay1, long fixedDate) {
1834 1774 // We can always use `jcal' since Julian and Gregorian are the
1835 1775 // same thing for this calculation.
1836 1776 long fixedDay1st = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(fixedDay1 + 6,
1837 1777 getFirstDayOfWeek());
1838 1778 int ndays = (int)(fixedDay1st - fixedDay1);
1839 1779 assert ndays <= 7;
1840 1780 if (ndays >= getMinimalDaysInFirstWeek()) {
1841 1781 fixedDay1st -= 7;
1842 1782 }
1843 1783 int normalizedDayOfPeriod = (int)(fixedDate - fixedDay1st);
1844 1784 if (normalizedDayOfPeriod >= 0) {
1845 1785 return normalizedDayOfPeriod / 7 + 1;
1846 1786 }
1847 1787 return CalendarUtils.floorDivide(normalizedDayOfPeriod, 7) + 1;
1848 1788 }
1849 1789
1850 1790 /**
1851 1791 * Converts calendar field values to the time value (millisecond
1852 1792 * offset from the <a href="Calendar.html#Epoch">Epoch</a>).
1853 1793 *
1854 1794 * @exception IllegalArgumentException if any calendar fields are invalid.
1855 1795 */
1856 1796 protected void computeTime() {
1857 1797 // In non-lenient mode, perform brief checking of calendar
1858 1798 // fields which have been set externally. Through this
1859 1799 // checking, the field values are stored in originalFields[]
1860 1800 // to see if any of them are normalized later.
1861 1801 if (!isLenient()) {
1862 1802 if (originalFields == null) {
1863 1803 originalFields = new int[FIELD_COUNT];
1864 1804 }
1865 1805 for (int field = 0; field < FIELD_COUNT; field++) {
1866 1806 int value = internalGet(field);
1867 1807 if (isExternallySet(field)) {
1868 1808 // Quick validation for any out of range values
1869 1809 if (value < getMinimum(field) || value > getMaximum(field)) {
1870 1810 throw new IllegalArgumentException(getFieldName(field));
1871 1811 }
1872 1812 }
1873 1813 originalFields[field] = value;
1874 1814 }
1875 1815 }
1876 1816
1877 1817 // Let the super class determine which calendar fields to be
1878 1818 // used to calculate the time.
1879 1819 int fieldMask = selectFields();
1880 1820
1881 1821 int year;
1882 1822 int era;
1883 1823
1884 1824 if (isSet(ERA)) {
1885 1825 era = internalGet(ERA);
1886 1826 year = isSet(YEAR) ? internalGet(YEAR) : 1;
1887 1827 } else {
1888 1828 if (isSet(YEAR)) {
1889 1829 era = eras.length - 1;
1890 1830 year = internalGet(YEAR);
1891 1831 } else {
1892 1832 // Equivalent to 1970 (Gregorian)
1893 1833 era = SHOWA;
1894 1834 year = 45;
1895 1835 }
1896 1836 }
1897 1837
1898 1838 // Calculate the time of day. We rely on the convention that
1899 1839 // an UNSET field has 0.
1900 1840 long timeOfDay = 0;
1901 1841 if (isFieldSet(fieldMask, HOUR_OF_DAY)) {
1902 1842 timeOfDay += (long) internalGet(HOUR_OF_DAY);
1903 1843 } else {
1904 1844 timeOfDay += internalGet(HOUR);
1905 1845 // The default value of AM_PM is 0 which designates AM.
1906 1846 if (isFieldSet(fieldMask, AM_PM)) {
1907 1847 timeOfDay += 12 * internalGet(AM_PM);
1908 1848 }
1909 1849 }
1910 1850 timeOfDay *= 60;
1911 1851 timeOfDay += internalGet(MINUTE);
1912 1852 timeOfDay *= 60;
1913 1853 timeOfDay += internalGet(SECOND);
1914 1854 timeOfDay *= 1000;
1915 1855 timeOfDay += internalGet(MILLISECOND);
1916 1856
1917 1857 // Convert the time of day to the number of days and the
1918 1858 // millisecond offset from midnight.
1919 1859 long fixedDate = timeOfDay / ONE_DAY;
1920 1860 timeOfDay %= ONE_DAY;
1921 1861 while (timeOfDay < 0) {
1922 1862 timeOfDay += ONE_DAY;
1923 1863 --fixedDate;
1924 1864 }
1925 1865
1926 1866 // Calculate the fixed date since January 1, 1 (Gregorian).
1927 1867 fixedDate += getFixedDate(era, year, fieldMask);
1928 1868
1929 1869 // millis represents local wall-clock time in milliseconds.
1930 1870 long millis = (fixedDate - EPOCH_OFFSET) * ONE_DAY + timeOfDay;
1931 1871
1932 1872 // Compute the time zone offset and DST offset. There are two potential
1933 1873 // ambiguities here. We'll assume a 2:00 am (wall time) switchover time
1934 1874 // for discussion purposes here.
1935 1875 // 1. The transition into DST. Here, a designated time of 2:00 am - 2:59 am
1936 1876 // can be in standard or in DST depending. However, 2:00 am is an invalid
1937 1877 // representation (the representation jumps from 1:59:59 am Std to 3:00:00 am DST).
1938 1878 // We assume standard time.
1939 1879 // 2. The transition out of DST. Here, a designated time of 1:00 am - 1:59 am
1940 1880 // can be in standard or DST. Both are valid representations (the rep
1941 1881 // jumps from 1:59:59 DST to 1:00:00 Std).
1942 1882 // Again, we assume standard time.
1943 1883 // We use the TimeZone object, unless the user has explicitly set the ZONE_OFFSET
1944 1884 // or DST_OFFSET fields; then we use those fields.
1945 1885 TimeZone zone = getZone();
1946 1886 if (zoneOffsets == null) {
1947 1887 zoneOffsets = new int[2];
1948 1888 }
1949 1889 int tzMask = fieldMask & (ZONE_OFFSET_MASK|DST_OFFSET_MASK);
1950 1890 if (tzMask != (ZONE_OFFSET_MASK|DST_OFFSET_MASK)) {
1951 1891 if (zone instanceof ZoneInfo) {
1952 1892 ((ZoneInfo)zone).getOffsetsByWall(millis, zoneOffsets);
1953 1893 } else {
1954 1894 zone.getOffsets(millis - zone.getRawOffset(), zoneOffsets);
1955 1895 }
1956 1896 }
1957 1897 if (tzMask != 0) {
1958 1898 if (isFieldSet(tzMask, ZONE_OFFSET)) {
1959 1899 zoneOffsets[0] = internalGet(ZONE_OFFSET);
1960 1900 }
1961 1901 if (isFieldSet(tzMask, DST_OFFSET)) {
1962 1902 zoneOffsets[1] = internalGet(DST_OFFSET);
1963 1903 }
1964 1904 }
1965 1905
1966 1906 // Adjust the time zone offset values to get the UTC time.
1967 1907 millis -= zoneOffsets[0] + zoneOffsets[1];
1968 1908
1969 1909 // Set this calendar's time in milliseconds
1970 1910 time = millis;
1971 1911
1972 1912 int mask = computeFields(fieldMask | getSetStateFields(), tzMask);
1973 1913
1974 1914 if (!isLenient()) {
1975 1915 for (int field = 0; field < FIELD_COUNT; field++) {
1976 1916 if (!isExternallySet(field)) {
1977 1917 continue;
1978 1918 }
1979 1919 if (originalFields[field] != internalGet(field)) {
1980 1920 int wrongValue = internalGet(field);
1981 1921 // Restore the original field values
1982 1922 System.arraycopy(originalFields, 0, fields, 0, fields.length);
1983 1923 throw new IllegalArgumentException(getFieldName(field) + "=" + wrongValue
1984 1924 + ", expected " + originalFields[field]);
1985 1925 }
1986 1926 }
1987 1927 }
1988 1928 setFieldsNormalized(mask);
1989 1929 }
1990 1930
1991 1931 /**
1992 1932 * Computes the fixed date under either the Gregorian or the
1993 1933 * Julian calendar, using the given year and the specified calendar fields.
1994 1934 *
1995 1935 * @param cal the CalendarSystem to be used for the date calculation
1996 1936 * @param year the normalized year number, with 0 indicating the
1997 1937 * year 1 BCE, -1 indicating 2 BCE, etc.
1998 1938 * @param fieldMask the calendar fields to be used for the date calculation
1999 1939 * @return the fixed date
2000 1940 * @see Calendar#selectFields
2001 1941 */
2002 1942 private long getFixedDate(int era, int year, int fieldMask) {
2003 1943 int month = JANUARY;
2004 1944 int firstDayOfMonth = 1;
2005 1945 if (isFieldSet(fieldMask, MONTH)) {
2006 1946 // No need to check if MONTH has been set (no isSet(MONTH)
2007 1947 // call) since its unset value happens to be JANUARY (0).
2008 1948 month = internalGet(MONTH);
2009 1949
2010 1950 // If the month is out of range, adjust it into range.
2011 1951 if (month > DECEMBER) {
2012 1952 year += month / 12;
2013 1953 month %= 12;
2014 1954 } else if (month < JANUARY) {
2015 1955 int[] rem = new int[1];
2016 1956 year += CalendarUtils.floorDivide(month, 12, rem);
2017 1957 month = rem[0];
2018 1958 }
2019 1959 } else {
2020 1960 if (year == 1 && era != 0) {
2021 1961 CalendarDate d = eras[era].getSinceDate();
2022 1962 month = d.getMonth() - 1;
2023 1963 firstDayOfMonth = d.getDayOfMonth();
2024 1964 }
2025 1965 }
2026 1966
2027 1967 // Adjust the base date if year is the minimum value.
2028 1968 if (year == MIN_VALUES[YEAR]) {
2029 1969 CalendarDate dx = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
2030 1970 int m = dx.getMonth() - 1;
2031 1971 if (month < m) {
2032 1972 month = m;
2033 1973 }
2034 1974 if (month == m) {
2035 1975 firstDayOfMonth = dx.getDayOfMonth();
2036 1976 }
2037 1977 }
2038 1978
2039 1979 LocalGregorianCalendar.Date date = jcal.newCalendarDate(TimeZone.NO_TIMEZONE);
2040 1980 date.setEra(era > 0 ? eras[era] : null);
2041 1981 date.setDate(year, month + 1, firstDayOfMonth);
2042 1982 jcal.normalize(date);
2043 1983
2044 1984 // Get the fixed date since Jan 1, 1 (Gregorian). We are on
2045 1985 // the first day of either `month' or January in 'year'.
2046 1986 long fixedDate = jcal.getFixedDate(date);
2047 1987
2048 1988 if (isFieldSet(fieldMask, MONTH)) {
2049 1989 // Month-based calculations
2050 1990 if (isFieldSet(fieldMask, DAY_OF_MONTH)) {
2051 1991 // We are on the "first day" of the month (which may
2052 1992 // not be 1). Just add the offset if DAY_OF_MONTH is
2053 1993 // set. If the isSet call returns false, that means
2054 1994 // DAY_OF_MONTH has been selected just because of the
2055 1995 // selected combination. We don't need to add any
2056 1996 // since the default value is the "first day".
2057 1997 if (isSet(DAY_OF_MONTH)) {
2058 1998 // To avoid underflow with DAY_OF_MONTH-firstDayOfMonth, add
2059 1999 // DAY_OF_MONTH, then subtract firstDayOfMonth.
2060 2000 fixedDate += internalGet(DAY_OF_MONTH);
2061 2001 fixedDate -= firstDayOfMonth;
2062 2002 }
2063 2003 } else {
2064 2004 if (isFieldSet(fieldMask, WEEK_OF_MONTH)) {
2065 2005 long firstDayOfWeek = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(fixedDate + 6,
2066 2006 getFirstDayOfWeek());
2067 2007 // If we have enough days in the first week, then
2068 2008 // move to the previous week.
2069 2009 if ((firstDayOfWeek - fixedDate) >= getMinimalDaysInFirstWeek()) {
2070 2010 firstDayOfWeek -= 7;
2071 2011 }
2072 2012 if (isFieldSet(fieldMask, DAY_OF_WEEK)) {
2073 2013 firstDayOfWeek = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(firstDayOfWeek + 6,
2074 2014 internalGet(DAY_OF_WEEK));
2075 2015 }
2076 2016 // In lenient mode, we treat days of the previous
2077 2017 // months as a part of the specified
2078 2018 // WEEK_OF_MONTH. See 4633646.
2079 2019 fixedDate = firstDayOfWeek + 7 * (internalGet(WEEK_OF_MONTH) - 1);
2080 2020 } else {
2081 2021 int dayOfWeek;
2082 2022 if (isFieldSet(fieldMask, DAY_OF_WEEK)) {
2083 2023 dayOfWeek = internalGet(DAY_OF_WEEK);
2084 2024 } else {
2085 2025 dayOfWeek = getFirstDayOfWeek();
2086 2026 }
2087 2027 // We are basing this on the day-of-week-in-month. The only
2088 2028 // trickiness occurs if the day-of-week-in-month is
2089 2029 // negative.
2090 2030 int dowim;
2091 2031 if (isFieldSet(fieldMask, DAY_OF_WEEK_IN_MONTH)) {
2092 2032 dowim = internalGet(DAY_OF_WEEK_IN_MONTH);
2093 2033 } else {
2094 2034 dowim = 1;
2095 2035 }
2096 2036 if (dowim >= 0) {
2097 2037 fixedDate = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(fixedDate + (7 * dowim) - 1,
2098 2038 dayOfWeek);
2099 2039 } else {
2100 2040 // Go to the first day of the next week of
2101 2041 // the specified week boundary.
2102 2042 int lastDate = monthLength(month, year) + (7 * (dowim + 1));
2103 2043 // Then, get the day of week date on or before the last date.
2104 2044 fixedDate = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(fixedDate + lastDate - 1,
2105 2045 dayOfWeek);
2106 2046 }
2107 2047 }
2108 2048 }
2109 2049 } else {
2110 2050 // We are on the first day of the year.
2111 2051 if (isFieldSet(fieldMask, DAY_OF_YEAR)) {
2112 2052 if (isTransitionYear(date.getNormalizedYear())) {
2113 2053 fixedDate = getFixedDateJan1(date, fixedDate);
2114 2054 }
2115 2055 // Add the offset, then subtract 1. (Make sure to avoid underflow.)
2116 2056 fixedDate += internalGet(DAY_OF_YEAR);
2117 2057 fixedDate--;
2118 2058 } else {
2119 2059 long firstDayOfWeek = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(fixedDate + 6,
2120 2060 getFirstDayOfWeek());
2121 2061 // If we have enough days in the first week, then move
2122 2062 // to the previous week.
2123 2063 if ((firstDayOfWeek - fixedDate) >= getMinimalDaysInFirstWeek()) {
2124 2064 firstDayOfWeek -= 7;
2125 2065 }
2126 2066 if (isFieldSet(fieldMask, DAY_OF_WEEK)) {
2127 2067 int dayOfWeek = internalGet(DAY_OF_WEEK);
2128 2068 if (dayOfWeek != getFirstDayOfWeek()) {
2129 2069 firstDayOfWeek = LocalGregorianCalendar.getDayOfWeekDateOnOrBefore(firstDayOfWeek + 6,
2130 2070 dayOfWeek);
2131 2071 }
2132 2072 }
2133 2073 fixedDate = firstDayOfWeek + 7 * ((long)internalGet(WEEK_OF_YEAR) - 1);
2134 2074 }
2135 2075 }
2136 2076 return fixedDate;
2137 2077 }
2138 2078
2139 2079 /**
2140 2080 * Returns the fixed date of the first day of the year (usually
2141 2081 * January 1) before the specified date.
2142 2082 *
2143 2083 * @param date the date for which the first day of the year is
2144 2084 * calculated. The date has to be in the cut-over year.
2145 2085 * @param fixedDate the fixed date representation of the date
2146 2086 */
2147 2087 private long getFixedDateJan1(LocalGregorianCalendar.Date date, long fixedDate) {
2148 2088 Era era = date.getEra();
2149 2089 if (date.getEra() != null && date.getYear() == 1) {
2150 2090 for (int eraIndex = getEraIndex(date); eraIndex > 0; eraIndex--) {
2151 2091 CalendarDate d = eras[eraIndex].getSinceDate();
2152 2092 long fd = gcal.getFixedDate(d);
2153 2093 // There might be multiple era transitions in a year.
2154 2094 if (fd > fixedDate) {
2155 2095 continue;
2156 2096 }
2157 2097 return fd;
2158 2098 }
2159 2099 }
2160 2100 CalendarDate d = gcal.newCalendarDate(TimeZone.NO_TIMEZONE);
2161 2101 d.setDate(date.getNormalizedYear(), Gregorian.JANUARY, 1);
2162 2102 return gcal.getFixedDate(d);
2163 2103 }
2164 2104
2165 2105 /**
2166 2106 * Returns the fixed date of the first date of the month (usually
2167 2107 * the 1st of the month) before the specified date.
2168 2108 *
2169 2109 * @param date the date for which the first day of the month is
2170 2110 * calculated. The date must be in the era transition year.
2171 2111 * @param fixedDate the fixed date representation of the date
2172 2112 */
2173 2113 private long getFixedDateMonth1(LocalGregorianCalendar.Date date,
2174 2114 long fixedDate) {
2175 2115 int eraIndex = getTransitionEraIndex(date);
2176 2116 if (eraIndex != -1) {
2177 2117 long transition = sinceFixedDates[eraIndex];
2178 2118 // If the given date is on or after the transition date, then
2179 2119 // return the transition date.
2180 2120 if (transition <= fixedDate) {
2181 2121 return transition;
2182 2122 }
2183 2123 }
2184 2124
2185 2125 // Otherwise, we can use the 1st day of the month.
2186 2126 return fixedDate - date.getDayOfMonth() + 1;
2187 2127 }
2188 2128
2189 2129 /**
2190 2130 * Returns a LocalGregorianCalendar.Date produced from the specified fixed date.
2191 2131 *
2192 2132 * @param fd the fixed date
2193 2133 */
2194 2134 private static LocalGregorianCalendar.Date getCalendarDate(long fd) {
2195 2135 LocalGregorianCalendar.Date d = jcal.newCalendarDate(TimeZone.NO_TIMEZONE);
2196 2136 jcal.getCalendarDateFromFixedDate(d, fd);
2197 2137 return d;
2198 2138 }
2199 2139
2200 2140 /**
2201 2141 * Returns the length of the specified month in the specified
2202 2142 * Gregorian year. The year number must be normalized.
2203 2143 *
2204 2144 * @see #isLeapYear(int)
2205 2145 */
2206 2146 private int monthLength(int month, int gregorianYear) {
2207 2147 return CalendarUtils.isGregorianLeapYear(gregorianYear) ?
2208 2148 GregorianCalendar.LEAP_MONTH_LENGTH[month] : GregorianCalendar.MONTH_LENGTH[month];
2209 2149 }
2210 2150
2211 2151 /**
2212 2152 * Returns the length of the specified month in the year provided
2213 2153 * by internalGet(YEAR).
2214 2154 *
2215 2155 * @see #isLeapYear(int)
2216 2156 */
2217 2157 private int monthLength(int month) {
2218 2158 assert jdate.isNormalized();
2219 2159 return jdate.isLeapYear() ?
2220 2160 GregorianCalendar.LEAP_MONTH_LENGTH[month] : GregorianCalendar.MONTH_LENGTH[month];
2221 2161 }
2222 2162
2223 2163 private int actualMonthLength() {
2224 2164 int length = jcal.getMonthLength(jdate);
2225 2165 int eraIndex = getTransitionEraIndex(jdate);
2226 2166 if (eraIndex == -1) {
2227 2167 long transitionFixedDate = sinceFixedDates[eraIndex];
2228 2168 CalendarDate d = eras[eraIndex].getSinceDate();
2229 2169 if (transitionFixedDate <= cachedFixedDate) {
2230 2170 length -= d.getDayOfMonth() - 1;
2231 2171 } else {
2232 2172 length = d.getDayOfMonth() - 1;
2233 2173 }
2234 2174 }
2235 2175 return length;
2236 2176 }
2237 2177
2238 2178 /**
2239 2179 * Returns the index to the new era if the given date is in a
2240 2180 * transition month. For example, if the give date is Heisei 1
2241 2181 * (1989) January 20, then the era index for Heisei is
2242 2182 * returned. Likewise, if the given date is Showa 64 (1989)
2243 2183 * January 3, then the era index for Heisei is returned. If the
2244 2184 * given date is not in any transition month, then -1 is returned.
2245 2185 */
2246 2186 private static int getTransitionEraIndex(LocalGregorianCalendar.Date date) {
2247 2187 int eraIndex = getEraIndex(date);
2248 2188 CalendarDate transitionDate = eras[eraIndex].getSinceDate();
2249 2189 if (transitionDate.getYear() == date.getNormalizedYear() &&
2250 2190 transitionDate.getMonth() == date.getMonth()) {
2251 2191 return eraIndex;
2252 2192 }
2253 2193 if (eraIndex < eras.length - 1) {
2254 2194 transitionDate = eras[++eraIndex].getSinceDate();
2255 2195 if (transitionDate.getYear() == date.getNormalizedYear() &&
2256 2196 transitionDate.getMonth() == date.getMonth()) {
2257 2197 return eraIndex;
2258 2198 }
2259 2199 }
2260 2200 return -1;
2261 2201 }
2262 2202
2263 2203 private boolean isTransitionYear(int normalizedYear) {
2264 2204 for (int i = eras.length - 1; i > 0; i--) {
2265 2205 int transitionYear = eras[i].getSinceDate().getYear();
2266 2206 if (normalizedYear == transitionYear) {
2267 2207 return true;
2268 2208 }
2269 2209 if (normalizedYear > transitionYear) {
2270 2210 break;
2271 2211 }
2272 2212 }
2273 2213 return false;
2274 2214 }
2275 2215
2276 2216 private static int getEraIndex(LocalGregorianCalendar.Date date) {
2277 2217 Era era = date.getEra();
2278 2218 for (int i = eras.length - 1; i > 0; i--) {
2279 2219 if (eras[i] == era) {
2280 2220 return i;
2281 2221 }
2282 2222 }
2283 2223 return 0;
2284 2224 }
2285 2225
2286 2226 /**
2287 2227 * Returns this object if it's normalized (all fields and time are
2288 2228 * in sync). Otherwise, a cloned object is returned after calling
2289 2229 * complete() in lenient mode.
2290 2230 */
2291 2231 private JapaneseImperialCalendar getNormalizedCalendar() {
2292 2232 JapaneseImperialCalendar jc;
2293 2233 if (isFullyNormalized()) {
2294 2234 jc = this;
2295 2235 } else {
2296 2236 // Create a clone and normalize the calendar fields
2297 2237 jc = (JapaneseImperialCalendar) this.clone();
2298 2238 jc.setLenient(true);
2299 2239 jc.complete();
2300 2240 }
2301 2241 return jc;
2302 2242 }
2303 2243
2304 2244 /**
2305 2245 * After adjustments such as add(MONTH), add(YEAR), we don't want the
2306 2246 * month to jump around. E.g., we don't want Jan 31 + 1 month to go to Mar
2307 2247 * 3, we want it to go to Feb 28. Adjustments which might run into this
2308 2248 * problem call this method to retain the proper month.
2309 2249 */
2310 2250 private void pinDayOfMonth(LocalGregorianCalendar.Date date) {
2311 2251 int year = date.getYear();
2312 2252 int dom = date.getDayOfMonth();
2313 2253 if (year != getMinimum(YEAR)) {
2314 2254 date.setDayOfMonth(1);
2315 2255 jcal.normalize(date);
2316 2256 int monthLength = jcal.getMonthLength(date);
2317 2257 if (dom > monthLength) {
2318 2258 date.setDayOfMonth(monthLength);
2319 2259 } else {
2320 2260 date.setDayOfMonth(dom);
2321 2261 }
2322 2262 jcal.normalize(date);
2323 2263 } else {
2324 2264 LocalGregorianCalendar.Date d = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
2325 2265 LocalGregorianCalendar.Date realDate = jcal.getCalendarDate(time, getZone());
2326 2266 long tod = realDate.getTimeOfDay();
2327 2267 // Use an equivalent year.
2328 2268 realDate.addYear(+400);
2329 2269 realDate.setMonth(date.getMonth());
2330 2270 realDate.setDayOfMonth(1);
2331 2271 jcal.normalize(realDate);
2332 2272 int monthLength = jcal.getMonthLength(realDate);
2333 2273 if (dom > monthLength) {
2334 2274 realDate.setDayOfMonth(monthLength);
2335 2275 } else {
2336 2276 if (dom < d.getDayOfMonth()) {
2337 2277 realDate.setDayOfMonth(d.getDayOfMonth());
2338 2278 } else {
2339 2279 realDate.setDayOfMonth(dom);
2340 2280 }
2341 2281 }
2342 2282 if (realDate.getDayOfMonth() == d.getDayOfMonth() && tod < d.getTimeOfDay()) {
2343 2283 realDate.setDayOfMonth(Math.min(dom + 1, monthLength));
2344 2284 }
2345 2285 // restore the year.
2346 2286 date.setDate(year, realDate.getMonth(), realDate.getDayOfMonth());
2347 2287 // Don't normalize date here so as not to cause underflow.
2348 2288 }
2349 2289 }
2350 2290
2351 2291 /**
2352 2292 * Returns the new value after 'roll'ing the specified value and amount.
2353 2293 */
2354 2294 private static int getRolledValue(int value, int amount, int min, int max) {
2355 2295 assert value >= min && value <= max;
2356 2296 int range = max - min + 1;
2357 2297 amount %= range;
2358 2298 int n = value + amount;
2359 2299 if (n > max) {
2360 2300 n -= range;
2361 2301 } else if (n < min) {
2362 2302 n += range;
2363 2303 }
2364 2304 assert n >= min && n <= max;
2365 2305 return n;
2366 2306 }
2367 2307
2368 2308 /**
2369 2309 * Returns the ERA. We need a special method for this because the
2370 2310 * default ERA is the current era, but a zero (unset) ERA means before Meiji.
2371 2311 */
2372 2312 private int internalGetEra() {
2373 2313 return isSet(ERA) ? internalGet(ERA) : eras.length - 1;
2374 2314 }
2375 2315
2376 2316 /**
2377 2317 * Updates internal state.
2378 2318 */
2379 2319 private void readObject(ObjectInputStream stream)
2380 2320 throws IOException, ClassNotFoundException {
2381 2321 stream.defaultReadObject();
2382 2322 if (jdate == null) {
2383 2323 jdate = jcal.newCalendarDate(getZone());
2384 2324 cachedFixedDate = Long.MIN_VALUE;
2385 2325 }
2386 2326 }
2387 2327 }
↓ open down ↓ |
1317 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX