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/GregorianCalendar.java
+++ new/src/share/classes/java/util/GregorianCalendar.java
1 1 /*
2 - * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
2 + * Copyright (c) 1996, 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.
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 /*
27 27 * (C) Copyright Taligent, Inc. 1996-1998 - All Rights Reserved
28 28 * (C) Copyright IBM Corp. 1996-1998 - All Rights Reserved
29 29 *
30 30 * The original version of this source code and documentation is copyrighted
31 31 * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
32 32 * materials are provided under terms of a License Agreement between Taligent
↓ open down ↓ |
20 lines elided |
↑ open up ↑ |
33 33 * and Sun. This technology is protected by multiple US and International
34 34 * patents. This notice and attribution to Taligent may not be removed.
35 35 * Taligent is a registered trademark of Taligent, Inc.
36 36 *
37 37 */
38 38
39 39 package java.util;
40 40
41 41 import java.io.IOException;
42 42 import java.io.ObjectInputStream;
43 +import sun.util.locale.provider.CalendarDataUtility;
43 44 import sun.util.calendar.BaseCalendar;
44 45 import sun.util.calendar.CalendarDate;
45 46 import sun.util.calendar.CalendarSystem;
46 47 import sun.util.calendar.CalendarUtils;
47 48 import sun.util.calendar.Era;
48 49 import sun.util.calendar.Gregorian;
49 50 import sun.util.calendar.JulianCalendar;
50 51 import sun.util.calendar.ZoneInfo;
51 52
52 53 /**
53 54 * <code>GregorianCalendar</code> is a concrete subclass of
54 55 * <code>Calendar</code> and provides the standard calendar system
55 56 * used by most of the world.
56 57 *
57 58 * <p> <code>GregorianCalendar</code> is a hybrid calendar that
58 59 * supports both the Julian and Gregorian calendar systems with the
59 60 * support of a single discontinuity, which corresponds by default to
60 61 * the Gregorian date when the Gregorian calendar was instituted
61 62 * (October 15, 1582 in some countries, later in others). The cutover
62 63 * date may be changed by the caller by calling {@link
63 64 * #setGregorianChange(Date) setGregorianChange()}.
64 65 *
65 66 * <p>
66 67 * Historically, in those countries which adopted the Gregorian calendar first,
67 68 * October 4, 1582 (Julian) was thus followed by October 15, 1582 (Gregorian). This calendar models
68 69 * this correctly. Before the Gregorian cutover, <code>GregorianCalendar</code>
69 70 * implements the Julian calendar. The only difference between the Gregorian
70 71 * and the Julian calendar is the leap year rule. The Julian calendar specifies
71 72 * leap years every four years, whereas the Gregorian calendar omits century
72 73 * years which are not divisible by 400.
73 74 *
74 75 * <p>
75 76 * <code>GregorianCalendar</code> implements <em>proleptic</em> Gregorian and
76 77 * Julian calendars. That is, dates are computed by extrapolating the current
77 78 * rules indefinitely far backward and forward in time. As a result,
78 79 * <code>GregorianCalendar</code> may be used for all years to generate
79 80 * meaningful and consistent results. However, dates obtained using
80 81 * <code>GregorianCalendar</code> are historically accurate only from March 1, 4
81 82 * AD onward, when modern Julian calendar rules were adopted. Before this date,
82 83 * leap year rules were applied irregularly, and before 45 BC the Julian
83 84 * calendar did not even exist.
84 85 *
85 86 * <p>
86 87 * Prior to the institution of the Gregorian calendar, New Year's Day was
87 88 * March 25. To avoid confusion, this calendar always uses January 1. A manual
88 89 * adjustment may be made if desired for dates that are prior to the Gregorian
89 90 * changeover and which fall between January 1 and March 24.
90 91 *
91 92 * <h4><a name="week_and_year">Week Of Year and Week Year</a></h4>
92 93 *
93 94 * <p>Values calculated for the {@link Calendar#WEEK_OF_YEAR
94 95 * WEEK_OF_YEAR} field range from 1 to 53. The first week of a
95 96 * calendar year is the earliest seven day period starting on {@link
96 97 * Calendar#getFirstDayOfWeek() getFirstDayOfWeek()} that contains at
97 98 * least {@link Calendar#getMinimalDaysInFirstWeek()
98 99 * getMinimalDaysInFirstWeek()} days from that year. It thus depends
99 100 * on the values of {@code getMinimalDaysInFirstWeek()}, {@code
100 101 * getFirstDayOfWeek()}, and the day of the week of January 1. Weeks
101 102 * between week 1 of one year and week 1 of the following year
102 103 * (exclusive) are numbered sequentially from 2 to 52 or 53 (except
103 104 * for year(s) involved in the Julian-Gregorian transition).
104 105 *
105 106 * <p>The {@code getFirstDayOfWeek()} and {@code
106 107 * getMinimalDaysInFirstWeek()} values are initialized using
107 108 * locale-dependent resources when constructing a {@code
108 109 * GregorianCalendar}. <a name="iso8601_compatible_setting">The week
109 110 * determination is compatible</a> with the ISO 8601 standard when {@code
110 111 * getFirstDayOfWeek()} is {@code MONDAY} and {@code
111 112 * getMinimalDaysInFirstWeek()} is 4, which values are used in locales
112 113 * where the standard is preferred. These values can explicitly be set by
113 114 * calling {@link Calendar#setFirstDayOfWeek(int) setFirstDayOfWeek()} and
114 115 * {@link Calendar#setMinimalDaysInFirstWeek(int)
115 116 * setMinimalDaysInFirstWeek()}.
116 117 *
117 118 * <p>A <a name="week_year"><em>week year</em></a> is in sync with a
118 119 * {@code WEEK_OF_YEAR} cycle. All weeks between the first and last
119 120 * weeks (inclusive) have the same <em>week year</em> value.
120 121 * Therefore, the first and last days of a week year may have
121 122 * different calendar year values.
122 123 *
123 124 * <p>For example, January 1, 1998 is a Thursday. If {@code
124 125 * getFirstDayOfWeek()} is {@code MONDAY} and {@code
125 126 * getMinimalDaysInFirstWeek()} is 4 (ISO 8601 standard compatible
126 127 * setting), then week 1 of 1998 starts on December 29, 1997, and ends
127 128 * on January 4, 1998. The week year is 1998 for the last three days
128 129 * of calendar year 1997. If, however, {@code getFirstDayOfWeek()} is
129 130 * {@code SUNDAY}, then week 1 of 1998 starts on January 4, 1998, and
130 131 * ends on January 10, 1998; the first three days of 1998 then are
131 132 * part of week 53 of 1997 and their week year is 1997.
132 133 *
133 134 * <h4>Week Of Month</h4>
134 135 *
135 136 * <p>Values calculated for the <code>WEEK_OF_MONTH</code> field range from 0
136 137 * to 6. Week 1 of a month (the days with <code>WEEK_OF_MONTH =
137 138 * 1</code>) is the earliest set of at least
138 139 * <code>getMinimalDaysInFirstWeek()</code> contiguous days in that month,
139 140 * ending on the day before <code>getFirstDayOfWeek()</code>. Unlike
140 141 * week 1 of a year, week 1 of a month may be shorter than 7 days, need
141 142 * not start on <code>getFirstDayOfWeek()</code>, and will not include days of
142 143 * the previous month. Days of a month before week 1 have a
143 144 * <code>WEEK_OF_MONTH</code> of 0.
144 145 *
145 146 * <p>For example, if <code>getFirstDayOfWeek()</code> is <code>SUNDAY</code>
146 147 * and <code>getMinimalDaysInFirstWeek()</code> is 4, then the first week of
147 148 * January 1998 is Sunday, January 4 through Saturday, January 10. These days
148 149 * have a <code>WEEK_OF_MONTH</code> of 1. Thursday, January 1 through
149 150 * Saturday, January 3 have a <code>WEEK_OF_MONTH</code> of 0. If
150 151 * <code>getMinimalDaysInFirstWeek()</code> is changed to 3, then January 1
151 152 * through January 3 have a <code>WEEK_OF_MONTH</code> of 1.
152 153 *
153 154 * <h4>Default Fields Values</h4>
154 155 *
155 156 * <p>The <code>clear</code> method sets calendar field(s)
156 157 * undefined. <code>GregorianCalendar</code> uses the following
157 158 * default value for each calendar field if its value is undefined.
158 159 *
159 160 * <table cellpadding="0" cellspacing="3" border="0"
160 161 * summary="GregorianCalendar default field values"
161 162 * style="text-align: left; width: 66%;">
162 163 * <tbody>
163 164 * <tr>
164 165 * <th style="vertical-align: top; background-color: rgb(204, 204, 255);
165 166 * text-align: center;">Field<br>
166 167 * </th>
167 168 * <th style="vertical-align: top; background-color: rgb(204, 204, 255);
168 169 * text-align: center;">Default Value<br>
169 170 * </th>
170 171 * </tr>
171 172 * <tr>
172 173 * <td style="vertical-align: middle;">
173 174 * <code>ERA<br></code>
174 175 * </td>
175 176 * <td style="vertical-align: middle;">
176 177 * <code>AD<br></code>
177 178 * </td>
178 179 * </tr>
179 180 * <tr>
180 181 * <td style="vertical-align: middle; background-color: rgb(238, 238, 255);">
181 182 * <code>YEAR<br></code>
182 183 * </td>
183 184 * <td style="vertical-align: middle; background-color: rgb(238, 238, 255);">
184 185 * <code>1970<br></code>
185 186 * </td>
186 187 * </tr>
187 188 * <tr>
188 189 * <td style="vertical-align: middle;">
189 190 * <code>MONTH<br></code>
190 191 * </td>
191 192 * <td style="vertical-align: middle;">
192 193 * <code>JANUARY<br></code>
193 194 * </td>
194 195 * </tr>
195 196 * <tr>
196 197 * <td style="vertical-align: top; background-color: rgb(238, 238, 255);">
197 198 * <code>DAY_OF_MONTH<br></code>
198 199 * </td>
199 200 * <td style="vertical-align: top; background-color: rgb(238, 238, 255);">
200 201 * <code>1<br></code>
201 202 * </td>
202 203 * </tr>
203 204 * <tr>
204 205 * <td style="vertical-align: middle;">
205 206 * <code>DAY_OF_WEEK<br></code>
206 207 * </td>
207 208 * <td style="vertical-align: middle;">
208 209 * <code>the first day of week<br></code>
209 210 * </td>
210 211 * </tr>
211 212 * <tr>
212 213 * <td style="vertical-align: top; background-color: rgb(238, 238, 255);">
213 214 * <code>WEEK_OF_MONTH<br></code>
214 215 * </td>
215 216 * <td style="vertical-align: top; background-color: rgb(238, 238, 255);">
216 217 * <code>0<br></code>
217 218 * </td>
218 219 * </tr>
219 220 * <tr>
220 221 * <td style="vertical-align: top;">
221 222 * <code>DAY_OF_WEEK_IN_MONTH<br></code>
222 223 * </td>
223 224 * <td style="vertical-align: top;">
224 225 * <code>1<br></code>
225 226 * </td>
226 227 * </tr>
227 228 * <tr>
228 229 * <td style="vertical-align: middle; background-color: rgb(238, 238, 255);">
229 230 * <code>AM_PM<br></code>
230 231 * </td>
231 232 * <td style="vertical-align: middle; background-color: rgb(238, 238, 255);">
232 233 * <code>AM<br></code>
233 234 * </td>
234 235 * </tr>
235 236 * <tr>
236 237 * <td style="vertical-align: middle;">
237 238 * <code>HOUR, HOUR_OF_DAY, MINUTE, SECOND, MILLISECOND<br></code>
238 239 * </td>
239 240 * <td style="vertical-align: middle;">
240 241 * <code>0<br></code>
241 242 * </td>
242 243 * </tr>
243 244 * </tbody>
244 245 * </table>
245 246 * <br>Default values are not applicable for the fields not listed above.
246 247 *
247 248 * <p>
248 249 * <strong>Example:</strong>
249 250 * <blockquote>
250 251 * <pre>
251 252 * // get the supported ids for GMT-08:00 (Pacific Standard Time)
252 253 * String[] ids = TimeZone.getAvailableIDs(-8 * 60 * 60 * 1000);
253 254 * // if no ids were returned, something is wrong. get out.
254 255 * if (ids.length == 0)
255 256 * System.exit(0);
256 257 *
257 258 * // begin output
258 259 * System.out.println("Current Time");
259 260 *
260 261 * // create a Pacific Standard Time time zone
261 262 * SimpleTimeZone pdt = new SimpleTimeZone(-8 * 60 * 60 * 1000, ids[0]);
262 263 *
263 264 * // set up rules for Daylight Saving Time
264 265 * pdt.setStartRule(Calendar.APRIL, 1, Calendar.SUNDAY, 2 * 60 * 60 * 1000);
265 266 * pdt.setEndRule(Calendar.OCTOBER, -1, Calendar.SUNDAY, 2 * 60 * 60 * 1000);
266 267 *
267 268 * // create a GregorianCalendar with the Pacific Daylight time zone
268 269 * // and the current date and time
269 270 * Calendar calendar = new GregorianCalendar(pdt);
270 271 * Date trialTime = new Date();
271 272 * calendar.setTime(trialTime);
272 273 *
273 274 * // print out a bunch of interesting things
274 275 * System.out.println("ERA: " + calendar.get(Calendar.ERA));
275 276 * System.out.println("YEAR: " + calendar.get(Calendar.YEAR));
276 277 * System.out.println("MONTH: " + calendar.get(Calendar.MONTH));
277 278 * System.out.println("WEEK_OF_YEAR: " + calendar.get(Calendar.WEEK_OF_YEAR));
278 279 * System.out.println("WEEK_OF_MONTH: " + calendar.get(Calendar.WEEK_OF_MONTH));
279 280 * System.out.println("DATE: " + calendar.get(Calendar.DATE));
280 281 * System.out.println("DAY_OF_MONTH: " + calendar.get(Calendar.DAY_OF_MONTH));
281 282 * System.out.println("DAY_OF_YEAR: " + calendar.get(Calendar.DAY_OF_YEAR));
282 283 * System.out.println("DAY_OF_WEEK: " + calendar.get(Calendar.DAY_OF_WEEK));
283 284 * System.out.println("DAY_OF_WEEK_IN_MONTH: "
284 285 * + calendar.get(Calendar.DAY_OF_WEEK_IN_MONTH));
285 286 * System.out.println("AM_PM: " + calendar.get(Calendar.AM_PM));
286 287 * System.out.println("HOUR: " + calendar.get(Calendar.HOUR));
287 288 * System.out.println("HOUR_OF_DAY: " + calendar.get(Calendar.HOUR_OF_DAY));
288 289 * System.out.println("MINUTE: " + calendar.get(Calendar.MINUTE));
289 290 * System.out.println("SECOND: " + calendar.get(Calendar.SECOND));
290 291 * System.out.println("MILLISECOND: " + calendar.get(Calendar.MILLISECOND));
291 292 * System.out.println("ZONE_OFFSET: "
292 293 * + (calendar.get(Calendar.ZONE_OFFSET)/(60*60*1000)));
293 294 * System.out.println("DST_OFFSET: "
294 295 * + (calendar.get(Calendar.DST_OFFSET)/(60*60*1000)));
295 296
296 297 * System.out.println("Current Time, with hour reset to 3");
297 298 * calendar.clear(Calendar.HOUR_OF_DAY); // so doesn't override
298 299 * calendar.set(Calendar.HOUR, 3);
299 300 * System.out.println("ERA: " + calendar.get(Calendar.ERA));
300 301 * System.out.println("YEAR: " + calendar.get(Calendar.YEAR));
301 302 * System.out.println("MONTH: " + calendar.get(Calendar.MONTH));
302 303 * System.out.println("WEEK_OF_YEAR: " + calendar.get(Calendar.WEEK_OF_YEAR));
303 304 * System.out.println("WEEK_OF_MONTH: " + calendar.get(Calendar.WEEK_OF_MONTH));
304 305 * System.out.println("DATE: " + calendar.get(Calendar.DATE));
305 306 * System.out.println("DAY_OF_MONTH: " + calendar.get(Calendar.DAY_OF_MONTH));
306 307 * System.out.println("DAY_OF_YEAR: " + calendar.get(Calendar.DAY_OF_YEAR));
307 308 * System.out.println("DAY_OF_WEEK: " + calendar.get(Calendar.DAY_OF_WEEK));
308 309 * System.out.println("DAY_OF_WEEK_IN_MONTH: "
309 310 * + calendar.get(Calendar.DAY_OF_WEEK_IN_MONTH));
310 311 * System.out.println("AM_PM: " + calendar.get(Calendar.AM_PM));
311 312 * System.out.println("HOUR: " + calendar.get(Calendar.HOUR));
312 313 * System.out.println("HOUR_OF_DAY: " + calendar.get(Calendar.HOUR_OF_DAY));
313 314 * System.out.println("MINUTE: " + calendar.get(Calendar.MINUTE));
314 315 * System.out.println("SECOND: " + calendar.get(Calendar.SECOND));
315 316 * System.out.println("MILLISECOND: " + calendar.get(Calendar.MILLISECOND));
316 317 * System.out.println("ZONE_OFFSET: "
317 318 * + (calendar.get(Calendar.ZONE_OFFSET)/(60*60*1000))); // in hours
318 319 * System.out.println("DST_OFFSET: "
319 320 * + (calendar.get(Calendar.DST_OFFSET)/(60*60*1000))); // in hours
320 321 * </pre>
321 322 * </blockquote>
322 323 *
323 324 * @see TimeZone
324 325 * @author David Goldsmith, Mark Davis, Chen-Lieh Huang, Alan Liu
325 326 * @since JDK1.1
326 327 */
327 328 public class GregorianCalendar extends Calendar {
328 329 /*
329 330 * Implementation Notes
330 331 *
331 332 * The epoch is the number of days or milliseconds from some defined
332 333 * starting point. The epoch for java.util.Date is used here; that is,
333 334 * milliseconds from January 1, 1970 (Gregorian), midnight UTC. Other
334 335 * epochs which are used are January 1, year 1 (Gregorian), which is day 1
335 336 * of the Gregorian calendar, and December 30, year 0 (Gregorian), which is
336 337 * day 1 of the Julian calendar.
337 338 *
338 339 * We implement the proleptic Julian and Gregorian calendars. This means we
339 340 * implement the modern definition of the calendar even though the
340 341 * historical usage differs. For example, if the Gregorian change is set
341 342 * to new Date(Long.MIN_VALUE), we have a pure Gregorian calendar which
342 343 * labels dates preceding the invention of the Gregorian calendar in 1582 as
343 344 * if the calendar existed then.
344 345 *
345 346 * Likewise, with the Julian calendar, we assume a consistent
346 347 * 4-year leap year rule, even though the historical pattern of
347 348 * leap years is irregular, being every 3 years from 45 BCE
348 349 * through 9 BCE, then every 4 years from 8 CE onwards, with no
349 350 * leap years in-between. Thus date computations and functions
350 351 * such as isLeapYear() are not intended to be historically
351 352 * accurate.
352 353 */
353 354
354 355 //////////////////
355 356 // Class Variables
356 357 //////////////////
357 358
358 359 /**
359 360 * Value of the <code>ERA</code> field indicating
360 361 * the period before the common era (before Christ), also known as BCE.
361 362 * The sequence of years at the transition from <code>BC</code> to <code>AD</code> is
362 363 * ..., 2 BC, 1 BC, 1 AD, 2 AD,...
363 364 *
364 365 * @see #ERA
365 366 */
366 367 public static final int BC = 0;
367 368
368 369 /**
369 370 * Value of the {@link #ERA} field indicating
370 371 * the period before the common era, the same value as {@link #BC}.
371 372 *
372 373 * @see #CE
373 374 */
374 375 static final int BCE = 0;
375 376
376 377 /**
377 378 * Value of the <code>ERA</code> field indicating
378 379 * the common era (Anno Domini), also known as CE.
379 380 * The sequence of years at the transition from <code>BC</code> to <code>AD</code> is
380 381 * ..., 2 BC, 1 BC, 1 AD, 2 AD,...
381 382 *
382 383 * @see #ERA
383 384 */
384 385 public static final int AD = 1;
385 386
386 387 /**
387 388 * Value of the {@link #ERA} field indicating
388 389 * the common era, the same value as {@link #AD}.
389 390 *
390 391 * @see #BCE
391 392 */
392 393 static final int CE = 1;
393 394
394 395 private static final int EPOCH_OFFSET = 719163; // Fixed date of January 1, 1970 (Gregorian)
395 396 private static final int EPOCH_YEAR = 1970;
396 397
397 398 static final int MONTH_LENGTH[]
398 399 = {31,28,31,30,31,30,31,31,30,31,30,31}; // 0-based
399 400 static final int LEAP_MONTH_LENGTH[]
400 401 = {31,29,31,30,31,30,31,31,30,31,30,31}; // 0-based
401 402
402 403 // Useful millisecond constants. Although ONE_DAY and ONE_WEEK can fit
403 404 // into ints, they must be longs in order to prevent arithmetic overflow
404 405 // when performing (bug 4173516).
405 406 private static final int ONE_SECOND = 1000;
406 407 private static final int ONE_MINUTE = 60*ONE_SECOND;
407 408 private static final int ONE_HOUR = 60*ONE_MINUTE;
408 409 private static final long ONE_DAY = 24*ONE_HOUR;
409 410 private static final long ONE_WEEK = 7*ONE_DAY;
410 411
411 412 /*
412 413 * <pre>
413 414 * Greatest Least
414 415 * Field name Minimum Minimum Maximum Maximum
415 416 * ---------- ------- ------- ------- -------
416 417 * ERA 0 0 1 1
417 418 * YEAR 1 1 292269054 292278994
418 419 * MONTH 0 0 11 11
419 420 * WEEK_OF_YEAR 1 1 52* 53
420 421 * WEEK_OF_MONTH 0 0 4* 6
421 422 * DAY_OF_MONTH 1 1 28* 31
422 423 * DAY_OF_YEAR 1 1 365* 366
423 424 * DAY_OF_WEEK 1 1 7 7
424 425 * DAY_OF_WEEK_IN_MONTH -1 -1 4* 6
425 426 * AM_PM 0 0 1 1
426 427 * HOUR 0 0 11 11
427 428 * HOUR_OF_DAY 0 0 23 23
428 429 * MINUTE 0 0 59 59
429 430 * SECOND 0 0 59 59
430 431 * MILLISECOND 0 0 999 999
431 432 * ZONE_OFFSET -13:00 -13:00 14:00 14:00
432 433 * DST_OFFSET 0:00 0:00 0:20 2:00
433 434 * </pre>
434 435 * *: depends on the Gregorian change date
435 436 */
436 437 static final int MIN_VALUES[] = {
437 438 BCE, // ERA
438 439 1, // YEAR
439 440 JANUARY, // MONTH
440 441 1, // WEEK_OF_YEAR
441 442 0, // WEEK_OF_MONTH
442 443 1, // DAY_OF_MONTH
443 444 1, // DAY_OF_YEAR
444 445 SUNDAY, // DAY_OF_WEEK
445 446 1, // DAY_OF_WEEK_IN_MONTH
446 447 AM, // AM_PM
447 448 0, // HOUR
448 449 0, // HOUR_OF_DAY
449 450 0, // MINUTE
450 451 0, // SECOND
451 452 0, // MILLISECOND
452 453 -13*ONE_HOUR, // ZONE_OFFSET (UNIX compatibility)
453 454 0 // DST_OFFSET
454 455 };
455 456 static final int LEAST_MAX_VALUES[] = {
456 457 CE, // ERA
457 458 292269054, // YEAR
458 459 DECEMBER, // MONTH
459 460 52, // WEEK_OF_YEAR
460 461 4, // WEEK_OF_MONTH
461 462 28, // DAY_OF_MONTH
462 463 365, // DAY_OF_YEAR
463 464 SATURDAY, // DAY_OF_WEEK
464 465 4, // DAY_OF_WEEK_IN
465 466 PM, // AM_PM
466 467 11, // HOUR
467 468 23, // HOUR_OF_DAY
468 469 59, // MINUTE
469 470 59, // SECOND
470 471 999, // MILLISECOND
471 472 14*ONE_HOUR, // ZONE_OFFSET
472 473 20*ONE_MINUTE // DST_OFFSET (historical least maximum)
473 474 };
474 475 static final int MAX_VALUES[] = {
475 476 CE, // ERA
476 477 292278994, // YEAR
477 478 DECEMBER, // MONTH
478 479 53, // WEEK_OF_YEAR
479 480 6, // WEEK_OF_MONTH
480 481 31, // DAY_OF_MONTH
481 482 366, // DAY_OF_YEAR
482 483 SATURDAY, // DAY_OF_WEEK
483 484 6, // DAY_OF_WEEK_IN
484 485 PM, // AM_PM
↓ open down ↓ |
432 lines elided |
↑ open up ↑ |
485 486 11, // HOUR
486 487 23, // HOUR_OF_DAY
487 488 59, // MINUTE
488 489 59, // SECOND
489 490 999, // MILLISECOND
490 491 14*ONE_HOUR, // ZONE_OFFSET
491 492 2*ONE_HOUR // DST_OFFSET (double summer time)
492 493 };
493 494
494 495 // Proclaim serialization compatibility with JDK 1.1
496 + @SuppressWarnings("FieldNameHidesFieldInSuperclass")
495 497 static final long serialVersionUID = -8125100834729963327L;
496 498
497 499 // Reference to the sun.util.calendar.Gregorian instance (singleton).
498 500 private static final Gregorian gcal =
499 501 CalendarSystem.getGregorianCalendar();
500 502
501 503 // Reference to the JulianCalendar instance (singleton), set as needed. See
502 504 // getJulianCalendarSystem().
503 505 private static JulianCalendar jcal;
504 506
505 507 // JulianCalendar eras. See getJulianCalendarSystem().
506 508 private static Era[] jeras;
507 509
508 510 // The default value of gregorianCutover.
509 511 static final long DEFAULT_GREGORIAN_CUTOVER = -12219292800000L;
510 512
511 513 /////////////////////
512 514 // Instance Variables
513 515 /////////////////////
514 516
515 517 /**
516 518 * The point at which the Gregorian calendar rules are used, measured in
517 519 * milliseconds from the standard epoch. Default is October 15, 1582
518 520 * (Gregorian) 00:00:00 UTC or -12219292800000L. For this value, October 4,
519 521 * 1582 (Julian) is followed by October 15, 1582 (Gregorian). This
520 522 * corresponds to Julian day number 2299161.
521 523 * @serial
522 524 */
523 525 private long gregorianCutover = DEFAULT_GREGORIAN_CUTOVER;
524 526
525 527 /**
526 528 * The fixed date of the gregorianCutover.
527 529 */
528 530 private transient long gregorianCutoverDate =
529 531 (((DEFAULT_GREGORIAN_CUTOVER + 1)/ONE_DAY) - 1) + EPOCH_OFFSET; // == 577736
530 532
531 533 /**
532 534 * The normalized year of the gregorianCutover in Gregorian, with
533 535 * 0 representing 1 BCE, -1 representing 2 BCE, etc.
534 536 */
535 537 private transient int gregorianCutoverYear = 1582;
536 538
537 539 /**
538 540 * The normalized year of the gregorianCutover in Julian, with 0
539 541 * representing 1 BCE, -1 representing 2 BCE, etc.
540 542 */
541 543 private transient int gregorianCutoverYearJulian = 1582;
542 544
543 545 /**
544 546 * gdate always has a sun.util.calendar.Gregorian.Date instance to
545 547 * avoid overhead of creating it. The assumption is that most
546 548 * applications will need only Gregorian calendar calculations.
547 549 */
548 550 private transient BaseCalendar.Date gdate;
549 551
550 552 /**
551 553 * Reference to either gdate or a JulianCalendar.Date
552 554 * instance. After calling complete(), this value is guaranteed to
553 555 * be set.
554 556 */
555 557 private transient BaseCalendar.Date cdate;
556 558
557 559 /**
558 560 * The CalendarSystem used to calculate the date in cdate. After
559 561 * calling complete(), this value is guaranteed to be set and
560 562 * consistent with the cdate value.
561 563 */
562 564 private transient BaseCalendar calsys;
563 565
564 566 /**
565 567 * Temporary int[2] to get time zone offsets. zoneOffsets[0] gets
566 568 * the GMT offset value and zoneOffsets[1] gets the DST saving
567 569 * value.
568 570 */
569 571 private transient int[] zoneOffsets;
570 572
571 573 /**
572 574 * Temporary storage for saving original fields[] values in
573 575 * non-lenient mode.
574 576 */
575 577 private transient int[] originalFields;
576 578
577 579 ///////////////
578 580 // Constructors
579 581 ///////////////
580 582
581 583 /**
582 584 * Constructs a default <code>GregorianCalendar</code> using the current time
583 585 * in the default time zone with the default locale.
584 586 */
585 587 public GregorianCalendar() {
586 588 this(TimeZone.getDefaultRef(), Locale.getDefault(Locale.Category.FORMAT));
587 589 setZoneShared(true);
588 590 }
589 591
590 592 /**
591 593 * Constructs a <code>GregorianCalendar</code> based on the current time
592 594 * in the given time zone with the default locale.
593 595 *
594 596 * @param zone the given time zone.
595 597 */
596 598 public GregorianCalendar(TimeZone zone) {
597 599 this(zone, Locale.getDefault(Locale.Category.FORMAT));
598 600 }
599 601
600 602 /**
601 603 * Constructs a <code>GregorianCalendar</code> based on the current time
602 604 * in the default time zone with the given locale.
603 605 *
604 606 * @param aLocale the given locale.
605 607 */
606 608 public GregorianCalendar(Locale aLocale) {
607 609 this(TimeZone.getDefaultRef(), aLocale);
608 610 setZoneShared(true);
609 611 }
610 612
611 613 /**
612 614 * Constructs a <code>GregorianCalendar</code> based on the current time
613 615 * in the given time zone with the given locale.
614 616 *
615 617 * @param zone the given time zone.
616 618 * @param aLocale the given locale.
617 619 */
618 620 public GregorianCalendar(TimeZone zone, Locale aLocale) {
619 621 super(zone, aLocale);
620 622 gdate = (BaseCalendar.Date) gcal.newCalendarDate(zone);
621 623 setTimeInMillis(System.currentTimeMillis());
622 624 }
623 625
624 626 /**
625 627 * Constructs a <code>GregorianCalendar</code> with the given date set
626 628 * in the default time zone with the default locale.
627 629 *
628 630 * @param year the value used to set the <code>YEAR</code> calendar field in the calendar.
629 631 * @param month the value used to set the <code>MONTH</code> calendar field in the calendar.
630 632 * Month value is 0-based. e.g., 0 for January.
631 633 * @param dayOfMonth the value used to set the <code>DAY_OF_MONTH</code> calendar field in the calendar.
632 634 */
633 635 public GregorianCalendar(int year, int month, int dayOfMonth) {
634 636 this(year, month, dayOfMonth, 0, 0, 0, 0);
635 637 }
636 638
637 639 /**
638 640 * Constructs a <code>GregorianCalendar</code> with the given date
639 641 * and time set for the default time zone with the default locale.
640 642 *
641 643 * @param year the value used to set the <code>YEAR</code> calendar field in the calendar.
642 644 * @param month the value used to set the <code>MONTH</code> calendar field in the calendar.
643 645 * Month value is 0-based. e.g., 0 for January.
644 646 * @param dayOfMonth the value used to set the <code>DAY_OF_MONTH</code> calendar field in the calendar.
645 647 * @param hourOfDay the value used to set the <code>HOUR_OF_DAY</code> calendar field
646 648 * in the calendar.
647 649 * @param minute the value used to set the <code>MINUTE</code> calendar field
648 650 * in the calendar.
649 651 */
650 652 public GregorianCalendar(int year, int month, int dayOfMonth, int hourOfDay,
651 653 int minute) {
652 654 this(year, month, dayOfMonth, hourOfDay, minute, 0, 0);
653 655 }
654 656
655 657 /**
656 658 * Constructs a GregorianCalendar with the given date
657 659 * and time set for the default time zone with the default locale.
658 660 *
659 661 * @param year the value used to set the <code>YEAR</code> calendar field in the calendar.
660 662 * @param month the value used to set the <code>MONTH</code> calendar field in the calendar.
661 663 * Month value is 0-based. e.g., 0 for January.
662 664 * @param dayOfMonth the value used to set the <code>DAY_OF_MONTH</code> calendar field in the calendar.
663 665 * @param hourOfDay the value used to set the <code>HOUR_OF_DAY</code> calendar field
664 666 * in the calendar.
665 667 * @param minute the value used to set the <code>MINUTE</code> calendar field
666 668 * in the calendar.
667 669 * @param second the value used to set the <code>SECOND</code> calendar field
668 670 * in the calendar.
669 671 */
670 672 public GregorianCalendar(int year, int month, int dayOfMonth, int hourOfDay,
671 673 int minute, int second) {
672 674 this(year, month, dayOfMonth, hourOfDay, minute, second, 0);
673 675 }
674 676
675 677 /**
676 678 * Constructs a <code>GregorianCalendar</code> with the given date
677 679 * and time set for the default time zone with the default locale.
678 680 *
679 681 * @param year the value used to set the <code>YEAR</code> calendar field in the calendar.
680 682 * @param month the value used to set the <code>MONTH</code> calendar field in the calendar.
681 683 * Month value is 0-based. e.g., 0 for January.
682 684 * @param dayOfMonth the value used to set the <code>DAY_OF_MONTH</code> calendar field in the calendar.
683 685 * @param hourOfDay the value used to set the <code>HOUR_OF_DAY</code> calendar field
684 686 * in the calendar.
685 687 * @param minute the value used to set the <code>MINUTE</code> calendar field
686 688 * in the calendar.
687 689 * @param second the value used to set the <code>SECOND</code> calendar field
688 690 * in the calendar.
689 691 * @param millis the value used to set the <code>MILLISECOND</code> calendar field
690 692 */
691 693 GregorianCalendar(int year, int month, int dayOfMonth,
692 694 int hourOfDay, int minute, int second, int millis) {
693 695 super();
694 696 gdate = (BaseCalendar.Date) gcal.newCalendarDate(getZone());
695 697 this.set(YEAR, year);
696 698 this.set(MONTH, month);
697 699 this.set(DAY_OF_MONTH, dayOfMonth);
698 700
699 701 // Set AM_PM and HOUR here to set their stamp values before
700 702 // setting HOUR_OF_DAY (6178071).
701 703 if (hourOfDay >= 12 && hourOfDay <= 23) {
702 704 // If hourOfDay is a valid PM hour, set the correct PM values
703 705 // so that it won't throw an exception in case it's set to
704 706 // non-lenient later.
705 707 this.internalSet(AM_PM, PM);
706 708 this.internalSet(HOUR, hourOfDay - 12);
707 709 } else {
708 710 // The default value for AM_PM is AM.
709 711 // We don't care any out of range value here for leniency.
710 712 this.internalSet(HOUR, hourOfDay);
711 713 }
712 714 // The stamp values of AM_PM and HOUR must be COMPUTED. (6440854)
713 715 setFieldsComputed(HOUR_MASK|AM_PM_MASK);
714 716
715 717 this.set(HOUR_OF_DAY, hourOfDay);
716 718 this.set(MINUTE, minute);
717 719 this.set(SECOND, second);
718 720 // should be changed to set() when this constructor is made
719 721 // public.
720 722 this.internalSet(MILLISECOND, millis);
721 723 }
722 724
723 725 /////////////////
724 726 // Public methods
725 727 /////////////////
726 728
727 729 /**
728 730 * Sets the <code>GregorianCalendar</code> change date. This is the point when the switch
729 731 * from Julian dates to Gregorian dates occurred. Default is October 15,
730 732 * 1582 (Gregorian). Previous to this, dates will be in the Julian calendar.
731 733 * <p>
732 734 * To obtain a pure Julian calendar, set the change date to
733 735 * <code>Date(Long.MAX_VALUE)</code>. To obtain a pure Gregorian calendar,
734 736 * set the change date to <code>Date(Long.MIN_VALUE)</code>.
735 737 *
736 738 * @param date the given Gregorian cutover date.
737 739 */
738 740 public void setGregorianChange(Date date) {
739 741 long cutoverTime = date.getTime();
740 742 if (cutoverTime == gregorianCutover) {
741 743 return;
742 744 }
743 745 // Before changing the cutover date, make sure to have the
744 746 // time of this calendar.
745 747 complete();
746 748 setGregorianChange(cutoverTime);
747 749 }
748 750
749 751 private void setGregorianChange(long cutoverTime) {
750 752 gregorianCutover = cutoverTime;
751 753 gregorianCutoverDate = CalendarUtils.floorDivide(cutoverTime, ONE_DAY)
752 754 + EPOCH_OFFSET;
753 755
754 756 // To provide the "pure" Julian calendar as advertised.
755 757 // Strictly speaking, the last millisecond should be a
756 758 // Gregorian date. However, the API doc specifies that setting
757 759 // the cutover date to Long.MAX_VALUE will make this calendar
↓ open down ↓ |
253 lines elided |
↑ open up ↑ |
758 760 // a pure Julian calendar. (See 4167995)
759 761 if (cutoverTime == Long.MAX_VALUE) {
760 762 gregorianCutoverDate++;
761 763 }
762 764
763 765 BaseCalendar.Date d = getGregorianCutoverDate();
764 766
765 767 // Set the cutover year (in the Gregorian year numbering)
766 768 gregorianCutoverYear = d.getYear();
767 769
768 - BaseCalendar jcal = getJulianCalendarSystem();
769 - d = (BaseCalendar.Date) jcal.newCalendarDate(TimeZone.NO_TIMEZONE);
770 - jcal.getCalendarDateFromFixedDate(d, gregorianCutoverDate - 1);
770 + BaseCalendar julianCal = getJulianCalendarSystem();
771 + d = (BaseCalendar.Date) julianCal.newCalendarDate(TimeZone.NO_TIMEZONE);
772 + julianCal.getCalendarDateFromFixedDate(d, gregorianCutoverDate - 1);
771 773 gregorianCutoverYearJulian = d.getNormalizedYear();
772 774
773 775 if (time < gregorianCutover) {
774 776 // The field values are no longer valid under the new
775 777 // cutover date.
776 778 setUnnormalized();
777 779 }
778 780 }
779 781
780 782 /**
781 783 * Gets the Gregorian Calendar change date. This is the point when the
782 784 * switch from Julian dates to Gregorian dates occurred. Default is
783 785 * October 15, 1582 (Gregorian). Previous to this, dates will be in the Julian
784 786 * calendar.
785 787 *
786 788 * @return the Gregorian cutover date for this <code>GregorianCalendar</code> object.
787 789 */
788 790 public final Date getGregorianChange() {
789 791 return new Date(gregorianCutover);
790 792 }
791 793
792 794 /**
793 795 * Determines if the given year is a leap year. Returns <code>true</code> if
794 796 * the given year is a leap year. To specify BC year numbers,
795 797 * <code>1 - year number</code> must be given. For example, year BC 4 is
796 798 * specified as -3.
797 799 *
798 800 * @param year the given year.
799 801 * @return <code>true</code> if the given year is a leap year; <code>false</code> otherwise.
800 802 */
801 803 public boolean isLeapYear(int year) {
802 804 if ((year & 3) != 0) {
803 805 return false;
804 806 }
805 807
806 808 if (year > gregorianCutoverYear) {
807 809 return (year%100 != 0) || (year%400 == 0); // Gregorian
808 810 }
809 811 if (year < gregorianCutoverYearJulian) {
810 812 return true; // Julian
811 813 }
812 814 boolean gregorian;
813 815 // If the given year is the Gregorian cutover year, we need to
814 816 // determine which calendar system to be applied to February in the year.
↓ open down ↓ |
34 lines elided |
↑ open up ↑ |
815 817 if (gregorianCutoverYear == gregorianCutoverYearJulian) {
816 818 BaseCalendar.Date d = getCalendarDate(gregorianCutoverDate); // Gregorian
817 819 gregorian = d.getMonth() < BaseCalendar.MARCH;
818 820 } else {
819 821 gregorian = year == gregorianCutoverYear;
820 822 }
821 823 return gregorian ? (year%100 != 0) || (year%400 == 0) : true;
822 824 }
823 825
824 826 /**
827 + * Returns {@code "gregory"} as the calendar type.
828 + *
829 + * @return {@code "gregory"}
830 + * @since 1.8
831 + */
832 + @Override
833 + public String getCalendarType() {
834 + return "gregory";
835 + }
836 +
837 + /**
825 838 * Compares this <code>GregorianCalendar</code> to the specified
826 839 * <code>Object</code>. The result is <code>true</code> if and
827 840 * only if the argument is a <code>GregorianCalendar</code> object
828 841 * that represents the same time value (millisecond offset from
829 842 * the <a href="Calendar.html#Epoch">Epoch</a>) under the same
830 843 * <code>Calendar</code> parameters and Gregorian change date as
831 844 * this object.
832 845 *
833 846 * @param obj the object to compare with.
834 847 * @return <code>true</code> if this object is equal to <code>obj</code>;
835 848 * <code>false</code> otherwise.
836 849 * @see Calendar#compareTo(Calendar)
837 850 */
838 851 public boolean equals(Object obj) {
839 852 return obj instanceof GregorianCalendar &&
840 853 super.equals(obj) &&
841 854 gregorianCutover == ((GregorianCalendar)obj).gregorianCutover;
842 855 }
843 856
844 857 /**
845 858 * Generates the hash code for this <code>GregorianCalendar</code> object.
846 859 */
847 860 public int hashCode() {
848 861 return super.hashCode() ^ (int)gregorianCutoverDate;
849 862 }
850 863
851 864 /**
852 865 * Adds the specified (signed) amount of time to the given calendar field,
853 866 * based on the calendar's rules.
854 867 *
855 868 * <p><em>Add rule 1</em>. The value of <code>field</code>
856 869 * after the call minus the value of <code>field</code> before the
857 870 * call is <code>amount</code>, modulo any overflow that has occurred in
858 871 * <code>field</code>. Overflow occurs when a field value exceeds its
859 872 * range and, as a result, the next larger field is incremented or
860 873 * decremented and the field value is adjusted back into its range.</p>
861 874 *
862 875 * <p><em>Add rule 2</em>. If a smaller field is expected to be
863 876 * invariant, but it is impossible for it to be equal to its
864 877 * prior value because of changes in its minimum or maximum after
865 878 * <code>field</code> is changed, then its value is adjusted to be as close
866 879 * as possible to its expected value. A smaller field represents a
867 880 * smaller unit of time. <code>HOUR</code> is a smaller field than
868 881 * <code>DAY_OF_MONTH</code>. No adjustment is made to smaller fields
869 882 * that are not expected to be invariant. The calendar system
870 883 * determines what fields are expected to be invariant.</p>
871 884 *
872 885 * @param field the calendar field.
873 886 * @param amount the amount of date or time to be added to the field.
874 887 * @exception IllegalArgumentException if <code>field</code> is
875 888 * <code>ZONE_OFFSET</code>, <code>DST_OFFSET</code>, or unknown,
876 889 * or if any calendar fields have out-of-range values in
877 890 * non-lenient mode.
878 891 */
879 892 public void add(int field, int amount) {
880 893 // If amount == 0, do nothing even the given field is out of
881 894 // range. This is tested by JCK.
882 895 if (amount == 0) {
883 896 return; // Do nothing!
884 897 }
885 898
886 899 if (field < 0 || field >= ZONE_OFFSET) {
887 900 throw new IllegalArgumentException();
888 901 }
889 902
890 903 // Sync the time and calendar fields.
891 904 complete();
892 905
893 906 if (field == YEAR) {
894 907 int year = internalGet(YEAR);
895 908 if (internalGetEra() == CE) {
896 909 year += amount;
897 910 if (year > 0) {
898 911 set(YEAR, year);
899 912 } else { // year <= 0
900 913 set(YEAR, 1 - year);
901 914 // if year == 0, you get 1 BCE.
902 915 set(ERA, BCE);
903 916 }
904 917 }
905 918 else { // era == BCE
906 919 year -= amount;
907 920 if (year > 0) {
908 921 set(YEAR, year);
909 922 } else { // year <= 0
910 923 set(YEAR, 1 - year);
911 924 // if year == 0, you get 1 CE
912 925 set(ERA, CE);
913 926 }
914 927 }
915 928 pinDayOfMonth();
916 929 } else if (field == MONTH) {
917 930 int month = internalGet(MONTH) + amount;
918 931 int year = internalGet(YEAR);
919 932 int y_amount;
920 933
921 934 if (month >= 0) {
922 935 y_amount = month/12;
923 936 } else {
924 937 y_amount = (month+1)/12 - 1;
925 938 }
926 939 if (y_amount != 0) {
927 940 if (internalGetEra() == CE) {
928 941 year += y_amount;
929 942 if (year > 0) {
930 943 set(YEAR, year);
931 944 } else { // year <= 0
932 945 set(YEAR, 1 - year);
933 946 // if year == 0, you get 1 BCE
934 947 set(ERA, BCE);
935 948 }
936 949 }
937 950 else { // era == BCE
938 951 year -= y_amount;
939 952 if (year > 0) {
↓ open down ↓ |
105 lines elided |
↑ open up ↑ |
940 953 set(YEAR, year);
941 954 } else { // year <= 0
942 955 set(YEAR, 1 - year);
943 956 // if year == 0, you get 1 CE
944 957 set(ERA, CE);
945 958 }
946 959 }
947 960 }
948 961
949 962 if (month >= 0) {
950 - set(MONTH, month % 12);
963 + set(MONTH, month % 12);
951 964 } else {
952 965 // month < 0
953 966 month %= 12;
954 967 if (month < 0) {
955 968 month += 12;
956 969 }
957 970 set(MONTH, JANUARY + month);
958 971 }
959 972 pinDayOfMonth();
960 973 } else if (field == ERA) {
961 974 int era = internalGet(ERA) + amount;
962 975 if (era < 0) {
963 976 era = 0;
964 977 }
965 978 if (era > 1) {
966 979 era = 1;
967 980 }
968 981 set(ERA, era);
969 982 } else {
970 983 long delta = amount;
971 984 long timeOfDay = 0;
972 985 switch (field) {
973 986 // Handle the time fields here. Convert the given
974 987 // amount to milliseconds and call setTimeInMillis.
975 988 case HOUR:
976 989 case HOUR_OF_DAY:
977 990 delta *= 60 * 60 * 1000; // hours to minutes
978 991 break;
979 992
980 993 case MINUTE:
981 994 delta *= 60 * 1000; // minutes to seconds
982 995 break;
983 996
984 997 case SECOND:
985 998 delta *= 1000; // seconds to milliseconds
986 999 break;
987 1000
988 1001 case MILLISECOND:
989 1002 break;
990 1003
991 1004 // Handle week, day and AM_PM fields which involves
992 1005 // time zone offset change adjustment. Convert the
993 1006 // given amount to the number of days.
994 1007 case WEEK_OF_YEAR:
995 1008 case WEEK_OF_MONTH:
996 1009 case DAY_OF_WEEK_IN_MONTH:
997 1010 delta *= 7;
998 1011 break;
999 1012
1000 1013 case DAY_OF_MONTH: // synonym of DATE
1001 1014 case DAY_OF_YEAR:
1002 1015 case DAY_OF_WEEK:
1003 1016 break;
1004 1017
1005 1018 case AM_PM:
1006 1019 // Convert the amount to the number of days (delta)
1007 1020 // and +12 or -12 hours (timeOfDay).
1008 1021 delta = amount / 2;
1009 1022 timeOfDay = 12 * (amount % 2);
1010 1023 break;
1011 1024 }
1012 1025
1013 1026 // The time fields don't require time zone offset change
1014 1027 // adjustment.
1015 1028 if (field >= HOUR) {
1016 1029 setTimeInMillis(time + delta);
1017 1030 return;
1018 1031 }
1019 1032
1020 1033 // The rest of the fields (week, day or AM_PM fields)
1021 1034 // require time zone offset (both GMT and DST) change
1022 1035 // adjustment.
1023 1036
1024 1037 // Translate the current time to the fixed date and time
1025 1038 // of the day.
1026 1039 long fd = getCurrentFixedDate();
1027 1040 timeOfDay += internalGet(HOUR_OF_DAY);
1028 1041 timeOfDay *= 60;
1029 1042 timeOfDay += internalGet(MINUTE);
1030 1043 timeOfDay *= 60;
1031 1044 timeOfDay += internalGet(SECOND);
1032 1045 timeOfDay *= 1000;
1033 1046 timeOfDay += internalGet(MILLISECOND);
1034 1047 if (timeOfDay >= ONE_DAY) {
1035 1048 fd++;
1036 1049 timeOfDay -= ONE_DAY;
1037 1050 } else if (timeOfDay < 0) {
1038 1051 fd--;
1039 1052 timeOfDay += ONE_DAY;
1040 1053 }
1041 1054
1042 1055 fd += delta; // fd is the expected fixed date after the calculation
1043 1056 int zoneOffset = internalGet(ZONE_OFFSET) + internalGet(DST_OFFSET);
1044 1057 setTimeInMillis((fd - EPOCH_OFFSET) * ONE_DAY + timeOfDay - zoneOffset);
1045 1058 zoneOffset -= internalGet(ZONE_OFFSET) + internalGet(DST_OFFSET);
1046 1059 // If the time zone offset has changed, then adjust the difference.
1047 1060 if (zoneOffset != 0) {
1048 1061 setTimeInMillis(time + zoneOffset);
1049 1062 long fd2 = getCurrentFixedDate();
1050 1063 // If the adjustment has changed the date, then take
1051 1064 // the previous one.
1052 1065 if (fd2 != fd) {
1053 1066 setTimeInMillis(time - zoneOffset);
1054 1067 }
1055 1068 }
1056 1069 }
1057 1070 }
1058 1071
1059 1072 /**
1060 1073 * Adds or subtracts (up/down) a single unit of time on the given time
1061 1074 * field without changing larger fields.
1062 1075 * <p>
1063 1076 * <em>Example</em>: Consider a <code>GregorianCalendar</code>
1064 1077 * originally set to December 31, 1999. Calling {@link #roll(int,boolean) roll(Calendar.MONTH, true)}
1065 1078 * sets the calendar to January 31, 1999. The <code>YEAR</code> field is unchanged
1066 1079 * because it is a larger field than <code>MONTH</code>.</p>
1067 1080 *
1068 1081 * @param up indicates if the value of the specified calendar field is to be
1069 1082 * rolled up or rolled down. Use <code>true</code> if rolling up, <code>false</code> otherwise.
1070 1083 * @exception IllegalArgumentException if <code>field</code> is
1071 1084 * <code>ZONE_OFFSET</code>, <code>DST_OFFSET</code>, or unknown,
1072 1085 * or if any calendar fields have out-of-range values in
1073 1086 * non-lenient mode.
1074 1087 * @see #add(int,int)
1075 1088 * @see #set(int,int)
1076 1089 */
1077 1090 public void roll(int field, boolean up) {
1078 1091 roll(field, up ? +1 : -1);
1079 1092 }
1080 1093
1081 1094 /**
1082 1095 * Adds a signed amount to the specified calendar field without changing larger fields.
1083 1096 * A negative roll amount means to subtract from field without changing
1084 1097 * larger fields. If the specified amount is 0, this method performs nothing.
1085 1098 *
1086 1099 * <p>This method calls {@link #complete()} before adding the
1087 1100 * amount so that all the calendar fields are normalized. If there
1088 1101 * is any calendar field having an out-of-range value in non-lenient mode, then an
1089 1102 * <code>IllegalArgumentException</code> is thrown.
1090 1103 *
1091 1104 * <p>
1092 1105 * <em>Example</em>: Consider a <code>GregorianCalendar</code>
1093 1106 * originally set to August 31, 1999. Calling <code>roll(Calendar.MONTH,
1094 1107 * 8)</code> sets the calendar to April 30, <strong>1999</strong>. Using a
1095 1108 * <code>GregorianCalendar</code>, the <code>DAY_OF_MONTH</code> field cannot
1096 1109 * be 31 in the month April. <code>DAY_OF_MONTH</code> is set to the closest possible
1097 1110 * value, 30. The <code>YEAR</code> field maintains the value of 1999 because it
1098 1111 * is a larger field than <code>MONTH</code>.
1099 1112 * <p>
1100 1113 * <em>Example</em>: Consider a <code>GregorianCalendar</code>
1101 1114 * originally set to Sunday June 6, 1999. Calling
1102 1115 * <code>roll(Calendar.WEEK_OF_MONTH, -1)</code> sets the calendar to
1103 1116 * Tuesday June 1, 1999, whereas calling
1104 1117 * <code>add(Calendar.WEEK_OF_MONTH, -1)</code> sets the calendar to
1105 1118 * Sunday May 30, 1999. This is because the roll rule imposes an
1106 1119 * additional constraint: The <code>MONTH</code> must not change when the
1107 1120 * <code>WEEK_OF_MONTH</code> is rolled. Taken together with add rule 1,
1108 1121 * the resultant date must be between Tuesday June 1 and Saturday June
1109 1122 * 5. According to add rule 2, the <code>DAY_OF_WEEK</code>, an invariant
1110 1123 * when changing the <code>WEEK_OF_MONTH</code>, is set to Tuesday, the
1111 1124 * closest possible value to Sunday (where Sunday is the first day of the
1112 1125 * week).</p>
1113 1126 *
1114 1127 * @param field the calendar field.
1115 1128 * @param amount the signed amount to add to <code>field</code>.
1116 1129 * @exception IllegalArgumentException if <code>field</code> is
1117 1130 * <code>ZONE_OFFSET</code>, <code>DST_OFFSET</code>, or unknown,
1118 1131 * or if any calendar fields have out-of-range values in
1119 1132 * non-lenient mode.
1120 1133 * @see #roll(int,boolean)
1121 1134 * @see #add(int,int)
1122 1135 * @see #set(int,int)
1123 1136 * @since 1.2
1124 1137 */
1125 1138 public void roll(int field, int amount) {
1126 1139 // If amount == 0, do nothing even the given field is out of
1127 1140 // range. This is tested by JCK.
1128 1141 if (amount == 0) {
1129 1142 return;
1130 1143 }
1131 1144
1132 1145 if (field < 0 || field >= ZONE_OFFSET) {
1133 1146 throw new IllegalArgumentException();
1134 1147 }
1135 1148
1136 1149 // Sync the time and calendar fields.
1137 1150 complete();
1138 1151
1139 1152 int min = getMinimum(field);
1140 1153 int max = getMaximum(field);
1141 1154
1142 1155 switch (field) {
1143 1156 case AM_PM:
1144 1157 case ERA:
1145 1158 case YEAR:
1146 1159 case MINUTE:
1147 1160 case SECOND:
1148 1161 case MILLISECOND:
1149 1162 // These fields are handled simply, since they have fixed minima
1150 1163 // and maxima. The field DAY_OF_MONTH is almost as simple. Other
1151 1164 // fields are complicated, since the range within they must roll
1152 1165 // varies depending on the date.
1153 1166 break;
1154 1167
1155 1168 case HOUR:
1156 1169 case HOUR_OF_DAY:
1157 1170 {
1158 1171 int unit = max + 1; // 12 or 24 hours
1159 1172 int h = internalGet(field);
1160 1173 int nh = (h + amount) % unit;
1161 1174 if (nh < 0) {
1162 1175 nh += unit;
1163 1176 }
1164 1177 time += ONE_HOUR * (nh - h);
1165 1178
1166 1179 // The day might have changed, which could happen if
1167 1180 // the daylight saving time transition brings it to
1168 1181 // the next day, although it's very unlikely. But we
1169 1182 // have to make sure not to change the larger fields.
1170 1183 CalendarDate d = calsys.getCalendarDate(time, getZone());
1171 1184 if (internalGet(DAY_OF_MONTH) != d.getDayOfMonth()) {
1172 1185 d.setDate(internalGet(YEAR),
1173 1186 internalGet(MONTH) + 1,
1174 1187 internalGet(DAY_OF_MONTH));
1175 1188 if (field == HOUR) {
1176 1189 assert (internalGet(AM_PM) == PM);
1177 1190 d.addHours(+12); // restore PM
1178 1191 }
1179 1192 time = calsys.getTime(d);
1180 1193 }
1181 1194 int hourOfDay = d.getHours();
1182 1195 internalSet(field, hourOfDay % unit);
1183 1196 if (field == HOUR) {
1184 1197 internalSet(HOUR_OF_DAY, hourOfDay);
1185 1198 } else {
1186 1199 internalSet(AM_PM, hourOfDay / 12);
1187 1200 internalSet(HOUR, hourOfDay % 12);
1188 1201 }
1189 1202
1190 1203 // Time zone offset and/or daylight saving might have changed.
1191 1204 int zoneOffset = d.getZoneOffset();
1192 1205 int saving = d.getDaylightSaving();
1193 1206 internalSet(ZONE_OFFSET, zoneOffset - saving);
1194 1207 internalSet(DST_OFFSET, saving);
1195 1208 return;
1196 1209 }
1197 1210
1198 1211 case MONTH:
1199 1212 // Rolling the month involves both pinning the final value to [0, 11]
1200 1213 // and adjusting the DAY_OF_MONTH if necessary. We only adjust the
1201 1214 // DAY_OF_MONTH if, after updating the MONTH field, it is illegal.
1202 1215 // E.g., <jan31>.roll(MONTH, 1) -> <feb28> or <feb29>.
1203 1216 {
1204 1217 if (!isCutoverYear(cdate.getNormalizedYear())) {
1205 1218 int mon = (internalGet(MONTH) + amount) % 12;
1206 1219 if (mon < 0) {
1207 1220 mon += 12;
1208 1221 }
1209 1222 set(MONTH, mon);
1210 1223
1211 1224 // Keep the day of month in the range. We don't want to spill over
1212 1225 // into the next month; e.g., we don't want jan31 + 1 mo -> feb31 ->
1213 1226 // mar3.
1214 1227 int monthLen = monthLength(mon);
1215 1228 if (internalGet(DAY_OF_MONTH) > monthLen) {
1216 1229 set(DAY_OF_MONTH, monthLen);
1217 1230 }
1218 1231 } else {
1219 1232 // We need to take care of different lengths in
1220 1233 // year and month due to the cutover.
1221 1234 int yearLength = getActualMaximum(MONTH) + 1;
1222 1235 int mon = (internalGet(MONTH) + amount) % yearLength;
1223 1236 if (mon < 0) {
1224 1237 mon += yearLength;
1225 1238 }
1226 1239 set(MONTH, mon);
1227 1240 int monthLen = getActualMaximum(DAY_OF_MONTH);
1228 1241 if (internalGet(DAY_OF_MONTH) > monthLen) {
1229 1242 set(DAY_OF_MONTH, monthLen);
1230 1243 }
1231 1244 }
1232 1245 return;
1233 1246 }
1234 1247
1235 1248 case WEEK_OF_YEAR:
1236 1249 {
1237 1250 int y = cdate.getNormalizedYear();
1238 1251 max = getActualMaximum(WEEK_OF_YEAR);
1239 1252 set(DAY_OF_WEEK, internalGet(DAY_OF_WEEK));
1240 1253 int woy = internalGet(WEEK_OF_YEAR);
1241 1254 int value = woy + amount;
1242 1255 if (!isCutoverYear(y)) {
1243 1256 // If the new value is in between min and max
1244 1257 // (exclusive), then we can use the value.
1245 1258 if (value > min && value < max) {
1246 1259 set(WEEK_OF_YEAR, value);
1247 1260 return;
1248 1261 }
1249 1262 long fd = getCurrentFixedDate();
1250 1263 // Make sure that the min week has the current DAY_OF_WEEK
1251 1264 long day1 = fd - (7 * (woy - min));
1252 1265 if (calsys.getYearFromFixedDate(day1) != y) {
1253 1266 min++;
1254 1267 }
1255 1268
1256 1269 // Make sure the same thing for the max week
1257 1270 fd += 7 * (max - internalGet(WEEK_OF_YEAR));
1258 1271 if (calsys.getYearFromFixedDate(fd) != y) {
1259 1272 max--;
1260 1273 }
1261 1274 break;
1262 1275 }
1263 1276
1264 1277 // Handle cutover here.
1265 1278 long fd = getCurrentFixedDate();
1266 1279 BaseCalendar cal;
1267 1280 if (gregorianCutoverYear == gregorianCutoverYearJulian) {
1268 1281 cal = getCutoverCalendarSystem();
1269 1282 } else if (y == gregorianCutoverYear) {
1270 1283 cal = gcal;
1271 1284 } else {
1272 1285 cal = getJulianCalendarSystem();
1273 1286 }
1274 1287 long day1 = fd - (7 * (woy - min));
1275 1288 // Make sure that the min week has the current DAY_OF_WEEK
1276 1289 if (cal.getYearFromFixedDate(day1) != y) {
1277 1290 min++;
1278 1291 }
1279 1292
1280 1293 // Make sure the same thing for the max week
1281 1294 fd += 7 * (max - woy);
1282 1295 cal = (fd >= gregorianCutoverDate) ? gcal : getJulianCalendarSystem();
1283 1296 if (cal.getYearFromFixedDate(fd) != y) {
1284 1297 max--;
1285 1298 }
1286 1299 // value: the new WEEK_OF_YEAR which must be converted
1287 1300 // to month and day of month.
1288 1301 value = getRolledValue(woy, amount, min, max) - 1;
1289 1302 BaseCalendar.Date d = getCalendarDate(day1 + value * 7);
1290 1303 set(MONTH, d.getMonth() - 1);
1291 1304 set(DAY_OF_MONTH, d.getDayOfMonth());
1292 1305 return;
1293 1306 }
1294 1307
1295 1308 case WEEK_OF_MONTH:
1296 1309 {
1297 1310 boolean isCutoverYear = isCutoverYear(cdate.getNormalizedYear());
1298 1311 // dow: relative day of week from first day of week
1299 1312 int dow = internalGet(DAY_OF_WEEK) - getFirstDayOfWeek();
1300 1313 if (dow < 0) {
1301 1314 dow += 7;
1302 1315 }
1303 1316
1304 1317 long fd = getCurrentFixedDate();
1305 1318 long month1; // fixed date of the first day (usually 1) of the month
1306 1319 int monthLength; // actual month length
1307 1320 if (isCutoverYear) {
1308 1321 month1 = getFixedDateMonth1(cdate, fd);
1309 1322 monthLength = actualMonthLength();
1310 1323 } else {
1311 1324 month1 = fd - internalGet(DAY_OF_MONTH) + 1;
1312 1325 monthLength = calsys.getMonthLength(cdate);
1313 1326 }
1314 1327
1315 1328 // the first day of week of the month.
1316 1329 long monthDay1st = BaseCalendar.getDayOfWeekDateOnOrBefore(month1 + 6,
1317 1330 getFirstDayOfWeek());
1318 1331 // if the week has enough days to form a week, the
1319 1332 // week starts from the previous month.
1320 1333 if ((int)(monthDay1st - month1) >= getMinimalDaysInFirstWeek()) {
1321 1334 monthDay1st -= 7;
1322 1335 }
1323 1336 max = getActualMaximum(field);
1324 1337
1325 1338 // value: the new WEEK_OF_MONTH value
1326 1339 int value = getRolledValue(internalGet(field), amount, 1, max) - 1;
1327 1340
1328 1341 // nfd: fixed date of the rolled date
1329 1342 long nfd = monthDay1st + value * 7 + dow;
1330 1343
1331 1344 // Unlike WEEK_OF_YEAR, we need to change day of week if the
1332 1345 // nfd is out of the month.
1333 1346 if (nfd < month1) {
1334 1347 nfd = month1;
1335 1348 } else if (nfd >= (month1 + monthLength)) {
1336 1349 nfd = month1 + monthLength - 1;
1337 1350 }
1338 1351 int dayOfMonth;
1339 1352 if (isCutoverYear) {
1340 1353 // If we are in the cutover year, convert nfd to
1341 1354 // its calendar date and use dayOfMonth.
1342 1355 BaseCalendar.Date d = getCalendarDate(nfd);
1343 1356 dayOfMonth = d.getDayOfMonth();
1344 1357 } else {
1345 1358 dayOfMonth = (int)(nfd - month1) + 1;
1346 1359 }
1347 1360 set(DAY_OF_MONTH, dayOfMonth);
1348 1361 return;
1349 1362 }
1350 1363
1351 1364 case DAY_OF_MONTH:
1352 1365 {
1353 1366 if (!isCutoverYear(cdate.getNormalizedYear())) {
1354 1367 max = calsys.getMonthLength(cdate);
1355 1368 break;
1356 1369 }
1357 1370
1358 1371 // Cutover year handling
1359 1372 long fd = getCurrentFixedDate();
1360 1373 long month1 = getFixedDateMonth1(cdate, fd);
1361 1374 // It may not be a regular month. Convert the date and range to
1362 1375 // the relative values, perform the roll, and
1363 1376 // convert the result back to the rolled date.
1364 1377 int value = getRolledValue((int)(fd - month1), amount, 0, actualMonthLength() - 1);
1365 1378 BaseCalendar.Date d = getCalendarDate(month1 + value);
1366 1379 assert d.getMonth()-1 == internalGet(MONTH);
1367 1380 set(DAY_OF_MONTH, d.getDayOfMonth());
1368 1381 return;
1369 1382 }
1370 1383
1371 1384 case DAY_OF_YEAR:
1372 1385 {
1373 1386 max = getActualMaximum(field);
1374 1387 if (!isCutoverYear(cdate.getNormalizedYear())) {
1375 1388 break;
1376 1389 }
1377 1390
1378 1391 // Handle cutover here.
1379 1392 long fd = getCurrentFixedDate();
1380 1393 long jan1 = fd - internalGet(DAY_OF_YEAR) + 1;
1381 1394 int value = getRolledValue((int)(fd - jan1) + 1, amount, min, max);
1382 1395 BaseCalendar.Date d = getCalendarDate(jan1 + value - 1);
1383 1396 set(MONTH, d.getMonth() - 1);
1384 1397 set(DAY_OF_MONTH, d.getDayOfMonth());
1385 1398 return;
1386 1399 }
1387 1400
1388 1401 case DAY_OF_WEEK:
1389 1402 {
1390 1403 if (!isCutoverYear(cdate.getNormalizedYear())) {
1391 1404 // If the week of year is in the same year, we can
1392 1405 // just change DAY_OF_WEEK.
1393 1406 int weekOfYear = internalGet(WEEK_OF_YEAR);
1394 1407 if (weekOfYear > 1 && weekOfYear < 52) {
1395 1408 set(WEEK_OF_YEAR, weekOfYear); // update stamp[WEEK_OF_YEAR]
1396 1409 max = SATURDAY;
1397 1410 break;
1398 1411 }
1399 1412 }
1400 1413
1401 1414 // We need to handle it in a different way around year
1402 1415 // boundaries and in the cutover year. Note that
1403 1416 // changing era and year values violates the roll
1404 1417 // rule: not changing larger calendar fields...
1405 1418 amount %= 7;
1406 1419 if (amount == 0) {
1407 1420 return;
1408 1421 }
1409 1422 long fd = getCurrentFixedDate();
1410 1423 long dowFirst = BaseCalendar.getDayOfWeekDateOnOrBefore(fd, getFirstDayOfWeek());
1411 1424 fd += amount;
1412 1425 if (fd < dowFirst) {
1413 1426 fd += 7;
1414 1427 } else if (fd >= dowFirst + 7) {
1415 1428 fd -= 7;
1416 1429 }
1417 1430 BaseCalendar.Date d = getCalendarDate(fd);
1418 1431 set(ERA, (d.getNormalizedYear() <= 0 ? BCE : CE));
1419 1432 set(d.getYear(), d.getMonth() - 1, d.getDayOfMonth());
1420 1433 return;
1421 1434 }
1422 1435
1423 1436 case DAY_OF_WEEK_IN_MONTH:
1424 1437 {
1425 1438 min = 1; // after normalized, min should be 1.
1426 1439 if (!isCutoverYear(cdate.getNormalizedYear())) {
1427 1440 int dom = internalGet(DAY_OF_MONTH);
1428 1441 int monthLength = calsys.getMonthLength(cdate);
1429 1442 int lastDays = monthLength % 7;
1430 1443 max = monthLength / 7;
1431 1444 int x = (dom - 1) % 7;
1432 1445 if (x < lastDays) {
1433 1446 max++;
1434 1447 }
1435 1448 set(DAY_OF_WEEK, internalGet(DAY_OF_WEEK));
1436 1449 break;
1437 1450 }
1438 1451
1439 1452 // Cutover year handling
1440 1453 long fd = getCurrentFixedDate();
1441 1454 long month1 = getFixedDateMonth1(cdate, fd);
1442 1455 int monthLength = actualMonthLength();
1443 1456 int lastDays = monthLength % 7;
1444 1457 max = monthLength / 7;
1445 1458 int x = (int)(fd - month1) % 7;
1446 1459 if (x < lastDays) {
1447 1460 max++;
1448 1461 }
1449 1462 int value = getRolledValue(internalGet(field), amount, min, max) - 1;
1450 1463 fd = month1 + value * 7 + x;
1451 1464 BaseCalendar cal = (fd >= gregorianCutoverDate) ? gcal : getJulianCalendarSystem();
1452 1465 BaseCalendar.Date d = (BaseCalendar.Date) cal.newCalendarDate(TimeZone.NO_TIMEZONE);
1453 1466 cal.getCalendarDateFromFixedDate(d, fd);
1454 1467 set(DAY_OF_MONTH, d.getDayOfMonth());
1455 1468 return;
1456 1469 }
1457 1470 }
1458 1471
1459 1472 set(field, getRolledValue(internalGet(field), amount, min, max));
1460 1473 }
1461 1474
1462 1475 /**
1463 1476 * Returns the minimum value for the given calendar field of this
1464 1477 * <code>GregorianCalendar</code> instance. The minimum value is
1465 1478 * defined as the smallest value returned by the {@link
1466 1479 * Calendar#get(int) get} method for any possible time value,
1467 1480 * taking into consideration the current values of the
1468 1481 * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
1469 1482 * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
1470 1483 * {@link #getGregorianChange() getGregorianChange} and
1471 1484 * {@link Calendar#getTimeZone() getTimeZone} methods.
1472 1485 *
1473 1486 * @param field the calendar field.
1474 1487 * @return the minimum value for the given calendar field.
1475 1488 * @see #getMaximum(int)
1476 1489 * @see #getGreatestMinimum(int)
1477 1490 * @see #getLeastMaximum(int)
1478 1491 * @see #getActualMinimum(int)
1479 1492 * @see #getActualMaximum(int)
1480 1493 */
1481 1494 public int getMinimum(int field) {
1482 1495 return MIN_VALUES[field];
1483 1496 }
1484 1497
1485 1498 /**
1486 1499 * Returns the maximum value for the given calendar field of this
1487 1500 * <code>GregorianCalendar</code> instance. The maximum value is
1488 1501 * defined as the largest value returned by the {@link
1489 1502 * Calendar#get(int) get} method for any possible time value,
1490 1503 * taking into consideration the current values of the
1491 1504 * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
1492 1505 * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
1493 1506 * {@link #getGregorianChange() getGregorianChange} and
1494 1507 * {@link Calendar#getTimeZone() getTimeZone} methods.
1495 1508 *
1496 1509 * @param field the calendar field.
1497 1510 * @return the maximum value for the given calendar field.
1498 1511 * @see #getMinimum(int)
1499 1512 * @see #getGreatestMinimum(int)
1500 1513 * @see #getLeastMaximum(int)
1501 1514 * @see #getActualMinimum(int)
1502 1515 * @see #getActualMaximum(int)
1503 1516 */
1504 1517 public int getMaximum(int field) {
1505 1518 switch (field) {
1506 1519 case MONTH:
1507 1520 case DAY_OF_MONTH:
1508 1521 case DAY_OF_YEAR:
1509 1522 case WEEK_OF_YEAR:
1510 1523 case WEEK_OF_MONTH:
1511 1524 case DAY_OF_WEEK_IN_MONTH:
1512 1525 case YEAR:
1513 1526 {
1514 1527 // On or after Gregorian 200-3-1, Julian and Gregorian
1515 1528 // calendar dates are the same or Gregorian dates are
1516 1529 // larger (i.e., there is a "gap") after 300-3-1.
1517 1530 if (gregorianCutoverYear > 200) {
1518 1531 break;
1519 1532 }
1520 1533 // There might be "overlapping" dates.
1521 1534 GregorianCalendar gc = (GregorianCalendar) clone();
1522 1535 gc.setLenient(true);
1523 1536 gc.setTimeInMillis(gregorianCutover);
1524 1537 int v1 = gc.getActualMaximum(field);
1525 1538 gc.setTimeInMillis(gregorianCutover-1);
1526 1539 int v2 = gc.getActualMaximum(field);
1527 1540 return Math.max(MAX_VALUES[field], Math.max(v1, v2));
1528 1541 }
1529 1542 }
1530 1543 return MAX_VALUES[field];
1531 1544 }
1532 1545
1533 1546 /**
1534 1547 * Returns the highest minimum value for the given calendar field
1535 1548 * of this <code>GregorianCalendar</code> instance. The highest
1536 1549 * minimum value is defined as the largest value returned by
1537 1550 * {@link #getActualMinimum(int)} for any possible time value,
1538 1551 * taking into consideration the current values of the
1539 1552 * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
1540 1553 * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
1541 1554 * {@link #getGregorianChange() getGregorianChange} and
1542 1555 * {@link Calendar#getTimeZone() getTimeZone} methods.
1543 1556 *
1544 1557 * @param field the calendar field.
1545 1558 * @return the highest minimum value for the given calendar field.
1546 1559 * @see #getMinimum(int)
1547 1560 * @see #getMaximum(int)
1548 1561 * @see #getLeastMaximum(int)
1549 1562 * @see #getActualMinimum(int)
1550 1563 * @see #getActualMaximum(int)
1551 1564 */
1552 1565 public int getGreatestMinimum(int field) {
1553 1566 if (field == DAY_OF_MONTH) {
1554 1567 BaseCalendar.Date d = getGregorianCutoverDate();
1555 1568 long mon1 = getFixedDateMonth1(d, gregorianCutoverDate);
1556 1569 d = getCalendarDate(mon1);
1557 1570 return Math.max(MIN_VALUES[field], d.getDayOfMonth());
1558 1571 }
1559 1572 return MIN_VALUES[field];
1560 1573 }
1561 1574
1562 1575 /**
1563 1576 * Returns the lowest maximum value for the given calendar field
1564 1577 * of this <code>GregorianCalendar</code> instance. The lowest
1565 1578 * maximum value is defined as the smallest value returned by
1566 1579 * {@link #getActualMaximum(int)} for any possible time value,
1567 1580 * taking into consideration the current values of the
1568 1581 * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
1569 1582 * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
1570 1583 * {@link #getGregorianChange() getGregorianChange} and
1571 1584 * {@link Calendar#getTimeZone() getTimeZone} methods.
1572 1585 *
1573 1586 * @param field the calendar field
1574 1587 * @return the lowest maximum value for the given calendar field.
1575 1588 * @see #getMinimum(int)
1576 1589 * @see #getMaximum(int)
1577 1590 * @see #getGreatestMinimum(int)
1578 1591 * @see #getActualMinimum(int)
1579 1592 * @see #getActualMaximum(int)
1580 1593 */
1581 1594 public int getLeastMaximum(int field) {
1582 1595 switch (field) {
1583 1596 case MONTH:
1584 1597 case DAY_OF_MONTH:
1585 1598 case DAY_OF_YEAR:
1586 1599 case WEEK_OF_YEAR:
1587 1600 case WEEK_OF_MONTH:
1588 1601 case DAY_OF_WEEK_IN_MONTH:
1589 1602 case YEAR:
1590 1603 {
1591 1604 GregorianCalendar gc = (GregorianCalendar) clone();
1592 1605 gc.setLenient(true);
1593 1606 gc.setTimeInMillis(gregorianCutover);
1594 1607 int v1 = gc.getActualMaximum(field);
1595 1608 gc.setTimeInMillis(gregorianCutover-1);
1596 1609 int v2 = gc.getActualMaximum(field);
1597 1610 return Math.min(LEAST_MAX_VALUES[field], Math.min(v1, v2));
1598 1611 }
1599 1612 }
1600 1613 return LEAST_MAX_VALUES[field];
1601 1614 }
1602 1615
1603 1616 /**
1604 1617 * Returns the minimum value that this calendar field could have,
1605 1618 * taking into consideration the given time value and the current
1606 1619 * values of the
1607 1620 * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
1608 1621 * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
1609 1622 * {@link #getGregorianChange() getGregorianChange} and
1610 1623 * {@link Calendar#getTimeZone() getTimeZone} methods.
1611 1624 *
1612 1625 * <p>For example, if the Gregorian change date is January 10,
1613 1626 * 1970 and the date of this <code>GregorianCalendar</code> is
1614 1627 * January 20, 1970, the actual minimum value of the
1615 1628 * <code>DAY_OF_MONTH</code> field is 10 because the previous date
1616 1629 * of January 10, 1970 is December 27, 1996 (in the Julian
1617 1630 * calendar). Therefore, December 28, 1969 to January 9, 1970
1618 1631 * don't exist.
1619 1632 *
1620 1633 * @param field the calendar field
1621 1634 * @return the minimum of the given field for the time value of
1622 1635 * this <code>GregorianCalendar</code>
1623 1636 * @see #getMinimum(int)
1624 1637 * @see #getMaximum(int)
1625 1638 * @see #getGreatestMinimum(int)
1626 1639 * @see #getLeastMaximum(int)
1627 1640 * @see #getActualMaximum(int)
1628 1641 * @since 1.2
1629 1642 */
1630 1643 public int getActualMinimum(int field) {
1631 1644 if (field == DAY_OF_MONTH) {
1632 1645 GregorianCalendar gc = getNormalizedCalendar();
1633 1646 int year = gc.cdate.getNormalizedYear();
1634 1647 if (year == gregorianCutoverYear || year == gregorianCutoverYearJulian) {
1635 1648 long month1 = getFixedDateMonth1(gc.cdate, gc.calsys.getFixedDate(gc.cdate));
1636 1649 BaseCalendar.Date d = getCalendarDate(month1);
1637 1650 return d.getDayOfMonth();
1638 1651 }
1639 1652 }
1640 1653 return getMinimum(field);
1641 1654 }
1642 1655
1643 1656 /**
1644 1657 * Returns the maximum value that this calendar field could have,
1645 1658 * taking into consideration the given time value and the current
1646 1659 * values of the
1647 1660 * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
1648 1661 * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
1649 1662 * {@link #getGregorianChange() getGregorianChange} and
1650 1663 * {@link Calendar#getTimeZone() getTimeZone} methods.
1651 1664 * For example, if the date of this instance is February 1, 2004,
1652 1665 * the actual maximum value of the <code>DAY_OF_MONTH</code> field
1653 1666 * is 29 because 2004 is a leap year, and if the date of this
1654 1667 * instance is February 1, 2005, it's 28.
1655 1668 *
1656 1669 * <p>This method calculates the maximum value of {@link
1657 1670 * Calendar#WEEK_OF_YEAR WEEK_OF_YEAR} based on the {@link
1658 1671 * Calendar#YEAR YEAR} (calendar year) value, not the <a
1659 1672 * href="#week_year">week year</a>. Call {@link
1660 1673 * #getWeeksInWeekYear()} to get the maximum value of {@code
1661 1674 * WEEK_OF_YEAR} in the week year of this {@code GregorianCalendar}.
1662 1675 *
1663 1676 * @param field the calendar field
1664 1677 * @return the maximum of the given field for the time value of
1665 1678 * this <code>GregorianCalendar</code>
1666 1679 * @see #getMinimum(int)
1667 1680 * @see #getMaximum(int)
1668 1681 * @see #getGreatestMinimum(int)
1669 1682 * @see #getLeastMaximum(int)
1670 1683 * @see #getActualMinimum(int)
1671 1684 * @since 1.2
1672 1685 */
1673 1686 public int getActualMaximum(int field) {
1674 1687 final int fieldsForFixedMax = ERA_MASK|DAY_OF_WEEK_MASK|HOUR_MASK|AM_PM_MASK|
1675 1688 HOUR_OF_DAY_MASK|MINUTE_MASK|SECOND_MASK|MILLISECOND_MASK|
1676 1689 ZONE_OFFSET_MASK|DST_OFFSET_MASK;
1677 1690 if ((fieldsForFixedMax & (1<<field)) != 0) {
1678 1691 return getMaximum(field);
1679 1692 }
1680 1693
1681 1694 GregorianCalendar gc = getNormalizedCalendar();
1682 1695 BaseCalendar.Date date = gc.cdate;
1683 1696 BaseCalendar cal = gc.calsys;
1684 1697 int normalizedYear = date.getNormalizedYear();
1685 1698
1686 1699 int value = -1;
1687 1700 switch (field) {
1688 1701 case MONTH:
1689 1702 {
1690 1703 if (!gc.isCutoverYear(normalizedYear)) {
1691 1704 value = DECEMBER;
1692 1705 break;
1693 1706 }
1694 1707
1695 1708 // January 1 of the next year may or may not exist.
1696 1709 long nextJan1;
1697 1710 do {
1698 1711 nextJan1 = gcal.getFixedDate(++normalizedYear, BaseCalendar.JANUARY, 1, null);
1699 1712 } while (nextJan1 < gregorianCutoverDate);
1700 1713 BaseCalendar.Date d = (BaseCalendar.Date) date.clone();
1701 1714 cal.getCalendarDateFromFixedDate(d, nextJan1 - 1);
1702 1715 value = d.getMonth() - 1;
1703 1716 }
1704 1717 break;
1705 1718
1706 1719 case DAY_OF_MONTH:
1707 1720 {
1708 1721 value = cal.getMonthLength(date);
1709 1722 if (!gc.isCutoverYear(normalizedYear) || date.getDayOfMonth() == value) {
1710 1723 break;
1711 1724 }
1712 1725
1713 1726 // Handle cutover year.
1714 1727 long fd = gc.getCurrentFixedDate();
1715 1728 if (fd >= gregorianCutoverDate) {
1716 1729 break;
1717 1730 }
1718 1731 int monthLength = gc.actualMonthLength();
1719 1732 long monthEnd = gc.getFixedDateMonth1(gc.cdate, fd) + monthLength - 1;
1720 1733 // Convert the fixed date to its calendar date.
1721 1734 BaseCalendar.Date d = gc.getCalendarDate(monthEnd);
1722 1735 value = d.getDayOfMonth();
1723 1736 }
1724 1737 break;
1725 1738
1726 1739 case DAY_OF_YEAR:
1727 1740 {
1728 1741 if (!gc.isCutoverYear(normalizedYear)) {
1729 1742 value = cal.getYearLength(date);
1730 1743 break;
1731 1744 }
1732 1745
1733 1746 // Handle cutover year.
1734 1747 long jan1;
1735 1748 if (gregorianCutoverYear == gregorianCutoverYearJulian) {
1736 1749 BaseCalendar cocal = gc.getCutoverCalendarSystem();
1737 1750 jan1 = cocal.getFixedDate(normalizedYear, 1, 1, null);
1738 1751 } else if (normalizedYear == gregorianCutoverYearJulian) {
1739 1752 jan1 = cal.getFixedDate(normalizedYear, 1, 1, null);
1740 1753 } else {
1741 1754 jan1 = gregorianCutoverDate;
1742 1755 }
1743 1756 // January 1 of the next year may or may not exist.
1744 1757 long nextJan1 = gcal.getFixedDate(++normalizedYear, 1, 1, null);
1745 1758 if (nextJan1 < gregorianCutoverDate) {
1746 1759 nextJan1 = gregorianCutoverDate;
1747 1760 }
1748 1761 assert jan1 <= cal.getFixedDate(date.getNormalizedYear(), date.getMonth(),
1749 1762 date.getDayOfMonth(), date);
1750 1763 assert nextJan1 >= cal.getFixedDate(date.getNormalizedYear(), date.getMonth(),
1751 1764 date.getDayOfMonth(), date);
1752 1765 value = (int)(nextJan1 - jan1);
1753 1766 }
1754 1767 break;
1755 1768
1756 1769 case WEEK_OF_YEAR:
1757 1770 {
1758 1771 if (!gc.isCutoverYear(normalizedYear)) {
1759 1772 // Get the day of week of January 1 of the year
1760 1773 CalendarDate d = cal.newCalendarDate(TimeZone.NO_TIMEZONE);
1761 1774 d.setDate(date.getYear(), BaseCalendar.JANUARY, 1);
1762 1775 int dayOfWeek = cal.getDayOfWeek(d);
1763 1776 // Normalize the day of week with the firstDayOfWeek value
1764 1777 dayOfWeek -= getFirstDayOfWeek();
1765 1778 if (dayOfWeek < 0) {
1766 1779 dayOfWeek += 7;
1767 1780 }
1768 1781 value = 52;
1769 1782 int magic = dayOfWeek + getMinimalDaysInFirstWeek() - 1;
1770 1783 if ((magic == 6) ||
1771 1784 (date.isLeapYear() && (magic == 5 || magic == 12))) {
1772 1785 value++;
1773 1786 }
1774 1787 break;
1775 1788 }
1776 1789
1777 1790 if (gc == this) {
1778 1791 gc = (GregorianCalendar) gc.clone();
1779 1792 }
1780 1793 int maxDayOfYear = getActualMaximum(DAY_OF_YEAR);
1781 1794 gc.set(DAY_OF_YEAR, maxDayOfYear);
1782 1795 value = gc.get(WEEK_OF_YEAR);
1783 1796 if (internalGet(YEAR) != gc.getWeekYear()) {
1784 1797 gc.set(DAY_OF_YEAR, maxDayOfYear - 7);
1785 1798 value = gc.get(WEEK_OF_YEAR);
1786 1799 }
1787 1800 }
1788 1801 break;
1789 1802
1790 1803 case WEEK_OF_MONTH:
1791 1804 {
1792 1805 if (!gc.isCutoverYear(normalizedYear)) {
1793 1806 CalendarDate d = cal.newCalendarDate(null);
1794 1807 d.setDate(date.getYear(), date.getMonth(), 1);
1795 1808 int dayOfWeek = cal.getDayOfWeek(d);
1796 1809 int monthLength = cal.getMonthLength(d);
1797 1810 dayOfWeek -= getFirstDayOfWeek();
1798 1811 if (dayOfWeek < 0) {
1799 1812 dayOfWeek += 7;
1800 1813 }
1801 1814 int nDaysFirstWeek = 7 - dayOfWeek; // # of days in the first week
1802 1815 value = 3;
1803 1816 if (nDaysFirstWeek >= getMinimalDaysInFirstWeek()) {
1804 1817 value++;
1805 1818 }
1806 1819 monthLength -= nDaysFirstWeek + 7 * 3;
1807 1820 if (monthLength > 0) {
1808 1821 value++;
1809 1822 if (monthLength > 7) {
1810 1823 value++;
1811 1824 }
1812 1825 }
1813 1826 break;
1814 1827 }
1815 1828
1816 1829 // Cutover year handling
1817 1830 if (gc == this) {
1818 1831 gc = (GregorianCalendar) gc.clone();
1819 1832 }
1820 1833 int y = gc.internalGet(YEAR);
1821 1834 int m = gc.internalGet(MONTH);
1822 1835 do {
1823 1836 value = gc.get(WEEK_OF_MONTH);
1824 1837 gc.add(WEEK_OF_MONTH, +1);
1825 1838 } while (gc.get(YEAR) == y && gc.get(MONTH) == m);
1826 1839 }
1827 1840 break;
1828 1841
1829 1842 case DAY_OF_WEEK_IN_MONTH:
1830 1843 {
1831 1844 // may be in the Gregorian cutover month
1832 1845 int ndays, dow1;
1833 1846 int dow = date.getDayOfWeek();
1834 1847 if (!gc.isCutoverYear(normalizedYear)) {
1835 1848 BaseCalendar.Date d = (BaseCalendar.Date) date.clone();
1836 1849 ndays = cal.getMonthLength(d);
1837 1850 d.setDayOfMonth(1);
1838 1851 cal.normalize(d);
1839 1852 dow1 = d.getDayOfWeek();
1840 1853 } else {
1841 1854 // Let a cloned GregorianCalendar take care of the cutover cases.
1842 1855 if (gc == this) {
1843 1856 gc = (GregorianCalendar) clone();
1844 1857 }
1845 1858 ndays = gc.actualMonthLength();
1846 1859 gc.set(DAY_OF_MONTH, gc.getActualMinimum(DAY_OF_MONTH));
1847 1860 dow1 = gc.get(DAY_OF_WEEK);
1848 1861 }
1849 1862 int x = dow - dow1;
1850 1863 if (x < 0) {
1851 1864 x += 7;
1852 1865 }
1853 1866 ndays -= x;
1854 1867 value = (ndays + 6) / 7;
1855 1868 }
1856 1869 break;
1857 1870
1858 1871 case YEAR:
1859 1872 /* The year computation is no different, in principle, from the
1860 1873 * others, however, the range of possible maxima is large. In
1861 1874 * addition, the way we know we've exceeded the range is different.
1862 1875 * For these reasons, we use the special case code below to handle
1863 1876 * this field.
1864 1877 *
1865 1878 * The actual maxima for YEAR depend on the type of calendar:
1866 1879 *
1867 1880 * Gregorian = May 17, 292275056 BCE - Aug 17, 292278994 CE
1868 1881 * Julian = Dec 2, 292269055 BCE - Jan 3, 292272993 CE
1869 1882 * Hybrid = Dec 2, 292269055 BCE - Aug 17, 292278994 CE
1870 1883 *
1871 1884 * We know we've exceeded the maximum when either the month, date,
1872 1885 * time, or era changes in response to setting the year. We don't
1873 1886 * check for month, date, and time here because the year and era are
1874 1887 * sufficient to detect an invalid year setting. NOTE: If code is
1875 1888 * added to check the month and date in the future for some reason,
1876 1889 * Feb 29 must be allowed to shift to Mar 1 when setting the year.
1877 1890 */
1878 1891 {
1879 1892 if (gc == this) {
1880 1893 gc = (GregorianCalendar) clone();
1881 1894 }
1882 1895
1883 1896 // Calculate the millisecond offset from the beginning
1884 1897 // of the year of this calendar and adjust the max
1885 1898 // year value if we are beyond the limit in the max
1886 1899 // year.
1887 1900 long current = gc.getYearOffsetInMillis();
1888 1901
1889 1902 if (gc.internalGetEra() == CE) {
1890 1903 gc.setTimeInMillis(Long.MAX_VALUE);
1891 1904 value = gc.get(YEAR);
1892 1905 long maxEnd = gc.getYearOffsetInMillis();
1893 1906 if (current > maxEnd) {
1894 1907 value--;
1895 1908 }
1896 1909 } else {
1897 1910 CalendarSystem mincal = gc.getTimeInMillis() >= gregorianCutover ?
1898 1911 gcal : getJulianCalendarSystem();
1899 1912 CalendarDate d = mincal.getCalendarDate(Long.MIN_VALUE, getZone());
1900 1913 long maxEnd = (cal.getDayOfYear(d) - 1) * 24 + d.getHours();
1901 1914 maxEnd *= 60;
1902 1915 maxEnd += d.getMinutes();
1903 1916 maxEnd *= 60;
1904 1917 maxEnd += d.getSeconds();
1905 1918 maxEnd *= 1000;
1906 1919 maxEnd += d.getMillis();
1907 1920 value = d.getYear();
1908 1921 if (value <= 0) {
1909 1922 assert mincal == gcal;
1910 1923 value = 1 - value;
1911 1924 }
1912 1925 if (current < maxEnd) {
1913 1926 value--;
1914 1927 }
1915 1928 }
1916 1929 }
1917 1930 break;
1918 1931
1919 1932 default:
1920 1933 throw new ArrayIndexOutOfBoundsException(field);
1921 1934 }
1922 1935 return value;
1923 1936 }
1924 1937
1925 1938 /**
1926 1939 * Returns the millisecond offset from the beginning of this
1927 1940 * year. This Calendar object must have been normalized.
1928 1941 */
1929 1942 private long getYearOffsetInMillis() {
1930 1943 long t = (internalGet(DAY_OF_YEAR) - 1) * 24;
1931 1944 t += internalGet(HOUR_OF_DAY);
1932 1945 t *= 60;
1933 1946 t += internalGet(MINUTE);
1934 1947 t *= 60;
1935 1948 t += internalGet(SECOND);
1936 1949 t *= 1000;
1937 1950 return t + internalGet(MILLISECOND) -
1938 1951 (internalGet(ZONE_OFFSET) + internalGet(DST_OFFSET));
1939 1952 }
1940 1953
1941 1954 public Object clone()
1942 1955 {
1943 1956 GregorianCalendar other = (GregorianCalendar) super.clone();
1944 1957
1945 1958 other.gdate = (BaseCalendar.Date) gdate.clone();
1946 1959 if (cdate != null) {
1947 1960 if (cdate != gdate) {
1948 1961 other.cdate = (BaseCalendar.Date) cdate.clone();
1949 1962 } else {
1950 1963 other.cdate = other.gdate;
1951 1964 }
1952 1965 }
1953 1966 other.originalFields = null;
1954 1967 other.zoneOffsets = null;
1955 1968 return other;
1956 1969 }
1957 1970
1958 1971 public TimeZone getTimeZone() {
1959 1972 TimeZone zone = super.getTimeZone();
1960 1973 // To share the zone by CalendarDates
1961 1974 gdate.setZone(zone);
1962 1975 if (cdate != null && cdate != gdate) {
1963 1976 cdate.setZone(zone);
1964 1977 }
1965 1978 return zone;
1966 1979 }
1967 1980
1968 1981 public void setTimeZone(TimeZone zone) {
1969 1982 super.setTimeZone(zone);
1970 1983 // To share the zone by CalendarDates
1971 1984 gdate.setZone(zone);
1972 1985 if (cdate != null && cdate != gdate) {
1973 1986 cdate.setZone(zone);
1974 1987 }
1975 1988 }
1976 1989
1977 1990 /**
1978 1991 * Returns {@code true} indicating this {@code GregorianCalendar}
1979 1992 * supports week dates.
1980 1993 *
1981 1994 * @return {@code true} (always)
1982 1995 * @see #getWeekYear()
1983 1996 * @see #setWeekDate(int,int,int)
1984 1997 * @see #getWeeksInWeekYear()
1985 1998 * @since 1.7
1986 1999 */
1987 2000 @Override
1988 2001 public final boolean isWeekDateSupported() {
1989 2002 return true;
1990 2003 }
1991 2004
1992 2005 /**
1993 2006 * Returns the <a href="#week_year">week year</a> represented by this
1994 2007 * {@code GregorianCalendar}. The dates in the weeks between 1 and the
1995 2008 * maximum week number of the week year have the same week year value
1996 2009 * that may be one year before or after the {@link Calendar#YEAR YEAR}
1997 2010 * (calendar year) value.
1998 2011 *
1999 2012 * <p>This method calls {@link Calendar#complete()} before
2000 2013 * calculating the week year.
2001 2014 *
2002 2015 * @return the week year represented by this {@code GregorianCalendar}.
2003 2016 * If the {@link Calendar#ERA ERA} value is {@link #BC}, the year is
2004 2017 * represented by 0 or a negative number: BC 1 is 0, BC 2
2005 2018 * is -1, BC 3 is -2, and so on.
2006 2019 * @throws IllegalArgumentException
2007 2020 * if any of the calendar fields is invalid in non-lenient mode.
2008 2021 * @see #isWeekDateSupported()
2009 2022 * @see #getWeeksInWeekYear()
2010 2023 * @see Calendar#getFirstDayOfWeek()
2011 2024 * @see Calendar#getMinimalDaysInFirstWeek()
2012 2025 * @since 1.7
2013 2026 */
2014 2027 @Override
2015 2028 public int getWeekYear() {
2016 2029 int year = get(YEAR); // implicitly calls complete()
2017 2030 if (internalGetEra() == BCE) {
2018 2031 year = 1 - year;
2019 2032 }
2020 2033
2021 2034 // Fast path for the Gregorian calendar years that are never
2022 2035 // affected by the Julian-Gregorian transition
2023 2036 if (year > gregorianCutoverYear + 1) {
2024 2037 int weekOfYear = internalGet(WEEK_OF_YEAR);
2025 2038 if (internalGet(MONTH) == JANUARY) {
2026 2039 if (weekOfYear >= 52) {
2027 2040 --year;
2028 2041 }
2029 2042 } else {
2030 2043 if (weekOfYear == 1) {
2031 2044 ++year;
2032 2045 }
2033 2046 }
2034 2047 return year;
2035 2048 }
2036 2049
2037 2050 // General (slow) path
2038 2051 int dayOfYear = internalGet(DAY_OF_YEAR);
2039 2052 int maxDayOfYear = getActualMaximum(DAY_OF_YEAR);
2040 2053 int minimalDays = getMinimalDaysInFirstWeek();
2041 2054
2042 2055 // Quickly check the possibility of year adjustments before
2043 2056 // cloning this GregorianCalendar.
2044 2057 if (dayOfYear > minimalDays && dayOfYear < (maxDayOfYear - 6)) {
2045 2058 return year;
2046 2059 }
2047 2060
2048 2061 // Create a clone to work on the calculation
2049 2062 GregorianCalendar cal = (GregorianCalendar) clone();
2050 2063 cal.setLenient(true);
2051 2064 // Use GMT so that intermediate date calculations won't
2052 2065 // affect the time of day fields.
2053 2066 cal.setTimeZone(TimeZone.getTimeZone("GMT"));
2054 2067 // Go to the first day of the year, which is usually January 1.
2055 2068 cal.set(DAY_OF_YEAR, 1);
2056 2069 cal.complete();
2057 2070
2058 2071 // Get the first day of the first day-of-week in the year.
2059 2072 int delta = getFirstDayOfWeek() - cal.get(DAY_OF_WEEK);
2060 2073 if (delta != 0) {
2061 2074 if (delta < 0) {
2062 2075 delta += 7;
2063 2076 }
2064 2077 cal.add(DAY_OF_YEAR, delta);
2065 2078 }
2066 2079 int minDayOfYear = cal.get(DAY_OF_YEAR);
2067 2080 if (dayOfYear < minDayOfYear) {
2068 2081 if (minDayOfYear <= minimalDays) {
2069 2082 --year;
2070 2083 }
2071 2084 } else {
2072 2085 cal.set(YEAR, year + 1);
2073 2086 cal.set(DAY_OF_YEAR, 1);
2074 2087 cal.complete();
2075 2088 int del = getFirstDayOfWeek() - cal.get(DAY_OF_WEEK);
2076 2089 if (del != 0) {
2077 2090 if (del < 0) {
2078 2091 del += 7;
2079 2092 }
2080 2093 cal.add(DAY_OF_YEAR, del);
2081 2094 }
2082 2095 minDayOfYear = cal.get(DAY_OF_YEAR) - 1;
2083 2096 if (minDayOfYear == 0) {
2084 2097 minDayOfYear = 7;
2085 2098 }
2086 2099 if (minDayOfYear >= minimalDays) {
2087 2100 int days = maxDayOfYear - dayOfYear + 1;
2088 2101 if (days <= (7 - minDayOfYear)) {
2089 2102 ++year;
2090 2103 }
2091 2104 }
2092 2105 }
2093 2106 return year;
2094 2107 }
2095 2108
2096 2109 /**
2097 2110 * Sets this {@code GregorianCalendar} to the date given by the
2098 2111 * date specifiers - <a href="#week_year">{@code weekYear}</a>,
2099 2112 * {@code weekOfYear}, and {@code dayOfWeek}. {@code weekOfYear}
2100 2113 * follows the <a href="#week_and_year">{@code WEEK_OF_YEAR}
2101 2114 * numbering</a>. The {@code dayOfWeek} value must be one of the
2102 2115 * {@link Calendar#DAY_OF_WEEK DAY_OF_WEEK} values: {@link
2103 2116 * Calendar#SUNDAY SUNDAY} to {@link Calendar#SATURDAY SATURDAY}.
2104 2117 *
2105 2118 * <p>Note that the numeric day-of-week representation differs from
2106 2119 * the ISO 8601 standard, and that the {@code weekOfYear}
2107 2120 * numbering is compatible with the standard when {@code
2108 2121 * getFirstDayOfWeek()} is {@code MONDAY} and {@code
2109 2122 * getMinimalDaysInFirstWeek()} is 4.
2110 2123 *
2111 2124 * <p>Unlike the {@code set} method, all of the calendar fields
2112 2125 * and the instant of time value are calculated upon return.
2113 2126 *
2114 2127 * <p>If {@code weekOfYear} is out of the valid week-of-year
2115 2128 * range in {@code weekYear}, the {@code weekYear}
2116 2129 * and {@code weekOfYear} values are adjusted in lenient
2117 2130 * mode, or an {@code IllegalArgumentException} is thrown in
2118 2131 * non-lenient mode.
2119 2132 *
2120 2133 * @param weekYear the week year
2121 2134 * @param weekOfYear the week number based on {@code weekYear}
2122 2135 * @param dayOfWeek the day of week value: one of the constants
2123 2136 * for the {@link #DAY_OF_WEEK DAY_OF_WEEK} field:
2124 2137 * {@link Calendar#SUNDAY SUNDAY}, ...,
2125 2138 * {@link Calendar#SATURDAY SATURDAY}.
2126 2139 * @exception IllegalArgumentException
2127 2140 * if any of the given date specifiers is invalid,
2128 2141 * or if any of the calendar fields are inconsistent
2129 2142 * with the given date specifiers in non-lenient mode
2130 2143 * @see GregorianCalendar#isWeekDateSupported()
2131 2144 * @see Calendar#getFirstDayOfWeek()
2132 2145 * @see Calendar#getMinimalDaysInFirstWeek()
2133 2146 * @since 1.7
2134 2147 */
2135 2148 @Override
2136 2149 public void setWeekDate(int weekYear, int weekOfYear, int dayOfWeek) {
2137 2150 if (dayOfWeek < SUNDAY || dayOfWeek > SATURDAY) {
2138 2151 throw new IllegalArgumentException("invalid dayOfWeek: " + dayOfWeek);
2139 2152 }
2140 2153
2141 2154 // To avoid changing the time of day fields by date
2142 2155 // calculations, use a clone with the GMT time zone.
2143 2156 GregorianCalendar gc = (GregorianCalendar) clone();
2144 2157 gc.setLenient(true);
2145 2158 int era = gc.get(ERA);
2146 2159 gc.clear();
2147 2160 gc.setTimeZone(TimeZone.getTimeZone("GMT"));
2148 2161 gc.set(ERA, era);
2149 2162 gc.set(YEAR, weekYear);
2150 2163 gc.set(WEEK_OF_YEAR, 1);
2151 2164 gc.set(DAY_OF_WEEK, getFirstDayOfWeek());
2152 2165 int days = dayOfWeek - getFirstDayOfWeek();
2153 2166 if (days < 0) {
2154 2167 days += 7;
2155 2168 }
2156 2169 days += 7 * (weekOfYear - 1);
2157 2170 if (days != 0) {
2158 2171 gc.add(DAY_OF_YEAR, days);
2159 2172 } else {
2160 2173 gc.complete();
2161 2174 }
2162 2175
2163 2176 if (!isLenient() &&
2164 2177 (gc.getWeekYear() != weekYear
2165 2178 || gc.internalGet(WEEK_OF_YEAR) != weekOfYear
2166 2179 || gc.internalGet(DAY_OF_WEEK) != dayOfWeek)) {
2167 2180 throw new IllegalArgumentException();
2168 2181 }
2169 2182
2170 2183 set(ERA, gc.internalGet(ERA));
2171 2184 set(YEAR, gc.internalGet(YEAR));
2172 2185 set(MONTH, gc.internalGet(MONTH));
2173 2186 set(DAY_OF_MONTH, gc.internalGet(DAY_OF_MONTH));
2174 2187
2175 2188 // to avoid throwing an IllegalArgumentException in
2176 2189 // non-lenient, set WEEK_OF_YEAR internally
2177 2190 internalSet(WEEK_OF_YEAR, weekOfYear);
2178 2191 complete();
2179 2192 }
2180 2193
2181 2194 /**
2182 2195 * Returns the number of weeks in the <a href="#week_year">week year</a>
2183 2196 * represented by this {@code GregorianCalendar}.
2184 2197 *
2185 2198 * <p>For example, if this {@code GregorianCalendar}'s date is
2186 2199 * December 31, 2008 with <a href="#iso8601_compatible_setting">the ISO
2187 2200 * 8601 compatible setting</a>, this method will return 53 for the
2188 2201 * period: December 29, 2008 to January 3, 2010 while {@link
2189 2202 * #getActualMaximum(int) getActualMaximum(WEEK_OF_YEAR)} will return
2190 2203 * 52 for the period: December 31, 2007 to December 28, 2008.
2191 2204 *
2192 2205 * @return the number of weeks in the week year.
2193 2206 * @see Calendar#WEEK_OF_YEAR
2194 2207 * @see #getWeekYear()
2195 2208 * @see #getActualMaximum(int)
2196 2209 * @since 1.7
2197 2210 */
2198 2211 public int getWeeksInWeekYear() {
2199 2212 GregorianCalendar gc = getNormalizedCalendar();
2200 2213 int weekYear = gc.getWeekYear();
2201 2214 if (weekYear == gc.internalGet(YEAR)) {
2202 2215 return gc.getActualMaximum(WEEK_OF_YEAR);
2203 2216 }
2204 2217
2205 2218 // Use the 2nd week for calculating the max of WEEK_OF_YEAR
2206 2219 if (gc == this) {
2207 2220 gc = (GregorianCalendar) gc.clone();
2208 2221 }
2209 2222 gc.setWeekDate(weekYear, 2, internalGet(DAY_OF_WEEK));
2210 2223 return gc.getActualMaximum(WEEK_OF_YEAR);
2211 2224 }
2212 2225
2213 2226 /////////////////////////////
2214 2227 // Time => Fields computation
2215 2228 /////////////////////////////
2216 2229
2217 2230 /**
2218 2231 * The fixed date corresponding to gdate. If the value is
2219 2232 * Long.MIN_VALUE, the fixed date value is unknown. Currently,
2220 2233 * Julian calendar dates are not cached.
2221 2234 */
2222 2235 transient private long cachedFixedDate = Long.MIN_VALUE;
2223 2236
2224 2237 /**
2225 2238 * Converts the time value (millisecond offset from the <a
2226 2239 * href="Calendar.html#Epoch">Epoch</a>) to calendar field values.
2227 2240 * The time is <em>not</em>
2228 2241 * recomputed first; to recompute the time, then the fields, call the
2229 2242 * <code>complete</code> method.
2230 2243 *
2231 2244 * @see Calendar#complete
2232 2245 */
2233 2246 protected void computeFields() {
2234 2247 int mask = 0;
2235 2248 if (isPartiallyNormalized()) {
2236 2249 // Determine which calendar fields need to be computed.
2237 2250 mask = getSetStateFields();
2238 2251 int fieldMask = ~mask & ALL_FIELDS;
2239 2252 // We have to call computTime in case calsys == null in
2240 2253 // order to set calsys and cdate. (6263644)
2241 2254 if (fieldMask != 0 || calsys == null) {
2242 2255 mask |= computeFields(fieldMask,
2243 2256 mask & (ZONE_OFFSET_MASK|DST_OFFSET_MASK));
2244 2257 assert mask == ALL_FIELDS;
2245 2258 }
2246 2259 } else {
2247 2260 mask = ALL_FIELDS;
2248 2261 computeFields(mask, 0);
2249 2262 }
2250 2263 // After computing all the fields, set the field state to `COMPUTED'.
2251 2264 setFieldsComputed(mask);
2252 2265 }
2253 2266
2254 2267 /**
2255 2268 * This computeFields implements the conversion from UTC
2256 2269 * (millisecond offset from the Epoch) to calendar
2257 2270 * field values. fieldMask specifies which fields to change the
2258 2271 * setting state to COMPUTED, although all fields are set to
2259 2272 * the correct values. This is required to fix 4685354.
2260 2273 *
2261 2274 * @param fieldMask a bit mask to specify which fields to change
2262 2275 * the setting state.
2263 2276 * @param tzMask a bit mask to specify which time zone offset
2264 2277 * fields to be used for time calculations
2265 2278 * @return a new field mask that indicates what field values have
2266 2279 * actually been set.
2267 2280 */
2268 2281 private int computeFields(int fieldMask, int tzMask) {
2269 2282 int zoneOffset = 0;
2270 2283 TimeZone tz = getZone();
2271 2284 if (zoneOffsets == null) {
2272 2285 zoneOffsets = new int[2];
2273 2286 }
2274 2287 if (tzMask != (ZONE_OFFSET_MASK|DST_OFFSET_MASK)) {
2275 2288 if (tz instanceof ZoneInfo) {
2276 2289 zoneOffset = ((ZoneInfo)tz).getOffsets(time, zoneOffsets);
2277 2290 } else {
2278 2291 zoneOffset = tz.getOffset(time);
2279 2292 zoneOffsets[0] = tz.getRawOffset();
2280 2293 zoneOffsets[1] = zoneOffset - zoneOffsets[0];
2281 2294 }
2282 2295 }
2283 2296 if (tzMask != 0) {
2284 2297 if (isFieldSet(tzMask, ZONE_OFFSET)) {
2285 2298 zoneOffsets[0] = internalGet(ZONE_OFFSET);
2286 2299 }
2287 2300 if (isFieldSet(tzMask, DST_OFFSET)) {
2288 2301 zoneOffsets[1] = internalGet(DST_OFFSET);
2289 2302 }
2290 2303 zoneOffset = zoneOffsets[0] + zoneOffsets[1];
2291 2304 }
2292 2305
2293 2306 // By computing time and zoneOffset separately, we can take
2294 2307 // the wider range of time+zoneOffset than the previous
2295 2308 // implementation.
2296 2309 long fixedDate = zoneOffset / ONE_DAY;
2297 2310 int timeOfDay = zoneOffset % (int)ONE_DAY;
2298 2311 fixedDate += time / ONE_DAY;
2299 2312 timeOfDay += (int) (time % ONE_DAY);
2300 2313 if (timeOfDay >= ONE_DAY) {
2301 2314 timeOfDay -= ONE_DAY;
2302 2315 ++fixedDate;
2303 2316 } else {
2304 2317 while (timeOfDay < 0) {
2305 2318 timeOfDay += ONE_DAY;
2306 2319 --fixedDate;
2307 2320 }
2308 2321 }
2309 2322 fixedDate += EPOCH_OFFSET;
2310 2323
2311 2324 int era = CE;
2312 2325 int year;
2313 2326 if (fixedDate >= gregorianCutoverDate) {
2314 2327 // Handle Gregorian dates.
2315 2328 assert cachedFixedDate == Long.MIN_VALUE || gdate.isNormalized()
2316 2329 : "cache control: not normalized";
2317 2330 assert cachedFixedDate == Long.MIN_VALUE ||
2318 2331 gcal.getFixedDate(gdate.getNormalizedYear(),
2319 2332 gdate.getMonth(),
2320 2333 gdate.getDayOfMonth(), gdate)
2321 2334 == cachedFixedDate
2322 2335 : "cache control: inconsictency" +
2323 2336 ", cachedFixedDate=" + cachedFixedDate +
2324 2337 ", computed=" +
2325 2338 gcal.getFixedDate(gdate.getNormalizedYear(),
2326 2339 gdate.getMonth(),
2327 2340 gdate.getDayOfMonth(),
2328 2341 gdate) +
2329 2342 ", date=" + gdate;
2330 2343
2331 2344 // See if we can use gdate to avoid date calculation.
2332 2345 if (fixedDate != cachedFixedDate) {
2333 2346 gcal.getCalendarDateFromFixedDate(gdate, fixedDate);
2334 2347 cachedFixedDate = fixedDate;
2335 2348 }
2336 2349
2337 2350 year = gdate.getYear();
2338 2351 if (year <= 0) {
2339 2352 year = 1 - year;
2340 2353 era = BCE;
2341 2354 }
2342 2355 calsys = gcal;
2343 2356 cdate = gdate;
2344 2357 assert cdate.getDayOfWeek() > 0 : "dow="+cdate.getDayOfWeek()+", date="+cdate;
2345 2358 } else {
2346 2359 // Handle Julian calendar dates.
2347 2360 calsys = getJulianCalendarSystem();
2348 2361 cdate = (BaseCalendar.Date) jcal.newCalendarDate(getZone());
2349 2362 jcal.getCalendarDateFromFixedDate(cdate, fixedDate);
2350 2363 Era e = cdate.getEra();
2351 2364 if (e == jeras[0]) {
2352 2365 era = BCE;
2353 2366 }
2354 2367 year = cdate.getYear();
2355 2368 }
2356 2369
2357 2370 // Always set the ERA and YEAR values.
2358 2371 internalSet(ERA, era);
2359 2372 internalSet(YEAR, year);
2360 2373 int mask = fieldMask | (ERA_MASK|YEAR_MASK);
2361 2374
2362 2375 int month = cdate.getMonth() - 1; // 0-based
2363 2376 int dayOfMonth = cdate.getDayOfMonth();
2364 2377
2365 2378 // Set the basic date fields.
2366 2379 if ((fieldMask & (MONTH_MASK|DAY_OF_MONTH_MASK|DAY_OF_WEEK_MASK))
2367 2380 != 0) {
2368 2381 internalSet(MONTH, month);
2369 2382 internalSet(DAY_OF_MONTH, dayOfMonth);
2370 2383 internalSet(DAY_OF_WEEK, cdate.getDayOfWeek());
2371 2384 mask |= MONTH_MASK|DAY_OF_MONTH_MASK|DAY_OF_WEEK_MASK;
2372 2385 }
2373 2386
2374 2387 if ((fieldMask & (HOUR_OF_DAY_MASK|AM_PM_MASK|HOUR_MASK
2375 2388 |MINUTE_MASK|SECOND_MASK|MILLISECOND_MASK)) != 0) {
2376 2389 if (timeOfDay != 0) {
2377 2390 int hours = timeOfDay / ONE_HOUR;
2378 2391 internalSet(HOUR_OF_DAY, hours);
2379 2392 internalSet(AM_PM, hours / 12); // Assume AM == 0
2380 2393 internalSet(HOUR, hours % 12);
2381 2394 int r = timeOfDay % ONE_HOUR;
2382 2395 internalSet(MINUTE, r / ONE_MINUTE);
2383 2396 r %= ONE_MINUTE;
2384 2397 internalSet(SECOND, r / ONE_SECOND);
2385 2398 internalSet(MILLISECOND, r % ONE_SECOND);
2386 2399 } else {
2387 2400 internalSet(HOUR_OF_DAY, 0);
2388 2401 internalSet(AM_PM, AM);
2389 2402 internalSet(HOUR, 0);
2390 2403 internalSet(MINUTE, 0);
2391 2404 internalSet(SECOND, 0);
2392 2405 internalSet(MILLISECOND, 0);
2393 2406 }
2394 2407 mask |= (HOUR_OF_DAY_MASK|AM_PM_MASK|HOUR_MASK
2395 2408 |MINUTE_MASK|SECOND_MASK|MILLISECOND_MASK);
2396 2409 }
2397 2410
2398 2411 if ((fieldMask & (ZONE_OFFSET_MASK|DST_OFFSET_MASK)) != 0) {
2399 2412 internalSet(ZONE_OFFSET, zoneOffsets[0]);
2400 2413 internalSet(DST_OFFSET, zoneOffsets[1]);
2401 2414 mask |= (ZONE_OFFSET_MASK|DST_OFFSET_MASK);
2402 2415 }
2403 2416
2404 2417 if ((fieldMask & (DAY_OF_YEAR_MASK|WEEK_OF_YEAR_MASK|WEEK_OF_MONTH_MASK|DAY_OF_WEEK_IN_MONTH_MASK)) != 0) {
2405 2418 int normalizedYear = cdate.getNormalizedYear();
2406 2419 long fixedDateJan1 = calsys.getFixedDate(normalizedYear, 1, 1, cdate);
2407 2420 int dayOfYear = (int)(fixedDate - fixedDateJan1) + 1;
2408 2421 long fixedDateMonth1 = fixedDate - dayOfMonth + 1;
2409 2422 int cutoverGap = 0;
2410 2423 int cutoverYear = (calsys == gcal) ? gregorianCutoverYear : gregorianCutoverYearJulian;
2411 2424 int relativeDayOfMonth = dayOfMonth - 1;
2412 2425
2413 2426 // If we are in the cutover year, we need some special handling.
2414 2427 if (normalizedYear == cutoverYear) {
2415 2428 // Need to take care of the "missing" days.
2416 2429 if (gregorianCutoverYearJulian <= gregorianCutoverYear) {
2417 2430 // We need to find out where we are. The cutover
2418 2431 // gap could even be more than one year. (One
2419 2432 // year difference in ~48667 years.)
2420 2433 fixedDateJan1 = getFixedDateJan1(cdate, fixedDate);
2421 2434 if (fixedDate >= gregorianCutoverDate) {
2422 2435 fixedDateMonth1 = getFixedDateMonth1(cdate, fixedDate);
2423 2436 }
2424 2437 }
2425 2438 int realDayOfYear = (int)(fixedDate - fixedDateJan1) + 1;
2426 2439 cutoverGap = dayOfYear - realDayOfYear;
2427 2440 dayOfYear = realDayOfYear;
2428 2441 relativeDayOfMonth = (int)(fixedDate - fixedDateMonth1);
2429 2442 }
2430 2443 internalSet(DAY_OF_YEAR, dayOfYear);
2431 2444 internalSet(DAY_OF_WEEK_IN_MONTH, relativeDayOfMonth / 7 + 1);
2432 2445
2433 2446 int weekOfYear = getWeekNumber(fixedDateJan1, fixedDate);
2434 2447
2435 2448 // The spec is to calculate WEEK_OF_YEAR in the
2436 2449 // ISO8601-style. This creates problems, though.
2437 2450 if (weekOfYear == 0) {
2438 2451 // If the date belongs to the last week of the
2439 2452 // previous year, use the week number of "12/31" of
2440 2453 // the "previous" year. Again, if the previous year is
2441 2454 // the Gregorian cutover year, we need to take care of
2442 2455 // it. Usually the previous day of January 1 is
2443 2456 // December 31, which is not always true in
2444 2457 // GregorianCalendar.
2445 2458 long fixedDec31 = fixedDateJan1 - 1;
2446 2459 long prevJan1 = fixedDateJan1 - 365;
2447 2460 if (normalizedYear > (cutoverYear + 1)) {
2448 2461 if (CalendarUtils.isGregorianLeapYear(normalizedYear - 1)) {
2449 2462 --prevJan1;
2450 2463 }
2451 2464 } else if (normalizedYear <= gregorianCutoverYearJulian) {
2452 2465 if (CalendarUtils.isJulianLeapYear(normalizedYear - 1)) {
2453 2466 --prevJan1;
2454 2467 }
2455 2468 } else {
2456 2469 BaseCalendar calForJan1 = calsys;
2457 2470 //int prevYear = normalizedYear - 1;
2458 2471 int prevYear = getCalendarDate(fixedDec31).getNormalizedYear();
2459 2472 if (prevYear == gregorianCutoverYear) {
2460 2473 calForJan1 = getCutoverCalendarSystem();
2461 2474 if (calForJan1 == jcal) {
2462 2475 prevJan1 = calForJan1.getFixedDate(prevYear,
2463 2476 BaseCalendar.JANUARY,
2464 2477 1,
2465 2478 null);
2466 2479 } else {
2467 2480 prevJan1 = gregorianCutoverDate;
2468 2481 calForJan1 = gcal;
2469 2482 }
2470 2483 } else if (prevYear <= gregorianCutoverYearJulian) {
2471 2484 calForJan1 = getJulianCalendarSystem();
2472 2485 prevJan1 = calForJan1.getFixedDate(prevYear,
2473 2486 BaseCalendar.JANUARY,
2474 2487 1,
2475 2488 null);
2476 2489 }
2477 2490 }
2478 2491 weekOfYear = getWeekNumber(prevJan1, fixedDec31);
2479 2492 } else {
2480 2493 if (normalizedYear > gregorianCutoverYear ||
2481 2494 normalizedYear < (gregorianCutoverYearJulian - 1)) {
2482 2495 // Regular years
2483 2496 if (weekOfYear >= 52) {
2484 2497 long nextJan1 = fixedDateJan1 + 365;
2485 2498 if (cdate.isLeapYear()) {
2486 2499 nextJan1++;
2487 2500 }
2488 2501 long nextJan1st = BaseCalendar.getDayOfWeekDateOnOrBefore(nextJan1 + 6,
2489 2502 getFirstDayOfWeek());
2490 2503 int ndays = (int)(nextJan1st - nextJan1);
2491 2504 if (ndays >= getMinimalDaysInFirstWeek() && fixedDate >= (nextJan1st - 7)) {
2492 2505 // The first days forms a week in which the date is included.
2493 2506 weekOfYear = 1;
2494 2507 }
2495 2508 }
2496 2509 } else {
2497 2510 BaseCalendar calForJan1 = calsys;
2498 2511 int nextYear = normalizedYear + 1;
2499 2512 if (nextYear == (gregorianCutoverYearJulian + 1) &&
2500 2513 nextYear < gregorianCutoverYear) {
2501 2514 // In case the gap is more than one year.
2502 2515 nextYear = gregorianCutoverYear;
2503 2516 }
2504 2517 if (nextYear == gregorianCutoverYear) {
2505 2518 calForJan1 = getCutoverCalendarSystem();
2506 2519 }
2507 2520
2508 2521 long nextJan1;
2509 2522 if (nextYear > gregorianCutoverYear
2510 2523 || gregorianCutoverYearJulian == gregorianCutoverYear
2511 2524 || nextYear == gregorianCutoverYearJulian) {
2512 2525 nextJan1 = calForJan1.getFixedDate(nextYear,
2513 2526 BaseCalendar.JANUARY,
2514 2527 1,
2515 2528 null);
2516 2529 } else {
2517 2530 nextJan1 = gregorianCutoverDate;
2518 2531 calForJan1 = gcal;
2519 2532 }
2520 2533
2521 2534 long nextJan1st = BaseCalendar.getDayOfWeekDateOnOrBefore(nextJan1 + 6,
2522 2535 getFirstDayOfWeek());
2523 2536 int ndays = (int)(nextJan1st - nextJan1);
2524 2537 if (ndays >= getMinimalDaysInFirstWeek() && fixedDate >= (nextJan1st - 7)) {
2525 2538 // The first days forms a week in which the date is included.
2526 2539 weekOfYear = 1;
2527 2540 }
2528 2541 }
2529 2542 }
2530 2543 internalSet(WEEK_OF_YEAR, weekOfYear);
2531 2544 internalSet(WEEK_OF_MONTH, getWeekNumber(fixedDateMonth1, fixedDate));
2532 2545 mask |= (DAY_OF_YEAR_MASK|WEEK_OF_YEAR_MASK|WEEK_OF_MONTH_MASK|DAY_OF_WEEK_IN_MONTH_MASK);
2533 2546 }
2534 2547 return mask;
2535 2548 }
2536 2549
2537 2550 /**
2538 2551 * Returns the number of weeks in a period between fixedDay1 and
2539 2552 * fixedDate. The getFirstDayOfWeek-getMinimalDaysInFirstWeek rule
2540 2553 * is applied to calculate the number of weeks.
2541 2554 *
2542 2555 * @param fixedDay1 the fixed date of the first day of the period
2543 2556 * @param fixedDate the fixed date of the last day of the period
2544 2557 * @return the number of weeks of the given period
2545 2558 */
2546 2559 private int getWeekNumber(long fixedDay1, long fixedDate) {
2547 2560 // We can always use `gcal' since Julian and Gregorian are the
2548 2561 // same thing for this calculation.
2549 2562 long fixedDay1st = Gregorian.getDayOfWeekDateOnOrBefore(fixedDay1 + 6,
2550 2563 getFirstDayOfWeek());
2551 2564 int ndays = (int)(fixedDay1st - fixedDay1);
2552 2565 assert ndays <= 7;
2553 2566 if (ndays >= getMinimalDaysInFirstWeek()) {
2554 2567 fixedDay1st -= 7;
2555 2568 }
2556 2569 int normalizedDayOfPeriod = (int)(fixedDate - fixedDay1st);
2557 2570 if (normalizedDayOfPeriod >= 0) {
2558 2571 return normalizedDayOfPeriod / 7 + 1;
2559 2572 }
2560 2573 return CalendarUtils.floorDivide(normalizedDayOfPeriod, 7) + 1;
2561 2574 }
2562 2575
2563 2576 /**
2564 2577 * Converts calendar field values to the time value (millisecond
2565 2578 * offset from the <a href="Calendar.html#Epoch">Epoch</a>).
2566 2579 *
2567 2580 * @exception IllegalArgumentException if any calendar fields are invalid.
2568 2581 */
2569 2582 protected void computeTime() {
2570 2583 // In non-lenient mode, perform brief checking of calendar
2571 2584 // fields which have been set externally. Through this
2572 2585 // checking, the field values are stored in originalFields[]
2573 2586 // to see if any of them are normalized later.
2574 2587 if (!isLenient()) {
2575 2588 if (originalFields == null) {
2576 2589 originalFields = new int[FIELD_COUNT];
2577 2590 }
2578 2591 for (int field = 0; field < FIELD_COUNT; field++) {
2579 2592 int value = internalGet(field);
2580 2593 if (isExternallySet(field)) {
2581 2594 // Quick validation for any out of range values
2582 2595 if (value < getMinimum(field) || value > getMaximum(field)) {
2583 2596 throw new IllegalArgumentException(getFieldName(field));
2584 2597 }
2585 2598 }
2586 2599 originalFields[field] = value;
2587 2600 }
2588 2601 }
2589 2602
2590 2603 // Let the super class determine which calendar fields to be
2591 2604 // used to calculate the time.
2592 2605 int fieldMask = selectFields();
2593 2606
2594 2607 // The year defaults to the epoch start. We don't check
2595 2608 // fieldMask for YEAR because YEAR is a mandatory field to
2596 2609 // determine the date.
2597 2610 int year = isSet(YEAR) ? internalGet(YEAR) : EPOCH_YEAR;
2598 2611
2599 2612 int era = internalGetEra();
2600 2613 if (era == BCE) {
2601 2614 year = 1 - year;
2602 2615 } else if (era != CE) {
2603 2616 // Even in lenient mode we disallow ERA values other than CE & BCE.
2604 2617 // (The same normalization rule as add()/roll() could be
2605 2618 // applied here in lenient mode. But this checking is kept
2606 2619 // unchanged for compatibility as of 1.5.)
2607 2620 throw new IllegalArgumentException("Invalid era");
2608 2621 }
2609 2622
2610 2623 // If year is 0 or negative, we need to set the ERA value later.
2611 2624 if (year <= 0 && !isSet(ERA)) {
2612 2625 fieldMask |= ERA_MASK;
2613 2626 setFieldsComputed(ERA_MASK);
2614 2627 }
2615 2628
2616 2629 // Calculate the time of day. We rely on the convention that
2617 2630 // an UNSET field has 0.
2618 2631 long timeOfDay = 0;
2619 2632 if (isFieldSet(fieldMask, HOUR_OF_DAY)) {
2620 2633 timeOfDay += (long) internalGet(HOUR_OF_DAY);
2621 2634 } else {
2622 2635 timeOfDay += internalGet(HOUR);
2623 2636 // The default value of AM_PM is 0 which designates AM.
2624 2637 if (isFieldSet(fieldMask, AM_PM)) {
2625 2638 timeOfDay += 12 * internalGet(AM_PM);
2626 2639 }
2627 2640 }
2628 2641 timeOfDay *= 60;
2629 2642 timeOfDay += internalGet(MINUTE);
2630 2643 timeOfDay *= 60;
2631 2644 timeOfDay += internalGet(SECOND);
2632 2645 timeOfDay *= 1000;
2633 2646 timeOfDay += internalGet(MILLISECOND);
2634 2647
2635 2648 // Convert the time of day to the number of days and the
2636 2649 // millisecond offset from midnight.
2637 2650 long fixedDate = timeOfDay / ONE_DAY;
2638 2651 timeOfDay %= ONE_DAY;
2639 2652 while (timeOfDay < 0) {
2640 2653 timeOfDay += ONE_DAY;
2641 2654 --fixedDate;
2642 2655 }
2643 2656
2644 2657 // Calculate the fixed date since January 1, 1 (Gregorian).
2645 2658 calculateFixedDate: {
2646 2659 long gfd, jfd;
2647 2660 if (year > gregorianCutoverYear && year > gregorianCutoverYearJulian) {
2648 2661 gfd = fixedDate + getFixedDate(gcal, year, fieldMask);
2649 2662 if (gfd >= gregorianCutoverDate) {
2650 2663 fixedDate = gfd;
2651 2664 break calculateFixedDate;
2652 2665 }
2653 2666 jfd = fixedDate + getFixedDate(getJulianCalendarSystem(), year, fieldMask);
2654 2667 } else if (year < gregorianCutoverYear && year < gregorianCutoverYearJulian) {
2655 2668 jfd = fixedDate + getFixedDate(getJulianCalendarSystem(), year, fieldMask);
2656 2669 if (jfd < gregorianCutoverDate) {
2657 2670 fixedDate = jfd;
2658 2671 break calculateFixedDate;
2659 2672 }
2660 2673 gfd = jfd;
2661 2674 } else {
2662 2675 jfd = fixedDate + getFixedDate(getJulianCalendarSystem(), year, fieldMask);
2663 2676 gfd = fixedDate + getFixedDate(gcal, year, fieldMask);
2664 2677 }
2665 2678
2666 2679 // Now we have to determine which calendar date it is.
2667 2680
2668 2681 // If the date is relative from the beginning of the year
2669 2682 // in the Julian calendar, then use jfd;
2670 2683 if (isFieldSet(fieldMask, DAY_OF_YEAR) || isFieldSet(fieldMask, WEEK_OF_YEAR)) {
2671 2684 if (gregorianCutoverYear == gregorianCutoverYearJulian) {
2672 2685 fixedDate = jfd;
2673 2686 break calculateFixedDate;
2674 2687 } else if (year == gregorianCutoverYear) {
2675 2688 fixedDate = gfd;
2676 2689 break calculateFixedDate;
2677 2690 }
2678 2691 }
2679 2692
2680 2693 if (gfd >= gregorianCutoverDate) {
2681 2694 if (jfd >= gregorianCutoverDate) {
2682 2695 fixedDate = gfd;
2683 2696 } else {
2684 2697 // The date is in an "overlapping" period. No way
2685 2698 // to disambiguate it. Determine it using the
2686 2699 // previous date calculation.
2687 2700 if (calsys == gcal || calsys == null) {
2688 2701 fixedDate = gfd;
2689 2702 } else {
2690 2703 fixedDate = jfd;
2691 2704 }
2692 2705 }
2693 2706 } else {
2694 2707 if (jfd < gregorianCutoverDate) {
2695 2708 fixedDate = jfd;
2696 2709 } else {
2697 2710 // The date is in a "missing" period.
2698 2711 if (!isLenient()) {
2699 2712 throw new IllegalArgumentException("the specified date doesn't exist");
2700 2713 }
2701 2714 // Take the Julian date for compatibility, which
2702 2715 // will produce a Gregorian date.
2703 2716 fixedDate = jfd;
2704 2717 }
2705 2718 }
2706 2719 }
2707 2720
2708 2721 // millis represents local wall-clock time in milliseconds.
2709 2722 long millis = (fixedDate - EPOCH_OFFSET) * ONE_DAY + timeOfDay;
2710 2723
2711 2724 // Compute the time zone offset and DST offset. There are two potential
2712 2725 // ambiguities here. We'll assume a 2:00 am (wall time) switchover time
2713 2726 // for discussion purposes here.
2714 2727 // 1. The transition into DST. Here, a designated time of 2:00 am - 2:59 am
2715 2728 // can be in standard or in DST depending. However, 2:00 am is an invalid
2716 2729 // representation (the representation jumps from 1:59:59 am Std to 3:00:00 am DST).
2717 2730 // We assume standard time.
2718 2731 // 2. The transition out of DST. Here, a designated time of 1:00 am - 1:59 am
2719 2732 // can be in standard or DST. Both are valid representations (the rep
2720 2733 // jumps from 1:59:59 DST to 1:00:00 Std).
2721 2734 // Again, we assume standard time.
2722 2735 // We use the TimeZone object, unless the user has explicitly set the ZONE_OFFSET
2723 2736 // or DST_OFFSET fields; then we use those fields.
2724 2737 TimeZone zone = getZone();
2725 2738 if (zoneOffsets == null) {
2726 2739 zoneOffsets = new int[2];
2727 2740 }
2728 2741 int tzMask = fieldMask & (ZONE_OFFSET_MASK|DST_OFFSET_MASK);
2729 2742 if (tzMask != (ZONE_OFFSET_MASK|DST_OFFSET_MASK)) {
2730 2743 if (zone instanceof ZoneInfo) {
2731 2744 ((ZoneInfo)zone).getOffsetsByWall(millis, zoneOffsets);
2732 2745 } else {
2733 2746 int gmtOffset = isFieldSet(fieldMask, ZONE_OFFSET) ?
2734 2747 internalGet(ZONE_OFFSET) : zone.getRawOffset();
2735 2748 zone.getOffsets(millis - gmtOffset, zoneOffsets);
2736 2749 }
2737 2750 }
2738 2751 if (tzMask != 0) {
2739 2752 if (isFieldSet(tzMask, ZONE_OFFSET)) {
2740 2753 zoneOffsets[0] = internalGet(ZONE_OFFSET);
2741 2754 }
2742 2755 if (isFieldSet(tzMask, DST_OFFSET)) {
2743 2756 zoneOffsets[1] = internalGet(DST_OFFSET);
2744 2757 }
2745 2758 }
2746 2759
2747 2760 // Adjust the time zone offset values to get the UTC time.
2748 2761 millis -= zoneOffsets[0] + zoneOffsets[1];
2749 2762
2750 2763 // Set this calendar's time in milliseconds
2751 2764 time = millis;
2752 2765
2753 2766 int mask = computeFields(fieldMask | getSetStateFields(), tzMask);
2754 2767
2755 2768 if (!isLenient()) {
2756 2769 for (int field = 0; field < FIELD_COUNT; field++) {
2757 2770 if (!isExternallySet(field)) {
2758 2771 continue;
2759 2772 }
2760 2773 if (originalFields[field] != internalGet(field)) {
2761 2774 String s = originalFields[field] + " -> " + internalGet(field);
2762 2775 // Restore the original field values
2763 2776 System.arraycopy(originalFields, 0, fields, 0, fields.length);
2764 2777 throw new IllegalArgumentException(getFieldName(field) + ": " + s);
2765 2778 }
2766 2779 }
2767 2780 }
2768 2781 setFieldsNormalized(mask);
2769 2782 }
2770 2783
2771 2784 /**
2772 2785 * Computes the fixed date under either the Gregorian or the
2773 2786 * Julian calendar, using the given year and the specified calendar fields.
2774 2787 *
2775 2788 * @param cal the CalendarSystem to be used for the date calculation
2776 2789 * @param year the normalized year number, with 0 indicating the
2777 2790 * year 1 BCE, -1 indicating 2 BCE, etc.
2778 2791 * @param fieldMask the calendar fields to be used for the date calculation
2779 2792 * @return the fixed date
2780 2793 * @see Calendar#selectFields
2781 2794 */
2782 2795 private long getFixedDate(BaseCalendar cal, int year, int fieldMask) {
2783 2796 int month = JANUARY;
2784 2797 if (isFieldSet(fieldMask, MONTH)) {
2785 2798 // No need to check if MONTH has been set (no isSet(MONTH)
2786 2799 // call) since its unset value happens to be JANUARY (0).
2787 2800 month = internalGet(MONTH);
2788 2801
2789 2802 // If the month is out of range, adjust it into range
2790 2803 if (month > DECEMBER) {
2791 2804 year += month / 12;
2792 2805 month %= 12;
2793 2806 } else if (month < JANUARY) {
2794 2807 int[] rem = new int[1];
2795 2808 year += CalendarUtils.floorDivide(month, 12, rem);
2796 2809 month = rem[0];
2797 2810 }
2798 2811 }
2799 2812
2800 2813 // Get the fixed date since Jan 1, 1 (Gregorian). We are on
2801 2814 // the first day of either `month' or January in 'year'.
2802 2815 long fixedDate = cal.getFixedDate(year, month + 1, 1,
2803 2816 cal == gcal ? gdate : null);
2804 2817 if (isFieldSet(fieldMask, MONTH)) {
2805 2818 // Month-based calculations
2806 2819 if (isFieldSet(fieldMask, DAY_OF_MONTH)) {
2807 2820 // We are on the first day of the month. Just add the
2808 2821 // offset if DAY_OF_MONTH is set. If the isSet call
2809 2822 // returns false, that means DAY_OF_MONTH has been
2810 2823 // selected just because of the selected
2811 2824 // combination. We don't need to add any since the
2812 2825 // default value is the 1st.
2813 2826 if (isSet(DAY_OF_MONTH)) {
2814 2827 // To avoid underflow with DAY_OF_MONTH-1, add
2815 2828 // DAY_OF_MONTH, then subtract 1.
2816 2829 fixedDate += internalGet(DAY_OF_MONTH);
2817 2830 fixedDate--;
2818 2831 }
2819 2832 } else {
2820 2833 if (isFieldSet(fieldMask, WEEK_OF_MONTH)) {
2821 2834 long firstDayOfWeek = BaseCalendar.getDayOfWeekDateOnOrBefore(fixedDate + 6,
2822 2835 getFirstDayOfWeek());
2823 2836 // If we have enough days in the first week, then
2824 2837 // move to the previous week.
2825 2838 if ((firstDayOfWeek - fixedDate) >= getMinimalDaysInFirstWeek()) {
2826 2839 firstDayOfWeek -= 7;
2827 2840 }
2828 2841 if (isFieldSet(fieldMask, DAY_OF_WEEK)) {
2829 2842 firstDayOfWeek = BaseCalendar.getDayOfWeekDateOnOrBefore(firstDayOfWeek + 6,
2830 2843 internalGet(DAY_OF_WEEK));
2831 2844 }
2832 2845 // In lenient mode, we treat days of the previous
2833 2846 // months as a part of the specified
2834 2847 // WEEK_OF_MONTH. See 4633646.
2835 2848 fixedDate = firstDayOfWeek + 7 * (internalGet(WEEK_OF_MONTH) - 1);
2836 2849 } else {
2837 2850 int dayOfWeek;
2838 2851 if (isFieldSet(fieldMask, DAY_OF_WEEK)) {
2839 2852 dayOfWeek = internalGet(DAY_OF_WEEK);
2840 2853 } else {
2841 2854 dayOfWeek = getFirstDayOfWeek();
2842 2855 }
2843 2856 // We are basing this on the day-of-week-in-month. The only
2844 2857 // trickiness occurs if the day-of-week-in-month is
2845 2858 // negative.
2846 2859 int dowim;
2847 2860 if (isFieldSet(fieldMask, DAY_OF_WEEK_IN_MONTH)) {
2848 2861 dowim = internalGet(DAY_OF_WEEK_IN_MONTH);
2849 2862 } else {
2850 2863 dowim = 1;
2851 2864 }
2852 2865 if (dowim >= 0) {
2853 2866 fixedDate = BaseCalendar.getDayOfWeekDateOnOrBefore(fixedDate + (7 * dowim) - 1,
2854 2867 dayOfWeek);
2855 2868 } else {
2856 2869 // Go to the first day of the next week of
2857 2870 // the specified week boundary.
2858 2871 int lastDate = monthLength(month, year) + (7 * (dowim + 1));
2859 2872 // Then, get the day of week date on or before the last date.
2860 2873 fixedDate = BaseCalendar.getDayOfWeekDateOnOrBefore(fixedDate + lastDate - 1,
2861 2874 dayOfWeek);
2862 2875 }
2863 2876 }
2864 2877 }
2865 2878 } else {
2866 2879 if (year == gregorianCutoverYear && cal == gcal
2867 2880 && fixedDate < gregorianCutoverDate
2868 2881 && gregorianCutoverYear != gregorianCutoverYearJulian) {
2869 2882 // January 1 of the year doesn't exist. Use
2870 2883 // gregorianCutoverDate as the first day of the
2871 2884 // year.
2872 2885 fixedDate = gregorianCutoverDate;
2873 2886 }
2874 2887 // We are on the first day of the year.
2875 2888 if (isFieldSet(fieldMask, DAY_OF_YEAR)) {
2876 2889 // Add the offset, then subtract 1. (Make sure to avoid underflow.)
2877 2890 fixedDate += internalGet(DAY_OF_YEAR);
2878 2891 fixedDate--;
2879 2892 } else {
2880 2893 long firstDayOfWeek = BaseCalendar.getDayOfWeekDateOnOrBefore(fixedDate + 6,
2881 2894 getFirstDayOfWeek());
2882 2895 // If we have enough days in the first week, then move
2883 2896 // to the previous week.
2884 2897 if ((firstDayOfWeek - fixedDate) >= getMinimalDaysInFirstWeek()) {
2885 2898 firstDayOfWeek -= 7;
2886 2899 }
2887 2900 if (isFieldSet(fieldMask, DAY_OF_WEEK)) {
2888 2901 int dayOfWeek = internalGet(DAY_OF_WEEK);
2889 2902 if (dayOfWeek != getFirstDayOfWeek()) {
2890 2903 firstDayOfWeek = BaseCalendar.getDayOfWeekDateOnOrBefore(firstDayOfWeek + 6,
2891 2904 dayOfWeek);
2892 2905 }
2893 2906 }
2894 2907 fixedDate = firstDayOfWeek + 7 * ((long)internalGet(WEEK_OF_YEAR) - 1);
2895 2908 }
2896 2909 }
2897 2910
2898 2911 return fixedDate;
2899 2912 }
2900 2913
2901 2914 /**
2902 2915 * Returns this object if it's normalized (all fields and time are
2903 2916 * in sync). Otherwise, a cloned object is returned after calling
2904 2917 * complete() in lenient mode.
2905 2918 */
2906 2919 private GregorianCalendar getNormalizedCalendar() {
2907 2920 GregorianCalendar gc;
2908 2921 if (isFullyNormalized()) {
2909 2922 gc = this;
2910 2923 } else {
2911 2924 // Create a clone and normalize the calendar fields
2912 2925 gc = (GregorianCalendar) this.clone();
↓ open down ↓ |
1952 lines elided |
↑ open up ↑ |
2913 2926 gc.setLenient(true);
2914 2927 gc.complete();
2915 2928 }
2916 2929 return gc;
2917 2930 }
2918 2931
2919 2932 /**
2920 2933 * Returns the Julian calendar system instance (singleton). 'jcal'
2921 2934 * and 'jeras' are set upon the return.
2922 2935 */
2923 - synchronized private static BaseCalendar getJulianCalendarSystem() {
2936 + private static synchronized BaseCalendar getJulianCalendarSystem() {
2924 2937 if (jcal == null) {
2925 2938 jcal = (JulianCalendar) CalendarSystem.forName("julian");
2926 2939 jeras = jcal.getEras();
2927 2940 }
2928 2941 return jcal;
2929 2942 }
2930 2943
2931 2944 /**
2932 2945 * Returns the calendar system for dates before the cutover date
2933 2946 * in the cutover year. If the cutover date is January 1, the
2934 2947 * method returns Gregorian. Otherwise, Julian.
2935 2948 */
2936 2949 private BaseCalendar getCutoverCalendarSystem() {
↓ open down ↓ |
3 lines elided |
↑ open up ↑ |
2937 2950 if (gregorianCutoverYearJulian < gregorianCutoverYear) {
2938 2951 return gcal;
2939 2952 }
2940 2953 return getJulianCalendarSystem();
2941 2954 }
2942 2955
2943 2956 /**
2944 2957 * Determines if the specified year (normalized) is the Gregorian
2945 2958 * cutover year. This object must have been normalized.
2946 2959 */
2947 - private final boolean isCutoverYear(int normalizedYear) {
2960 + private boolean isCutoverYear(int normalizedYear) {
2948 2961 int cutoverYear = (calsys == gcal) ? gregorianCutoverYear : gregorianCutoverYearJulian;
2949 2962 return normalizedYear == cutoverYear;
2950 2963 }
2951 2964
2952 2965 /**
2953 2966 * Returns the fixed date of the first day of the year (usually
2954 2967 * January 1) before the specified date.
2955 2968 *
2956 2969 * @param date the date for which the first day of the year is
2957 2970 * calculated. The date has to be in the cut-over year (Gregorian
2958 2971 * or Julian).
2959 2972 * @param fixedDate the fixed date representation of the date
2960 2973 */
2961 2974 private long getFixedDateJan1(BaseCalendar.Date date, long fixedDate) {
2962 2975 assert date.getNormalizedYear() == gregorianCutoverYear ||
2963 2976 date.getNormalizedYear() == gregorianCutoverYearJulian;
↓ open down ↓ |
6 lines elided |
↑ open up ↑ |
2964 2977 if (gregorianCutoverYear != gregorianCutoverYearJulian) {
2965 2978 if (fixedDate >= gregorianCutoverDate) {
2966 2979 // Dates before the cutover date don't exist
2967 2980 // in the same (Gregorian) year. So, no
2968 2981 // January 1 exists in the year. Use the
2969 2982 // cutover date as the first day of the year.
2970 2983 return gregorianCutoverDate;
2971 2984 }
2972 2985 }
2973 2986 // January 1 of the normalized year should exist.
2974 - BaseCalendar jcal = getJulianCalendarSystem();
2975 - return jcal.getFixedDate(date.getNormalizedYear(), BaseCalendar.JANUARY, 1, null);
2987 + BaseCalendar juliancal = getJulianCalendarSystem();
2988 + return juliancal.getFixedDate(date.getNormalizedYear(), BaseCalendar.JANUARY, 1, null);
2976 2989 }
2977 2990
2978 2991 /**
2979 2992 * Returns the fixed date of the first date of the month (usually
2980 2993 * the 1st of the month) before the specified date.
2981 2994 *
2982 2995 * @param date the date for which the first day of the month is
2983 2996 * calculated. The date has to be in the cut-over year (Gregorian
2984 2997 * or Julian).
2985 2998 * @param fixedDate the fixed date representation of the date
2986 2999 */
2987 3000 private long getFixedDateMonth1(BaseCalendar.Date date, long fixedDate) {
2988 3001 assert date.getNormalizedYear() == gregorianCutoverYear ||
2989 3002 date.getNormalizedYear() == gregorianCutoverYearJulian;
2990 3003 BaseCalendar.Date gCutover = getGregorianCutoverDate();
2991 3004 if (gCutover.getMonth() == BaseCalendar.JANUARY
2992 3005 && gCutover.getDayOfMonth() == 1) {
2993 3006 // The cutover happened on January 1.
2994 3007 return fixedDate - date.getDayOfMonth() + 1;
2995 3008 }
2996 3009
2997 3010 long fixedDateMonth1;
2998 3011 // The cutover happened sometime during the year.
2999 3012 if (date.getMonth() == gCutover.getMonth()) {
3000 3013 // The cutover happened in the month.
3001 3014 BaseCalendar.Date jLastDate = getLastJulianDate();
3002 3015 if (gregorianCutoverYear == gregorianCutoverYearJulian
3003 3016 && gCutover.getMonth() == jLastDate.getMonth()) {
3004 3017 // The "gap" fits in the same month.
3005 3018 fixedDateMonth1 = jcal.getFixedDate(date.getNormalizedYear(),
3006 3019 date.getMonth(),
3007 3020 1,
3008 3021 null);
3009 3022 } else {
3010 3023 // Use the cutover date as the first day of the month.
3011 3024 fixedDateMonth1 = gregorianCutoverDate;
3012 3025 }
3013 3026 } else {
3014 3027 // The cutover happened before the month.
3015 3028 fixedDateMonth1 = fixedDate - date.getDayOfMonth() + 1;
3016 3029 }
3017 3030
3018 3031 return fixedDateMonth1;
3019 3032 }
3020 3033
3021 3034 /**
3022 3035 * Returns a CalendarDate produced from the specified fixed date.
3023 3036 *
3024 3037 * @param fd the fixed date
3025 3038 */
3026 3039 private BaseCalendar.Date getCalendarDate(long fd) {
3027 3040 BaseCalendar cal = (fd >= gregorianCutoverDate) ? gcal : getJulianCalendarSystem();
3028 3041 BaseCalendar.Date d = (BaseCalendar.Date) cal.newCalendarDate(TimeZone.NO_TIMEZONE);
3029 3042 cal.getCalendarDateFromFixedDate(d, fd);
3030 3043 return d;
3031 3044 }
3032 3045
3033 3046 /**
3034 3047 * Returns the Gregorian cutover date as a BaseCalendar.Date. The
3035 3048 * date is a Gregorian date.
3036 3049 */
3037 3050 private BaseCalendar.Date getGregorianCutoverDate() {
3038 3051 return getCalendarDate(gregorianCutoverDate);
3039 3052 }
3040 3053
3041 3054 /**
3042 3055 * Returns the day before the Gregorian cutover date as a
3043 3056 * BaseCalendar.Date. The date is a Julian date.
3044 3057 */
3045 3058 private BaseCalendar.Date getLastJulianDate() {
3046 3059 return getCalendarDate(gregorianCutoverDate - 1);
3047 3060 }
3048 3061
3049 3062 /**
3050 3063 * Returns the length of the specified month in the specified
3051 3064 * year. The year number must be normalized.
3052 3065 *
3053 3066 * @see #isLeapYear(int)
3054 3067 */
3055 3068 private int monthLength(int month, int year) {
3056 3069 return isLeapYear(year) ? LEAP_MONTH_LENGTH[month] : MONTH_LENGTH[month];
3057 3070 }
3058 3071
3059 3072 /**
3060 3073 * Returns the length of the specified month in the year provided
3061 3074 * by internalGet(YEAR).
3062 3075 *
3063 3076 * @see #isLeapYear(int)
3064 3077 */
3065 3078 private int monthLength(int month) {
3066 3079 int year = internalGet(YEAR);
3067 3080 if (internalGetEra() == BCE) {
3068 3081 year = 1 - year;
3069 3082 }
3070 3083 return monthLength(month, year);
3071 3084 }
3072 3085
3073 3086 private int actualMonthLength() {
3074 3087 int year = cdate.getNormalizedYear();
3075 3088 if (year != gregorianCutoverYear && year != gregorianCutoverYearJulian) {
3076 3089 return calsys.getMonthLength(cdate);
3077 3090 }
3078 3091 BaseCalendar.Date date = (BaseCalendar.Date) cdate.clone();
3079 3092 long fd = calsys.getFixedDate(date);
3080 3093 long month1 = getFixedDateMonth1(date, fd);
3081 3094 long next1 = month1 + calsys.getMonthLength(date);
3082 3095 if (next1 < gregorianCutoverDate) {
3083 3096 return (int)(next1 - month1);
3084 3097 }
3085 3098 if (cdate != gdate) {
3086 3099 date = (BaseCalendar.Date) gcal.newCalendarDate(TimeZone.NO_TIMEZONE);
3087 3100 }
3088 3101 gcal.getCalendarDateFromFixedDate(date, next1);
3089 3102 next1 = getFixedDateMonth1(date, next1);
3090 3103 return (int)(next1 - month1);
3091 3104 }
3092 3105
3093 3106 /**
3094 3107 * Returns the length (in days) of the specified year. The year
3095 3108 * must be normalized.
3096 3109 */
3097 3110 private int yearLength(int year) {
3098 3111 return isLeapYear(year) ? 366 : 365;
3099 3112 }
3100 3113
3101 3114 /**
3102 3115 * Returns the length (in days) of the year provided by
3103 3116 * internalGet(YEAR).
3104 3117 */
3105 3118 private int yearLength() {
3106 3119 int year = internalGet(YEAR);
3107 3120 if (internalGetEra() == BCE) {
3108 3121 year = 1 - year;
3109 3122 }
3110 3123 return yearLength(year);
3111 3124 }
3112 3125
3113 3126 /**
3114 3127 * After adjustments such as add(MONTH), add(YEAR), we don't want the
3115 3128 * month to jump around. E.g., we don't want Jan 31 + 1 month to go to Mar
3116 3129 * 3, we want it to go to Feb 28. Adjustments which might run into this
3117 3130 * problem call this method to retain the proper month.
3118 3131 */
3119 3132 private void pinDayOfMonth() {
3120 3133 int year = internalGet(YEAR);
3121 3134 int monthLen;
3122 3135 if (year > gregorianCutoverYear || year < gregorianCutoverYearJulian) {
3123 3136 monthLen = monthLength(internalGet(MONTH));
3124 3137 } else {
3125 3138 GregorianCalendar gc = getNormalizedCalendar();
3126 3139 monthLen = gc.getActualMaximum(DAY_OF_MONTH);
3127 3140 }
3128 3141 int dom = internalGet(DAY_OF_MONTH);
3129 3142 if (dom > monthLen) {
3130 3143 set(DAY_OF_MONTH, monthLen);
3131 3144 }
3132 3145 }
3133 3146
3134 3147 /**
3135 3148 * Returns the fixed date value of this object. The time value and
3136 3149 * calendar fields must be in synch.
3137 3150 */
3138 3151 private long getCurrentFixedDate() {
3139 3152 return (calsys == gcal) ? cachedFixedDate : calsys.getFixedDate(cdate);
3140 3153 }
3141 3154
3142 3155 /**
3143 3156 * Returns the new value after 'roll'ing the specified value and amount.
3144 3157 */
3145 3158 private static int getRolledValue(int value, int amount, int min, int max) {
3146 3159 assert value >= min && value <= max;
3147 3160 int range = max - min + 1;
3148 3161 amount %= range;
3149 3162 int n = value + amount;
3150 3163 if (n > max) {
3151 3164 n -= range;
3152 3165 } else if (n < min) {
3153 3166 n += range;
3154 3167 }
3155 3168 assert n >= min && n <= max;
3156 3169 return n;
3157 3170 }
3158 3171
3159 3172 /**
3160 3173 * Returns the ERA. We need a special method for this because the
3161 3174 * default ERA is CE, but a zero (unset) ERA is BCE.
3162 3175 */
3163 3176 private int internalGetEra() {
3164 3177 return isSet(ERA) ? internalGet(ERA) : CE;
3165 3178 }
3166 3179
3167 3180 /**
3168 3181 * Updates internal state.
3169 3182 */
3170 3183 private void readObject(ObjectInputStream stream)
3171 3184 throws IOException, ClassNotFoundException {
3172 3185 stream.defaultReadObject();
3173 3186 if (gdate == null) {
3174 3187 gdate = (BaseCalendar.Date) gcal.newCalendarDate(getZone());
3175 3188 cachedFixedDate = Long.MIN_VALUE;
3176 3189 }
3177 3190 setGregorianChange(gregorianCutover);
3178 3191 }
3179 3192 }
↓ open down ↓ |
194 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX