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