Coverage Report

Created: 2025-06-24 06:43

/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