Coverage Report

Created: 2025-06-13 06:34

/src/icu/icu4c/source/i18n/indiancal.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
 * Copyright (C) 2003-2014, International Business Machines Corporation
5
 * and others. All Rights Reserved.
6
 ******************************************************************************
7
 *
8
 * File INDIANCAL.CPP
9
 *****************************************************************************
10
 */
11
12
#include "indiancal.h"
13
#include <stdlib.h>
14
#if !UCONFIG_NO_FORMATTING
15
16
#include "mutex.h"
17
#include <float.h>
18
#include "gregoimp.h" // Math
19
#include "uhash.h"
20
21
// Debugging
22
#ifdef U_DEBUG_INDIANCAL
23
#include <stdio.h>
24
#include <stdarg.h>
25
26
#endif
27
28
U_NAMESPACE_BEGIN
29
30
// Implementation of the IndianCalendar class
31
32
//-------------------------------------------------------------------------
33
// Constructors...
34
//-------------------------------------------------------------------------
35
36
37
3.00k
IndianCalendar* IndianCalendar::clone() const {
38
3.00k
  return new IndianCalendar(*this);
39
3.00k
}
40
41
IndianCalendar::IndianCalendar(const Locale& aLocale, UErrorCode& success)
42
155
  :   Calendar(TimeZone::forLocaleOrDefault(aLocale), aLocale, success)
43
155
{
44
155
}
45
46
3.00k
IndianCalendar::IndianCalendar(const IndianCalendar& other) : Calendar(other) {
47
3.00k
}
48
49
IndianCalendar::~IndianCalendar()
50
3.10k
{
51
3.10k
}
52
0
const char *IndianCalendar::getType() const { 
53
0
   return "indian";
54
0
}
55
  
56
static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = {
57
    // Minimum  Greatest     Least   Maximum
58
    //           Minimum   Maximum
59
    {        0,        0,        0,        0}, // ERA
60
    { -5000000, -5000000,  5000000,  5000000}, // YEAR
61
    {        0,        0,       11,       11}, // MONTH
62
    {        1,        1,       52,       53}, // WEEK_OF_YEAR
63
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH
64
    {        1,        1,       30,       31}, // DAY_OF_MONTH
65
    {        1,        1,      365,      366}, // DAY_OF_YEAR
66
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK
67
    {       -1,       -1,        5,        5}, // DAY_OF_WEEK_IN_MONTH
68
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM
69
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR
70
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY
71
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE
72
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND
73
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND
74
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET
75
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET
76
    { -5000000, -5000000,  5000000,  5000000}, // YEAR_WOY
77
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL
78
    { -5000000, -5000000,  5000000,  5000000}, // EXTENDED_YEAR
79
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY
80
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY
81
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH
82
    {        0,        0,       11,       11}, // ORDINAL_MONTH
83
};
84
85
static const int32_t INDIAN_ERA_START  = 78;
86
static const int32_t INDIAN_YEAR_START = 80;
87
88
10.3k
int32_t IndianCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const {
89
10.3k
  return LIMITS[field][limitType];
90
10.3k
}
91
92
/*
93
 * Determine whether the given gregorian year is a Leap year 
94
 */
95
static UBool isGregorianLeap(int32_t year)
96
35.0k
{
97
35.0k
    return Grego::isLeapYear(year);
98
35.0k
}
99
  
100
//----------------------------------------------------------------------
101
// Calendar framework
102
//----------------------------------------------------------------------
103
104
/*
105
 * Return the length (in days) of the given month.
106
 *
107
 * @param eyear  The year in Saka Era
108
 * @param month  The month(0-based) in Indian calendar
109
 */
110
2.79k
int32_t IndianCalendar::handleGetMonthLength(int32_t eyear, int32_t month, UErrorCode& /* status */) const {
111
2.79k
   if (month < 0 || month > 11) {
112
55
      eyear += ClockMath::floorDivide(month, 12, &month);
113
55
   }
114
115
2.79k
   if (isGregorianLeap(eyear + INDIAN_ERA_START) && month == 0) {
116
236
       return 31;
117
236
   }
118
119
2.56k
   if (month >= 1 && month <= 5) {
120
708
       return 31;
121
708
   }
122
123
1.85k
   return 30;
124
2.56k
}
125
126
/*
127
 * Return the number of days in the given Indian year
128
 *
129
 * @param eyear The year in Saka Era.
130
 */
