1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
|
/*
* Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.util.calendar;
import java.util.TimeZone;
/**
* The <code>BaseCalendar</code> provides basic calendar calculation
* functions to support the Julian, Gregorian, and Gregorian-based
* calendar systems.
*
* @author Masayoshi Okutsu
* @since 1.5
*/
public abstract class BaseCalendar extends AbstractCalendar {
public static final int JANUARY = 1;
public static final int FEBRUARY = 2;
public static final int MARCH = 3;
public static final int APRIL = 4;
public static final int MAY = 5;
public static final int JUNE = 6;
public static final int JULY = 7;
public static final int AUGUST = 8;
public static final int SEPTEMBER = 9;
public static final int OCTOBER = 10;
public static final int NOVEMBER = 11;
public static final int DECEMBER = 12;
// day of week constants
public static final int SUNDAY = 1;
public static final int MONDAY = 2;
public static final int TUESDAY = 3;
public static final int WEDNESDAY = 4;
public static final int THURSDAY = 5;
public static final int FRIDAY = 6;
public static final int SATURDAY = 7;
// The base Gregorian year of FIXED_DATES[]
private static final int BASE_YEAR = 1970;
// Pre-calculated fixed dates of January 1 from BASE_YEAR
// (Gregorian). This table covers all the years that can be
// supported by the POSIX time_t (32-bit) after the Epoch. Note
// that the data type is int[].
private static final int[] FIXED_DATES = {
719163, // 1970
719528, // 1971
719893, // 1972
720259, // 1973
720624, // 1974
720989, // 1975
721354, // 1976
721720, // 1977
722085, // 1978
722450, // 1979
722815, // 1980
723181, // 1981
723546, // 1982
723911, // 1983
724276, // 1984
724642, // 1985
725007, // 1986
725372, // 1987
725737, // 1988
726103, // 1989
726468, // 1990
726833, // 1991
727198, // 1992
727564, // 1993
727929, // 1994
728294, // 1995
728659, // 1996
729025, // 1997
729390, // 1998
729755, // 1999
730120, // 2000
730486, // 2001
730851, // 2002
731216, // 2003
731581, // 2004
731947, // 2005
732312, // 2006
732677, // 2007
733042, // 2008
733408, // 2009
733773, // 2010
734138, // 2011
734503, // 2012
734869, // 2013
735234, // 2014
735599, // 2015
735964, // 2016
736330, // 2017
736695, // 2018
737060, // 2019
737425, // 2020
737791, // 2021
738156, // 2022
738521, // 2023
738886, // 2024
739252, // 2025
739617, // 2026
739982, // 2027
740347, // 2028
740713, // 2029
741078, // 2030
741443, // 2031
741808, // 2032
742174, // 2033
742539, // 2034
742904, // 2035
743269, // 2036
743635, // 2037
744000, // 2038
744365, // 2039
};
public abstract static class Date extends CalendarDate {
protected Date() {
super();
}
protected Date(TimeZone zone) {
super(zone);
}
public Date setNormalizedDate(int normalizedYear, int month, int dayOfMonth) {
setNormalizedYear(normalizedYear);
setMonth(month).setDayOfMonth(dayOfMonth);
return this;
}
public abstract int getNormalizedYear();
public abstract void setNormalizedYear(int normalizedYear);
// Cache for the fixed date of January 1 and year length of the
// cachedYear. A simple benchmark showed 7% performance
// improvement with >90% cache hit. The initial values are for Gregorian.
int cachedYear = 2004;
long cachedFixedDateJan1 = 731581L;
long cachedFixedDateNextJan1 = cachedFixedDateJan1 + 366;
protected final boolean hit(int year) {
return year == cachedYear;
}
protected final boolean hit(long fixedDate) {
return (fixedDate >= cachedFixedDateJan1 &&
fixedDate < cachedFixedDateNextJan1);
}
protected int getCachedYear() {
return cachedYear;
}
protected long getCachedJan1() {
return cachedFixedDateJan1;
}
protected void setCache(int year, long jan1, int len) {
cachedYear = year;
cachedFixedDateJan1 = jan1;
cachedFixedDateNextJan1 = jan1 + len;
}
}
public boolean validate(CalendarDate date) {
Date bdate = (Date) date;
if (bdate.isNormalized()) {
return true;
}
int month = bdate.getMonth();
if (month < JANUARY || month > DECEMBER) {
return false;
}
int d = bdate.getDayOfMonth();
if (d <= 0 || d > getMonthLength(bdate.getNormalizedYear(), month)) {
return false;
}
int dow = bdate.getDayOfWeek();
if (dow != Date.FIELD_UNDEFINED && dow != getDayOfWeek(bdate)) {
return false;
}
if (!validateTime(date)) {
return false;
}
bdate.setNormalized(true);
return true;
}
public boolean normalize(CalendarDate date) {
if (date.isNormalized()) {
return true;
}
Date bdate = (Date) date;
TimeZone zi = bdate.getZone();
// If the date has a time zone, then we need to recalculate
// the calendar fields. Let getTime() do it.
if (zi != null) {
getTime(date);
return true;
}
int days = normalizeTime(bdate);
normalizeMonth(bdate);
long d = (long)bdate.getDayOfMonth() + days;
int m = bdate.getMonth();
int y = bdate.getNormalizedYear();
int ml = getMonthLength(y, m);
if (!(d > 0 && d <= ml)) {
if (d <= 0 && d > -28) {
ml = getMonthLength(y, --m);
d += ml;
bdate.setDayOfMonth((int) d);
if (m == 0) {
m = DECEMBER;
bdate.setNormalizedYear(y - 1);
}
bdate.setMonth(m);
} else if (d > ml && d < (ml + 28)) {
d -= ml;
++m;
bdate.setDayOfMonth((int)d);
if (m > DECEMBER) {
bdate.setNormalizedYear(y + 1);
m = JANUARY;
}
bdate.setMonth(m);
} else {
long fixedDate = d + getFixedDate(y, m, 1, bdate) - 1L;
getCalendarDateFromFixedDate(bdate, fixedDate);
}
} else {
bdate.setDayOfWeek(getDayOfWeek(bdate));
}
date.setLeapYear(isLeapYear(bdate.getNormalizedYear()));
date.setZoneOffset(0);
date.setDaylightSaving(0);
bdate.setNormalized(true);
return true;
}
void normalizeMonth(CalendarDate date) {
Date bdate = (Date) date;
int year = bdate.getNormalizedYear();
long month = bdate.getMonth();
if (month <= 0) {
long xm = 1L - month;
year -= (int)((xm / 12) + 1);
month = 13 - (xm % 12);
bdate.setNormalizedYear(year);
bdate.setMonth((int) month);
} else if (month > DECEMBER) {
year += (int)((month - 1) / 12);
month = ((month - 1)) % 12 + 1;
bdate.setNormalizedYear(year);
bdate.setMonth((int) month);
}
}
/**
* Returns 366 if the specified date is in a leap year, or 365
* otherwise This method does not perform the normalization with
* the specified <code>CalendarDate</code>. The
* <code>CalendarDate</code> must be normalized to get a correct
* value.
*
* @param a <code>CalendarDate</code>
* @return a year length in days
* @throws ClassCastException if the specified date is not a
* {@link BaseCalendar.Date}
*/
public int getYearLength(CalendarDate date) {
return isLeapYear(((Date)date).getNormalizedYear()) ? 366 : 365;
}
public int getYearLengthInMonths(CalendarDate date) {
return 12;
}
static final int[] DAYS_IN_MONTH
// 12 1 2 3 4 5 6 7 8 9 10 11 12
= { 31, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
static final int[] ACCUMULATED_DAYS_IN_MONTH
// 12/1 1/1 2/1 3/1 4/1 5/1 6/1 7/1 8/1 9/1 10/1 11/1 12/1
= { -30, 0, 31, 59, 90,120,151,181,212,243, 273, 304, 334};
static final int[] ACCUMULATED_DAYS_IN_MONTH_LEAP
// 12/1 1/1 2/1 3/1 4/1 5/1 6/1 7/1 8/1 9/1 10/1 11/1 12/1
= { -30, 0, 31, 59+1, 90+1,120+1,151+1,181+1,212+1,243+1, 273+1, 304+1, 334+1};
public int getMonthLength(CalendarDate date) {
Date gdate = (Date) date;
int month = gdate.getMonth();
if (month < JANUARY || month > DECEMBER) {
throw new IllegalArgumentException("Illegal month value: " + month);
}
return getMonthLength(gdate.getNormalizedYear(), month);
}
// accepts 0 (December in the previous year) to 12.
private int getMonthLength(int year, int month) {
int days = DAYS_IN_MONTH[month];
if (month == FEBRUARY && isLeapYear(year)) {
days++;
}
return days;
}
public long getDayOfYear(CalendarDate date) {
return getDayOfYear(((Date)date).getNormalizedYear(),
date.getMonth(),
date.getDayOfMonth());
}
final long getDayOfYear(int year, int month, int dayOfMonth) {
return (long) dayOfMonth
+ (isLeapYear(year) ?
ACCUMULATED_DAYS_IN_MONTH_LEAP[month] : ACCUMULATED_DAYS_IN_MONTH[month]);
}
// protected
public long getFixedDate(CalendarDate date) {
if (!date.isNormalized()) {
normalizeMonth(date);
}
return getFixedDate(((Date)date).getNormalizedYear(),
date.getMonth(),
date.getDayOfMonth(),
(BaseCalendar.Date) date);
}
// public for java.util.GregorianCalendar
public long getFixedDate(int year, int month, int dayOfMonth, BaseCalendar.Date cache) {
boolean isJan1 = month == JANUARY && dayOfMonth == 1;
// Look up the one year cache
if (cache != null && cache.hit(year)) {
if (isJan1) {
return cache.getCachedJan1();
}
return cache.getCachedJan1() + getDayOfYear(year, month, dayOfMonth) - 1;
}
// Look up the pre-calculated fixed date table
int n = year - BASE_YEAR;
if (n >= 0 && n < FIXED_DATES.length) {
long jan1 = FIXED_DATES[n];
if (cache != null) {
cache.setCache(year, jan1, isLeapYear(year) ? 366 : 365);
}
return isJan1 ? jan1 : jan1 + getDayOfYear(year, month, dayOfMonth) - 1;
}
long prevyear = (long)year - 1;
long days = dayOfMonth;
if (prevyear >= 0) {
days += (365 * prevyear)
+ (prevyear / 4)
- (prevyear / 100)
+ (prevyear / 400)
+ ((367 * month - 362) / 12);
} else {
days += (365 * prevyear)
+ CalendarUtils.floorDivide(prevyear, 4)
- CalendarUtils.floorDivide(prevyear, 100)
+ CalendarUtils.floorDivide(prevyear, 400)
+ CalendarUtils.floorDivide((367 * month - 362), 12);
}
if (month > FEBRUARY) {
days -= isLeapYear(year) ? 1 : 2;
}
// If it's January 1, update the cache.
if (cache != null && isJan1) {
cache.setCache(year, days, isLeapYear(year) ? 366 : 365);
}
return days;
}
/**
* Calculates calendar fields and store them in the specified
* <code>CalendarDate</code>.
*/
// should be 'protected'
public void getCalendarDateFromFixedDate(CalendarDate date,
long fixedDate) {
Date gdate = (Date) date;
int year;
long jan1;
boolean isLeap;
if (gdate.hit(fixedDate)) {
year = gdate.getCachedYear();
jan1 = gdate.getCachedJan1();
isLeap = isLeapYear(year);
} else {
// Looking up FIXED_DATES[] here didn't improve performance
// much. So we calculate year and jan1. getFixedDate()
// will look up FIXED_DATES[] actually.
year = getGregorianYearFromFixedDate(fixedDate);
jan1 = getFixedDate(year, JANUARY, 1, null);
isLeap = isLeapYear(year);
// Update the cache data
gdate.setCache (year, jan1, isLeap ? 366 : 365);
}
int priorDays = (int)(fixedDate - jan1);
long mar1 = jan1 + 31 + 28;
if (isLeap) {
++mar1;
}
if (fixedDate >= mar1) {
priorDays += isLeap ? 1 : 2;
}
int month = 12 * priorDays + 373;
if (month > 0) {
month /= 367;
} else {
month = CalendarUtils.floorDivide(month, 367);
}
long month1 = jan1 + ACCUMULATED_DAYS_IN_MONTH[month];
if (isLeap && month >= MARCH) {
++month1;
}
int dayOfMonth = (int)(fixedDate - month1) + 1;
int dayOfWeek = getDayOfWeekFromFixedDate(fixedDate);
assert dayOfWeek > 0 : "negative day of week " + dayOfWeek;
gdate.setNormalizedYear(year);
gdate.setMonth(month);
gdate.setDayOfMonth(dayOfMonth);
gdate.setDayOfWeek(dayOfWeek);
gdate.setLeapYear(isLeap);
gdate.setNormalized(true);
}
/**
* Returns the day of week of the given Gregorian date.
*/
public int getDayOfWeek(CalendarDate date) {
long fixedDate = getFixedDate(date);
return getDayOfWeekFromFixedDate(fixedDate);
}
public static final int getDayOfWeekFromFixedDate(long fixedDate) {
// The fixed day 1 (January 1, 1 Gregorian) is Monday.
if (fixedDate >= 0) {
return (int)(fixedDate % 7) + SUNDAY;
}
return (int)CalendarUtils.mod(fixedDate, 7) + SUNDAY;
}
public int getYearFromFixedDate(long fixedDate) {
return getGregorianYearFromFixedDate(fixedDate);
}
/**
* Returns the Gregorian year number of the given fixed date.
*/
final int getGregorianYearFromFixedDate(long fixedDate) {
long d0;
int d1, d2, d3, d4;
int n400, n100, n4, n1;
int year;
if (fixedDate > 0) {
d0 = fixedDate - 1;
n400 = (int)(d0 / 146097);
d1 = (int)(d0 % 146097);
n100 = d1 / 36524;
d2 = d1 % 36524;
n4 = d2 / 1461;
d3 = d2 % 1461;
n1 = d3 / 365;
d4 = (d3 % 365) + 1;
} else {
d0 = fixedDate - 1;
n400 = (int)CalendarUtils.floorDivide(d0, 146097L);
d1 = (int)CalendarUtils.mod(d0, 146097L);
n100 = CalendarUtils.floorDivide(d1, 36524);
d2 = CalendarUtils.mod(d1, 36524);
n4 = CalendarUtils.floorDivide(d2, 1461);
d3 = CalendarUtils.mod(d2, 1461);
n1 = CalendarUtils.floorDivide(d3, 365);
d4 = CalendarUtils.mod(d3, 365) + 1;
}
year = 400 * n400 + 100 * n100 + 4 * n4 + n1;
if (!(n100 == 4 || n1 == 4)) {
++year;
}
return year;
}
/**
* @return true if the specified year is a Gregorian leap year, or
* false otherwise.
* @see BaseCalendar#isGregorianLeapYear
*/
protected boolean isLeapYear(CalendarDate date) {
return isLeapYear(((Date)date).getNormalizedYear());
}
boolean isLeapYear(int normalizedYear) {
return CalendarUtils.isGregorianLeapYear(normalizedYear);
}
}
|