Coverage Report

Created: 2026-09-28 06:10

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
287
:   Calendar(TimeZone::forLocaleOrDefault(aLocale), aLocale, success)
165
166
287
{
167
287
}
168
169
170
7.34k
HebrewCalendar::~HebrewCalendar() {
171
7.34k
}
172
173
22
const char *HebrewCalendar::getType() const {
174
22
    return "hebrew";
175
22
}
176
177
7.16k
HebrewCalendar* HebrewCalendar::clone() const {
178
7.16k
    return new HebrewCalendar(*this);
179
7.16k
}
180
181
7.16k
HebrewCalendar::HebrewCalendar(const HebrewCalendar& other) : Calendar(other) {
182
7.16k
}
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.8k
{
221
10.8k
    if(U_FAILURE(status)) {
222
101
        return;
223
101
    }
224
10.7k
    switch (field) {
225
3.11k
  case UCAL_MONTH:
226
4.28k
  case UCAL_ORDINAL_MONTH:
227
4.28k
      {
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.28k
          int64_t month = get(UCAL_MONTH, status);
234
4.28k
          int32_t year = get(UCAL_YEAR, status);
235
4.28k
          UBool acrossAdar1;
236
4.28k
          if (amount > 0) {
237
2.30k
              acrossAdar1 = (month < ADAR_1); // started before ADAR_1?
238
2.30k
              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.30k
              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
18.8k
              for (;;) {
251
18.8k
                  if (acrossAdar1 && month>=ADAR_1 && !isLeapYear(year)) {
252
11.2k
                      ++month;
253
11.2k
                  }
254
18.8k
                  if (month <= ELUL) {
255
2.30k
                      break;
256
2.30k
                  }
257
16.5k
                  month -= ELUL+1;
258
16.5k
                  ++year;
259
16.5k
                  acrossAdar1 = true;
260
16.5k
              }
261
2.30k
          } else {
262
1.98k
              acrossAdar1 = (month > ADAR_1); // started after ADAR_1?
263
1.98k
              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.98k
              if (month <= -MONTHS_IN_CYCLE) {
268
1.51k
                  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.51k
                  month %= MONTHS_IN_CYCLE;
273
1.51k
              }
274
17.5k
              for (;;) {
275
17.5k
                  if (acrossAdar1 && month<=ADAR_1 && !isLeapYear(year)) {
276
9.40k
                      --month;
277
9.40k
                  }
278
17.5k
                  if (month >= 0) {
279
1.98k
                      break;
280
1.98k
                  }
281
15.6k
                  month += ELUL+1;
282
15.6k
                  --year;
283
15.6k
                  acrossAdar1 = true;
284
15.6k
              }
285
1.98k
          }
286
4.28k
          set(UCAL_MONTH, month);
287
4.28k
          set(UCAL_YEAR, year);
288
4.28k
          pinField(UCAL_DAY_OF_MONTH, status);
289
4.28k
          break;
290
4.28k
      }
291
292
6.50k
  default:
293
6.50k
      Calendar::add(field, amount, status);
294
6.50k
      break;
295
10.7k
    }
296
10.7k
}
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
393
{
346
393
    if(U_FAILURE(status)) {
347
0
        return;
348
0
    }
349
393
    switch (field) {
350
84
  case UCAL_MONTH:
351
203
  case UCAL_ORDINAL_MONTH:
352
203
      {
353
203
          int32_t month = get(UCAL_MONTH, status);
354
203
          int32_t year = get(UCAL_YEAR, status);
355
356
203
          UBool leapYear = isLeapYear(year);
357
203
          int32_t yearLength = monthsInYear(year);
358
203
          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
203
          if (!leapYear) {
364
173
              if (amount > 0 && month < ADAR_1 && newMonth >= ADAR_1) {
365
20
                  newMonth++;
366
153
              } else if (amount < 0 && month > ADAR_1 && newMonth <= ADAR_1) {
367
12
                  newMonth--;
368
12
              }
369
173
          }
370
203
          set(UCAL_MONTH, (newMonth + 13) % 13);
371
203
          pinField(UCAL_DAY_OF_MONTH, status);
372
203
          return;
373
84
      }
374
190
  default:
375
190
      Calendar::roll(field, amount, status);
376
393
    }
377
393
}
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
201k
{
428
201k
    ucln_i18n_registerCleanup(UCLN_I18N_HEBREW_CALENDAR, calendar_hebrew_cleanup);
429
201k
    int64_t day = CalendarCache::get(&gCache, year, status);
430
201k
    if(U_FAILURE(status)) {
431
18
        return 0;
432
18
    }
433
434
201k
    if (day == 0) {
435
        // # of months before year
436
35.5k
        int64_t months = ClockMath::floorDivideInt64(
437
35.5k
            (235LL * static_cast<int64_t>(year) - 234LL), 19LL);
438
439
35.5k
        int64_t frac = months * MONTH_FRACT + BAHARAD;  // Fractional part of day #
440
35.5k
        int64_t fracDiv = ClockMath::floorDivideInt64(frac, DAY_PARTS);
441
35.5k
        day  = months * 29LL + fracDiv;                 // Whole # part of calculation
442
35.5k
        frac = frac - (fracDiv * DAY_PARTS);            // Time of day
443
444
35.5k
        int32_t wd = static_cast<int32_t>((day % 7 + 7) % 7); // Day of week (0 == Monday)
445
446
35.5k
        if (wd == 2 || wd == 4 || wd == 6) {
447
            // If the 1st is on Sun, Wed, or Fri, postpone to the next day
448
3.29k
            day += 1;
449
3.29k
            wd = static_cast<int32_t>((day % 7 + 7) % 7);
450
32.2k
        } else if (wd == 1 && frac >= 15*HOUR_PARTS+204 && !HebrewCalendar::isLeapYear(year) ) {
451
            // If the new moon falls after 3:11:20am (15h204p from the previous noon)
452
            // on a Tuesday and it is not a leap year, postpone by 2 days.
453
            // This prevents 356-day years.
454
253
            day += 2;
455
253
        }
456
32.0k
        else if (wd == 0 && frac >= 21*HOUR_PARTS+589 && HebrewCalendar::isLeapYear(year-1) ) {
457
            // If the new moon falls after 9:32:43 1/3am (21h589p from yesterday noon)
458
            // on a Monday and *last* year was a leap year, postpone by 1 day.
459
            // Prevents 382-day years.
460
63
            day += 1;
461
63
        }
462
35.5k
        if (day > INT32_MAX || day < INT32_MIN) {
463
186
            status = U_ILLEGAL_ARGUMENT_ERROR;
464
186
            return 0;
465
186
        }
466
35.3k
        CalendarCache::put(&gCache, year, static_cast<int32_t>(day), status);
467
35.3k
    }
468
    // Out of range value is alread rejected before putting into cache.
469
200k
    U_ASSERT(INT32_MIN <= day  &&  day <= INT32_MAX);
470
200k
    return day;
471
201k
}
472
473
69.7k
int32_t daysInYear(int32_t eyear, UErrorCode& status) {
474
69.7k
    if (U_FAILURE(status)) {
475
0
       return 0;
476
0
    }
477
69.7k
    return startOfYear(eyear+1, status) - startOfYear(eyear, status);
478
69.7k
}
479
480
/**
481
* Returns the type of a given year.
482
*  0   "Deficient" year with 353 or 383 days
483
*  1   "Normal"    year with 354 or 384 days
484
*  2   "Complete"  year with 355 or 385 days
485
*/
486
int32_t yearType(int32_t year, UErrorCode& status)
487
40.3k
{
488
40.3k
    if (U_FAILURE(status)) {
489
0
        return 0;
490
0
    }
491
40.3k
    int32_t yearLength = daysInYear(year, status);
492
40.3k
    if (U_FAILURE(status)) {
493
18
        return 0;
494
18
    }
495
496
40.3k
    if (yearLength > 380) {
497
13.1k
        yearLength -= 30;        // Subtract length of leap month.
498
13.1k
    }
499
500
40.3k
    int type = 0;
501
502
40.3k
    switch (yearLength) {
503
9.26k
  case 353:
504
9.26k
      type = 0; break;
505
9.22k
  case 354:
506
9.22k
      type = 1; break;
507
21.8k
  case 355:
508
21.8k
      type = 2; break;
509
0
  default:
510
      //throw new RuntimeException("Illegal year length " + yearLength + " in year " + year);
511
0
      type = 1;
512
40.3k
    }
513
40.3k
    return type;
514
40.3k
}
515
516
}  // namespace
517
   //