131
12.9k
int32_t IndianCalendar::handleGetYearLength(int32_t eyear, UErrorCode& status) const {
132
12.9k
    if (U_FAILURE(status)) return 0;
133
12.9k
    return isGregorianLeap(eyear + INDIAN_ERA_START) ? 366 : 365;
134
12.9k
}
135
/*
136
 * Returns the Julian Day corresponding to gregorian date
137
 *
138
 * @param year The Gregorian year
139
 * @param month The month in Gregorian Year, 0 based.
140
 * @param date The date in Gregorian day in month
141
 */
142
19.2k
static double gregorianToJD(int32_t year, int32_t month, int32_t date) {
143
19.2k
   return Grego::fieldsToDay(year, month, date) + kEpochStartAsJulianDay - 0.5;
144
19.2k
}
145
146
   
147
//-------------------------------------------------------------------------
148
// Functions for converting from field values to milliseconds....
149
//-------------------------------------------------------------------------
150
6.21k
static double IndianToJD(int32_t year, int32_t month, int32_t date) {
151
6.21k
   int32_t leapMonth, gyear, m;
152
6.21k
   double start, jd;
153
154
6.21k
   gyear = year + INDIAN_ERA_START;
155
156
157
6.21k
   if(isGregorianLeap(gyear)) {
158
1.87k
      leapMonth = 31;
159
1.87k
      start = gregorianToJD(gyear, 2 /* The third month in 0 based month */, 21);
160
1.87k
   } 
161
4.33k
   else {
162
4.33k
      leapMonth = 30;
163
4.33k
      start = gregorianToJD(gyear, 2 /* The third month in 0 based month */, 22);
164
4.33k
   }
165
166
6.21k
   if (month == 1) {
167
2.85k
      jd = start + (date - 1);
168
3.35k
   } else {
169
3.35k
      jd = start + leapMonth;
170
3.35k
      m = month - 2;
171
172
      //m = Math.min(m, 5);
173
3.35k
      if (m > 5) {
174
1.59k
          m = 5;
175
1.59k
      }
176
177
3.35k
      jd += m * 31;
178
179
3.35k
      if (month >= 8) {
180
1.59k
         m = month - 7;
181
1.59k
         jd += m * 30;
182
1.59k
      }
183
3.35k
      jd += date - 1;
184
3.35k
   }
185
186
6.21k
   return jd;
187
6.21k
}
188
189
/*
190
 * Return JD of start of given month/year of Indian Calendar
191
 * @param eyear The year in Indian Calendar measured from Saka Era (78 AD).
192
 * @param month The month in Indian calendar
193
 */
194
6.22k
int64_t IndianCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool /* useMonth */, UErrorCode& status) const {
195
6.22k
   if (U_FAILURE(status)) {
196
0
       return 0;
197
0
   }
198
199
   //month is 0 based; converting it to 1-based 
200
6.22k
   int32_t imonth;
201
202
    // If the month is out of range, adjust it into range, and adjust the extended year accordingly
203
6.22k
   if (month < 0 || month > 11) {
204
2.07k
      if (uprv_add32_overflow(eyear, ClockMath::floorDivide(month, 12, &month), &eyear)) {
205
10
          status = U_ILLEGAL_ARGUMENT_ERROR;
206
10
          return 0;
207
10
      }
208
2.07k
   }
209
210
6.21k
   if(month == 12){
211
0
       imonth = 1;
212
6.21k
   } else {
213
6.21k
       imonth = month + 1; 
214
6.21k
   }
215
   
216
6.21k
   int64_t jd = IndianToJD(eyear ,imonth, 1);
217
218
6.21k
   return jd;
219
6.22k
}
220
221
//-------------------------------------------------------------------------
222
// Functions for converting from milliseconds to field values
223
//-------------------------------------------------------------------------
224
225
5.84k
int32_t IndianCalendar::handleGetExtendedYear(UErrorCode& status) {
226
5.84k
    if (U_FAILURE(status)) {
227
0
        return 0;
228
0
    }
229
5.84k
    int32_t year;
230
231
5.84k
    if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) {
232
4.77k
        year = internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1
233
4.77k
    } else {
234
1.07k
        year = internalGet(UCAL_YEAR, 1); // Default to year 1
235
1.07k
    }
