Coverage Report

Created: 2025-12-07 06:36

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/icu/icu4c/source/i18n/hebrwcal.cpp
Line
Count
Source
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-2016, International Business Machines Corporation
6
* and others. All Rights Reserved.
7
******************************************************************************
8
*
9
* File HEBRWCAL.CPP
10
*
11
* Modification History:
12
*
13
*   Date        Name        Description
14
*   12/03/2003  srl         ported from java HebrewCalendar
15
*****************************************************************************
16
*/
17
18
#include "hebrwcal.h"
19
20
#if !UCONFIG_NO_FORMATTING
21
22
#include "cmemory.h"
23
#include "cstring.h"
24
#include "umutex.h"
25
#include <float.h>
26
#include "gregoimp.h" // ClockMath
27
#include "astro.h" // CalendarCache
28
#include "uhash.h"
29
#include "ucln_in.h"
30
31
// Hebrew Calendar implementation
32
33
/**
34
* The absolute date, in milliseconds since 1/1/1970 AD, Gregorian,
35
* of the start of the Hebrew calendar.  In order to keep this calendar's
36
* time of day in sync with that of the Gregorian calendar, we use
37
* midnight, rather than sunset the day before.
38
*/
39
//static const double EPOCH_MILLIS = -180799862400000.; // 1/1/1 HY
40
41
static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = {
42
    // Minimum  Greatest    Least  Maximum
43
    //           Minimum  Maximum
44
    {        0,        0,        0,        0}, // ERA
45
    { -5000000, -5000000,  5000000,  5000000}, // YEAR
46
    {        0,        0,       12,       12}, // MONTH
47
    {        1,        1,       51,       56}, // WEEK_OF_YEAR
48
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH
49
    {        1,        1,       29,       30}, // DAY_OF_MONTH
50
    {        1,        1,      353,      385}, // DAY_OF_YEAR
51
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK
52
    {       -1,       -1,        5,        5}, // DAY_OF_WEEK_IN_MONTH
53
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM
54
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR
55
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY
56
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE
57
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND
58
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND
59
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET
60
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET
61
    { -5000000, -5000000,  5000000,  5000000}, // YEAR_WOY
62
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL
63
    { -5000000, -5000000,  5000000,  5000000}, // EXTENDED_YEAR
64
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY
65
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY
66
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH
67
    {        0,        0,       11,       12}, // ORDINAL_MONTH
68
};
69
70
/**
71
* The lengths of the Hebrew months.  This is complicated, because there
72
* are three different types of years, or six if you count leap years.
73
* Due to the rules for postponing the start of the year to avoid having
74
* certain holidays fall on the sabbath, the year can end up being three
75
* different lengths, called "deficient", "normal", and "complete".
76
*/
77
static const int8_t MONTH_LENGTH[][3] = {
78
    // Deficient  Normal     Complete
79
    {   30,         30,         30     },           //Tishri
80
    {   29,         29,         30     },           //Heshvan
81
    {   29,         30,         30     },           //Kislev
82
    {   29,         29,         29     },           //Tevet
83
    {   30,         30,         30     },           //Shevat
84
    {   30,         30,         30     },           //Adar I (leap years only)
85
    {   29,         29,         29     },           //Adar
86
    {   30,         30,         30     },           //Nisan
87
    {   29,         29,         29     },           //Iyar
88
    {   30,         30,         30     },           //Sivan
89
    {   29,         29,         29     },           //Tammuz
90
    {   30,         30,         30     },           //Av
91
    {   29,         29,         29     },           //Elul
92
};
93
94
/**
95
* The cumulative # of days to the end of each month in a non-leap year
96
* Although this can be calculated from the MONTH_LENGTH table,
97
* keeping it around separately makes some calculations a lot faster
98
*/
99
100
static const int16_t MONTH_START[][3] = {
101
    // Deficient  Normal     Complete
102
    {    0,          0,          0  },          // (placeholder)
103
    {   30,         30,         30  },          // Tishri
104
    {   59,         59,         60  },          // Heshvan
105
    {   88,         89,         90  },          // Kislev
106
    {  117,        118,        119  },          // Tevet
107
    {  147,        148,        149  },          // Shevat
108
    {  147,        148,        149  },          // (Adar I placeholder)
109
    {  176,        177,        178  },          // Adar
110
    {  206,        207,        208  },          // Nisan
111
    {  235,        236,        237  },          // Iyar
112
    {  265,        266,        267  },          // Sivan
113
    {  294,        295,        296  },          // Tammuz
114
    {  324,        325,        326  },          // Av
115
    {  353,        354,        355  },          // Elul
116
};
117
118
/**
119
* The cumulative # of days to the end of each month in a leap year
120
*/
121
static const int16_t  LEAP_MONTH_START[][3] = {
122
    // Deficient  Normal     Complete
123
    {    0,          0,          0  },          // (placeholder)
124
    {   30,         30,         30  },          // Tishri
125
    {   59,         59,         60  },          // Heshvan
126
    {   88,         89,         90  },          // Kislev
127
    {  117,        118,        119  },          // Tevet
128
    {  147,        148,        149  },          // Shevat
129
    {  177,        178,        179  },          // Adar I
130
    {  206,        207,        208  },          // Adar II
131
    {  236,        237,        238  },          // Nisan
132
    {  265,        266,        267  },          // Iyar
133
    {  295,        296,        297  },          // Sivan
134
    {  324,        325,        326  },          // Tammuz
135
    {  354,        355,        356  },          // Av
136
    {  383,        384,        385  },          // Elul
137
};
138
139
// There are 235 months in 19 years cycle.
140
static const int32_t MONTHS_IN_CYCLE = 235;
141
static const int32_t YEARS_IN_CYCLE = 19;
142
143
static icu::CalendarCache *gCache =  nullptr;
144
145
U_CDECL_BEGIN
146
0
static UBool calendar_hebrew_cleanup() {
147
0
    delete gCache;
148
0
    gCache = nullptr;
149
0
    return true;
150
0
}
151
U_CDECL_END
152
153
U_NAMESPACE_BEGIN
154
//-------------------------------------------------------------------------
155
// Constructors...
156
//-------------------------------------------------------------------------
157
158
/**
159
* Constructs a default <code>HebrewCalendar</code> using the current time
160
* in the default time zone with the default locale.
161
* @internal
162
*/
163
HebrewCalendar::HebrewCalendar(const Locale& aLocale, UErrorCode& success)
164
281
:   Calendar(TimeZone::forLocaleOrDefault(aLocale), aLocale, success)
165
166
281
{
167
281
}
168
169
170
6.56k
HebrewCalendar::~HebrewCalendar() {
171
6.56k
}
172
173
34
const char *HebrewCalendar::getType() const {
174
34
    return "hebrew";
175
34
}
176
177
6.38k
HebrewCalendar* HebrewCalendar::clone() const {
178
6.38k
    return new HebrewCalendar(*this);
179
6.38k
}
180
181
6.38k
HebrewCalendar::HebrewCalendar(const HebrewCalendar& other) : Calendar(other) {
182
6.38k
}
183
184
185
//-------------------------------------------------------------------------
186
// Rolling and adding functions overridden from Calendar
187
//
188
// These methods call through to the default implementation in IBMCalendar
189
// for most of the fields and only handle the unusual ones themselves.
190
//-------------------------------------------------------------------------
191
192
/**
193
* Add a signed amount to a specified field, using this calendar's rules.
194
* For example, to add three days to the current date, you can call
195
* <code>add(Calendar.DATE, 3)</code>. 
196
* <p>
197
* When adding to certain fields, the values of other fields may conflict and
198
* need to be changed.  For example, when adding one to the {@link #MONTH MONTH} field
199
* for the date "30 Av 5758", the {@link #DAY_OF_MONTH DAY_OF_MONTH} field
200
* must be adjusted so that the result is "29 Elul 5758" rather than the invalid
201
* "30 Elul 5758".
202
* <p>
203
* This method is able to add to
204
* all fields except for {@link #ERA ERA}, {@link #DST_OFFSET DST_OFFSET},
205
* and {@link #ZONE_OFFSET ZONE_OFFSET}.
206
* <p>
207
* <b>Note:</b> You should always use {@link #roll roll} and add rather
208
* than attempting to perform arithmetic operations directly on the fields
209
* of a <tt>HebrewCalendar</tt>.  Since the {@link #MONTH MONTH} field behaves
210
* discontinuously in non-leap years, simple arithmetic can give invalid results.
211
* <p>
212
* @param field     the time field.
213
* @param amount    the amount to add to the field.
214
*
215
* @exception   IllegalArgumentException if the field is invalid or refers
216
*              to a field that cannot be handled by this method.
217
* @internal
218
*/
219
void HebrewCalendar::add(UCalendarDateFields field, int32_t amount, UErrorCode& status)
220
10.3k
{
221
10.3k
    if(U_FAILURE(status)) {
222
114
        return;
223
114
    }
224
10.2k
    switch (field) {
225
2.94k
  case UCAL_MONTH:
226
4.06k
  case UCAL_ORDINAL_MONTH:
227
4.06k
      {
228
          // We can't just do a set(MONTH, get(MONTH) + amount).  The
229
          // reason is ADAR_1.  Suppose amount is +2 and we land in
230
          // ADAR_1 -- then we have to bump to ADAR_2 aka ADAR.  But
231
          // if amount is -2 and we land in ADAR_1, then we have to
232
          // bump the other way -- down to SHEVAT.  - Alan 11/00
233
4.06k
          int64_t month = get(UCAL_MONTH, status);
234
4.06k
          int32_t year = get(UCAL_YEAR, status);
235
4.06k
          UBool acrossAdar1;
236
4.06k
          if (amount > 0) {
237
2.35k
              acrossAdar1 = (month < ADAR_1); // started before ADAR_1?
238
2.35k
              month += amount;
239
              // We know there are total 235 months in every 19 years. To speed
240
              // up the iteration, we first fast forward in the multiple of 235
241
              // months for 19 years before the iteration which check the leap year.
242
2.35k
              if (month >= MONTHS_IN_CYCLE) {
243
1.80k
                  if (uprv_add32_overflow(year, (month / MONTHS_IN_CYCLE) * YEARS_IN_CYCLE, &year)) {
244
0
                      status = U_ILLEGAL_ARGUMENT_ERROR;
245
0
                      return;
246
0
                  }
247
1.80k
                  month %= MONTHS_IN_CYCLE;
248
1.80k
              }
249
250
19.1k
              for (;;) {
251
19.1k
                  if (acrossAdar1 && month>=ADAR_1 && !isLeapYear(year)) {
252
11.4k
                      ++month;
253
11.4k
                  }
254
19.1k
                  if (month <= ELUL) {
255
2.35k
                      break;
256
2.35k
                  }
257
16.7k
                  month -= ELUL+1;
258
16.7k
                  ++year;
259
16.7k
                  acrossAdar1 = true;
260
16.7k
              }
261
2.35k
          } else {
262
1.70k
              acrossAdar1 = (month > ADAR_1); // started after ADAR_1?
263
1.70k
              month += amount;
264
              // We know there are total 235 months in every 19 years. To speed
265
              // up the iteration, we first fast forward in the multiple of 235
266
              // months for 19 years before the iteration which check the leap year.
267
1.70k
              if (month <= -MONTHS_IN_CYCLE) {
268
1.28k
                  if (uprv_add32_overflow(year, (month / MONTHS_IN_CYCLE) * YEARS_IN_CYCLE, &year)) {
269
0
                      status = U_ILLEGAL_ARGUMENT_ERROR;
270
0
                      return;
271
0
                  }
272
1.28k
                  month %= MONTHS_IN_CYCLE;
273
1.28k
              }
274
15.4k
              for (;;) {
275
15.4k
                  if (acrossAdar1 && month<=ADAR_1 && !isLeapYear(year)) {
276
8.09k
                      --month;
277
8.09k
                  }
278
15.4k
                  if (month >= 0) {
279
1.70k
                      break;
280
1.70k
                  }
281
13.7k
                  month += ELUL+1;
282
13.7k
                  --year;
283
13.7k
                  acrossAdar1 = true;
284
13.7k
              }
285
1.70k
          }
286
4.06k
          set(UCAL_MONTH, month);
287
4.06k
          set(UCAL_YEAR, year);
288
4.06k
          pinField(UCAL_DAY_OF_MONTH, status);
289
4.06k
          break;
290
4.06k
      }
291
292
6.22k
  default:
293
6.22k
      Calendar::add(field, amount, status);
294
6.22k
      break;
295
10.2k
    }
296
10.2k
}
297
298
/**
299
* @deprecated ICU 2.6 use UCalendarDateFields instead of EDateFields
300
*/
301
void HebrewCalendar::add(EDateFields field, int32_t amount, UErrorCode& status)
302
0
{
303
0
    add(static_cast<UCalendarDateFields>(field), amount, status);
304
0
}
305
306
namespace {
307
308
int32_t monthsInYear(int32_t year);
309
310
}  // namespace
311
312
/**
313
* Rolls (up/down) a specified amount time on the given field.  For
314
* example, to roll the current date up by three days, you can call
315
* <code>roll(Calendar.DATE, 3)</code>.  If the
316
* field is rolled past its maximum allowable value, it will "wrap" back
317
* to its minimum and continue rolling.  
318
* For example, calling <code>roll(Calendar.DATE, 10)</code>
319
* on a Hebrew calendar set to "25 Av 5758" will result in the date "5 Av 5758".
320
* <p>
321
* When rolling certain fields, the values of other fields may conflict and
322
* need to be changed.  For example, when rolling the {@link #MONTH MONTH} field
323
* upward by one for the date "30 Av 5758", the {@link #DAY_OF_MONTH DAY_OF_MONTH} field
324
* must be adjusted so that the result is "29 Elul 5758" rather than the invalid
325
* "30 Elul".
326
* <p>
327
* This method is able to roll
328
* all fields except for {@link #ERA ERA}, {@link #DST_OFFSET DST_OFFSET},
329
* and {@link #ZONE_OFFSET ZONE_OFFSET}.  Subclasses may, of course, add support for
330
* additional fields in their overrides of <code>roll</code>.
331
* <p>
332
* <b>Note:</b> You should always use roll and {@link #add add} rather
333
* than attempting to perform arithmetic operations directly on the fields
334
* of a <tt>HebrewCalendar</tt>.  Since the {@link #MONTH MONTH} field behaves
335
* discontinuously in non-leap years, simple arithmetic can give invalid results.
336
* <p>
337
* @param field     the time field.
338
* @param amount    the amount by which the field should be rolled.
339
*
340
* @exception   IllegalArgumentException if the field is invalid or refers
341
*              to a field that cannot be handled by this method.
342
* @internal
343
*/
344
void HebrewCalendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& status)
345
349
{
346
349
    if(U_FAILURE(status)) {
347
0
        return;
348
0
    }
349
349
    switch (field) {
350
74
  case UCAL_MONTH:
351
187
  case UCAL_ORDINAL_MONTH:
352
187
      {
353
187
          int32_t month = get(UCAL_MONTH, status);
354
187
          int32_t year = get(UCAL_YEAR, status);
355
356
187
          UBool leapYear = isLeapYear(year);
357
187
          int32_t yearLength = monthsInYear(year);
358
187
          int32_t newMonth = month + (amount % yearLength);
359
          //
360
          // If it's not a leap year and we're rolling past the missing month
361
          // of ADAR_1, we need to roll an extra month to make up for it.
362
          //
363
187
          if (!leapYear) {
364
151
              if (amount > 0 && month < ADAR_1 && newMonth >= ADAR_1) {
365
19
                  newMonth++;
366
132
              } else if (amount < 0 && month > ADAR_1 && newMonth <= ADAR_1) {
367
10
                  newMonth--;
368
10
              }
369
151
          }
370
187
          set(UCAL_MONTH, (newMonth + 13) % 13);
371
187
          pinField(UCAL_DAY_OF_MONTH, status);
372
187
          return;
373
74
      }
374
162
  default:
375
162
      Calendar::roll(field, amount, status);
376
349
    }
377
349
}
378
379
0
void HebrewCalendar::roll(EDateFields field, int32_t amount, UErrorCode& status) {
380
0
    roll(static_cast<UCalendarDateFields>(field), amount, status);
381
0
}
382
383
//-------------------------------------------------------------------------
384
// Support methods
385
//-------------------------------------------------------------------------
386
387
// Hebrew date calculations are performed in terms of days, hours, and
388
// "parts" (or halakim), which are 1/1080 of an hour, or 3 1/3 seconds.
389
static const int32_t HOUR_PARTS = 1080;
390
static const int32_t DAY_PARTS  = 24*HOUR_PARTS;
391
392
// An approximate value for the length of a lunar month.
393
// It is used to calculate the approximate year and month of a given
394
// absolute date.
395
static const int32_t  MONTH_DAYS = 29;
396
static const int32_t MONTH_FRACT = 12*HOUR_PARTS + 793;
397
static const int32_t MONTH_PARTS = MONTH_DAYS*DAY_PARTS + MONTH_FRACT;
398
399
// The time of the new moon (in parts) on 1 Tishri, year 1 (the epoch)
400
// counting from noon on the day before.  BAHARAD is an abbreviation of
401
// Bet (Monday), Hey (5 hours from sunset), Resh-Daled (204).
402
static const int32_t BAHARAD = 11*HOUR_PARTS + 204;
403
404
namespace {
405
406
/**
407
* Finds the day # of the first day in the given Hebrew year.
408
* To do this, we want to calculate the time of the Tishri 1 new moon
409
* in that year.
410
* <p>
411
* The algorithm here is similar to ones described in a number of
412
* references, including:
413
* <ul>
414
* <li>"Calendrical Calculations", by Nachum Dershowitz & Edward Reingold,
415
*     Cambridge University Press, 1997, pages 85-91.
416
*
417
* <li>Hebrew Calendar Science and Myths,
418
*     <a href="http://www.geocities.com/Athens/1584/">
419
*     http://www.geocities.com/Athens/1584/</a>
420
*
421
* <li>The Calendar FAQ,
422
*      <a href="http://www.faqs.org/faqs/calendars/faq/">
423
*      http://www.faqs.org/faqs/calendars/faq/</a>
424
* </ul>
425
*/
426
int32_t startOfYear(int32_t year, UErrorCode &status)
427
187k
{
428
187k
    ucln_i18n_registerCleanup(UCLN_I18N_HEBREW_CALENDAR, calendar_hebrew_cleanup);
429
187k
    int64_t day = CalendarCache::get(&gCache, year, status);
430
187k
    if(U_FAILURE(status)) {
431
19
        return 0;
432
19
    }
433
434
187k
    if (day == 0) {
435
        // # of months before year
436
36.5k
        int64_t months = ClockMath::floorDivideInt64(
437
36.5k
            (235LL * static_cast<int64_t>(year) - 234LL), 19LL);
438
439
36.5k
        int64_t frac = months * MONTH_FRACT + BAHARAD;  // Fractional part of day #
440
36.5k
        day  = months * 29LL + frac / DAY_PARTS;        // Whole # part of calculation
441
36.5k
        frac = frac % DAY_PARTS;                        // Time of day
442
443
36.5k
        int32_t wd = (day % 7);                        // Day of week (0 == Monday)
444
445
36.5k
        if (wd == 2 || wd == 4 || wd == 6) {
446
            // If the 1st is on Sun, Wed, or Fri, postpone to the next day
447
2.33k
            day += 1;
448
2.33k
            wd = (day % 7);
449
34.2k
        } else if (wd == 1 && frac > 15*HOUR_PARTS+204 && !HebrewCalendar::isLeapYear(year) ) {
450
            // If the new moon falls after 3:11:20am (15h204p from the previous noon)
451
            // on a Tuesday and it is not a leap year, postpone by 2 days.
452
            // This prevents 356-day years.
453
193
            day += 2;
454
193
        }
455
34.0k
        else if (wd == 0 && frac > 21*HOUR_PARTS+589 && HebrewCalendar::isLeapYear(year-1) ) {
456
            // If the new moon falls after 9:32:43 1/3am (21h589p from yesterday noon)
457
            // on a Monday and *last* year was a leap year, postpone by 1 day.
458
            // Prevents 382-day years.
459
48
            day += 1;
460
48
        }
461
36.5k
        if (day > INT32_MAX || day < INT32_MIN) {
462
225
            status = U_ILLEGAL_ARGUMENT_ERROR;
463
225
            return 0;
464
225
        }
465
36.3k
        CalendarCache::put(&gCache, year, static_cast<int32_t>(day), status);
466
36.3k
    }
467
    // Out of range value is alread rejected before putting into cache.
468
187k
    U_ASSERT(INT32_MIN <= day  &&  day <= INT32_MAX);
469
187k
    return day;
470
187k
}
471
472
65.0k
int32_t daysInYear(int32_t eyear, UErrorCode& status) {
473
65.0k
    if (U_FAILURE(status)) {
474
0
       return 0;
475
0
    }
476
65.0k
    return startOfYear(eyear+1, status) - startOfYear(eyear, status);
477
65.0k
}
478
479
/**
480
* Returns the type of a given year.
481
*  0   "Deficient" year with 353 or 383 days
482
*  1   "Normal"    year with 354 or 384 days
483
*  2   "Complete"  year with 355 or 385 days
484
*/
485
int32_t yearType(int32_t year, UErrorCode& status)
486
37.5k
{
487
37.5k
    if (U_FAILURE(status)) {
488
0
        return 0;
489
0
    }
490
37.5k
    int32_t yearLength = daysInYear(year, status);
491
37.5k
    if (U_FAILURE(status)) {
492
19
        return 0;
493
19
    }
494
495
37.5k
    if (yearLength > 380) {
496
11.1k
        yearLength -= 30;        // Subtract length of leap month.
497
11.1k
    }
498
499
37.5k
    int type = 0;
500
501
37.5k
    switch (yearLength) {
502
6.86k
  case 353:
503
6.86k
      type = 0; break;
504
12.0k
  case 354:
505
12.0k
      type = 1; break;
506
18.5k
  case 355:
507
18.5k
      type = 2; break;
508
33
  default:
509
      //throw new RuntimeException("Illegal year length " + yearLength + " in year " + year);
510
33
      type = 1;
511
37.5k
    }
512
37.5k
    return type;
513
37.5k
}
514
515
}  // namespace
516
   //
