1 | // © 2016 and later: Unicode, Inc. and others. |
2 | // License & terms of use: http://www.unicode.org/copyright.html |
3 | /* |
4 | ******************************************************************************* |
5 | * Copyright (C) 1997-2016, International Business Machines Corporation and |
6 | * others. All Rights Reserved. |
7 | ******************************************************************************* |
8 | * |
9 | * File GREGOCAL.CPP |
10 | * |
11 | * Modification History: |
12 | * |
13 | * Date Name Description |
14 | * 02/05/97 clhuang Creation. |
15 | * 03/28/97 aliu Made highly questionable fix to computeFields to |
16 | * handle DST correctly. |
17 | * 04/22/97 aliu Cleaned up code drastically. Added monthLength(). |
18 | * Finished unimplemented parts of computeTime() for |
19 | * week-based date determination. Removed quetionable |
20 | * fix and wrote correct fix for computeFields() and |
21 | * daylight time handling. Rewrote inDaylightTime() |
22 | * and computeFields() to handle sensitive Daylight to |
23 | * Standard time transitions correctly. |
24 | * 05/08/97 aliu Added code review changes. Fixed isLeapYear() to |
25 | * not cutover. |
26 | * 08/12/97 aliu Added equivalentTo. Misc other fixes. Updated |
27 | * add() from Java source. |
28 | * 07/28/98 stephen Sync up with JDK 1.2 |
29 | * 09/14/98 stephen Changed type of kOneDay, kOneWeek to double. |
30 | * Fixed bug in roll() |
31 | * 10/15/99 aliu Fixed j31, incorrect WEEK_OF_YEAR computation. |
32 | * 10/15/99 aliu Fixed j32, cannot set date to Feb 29 2000 AD. |
33 | * {JDK bug 4210209 4209272} |
34 | * 11/15/99 weiv Added YEAR_WOY and DOW_LOCAL computation |
35 | * to timeToFields method, updated kMinValues, kMaxValues & kLeastMaxValues |
36 | * 12/09/99 aliu Fixed j81, calculation errors and roll bugs |
37 | * in year of cutover. |
38 | * 01/24/2000 aliu Revised computeJulianDay for YEAR YEAR_WOY WOY. |
39 | ******************************************************************************** |
40 | */ |
41 | |
42 | #include "unicode/utypes.h" |
43 | #include <float.h> |
44 | |
45 | #if !UCONFIG_NO_FORMATTING |
46 | |
47 | #include "unicode/gregocal.h" |
48 | #include "gregoimp.h" |
49 | #include "umutex.h" |
50 | #include "uassert.h" |
51 | |
52 | // ***************************************************************************** |
53 | // class GregorianCalendar |
54 | // ***************************************************************************** |
55 | |
56 | /** |
57 | * Note that the Julian date used here is not a true Julian date, since |
58 | * it is measured from midnight, not noon. This value is the Julian |
59 | * day number of January 1, 1970 (Gregorian calendar) at noon UTC. [LIU] |
60 | */ |
61 | |
62 | static const int16_t kNumDays[] |
63 | = {0,31,59,90,120,151,181,212,243,273,304,334}; // 0-based, for day-in-year |
64 | static const int16_t kLeapNumDays[] |
65 | = {0,31,60,91,121,152,182,213,244,274,305,335}; // 0-based, for day-in-year |
66 | static const int8_t kMonthLength[] |
67 | = {31,28,31,30,31,30,31,31,30,31,30,31}; // 0-based |
68 | static const int8_t kLeapMonthLength[] |
69 | = {31,29,31,30,31,30,31,31,30,31,30,31}; // 0-based |
70 | |
71 | // setTimeInMillis() limits the Julian day range to +/-7F000000. |
72 | // This would seem to limit the year range to: |
73 | // ms=+183882168921600000 jd=7f000000 December 20, 5828963 AD |
74 | // ms=-184303902528000000 jd=81000000 September 20, 5838270 BC |
75 | // HOWEVER, CalendarRegressionTest/Test4167060 shows that the actual |
76 | // range limit on the year field is smaller (~ +/-140000). [alan 3.0] |
77 | |
78 | static const int32_t kGregorianCalendarLimits[UCAL_FIELD_COUNT][4] = { |
79 | // Minimum Greatest Least Maximum |
80 | // Minimum Maximum |
81 | { 0, 0, 1, 1}, // ERA |
82 | { 1, 1, 140742, 144683}, // YEAR |
83 | { 0, 0, 11, 11}, // MONTH |
84 | { 1, 1, 52, 53}, // WEEK_OF_YEAR |
85 | {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH |
86 | { 1, 1, 28, 31}, // DAY_OF_MONTH |
87 | { 1, 1, 365, 366}, // DAY_OF_YEAR |
88 | {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK |
89 | { -1, -1, 4, 5}, // DAY_OF_WEEK_IN_MONTH |
90 | {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM |
91 | {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR |
92 | {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY |
93 | {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE |
94 | {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND |
95 | {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND |
96 | {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET |
97 | {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET |
98 | { -140742, -140742, 140742, 144683}, // YEAR_WOY |
99 | {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL |
100 | { -140742, -140742, 140742, 144683}, // EXTENDED_YEAR |
101 | {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY |
102 | {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY |
103 | {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH |
104 | }; |
105 | |
106 | /* |
107 | * <pre> |
108 | * Greatest Least |
109 | * Field name Minimum Minimum Maximum Maximum |
110 | * ---------- ------- ------- ------- ------- |
111 | * ERA 0 0 1 1 |
112 | * YEAR 1 1 140742 144683 |
113 | * MONTH 0 0 11 11 |
114 | * WEEK_OF_YEAR 1 1 52 53 |
115 | * WEEK_OF_MONTH 0 0 4 6 |
116 | * DAY_OF_MONTH 1 1 28 31 |
117 | * DAY_OF_YEAR 1 1 365 366 |
118 | * DAY_OF_WEEK 1 1 7 7 |
119 | * DAY_OF_WEEK_IN_MONTH -1 -1 4 5 |
120 | * AM_PM 0 0 1 1 |
121 | * HOUR 0 0 11 11 |
122 | * HOUR_OF_DAY 0 0 23 23 |
123 | * MINUTE 0 0 59 59 |
124 | * SECOND 0 0 59 59 |
125 | * MILLISECOND 0 0 999 999 |
126 | * ZONE_OFFSET -12* -12* 12* 12* |
127 | * DST_OFFSET 0 0 1* 1* |
128 | * YEAR_WOY 1 1 140742 144683 |
129 | * DOW_LOCAL 1 1 7 7 |
130 | * </pre> |
131 | * (*) In units of one-hour |
132 | */ |
133 | |
134 | #if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL) |
135 | #include <stdio.h> |
136 | #endif |
137 | |
138 | U_NAMESPACE_BEGIN |
139 | |
140 | UOBJECT_DEFINE_RTTI_IMPLEMENTATION(GregorianCalendar) |
141 | |
142 | // 00:00:00 UTC, October 15, 1582, expressed in ms from the epoch. |
143 | // Note that only Italy and other Catholic countries actually |
144 | // observed this cutover. Most other countries followed in |
145 | // the next few centuries, some as late as 1928. [LIU] |
146 | // in Java, -12219292800000L |
147 | //const UDate GregorianCalendar::kPapalCutover = -12219292800000L; |
148 | static const uint32_t kCutoverJulianDay = 2299161; |
149 | static const UDate kPapalCutover = (2299161.0 - kEpochStartAsJulianDay) * U_MILLIS_PER_DAY; |
150 | //static const UDate kPapalCutoverJulian = (2299161.0 - kEpochStartAsJulianDay); |
151 | |
152 | // ------------------------------------- |
153 | |
154 | GregorianCalendar::GregorianCalendar(UErrorCode& status) |
155 | : Calendar(status), |
156 | fGregorianCutover(kPapalCutover), |
157 | fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582), |
158 | fIsGregorian(TRUE), fInvertGregorian(FALSE) |
159 | { |
160 | setTimeInMillis(getNow(), status); |
161 | } |
162 | |
163 | // ------------------------------------- |
164 | |
165 | GregorianCalendar::GregorianCalendar(TimeZone* zone, UErrorCode& status) |
166 | : Calendar(zone, Locale::getDefault(), status), |
167 | fGregorianCutover(kPapalCutover), |
168 | fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582), |
169 | fIsGregorian(TRUE), fInvertGregorian(FALSE) |
170 | { |
171 | setTimeInMillis(getNow(), status); |
172 | } |
173 | |
174 | // ------------------------------------- |
175 | |
176 | GregorianCalendar::GregorianCalendar(const TimeZone& zone, UErrorCode& status) |
177 | : Calendar(zone, Locale::getDefault(), status), |
178 | fGregorianCutover(kPapalCutover), |
179 | fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582), |
180 | fIsGregorian(TRUE), fInvertGregorian(FALSE) |
181 | { |
182 | setTimeInMillis(getNow(), status); |
183 | } |
184 | |
185 | // ------------------------------------- |
186 | |
187 | GregorianCalendar::GregorianCalendar(const Locale& aLocale, UErrorCode& status) |
188 | : Calendar(TimeZone::createDefault(), aLocale, status), |
189 | fGregorianCutover(kPapalCutover), |
190 | fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582), |
191 | fIsGregorian(TRUE), fInvertGregorian(FALSE) |
192 | { |
193 | setTimeInMillis(getNow(), status); |
194 | } |
195 | |
196 | // ------------------------------------- |
197 | |
198 | GregorianCalendar::GregorianCalendar(TimeZone* zone, const Locale& aLocale, |
199 | UErrorCode& status) |
200 | : Calendar(zone, aLocale, status), |
201 | fGregorianCutover(kPapalCutover), |
202 | fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582), |
203 | fIsGregorian(TRUE), fInvertGregorian(FALSE) |
204 | { |
205 | setTimeInMillis(getNow(), status); |
206 | } |
207 | |
208 | // ------------------------------------- |
209 | |
210 | GregorianCalendar::GregorianCalendar(const TimeZone& zone, const Locale& aLocale, |
211 | UErrorCode& status) |
212 | : Calendar(zone, aLocale, status), |
213 | fGregorianCutover(kPapalCutover), |
214 | fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582), |
215 | fIsGregorian(TRUE), fInvertGregorian(FALSE) |
216 | { |
217 | setTimeInMillis(getNow(), status); |
218 | } |
219 | |
220 | // ------------------------------------- |
221 | |
222 | GregorianCalendar::GregorianCalendar(int32_t year, int32_t month, int32_t date, |
223 | UErrorCode& status) |
224 | : Calendar(TimeZone::createDefault(), Locale::getDefault(), status), |
225 | fGregorianCutover(kPapalCutover), |
226 | fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582), |
227 | fIsGregorian(TRUE), fInvertGregorian(FALSE) |
228 | { |
229 | set(UCAL_ERA, AD); |
230 | set(UCAL_YEAR, year); |
231 | set(UCAL_MONTH, month); |
232 | set(UCAL_DATE, date); |
233 | } |
234 | |
235 | // ------------------------------------- |
236 | |
237 | GregorianCalendar::GregorianCalendar(int32_t year, int32_t month, int32_t date, |
238 | int32_t hour, int32_t minute, UErrorCode& status) |
239 | : Calendar(TimeZone::createDefault(), Locale::getDefault(), status), |
240 | fGregorianCutover(kPapalCutover), |
241 | fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582), |
242 | fIsGregorian(TRUE), fInvertGregorian(FALSE) |
243 | { |
244 | set(UCAL_ERA, AD); |
245 | set(UCAL_YEAR, year); |
246 | set(UCAL_MONTH, month); |
247 | set(UCAL_DATE, date); |
248 | set(UCAL_HOUR_OF_DAY, hour); |
249 | set(UCAL_MINUTE, minute); |
250 | } |
251 | |
252 | // ------------------------------------- |
253 | |
254 | GregorianCalendar::GregorianCalendar(int32_t year, int32_t month, int32_t date, |
255 | int32_t hour, int32_t minute, int32_t second, |
256 | UErrorCode& status) |
257 | : Calendar(TimeZone::createDefault(), Locale::getDefault(), status), |
258 | fGregorianCutover(kPapalCutover), |
259 | fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582), |
260 | fIsGregorian(TRUE), fInvertGregorian(FALSE) |
261 | { |
262 | set(UCAL_ERA, AD); |
263 | set(UCAL_YEAR, year); |
264 | set(UCAL_MONTH, month); |
265 | set(UCAL_DATE, date); |
266 | set(UCAL_HOUR_OF_DAY, hour); |
267 | set(UCAL_MINUTE, minute); |
268 | set(UCAL_SECOND, second); |
269 | } |
270 | |
271 | // ------------------------------------- |
272 | |
273 | GregorianCalendar::~GregorianCalendar() |
274 | { |
275 | } |
276 | |
277 | // ------------------------------------- |
278 | |
279 | GregorianCalendar::GregorianCalendar(const GregorianCalendar &source) |
280 | : Calendar(source), |
281 | fGregorianCutover(source.fGregorianCutover), |
282 | fCutoverJulianDay(source.fCutoverJulianDay), fNormalizedGregorianCutover(source.fNormalizedGregorianCutover), fGregorianCutoverYear(source.fGregorianCutoverYear), |
283 | fIsGregorian(source.fIsGregorian), fInvertGregorian(source.fInvertGregorian) |
284 | { |
285 | } |
286 | |
287 | // ------------------------------------- |
288 | |
289 | GregorianCalendar* GregorianCalendar::clone() const |
290 | { |
291 | return new GregorianCalendar(*this); |
292 | } |
293 | |
294 | // ------------------------------------- |
295 | |
296 | GregorianCalendar & |
297 | GregorianCalendar::operator=(const GregorianCalendar &right) |
298 | { |
299 | if (this != &right) |
300 | { |
301 | Calendar::operator=(right); |
302 | fGregorianCutover = right.fGregorianCutover; |
303 | fNormalizedGregorianCutover = right.fNormalizedGregorianCutover; |
304 | fGregorianCutoverYear = right.fGregorianCutoverYear; |
305 | fCutoverJulianDay = right.fCutoverJulianDay; |
306 | } |
307 | return *this; |
308 | } |
309 | |
310 | // ------------------------------------- |
311 | |
312 | UBool GregorianCalendar::isEquivalentTo(const Calendar& other) const |
313 | { |
314 | // Calendar override. |
315 | return Calendar::isEquivalentTo(other) && |
316 | fGregorianCutover == ((GregorianCalendar*)&other)->fGregorianCutover; |
317 | } |
318 | |
319 | // ------------------------------------- |
320 | |
321 | void |
322 | GregorianCalendar::setGregorianChange(UDate date, UErrorCode& status) |
323 | { |
324 | if (U_FAILURE(status)) |
325 | return; |
326 | |
327 | // Precompute two internal variables which we use to do the actual |
328 | // cutover computations. These are the normalized cutover, which is the |
329 | // midnight at or before the cutover, and the cutover year. The |
330 | // normalized cutover is in pure date milliseconds; it contains no time |
331 | // of day or timezone component, and it used to compare against other |
332 | // pure date values. |
333 | double cutoverDay = ClockMath::floorDivide(date, (double)kOneDay); |
334 | |
335 | // Handle the rare case of numeric overflow where the user specifies a time |
336 | // outside of INT32_MIN .. INT32_MAX number of days. |
337 | |
338 | if (cutoverDay <= INT32_MIN) { |
339 | cutoverDay = INT32_MIN; |
340 | fGregorianCutover = fNormalizedGregorianCutover = cutoverDay * kOneDay; |
341 | } else if (cutoverDay >= INT32_MAX) { |
342 | cutoverDay = INT32_MAX; |
343 | fGregorianCutover = fNormalizedGregorianCutover = cutoverDay * kOneDay; |
344 | } else { |
345 | fNormalizedGregorianCutover = cutoverDay * kOneDay; |
346 | fGregorianCutover = date; |
347 | } |
348 | |
349 | // Normalize the year so BC values are represented as 0 and negative |
350 | // values. |
351 | GregorianCalendar *cal = new GregorianCalendar(getTimeZone(), status); |
352 | /* test for NULL */ |
353 | if (cal == 0) { |
354 | status = U_MEMORY_ALLOCATION_ERROR; |
355 | return; |
356 | } |
357 | if(U_FAILURE(status)) |
358 | return; |
359 | cal->setTime(date, status); |
360 | fGregorianCutoverYear = cal->get(UCAL_YEAR, status); |
361 | if (cal->get(UCAL_ERA, status) == BC) |
362 | fGregorianCutoverYear = 1 - fGregorianCutoverYear; |
363 | fCutoverJulianDay = (int32_t)cutoverDay; |
364 | delete cal; |
365 | } |
366 | |
367 | |
368 | void GregorianCalendar::handleComputeFields(int32_t julianDay, UErrorCode& status) { |
369 | int32_t eyear, month, dayOfMonth, dayOfYear, unusedRemainder; |
370 | |
371 | |
372 | if(U_FAILURE(status)) { |
373 | return; |
374 | } |
375 | |
376 | #if defined (U_DEBUG_CAL) |
377 | fprintf(stderr, "%s:%d: jd%d- (greg's %d)- [cut=%d]\n" , |
378 | __FILE__, __LINE__, julianDay, getGregorianDayOfYear(), fCutoverJulianDay); |
379 | #endif |
380 | |
381 | |
382 | if (julianDay >= fCutoverJulianDay) { |
383 | month = getGregorianMonth(); |
384 | dayOfMonth = getGregorianDayOfMonth(); |
385 | dayOfYear = getGregorianDayOfYear(); |
386 | eyear = getGregorianYear(); |
387 | } else { |
388 | // The Julian epoch day (not the same as Julian Day) |
389 | // is zero on Saturday December 30, 0 (Gregorian). |
390 | int32_t julianEpochDay = julianDay - (kJan1_1JulianDay - 2); |
391 | eyear = (int32_t) ClockMath::floorDivide((4.0*julianEpochDay) + 1464.0, (int32_t) 1461, unusedRemainder); |
392 | |
393 | // Compute the Julian calendar day number for January 1, eyear |
394 | int32_t january1 = 365*(eyear-1) + ClockMath::floorDivide(eyear-1, (int32_t)4); |
395 | dayOfYear = (julianEpochDay - january1); // 0-based |
396 | |
397 | // Julian leap years occurred historically every 4 years starting |
398 | // with 8 AD. Before 8 AD the spacing is irregular; every 3 years |
399 | // from 45 BC to 9 BC, and then none until 8 AD. However, we don't |
400 | // implement this historical detail; instead, we implement the |
401 | // computatinally cleaner proleptic calendar, which assumes |
402 | // consistent 4-year cycles throughout time. |
403 | UBool isLeap = ((eyear&0x3) == 0); // equiv. to (eyear%4 == 0) |
404 | |
405 | // Common Julian/Gregorian calculation |
406 | int32_t correction = 0; |
407 | int32_t march1 = isLeap ? 60 : 59; // zero-based DOY for March 1 |
408 | if (dayOfYear >= march1) { |
409 | correction = isLeap ? 1 : 2; |
410 | } |
411 | month = (12 * (dayOfYear + correction) + 6) / 367; // zero-based month |
412 | dayOfMonth = dayOfYear - (isLeap?kLeapNumDays[month]:kNumDays[month]) + 1; // one-based DOM |
413 | ++dayOfYear; |
414 | #if defined (U_DEBUG_CAL) |
415 | // fprintf(stderr, "%d - %d[%d] + 1\n", dayOfYear, isLeap?kLeapNumDays[month]:kNumDays[month], month ); |
416 | // fprintf(stderr, "%s:%d: greg's HCF %d -> %d/%d/%d not %d/%d/%d\n", |
417 | // __FILE__, __LINE__,julianDay, |
418 | // eyear,month,dayOfMonth, |
419 | // getGregorianYear(), getGregorianMonth(), getGregorianDayOfMonth() ); |
420 | fprintf(stderr, "%s:%d: doy %d (greg's %d)- [cut=%d]\n" , |
421 | __FILE__, __LINE__, dayOfYear, getGregorianDayOfYear(), fCutoverJulianDay); |
422 | #endif |
423 | |
424 | } |
425 | |
426 | // [j81] if we are after the cutover in its year, shift the day of the year |
427 | if((eyear == fGregorianCutoverYear) && (julianDay >= fCutoverJulianDay)) { |
428 | //from handleComputeMonthStart |
429 | int32_t gregShift = Grego::gregorianShift(eyear); |
430 | #if defined (U_DEBUG_CAL) |
431 | fprintf(stderr, "%s:%d: gregorian shift %d ::: doy%d => %d [cut=%d]\n" , |
432 | __FILE__, __LINE__,gregShift, dayOfYear, dayOfYear+gregShift, fCutoverJulianDay); |
433 | #endif |
434 | dayOfYear += gregShift; |
435 | } |
436 | |
437 | internalSet(UCAL_MONTH, month); |
438 | internalSet(UCAL_DAY_OF_MONTH, dayOfMonth); |
439 | internalSet(UCAL_DAY_OF_YEAR, dayOfYear); |
440 | internalSet(UCAL_EXTENDED_YEAR, eyear); |
441 | int32_t era = AD; |
442 | if (eyear < 1) { |
443 | era = BC; |
444 | eyear = 1 - eyear; |
445 | } |
446 | internalSet(UCAL_ERA, era); |
447 | internalSet(UCAL_YEAR, eyear); |
448 | } |
449 | |
450 | |
451 | // ------------------------------------- |
452 | |
453 | UDate |
454 | GregorianCalendar::getGregorianChange() const |
455 | { |
456 | return fGregorianCutover; |
457 | } |
458 | |
459 | // ------------------------------------- |
460 | |
461 | UBool |
462 | GregorianCalendar::isLeapYear(int32_t year) const |
463 | { |
464 | // MSVC complains bitterly if we try to use Grego::isLeapYear here |
465 | // NOTE: year&0x3 == year%4 |
466 | return (year >= fGregorianCutoverYear ? |
467 | (((year&0x3) == 0) && ((year%100 != 0) || (year%400 == 0))) : // Gregorian |
468 | ((year&0x3) == 0)); // Julian |
469 | } |
470 | |
471 | // ------------------------------------- |
472 | |
473 | int32_t GregorianCalendar::handleComputeJulianDay(UCalendarDateFields bestField) |
474 | { |
475 | fInvertGregorian = FALSE; |
476 | |
477 | int32_t jd = Calendar::handleComputeJulianDay(bestField); |
478 | |
479 | if((bestField == UCAL_WEEK_OF_YEAR) && // if we are doing WOY calculations, we are counting relative to Jan 1 *julian* |
480 | (internalGet(UCAL_EXTENDED_YEAR)==fGregorianCutoverYear) && |
481 | jd >= fCutoverJulianDay) { |
482 | fInvertGregorian = TRUE; // So that the Julian Jan 1 will be used in handleComputeMonthStart |
483 | return Calendar::handleComputeJulianDay(bestField); |
484 | } |
485 | |
486 | |
487 | // The following check handles portions of the cutover year BEFORE the |
488 | // cutover itself happens. |
489 | //if ((fIsGregorian==TRUE) != (jd >= fCutoverJulianDay)) { /* cutoverJulianDay)) { */ |
490 | if ((fIsGregorian==TRUE) != (jd >= fCutoverJulianDay)) { /* cutoverJulianDay)) { */ |
491 | #if defined (U_DEBUG_CAL) |
492 | fprintf(stderr, "%s:%d: jd [invert] %d\n" , |
493 | __FILE__, __LINE__, jd); |
494 | #endif |
495 | fInvertGregorian = TRUE; |
496 | jd = Calendar::handleComputeJulianDay(bestField); |
497 | #if defined (U_DEBUG_CAL) |
498 | fprintf(stderr, "%s:%d: fIsGregorian %s, fInvertGregorian %s - " , |
499 | __FILE__, __LINE__,fIsGregorian?"T" :"F" , fInvertGregorian?"T" :"F" ); |
500 | fprintf(stderr, " jd NOW %d\n" , |
501 | jd); |
502 | #endif |
503 | } else { |
504 | #if defined (U_DEBUG_CAL) |
505 | fprintf(stderr, "%s:%d: jd [==] %d - %sfIsGregorian %sfInvertGregorian, %d\n" , |
506 | __FILE__, __LINE__, jd, fIsGregorian?"T" :"F" , fInvertGregorian?"T" :"F" , bestField); |
507 | #endif |
508 | } |
509 | |
510 | if(fIsGregorian && (internalGet(UCAL_EXTENDED_YEAR) == fGregorianCutoverYear)) { |
511 | int32_t gregShift = Grego::gregorianShift(internalGet(UCAL_EXTENDED_YEAR)); |
512 | if (bestField == UCAL_DAY_OF_YEAR) { |
513 | #if defined (U_DEBUG_CAL) |
514 | fprintf(stderr, "%s:%d: [DOY%d] gregorian shift of JD %d += %d\n" , |
515 | __FILE__, __LINE__, fFields[bestField],jd, gregShift); |
516 | #endif |
517 | jd -= gregShift; |
518 | } else if ( bestField == UCAL_WEEK_OF_MONTH ) { |
519 | int32_t weekShift = 14; |
520 | #if defined (U_DEBUG_CAL) |
521 | fprintf(stderr, "%s:%d: [WOY/WOM] gregorian week shift of %d += %d\n" , |
522 | __FILE__, __LINE__, jd, weekShift); |
523 | #endif |
524 | jd += weekShift; // shift by weeks for week based fields. |
525 | } |
526 | } |
527 | |
528 | return jd; |
529 | } |
530 | |
531 | int32_t GregorianCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, |
532 | |
533 | UBool /* useMonth */) const |
534 | { |
535 | GregorianCalendar *nonConstThis = (GregorianCalendar*)this; // cast away const |
536 | |
537 | // If the month is out of range, adjust it into range, and |
538 | // modify the extended year value accordingly. |
539 | if (month < 0 || month > 11) { |
540 | eyear += ClockMath::floorDivide(month, 12, month); |
541 | } |
542 | |
543 | UBool isLeap = eyear%4 == 0; |
544 | int64_t y = (int64_t)eyear-1; |
545 | int64_t julianDay = 365*y + ClockMath::floorDivide(y, (int64_t)4) + (kJan1_1JulianDay - 3); |
546 | |
547 | nonConstThis->fIsGregorian = (eyear >= fGregorianCutoverYear); |
548 | #if defined (U_DEBUG_CAL) |
549 | fprintf(stderr, "%s:%d: (hcms%d/%d) fIsGregorian %s, fInvertGregorian %s\n" , |
550 | __FILE__, __LINE__, eyear,month, fIsGregorian?"T" :"F" , fInvertGregorian?"T" :"F" ); |
551 | #endif |
552 | if (fInvertGregorian) { |
553 | nonConstThis->fIsGregorian = !fIsGregorian; |
554 | } |
555 | if (fIsGregorian) { |
556 | isLeap = isLeap && ((eyear%100 != 0) || (eyear%400 == 0)); |
557 | // Add 2 because Gregorian calendar starts 2 days after |
558 | // Julian calendar |
559 | int32_t gregShift = Grego::gregorianShift(eyear); |
560 | #if defined (U_DEBUG_CAL) |
561 | fprintf(stderr, "%s:%d: (hcms%d/%d) gregorian shift of %d += %d\n" , |
562 | __FILE__, __LINE__, eyear, month, julianDay, gregShift); |
563 | #endif |
564 | julianDay += gregShift; |
565 | } |
566 | |
567 | // At this point julianDay indicates the day BEFORE the first |
568 | // day of January 1, <eyear> of either the Julian or Gregorian |
569 | // calendar. |
570 | |
571 | if (month != 0) { |
572 | julianDay += isLeap?kLeapNumDays[month]:kNumDays[month]; |
573 | } |
574 | |
575 | return static_cast<int32_t>(julianDay); |
576 | } |
577 | |
578 | int32_t GregorianCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const |
579 | { |
580 | // If the month is out of range, adjust it into range, and |
581 | // modify the extended year value accordingly. |
582 | if (month < 0 || month > 11) { |
583 | extendedYear += ClockMath::floorDivide(month, 12, month); |
584 | } |
585 | |
586 | return isLeapYear(extendedYear) ? kLeapMonthLength[month] : kMonthLength[month]; |
587 | } |
588 | |
589 | int32_t GregorianCalendar::handleGetYearLength(int32_t eyear) const { |
590 | return isLeapYear(eyear) ? 366 : 365; |
591 | } |
592 | |
593 | |
594 | int32_t |
595 | GregorianCalendar::monthLength(int32_t month) const |
596 | { |
597 | int32_t year = internalGet(UCAL_EXTENDED_YEAR); |
598 | return handleGetMonthLength(year, month); |
599 | } |
600 | |
601 | // ------------------------------------- |
602 | |
603 | int32_t |
604 | GregorianCalendar::monthLength(int32_t month, int32_t year) const |
605 | { |
606 | return isLeapYear(year) ? kLeapMonthLength[month] : kMonthLength[month]; |
607 | } |
608 | |
609 | // ------------------------------------- |
610 | |
611 | int32_t |
612 | GregorianCalendar::yearLength(int32_t year) const |
613 | { |
614 | return isLeapYear(year) ? 366 : 365; |
615 | } |
616 | |
617 | // ------------------------------------- |
618 | |
619 | int32_t |
620 | GregorianCalendar::yearLength() const |
621 | { |
622 | return isLeapYear(internalGet(UCAL_YEAR)) ? 366 : 365; |
623 | } |
624 | |
625 | // ------------------------------------- |
626 | |
627 | /** |
628 | * After adjustments such as add(MONTH), add(YEAR), we don't want the |
629 | * month to jump around. E.g., we don't want Jan 31 + 1 month to go to Mar |
630 | * 3, we want it to go to Feb 28. Adjustments which might run into this |
631 | * problem call this method to retain the proper month. |
632 | */ |
633 | void |
634 | GregorianCalendar::pinDayOfMonth() |
635 | { |
636 | int32_t monthLen = monthLength(internalGet(UCAL_MONTH)); |
637 | int32_t dom = internalGet(UCAL_DATE); |
638 | if(dom > monthLen) |
639 | set(UCAL_DATE, monthLen); |
640 | } |
641 | |
642 | // ------------------------------------- |
643 | |
644 | |
645 | UBool |
646 | GregorianCalendar::validateFields() const |
647 | { |
648 | for (int32_t field = 0; field < UCAL_FIELD_COUNT; field++) { |
649 | // Ignore DATE and DAY_OF_YEAR which are handled below |
650 | if (field != UCAL_DATE && |
651 | field != UCAL_DAY_OF_YEAR && |
652 | isSet((UCalendarDateFields)field) && |
653 | ! boundsCheck(internalGet((UCalendarDateFields)field), (UCalendarDateFields)field)) |
654 | return FALSE; |
655 | } |
656 | |
657 | // Values differ in Least-Maximum and Maximum should be handled |
658 | // specially. |
659 | if (isSet(UCAL_DATE)) { |
660 | int32_t date = internalGet(UCAL_DATE); |
661 | if (date < getMinimum(UCAL_DATE) || |
662 | date > monthLength(internalGet(UCAL_MONTH))) { |
663 | return FALSE; |
664 | } |
665 | } |
666 | |
667 | if (isSet(UCAL_DAY_OF_YEAR)) { |
668 | int32_t days = internalGet(UCAL_DAY_OF_YEAR); |
669 | if (days < 1 || days > yearLength()) { |
670 | return FALSE; |
671 | } |
672 | } |
673 | |
674 | // Handle DAY_OF_WEEK_IN_MONTH, which must not have the value zero. |
675 | // We've checked against minimum and maximum above already. |
676 | if (isSet(UCAL_DAY_OF_WEEK_IN_MONTH) && |
677 | 0 == internalGet(UCAL_DAY_OF_WEEK_IN_MONTH)) { |
678 | return FALSE; |
679 | } |
680 | |
681 | return TRUE; |
682 | } |
683 | |
684 | // ------------------------------------- |
685 | |
686 | UBool |
687 | GregorianCalendar::boundsCheck(int32_t value, UCalendarDateFields field) const |
688 | { |
689 | return value >= getMinimum(field) && value <= getMaximum(field); |
690 | } |
691 | |
692 | // ------------------------------------- |
693 | |
694 | UDate |
695 | GregorianCalendar::getEpochDay(UErrorCode& status) |
696 | { |
697 | complete(status); |
698 | // Divide by 1000 (convert to seconds) in order to prevent overflow when |
699 | // dealing with UDate(Long.MIN_VALUE) and UDate(Long.MAX_VALUE). |
700 | double wallSec = internalGetTime()/1000 + (internalGet(UCAL_ZONE_OFFSET) + internalGet(UCAL_DST_OFFSET))/1000; |
701 | |
702 | return ClockMath::floorDivide(wallSec, kOneDay/1000.0); |
703 | } |
704 | |
705 | // ------------------------------------- |
706 | |
707 | |
708 | // ------------------------------------- |
709 | |
710 | /** |
711 | * Compute the julian day number of the day BEFORE the first day of |
712 | * January 1, year 1 of the given calendar. If julianDay == 0, it |
713 | * specifies (Jan. 1, 1) - 1, in whatever calendar we are using (Julian |
714 | * or Gregorian). |
715 | */ |
716 | double GregorianCalendar::computeJulianDayOfYear(UBool isGregorian, |
717 | int32_t year, UBool& isLeap) |
718 | { |
719 | isLeap = year%4 == 0; |
720 | int32_t y = year - 1; |
721 | double julianDay = 365.0*y + ClockMath::floorDivide(y, 4) + (kJan1_1JulianDay - 3); |
722 | |
723 | if (isGregorian) { |
724 | isLeap = isLeap && ((year%100 != 0) || (year%400 == 0)); |
725 | // Add 2 because Gregorian calendar starts 2 days after Julian calendar |
726 | julianDay += Grego::gregorianShift(year); |
727 | } |
728 | |
729 | return julianDay; |
730 | } |
731 | |
732 | // /** |
733 | // * Compute the day of week, relative to the first day of week, from |
734 | // * 0..6, of the current DOW_LOCAL or DAY_OF_WEEK fields. This is |
735 | // * equivalent to get(DOW_LOCAL) - 1. |
736 | // */ |
737 | // int32_t GregorianCalendar::computeRelativeDOW() const { |
738 | // int32_t relDow = 0; |
739 | // if (fStamp[UCAL_DOW_LOCAL] > fStamp[UCAL_DAY_OF_WEEK]) { |
740 | // relDow = internalGet(UCAL_DOW_LOCAL) - 1; // 1-based |
741 | // } else if (fStamp[UCAL_DAY_OF_WEEK] != kUnset) { |
742 | // relDow = internalGet(UCAL_DAY_OF_WEEK) - getFirstDayOfWeek(); |
743 | // if (relDow < 0) relDow += 7; |
744 | // } |
745 | // return relDow; |
746 | // } |
747 | |
748 | // /** |
749 | // * Compute the day of week, relative to the first day of week, |
750 | // * from 0..6 of the given julian day. |
751 | // */ |
752 | // int32_t GregorianCalendar::computeRelativeDOW(double julianDay) const { |
753 | // int32_t relDow = julianDayToDayOfWeek(julianDay) - getFirstDayOfWeek(); |
754 | // if (relDow < 0) { |
755 | // relDow += 7; |
756 | // } |
757 | // return relDow; |
758 | // } |
759 | |
760 | // /** |
761 | // * Compute the DOY using the WEEK_OF_YEAR field and the julian day |
762 | // * of the day BEFORE January 1 of a year (a return value from |
763 | // * computeJulianDayOfYear). |
764 | // */ |
765 | // int32_t GregorianCalendar::computeDOYfromWOY(double julianDayOfYear) const { |
766 | // // Compute DOY from day of week plus week of year |
767 | |
768 | // // Find the day of the week for the first of this year. This |
769 | // // is zero-based, with 0 being the locale-specific first day of |
770 | // // the week. Add 1 to get first day of year. |
771 | // int32_t fdy = computeRelativeDOW(julianDayOfYear + 1); |
772 | |
773 | // return |
774 | // // Compute doy of first (relative) DOW of WOY 1 |
775 | // (((7 - fdy) < getMinimalDaysInFirstWeek()) |
776 | // ? (8 - fdy) : (1 - fdy)) |
777 | |
778 | // // Adjust for the week number. |
779 | // + (7 * (internalGet(UCAL_WEEK_OF_YEAR) - 1)) |
780 | |
781 | // // Adjust for the DOW |
782 | // + computeRelativeDOW(); |
783 | // } |
784 | |
785 | // ------------------------------------- |
786 | |
787 | double |
788 | GregorianCalendar::millisToJulianDay(UDate millis) |
789 | { |
790 | return (double)kEpochStartAsJulianDay + ClockMath::floorDivide(millis, (double)kOneDay); |
791 | } |
792 | |
793 | // ------------------------------------- |
794 | |
795 | UDate |
796 | GregorianCalendar::julianDayToMillis(double julian) |
797 | { |
798 | return (UDate) ((julian - kEpochStartAsJulianDay) * (double) kOneDay); |
799 | } |
800 | |
801 | // ------------------------------------- |
802 | |
803 | int32_t |
804 | GregorianCalendar::aggregateStamp(int32_t stamp_a, int32_t stamp_b) |
805 | { |
806 | return (((stamp_a != kUnset && stamp_b != kUnset) |
807 | ? uprv_max(stamp_a, stamp_b) |
808 | : (int32_t)kUnset)); |
809 | } |
810 | |
811 | // ------------------------------------- |
812 | |
813 | /** |
814 | * Roll a field by a signed amount. |
815 | * Note: This will be made public later. [LIU] |
816 | */ |
817 | |
818 | void |
819 | GregorianCalendar::roll(EDateFields field, int32_t amount, UErrorCode& status) { |
820 | roll((UCalendarDateFields) field, amount, status); |
821 | } |
822 | |
823 | void |
824 | GregorianCalendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& status) |
825 | { |
826 | if((amount == 0) || U_FAILURE(status)) { |
827 | return; |
828 | } |
829 | |
830 | // J81 processing. (gregorian cutover) |
831 | UBool inCutoverMonth = FALSE; |
832 | int32_t cMonthLen=0; // 'c' for cutover; in days |
833 | int32_t cDayOfMonth=0; // no discontinuity: [0, cMonthLen) |
834 | double cMonthStart=0.0; // in ms |
835 | |
836 | // Common code - see if we're in the cutover month of the cutover year |
837 | if(get(UCAL_EXTENDED_YEAR, status) == fGregorianCutoverYear) { |
838 | switch (field) { |
839 | case UCAL_DAY_OF_MONTH: |
840 | case UCAL_WEEK_OF_MONTH: |
841 | { |
842 | int32_t max = monthLength(internalGet(UCAL_MONTH)); |
843 | UDate t = internalGetTime(); |
844 | // We subtract 1 from the DAY_OF_MONTH to make it zero-based, and an |
845 | // additional 10 if we are after the cutover. Thus the monthStart |
846 | // value will be correct iff we actually are in the cutover month. |
847 | cDayOfMonth = internalGet(UCAL_DAY_OF_MONTH) - ((t >= fGregorianCutover) ? 10 : 0); |
848 | cMonthStart = t - ((cDayOfMonth - 1) * kOneDay); |
849 | // A month containing the cutover is 10 days shorter. |
850 | if ((cMonthStart < fGregorianCutover) && |
851 | (cMonthStart + (cMonthLen=(max-10))*kOneDay >= fGregorianCutover)) { |
852 | inCutoverMonth = TRUE; |
853 | } |
854 | } |
855 | break; |
856 | default: |
857 | ; |
858 | } |
859 | } |
860 | |
861 | switch (field) { |
862 | case UCAL_WEEK_OF_YEAR: { |
863 | // Unlike WEEK_OF_MONTH, WEEK_OF_YEAR never shifts the day of the |
864 | // week. Also, rolling the week of the year can have seemingly |
865 | // strange effects simply because the year of the week of year |
866 | // may be different from the calendar year. For example, the |
867 | // date Dec 28, 1997 is the first day of week 1 of 1998 (if |
868 | // weeks start on Sunday and the minimal days in first week is |
869 | // <= 3). |
870 | int32_t woy = get(UCAL_WEEK_OF_YEAR, status); |
871 | // Get the ISO year, which matches the week of year. This |
872 | // may be one year before or after the calendar year. |
873 | int32_t isoYear = get(UCAL_YEAR_WOY, status); |
874 | int32_t isoDoy = internalGet(UCAL_DAY_OF_YEAR); |
875 | if (internalGet(UCAL_MONTH) == UCAL_JANUARY) { |
876 | if (woy >= 52) { |
877 | isoDoy += handleGetYearLength(isoYear); |
878 | } |
879 | } else { |
880 | if (woy == 1) { |
881 | isoDoy -= handleGetYearLength(isoYear - 1); |
882 | } |
883 | } |
884 | woy += amount; |
885 | // Do fast checks to avoid unnecessary computation: |
886 | if (woy < 1 || woy > 52) { |
887 | // Determine the last week of the ISO year. |
888 | // We do this using the standard formula we use |
889 | // everywhere in this file. If we can see that the |
890 | // days at the end of the year are going to fall into |
891 | // week 1 of the next year, we drop the last week by |
892 | // subtracting 7 from the last day of the year. |
893 | int32_t lastDoy = handleGetYearLength(isoYear); |
894 | int32_t lastRelDow = (lastDoy - isoDoy + internalGet(UCAL_DAY_OF_WEEK) - |
895 | getFirstDayOfWeek()) % 7; |
896 | if (lastRelDow < 0) lastRelDow += 7; |
897 | if ((6 - lastRelDow) >= getMinimalDaysInFirstWeek()) lastDoy -= 7; |
898 | int32_t lastWoy = weekNumber(lastDoy, lastRelDow + 1); |
899 | woy = ((woy + lastWoy - 1) % lastWoy) + 1; |
900 | } |
901 | set(UCAL_WEEK_OF_YEAR, woy); |
902 | set(UCAL_YEAR_WOY,isoYear); |
903 | return; |
904 | } |
905 | |
906 | case UCAL_DAY_OF_MONTH: |
907 | if( !inCutoverMonth ) { |
908 | Calendar::roll(field, amount, status); |
909 | return; |
910 | } else { |
911 | // [j81] 1582 special case for DOM |
912 | // The default computation works except when the current month |
913 | // contains the Gregorian cutover. We handle this special case |
914 | // here. [j81 - aliu] |
915 | double monthLen = cMonthLen * kOneDay; |
916 | double msIntoMonth = uprv_fmod(internalGetTime() - cMonthStart + |
917 | amount * kOneDay, monthLen); |
918 | if (msIntoMonth < 0) { |
919 | msIntoMonth += monthLen; |
920 | } |
921 | #if defined (U_DEBUG_CAL) |
922 | fprintf(stderr, "%s:%d: roll DOM %d -> %.0lf ms \n" , |
923 | __FILE__, __LINE__,amount, cMonthLen, cMonthStart+msIntoMonth); |
924 | #endif |
925 | setTimeInMillis(cMonthStart + msIntoMonth, status); |
926 | return; |
927 | } |
928 | |
929 | case UCAL_WEEK_OF_MONTH: |
930 | if( !inCutoverMonth ) { |
931 | Calendar::roll(field, amount, status); |
932 | return; |
933 | } else { |
934 | #if defined (U_DEBUG_CAL) |
935 | fprintf(stderr, "%s:%d: roll WOM %d ??????????????????? \n" , |
936 | __FILE__, __LINE__,amount); |
937 | #endif |
938 | // NOTE: following copied from the old |
939 | // GregorianCalendar::roll( WEEK_OF_MONTH ) code |
940 | |
941 | // This is tricky, because during the roll we may have to shift |
942 | // to a different day of the week. For example: |
943 | |
944 | // s m t w r f s |
945 | // 1 2 3 4 5 |
946 | // 6 7 8 9 10 11 12 |
947 | |
948 | // When rolling from the 6th or 7th back one week, we go to the |
949 | // 1st (assuming that the first partial week counts). The same |
950 | // thing happens at the end of the month. |
951 | |
952 | // The other tricky thing is that we have to figure out whether |
953 | // the first partial week actually counts or not, based on the |
954 | // minimal first days in the week. And we have to use the |
955 | // correct first day of the week to delineate the week |
956 | // boundaries. |
957 | |
958 | // Here's our algorithm. First, we find the real boundaries of |
959 | // the month. Then we discard the first partial week if it |
960 | // doesn't count in this locale. Then we fill in the ends with |
961 | // phantom days, so that the first partial week and the last |
962 | // partial week are full weeks. We then have a nice square |
963 | // block of weeks. We do the usual rolling within this block, |
964 | // as is done elsewhere in this method. If we wind up on one of |
965 | // the phantom days that we added, we recognize this and pin to |
966 | // the first or the last day of the month. Easy, eh? |
967 | |
968 | // Another wrinkle: To fix jitterbug 81, we have to make all this |
969 | // work in the oddball month containing the Gregorian cutover. |
970 | // This month is 10 days shorter than usual, and also contains |
971 | // a discontinuity in the days; e.g., the default cutover month |
972 | // is Oct 1582, and goes from day of month 4 to day of month 15. |
973 | |
974 | // Normalize the DAY_OF_WEEK so that 0 is the first day of the week |
975 | // in this locale. We have dow in 0..6. |
976 | int32_t dow = internalGet(UCAL_DAY_OF_WEEK) - getFirstDayOfWeek(); |
977 | if (dow < 0) |
978 | dow += 7; |
979 | |
980 | // Find the day of month, compensating for cutover discontinuity. |
981 | int32_t dom = cDayOfMonth; |
982 | |
983 | // Find the day of the week (normalized for locale) for the first |
984 | // of the month. |
985 | int32_t fdm = (dow - dom + 1) % 7; |
986 | if (fdm < 0) |
987 | fdm += 7; |
988 | |
989 | // Get the first day of the first full week of the month, |
990 | // including phantom days, if any. Figure out if the first week |
991 | // counts or not; if it counts, then fill in phantom days. If |
992 | // not, advance to the first real full week (skip the partial week). |
993 | int32_t start; |
994 | if ((7 - fdm) < getMinimalDaysInFirstWeek()) |
995 | start = 8 - fdm; // Skip the first partial week |
996 | else |
997 | start = 1 - fdm; // This may be zero or negative |
998 | |
999 | // Get the day of the week (normalized for locale) for the last |
1000 | // day of the month. |
1001 | int32_t monthLen = cMonthLen; |
1002 | int32_t ldm = (monthLen - dom + dow) % 7; |
1003 | // We know monthLen >= DAY_OF_MONTH so we skip the += 7 step here. |
1004 | |
1005 | // Get the limit day for the blocked-off rectangular month; that |
1006 | // is, the day which is one past the last day of the month, |
1007 | // after the month has already been filled in with phantom days |
1008 | // to fill out the last week. This day has a normalized DOW of 0. |
1009 | int32_t limit = monthLen + 7 - ldm; |
1010 | |
1011 | // Now roll between start and (limit - 1). |
1012 | int32_t gap = limit - start; |
1013 | int32_t newDom = (dom + amount*7 - start) % gap; |
1014 | if (newDom < 0) |
1015 | newDom += gap; |
1016 | newDom += start; |
1017 | |
1018 | // Finally, pin to the real start and end of the month. |
1019 | if (newDom < 1) |
1020 | newDom = 1; |
1021 | if (newDom > monthLen) |
1022 | newDom = monthLen; |
1023 | |
1024 | // Set the DAY_OF_MONTH. We rely on the fact that this field |
1025 | // takes precedence over everything else (since all other fields |
1026 | // are also set at this point). If this fact changes (if the |
1027 | // disambiguation algorithm changes) then we will have to unset |
1028 | // the appropriate fields here so that DAY_OF_MONTH is attended |
1029 | // to. |
1030 | |
1031 | // If we are in the cutover month, manipulate ms directly. Don't do |
1032 | // this in general because it doesn't work across DST boundaries |
1033 | // (details, details). This takes care of the discontinuity. |
1034 | setTimeInMillis(cMonthStart + (newDom-1)*kOneDay, status); |
1035 | return; |
1036 | } |
1037 | |
1038 | default: |
1039 | Calendar::roll(field, amount, status); |
1040 | return; |
1041 | } |
1042 | } |
1043 | |
1044 | // ------------------------------------- |
1045 | |
1046 | |
1047 | /** |
1048 | * Return the minimum value that this field could have, given the current date. |
1049 | * For the Gregorian calendar, this is the same as getMinimum() and getGreatestMinimum(). |
1050 | * @param field the time field. |
1051 | * @return the minimum value that this field could have, given the current date. |
1052 | * @deprecated ICU 2.6. Use getActualMinimum(UCalendarDateFields field) instead. |
1053 | */ |
1054 | int32_t GregorianCalendar::getActualMinimum(EDateFields field) const |
1055 | { |
1056 | return getMinimum((UCalendarDateFields)field); |
1057 | } |
1058 | |
1059 | int32_t GregorianCalendar::getActualMinimum(EDateFields field, UErrorCode& /* status */) const |
1060 | { |
1061 | return getMinimum((UCalendarDateFields)field); |
1062 | } |
1063 | |
1064 | /** |
1065 | * Return the minimum value that this field could have, given the current date. |
1066 | * For the Gregorian calendar, this is the same as getMinimum() and getGreatestMinimum(). |
1067 | * @param field the time field. |
1068 | * @return the minimum value that this field could have, given the current date. |
1069 | * @draft ICU 2.6. |
1070 | */ |
1071 | int32_t GregorianCalendar::getActualMinimum(UCalendarDateFields field, UErrorCode& /* status */) const |
1072 | { |
1073 | return getMinimum(field); |
1074 | } |
1075 | |
1076 | |
1077 | // ------------------------------------ |
1078 | |
1079 | /** |
1080 | * Old year limits were least max 292269054, max 292278994. |
1081 | */ |
1082 | |
1083 | /** |
1084 | * @stable ICU 2.0 |
1085 | */ |
1086 | int32_t GregorianCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const { |
1087 | return kGregorianCalendarLimits[field][limitType]; |
1088 | } |
1089 | |
1090 | /** |
1091 | * Return the maximum value that this field could have, given the current date. |
1092 | * For example, with the date "Feb 3, 1997" and the DAY_OF_MONTH field, the actual |
1093 | * maximum would be 28; for "Feb 3, 1996" it s 29. Similarly for a Hebrew calendar, |
1094 | * for some years the actual maximum for MONTH is 12, and for others 13. |
1095 | * @stable ICU 2.0 |
1096 | */ |
1097 | int32_t GregorianCalendar::getActualMaximum(UCalendarDateFields field, UErrorCode& status) const |
1098 | { |
1099 | /* It is a known limitation that the code here (and in getActualMinimum) |
1100 | * won't behave properly at the extreme limits of GregorianCalendar's |
1101 | * representable range (except for the code that handles the YEAR |
1102 | * field). That's because the ends of the representable range are at |
1103 | * odd spots in the year. For calendars with the default Gregorian |
1104 | * cutover, these limits are Sun Dec 02 16:47:04 GMT 292269055 BC to Sun |
1105 | * Aug 17 07:12:55 GMT 292278994 AD, somewhat different for non-GMT |
1106 | * zones. As a result, if the calendar is set to Aug 1 292278994 AD, |
1107 | * the actual maximum of DAY_OF_MONTH is 17, not 30. If the date is Mar |
1108 | * 31 in that year, the actual maximum month might be Jul, whereas is |
1109 | * the date is Mar 15, the actual maximum might be Aug -- depending on |
1110 | * the precise semantics that are desired. Similar considerations |
1111 | * affect all fields. Nonetheless, this effect is sufficiently arcane |
1112 | * that we permit it, rather than complicating the code to handle such |
1113 | * intricacies. - liu 8/20/98 |
1114 | |
1115 | * UPDATE: No longer true, since we have pulled in the limit values on |
1116 | * the year. - Liu 11/6/00 */ |
1117 | |
1118 | switch (field) { |
1119 | |
1120 | case UCAL_YEAR: |
1121 | /* The year computation is no different, in principle, from the |
1122 | * others, however, the range of possible maxima is large. In |
1123 | * addition, the way we know we've exceeded the range is different. |
1124 | * For these reasons, we use the special case code below to handle |
1125 | * this field. |
1126 | * |
1127 | * The actual maxima for YEAR depend on the type of calendar: |
1128 | * |
1129 | * Gregorian = May 17, 292275056 BC - Aug 17, 292278994 AD |
1130 | * Julian = Dec 2, 292269055 BC - Jan 3, 292272993 AD |
1131 | * Hybrid = Dec 2, 292269055 BC - Aug 17, 292278994 AD |
1132 | * |
1133 | * We know we've exceeded the maximum when either the month, date, |
1134 | * time, or era changes in response to setting the year. We don't |
1135 | * check for month, date, and time here because the year and era are |
1136 | * sufficient to detect an invalid year setting. NOTE: If code is |
1137 | * added to check the month and date in the future for some reason, |
1138 | * Feb 29 must be allowed to shift to Mar 1 when setting the year. |
1139 | */ |
1140 | { |
1141 | if(U_FAILURE(status)) return 0; |
1142 | Calendar *cal = clone(); |
1143 | if(!cal) { |
1144 | status = U_MEMORY_ALLOCATION_ERROR; |
1145 | return 0; |
1146 | } |
1147 | |
1148 | cal->setLenient(TRUE); |
1149 | |
1150 | int32_t era = cal->get(UCAL_ERA, status); |
1151 | UDate d = cal->getTime(status); |
1152 | |
1153 | /* Perform a binary search, with the invariant that lowGood is a |
1154 | * valid year, and highBad is an out of range year. |
1155 | */ |
1156 | int32_t lowGood = kGregorianCalendarLimits[UCAL_YEAR][1]; |
1157 | int32_t highBad = kGregorianCalendarLimits[UCAL_YEAR][2]+1; |
1158 | while ((lowGood + 1) < highBad) { |
1159 | int32_t y = (lowGood + highBad) / 2; |
1160 | cal->set(UCAL_YEAR, y); |
1161 | if (cal->get(UCAL_YEAR, status) == y && cal->get(UCAL_ERA, status) == era) { |
1162 | lowGood = y; |
1163 | } else { |
1164 | highBad = y; |
1165 | cal->setTime(d, status); // Restore original fields |
1166 | } |
1167 | } |
1168 | |
1169 | delete cal; |
1170 | return lowGood; |
1171 | } |
1172 | |
1173 | default: |
1174 | return Calendar::getActualMaximum(field,status); |
1175 | } |
1176 | } |
1177 | |
1178 | |
1179 | int32_t GregorianCalendar::handleGetExtendedYear() { |
1180 | // the year to return |
1181 | int32_t year = kEpochYear; |
1182 | |
1183 | // year field to use |
1184 | int32_t yearField = UCAL_EXTENDED_YEAR; |
1185 | |
1186 | // There are three separate fields which could be used to |
1187 | // derive the proper year. Use the one most recently set. |
1188 | if (fStamp[yearField] < fStamp[UCAL_YEAR]) |
1189 | yearField = UCAL_YEAR; |
1190 | if (fStamp[yearField] < fStamp[UCAL_YEAR_WOY]) |
1191 | yearField = UCAL_YEAR_WOY; |
1192 | |
1193 | // based on the "best" year field, get the year |
1194 | switch(yearField) { |
1195 | case UCAL_EXTENDED_YEAR: |
1196 | year = internalGet(UCAL_EXTENDED_YEAR, kEpochYear); |
1197 | break; |
1198 | |
1199 | case UCAL_YEAR: |
1200 | { |
1201 | // The year defaults to the epoch start, the era to AD |
1202 | int32_t era = internalGet(UCAL_ERA, AD); |
1203 | if (era == BC) { |
1204 | year = 1 - internalGet(UCAL_YEAR, 1); // Convert to extended year |
1205 | } else { |
1206 | year = internalGet(UCAL_YEAR, kEpochYear); |
1207 | } |
1208 | } |
1209 | break; |
1210 | |
1211 | case UCAL_YEAR_WOY: |
1212 | year = handleGetExtendedYearFromWeekFields(internalGet(UCAL_YEAR_WOY), internalGet(UCAL_WEEK_OF_YEAR)); |
1213 | #if defined (U_DEBUG_CAL) |
1214 | // if(internalGet(UCAL_YEAR_WOY) != year) { |
1215 | fprintf(stderr, "%s:%d: hGEYFWF[%d,%d] -> %d\n" , |
1216 | __FILE__, __LINE__,internalGet(UCAL_YEAR_WOY),internalGet(UCAL_WEEK_OF_YEAR),year); |
1217 | //} |
1218 | #endif |
1219 | break; |
1220 | |
1221 | default: |
1222 | year = kEpochYear; |
1223 | } |
1224 | return year; |
1225 | } |
1226 | |
1227 | int32_t GregorianCalendar::handleGetExtendedYearFromWeekFields(int32_t yearWoy, int32_t woy) |
1228 | { |
1229 | // convert year to extended form |
1230 | int32_t era = internalGet(UCAL_ERA, AD); |
1231 | if(era == BC) { |
1232 | yearWoy = 1 - yearWoy; |
1233 | } |
1234 | return Calendar::handleGetExtendedYearFromWeekFields(yearWoy, woy); |
1235 | } |
1236 | |
1237 | |
1238 | // ------------------------------------- |
1239 | |
1240 | UBool |
1241 | GregorianCalendar::inDaylightTime(UErrorCode& status) const |
1242 | { |
1243 | if (U_FAILURE(status) || !getTimeZone().useDaylightTime()) |
1244 | return FALSE; |
1245 | |
1246 | // Force an update of the state of the Calendar. |
1247 | ((GregorianCalendar*)this)->complete(status); // cast away const |
1248 | |
1249 | return (UBool)(U_SUCCESS(status) ? (internalGet(UCAL_DST_OFFSET) != 0) : FALSE); |
1250 | } |
1251 | |
1252 | // ------------------------------------- |
1253 | |
1254 | /** |
1255 | * Return the ERA. We need a special method for this because the |
1256 | * default ERA is AD, but a zero (unset) ERA is BC. |
1257 | */ |
1258 | int32_t |
1259 | GregorianCalendar::internalGetEra() const { |
1260 | return isSet(UCAL_ERA) ? internalGet(UCAL_ERA) : (int32_t)AD; |
1261 | } |
1262 | |
1263 | const char * |
1264 | GregorianCalendar::getType() const { |
1265 | //static const char kGregorianType = "gregorian"; |
1266 | |
1267 | return "gregorian" ; |
1268 | } |
1269 | |
1270 | /** |
1271 | * The system maintains a static default century start date and Year. They are |
1272 | * initialized the first time they are used. Once the system default century date |
1273 | * and year are set, they do not change. |
1274 | */ |
1275 | static UDate gSystemDefaultCenturyStart = DBL_MIN; |
1276 | static int32_t gSystemDefaultCenturyStartYear = -1; |
1277 | static icu::UInitOnce gSystemDefaultCenturyInit = U_INITONCE_INITIALIZER; |
1278 | |
1279 | |
1280 | UBool GregorianCalendar::haveDefaultCentury() const |
1281 | { |
1282 | return TRUE; |
1283 | } |
1284 | |
1285 | static void U_CALLCONV |
1286 | initializeSystemDefaultCentury() |
1287 | { |
1288 | // initialize systemDefaultCentury and systemDefaultCenturyYear based |
1289 | // on the current time. They'll be set to 80 years before |
1290 | // the current time. |
1291 | UErrorCode status = U_ZERO_ERROR; |
1292 | GregorianCalendar calendar(status); |
1293 | if (U_SUCCESS(status)) { |
1294 | calendar.setTime(Calendar::getNow(), status); |
1295 | calendar.add(UCAL_YEAR, -80, status); |
1296 | |
1297 | gSystemDefaultCenturyStart = calendar.getTime(status); |
1298 | gSystemDefaultCenturyStartYear = calendar.get(UCAL_YEAR, status); |
1299 | } |
1300 | // We have no recourse upon failure unless we want to propagate the failure |
1301 | // out. |
1302 | } |
1303 | |
1304 | UDate GregorianCalendar::defaultCenturyStart() const { |
1305 | // lazy-evaluate systemDefaultCenturyStart |
1306 | umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); |
1307 | return gSystemDefaultCenturyStart; |
1308 | } |
1309 | |
1310 | int32_t GregorianCalendar::defaultCenturyStartYear() const { |
1311 | // lazy-evaluate systemDefaultCenturyStartYear |
1312 | umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); |
1313 | return gSystemDefaultCenturyStartYear; |
1314 | } |
1315 | |
1316 | U_NAMESPACE_END |
1317 | |
1318 | #endif /* #if !UCONFIG_NO_FORMATTING */ |
1319 | |
1320 | //eof |
1321 | |