Line data Source code
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 : }
76 :
77 0 : Calendar* PersianCalendar::clone() const {
78 0 : return new PersianCalendar(*this);
79 : }
80 :
81 0 : PersianCalendar::PersianCalendar(const Locale& aLocale, UErrorCode& success)
82 0 : : Calendar(TimeZone::createDefault(), aLocale, success)
83 : {
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 0 : PersianCalendar::~PersianCalendar()
91 : {
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 : }
102 :
103 : //-------------------------------------------------------------------------
104 : // Assorted calculation utilities
105 : //
106 :
107 : /**
108 : * Determine whether a year is a leap year in the Persian calendar
109 : */
110 0 : UBool PersianCalendar::isLeapYear(int32_t year)
111 : {
112 : int32_t remainder;
113 0 : ClockMath::floorDivide(25 * year + 11, 33, remainder);
114 0 : return (remainder < 8);
115 : }
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 : }
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 : }
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 : }
152 :
153 0 : return isLeapYear(extendedYear) ? kPersianLeapMonthLength[month] : kPersianMonthLength[month];
154 : }
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 : }
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 : }
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 : }
180 :
181 0 : return julianDay;
182 : }
183 :
184 : //-------------------------------------------------------------------------
185 : // Functions for converting from milliseconds to field values
186 : //-------------------------------------------------------------------------
187 :
188 0 : int32_t PersianCalendar::handleGetExtendedYear() {
189 : 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 : } else {
193 0 : year = internalGet(UCAL_YEAR, 1); // Default to year 1
194 : }
195 0 : return year;
196 : }
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 : int32_t year, month, dayOfMonth, dayOfYear;
214 :
215 0 : int32_t daysSinceEpoch = julianDay - PERSIAN_EPOCH;
216 0 : year = 1 + ClockMath::floorDivide(33 * daysSinceEpoch + 3, 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 : } else {
223 0 : month = (dayOfYear - 6) / 30;
224 : }
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 0 : PersianCalendar::inDaylightTime(UErrorCode& status) const
238 : {
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 : }
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 0 : UBool PersianCalendar::haveDefaultCentury() const
256 : {
257 0 : return TRUE;
258 : }
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 : {
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 : }
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 : }
283 :
284 0 : int32_t PersianCalendar::defaultCenturyStartYear() const {
285 : // lazy-evaluate systemDefaultCenturyStartYear
286 0 : umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury);
287 0 : return gSystemDefaultCenturyStartYear;
288 : }
289 :
290 0 : UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PersianCalendar)
291 :
292 : U_NAMESPACE_END
293 :
294 : #endif
295 :
|