< Summary

Line coverage
0%
Covered lines: 0
Uncovered lines: 374
Coverable lines: 374
Total lines: 897
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 106
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Private.CoreLib/src/System/Globalization/HebrewCalendar.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Diagnostics;
 5
 6namespace System.Globalization
 7{
 8    /// <remarks>
 9    /// Rules for the Hebrew calendar:
 10    ///   - The Hebrew calendar is both a Lunar (months) and Solar (years)
 11    ///       calendar, but allows for a week of seven days.
 12    ///   - Days begin at sunset.
 13    ///   - Leap Years occur in the 3, 6, 8, 11, 14, 17, &amp; 19th years of a
 14    ///       19-year cycle.  Year = leap iff ((7y+1) mod 19 &lt; 7).
 15    ///   - There are 12 months in a common year and 13 months in a leap year.
 16    ///   - In a common year, the 6th month, Adar, has 29 days.  In a leap
 17    ///       year, the 6th month, Adar I, has 30 days and the leap month,
 18    ///       Adar II, has 29 days.
 19    ///   - Common years have 353-355 days.  Leap years have 383-385 days.
 20    ///   - The Hebrew new year (Rosh HaShanah) begins on the 1st of Tishri,
 21    ///       the 7th month in the list below.
 22    ///       - The new year may not begin on Sunday, Wednesday, or Friday.
 23    ///       - If the new year would fall on a Tuesday and the conjunction of
 24    ///           the following year were at midday or later, the new year is
 25    ///           delayed until Thursday.
 26    ///       - If the new year would fall on a Monday after a leap year, the
 27    ///           new year is delayed until Tuesday.
 28    ///   - The length of the 8th and 9th months vary from year to year,
 29    ///       depending on the overall length of the year.
 30    ///       - The length of a year is determined by the dates of the new
 31    ///           years (Tishri 1) preceding and following the year in question.
 32    ///       - The 2th month is long (30 days) if the year has 355 or 385 days.
 33    ///       - The 3th month is short (29 days) if the year has 353 or 383 days.
 34    ///   - The Hebrew months are:
 35    ///       1.  Tishri        (30 days)
 36    ///       2.  Heshvan       (29 or 30 days)
 37    ///       3.  Kislev        (29 or 30 days)
 38    ///       4.  Teveth        (29 days)
 39    ///       5.  Shevat        (30 days)
 40    ///       6.  Adar I        (30 days)
 41    ///       7.  Adar {II}     (29 days, this only exists if that year is a leap year)
 42    ///       8.  Nisan         (30 days)
 43    ///       9.  Iyyar         (29 days)
 44    ///       10. Sivan         (30 days)
 45    ///       11. Tammuz        (29 days)
 46    ///       12. Av            (30 days)
 47    ///       13. Elul          (29 days)
 48    /// Calendar support range:
 49    ///     Calendar    Minimum     Maximum
 50    ///     ==========  ==========  ==========
 51    ///     Gregorian   1583/01/01  2239/09/29
 52    ///     Hebrew      5343/04/07  5999/13/29
 53    ///
 54    /// Includes CHebrew implementation;i.e All the code necessary for converting
 55    /// Gregorian to Hebrew Lunar from 1583 to 2239.
 56    /// </remarks>
 57    public class HebrewCalendar : Calendar
 58    {
 059        public static readonly int HebrewEra = 1;
 60
 61        private const int DatePartYear = 0;
 62        private const int DatePartMonth = 2;
 63        private const int DatePartDay = 3;
 64
 65        // Hebrew Translation Table.
 66        //
 67        //  This table is used to get the following Hebrew calendar information for a
 68        //  given Gregorian year:
 69        //      1. The day of the Hebrew month corresponding to Gregorian January 1st
 70        //         for a given Gregorian year.
 71        //      2. The month of the Hebrew month corresponding to Gregorian January 1st
 72        //         for a given Gregorian year.
 73        //         The information is not directly in the table.  Instead, the info is decoded
 74        //          by special values (numbers above 29 and below 1).
 75        //      3. The type of the Hebrew year for a given Gregorian year.
 76        //
 77        //  More notes:
 78        //  This table includes 2 numbers for each year.
 79        //  The offset into the table determines the year. (offset 0 is Gregorian year 1500)
 80        //  1st number determines the day of the Hebrew month coresponeds to January 1st.
 81        //  2nd number determines the type of the Hebrew year. (the type determines how
 82        //   many days are there in the year.)
 83        //
 84        //   normal years : 1 = 353 days   2 = 354 days   3 = 355 days.
 85        //   Leap years   : 4 = 383        5   384        6 = 385 days.
 86        //
 87        //   A 99 means the year is not supported for translation.
 88        //   for convenience the table was defined for 750 year,
 89        //   but only 640 years are supported. (from 1583 to 2239)
 90        //   the years before 1582 (starting of Georgian calendar)
 91        //   and after 2239, are filled with 99.
 92        //
 93        //   Greogrian January 1st falls usually in Tevet (4th month). Tevet has always 29 days.
 94        //   That's why, there no nead to specify the lunar month in the table.
 95        //   There are exceptions, these are coded by giving numbers above 29 and below 1.
 96        //   Actual decoding is takenig place whenever fetching information from the table.
 97        //   The function for decoding is in GetLunarMonthDay().
 98        //
 99        //   Example:
 100        //      The data for 2000 - 2005 A.D. is:
 101        //          23,6,6,1,17,2,27,6,7,3,         // 2000 - 2004
 102        //
 103        //      For year 2000, we know it has a Hebrew year type 6, which means it has 385 days.
 104        //      And 1/1/2000 A.D. is Hebrew year 5760, 23rd day of 4th month.
 105        //
 106        //  Jewish Era in use today is dated from the supposed year of the
 107        //  Creation with its beginning in 3761 B.C.
 108        //
 109        // The Hebrew year of Gregorian 1st year AD.
 110        // 0001/01/01 AD is Hebrew 3760/01/01
 111        private const int HebrewYearOf1AD = 3760;
 112
 113        private const int FirstGregorianTableYear = 1583;   // == Hebrew Year 5343
 114        private const int LastGregorianTableYear = 2239;    // == Hebrew Year 5999
 115
 116        private const int TableSize = (LastGregorianTableYear - FirstGregorianTableYear);
 117
 118        private const int MinHebrewYear = HebrewYearOf1AD + FirstGregorianTableYear;   // == 5343
 119        private const int MaxHebrewYear = HebrewYearOf1AD + LastGregorianTableYear;    // == 5999
 120
 121        private static ReadOnlySpan<byte> HebrewTable =>
 0122        [
 0123            7, 3, 17, 3,         // 1583-1584  (Hebrew year: 5343 - 5344)
 0124            0, 4, 11, 2, 21, 6, 1, 3, 13, 2,             // 1585-1589
 0125            25, 4, 5, 3, 16, 2, 27, 6, 9, 1,             // 1590-1594
 0126            20, 2, 0, 6, 11, 3, 23, 4, 4, 2,             // 1595-1599
 0127            14, 3, 27, 4, 8, 2, 18, 3, 28, 6,            // 1600
 0128            11, 1, 22, 5, 2, 3, 12, 3, 25, 4,      // 1605
 0129            6, 2, 16, 3, 26, 6, 8, 2, 20, 1,      // 1610
 0130            0, 6, 11, 2, 24, 4, 4, 3, 15, 2,      // 1615
 0131            25, 6, 8, 1, 19, 2, 29, 6, 9, 3,      // 1620
 0132            22, 4, 3, 2, 13, 3, 25, 4, 6, 3,      // 1625
 0133            17, 2, 27, 6, 7, 3, 19, 2, 31, 4,      // 1630
 0134            11, 3, 23, 4, 5, 2, 15, 3, 25, 6,      // 1635
 0135            6, 2, 19, 1, 29, 6, 10, 2, 22, 4,      // 1640
 0136            3, 3, 14, 2, 24, 6, 6, 1, 17, 3,      // 1645
 0137            28, 5, 8, 3, 20, 1, 32, 5, 12, 3,      // 1650
 0138            22, 6, 4, 1, 16, 2, 26, 6, 6, 3,      // 1655
 0139            17, 2, 0, 4, 10, 3, 22, 4, 3, 2,      // 1660
 0140            14, 3, 24, 6, 5, 2, 17, 1, 28, 6,      // 1665
 0141            9, 2, 19, 3, 31, 4, 13, 2, 23, 6,      // 1670
 0142            3, 3, 15, 1, 27, 5, 7, 3, 17, 3,      // 1675
 0143            29, 4, 11, 2, 21, 6, 3, 1, 14, 2,      // 1680
 0144            25, 6, 5, 3, 16, 2, 28, 4, 9, 3,      // 1685
 0145            20, 2, 0, 6, 12, 1, 23, 6, 4, 2,      // 1690
 0146            14, 3, 26, 4, 8, 2, 18, 3, 0, 4,      // 1695
 0147            10, 3, 21, 5, 1, 3, 13, 1, 24, 5,      // 1700
 0148            5, 3, 15, 3, 27, 4, 8, 2, 19, 3,      // 1705
 0149            29, 6, 10, 2, 22, 4, 3, 3, 14, 2,      // 1710
 0150            26, 4, 6, 3, 18, 2, 28, 6, 10, 1,      // 1715
 0151            20, 6, 2, 2, 12, 3, 24, 4, 5, 2,      // 1720
 0152            16, 3, 28, 4, 8, 3, 19, 2, 0, 6,      // 1725
 0153            12, 1, 23, 5, 3, 3, 14, 3, 26, 4,      // 1730
 0154            7, 2, 17, 3, 28, 6, 9, 2, 21, 4,      // 1735
 0155            1, 3, 13, 2, 25, 4, 5, 3, 16, 2,      // 1740
 0156            27, 6, 9, 1, 19, 3, 0, 5, 11, 3,      // 1745
 0157            23, 4, 4, 2, 14, 3, 25, 6, 7, 1,      // 1750
 0158            18, 2, 28, 6, 9, 3, 21, 4, 2, 2,      // 1755
 0159            12, 3, 25, 4, 6, 2, 16, 3, 26, 6,      // 1760
 0160            8, 2, 20, 1, 0, 6, 11, 2, 22, 6,      // 1765
 0161            4, 1, 15, 2, 25, 6, 6, 3, 18, 1,      // 1770
 0162            29, 5, 9, 3, 22, 4, 2, 3, 13, 2,      // 1775
 0163            23, 6, 4, 3, 15, 2, 27, 4, 7, 3,      // 1780
 0164            19, 2, 31, 4, 11, 3, 21, 6, 3, 2,      // 1785
 0165            15, 1, 25, 6, 6, 2, 17, 3, 29, 4,      // 1790
 0166            10, 2, 20, 6, 3, 1, 13, 3, 24, 5,      // 1795
 0167            4, 3, 16, 1, 27, 5, 7, 3, 17, 3,      // 1800
 0168            0, 4, 11, 2, 21, 6, 1, 3, 13, 2,      // 1805
 0169            25, 4, 5, 3, 16, 2, 29, 4, 9, 3,      // 1810
 0170            19, 6, 30, 2, 13, 1, 23, 6, 4, 2,      // 1815
 0171            14, 3, 27, 4, 8, 2, 18, 3, 0, 4,      // 1820
 0172            11, 3, 22, 5, 2, 3, 14, 1, 26, 5,      // 1825
 0173            6, 3, 16, 3, 28, 4, 10, 2, 20, 6,      // 1830
 0174            30, 3, 11, 2, 24, 4, 4, 3, 15, 2,      // 1835
 0175            25, 6, 8, 1, 19, 2, 29, 6, 9, 3,      // 1840
 0176            22, 4, 3, 2, 13, 3, 25, 4, 7, 2,      // 1845
 0177            17, 3, 27, 6, 9, 1, 21, 5, 1, 3,      // 1850
 0178            11, 3, 23, 4, 5, 2, 15, 3, 25, 6,      // 1855
 0179            6, 2, 19, 1, 29, 6, 10, 2, 22, 4,      // 1860
 0180            3, 3, 14, 2, 24, 6, 6, 1, 18, 2,      // 1865
 0181            28, 6, 8, 3, 20, 4, 2, 2, 12, 3,      // 1870
 0182            24, 4, 4, 3, 16, 2, 26, 6, 6, 3,      // 1875
 0183            17, 2, 0, 4, 10, 3, 22, 4, 3, 2,      // 1880
 0184            14, 3, 24, 6, 5, 2, 17, 1, 28, 6,      // 1885
 0185            9, 2, 21, 4, 1, 3, 13, 2, 23, 6,      // 1890
 0186            5, 1, 15, 3, 27, 5, 7, 3, 19, 1,      // 1895
 0187            0, 5, 10, 3, 22, 4, 2, 3, 13, 2,      // 1900
 0188            24, 6, 4, 3, 15, 2, 27, 4, 8, 3,      // 1905
 0189            20, 4, 1, 2, 11, 3, 22, 6, 3, 2,      // 1910
 0190            15, 1, 25, 6, 7, 2, 17, 3, 29, 4,      // 1915
 0191            10, 2, 21, 6, 1, 3, 13, 1, 24, 5,      // 1920
 0192            5, 3, 15, 3, 27, 4, 8, 2, 19, 6,      // 1925
 0193            1, 1, 12, 2, 22, 6, 3, 3, 14, 2,      // 1930
 0194            26, 4, 6, 3, 18, 2, 28, 6, 10, 1,      // 1935
 0195            20, 6, 2, 2, 12, 3, 24, 4, 5, 2,      // 1940
 0196            16, 3, 28, 4, 9, 2, 19, 6, 30, 3,      // 1945
 0197            12, 1, 23, 5, 3, 3, 14, 3, 26, 4,      // 1950
 0198            7, 2, 17, 3, 28, 6, 9, 2, 21, 4,      // 1955
 0199            1, 3, 13, 2, 25, 4, 5, 3, 16, 2,      // 1960
 0200            27, 6, 9, 1, 19, 6, 30, 2, 11, 3,      // 1965
 0201            23, 4, 4, 2, 14, 3, 27, 4, 7, 3,      // 1970
 0202            18, 2, 28, 6, 11, 1, 22, 5, 2, 3,      // 1975
 0203            12, 3, 25, 4, 6, 2, 16, 3, 26, 6,      // 1980
 0204            8, 2, 20, 4, 30, 3, 11, 2, 24, 4,      // 1985
 0205            4, 3, 15, 2, 25, 6, 8, 1, 18, 3,      // 1990
 0206            29, 5, 9, 3, 22, 4, 3, 2, 13, 3,      // 1995
 0207            23, 6, 6, 1, 17, 2, 27, 6, 7, 3,         // 2000 - 2004
 0208            20, 4, 1, 2, 11, 3, 23, 4, 5, 2,         // 2005 - 2009
 0209            15, 3, 25, 6, 6, 2, 19, 1, 29, 6,        // 2010
 0210            10, 2, 20, 6, 3, 1, 14, 2, 24, 6,      // 2015
 0211            4, 3, 17, 1, 28, 5, 8, 3, 20, 4,      // 2020
 0212            1, 3, 12, 2, 22, 6, 2, 3, 14, 2,      // 2025
 0213            26, 4, 6, 3, 17, 2, 0, 4, 10, 3,      // 2030
 0214            20, 6, 1, 2, 14, 1, 24, 6, 5, 2,      // 2035
 0215            15, 3, 28, 4, 9, 2, 19, 6, 1, 1,      // 2040
 0216            12, 3, 23, 5, 3, 3, 15, 1, 27, 5,      // 2045
 0217            7, 3, 17, 3, 29, 4, 11, 2, 21, 6,      // 2050
 0218            1, 3, 12, 2, 25, 4, 5, 3, 16, 2,      // 2055
 0219            28, 4, 9, 3, 19, 6, 30, 2, 12, 1,      // 2060
 0220            23, 6, 4, 2, 14, 3, 26, 4, 8, 2,      // 2065
 0221            18, 3, 0, 4, 10, 3, 22, 5, 2, 3,      // 2070
 0222            14, 1, 25, 5, 6, 3, 16, 3, 28, 4,      // 2075
 0223            9, 2, 20, 6, 30, 3, 11, 2, 23, 4,      // 2080
 0224            4, 3, 15, 2, 27, 4, 7, 3, 19, 2,      // 2085
 0225            29, 6, 11, 1, 21, 6, 3, 2, 13, 3,      // 2090
 0226            25, 4, 6, 2, 17, 3, 27, 6, 9, 1,      // 2095
 0227            20, 5, 30, 3, 10, 3, 22, 4, 3, 2,      // 2100
 0228            14, 3, 24, 6, 5, 2, 17, 1, 28, 6,      // 2105
 0229            9, 2, 21, 4, 1, 3, 13, 2, 23, 6,      // 2110
 0230            5, 1, 16, 2, 27, 6, 7, 3, 19, 4,      // 2115
 0231            30, 2, 11, 3, 23, 4, 3, 3, 14, 2,      // 2120
 0232            25, 6, 5, 3, 16, 2, 28, 4, 9, 3,      // 2125
 0233            21, 4, 2, 2, 12, 3, 23, 6, 4, 2,      // 2130
 0234            16, 1, 26, 6, 8, 2, 20, 4, 30, 3,      // 2135
 0235            11, 2, 22, 6, 4, 1, 14, 3, 25, 5,      // 2140
 0236            6, 3, 18, 1, 29, 5, 9, 3, 22, 4,      // 2145
 0237            2, 3, 13, 2, 23, 6, 4, 3, 15, 2,      // 2150
 0238            27, 4, 7, 3, 20, 4, 1, 2, 11, 3,      // 2155
 0239            21, 6, 3, 2, 15, 1, 25, 6, 6, 2,      // 2160
 0240            17, 3, 29, 4, 10, 2, 20, 6, 3, 1,      // 2165
 0241            13, 3, 24, 5, 4, 3, 17, 1, 28, 5,      // 2170
 0242            8, 3, 18, 6, 1, 1, 12, 2, 22, 6,      // 2175
 0243            2, 3, 14, 2, 26, 4, 6, 3, 17, 2,      // 2180
 0244            28, 6, 10, 1, 20, 6, 1, 2, 12, 3,    // 2185
 0245            24, 4, 5, 2, 15, 3, 28, 4, 9, 2,     // 2190
 0246            19, 6, 33, 3, 12, 1, 23, 5, 3, 3,    // 2195
 0247            13, 3, 25, 4, 6, 2, 16, 3, 26, 6,    // 2200
 0248            8, 2, 20, 4, 30, 3, 11, 2, 24, 4,    // 2205
 0249            4, 3, 15, 2, 25, 6, 8, 1, 18, 6,     // 2210
 0250            33, 2, 9, 3, 22, 4, 3, 2, 13, 3,     // 2215
 0251            25, 4, 6, 3, 17, 2, 27, 6, 9, 1,     // 2220
 0252            21, 5, 1, 3, 11, 3, 23, 4, 5, 2,     // 2225
 0253            15, 3, 25, 6, 6, 2, 19, 4, 33, 3,    // 2230
 0254            10, 2, 22, 4, 3, 3, 14, 2, 24, 6,    // 2235
 0255            6, 1    // 2240 (Hebrew year: 6000)
 0256        ];
 257
 258        private const int MaxMonthPlusOne = 14;
 259
 260        // The lunar calendar has 6 different variations of month lengths
 261        // within a year.
 262        private static ReadOnlySpan<byte> LunarMonthLen =>
 0263        [
 0264            0, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0,
 0265            0, 30, 29, 29, 29, 30, 29, 30, 29, 30, 29, 30, 29, 0,     // 3 common year variations
 0266            0, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 0,
 0267            0, 30, 30, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 0,
 0268            0, 30, 29, 29, 29, 30, 30, 29, 30, 29, 30, 29, 30, 29,    // 3 leap year variations
 0269            0, 30, 29, 30, 29, 30, 30, 29, 30, 29, 30, 29, 30, 29,
 0270            0, 30, 30, 30, 29, 30, 30, 29, 30, 29, 30, 29, 30, 29
 0271        ];
 272
 0273        private static readonly DateTime s_calendarMinValue = new DateTime(1583, 1, 1);
 274
 275        // Gregorian 2239/9/29 = Hebrew 5999/13/29 (last day in Hebrew year 5999).
 276        // We can only format/parse Hebrew numbers up to 999, so we limit the max range to Hebrew year 5999.
 0277        private static readonly DateTime s_calendarMaxValue = new DateTime((new DateTime(2239, 9, 29, 23, 59, 59, 999)).
 278
 0279        public override DateTime MinSupportedDateTime => s_calendarMinValue;
 280
 0281        public override DateTime MaxSupportedDateTime => s_calendarMaxValue;
 282
 0283        public override CalendarAlgorithmType AlgorithmType => CalendarAlgorithmType.LunisolarCalendar;
 284
 0285        public HebrewCalendar()
 286        {
 0287        }
 288
 0289        internal override CalendarId ID => CalendarId.HEBREW;
 290
 291        private static void CheckHebrewYearValue(int y, int era, string varName)
 292        {
 0293            CheckEraRange(era);
 0294            if (y > MaxHebrewYear || y < MinHebrewYear)
 295            {
 0296                throw new ArgumentOutOfRangeException(
 0297                    varName,
 0298                    y,
 0299                    SR.Format(SR.ArgumentOutOfRange_Range, MinHebrewYear, MaxHebrewYear));
 300            }
 0301        }
 302
 303        /// <summary>
 304        /// Check if the Hebrew month value is valid.
 305        /// </summary>
 306        /// <remarks>
 307        /// Call CheckHebrewYearValue() before calling this to verify the year value is supported.
 308        /// </remarks>
 309        private void CheckHebrewMonthValue(int year, int month, int era)
 310        {
 0311            int monthsInYear = GetMonthsInYear(year, era);
 0312            if (month < 1 || month > monthsInYear)
 313            {
 0314                throw new ArgumentOutOfRangeException(
 0315                    nameof(month),
 0316                    month,
 0317                    SR.Format(SR.ArgumentOutOfRange_Range, 1, monthsInYear));
 318            }
 0319        }
 320
 321        /// <summary>
 322        /// Check if the Hebrew day value is valid.
 323        /// </summary>
 324        /// <remarks>
 325        /// Call CheckHebrewYearValue()/CheckHebrewMonthValue() before calling this to verify the year/month values are 
 326        /// </remarks>
 327        private void CheckHebrewDayValue(int year, int month, int day, int era)
 328        {
 0329            int daysInMonth = GetDaysInMonth(year, month, era);
 0330            if (day < 1 || day > daysInMonth)
 331            {
 0332                throw new ArgumentOutOfRangeException(
 0333                    nameof(day),
 0334                    day,
 0335                    SR.Format(SR.ArgumentOutOfRange_Range, 1, daysInMonth));
 336            }
 0337        }
 338
 339        private static void CheckEraRange(int era)
 340        {
 0341            if (era != CurrentEra && era != HebrewEra)
 342            {
 0343                throw new ArgumentOutOfRangeException(nameof(era), era, SR.ArgumentOutOfRange_InvalidEraValue);
 344            }
 0345        }
 346
 347        private static void CheckTicksRange(long ticks)
 348        {
 0349            if (ticks < s_calendarMinValue.Ticks || ticks > s_calendarMaxValue.Ticks)
 350            {
 0351                throw new ArgumentOutOfRangeException(
 0352                    "time",
 0353                    ticks,
 0354                    // Print out the date in Gregorian using InvariantCulture since the DateTime is based on GreograinCa
 0355                    SR.Format(
 0356                        CultureInfo.InvariantCulture,
 0357                        SR.ArgumentOutOfRange_CalendarRange,
 0358                        s_calendarMinValue,
 0359                        s_calendarMaxValue));
 360            }
 0361        }
 362
 363        private static int GetResult(DateBuffer result, int part)
 364        {
 0365            return part switch
 0366            {
 0367                DatePartYear => result.year,
 0368                DatePartMonth => result.month,
 0369                DatePartDay => result.day,
 0370                _ => throw new InvalidOperationException(SR.InvalidOperation_DateTimeParsing),
 0371            };
 372        }
 373
 374        /// <summary>
 375        /// Using the Hebrew table (HebrewTable) to get the Hebrew month/day value for Gregorian January 1st
 376        /// in a given Gregorian year.
 377        /// Greogrian January 1st falls usually in Tevet (4th month). Tevet has always 29 days.
 378        /// That's why, there no nead to specify the lunar month in the table.  There are exceptions, and these
 379        /// are coded by giving numbers above 29 and below 1.
 380        /// Actual decoding is takenig place in the switch statement below.
 381        /// </summary>
 382        /// <returns>
 383        /// The Hebrew year type. The value is from 1 to 6.
 384        /// normal years : 1 = 353 days   2 = 354 days   3 = 355 days.
 385        /// Leap years   : 4 = 383        5   384        6 = 385 days.
 386        /// </returns>
 387        internal static int GetLunarMonthDay(int gregorianYear, DateBuffer lunarDate)
 388        {
 389            // Get the offset into the LunarMonthLen array and the lunar day
 390            //  for January 1st.
 0391            int index = gregorianYear - FirstGregorianTableYear;
 0392            ArgumentOutOfRangeException.ThrowIfNegative(index, nameof(gregorianYear));
 0393            ArgumentOutOfRangeException.ThrowIfGreaterThan(index, TableSize, nameof(gregorianYear));
 394
 0395            index *= 2;
 0396            lunarDate.day = HebrewTable[index];
 397
 398            // Get the type of the year. The value is from 1 to 6
 0399            int lunarYearType = HebrewTable[index + 1];
 400
 401            // Get the Lunar Month.
 0402            switch (lunarDate.day)
 403            {
 404                case 0:                   // 1/1 is on Shvat 1
 0405                    lunarDate.month = 5;
 0406                    lunarDate.day = 1;
 0407                    break;
 408                case 30:                  // 1/1 is on Kislev 30
 0409                    lunarDate.month = 3;
 0410                    break;
 411                case 31:                  // 1/1 is on Shvat 2
 0412                    lunarDate.month = 5;
 0413                    lunarDate.day = 2;
 0414                    break;
 415                case 32:                  // 1/1 is on Shvat 3
 0416                    lunarDate.month = 5;
 0417                    lunarDate.day = 3;
 0418                    break;
 419                case 33:                  // 1/1 is on Kislev 29
 0420                    lunarDate.month = 3;
 0421                    lunarDate.day = 29;
 0422                    break;
 423                default:                      // 1/1 is on Tevet (This is the general case)
 0424                    lunarDate.month = 4;
 425                    break;
 426            }
 427
 0428            return lunarYearType;
 429        }
 430
 431        /// <summary>
 432        /// Returns a given date part of this DateTime. This method is used
 433        /// to compute the year, day-of-year, month, or day part.
 434        /// </summary>
 435        private static int GetDatePart(long ticks, int part)
 436        {
 437            // The Gregorian year, month, day value for ticks.
 438            int hebrewYearType;                // lunar year type
 439            long AbsoluteDate;                // absolute date - absolute date 1/1/1600
 440
 441            // Make sure we have a valid Gregorian date that will fit into our
 442            // Hebrew conversion limits.
 0443            CheckTicksRange(ticks);
 444
 0445            DateTime time = new DateTime(ticks);
 446
 447            // Save the Gregorian date values.
 0448            time.GetDate(out int gregorianYear, out int gregorianMonth, out int gregorianDay);
 449
 0450            DateBuffer lunarDate = new DateBuffer();    // lunar month and day for Jan 1
 451
 452            // From the table looking-up value of HebrewTable[index] (stored in lunarDate.day), we get the
 453            // lunar month and lunar day where the Gregorian date 1/1 falls.
 0454            lunarDate.year = gregorianYear + HebrewYearOf1AD;
 0455            hebrewYearType = GetLunarMonthDay(gregorianYear, lunarDate);
 456
 457            // This is the buffer used to store the result Hebrew date.
 0458            DateBuffer result = new DateBuffer();
 459
 460            // Store the values for the start of the new year - 1/1.
 0461            result.year = lunarDate.year;
 0462            result.month = lunarDate.month;
 0463            result.day = lunarDate.day;
 464
 465            // Get the absolute date from 1/1/1600.
 0466            AbsoluteDate = GregorianCalendar.GetAbsoluteDate(gregorianYear, gregorianMonth, gregorianDay);
 467
 468            // If the requested date was 1/1, then we're done.
 0469            if ((gregorianMonth == 1) && (gregorianDay == 1))
 470            {
 0471                return GetResult(result, part);
 472            }
 473
 474            // Calculate the number of days between 1/1 and the requested date.
 0475            long numDays = AbsoluteDate - GregorianCalendar.GetAbsoluteDate(gregorianYear, 1, 1);
 476
 477            // If the requested date is within the current lunar month, then
 478            // we're done.
 0479            if ((numDays + (long)lunarDate.day) <= (long)(LunarMonthLen[hebrewYearType * MaxMonthPlusOne + lunarDate.mon
 480            {
 0481                result.day += (int)numDays;
 0482                return GetResult(result, part);
 483            }
 484
 485            // Adjust for the current partial month.
 0486            result.month++;
 0487            result.day = 1;
 488
 489            // Adjust the Lunar Month and Year (if necessary) based on the number
 490            // of days between 1/1 and the requested date.
 491            // Assumes Jan 1 can never translate to the last Lunar month, which
 492            // is true.
 0493            numDays -= (long)(LunarMonthLen[hebrewYearType * MaxMonthPlusOne + lunarDate.month] - lunarDate.day);
 0494            Debug.Assert(numDays >= 1, "NumDays >= 1");
 495
 496            // If NumDays is 1, then we are done.  Otherwise, find the correct Hebrew month
 497            // and day.
 0498            if (numDays > 1)
 499            {
 500                // See if we're on the correct Lunar month.
 0501                while (numDays > (long)(LunarMonthLen[hebrewYearType * MaxMonthPlusOne + result.month]))
 502                {
 503                    // Adjust the number of days and move to the next month.
 0504                    numDays -= (long)(LunarMonthLen[hebrewYearType * MaxMonthPlusOne + result.month++]);
 505
 506                    // See if we need to adjust the Year.
 507                    // Must handle both 12 and 13 month years.
 0508                    if ((result.month > 13) || (LunarMonthLen[hebrewYearType * MaxMonthPlusOne + result.month] == 0))
 509                    {
 510                        // Adjust the Year.
 0511                        result.year++;
 0512                        hebrewYearType = HebrewTable[(gregorianYear + 1 - FirstGregorianTableYear) * 2 + 1];
 513
 514                        // Adjust the Month.
 0515                        result.month = 1;
 516                    }
 517                }
 518
 519                // Found the right Lunar month.
 0520                result.day += (int)(numDays - 1);
 521            }
 522
 0523            return GetResult(result, part);
 524        }
 525
 526        public override DateTime AddMonths(DateTime time, int months)
 527        {
 528            try
 529            {
 0530                int y = GetDatePart(time.Ticks, DatePartYear);
 0531                int m = GetDatePart(time.Ticks, DatePartMonth);
 0532                int d = GetDatePart(time.Ticks, DatePartDay);
 533
 534                int monthsInYear;
 535                int i;
 0536                if (months >= 0)
 537                {
 0538                    i = m + months;
 0539                    while (i > (monthsInYear = GetMonthsInYear(y, CurrentEra)))
 540                    {
 0541                        y++;
 0542                        i -= monthsInYear;
 543                    }
 544                }
 545                else
 546                {
 0547                    if ((i = m + months) <= 0)
 548                    {
 0549                        months = -months;
 0550                        months -= m;
 0551                        y--;
 552
 0553                        while (months > (monthsInYear = GetMonthsInYear(y, CurrentEra)))
 554                        {
 0555                            y--;
 0556                            months -= monthsInYear;
 557                        }
 0558                        monthsInYear = GetMonthsInYear(y, CurrentEra);
 0559                        i = monthsInYear - months;
 560                    }
 561                }
 562
 0563                int days = GetDaysInMonth(y, i);
 0564                if (d > days)
 565                {
 0566                    d = days;
 567                }
 568
 0569                return new DateTime(ToDateTime(y, i, d, 0, 0, 0, 0).Ticks + (time.Ticks % TimeSpan.TicksPerDay));
 570            }
 571            // We expect ArgumentException and ArgumentOutOfRangeException (which is subclass of ArgumentException)
 572            // If exception is thrown in the calls above, we are out of the supported range of this calendar.
 0573            catch (ArgumentException)
 574            {
 0575                throw new ArgumentOutOfRangeException(nameof(months), months, SR.ArgumentOutOfRange_AddValue);
 576            }
 0577        }
 578
 579        public override DateTime AddYears(DateTime time, int years)
 580        {
 0581            int y = GetDatePart(time.Ticks, DatePartYear);
 0582            int m = GetDatePart(time.Ticks, DatePartMonth);
 0583            int d = GetDatePart(time.Ticks, DatePartDay);
 584
 0585            y += years;
 0586            CheckHebrewYearValue(y, CurrentEra, nameof(years));
 587
 0588            int months = GetMonthsInYear(y, CurrentEra);
 0589            if (m > months)
 590            {
 0591                m = months;
 592            }
 593
 0594            int days = GetDaysInMonth(y, m);
 0595            if (d > days)
 596            {
 0597                d = days;
 598            }
 599
 0600            long ticks = ToDateTime(y, m, d, 0, 0, 0, 0).Ticks + (time.Ticks % TimeSpan.TicksPerDay);
 0601            CheckAddResult(ticks, MinSupportedDateTime, MaxSupportedDateTime);
 0602            return new DateTime(ticks);
 603        }
 604
 605        public override int GetDayOfMonth(DateTime time)
 606        {
 0607            return GetDatePart(time.Ticks, DatePartDay);
 608        }
 609
 610        public override DayOfWeek GetDayOfWeek(DateTime time)
 611        {
 612            // If we calculate back, the Hebrew day of week for Gregorian 0001/1/1 is Monday (1).
 0613            return time.DayOfWeek;
 614        }
 615
 616        internal static int GetHebrewYearType(int year, int era)
 617        {
 0618            CheckHebrewYearValue(year, era, nameof(year));
 619            // The HebrewTable is indexed by Gregorian year and starts from FirstGregorianYear.
 620            // So we need to convert year (Hebrew year value) to Gregorian Year below.
 0621            return HebrewTable[(year - HebrewYearOf1AD - FirstGregorianTableYear) * 2 + 1];
 622        }
 623
 624        public override int GetDayOfYear(DateTime time)
 625        {
 626            // Get Hebrew year value of the specified time.
 0627            int year = GetYear(time);
 628            DateTime beginOfYearDate;
 0629            if (year == 5343)
 630            {
 631                // Gregorian 1583/01/01 corresponds to Hebrew 5343/04/07 (MinSupportedDateTime)
 632                // To figure out the Gregorian date associated with Hebrew 5343/01/01, we need to
 633                // count the days from 5343/01/01 to 5343/04/07 and subtract that from Gregorian
 634                // 1583/01/01.
 635                //        1.  Tishri        (30 days)
 636                //        2.  Heshvan       (30 days since 5343 has 355 days)
 637                //        3.  Kislev        (30 days since 5343 has 355 days)
 638                // 96 days to get from 5343/01/01 to 5343/04/07
 639                // Gregorian 1583/01/01 - 96 days = 1582/9/27
 640
 641                // the beginning of Hebrew year 5343 corresponds to Gregorian September 27, 1582.
 0642                beginOfYearDate = new DateTime(1582, 9, 27);
 643            }
 644            else
 645            {
 646                // following line will fail when year is 5343 (first supported year)
 0647                beginOfYearDate = ToDateTime(year, 1, 1, 0, 0, 0, 0, CurrentEra);
 648            }
 649
 0650            return (int)((time.Ticks - beginOfYearDate.Ticks) / TimeSpan.TicksPerDay) + 1;
 651        }
 652
 653        public override int GetDaysInMonth(int year, int month, int era)
 654        {
 0655            CheckEraRange(era);
 0656            int hebrewYearType = GetHebrewYearType(year, era);
 0657            CheckHebrewMonthValue(year, month, era);
 658
 0659            Debug.Assert(hebrewYearType >= 1 && hebrewYearType <= 6,
 0660                $"hebrewYearType should be from  1 to 6, but now hebrewYearType = {hebrewYearType} for hebrew year {year
 0661            int monthDays = LunarMonthLen[hebrewYearType * MaxMonthPlusOne + month];
 0662            if (monthDays == 0)
 663            {
 0664                ThrowHelper.ThrowArgumentOutOfRange_Month(month);
 665            }
 666
 0667            return monthDays;
 668        }
 669
 670        public override int GetDaysInYear(int year, int era)
 671        {
 0672            CheckEraRange(era);
 673            // normal years : 1 = 353 days   2 = 354 days   3 = 355 days.
 674            // Leap years   : 4 = 383        5   384        6 = 385 days.
 675
 676            // LunarYearType is from 1 to 6
 0677            int LunarYearType = GetHebrewYearType(year, era);
 0678            if (LunarYearType < 4)
 679            {
 680                // common year: LunarYearType = 1, 2, 3
 0681                return 352 + LunarYearType;
 682            }
 683
 0684            return 382 + (LunarYearType - 3);
 685        }
 686
 0687        public override int GetEra(DateTime time) => HebrewEra;
 688
 0689        public override int[] Eras => [HebrewEra];
 690
 691        public override int GetMonth(DateTime time)
 692        {
 0693            return GetDatePart(time.Ticks, DatePartMonth);
 694        }
 695
 696        public override int GetMonthsInYear(int year, int era)
 697        {
 0698            return IsLeapYear(year, era) ? 13 : 12;
 699        }
 700
 701        public override int GetYear(DateTime time)
 702        {
 0703            return GetDatePart(time.Ticks, DatePartYear);
 704        }
 705
 706        public override bool IsLeapDay(int year, int month, int day, int era)
 707        {
 0708            if (IsLeapMonth(year, month, era))
 709            {
 710                // Every day in a leap month is a leap day.
 0711                CheckHebrewDayValue(year, month, day, era);
 0712                return true;
 713            }
 0714            else if (IsLeapYear(year, CurrentEra))
 715            {
 716                // There is an additional day in the 6th month in the leap year (the extra day is the 30th day in the 6t
 717                // so we should return true for 6/30 if that's in a leap year.
 0718                if (month == 6 && day == 30)
 719                {
 0720                    return true;
 721                }
 722            }
 723
 0724            CheckHebrewDayValue(year, month, day, era);
 0725            return false;
 726        }
 727
 728        public override int GetLeapMonth(int year, int era)
 729        {
 730            // Year/era values are checked in IsLeapYear().
 0731            if (IsLeapYear(year, era))
 732            {
 733                // The 7th month in a leap year is a leap month.
 0734                return 7;
 735            }
 0736            return 0;
 737        }
 738
 739        public override bool IsLeapMonth(int year, int month, int era)
 740        {
 741            // Year/era values are checked in IsLeapYear().
 0742            bool isLeapYear = IsLeapYear(year, era);
 0743            CheckHebrewMonthValue(year, month, era);
 744
 745            // The 7th month in a leap year is a leap month.
 0746            return isLeapYear && month == 7;
 747        }
 748
 749        public override bool IsLeapYear(int year, int era)
 750        {
 0751            CheckHebrewYearValue(year, era, nameof(year));
 0752            return ((7 * (long)year + 1) % 19) < 7;
 753        }
 754
 755        /// <summary>
 756        /// (month1, day1) - (month2, day2)
 757        /// </summary>
 758        private static int GetDayDifference(int lunarYearType, int month1, int day1, int month2, int day2)
 759        {
 0760            if (month1 == month2)
 761            {
 0762                return day1 - day2;
 763            }
 764
 765            // Make sure that (month1, day1) < (month2, day2)
 0766            bool swap = (month1 > month2);
 0767            if (swap)
 768            {
 769                // (month1, day1) < (month2, day2).  Swap the values.
 770                // The result will be a negative number.
 771                int tempMonth, tempDay;
 0772                tempMonth = month1; tempDay = day1;
 0773                month1 = month2; day1 = day2;
 0774                month2 = tempMonth; day2 = tempDay;
 775            }
 776
 777            // Get the number of days from (month1,day1) to (month1, end of month1)
 0778            int days = LunarMonthLen[lunarYearType * MaxMonthPlusOne + month1] - day1;
 779
 780            // Move to next month.
 0781            month1++;
 782
 783            // Add up the days.
 0784            while (month1 < month2)
 785            {
 0786                days += LunarMonthLen[lunarYearType * MaxMonthPlusOne + month1++];
 787            }
 0788            days += day2;
 789
 0790            return swap ? days : -days;
 791        }
 792
 793        /// <summary>
 794        /// Convert Hebrew date to Gregorian date.
 795        ///  The algorithm is like this:
 796        ///     The hebrew year has an offset to the Gregorian year, so we can guess the Gregorian year for
 797        ///     the specified Hebrew year.  That is, GreogrianYear = HebrewYear - FirstHebrewYearOf1AD.
 798        ///
 799        ///     From the Gregorian year and HebrewTable, we can get the Hebrew month/day value
 800        ///     of the Gregorian date January 1st.  Let's call this month/day value [hebrewDateForJan1]
 801        ///
 802        ///     If the requested Hebrew month/day is less than [hebrewDateForJan1], we know the result
 803        ///     Gregorian date falls in previous year.  So we decrease the Gregorian year value, and
 804        ///     retrieve the Hebrew month/day value of the Gregorian date january 1st again.
 805        ///
 806        ///     Now, we get the answer of the Gregorian year.
 807        ///
 808        ///     The next step is to get the number of days between the requested Hebrew month/day
 809        ///     and [hebrewDateForJan1].  When we get that, we can create the DateTime by adding/subtracting
 810        ///     the ticks value of the number of days.
 811        /// </summary>
 812        private static DateTime HebrewToGregorian(int hebrewYear, int hebrewMonth, int hebrewDay, int hour, int minute, 
 813        {
 814            // Get the rough Gregorian year for the specified hebrewYear.
 0815            int gregorianYear = hebrewYear - HebrewYearOf1AD;
 816
 0817            DateBuffer hebrewDateOfJan1 = new DateBuffer(); // year value is unused.
 0818            int lunarYearType = GetLunarMonthDay(gregorianYear, hebrewDateOfJan1);
 819
 0820            if ((hebrewMonth == hebrewDateOfJan1.month) && (hebrewDay == hebrewDateOfJan1.day))
 821            {
 0822                return new DateTime(gregorianYear, 1, 1, hour, minute, second, millisecond);
 823            }
 824
 0825            int days = GetDayDifference(lunarYearType, hebrewMonth, hebrewDay, hebrewDateOfJan1.month, hebrewDateOfJan1.
 826
 0827            DateTime gregorianNewYear = new DateTime(gregorianYear, 1, 1);
 0828            return new DateTime(gregorianNewYear.Ticks + days * TimeSpan.TicksPerDay + TimeToTicks(hour, minute, second,
 829        }
 830
 831        public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisec
 832        {
 0833            CheckHebrewYearValue(year, era, nameof(year));
 0834            CheckHebrewMonthValue(year, month, era);
 0835            CheckHebrewDayValue(year, month, day, era);
 0836            DateTime dt = HebrewToGregorian(year, month, day, hour, minute, second, millisecond);
 0837            CheckTicksRange(dt.Ticks);
 0838            return dt;
 839        }
 840
 841        private const int DefaultTwoDigitYearMax = 5790;
 842
 843        public override int TwoDigitYearMax
 844        {
 845            get
 846            {
 0847                if (_twoDigitYearMax == -1)
 848                {
 0849                    _twoDigitYearMax = GetSystemTwoDigitYearSetting(ID, DefaultTwoDigitYearMax);
 850                }
 851
 0852                return _twoDigitYearMax;
 853            }
 854            set
 855            {
 0856                VerifyWritable();
 0857                if (value == 99)
 858                {
 859                    // Do nothing here. Year 99 is allowed so that TwoDitYearMax is disabled.
 860                }
 861                else
 862                {
 0863                    CheckHebrewYearValue(value, HebrewEra, nameof(value));
 864                }
 865
 0866                _twoDigitYearMax = value;
 0867            }
 868        }
 869
 870        public override int ToFourDigitYear(int year)
 871        {
 0872            ArgumentOutOfRangeException.ThrowIfNegative(year);
 873
 0874            if (year < 100)
 875            {
 0876                return base.ToFourDigitYear(year);
 877            }
 878
 0879            if (year > MaxHebrewYear || year < MinHebrewYear)
 880            {
 0881                throw new ArgumentOutOfRangeException(
 0882                    nameof(year),
 0883                    year,
 0884                    SR.Format(SR.ArgumentOutOfRange_Range, MinHebrewYear, MaxHebrewYear));
 885            }
 0886            return year;
 887        }
 888
 889        internal sealed class DateBuffer
 890        {
 891            internal int year;
 892            internal int month;
 893            internal int day;
 894        }
 895    }
 896}
 897

Methods/Properties

.cctor()
HebrewTable()
LunarMonthLen()
MinSupportedDateTime()
MaxSupportedDateTime()
AlgorithmType()
.ctor()
ID()
CheckHebrewYearValue(System.Int32,System.Int32,System.String)
CheckHebrewMonthValue(System.Int32,System.Int32,System.Int32)
CheckHebrewDayValue(System.Int32,System.Int32,System.Int32,System.Int32)
CheckEraRange(System.Int32)
CheckTicksRange(System.Int64)
GetResult(System.Globalization.HebrewCalendar/DateBuffer,System.Int32)
GetLunarMonthDay(System.Int32,System.Globalization.HebrewCalendar/DateBuffer)
GetDatePart(System.Int64,System.Int32)
AddMonths(System.DateTime,System.Int32)
AddYears(System.DateTime,System.Int32)
GetDayOfMonth(System.DateTime)
GetDayOfWeek(System.DateTime)
GetHebrewYearType(System.Int32,System.Int32)
GetDayOfYear(System.DateTime)
GetDaysInMonth(System.Int32,System.Int32,System.Int32)
GetDaysInYear(System.Int32,System.Int32)
GetEra(System.DateTime)
Eras()
GetMonth(System.DateTime)
GetMonthsInYear(System.Int32,System.Int32)
GetYear(System.DateTime)
IsLeapDay(System.Int32,System.Int32,System.Int32,System.Int32)
GetLeapMonth(System.Int32,System.Int32)
IsLeapMonth(System.Int32,System.Int32,System.Int32)
IsLeapYear(System.Int32,System.Int32)
GetDayDifference(System.Int32,System.Int32,System.Int32,System.Int32,System.Int32)
HebrewToGregorian(System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32)
ToDateTime(System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32)
TwoDigitYearMax()
TwoDigitYearMax(System.Int32)
ToFourDigitYear(System.Int32)