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