• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
jalali_to_julian(int year,int month,int day)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 
julian_to_jalali(int32_t daysNo,int * h_y,int * h_m,int * h_d)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 
getType() const182 const char *PersianCalendar::getType() const {
183     return "persian";
184 }
185 
clone() const186 Calendar* PersianCalendar::clone() const {
187     return new PersianCalendar(*this);
188 }
189 
PersianCalendar(const Locale & aLocale,UErrorCode & success)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 
PersianCalendar(const PersianCalendar & other)196 PersianCalendar::PersianCalendar(const PersianCalendar& other) : Calendar(other) {
197 }
198 
~PersianCalendar()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 
handleGetLimit(UCalendarDateFields field,ELimitType limitType) const254 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  */
isLeapYear(int32_t year)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  */
yearStart(int32_t year)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  */
monthStart(int32_t year,int32_t month) const285 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  */
handleGetMonthLength(int32_t extendedYear,int32_t month) const299 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  */
handleGetYearLength(int32_t extendedYear) const306 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
handleComputeMonthStart(int32_t eyear,int32_t month,UBool useMonth) const315 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 
handleGetExtendedYear()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  */
handleComputeFields(int32_t julianDay,UErrorCode &)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
inDaylightTime(UErrorCode & status) const366 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 
haveDefaultCentury() const385 UBool PersianCalendar::haveDefaultCentury() const
386 {
387     return TRUE;
388 }
389 
defaultCenturyStart() const390 UDate PersianCalendar::defaultCenturyStart() const
391 {
392     return internalGetDefaultCenturyStart();
393 }
394 
defaultCenturyStartYear() const395 int32_t PersianCalendar::defaultCenturyStartYear() const
396 {
397     return internalGetDefaultCenturyStartYear();
398 }
399 
400 UDate
internalGetDefaultCenturyStart() const401 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
internalGetDefaultCenturyStartYear() const418 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
initializeSystemDefaultCentury()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     UErrorCode status = U_ZERO_ERROR;
441     PersianCalendar calendar(Locale("@calendar=persian"),status);
442     if (U_SUCCESS(status))
443     {
444         calendar.setTime(Calendar::getNow(), status);
445         calendar.add(UCAL_YEAR, -80, status);
446         UDate    newStart =  calendar.getTime(status);
447         int32_t  newYear  =  calendar.get(UCAL_YEAR, status);
448         umtx_lock(NULL);
449         if (fgSystemDefaultCenturyStart == fgSystemDefaultCentury)
450         {
451             fgSystemDefaultCenturyStartYear = newYear;
452             fgSystemDefaultCenturyStart = newStart;
453         }
454         umtx_unlock(NULL);
455     }
456     // We have no recourse upon failure unless we want to propagate the failure
457     // out.
458 }
459 
460 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PersianCalendar)
461 
462 U_NAMESPACE_END
463 
464 #endif
465 
466