/src/icu/source/i18n/persncal.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) 2003-2013, International Business Machines Corporation  | 
6  |  |  * and others. All Rights Reserved.  | 
7  |  |  ******************************************************************************  | 
8  |  |  *  | 
9  |  |  * File PERSNCAL.CPP  | 
10  |  |  *  | 
11  |  |  * Modification History:  | 
12  |  |  *  | 
13  |  |  *   Date        Name        Description  | 
14  |  |  *   9/23/2003   mehran      posted to icu-design  | 
15  |  |  *   10/1/2012   roozbeh     Fixed algorithm and heavily refactored and rewrote  | 
16  |  |  *                           based on the implementation of Gregorian  | 
17  |  |  *****************************************************************************  | 
18  |  |  */  | 
19  |  |  | 
20  |  | #include "persncal.h"  | 
21  |  |  | 
22  |  | #if !UCONFIG_NO_FORMATTING  | 
23  |  |  | 
24  |  | #include "umutex.h"  | 
25  |  | #include "gregoimp.h" // Math  | 
26  |  | #include <float.h>  | 
27  |  |  | 
28  |  | static const int16_t kPersianNumDays[]  | 
29  |  | = {0,31,62,93,124,155,186,216,246,276,306,336}; // 0-based, for day-in-year | 
30  |  | static const int8_t kPersianMonthLength[]  | 
31  |  | = {31,31,31,31,31,31,30,30,30,30,30,29}; // 0-based | 
32  |  | static const int8_t kPersianLeapMonthLength[]  | 
33  |  | = {31,31,31,31,31,31,30,30,30,30,30,30}; // 0-based | 
34  |  |  | 
35  |  | static const int32_t kPersianCalendarLimits[UCAL_FIELD_COUNT][4] = { | 
36  |  |     // Minimum  Greatest     Least   Maximum  | 
37  |  |     //           Minimum   Maximum  | 
38  |  |     {        0,        0,        0,        0}, // ERA | 
39  |  |     { -5000000, -5000000,  5000000,  5000000}, // YEAR | 
40  |  |     {        0,        0,       11,       11}, // MONTH | 
41  |  |     {        1,        1,       52,       53}, // WEEK_OF_YEAR | 
42  |  |     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH | 
43  |  |     {        1,       1,        29,       31}, // DAY_OF_MONTH | 
44  |  |     {        1,       1,       365,      366}, // DAY_OF_YEAR | 
45  |  |     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK | 
46  |  |     {        1,       1,         5,        5}, // DAY_OF_WEEK_IN_MONTH | 
47  |  |     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM | 
48  |  |     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR | 
49  |  |     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY | 
50  |  |     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE | 
51  |  |     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND | 
52  |  |     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND | 
53  |  |     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET | 
54  |  |     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET | 
55  |  |     { -5000000, -5000000,  5000000,  5000000}, // YEAR_WOY | 
56  |  |     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL | 
57  |  |     { -5000000, -5000000,  5000000,  5000000}, // EXTENDED_YEAR | 
58  |  |     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY | 
59  |  |     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY | 
60  |  |     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH | 
61  |  | };  | 
62  |  |  | 
63  |  | U_NAMESPACE_BEGIN  | 
64  |  |  | 
65  |  | static const int32_t PERSIAN_EPOCH = 1948320;  | 
66  |  |  | 
67  |  | // Implementation of the PersianCalendar class  | 
68  |  |  | 
69  |  | //-------------------------------------------------------------------------  | 
70  |  | // Constructors...  | 
71  |  | //-------------------------------------------------------------------------  | 
72  |  |  | 
73  | 0  | const char *PersianCalendar::getType() const {  | 
74  | 0  |     return "persian";  | 
75  | 0  | }  | 
76  |  |  | 
77  | 0  | PersianCalendar* PersianCalendar::clone() const { | 
78  | 0  |     return new PersianCalendar(*this);  | 
79  | 0  | }  | 
80  |  |  | 
81  |  | PersianCalendar::PersianCalendar(const Locale& aLocale, UErrorCode& success)  | 
82  | 0  |   :   Calendar(TimeZone::forLocaleOrDefault(aLocale), aLocale, success)  | 
83  | 0  | { | 
84  | 0  |     setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly.  | 
85  | 0  | }  | 
86  |  |  | 
87  | 0  | PersianCalendar::PersianCalendar(const PersianCalendar& other) : Calendar(other) { | 
88  | 0  | }  | 
89  |  |  | 
90  |  | PersianCalendar::~PersianCalendar()  | 
91  | 0  | { | 
92  | 0  | }  | 
93  |  |  | 
94  |  | //-------------------------------------------------------------------------  | 
95  |  | // Minimum / Maximum access functions  | 
96  |  | //-------------------------------------------------------------------------  | 
97  |  |  | 
98  |  |  | 
99  | 0  | int32_t PersianCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const { | 
100  | 0  |     return kPersianCalendarLimits[field][limitType];  | 
101  | 0  | }  | 
102  |  |  | 
103  |  | //-------------------------------------------------------------------------  | 
104  |  | // Assorted calculation utilities  | 
105  |  | //  | 
106  |  |  | 
107  |  | /**  | 
108  |  |  * Determine whether a year is a leap year in the Persian calendar  | 
109  |  |  */  | 
110  |  | UBool PersianCalendar::isLeapYear(int32_t year)  | 
111  | 0  | { | 
112  | 0  |     int32_t remainder;  | 
113  | 0  |     ClockMath::floorDivide(25 * year + 11, 33, remainder);  | 
114  | 0  |     return (remainder < 8);  | 
115  | 0  | }  | 
116  |  |       | 
117  |  | /**  | 
118  |  |  * Return the day # on which the given year starts.  Days are counted  | 
119  |  |  * from the Persian epoch, origin 0.  | 
120  |  |  */  | 
121  | 0  | int32_t PersianCalendar::yearStart(int32_t year) { | 
122  | 0  |     return handleComputeMonthStart(year,0,FALSE);  | 
123  | 0  | }  | 
124  |  |       | 
125  |  | /**  | 
126  |  |  * Return the day # on which the given month starts.  Days are counted  | 
127  |  |  * from the Persian epoch, origin 0.  | 
128  |  |  *  | 
129  |  |  * @param year  The Persian year  | 
130  |  |  * @param year  The Persian month, 0-based  | 
131  |  |  */  | 
132  | 0  | int32_t PersianCalendar::monthStart(int32_t year, int32_t month) const { | 
133  | 0  |     return handleComputeMonthStart(year,month,TRUE);  | 
134  | 0  | }  | 
135  |  |       | 
136  |  | //----------------------------------------------------------------------  | 
137  |  | // Calendar framework  | 
138  |  | //----------------------------------------------------------------------  | 
139  |  |  | 
140  |  | /**  | 
141  |  |  * Return the length (in days) of the given month.  | 
142  |  |  *  | 
143  |  |  * @param year  The Persian year  | 
144  |  |  * @param year  The Persian month, 0-based  | 
145  |  |  */  | 
146  | 0  | int32_t PersianCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const { | 
147  |  |     // If the month is out of range, adjust it into range, and  | 
148  |  |     // modify the extended year value accordingly.  | 
149  | 0  |     if (month < 0 || month > 11) { | 
150  | 0  |         extendedYear += ClockMath::floorDivide(month, 12, month);  | 
151  | 0  |     }  | 
152  |  | 
  | 
153  | 0  |     return isLeapYear(extendedYear) ? kPersianLeapMonthLength[month] : kPersianMonthLength[month];  | 
154  | 0  | }  | 
155  |  |  | 
156  |  | /**  | 
157  |  |  * Return the number of days in the given Persian year  | 
158  |  |  */  | 
159  | 0  | int32_t PersianCalendar::handleGetYearLength(int32_t extendedYear) const { | 
160  | 0  |     return isLeapYear(extendedYear) ? 366 : 365;  | 
161  | 0  | }  | 
162  |  |       | 
163  |  | //-------------------------------------------------------------------------  | 
164  |  | // Functions for converting from field values to milliseconds....  | 
165  |  | //-------------------------------------------------------------------------  | 
166  |  |  | 
167  |  | // Return JD of start of given month/year  | 
168  | 0  | int32_t PersianCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool /*useMonth*/) const { | 
169  |  |     // If the month is out of range, adjust it into range, and  | 
170  |  |     // modify the extended year value accordingly.  | 
171  | 0  |     if (month < 0 || month > 11) { | 
172  | 0  |         eyear += ClockMath::floorDivide(month, 12, month);  | 
173  | 0  |     }  | 
174  |  | 
  | 
175  | 0  |     int32_t julianDay = PERSIAN_EPOCH - 1 + 365 * (eyear - 1) + ClockMath::floorDivide(8 * eyear + 21, 33);  | 
176  |  | 
  | 
177  | 0  |     if (month != 0) { | 
178  | 0  |         julianDay += kPersianNumDays[month];  | 
179  | 0  |     }  | 
180  |  | 
  | 
181  | 0  |     return julianDay;  | 
182  | 0  | }  | 
183  |  |  | 
184  |  | //-------------------------------------------------------------------------  | 
185  |  | // Functions for converting from milliseconds to field values  | 
186  |  | //-------------------------------------------------------------------------  | 
187  |  |  | 
188  | 0  | int32_t PersianCalendar::handleGetExtendedYear() { | 
189  | 0  |     int32_t year;  | 
190  | 0  |     if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) { | 
191  | 0  |         year = internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1  | 
192  | 0  |     } else { | 
193  | 0  |         year = internalGet(UCAL_YEAR, 1); // Default to year 1  | 
194  | 0  |     }  | 
195  | 0  |     return year;  | 
196  | 0  | }  | 
197  |  |  | 
198  |  | /**  | 
199  |  |  * Override Calendar to compute several fields specific to the Persian  | 
200  |  |  * calendar system.  These are:  | 
201  |  |  *  | 
202  |  |  * <ul><li>ERA  | 
203  |  |  * <li>YEAR  | 
204  |  |  * <li>MONTH  | 
205  |  |  * <li>DAY_OF_MONTH  | 
206  |  |  * <li>DAY_OF_YEAR  | 
207  |  |  * <li>EXTENDED_YEAR</ul>  | 
208  |  |  *   | 
209  |  |  * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this  | 
210  |  |  * method is called.  | 
211  |  |  */  | 
212  | 0  | void PersianCalendar::handleComputeFields(int32_t julianDay, UErrorCode &/*status*/) { | 
213  | 0  |     int32_t year, month, dayOfMonth, dayOfYear;  | 
214  |  | 
  | 
215  | 0  |     int32_t daysSinceEpoch = julianDay - PERSIAN_EPOCH;  | 
216  | 0  |     year = 1 + (int32_t)ClockMath::floorDivide(33 * (int64_t)daysSinceEpoch + 3, (int64_t)12053);  | 
217  |  | 
  | 
218  | 0  |     int32_t farvardin1 = 365 * (year - 1) + ClockMath::floorDivide(8 * year + 21, 33);  | 
219  | 0  |     dayOfYear = (daysSinceEpoch - farvardin1); // 0-based  | 
220  | 0  |     if (dayOfYear < 216) { // Compute 0-based month | 
221  | 0  |         month = dayOfYear / 31;  | 
222  | 0  |     } else { | 
223  | 0  |         month = (dayOfYear - 6) / 30;  | 
224  | 0  |     }  | 
225  | 0  |     dayOfMonth = dayOfYear - kPersianNumDays[month] + 1;  | 
226  | 0  |     ++dayOfYear; // Make it 1-based now  | 
227  |  | 
  | 
228  | 0  |     internalSet(UCAL_ERA, 0);  | 
229  | 0  |     internalSet(UCAL_YEAR, year);  | 
230  | 0  |     internalSet(UCAL_EXTENDED_YEAR, year);  | 
231  | 0  |     internalSet(UCAL_MONTH, month);  | 
232  | 0  |     internalSet(UCAL_DAY_OF_MONTH, dayOfMonth);  | 
233  | 0  |     internalSet(UCAL_DAY_OF_YEAR, dayOfYear);  | 
234  | 0  | }      | 
235  |  |  | 
236  |  | UBool  | 
237  |  | PersianCalendar::inDaylightTime(UErrorCode& status) const  | 
238  | 0  | { | 
239  |  |     // copied from GregorianCalendar  | 
240  | 0  |     if (U_FAILURE(status) || !getTimeZone().useDaylightTime())   | 
241  | 0  |         return FALSE;  | 
242  |  |  | 
243  |  |     // Force an update of the state of the Calendar.  | 
244  | 0  |     ((PersianCalendar*)this)->complete(status); // cast away const  | 
245  |  | 
  | 
246  | 0  |     return (UBool)(U_SUCCESS(status) ? (internalGet(UCAL_DST_OFFSET) != 0) : FALSE);  | 
247  | 0  | }  | 
248  |  |  | 
249  |  | // default century  | 
250  |  |  | 
251  |  | static UDate           gSystemDefaultCenturyStart       = DBL_MIN;  | 
252  |  | static int32_t         gSystemDefaultCenturyStartYear   = -1;  | 
253  |  | static icu::UInitOnce  gSystemDefaultCenturyInit        = U_INITONCE_INITIALIZER;  | 
254  |  |  | 
255  |  | UBool PersianCalendar::haveDefaultCentury() const  | 
256  | 0  | { | 
257  | 0  |     return TRUE;  | 
258  | 0  | }  | 
259  |  |  | 
260  | 0  | static void U_CALLCONV initializeSystemDefaultCentury() { | 
261  |  |     // initialize systemDefaultCentury and systemDefaultCenturyYear based  | 
262  |  |     // on the current time.  They'll be set to 80 years before  | 
263  |  |     // the current time.  | 
264  | 0  |     UErrorCode status = U_ZERO_ERROR;  | 
265  | 0  |     PersianCalendar calendar(Locale("@calendar=persian"),status); | 
266  | 0  |     if (U_SUCCESS(status))  | 
267  | 0  |     { | 
268  | 0  |         calendar.setTime(Calendar::getNow(), status);  | 
269  | 0  |         calendar.add(UCAL_YEAR, -80, status);  | 
270  |  | 
  | 
271  | 0  |         gSystemDefaultCenturyStart = calendar.getTime(status);  | 
272  | 0  |         gSystemDefaultCenturyStartYear = calendar.get(UCAL_YEAR, status);  | 
273  | 0  |     }  | 
274  |  |     // We have no recourse upon failure unless we want to propagate the failure  | 
275  |  |     // out.  | 
276  | 0  | }  | 
277  |  |  | 
278  | 0  | UDate PersianCalendar::defaultCenturyStart() const { | 
279  |  |     // lazy-evaluate systemDefaultCenturyStart  | 
280  | 0  |     umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury);  | 
281  | 0  |     return gSystemDefaultCenturyStart;  | 
282  | 0  | }  | 
283  |  |  | 
284  | 0  | int32_t PersianCalendar::defaultCenturyStartYear() const { | 
285  |  |     // lazy-evaluate systemDefaultCenturyStartYear  | 
286  | 0  |     umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury);  | 
287  | 0  |     return gSystemDefaultCenturyStartYear;  | 
288  | 0  | }  | 
289  |  |  | 
290  |  | UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PersianCalendar)  | 
291  |  |  | 
292  |  | U_NAMESPACE_END  | 
293  |  |  | 
294  |  | #endif  | 
295  |  |  |