518
/**
519
* Determine whether a given Hebrew year is a leap year
520
*
521
* The rule here is that if (year % 19) == 0, 3, 6, 8, 11, 14, or 17.
522
* The formula below performs the same test, believe it or not.
523
*/
524
76.5M
UBool HebrewCalendar::isLeapYear(int32_t year) {
525
76.5M
    int64_t x = ((year*12LL + 17) % YEARS_IN_CYCLE + YEARS_IN_CYCLE) % YEARS_IN_CYCLE;
526
76.5M
    return x >= 12;
527
76.5M
}
528
529
namespace{
530
531
76.4M
int32_t monthsInYear(int32_t year) {
532
76.4M
    return HebrewCalendar::isLeapYear(year) ? 13 : 12;
533
76.4M
}
534
535
}  // namespace
536
537
//-------------------------------------------------------------------------
538
// Calendar framework
539
//-------------------------------------------------------------------------
540
541
/**
542
* @internal
543
*/
544
78.5k
int32_t HebrewCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const {
545
78.5k
    return LIMITS[field][limitType];
546
78.5k
}
547
548
/**
549
* Returns the length of the given month in the given year
550
* @internal
551
*/
552
6.66k
int32_t HebrewCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month, UErrorCode& status) const {
553
6.66k
    if(U_FAILURE(status)) {
554
89
        return 0;
555
89
    }
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
34.5M
    while (month < 0) {
563
34.5M
        month += monthsInYear(--extendedYear);
564
34.5M
    }
565
    // Careful: allow 0..12 in all years
566
41.9M
    while (month > 12) {
567
41.9M
        month -= monthsInYear(extendedYear++);
568
41.9M
    }
569
570
6.57k
    switch (month) {
571
485
    case HESHVAN:
572
1.23k
    case KISLEV:
573
1.23k
      {
574
          // These two month lengths can vary
575
1.23k
          int32_t type = yearType(extendedYear, status);
576
1.23k
          if(U_FAILURE(status)) {
577
0
              return 0;
578
0
          }
579
1.23k
          return MONTH_LENGTH[month][type];
580
1.23k
      }
581
582
5.34k
    default:
583
      // The rest are a fixed length
584
5.34k
      return MONTH_LENGTH[month][0];
585
6.57k
    }
586
6.57k
}
587
588
/**
589
* Returns the number of days in the given Hebrew year
590
* @internal
591
*/
592
29.3k
int32_t HebrewCalendar::handleGetYearLength(int32_t eyear, UErrorCode& status) const {
593
29.3k
    return daysInYear(eyear, status);
594
29.3k
}
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
29.3k
void HebrewCalendar::handleComputeFields(int32_t julianDay, UErrorCode &status) {
633
29.3k
    if (U_FAILURE(status)) {
634
36
        return;
635
36
    }
636
29.3k
    int32_t d = julianDay - 347997;
637
29.3k
    double m = ClockMath::floorDivide((d * static_cast<double>(DAY_PARTS)), static_cast<double>(MONTH_PARTS)); // Months (approx)
638
29.3k
    int32_t year = static_cast<int32_t>(ClockMath::floorDivide((19. * m + 234.), 235.) + 1.); // Years (approx)
639
29.3k
    int32_t ys  = startOfYear(year, status);                   // 1st day of year
640
29.3k
    if (U_FAILURE(status)) {
641
0
        return;
642
0
    }
643
29.3k
    int32_t dayOfYear = (d - ys);
644
645
    // Because of the postponement rules, it's possible to guess wrong.  Fix it.
646
47.8k
    while (dayOfYear < 1) {
647
18.5k
        year--;
648
18.5k
        ys  = startOfYear(year, status);
649
18.5k
        if (U_FAILURE(status)) {
650
0
            return;
651
0
        }
652
18.5k
        dayOfYear = (d - ys);
653
18.5k
    }
654
655
    // Now figure out which month we're in, and the date within that month
656
29.3k
    int32_t type = yearType(year, status);
657
29.3k
    if (U_FAILURE(status)) {
658
0
        return;
659
0
    }
660
29.3k
    UBool isLeap = isLeapYear(year);
661
662
29.3k
    int32_t month = 0;
663
29.3k
    int32_t momax = UPRV_LENGTHOF(MONTH_START);
664
162k
    while (month < momax &&
665
162k
           dayOfYear > (  isLeap ? LEAP_MONTH_START[month][type] : MONTH_START[month][type] ) ) {
666
132k
        month++;
667
132k
    }
668
29.3k
    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
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
679
0
        return;
680
0
    }
