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