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