517
/**
518
* Determine whether a given Hebrew year is a leap year
519
*
520
* The rule here is that if (year % 19) == 0, 3, 6, 8, 11, 14, or 17.
521
* The formula below performs the same test, believe it or not.
522
*/
523
43.3M
UBool HebrewCalendar::isLeapYear(int32_t year) {
524
    //return (year * 12 + 17) % 19 >= 12;
525
43.3M
    int64_t x = (year*12LL + 17) % YEARS_IN_CYCLE;
526
43.3M
    return x >= ((x < 0) ? -7 : 12);
527
43.3M
}
528
529
namespace{
530
531
43.2M
int32_t monthsInYear(int32_t year) {
532
43.2M
    return HebrewCalendar::isLeapYear(year) ? 13 : 12;
533
43.2M
}
534
535
}  // namespace
536
537
//-------------------------------------------------------------------------
538
// Calendar framework
539
//-------------------------------------------------------------------------
540
541
/**
542
* @internal
543
*/
544
72.3k
int32_t HebrewCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const {
545
72.3k
    return LIMITS[field][limitType];
546
72.3k
}
547
548
/**
549
* Returns the length of the given month in the given year
550
* @internal
551
*/
552
5.88k
int32_t HebrewCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month, UErrorCode& status) const {
553
5.88k
    if(U_FAILURE(status)) {
554
102
        return 0;
555
102
    }
556
    // Resolve out-of-range months.  This is necessary in order to
557
    // obtain the correct year.  We correct to
558
    // a 12- or 13-month year (add/subtract 12 or 13, depending
559
    // on the year) but since we _always_ number from 0..12, and
560
    // the leap year determines whether or not month 5 (Adar 1)
561
    // is present, we allow 0..12 in any given year.
562
25.2M
    while (month < 0) {
563
25.2M
        month += monthsInYear(--extendedYear);
564
25.2M
    }
565
    // Careful: allow 0..12 in all years
566
17.9M
    while (month > 12) {
567
17.9M
        month -= monthsInYear(extendedYear++);
568
17.9M
    }
569
570
5.78k
    switch (month) {
571
298
    case HESHVAN:
572
1.11k
    case KISLEV:
573
1.11k
      {
574
          // These two month lengths can vary
575
1.11k
          int32_t type = yearType(extendedYear, status);
576
1.11k
          if(U_FAILURE(status)) {
577
0
              return 0;
578
0
          }
579
1.11k
          return MONTH_LENGTH[month][type];
580
1.11k
      }
581
582
4.67k
    default:
583
      // The rest are a fixed length
584
4.67k
      return MONTH_LENGTH[month][0];
585
5.78k
    }
586
5.78k
}
587
588
/**
589
* Returns the number of days in the given Hebrew year
590
* @internal
591
*/
592
27.4k
int32_t HebrewCalendar::handleGetYearLength(int32_t eyear, UErrorCode& status) const {
593
27.4k
    return daysInYear(eyear, status);
594
27.4k
}
595
596
0
void HebrewCalendar::validateField(UCalendarDateFields field, UErrorCode &status) {
597
0
    if ((field == UCAL_MONTH || field == UCAL_ORDINAL_MONTH)
598
0
        && !isLeapYear(handleGetExtendedYear(status)) && internalGetMonth(status) == ADAR_1) {
599
0
        if (U_FAILURE(status)) {
600
0
            return;
601
0
        }
602
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
603
0
        return;
604
0
    }
605
0
    Calendar::validateField(field, status);
606
0
}
607
//-------------------------------------------------------------------------
608
// Functions for converting from milliseconds to field values
609
//-------------------------------------------------------------------------
610
611
/**
612
* Subclasses may override this method to compute several fields
613
* specific to each calendar system.  These are:
614
*
615
* <ul><li>ERA
616
* <li>YEAR
617
* <li>MONTH
618
* <li>DAY_OF_MONTH
619
* <li>DAY_OF_YEAR
620
* <li>EXTENDED_YEAR</ul>
621
* 
622
* Subclasses can refer to the DAY_OF_WEEK and DOW_LOCAL fields,
623
* which will be set when this method is called.  Subclasses can
624
* also call the getGregorianXxx() methods to obtain Gregorian
625
* calendar equivalents for the given Julian day.
626
*
627
* <p>In addition, subclasses should compute any subclass-specific
628
* fields, that is, fields from BASE_FIELD_COUNT to
629
* getFieldCount() - 1.
630
* @internal
631
*/
632
27.4k
void HebrewCalendar::handleComputeFields(int32_t julianDay, UErrorCode &status) {
633
27.4k
    if (U_FAILURE(status)) {
634
29
        return;
635
29
    }
636
27.4k
    int32_t d = julianDay - 347997;
637
27.4k
    double m = ClockMath::floorDivide((d * static_cast<double>(DAY_PARTS)), static_cast<double>(MONTH_PARTS)); // Months (approx)
638
27.4k
    int32_t year = static_cast<int32_t>(ClockMath::floorDivide((19. * m + 234.), 235.) + 1.); // Years (approx)
639
27.4k
    int32_t ys  = startOfYear(year, status);                   // 1st day of year
640
27.4k
    if (U_FAILURE(status)) {
641
0
        return;
642
0
    }
643
27.4k
    int32_t dayOfYear = (d - ys);
644
645
    // Because of the postponement rules, it's possible to guess wrong.  Fix it.
646
45.4k
    while (dayOfYear < 1) {
647
18.0k
        year--;
648
18.0k
        ys  = startOfYear(year, status);
649
18.0k
        if (U_FAILURE(status)) {
650
0
            return;
651
0
        }
652
18.0k
        dayOfYear = (d - ys);
653
18.0k
    }
654
655
    // Now figure out which month we're in, and the date within that month
656
27.4k
    int32_t type = yearType(year, status);
657
27.4k
    if (U_FAILURE(status)) {
658
0
        return;
659
0
    }
660
27.4k
    UBool isLeap = isLeapYear(year);
661
662
27.4k
    int32_t month = 0;
663
27.4k
    int32_t momax = UPRV_LENGTHOF(MONTH_START);
664
157k
    while (month < momax &&
665
157k
           dayOfYear > (  isLeap ? LEAP_MONTH_START[month][type] : MONTH_START[month][type] ) ) {
666
129k
        month++;
667
129k
    }
668
27.4k
    if (month >= momax || month<=0) {
669
        // TODO: I found dayOfYear could be out of range when
670
        // a large value is set to julianDay.  I patched startOfYear
671
        // to reduce the chace, but it could be still reproduced either
672
        // by startOfYear or other places.  For now, we check
673
        // the month is in valid range to avoid out of array index
674
        // access problem here.  However, we need to carefully review
675
        // the calendar implementation to check the extreme limit of
676
        // each calendar field and the code works well for any values
677
        // in the valid value range.  -yoshito
678
20
        status = U_ILLEGAL_ARGUMENT_ERROR;
679
20
        return;
680
20
    }
681
27.4k
    month--;
682
27.4k
    int dayOfMonth = dayOfYear - (isLeap ? LEAP_MONTH_START[month][type] : MONTH_START[month][type]);
683
684
27.4k
    internalSet(UCAL_ERA, 0);
685
    // Check out of bound year
686
27.4k
    int32_t min_year = handleGetLimit(UCAL_EXTENDED_YEAR, UCAL_LIMIT_MINIMUM);
687
27.4k
    if (year < min_year) {
688
2.17k
        if (!isLenient()) {
689
0
            status = U_ILLEGAL_ARGUMENT_ERROR;
690
0
            return;
691
0
        }
692
2.17k
        year = min_year;
693
2.17k
    }
694
27.4k
    int32_t max_year = handleGetLimit(UCAL_EXTENDED_YEAR, UCAL_LIMIT_MAXIMUM);
695
27.4k
    if (max_year < year) {
696
1.48k
        if (!isLenient()) {
697
0
            status = U_ILLEGAL_ARGUMENT_ERROR;
698
0
            return;
699
0
        }
700
1.48k
        year = max_year;
701
1.48k
    }
702
27.4k
    internalSet(UCAL_YEAR, year);
703
27.4k
    internalSet(UCAL_EXTENDED_YEAR, year);
704
27.4k
    int32_t ordinal_month = month;
705
27.4k
    if (!isLeap && ordinal_month > ADAR_1) {
706
5.34k
        ordinal_month--;
707
5.34k
    }
708
27.4k
    internalSet(UCAL_ORDINAL_MONTH, ordinal_month);
709
27.4k
    internalSet(UCAL_MONTH, month);
710
27.4k
    internalSet(UCAL_DAY_OF_MONTH, dayOfMonth);
711
27.4k
    internalSet(UCAL_DAY_OF_YEAR, dayOfYear);
712
27.4k
}
713
714
//-------------------------------------------------------------------------
715
// Functions for converting from field values to milliseconds
716
//-------------------------------------------------------------------------
717
718
/**
719
* @internal
720
*/
721
11.6k
int32_t HebrewCalendar::handleGetExtendedYear(UErrorCode& status ) {
722
11.6k
    if (U_FAILURE(status)) {
723
0
        return 0;
724
0
    }
725
11.6k
    if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) {
726
3.00k
        return internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1
727
3.00k
    }