681
29.3k
    month--;
682
29.3k
    int dayOfMonth = dayOfYear - (isLeap ? LEAP_MONTH_START[month][type] : MONTH_START[month][type]);
683
684
29.3k
    internalSet(UCAL_ERA, 0);
685
    // Check out of bound year
686
29.3k
    int32_t min_year = handleGetLimit(UCAL_EXTENDED_YEAR, UCAL_LIMIT_MINIMUM);
687
29.3k
    if (year < min_year) {
688
2.63k
        if (!isLenient()) {
689
0
            status = U_ILLEGAL_ARGUMENT_ERROR;
690
0
            return;
691
0
        }
692
2.63k
        year = min_year;
693
2.63k
    }
694
29.3k
    int32_t max_year = handleGetLimit(UCAL_EXTENDED_YEAR, UCAL_LIMIT_MAXIMUM);
695
29.3k
    if (max_year < year) {
696
1.58k
        if (!isLenient()) {
697
0
            status = U_ILLEGAL_ARGUMENT_ERROR;
698
0
            return;
699
0
        }
700
1.58k
        year = max_year;
701
1.58k
    }
702
29.3k
    internalSet(UCAL_YEAR, year);
703
29.3k
    internalSet(UCAL_EXTENDED_YEAR, year);
704
29.3k
    int32_t ordinal_month = month;
