]>
Commit | Line | Data |
---|---|---|
46f4442e A |
1 | /* |
2 | ****************************************************************************** | |
3 | * Copyright (C) 2003-2008, International Business Machines Corporation | |
4 | * and others. All Rights Reserved. | |
5 | ****************************************************************************** | |
6 | * | |
7 | * File PERSNCAL.CPP | |
8 | * | |
9 | * Modification History: | |
10 | * | |
11 | * Date Name Description | |
12 | * 9/23/2003 mehran posted to icu-design | |
13 | ***************************************************************************** | |
14 | */ | |
15 | ||
16 | #include "persncal.h" | |
17 | ||
18 | #if !UCONFIG_NO_FORMATTING | |
19 | ||
20 | #include "umutex.h" | |
21 | #include <float.h> | |
22 | ||
23 | static const int8_t monthDays[] = { 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29 }; | |
24 | ||
25 | static int32_t | |
26 | jalali_to_julian(int year, int month, int day) | |
27 | { | |
28 | int32_t daysNo=0; | |
29 | int i; | |
30 | ||
31 | year = year -475+2820; | |
32 | month -= 1; | |
33 | ||
34 | daysNo=(year/2820)*1029983; | |
35 | year=year % 2820; | |
36 | ||
37 | daysNo+=(year/128)* 46751; | |
38 | if((year/128)>21) | |
39 | { | |
40 | daysNo-=46751; | |
41 | year=(year%128)+128; | |
42 | } | |
43 | else | |
44 | year=year%128; | |
45 | ||
46 | if(year>=29) | |
47 | { | |
48 | year-=29; | |
49 | daysNo+=10592; | |
50 | } | |
51 | ||
52 | if(year>=66) | |
53 | { | |
54 | year-=66; | |
55 | daysNo+=24106; | |
56 | } | |
57 | else if( year>=33) | |
58 | { | |
59 | daysNo+=(year/33)* 12053; | |
60 | year=year%33; | |
61 | } | |
62 | ||
63 | if (year >= 5) | |
64 | { | |
65 | daysNo += 1826; | |
66 | year -=5; | |
67 | } | |
68 | else if (year == 4) | |
69 | { | |
70 | daysNo += 1460; | |
71 | year -=4; | |
72 | } | |
73 | ||
74 | daysNo += 1461 * (year/4); | |
75 | year %= 4; | |
76 | daysNo += 365 * year; | |
77 | ||
78 | for (i = 0; i < month; i++) { | |
79 | daysNo += monthDays[i]; | |
80 | } | |
81 | ||
82 | daysNo += day; | |
83 | ||
84 | return daysNo-856493; | |
85 | } | |
86 | ||
87 | static void julian_to_jalali (int32_t daysNo, int *h_y, int *h_m, int *h_d) | |
88 | { | |
89 | int year=0, month=0, day=0,scalarDays=0; | |
90 | int i; | |
91 | ||
92 | daysNo+=856493; | |
93 | scalarDays=daysNo; | |
94 | year=(daysNo/1029983)*2820; | |
95 | daysNo=daysNo%1029983; | |
96 | ||
97 | if((daysNo/46751)<=21) | |
98 | { | |
99 | year+=(daysNo/46751)* 128; | |
100 | daysNo=daysNo%46751; | |
101 | } | |
102 | else | |
103 | { | |
104 | year+=(daysNo/46751)* 128; | |
105 | daysNo=daysNo%46751; | |
106 | year-=128; | |
107 | daysNo+=46751; | |
108 | } | |
109 | ||
110 | if (daysNo >= 10592) | |
111 | { | |
112 | year+= 29; | |
113 | daysNo -= 10592; | |
114 | } | |
115 | ||
116 | if(daysNo>=24106) | |
117 | { | |
118 | daysNo-=24106; | |
119 | year+=66; | |
120 | } | |
121 | ||
122 | if(daysNo>=12053) | |
123 | { | |
124 | daysNo-=12053; | |
125 | year+=33; | |
126 | } | |
127 | ||
128 | ||
129 | if (daysNo >= 1826) | |
130 | { | |
131 | year+= 5; | |
132 | daysNo -= 1826; | |
133 | } | |
134 | else if (daysNo > 1095) | |
135 | { | |
136 | year+= 3; | |
137 | daysNo -= 1095; | |
138 | ||
139 | } | |
140 | ||
141 | year +=(4 * (daysNo/1461)); | |
142 | daysNo %= 1461; | |
143 | ||
144 | if (daysNo == 0) | |
145 | { | |
146 | year -= 1; | |
147 | daysNo = 366; | |
148 | } | |
149 | else | |
150 | { | |
151 | year += daysNo/365; | |
152 | daysNo = daysNo % 365; | |
153 | if (daysNo == 0) | |
154 | { | |
155 | year -= 1; | |
156 | daysNo = 365; | |
157 | } | |
158 | ||
159 | } | |
160 | ||
161 | for (i = 0; i < 11 && daysNo > monthDays[i]; ++i) { | |
162 | daysNo -= monthDays[i]; | |
163 | } | |
164 | ||
165 | month = i + 1; | |
166 | ||
167 | day = daysNo; | |
168 | ||
169 | *h_d = day; | |
170 | *h_m = month; | |
171 | *h_y = year-2345; | |
172 | } | |
173 | ||
174 | U_NAMESPACE_BEGIN | |
175 | ||
176 | // Implementation of the PersianCalendar class | |
177 | ||
178 | //------------------------------------------------------------------------- | |
179 | // Constructors... | |
180 | //------------------------------------------------------------------------- | |
181 | ||
182 | const char *PersianCalendar::getType() const { | |
183 | return "persian"; | |
184 | } | |
185 | ||
186 | Calendar* PersianCalendar::clone() const { | |
187 | return new PersianCalendar(*this); | |
188 | } | |
189 | ||
190 | PersianCalendar::PersianCalendar(const Locale& aLocale, UErrorCode& success) | |
191 | : Calendar(TimeZone::createDefault(), aLocale, success) | |
192 | { | |
193 | setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly. | |
194 | } | |
195 | ||
196 | PersianCalendar::PersianCalendar(const PersianCalendar& other) : Calendar(other) { | |
197 | } | |
198 | ||
199 | PersianCalendar::~PersianCalendar() | |
200 | { | |
201 | } | |
202 | ||
203 | //------------------------------------------------------------------------- | |
204 | // Minimum / Maximum access functions | |
205 | //------------------------------------------------------------------------- | |
206 | ||
207 | static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = { | |
208 | // Minimum Greatest Least Maximum | |
209 | // Minimum Maximum | |
210 | { 0, 0, 0, 0}, // ERA | |
211 | { -5000000, -5000000, 5000000, 5000000}, // YEAR | |
212 | { 0, 0, 11, 11}, // MONTH | |
213 | { 1, 1, 52, 53}, // WEEK_OF_YEAR | |
214 | {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH | |
215 | { 1, 1, 29, 31}, // DAY_OF_MONTH | |
216 | { 1, 1, 365, 366}, // DAY_OF_YEAR | |
217 | {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK | |
218 | { 1, 1, 5, 5}, // DAY_OF_WEEK_IN_MONTH | |
219 | {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM | |
220 | {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR | |
221 | {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY | |
222 | {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE | |
223 | {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND | |
224 | {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND | |
225 | {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET | |
226 | {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET | |
227 | { -5000000, -5000000, 5000000, 5000000}, // YEAR_WOY | |
228 | {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL | |
229 | { -5000000, -5000000, 5000000, 5000000}, // EXTENDED_YEAR | |
230 | {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY | |
231 | {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY | |
232 | {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH | |
233 | }; | |
234 | static const int32_t MONTH_COUNT[12][4] = { | |
235 | //len len2 st st2 | |
236 | { 31, 31, 0, 0 }, // Farvardin | |
237 | { 31, 31, 31, 31 }, // Ordibehesht | |
238 | { 31, 31, 62, 62 }, // Khordad | |
239 | { 31, 31, 93, 93 }, // Tir | |
240 | { 31, 31, 124, 124 }, // Mordad | |
241 | { 31, 31, 155, 155 }, // Shahrivar | |
242 | { 30, 30, 186, 186 }, // Mehr | |
243 | { 30, 30, 216, 216 }, // Aban | |
244 | { 30, 30, 246, 246 }, // Azar | |
245 | { 30, 30, 276, 276 }, // Dey | |
246 | { 30, 30, 306, 306 }, // Bahman | |
247 | { 29, 30, 336, 336 } // Esfand | |
248 | // len length of month | |
249 | // len2 length of month in a leap year | |
250 | // st days in year before start of month | |
251 | // st2 days in year before month in leap year | |
252 | }; | |
253 | ||
254 | int32_t PersianCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const { | |
255 | return LIMITS[field][limitType]; | |
256 | } | |
257 | ||
258 | //------------------------------------------------------------------------- | |
259 | // Assorted calculation utilities | |
260 | // | |
261 | ||
262 | /** | |
263 | * Determine whether a year is a leap year in the Persian calendar | |
264 | */ | |
265 | UBool PersianCalendar::isLeapYear(int32_t year) | |
266 | { | |
267 | return jalali_to_julian(year+1,1,1)-jalali_to_julian(year,1,1) == 366; | |
268 | } | |
269 | ||
270 | /** | |
271 | * Return the day # on which the given year starts. Days are counted | |
272 | * from the Hijri epoch, origin 0. | |
273 | */ | |
274 | int32_t PersianCalendar::yearStart(int32_t year) { | |
275 | return handleComputeMonthStart(year,1,FALSE); | |
276 | } | |
277 | ||
278 | /** | |
279 | * Return the day # on which the given month starts. Days are counted | |
280 | * from the Hijri epoch, origin 0. | |
281 | * | |
282 | * @param year The hijri shamsi year | |
283 | * @param year The hijri shamsi month, 0-based | |
284 | */ | |
285 | int32_t PersianCalendar::monthStart(int32_t year, int32_t month) const { | |
286 | return handleComputeMonthStart(year,month,FALSE); | |
287 | } | |
288 | ||
289 | //---------------------------------------------------------------------- | |
290 | // Calendar framework | |
291 | //---------------------------------------------------------------------- | |
292 | ||
293 | /** | |
294 | * Return the length (in days) of the given month. | |
295 | * | |
296 | * @param year The hijri shamsi year | |
297 | * @param year The hijri shamsi month, 0-based | |
298 | */ | |
299 | int32_t PersianCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const { | |
300 | return MONTH_COUNT[month][PersianCalendar::isLeapYear(extendedYear)?1:0]; | |
301 | } | |
302 | ||
303 | /** | |
304 | * Return the number of days in the given Persian year | |
305 | */ | |
306 | int32_t PersianCalendar::handleGetYearLength(int32_t extendedYear) const { | |
307 | return 365 + (PersianCalendar::isLeapYear(extendedYear) ? 1 : 0); | |
308 | } | |
309 | ||
310 | //------------------------------------------------------------------------- | |
311 | // Functions for converting from field values to milliseconds.... | |
312 | //------------------------------------------------------------------------- | |
313 | ||
314 | // Return JD of start of given month/year | |
315 | int32_t PersianCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool useMonth) const { | |
316 | // If the month is out of range, adjust it into range, and | |
317 | // modify the extended year value accordingly. | |
318 | if (month < 0 || month > 11) { | |
319 | eyear += month / 12; | |
320 | month = month % 12; | |
321 | } | |
322 | return jalali_to_julian(eyear,(useMonth?month+1:1),1)-1+1947955; | |
323 | } | |
324 | ||
325 | //------------------------------------------------------------------------- | |
326 | // Functions for converting from milliseconds to field values | |
327 | //------------------------------------------------------------------------- | |
328 | ||
329 | int32_t PersianCalendar::handleGetExtendedYear() { | |
330 | int32_t year; | |
331 | if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) { | |
332 | year = internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1 | |
333 | } else { | |
334 | year = internalGet(UCAL_YEAR, 1); // Default to year 1 | |
335 | } | |
336 | return year; | |
337 | } | |
338 | ||
339 | /** | |
340 | * Override Calendar to compute several fields specific to the Persian | |
341 | * calendar system. These are: | |
342 | * | |
343 | * <ul><li>ERA | |
344 | * <li>YEAR | |
345 | * <li>MONTH | |
346 | * <li>DAY_OF_MONTH | |
347 | * <li>DAY_OF_YEAR | |
348 | * <li>EXTENDED_YEAR</ul> | |
349 | * | |
350 | * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this | |
351 | * method is called. The getGregorianXxx() methods return Gregorian | |
352 | * calendar equivalents for the given Julian day. | |
353 | */ | |
354 | void PersianCalendar::handleComputeFields(int32_t julianDay, UErrorCode &/*status*/) { | |
355 | int jy,jm,jd; | |
356 | julian_to_jalali(julianDay-1947955,&jy,&jm,&jd); | |
357 | internalSet(UCAL_ERA, 0); | |
358 | internalSet(UCAL_YEAR, jy); | |
359 | internalSet(UCAL_EXTENDED_YEAR, jy); | |
360 | internalSet(UCAL_MONTH, jm-1); | |
361 | internalSet(UCAL_DAY_OF_MONTH, jd); | |
362 | internalSet(UCAL_DAY_OF_YEAR, jd + MONTH_COUNT[jm-1][2]); | |
363 | } | |
364 | ||
365 | UBool | |
366 | PersianCalendar::inDaylightTime(UErrorCode& status) const | |
367 | { | |
368 | // copied from GregorianCalendar | |
369 | if (U_FAILURE(status) || !getTimeZone().useDaylightTime()) | |
370 | return FALSE; | |
371 | ||
372 | // Force an update of the state of the Calendar. | |
373 | ((PersianCalendar*)this)->complete(status); // cast away const | |
374 | ||
375 | return (UBool)(U_SUCCESS(status) ? (internalGet(UCAL_DST_OFFSET) != 0) : FALSE); | |
376 | } | |
377 | ||
378 | // default century | |
379 | const UDate PersianCalendar::fgSystemDefaultCentury = DBL_MIN; | |
380 | const int32_t PersianCalendar::fgSystemDefaultCenturyYear = -1; | |
381 | ||
382 | UDate PersianCalendar::fgSystemDefaultCenturyStart = DBL_MIN; | |
383 | int32_t PersianCalendar::fgSystemDefaultCenturyStartYear = -1; | |
384 | ||
385 | UBool PersianCalendar::haveDefaultCentury() const | |
386 | { | |
387 | return TRUE; | |
388 | } | |
389 | ||
390 | UDate PersianCalendar::defaultCenturyStart() const | |
391 | { | |
392 | return internalGetDefaultCenturyStart(); | |
393 | } | |
394 | ||
395 | int32_t PersianCalendar::defaultCenturyStartYear() const | |
396 | { | |
397 | return internalGetDefaultCenturyStartYear(); | |
398 | } | |
399 | ||
400 | UDate | |
401 | PersianCalendar::internalGetDefaultCenturyStart() const | |
402 | { | |
403 | // lazy-evaluate systemDefaultCenturyStart | |
404 | UBool needsUpdate; | |
405 | UMTX_CHECK(NULL, (fgSystemDefaultCenturyStart == fgSystemDefaultCentury), needsUpdate); | |
406 | ||
407 | if (needsUpdate) { | |
408 | initializeSystemDefaultCentury(); | |
409 | } | |
410 | ||
411 | // use defaultCenturyStart unless it's the flag value; | |
412 | // then use systemDefaultCenturyStart | |
413 | ||
414 | return fgSystemDefaultCenturyStart; | |
415 | } | |
416 | ||
417 | int32_t | |
418 | PersianCalendar::internalGetDefaultCenturyStartYear() const | |
419 | { | |
420 | // lazy-evaluate systemDefaultCenturyStartYear | |
421 | UBool needsUpdate; | |
422 | UMTX_CHECK(NULL, (fgSystemDefaultCenturyStart == fgSystemDefaultCentury), needsUpdate); | |
423 | ||
424 | if (needsUpdate) { | |
425 | initializeSystemDefaultCentury(); | |
426 | } | |
427 | ||
428 | // use defaultCenturyStart unless it's the flag value; | |
429 | // then use systemDefaultCenturyStartYear | |
430 | ||
431 | return fgSystemDefaultCenturyStartYear; | |
432 | } | |
433 | ||
434 | void | |
435 | PersianCalendar::initializeSystemDefaultCentury() | |
436 | { | |
437 | // initialize systemDefaultCentury and systemDefaultCenturyYear based | |
438 | // on the current time. They'll be set to 80 years before | |
439 | // the current time. | |
440 | // No point in locking as it should be idempotent. | |
441 | if (fgSystemDefaultCenturyStart == fgSystemDefaultCentury) | |
442 | { | |
443 | UErrorCode status = U_ZERO_ERROR; | |
444 | PersianCalendar calendar(Locale("@calendar=persian"),status); | |
445 | if (U_SUCCESS(status)) | |
446 | { | |
447 | calendar.setTime(Calendar::getNow(), status); | |
448 | calendar.add(UCAL_YEAR, -80, status); | |
449 | UDate newStart = calendar.getTime(status); | |
450 | int32_t newYear = calendar.get(UCAL_YEAR, status); | |
451 | { | |
452 | umtx_lock(NULL); | |
453 | fgSystemDefaultCenturyStart = newStart; | |
454 | fgSystemDefaultCenturyStartYear = newYear; | |
455 | umtx_unlock(NULL); | |
456 | } | |
457 | } | |
458 | // We have no recourse upon failure unless we want to propagate the failure | |
459 | // out. | |
460 | } | |
461 | } | |
462 | ||
463 | UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PersianCalendar) | |
464 | ||
465 | U_NAMESPACE_END | |
466 | ||
467 | #endif | |
468 |