236
237
5.84k
    return year;
238
5.84k
}
239
240
/*
241
 * Override Calendar to compute several fields specific to the Indian
242
 * calendar system.  These are:
243
 *
244
 * <ul><li>ERA
245
 * <li>YEAR
246
 * <li>MONTH
247
 * <li>DAY_OF_MONTH
248
 * <li>EXTENDED_YEAR</ul>
249
 * 
250
 * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this
251
 * method is called. The getGregorianXxx() methods return Gregorian
252
 * calendar equivalents for the given Julian day.
253
 */
254
13.0k
void IndianCalendar::handleComputeFields(int32_t julianDay, UErrorCode& /* status */) {
255
13.0k
    double jdAtStartOfGregYear;
256
13.0k
    int32_t leapMonth, IndianYear, yday, IndianMonth, IndianDayOfMonth, mday;
257
    // Stores gregorian date corresponding to Julian day;
258
13.0k
    int32_t gregorianYear = getGregorianYear();
259
260
13.0k
    IndianYear = gregorianYear - INDIAN_ERA_START;            // Year in Saka era
261
13.0k
    jdAtStartOfGregYear = gregorianToJD(gregorianYear, 0, 1); // JD at start of Gregorian year
262
13.0k
    yday = static_cast<int32_t>(julianDay - jdAtStartOfGregYear); // Day number in Gregorian year (starting from 0)
263
264
13.0k
    if (yday < INDIAN_YEAR_START) {
265
        // Day is at the end of the preceding Saka year
266
1.71k
        IndianYear -= 1;
267
1.71k
        leapMonth = isGregorianLeap(gregorianYear - 1) ? 31 : 30; // Days in leapMonth this year, previous Gregorian year
268
1.71k
        yday += leapMonth + (31 * 5) + (30 * 3) + 10;
269
11.3k
    } else {
270
11.3k
        leapMonth = isGregorianLeap(gregorianYear) ? 31 : 30; // Days in leapMonth this year
271
11.3k
        yday -= INDIAN_YEAR_START;
272
11.3k
    }
273
274
13.0k
    if (yday < leapMonth) {
275
6.24k
        IndianMonth = 0;
276
6.24k
        IndianDayOfMonth = yday + 1;
277
6.79k
    } else {
278
6.79k
        mday = yday - leapMonth;
279
6.79k
        if (mday < (31 * 5)) {
280
2.62k
            IndianMonth = static_cast<int32_t>(uprv_floor(mday / 31)) + 1;
281
2.62k
            IndianDayOfMonth = (mday % 31) + 1;
282
4.17k
        } else {
283
4.17k
            mday -= 31 * 5;
284
4.17k
            IndianMonth = static_cast<int32_t>(uprv_floor(mday / 30)) + 6;
285
4.17k
            IndianDayOfMonth = (mday % 30) + 1;
286
4.17k
        }
287
6.79k
   }
288
289
13.0k
   internalSet(UCAL_ERA, 0);
290
13.0k
   internalSet(UCAL_EXTENDED_YEAR, IndianYear);
291
13.0k
   internalSet(UCAL_YEAR, IndianYear);
292
13.0k
   internalSet(UCAL_MONTH, IndianMonth);
293
13.0k
   internalSet(UCAL_ORDINAL_MONTH, IndianMonth);
294
13.0k
   internalSet(UCAL_DAY_OF_MONTH, IndianDayOfMonth);
295
13.0k
   internalSet(UCAL_DAY_OF_YEAR, yday + 1); // yday is 0-based
296
13.0k
}
297
298
IMPL_SYSTEM_DEFAULT_CENTURY(IndianCalendar, "@calendar=indian")
299
300
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IndianCalendar)
301
302
0
int32_t IndianCalendar::getRelatedYearDifference() const {
303
0
    constexpr int32_t kIndianCalendarRelatedYearDifference = 79;
304
0
    return kIndianCalendarRelatedYearDifference;
305
0
}
306
307
U_NAMESPACE_END
308
309
#endif
310