728
8.61k
    return internalGet(UCAL_YEAR, 1); // Default to year 1
729
11.6k
}
730
731
/**
732
* Return JD of start of given month/year.
733
* @internal
734
*/
735
int64_t HebrewCalendar::handleComputeMonthStart(
736
12.1k
    int32_t eyear, int32_t month, UBool /*useMonth*/, UErrorCode& status) const {
737
12.1k
    if (U_FAILURE(status)) {
738
0
        return 0;
739
0
    }
740
    // Resolve out-of-range months.  This is necessary in order to
741
    // obtain the correct year.  We correct to
742
    // a 12- or 13-month year (add/subtract 12 or 13, depending
743
    // on the year) but since we _always_ number from 0..12, and
744
    // the leap year determines whether or not month 5 (Adar 1)
745
    // is present, we allow 0..12 in any given year.
746
747
    // The month could be in large value, we first roll 235 months to 19 years
748
    // before the while loop.
749
12.1k
    if (month <= -MONTHS_IN_CYCLE || month >= MONTHS_IN_CYCLE) {
750
193
        if (uprv_add32_overflow(eyear, (month / MONTHS_IN_CYCLE) * YEARS_IN_CYCLE, &eyear)) {
751
10
            status = U_ILLEGAL_ARGUMENT_ERROR;
752
10
            return 0;
753
10
        }
754
183
        month %= MONTHS_IN_CYCLE;
755
183
    }
756
13.0k
    while (month < 0) {
757
939
        if (uprv_add32_overflow(eyear, -1, &eyear) ||
758
929
            uprv_add32_overflow(month, monthsInYear(eyear), &month)) {
759
10
            status = U_ILLEGAL_ARGUMENT_ERROR;
760
10
            return 0;
761
10
        }
762
939
    }
763
    // Careful: allow 0..12 in all years
764
13.3k
    while (month > 12) {
765
1.22k
        if (uprv_add32_overflow(month, -monthsInYear(eyear), &month) ||
766
1.22k
            uprv_add32_overflow(eyear, 1, &eyear)) {
767
0
            status = U_ILLEGAL_ARGUMENT_ERROR;
768
0
            return 0;
769
0
        }
770
1.22k
    }
771
772
12.0k
    int64_t day = startOfYear(eyear, status);
773
774
12.0k
    if(U_FAILURE(status)) {
775
206
        return 0;
776
206
    }
777
778
11.8k
    if (month != 0) {
779
9.01k
        int32_t type = yearType(eyear, status);
780
9.01k
        if (U_FAILURE(status)) {
781
19
            return 0;
782
19
        }
783
8.99k
        if (isLeapYear(eyear)) {
784
3.73k
            day += LEAP_MONTH_START[month][type];
785
5.26k
        } else {
786
5.26k
            day += MONTH_START[month][type];
787
5.26k
        }
788
8.99k
    }
789
790
11.8k
    return day + 347997LL;
791
11.8k
}
792
793
IMPL_SYSTEM_DEFAULT_CENTURY(HebrewCalendar, "@calendar=hebrew")
794
795
0
bool HebrewCalendar::inTemporalLeapYear(UErrorCode& status) const {
796
0
    if (U_FAILURE(status)) {
797
0
        return false;
798
0
    }
799
0
    int32_t eyear = get(UCAL_EXTENDED_YEAR, status);
800
0
    if (U_FAILURE(status)) {
801
0
        return false;
802
0
    }
803
0
    return isLeapYear(eyear);
804
0
}
805
806
static const char * const gTemporalMonthCodesForHebrew[] = {
807
    "M01", "M02", "M03", "M04", "M05", "M05L", "M06",
808
    "M07", "M08", "M09", "M10", "M11", "M12", nullptr
809
};
810
811
0
const char* HebrewCalendar::getTemporalMonthCode(UErrorCode& status) const {
812
0
    int32_t month = get(UCAL_MONTH, status);
813
0
    if (U_FAILURE(status)) {
814
0
        return nullptr;
815
0
    }
816
0
    return gTemporalMonthCodesForHebrew[month];
817
0
}
818
819
void HebrewCalendar::setTemporalMonthCode(const char* code, UErrorCode& status )
820
0
{
821
0
    if (U_FAILURE(status)) {
822
0
        return;
823
0
    }
824
0
    int32_t len = static_cast<int32_t>(uprv_strlen(code));
825
0
    if (len == 3 || len == 4) {
826
0
        for (int m = 0; gTemporalMonthCodesForHebrew[m] != nullptr; m++) {
827
0
            if (uprv_strcmp(code, gTemporalMonthCodesForHebrew[m]) == 0) {
828
0
                set(UCAL_MONTH, m);
829
0
                return;
830
0
            }
831
0
        }
832
0
    }
833
0
    status = U_ILLEGAL_ARGUMENT_ERROR;
834
0
}
835
836
11.7k
int32_t HebrewCalendar::internalGetMonth(UErrorCode& status) const {
837
11.7k
    if (U_FAILURE(status)) {
838
0
        return 0;
839
0
    }
840
11.7k
    if (resolveFields(kMonthPrecedence) == UCAL_ORDINAL_MONTH) {
841
189
        int32_t ordinalMonth = internalGet(UCAL_ORDINAL_MONTH);
842
189
        HebrewCalendar* nonConstThis = const_cast<HebrewCalendar*>(this); // cast away const
843
844
189
        int32_t year = nonConstThis->handleGetExtendedYear(status);
845
189
        if (U_FAILURE(status)) {
846
0
            return 0;
847
0
        }
848
189
        if (isLeapYear(year) || ordinalMonth <= ADAR_1) {
849
84
            return ordinalMonth;
850
84
        }
851
105
        if (!uprv_add32_overflow(ordinalMonth, 1, &ordinalMonth)) {
852
87
            return ordinalMonth;
853
87
        }
854
105
    }
855
11.5k
    return Calendar::internalGetMonth(status);
856
11.7k
}
857
858
0
int32_t HebrewCalendar::getRelatedYearDifference() const {
859
0
    constexpr int32_t kHebrewCalendarRelatedYearDifference = -3760;
860
0
    return kHebrewCalendarRelatedYearDifference;
861
0
}
862
863
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(HebrewCalendar)
864
865
U_NAMESPACE_END
866
867
#endif // UCONFIG_NO_FORMATTING
868