705
29.3k
    if (!isLeap && ordinal_month > ADAR_1) {
706
5.35k
        ordinal_month--;
707
5.35k
    }
708
29.3k
    internalSet(UCAL_ORDINAL_MONTH, ordinal_month);
709
29.3k
    internalSet(UCAL_MONTH, month);
710
29.3k
    internalSet(UCAL_DAY_OF_MONTH, dayOfMonth);
711
29.3k
    internalSet(UCAL_DAY_OF_YEAR, dayOfYear);
712
29.3k
}
713
714
//-------------------------------------------------------------------------
715
// Functions for converting from field values to milliseconds
716
//-------------------------------------------------------------------------
717
718
/**
719
* @internal
720
*/
721
13.1k
int32_t HebrewCalendar::handleGetExtendedYear(UErrorCode& status ) {
722
13.1k
    if (U_FAILURE(status)) {
723
0
        return 0;
724
0
    }
725
13.1k
    if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) {
726
3.45k
        return internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1
727
3.45k
    }
728
9.70k
    return internalGet(UCAL_YEAR, 1); // Default to year 1
729
13.1k
}
730
731
/**
732
* Return JD of start of given month/year.
733
* @internal
734
*/
735
int64_t HebrewCalendar::handleComputeMonthStart(
736
13.6k
    int32_t eyear, int32_t month, UBool /*useMonth*/, UErrorCode& status) const {
737
13.6k
    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
13.6k
    if (month <= -MONTHS_IN_CYCLE || month >= MONTHS_IN_CYCLE) {
750
197
        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
187
        month %= MONTHS_IN_CYCLE;
755
187
    }
756
14.7k
    while (month < 0) {
757
1.12k
        if (uprv_add32_overflow(eyear, -1, &eyear) ||
758
1.11k
            uprv_add32_overflow(month, monthsInYear(eyear), &month)) {
759
10
            status = U_ILLEGAL_ARGUMENT_ERROR;
760
10
            return 0;
761
10
        }
762
1.12k
    }
763
    // Careful: allow 0..12 in all years
764
14.5k
    while (month > 12) {
765
886
        if (uprv_add32_overflow(month, -monthsInYear(eyear), &month) ||
766
886
            uprv_add32_overflow(eyear, 1, &eyear)) {
767
0
            status = U_ILLEGAL_ARGUMENT_ERROR;
768
0
            return 0;
769
0
        }
770
886
    }
771
772
13.6k
    int64_t day = startOfYear(eyear, status);
773
774
13.6k
    if(U_FAILURE(status)) {
775
168
        return 0;
776
168
    }
777
778
13.4k
    if (month != 0) {
779
9.78k
        int32_t type = yearType(eyear, status);
780
9.78k
        if (U_FAILURE(status)) {
781
18
            return 0;
782
18
        }
783
9.76k
        if (isLeapYear(eyear)) {
784
4.15k
            day += LEAP_MONTH_START[month][type];
785
5.61k
        } else {
786
5.61k
            day += MONTH_START[month][type];
787
5.61k
        }
788
9.76k
    }
789
790
13.4k
    return day + 347997LL;
791
13.4k
}
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
13.2k
int32_t HebrewCalendar::internalGetMonth(UErrorCode& status) const {
837
13.2k
    if (U_FAILURE(status)) {
838
0
        return 0;
839
0
    }
840
13.2k
    if (resolveFields(kMonthPrecedence) == UCAL_ORDINAL_MONTH) {
841
177
        int32_t ordinalMonth = internalGet(UCAL_ORDINAL_MONTH);
842
177
        HebrewCalendar* nonConstThis = const_cast<HebrewCalendar*>(this); // cast away const
843
844
177
        int32_t year = nonConstThis->handleGetExtendedYear(status);
845
177
        if (U_FAILURE(status)) {
846
0
            return 0;
847
0
        }
848
177
        if (isLeapYear(year) || ordinalMonth <= ADAR_1) {
849
100
            return ordinalMonth;
850
100
        }
851
77
        if (!uprv_add32_overflow(ordinalMonth, 1, &ordinalMonth)) {
852
59
            return ordinalMonth;
853
59
        }
854
77
    }
855
13.1k
    return Calendar::internalGetMonth(status);
856
13.2k
}
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