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 // See the LICENSE file in the project root for more information.
4 
5 using System.Diagnostics;
6 
7 namespace System.Globalization
8 {
9     /*
10     **  Calendar support range:
11     **      Calendar    Minimum     Maximum
12     **      ==========  ==========  ==========
13     **      Gregorian   1900/04/30   2077/05/13
14     **      UmAlQura    1318/01/01   1500/12/30
15     */
16 
17     public partial class UmAlQuraCalendar : Calendar
18     {
19         internal const int MinCalendarYear = 1318;
20         internal const int MaxCalendarYear = 1500;
21 
22         internal struct DateMapping
23         {
DateMappingSystem.Globalization.UmAlQuraCalendar.DateMapping24             internal DateMapping(int MonthsLengthFlags, int GYear, int GMonth, int GDay)
25             {
26                 HijriMonthsLengthFlags = MonthsLengthFlags;
27                 GregorianDate = new DateTime(GYear, GMonth, GDay);
28             }
29             internal int HijriMonthsLengthFlags;
30             internal DateTime GregorianDate;
31         }
32 
33         private static readonly DateMapping[] s_hijriYearInfo = InitDateMapping();
34 
InitDateMapping()35         private static DateMapping[] InitDateMapping()
36         {
37             short[] rawData = new short[]
38             {
39                 //These data is taken from Tables/Excel/UmAlQura.xls please make sure that the two places are in sync
40                 /*  DaysPerM     GY      GM     GD     D1   D2   D3   D4   D5   D6   D7   D8   D9   D10  D11  D12
41                 1318*/0x02EA,    1900,     4,    30,/* 0    1    0    1    0    1    1    1    0    1    0    0    4/30/1900
42                 1319*/0x06E9,    1901,     4,    19,/* 1    0    0    1    0    1    1    1    0    1    1    0    4/19/1901
43                 1320*/0x0ED2,    1902,     4,     9,/* 0    1    0    0    1    0    1    1    0    1    1    1    4/9/1902
44                 1321*/0x0EA4,    1903,     3,    30,/* 0    0    1    0    0    1    0    1    0    1    1    1    3/30/1903
45                 1322*/0x0D4A,    1904,     3,    18,/* 0    1    0    1    0    0    1    0    1    0    1    1    3/18/1904
46                 1323*/0x0A96,    1905,     3,     7,/* 0    1    1    0    1    0    0    1    0    1    0    1    3/7/1905
47                 1324*/0x0536,    1906,     2,    24,/* 0    1    1    0    1    1    0    0    1    0    1    0    2/24/1906
48                 1325*/0x0AB5,    1907,     2,    13,/* 1    0    1    0    1    1    0    1    0    1    0    1    2/13/1907
49                 1326*/0x0DAA,    1908,     2,     3,/* 0    1    0    1    0    1    0    1    1    0    1    1    2/3/1908
50                 1327*/0x0BA4,    1909,     1,    23,/* 0    0    1    0    0    1    0    1    1    1    0    1    1/23/1909
51                 1328*/0x0B49,    1910,     1,    12,/* 1    0    0    1    0    0    1    0    1    1    0    1    1/12/1910
52                 1329*/0x0A93,    1911,     1,     1,/* 1    1    0    0    1    0    0    1    0    1    0    1    1/1/1911
53                 1330*/0x052B,    1911,    12,    21,/* 1    1    0    1    0    1    0    0    1    0    1    0    12/21/1911
54                 1331*/0x0A57,    1912,    12,     9,/* 1    1    1    0    1    0    1    0    0    1    0    1    12/9/1912
55                 1332*/0x04B6,    1913,    11,    29,/* 0    1    1    0    1    1    0    1    0    0    1    0    11/29/1913
56                 1333*/0x0AB5,    1914,    11,    18,/* 1    0    1    0    1    1    0    1    0    1    0    1    11/18/1914
57                 1334*/0x05AA,    1915,    11,     8,/* 0    1    0    1    0    1    0    1    1    0    1    0    11/8/1915
58                 1335*/0x0D55,    1916,    10,    27,/* 1    0    1    0    1    0    1    0    1    0    1    1    10/27/1916
59                 1336*/0x0D2A,    1917,    10,    17,/* 0    1    0    1    0    1    0    0    1    0    1    1    10/17/1917
60                 1337*/0x0A56,    1918,    10,     6,/* 0    1    1    0    1    0    1    0    0    1    0    1    10/6/1918
61                 1338*/0x04AE,    1919,     9,    25,/* 0    1    1    1    0    1    0    1    0    0    1    0    9/25/1919
62                 1339*/0x095D,    1920,     9,    13,/* 1    0    1    1    1    0    1    0    1    0    0    1    9/13/1920
63                 1340*/0x02EC,    1921,     9,     3,/* 0    0    1    1    0    1    1    1    0    1    0    0    9/3/1921
64                 1341*/0x06D5,    1922,     8,    23,/* 1    0    1    0    1    0    1    1    0    1    1    0    8/23/1922
65                 1342*/0x06AA,    1923,     8,    13,/* 0    1    0    1    0    1    0    1    0    1    1    0    8/13/1923
66                 1343*/0x0555,    1924,     8,     1,/* 1    0    1    0    1    0    1    0    1    0    1    0    8/1/1924
67                 1344*/0x04AB,    1925,     7,    21,/* 1    1    0    1    0    1    0    1    0    0    1    0    7/21/1925
68                 1345*/0x095B,    1926,     7,    10,/* 1    1    0    1    1    0    1    0    1    0    0    1    7/10/1926
69                 1346*/0x02BA,    1927,     6,    30,/* 0    1    0    1    1    1    0    1    0    1    0    0    6/30/1927
70                 1347*/0x0575,    1928,     6,    18,/* 1    0    1    0    1    1    1    0    1    0    1    0    6/18/1928
71                 1348*/0x0BB2,    1929,     6,     8,/* 0    1    0    0    1    1    0    1    1    1    0    1    6/8/1929
72                 1349*/0x0764,    1930,     5,    29,/* 0    0    1    0    0    1    1    0    1    1    1    0    5/29/1930
73                 1350*/0x0749,    1931,     5,    18,/* 1    0    0    1    0    0    1    0    1    1    1    0    5/18/1931
74                 1351*/0x0655,    1932,     5,     6,/* 1    0    1    0    1    0    1    0    0    1    1    0    5/6/1932
75                 1352*/0x02AB,    1933,     4,    25,/* 1    1    0    1    0    1    0    1    0    1    0    0    4/25/1933
76                 1353*/0x055B,    1934,     4,    14,/* 1    1    0    1    1    0    1    0    1    0    1    0    4/14/1934
77                 1354*/0x0ADA,    1935,     4,     4,/* 0    1    0    1    1    0    1    1    0    1    0    1    4/4/1935
78                 1355*/0x06D4,    1936,     3,    24,/* 0    0    1    0    1    0    1    1    0    1    1    0    3/24/1936
79                 1356*/0x0EC9,    1937,     3,    13,/* 1    0    0    1    0    0    1    1    0    1    1    1    3/13/1937
80                 1357*/0x0D92,    1938,     3,     3,/* 0    1    0    0    1    0    0    1    1    0    1    1    3/3/1938
81                 1358*/0x0D25,    1939,     2,    20,/* 1    0    1    0    0    1    0    0    1    0    1    1    2/20/1939
82                 1359*/0x0A4D,    1940,     2,     9,/* 1    0    1    1    0    0    1    0    0    1    0    1    2/9/1940
83                 1360*/0x02AD,    1941,     1,    28,/* 1    0    1    1    0    1    0    1    0    1    0    0    1/28/1941
84                 1361*/0x056D,    1942,     1,    17,/* 1    0    1    1    0    1    1    0    1    0    1    0    1/17/1942
85                 1362*/0x0B6A,    1943,     1,     7,/* 0    1    0    1    0    1    1    0    1    1    0    1    1/7/1943
86                 1363*/0x0B52,    1943,    12,    28,/* 0    1    0    0    1    0    1    0    1    1    0    1    12/28/1943
87                 1364*/0x0AA5,    1944,    12,    16,/* 1    0    1    0    0    1    0    1    0    1    0    1    12/16/1944
88                 1365*/0x0A4B,    1945,    12,     5,/* 1    1    0    1    0    0    1    0    0    1    0    1    12/5/1945
89                 1366*/0x0497,    1946,    11,    24,/* 1    1    1    0    1    0    0    1    0    0    1    0    11/24/1946
90                 1367*/0x0937,    1947,    11,    13,/* 1    1    1    0    1    1    0    0    1    0    0    1    11/13/1947
91                 1368*/0x02B6,    1948,    11,     2,/* 0    1    1    0    1    1    0    1    0    1    0    0    11/2/1948
92                 1369*/0x0575,    1949,    10,    22,/* 1    0    1    0    1    1    1    0    1    0    1    0    10/22/1949
93                 1370*/0x0D6A,    1950,    10,    12,/* 0    1    0    1    0    1    1    0    1    0    1    1    10/12/1950
94                 1371*/0x0D52,    1951,    10,     2,/* 0    1    0    0    1    0    1    0    1    0    1    1    10/2/1951
95                 1372*/0x0A96,    1952,     9,    20,/* 0    1    1    0    1    0    0    1    0    1    0    1    9/20/1952
96                 1373*/0x092D,    1953,     9,     9,/* 1    0    1    1    0    1    0    0    1    0    0    1    9/9/1953
97                 1374*/0x025D,    1954,     8,    29,/* 1    0    1    1    1    0    1    0    0    1    0    0    8/29/1954
98                 1375*/0x04DD,    1955,     8,    18,/* 1    0    1    1    1    0    1    1    0    0    1    0    8/18/1955
99                 1376*/0x0ADA,    1956,     8,     7,/* 0    1    0    1    1    0    1    1    0    1    0    1    8/7/1956
100                 1377*/0x05D4,    1957,     7,    28,/* 0    0    1    0    1    0    1    1    1    0    1    0    7/28/1957
101                 1378*/0x0DA9,    1958,     7,    17,/* 1    0    0    1    0    1    0    1    1    0    1    1    7/17/1958
102                 1379*/0x0D52,    1959,     7,     7,/* 0    1    0    0    1    0    1    0    1    0    1    1    7/7/1959
103                 1380*/0x0AAA,    1960,     6,    25,/* 0    1    0    1    0    1    0    1    0    1    0    1    6/25/1960
104                 1381*/0x04D6,    1961,     6,    14,/* 0    1    1    0    1    0    1    1    0    0    1    0    6/14/1961
105                 1382*/0x09B6,    1962,     6,     3,/* 0    1    1    0    1    1    0    1    1    0    0    1    6/3/1962
106                 1383*/0x0374,    1963,     5,    24,/* 0    0    1    0    1    1    1    0    1    1    0    0    5/24/1963
107                 1384*/0x0769,    1964,     5,    12,/* 1    0    0    1    0    1    1    0    1    1    1    0    5/12/1964
108                 1385*/0x0752,    1965,     5,     2,/* 0    1    0    0    1    0    1    0    1    1    1    0    5/2/1965
109                 1386*/0x06A5,    1966,     4,    21,/* 1    0    1    0    0    1    0    1    0    1    1    0    4/21/1966
110                 1387*/0x054B,    1967,     4,    10,/* 1    1    0    1    0    0    1    0    1    0    1    0    4/10/1967
111                 1388*/0x0AAB,    1968,     3,    29,/* 1    1    0    1    0    1    0    1    0    1    0    1    3/29/1968
112                 1389*/0x055A,    1969,     3,    19,/* 0    1    0    1    1    0    1    0    1    0    1    0    3/19/1969
113                 1390*/0x0AD5,    1970,     3,     8,/* 1    0    1    0    1    0    1    1    0    1    0    1    3/8/1970
114                 1391*/0x0DD2,    1971,     2,    26,/* 0    1    0    0    1    0    1    1    1    0    1    1    2/26/1971
115                 1392*/0x0DA4,    1972,     2,    16,/* 0    0    1    0    0    1    0    1    1    0    1    1    2/16/1972
116                 1393*/0x0D49,    1973,     2,     4,/* 1    0    0    1    0    0    1    0    1    0    1    1    2/4/1973
117                 1394*/0x0A95,    1974,     1,    24,/* 1    0    1    0    1    0    0    1    0    1    0    1    1/24/1974
118                 1395*/0x052D,    1975,     1,    13,/* 1    0    1    1    0    1    0    0    1    0    1    0    1/13/1975
119                 1396*/0x0A5D,    1976,     1,     2,/* 1    0    1    1    1    0    1    0    0    1    0    1    1/2/1976
120                 1397*/0x055A,    1976,    12,    22,/* 0    1    0    1    1    0    1    0    1    0    1    0    12/22/1976
121                 1398*/0x0AD5,    1977,    12,    11,/* 1    0    1    0    1    0    1    1    0    1    0    1    12/11/1977
122                 1399*/0x06AA,    1978,    12,     1,/* 0    1    0    1    0    1    0    1    0    1    1    0    12/1/1978
123                 1400*/0x0695,    1979,    11,    20,/* 1    0    1    0    1    0    0    1    0    1    1    0    11/20/1979
124                 1401*/0x052B,    1980,    11,     8,/* 1    1    0    1    0    1    0    0    1    0    1    0    11/8/1980
125                 1402*/0x0A57,    1981,    10,    28,/* 1    1    1    0    1    0    1    0    0    1    0    1    10/28/1981
126                 1403*/0x04AE,    1982,    10,    18,/* 0    1    1    1    0    1    0    1    0    0    1    0    10/18/1982
127                 1404*/0x0976,    1983,    10,     7,/* 0    1    1    0    1    1    1    0    1    0    0    1    10/7/1983
128                 1405*/0x056C,    1984,     9,    26,/* 0    0    1    1    0    1    1    0    1    0    1    0    9/26/1984
129                 1406*/0x0B55,    1985,     9,    15,/* 1    0    1    0    1    0    1    0    1    1    0    1    9/15/1985
130                 1407*/0x0AAA,    1986,     9,     5,/* 0    1    0    1    0    1    0    1    0    1    0    1    9/5/1986
131                 1408*/0x0A55,    1987,     8,    25,/* 1    0    1    0    1    0    1    0    0    1    0    1    8/25/1987
132                 1409*/0x04AD,    1988,     8,    13,/* 1    0    1    1    0    1    0    1    0    0    1    0    8/13/1988
133                 1410*/0x095D,    1989,     8,     2,/* 1    0    1    1    1    0    1    0    1    0    0    1    8/2/1989
134                 1411*/0x02DA,    1990,     7,    23,/* 0    1    0    1    1    0    1    1    0    1    0    0    7/23/1990
135                 1412*/0x05D9,    1991,     7,    12,/* 1    0    0    1    1    0    1    1    1    0    1    0    7/12/1991
136                 1413*/0x0DB2,    1992,     7,     1,/* 0    1    0    0    1    1    0    1    1    0    1    1    7/1/1992
137                 1414*/0x0BA4,    1993,     6,    21,/* 0    0    1    0    0    1    0    1    1    1    0    1    6/21/1993
138                 1415*/0x0B4A,    1994,     6,    10,/* 0    1    0    1    0    0    1    0    1    1    0    1    6/10/1994
139                 1416*/0x0A55,    1995,     5,    30,/* 1    0    1    0    1    0    1    0    0    1    0    1    5/30/1995
140                 1417*/0x02B5,    1996,     5,    18,/* 1    0    1    0    1    1    0    1    0    1    0    0    5/18/1996
141                 1418*/0x0575,    1997,     5,     7,/* 1    0    1    0    1    1    1    0    1    0    1    0    5/7/1997
142                 1419*/0x0B6A,    1998,     4,    27,/* 0    1    0    1    0    1    1    0    1    1    0    1    4/27/1998
143                 1420*/0x0BD2,    1999,     4,    17,/* 0    1    0    0    1    0    1    1    1    1    0    1    4/17/1999
144                 1421*/0x0BC4,    2000,     4,     6,/* 0    0    1    0    0    0    1    1    1    1    0    1    4/6/2000
145                 1422*/0x0B89,    2001,     3,    26,/* 1    0    0    1    0    0    0    1    1    1    0    1    3/26/2001
146                 1423*/0x0A95,    2002,     3,    15,/* 1    0    1    0    1    0    0    1    0    1    0    1    3/15/2002
147                 1424*/0x052D,    2003,     3,     4,/* 1    0    1    1    0    1    0    0    1    0    1    0    3/4/2003
148                 1425*/0x05AD,    2004,     2,    21,/* 1    0    1    1    0    1    0    1    1    0    1    0    2/21/2004
149                 1426*/0x0B6A,    2005,     2,    10,/* 0    1    0    1    0    1    1    0    1    1    0    1    2/10/2005
150                 1427*/0x06D4,    2006,     1,    31,/* 0    0    1    0    1    0    1    1    0    1    1    0    1/31/2006
151                 1428*/0x0DC9,    2007,     1,    20,/* 1    0    0    1    0    0    1    1    1    0    1    1    1/20/2007
152                 1429*/0x0D92,    2008,     1,    10,/* 0    1    0    0    1    0    0    1    1    0    1    1    1/10/2008
153                 1430*/0x0AA6,    2008,    12,    29,/* 0    1    1    0    0    1    0    1    0    1    0    1    12/29/2008
154                 1431*/0x0956,    2009,    12,    18,/* 0    1    1    0    1    0    1    0    1    0    0    1    12/18/2009
155                 1432*/0x02AE,    2010,    12,     7,/* 0    1    1    1    0    1    0    1    0    1    0    0    12/7/2010
156                 1433*/0x056D,    2011,    11,    26,/* 1    0    1    1    0    1    1    0    1    0    1    0    11/26/2011
157                 1434*/0x036A,    2012,    11,    15,/* 0    1    0    1    0    1    1    0    1    1    0    0    11/15/2012
158                 1435*/0x0B55,    2013,    11,     4,/* 1    0    1    0    1    0    1    0    1    1    0    1    11/4/2013
159                 1436*/0x0AAA,    2014,    10,    25,/* 0    1    0    1    0    1    0    1    0    1    0    1    10/25/2014
160                 1437*/0x094D,    2015,    10,    14,/* 1    0    1    1    0    0    1    0    1    0    0    1    10/14/2015
161                 1438*/0x049D,    2016,    10,     2,/* 1    0    1    1    1    0    0    1    0    0    1    0    10/2/2016
162                 1439*/0x095D,    2017,     9,    21,/* 1    0    1    1    1    0    1    0    1    0    0    1    9/21/2017
163                 1440*/0x02BA,    2018,     9,    11,/* 0    1    0    1    1    1    0    1    0    1    0    0    9/11/2018
164                 1441*/0x05B5,    2019,     8,    31,/* 1    0    1    0    1    1    0    1    1    0    1    0    8/31/2019
165                 1442*/0x05AA,    2020,     8,    20,/* 0    1    0    1    0    1    0    1    1    0    1    0    8/20/2020
166                 1443*/0x0D55,    2021,     8,     9,/* 1    0    1    0    1    0    1    0    1    0    1    1    8/9/2021
167                 1444*/0x0A9A,    2022,     7,    30,/* 0    1    0    1    1    0    0    1    0    1    0    1    7/30/2022
168                 1445*/0x092E,    2023,     7,    19,/* 0    1    1    1    0    1    0    0    1    0    0    1    7/19/2023
169                 1446*/0x026E,    2024,     7,     7,/* 0    1    1    1    0    1    1    0    0    1    0    0    7/7/2024
170                 1447*/0x055D,    2025,     6,    26,/* 1    0    1    1    1    0    1    0    1    0    1    0    6/26/2025
171                 1448*/0x0ADA,    2026,     6,    16,/* 0    1    0    1    1    0    1    1    0    1    0    1    6/16/2026
172                 1449*/0x06D4,    2027,     6,     6,/* 0    0    1    0    1    0    1    1    0    1    1    0    6/6/2027
173                 1450*/0x06A5,    2028,     5,    25,/* 1    0    1    0    0    1    0    1    0    1    1    0    5/25/2028
174                 1451*/0x054B,    2029,     5,    14,/* 1    1    0    1    0    0    1    0    1    0    1    0    5/14/2029
175                 1452*/0x0A97,    2030,     5,     3,/* 1    1    1    0    1    0    0    1    0    1    0    1    5/3/2030
176                 1453*/0x054E,    2031,     4,    23,/* 0    1    1    1    0    0    1    0    1    0    1    0    4/23/2031
177                 1454*/0x0AAE,    2032,     4,    11,/* 0    1    1    1    0    1    0    1    0    1    0    1    4/11/2032
178                 1455*/0x05AC,    2033,     4,     1,/* 0    0    1    1    0    1    0    1    1    0    1    0    4/1/2033
179                 1456*/0x0BA9,    2034,     3,    21,/* 1    0    0    1    0    1    0    1    1    1    0    1    3/21/2034
180                 1457*/0x0D92,    2035,     3,    11,/* 0    1    0    0    1    0    0    1    1    0    1    1    3/11/2035
181                 1458*/0x0B25,    2036,     2,    28,/* 1    0    1    0    0    1    0    0    1    1    0    1    2/28/2036
182                 1459*/0x064B,    2037,     2,    16,/* 1    1    0    1    0    0    1    0    0    1    1    0    2/16/2037
183                 1460*/0x0CAB,    2038,     2,     5,/* 1    1    0    1    0    1    0    1    0    0    1    1    2/5/2038
184                 1461*/0x055A,    2039,     1,    26,/* 0    1    0    1    1    0    1    0    1    0    1    0    1/26/2039
185                 1462*/0x0B55,    2040,     1,    15,/* 1    0    1    0    1    0    1    0    1    1    0    1    1/15/2040
186                 1463*/0x06D2,    2041,     1,     4,/* 0    1    0    0    1    0    1    1    0    1    1    0    1/4/2041
187                 1464*/0x0EA5,    2041,     12,   24,/* 1    0    1    0    0    1    0    1    0    1    1    1    12/24/2041
188                 1465*/0x0E4A,    2042,     12,   14,/* 0    1    0    1    0    0    1    0    0    1    1    1    12/14/2042
189                 1466*/0x0A95,    2043,     12,    3,/* 1    0    1    0    1    0    0    1    0    1    0    1    12/3/2043
190                 1467*/0x052D,    2044,     11,   21,/* 1    0    1    1    0    1    0    0    1    0    1    0    11/21/2044
191                 1468*/0x0AAD,    2045,     11,   10,/* 1    0    1    1    0    1    0    1    0    1    0    1    11/10/2045
192                 1469*/0x036C,    2046,     10,   31,/* 0    0    1    1    0    1    1    0    1    1    0    0    10/31/2046
193                 1470*/0x0759,    2047,     10,   20,/* 1    0    0    1    1    0    1    0    1    1    1    0    10/20/2047
194                 1471*/0x06D2,    2048,     10,    9,/* 0    1    0    0    1    0    1    1    0    1    1    0    10/9/2048
195                 1472*/0x0695,    2049,     9,    28,/* 1    0    1    0    1    0    0    1    0    1    1    0    9/28/2049
196                 1473*/0x052D,    2050,     9,    17,/* 1    0    1    1    0    1    0    0    1    0    1    0    9/17/2050
197                 1474*/0x0A5B,    2051,     9,     6,/* 1    1    0    1    1    0    1    0    0    1    0    1    9/6/2051
198                 1475*/0x04BA,    2052,     8,    26,/* 0    1    0    1    1    1    0    1    0    0    1    0    8/26/2052
199                 1476*/0x09BA,    2053,     8,    15,/* 0    1    0    1    1    1    0    1    1    0    0    1    8/15/2053
200                 1477*/0x03B4,    2054,     8,     5,/* 0    0    1    0    1    1    0    1    1    1    0    0    8/5/2054
201                 1478*/0x0B69,    2055,     7,    25,/* 1    0    0    1    0    1    1    0    1    1    0    1    7/25/2055
202                 1479*/0x0B52,    2056,     7,    14,/* 0    1    0    0    1    0    1    0    1    1    0    1    7/14/2056
203                 1480*/0x0AA6,    2057,     7,     3,/* 0    1    1    0    0    1    0    1    0    1    0    1    7/3/2057
204                 1481*/0x04B6,    2058,     6,    22,/* 0    1    1    0    1    1    0    1    0    0    1    0    6/22/2058
205                 1482*/0x096D,    2059,     6,    11,/* 1    0    1    1    0    1    1    0    1    0    0    1    6/11/2059
206                 1483*/0x02EC,    2060,     5,    31,/* 0    0    1    1    0    1    1    1    0    1    0    0    5/31/2060
207                 1484*/0x06D9,    2061,     5,    20,/* 1    0    0    1    1    0    1    1    0    1    1    0    5/20/2061
208                 1485*/0x0EB2,    2062,     5,    10,/* 0    1    0    0    1    1    0    1    0    1    1    1    5/10/2062
209                 1486*/0x0D54,    2063,     4,    30,/* 0    0    1    0    1    0    1    0    1    0    1    1    4/30/2063
210                 1487*/0x0D2A,    2064,     4,    18,/* 0    1    0    1    0    1    0    0    1    0    1    1    4/18/2064
211                 1488*/0x0A56,    2065,     4,     7,/* 0    1    1    0    1    0    1    0    0    1    0    1    4/7/2065
212                 1489*/0x04AE,    2066,     3,    27,/* 0    1    1    1    0    1    0    1    0    0    1    0    3/27/2066
213                 1490*/0x096D,    2067,     3,    16,/* 1    0    1    1    0    1    1    0    1    0    0    1    3/16/2067
214                 1491*/0x0D6A,    2068,     3,     5,/* 0    1    0    1    0    1    1    0    1    0    1    1    3/5/2068
215                 1492*/0x0B54,    2069,     2,    23,/* 0    0    1    0    1    0    1    0    1    1    0    1    2/23/2069
216                 1493*/0x0B29,    2070,     2,    12,/* 1    0    0    1    0    1    0    0    1    1    0    1    2/12/2070
217                 1494*/0x0A93,    2071,     2,     1,/* 1    1    0    0    1    0    0    1    0    1    0    1    2/1/2071
218                 1495*/0x052B,    2072,     1,    21,/* 1    1    0    1    0    1    0    0    1    0    1    0    1/21/2072
219                 1496*/0x0A57,    2073,     1,     9,/* 1    1    1    0    1    0    1    0    0    1    0    1    1/9/2073
220                 1497*/0x0536,    2073,     12,   30,/* 0    1    1    0    1    1    0    0    1    0    1    0    12/30/2073
221                 1498*/0x0AB5,    2074,     12,   19,/* 1    0    1    0    1    1    0    1    0    1    0    1    12/19/2074
222                 1499*/0x06AA,    2075,     12,    9,/* 0    1    0    1    0    1    0    1    0    1    1    0    12/9/2075
223                 1500*/0x0E93,    2076,     11,   27,/* 1    1    0    0    1    0    0    1    0    1    1    1    11/27/2076
224                 1501*/     0,    2077,     11,   17,/* 0    0    0    0    0    0    0    0    0    0    0    0    11/17/2077
225     */      };
226             // Direct inline initialization of DateMapping array would produce a lot of code bloat.
227 
228             // We take advantage of C# compiler compiles inline initialization of primitive type array into very compact code.
229             // So we start with raw data stored in primitive type array, and initialize the DateMapping out of it
230 
231             DateMapping[] mapping = new DateMapping[rawData.Length / 4];
232             for (int i = 0; i < mapping.Length; i++)
233                 mapping[i] = new DateMapping(rawData[i * 4], rawData[i * 4 + 1], rawData[i * 4 + 2], rawData[i * 4 + 3]);
234             return mapping;
235         }
236 
237         public const int UmAlQuraEra = 1;
238 
239         internal const int DateCycle = 30;
240         internal const int DatePartYear = 0;
241         internal const int DatePartDayOfYear = 1;
242         internal const int DatePartMonth = 2;
243         internal const int DatePartDay = 3;
244 
245 
246         // This is the minimal Gregorian date that we support in the UmAlQuraCalendar.
247         internal static DateTime minDate = new DateTime(1900, 4, 30);
248         internal static DateTime maxDate = new DateTime((new DateTime(2077, 11, 16, 23, 59, 59, 999)).Ticks + 9999);
249 
250         public override DateTime MinSupportedDateTime
251         {
252             get
253             {
254                 return (minDate);
255             }
256         }
257 
258         public override DateTime MaxSupportedDateTime
259         {
260             get
261             {
262                 return (maxDate);
263             }
264         }
265 
266         public override CalendarAlgorithmType AlgorithmType
267         {
268             get
269             {
270                 return CalendarAlgorithmType.LunarCalendar;
271             }
272         }
273 
UmAlQuraCalendar()274         public UmAlQuraCalendar()
275         {
276         }
277 
278         internal override CalendarId BaseCalendarID
279         {
280             get
281             {
282                 return (CalendarId.HIJRI);
283             }
284         }
285 
286         internal override CalendarId ID
287         {
288             get
289             {
290                 return (CalendarId.UMALQURA);
291             }
292         }
293 
294         protected override int DaysInYearBeforeMinSupportedYear
295         {
296             get
297             {
298                 // HijriCalendar has same number of days as UmAlQuraCalendar for any given year
299                 // HijriCalendar says year 1317 has 355 days.
300                 return 355;
301             }
302         }
303 
304         /*==========================ConvertHijriToGregorian==========================
305         ** Purpose: convert Hdate(year,month,day) to Gdate(year,month,day)
306         ** Arguments:
307         ** Input/Ouput: Hijrah  date: year:yh, month:mh, day:dh
308         ** Output: Gregorian date: year:yg, month:mg, day:dg , day of week:dayweek
309         **       and returns flag found:1 not found:0
310         =========================ConvertHijriToGregorian============================*/
ConvertHijriToGregorian(int HijriYear, int HijriMonth, int HijriDay, ref int yg, ref int mg, ref int dg)311         private static void ConvertHijriToGregorian(int HijriYear, int HijriMonth, int HijriDay, ref int yg, ref int mg, ref int dg)
312         {
313             Debug.Assert((HijriYear >= MinCalendarYear) && (HijriYear <= MaxCalendarYear), "Hijri year is out of range.");
314             Debug.Assert(HijriMonth >= 1, "Hijri month is out of range.");
315             Debug.Assert(HijriDay >= 1, "Hijri day is out of range.");
316             int index, b, nDays = HijriDay - 1;
317             DateTime dt;
318 
319 
320             index = HijriYear - MinCalendarYear;
321             dt = s_hijriYearInfo[index].GregorianDate;
322 
323 
324             b = s_hijriYearInfo[index].HijriMonthsLengthFlags;
325 
326             for (int m = 1; m < HijriMonth; m++)
327             {
328                 nDays = nDays + 29 + (b & 1);   /* Add the months lengths before mh */
329                 b = b >> 1;
330             }
331 
332             dt = dt.AddDays(nDays);
333             dt.GetDatePart(out yg, out mg, out dg);
334         }
335 
336         /*=================================GetAbsoluteDateUmAlQura==========================
337         **Action: Gets the Absolute date for the given UmAlQura date.  The absolute date means
338         **       the number of days from January 1st, 1 A.D.
339         **Returns:
340         **Arguments:
341         **Exceptions:
342         ============================================================================*/
GetAbsoluteDateUmAlQura(int year, int month, int day)343         private static long GetAbsoluteDateUmAlQura(int year, int month, int day)
344         {
345             //Caller should check the validaty of year, month and day.
346 
347             int yg = 0, mg = 0, dg = 0;
348             ConvertHijriToGregorian(year, month, day, ref yg, ref mg, ref dg);
349             return GregorianCalendar.GetAbsoluteDate(yg, mg, dg);
350         }
351 
CheckTicksRange(long ticks)352         internal static void CheckTicksRange(long ticks)
353         {
354             if (ticks < minDate.Ticks || ticks > maxDate.Ticks)
355             {
356                 throw new ArgumentOutOfRangeException(
357                             "time",
358                             String.Format(
359                                 CultureInfo.InvariantCulture,
360                                 SR.ArgumentOutOfRange_CalendarRange,
361                                 minDate,
362                                 maxDate));
363             }
364         }
365 
CheckEraRange(int era)366         internal static void CheckEraRange(int era)
367         {
368             if (era != CurrentEra && era != UmAlQuraEra)
369             {
370                 throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue);
371             }
372         }
373 
CheckYearRange(int year, int era)374         internal static void CheckYearRange(int year, int era)
375         {
376             CheckEraRange(era);
377             if (year < MinCalendarYear || year > MaxCalendarYear)
378             {
379                 throw new ArgumentOutOfRangeException(
380                             nameof(year),
381                             String.Format(
382                                 CultureInfo.CurrentCulture,
383                                 SR.ArgumentOutOfRange_Range,
384                                 MinCalendarYear,
385                                 MaxCalendarYear));
386             }
387         }
388 
CheckYearMonthRange(int year, int month, int era)389         internal static void CheckYearMonthRange(int year, int month, int era)
390         {
391             CheckYearRange(year, era);
392             if (month < 1 || month > 12)
393             {
394                 throw new ArgumentOutOfRangeException(nameof(month), SR.ArgumentOutOfRange_Month);
395             }
396         }
397 
398         /*========================ConvertGregorianToHijri============================
399         ** Purpose: convert DateTime to Hdate(year,month,day)
400         ** Arguments:
401         ** Input: DateTime
402         ** Output: Hijrah  date: year:yh, month:mh, day:dh
403         ============================================================================*/
ConvertGregorianToHijri(DateTime time, ref int HijriYear, ref int HijriMonth, ref int HijriDay)404         private static void ConvertGregorianToHijri(DateTime time, ref int HijriYear, ref int HijriMonth, ref int HijriDay)
405         {
406             int index, b, DaysPerThisMonth;
407             double nDays;
408             TimeSpan ts;
409             int yh1 = 0, mh1 = 0, dh1 = 0;
410 
411             Debug.Assert((time.Ticks >= minDate.Ticks) && (time.Ticks <= maxDate.Ticks), "Gregorian date is out of range.");
412 
413             // Find the index where we should start our search by quessing the Hijri year that we will be in HijriYearInfo.
414             // A Hijri year is 354 or 355 days.  Use 355 days so that we will search from a lower index.
415 
416             index = (int)((time.Ticks - minDate.Ticks) / Calendar.TicksPerDay) / 355;
417             do
418             {
419             } while (time.CompareTo(s_hijriYearInfo[++index].GregorianDate) > 0); //while greater
420 
421             if (time.CompareTo(s_hijriYearInfo[index].GregorianDate) != 0)
422             {
423                 index--;
424             }
425 
426             ts = time.Subtract(s_hijriYearInfo[index].GregorianDate);
427             yh1 = index + MinCalendarYear;
428 
429             mh1 = 1;
430             dh1 = 1;
431             nDays = ts.TotalDays;
432             b = s_hijriYearInfo[index].HijriMonthsLengthFlags;
433             DaysPerThisMonth = 29 + (b & 1);
434 
435             while (nDays >= DaysPerThisMonth)
436             {
437                 nDays -= DaysPerThisMonth;
438                 b = b >> 1;
439                 DaysPerThisMonth = 29 + (b & 1);
440                 mh1++;
441             }
442             dh1 += (int)nDays;
443 
444             HijriDay = dh1;
445             HijriMonth = mh1;
446             HijriYear = yh1;
447         }
448 
449         /*=================================GetDatePart==========================
450         **Action: Returns a given date part of this <i>DateTime</i>. This method is used
451         **       to compute the year, day-of-year, month, or day part.
452         **Returns:
453         **Arguments:
454         **Exceptions:  ArgumentException if part is incorrect.
455         **Notes:
456         **      First, we get the absolute date (the number of days from January 1st, 1 A.C) for the given ticks.
457         **      Use the formula (((AbsoluteDate - 226894) * 33) / (33 * 365 + 8)) + 1, we can a rough value for the UmAlQura year.
458         **      In order to get the exact UmAlQura year, we compare the exact absolute date for UmAlQuraYear and (UmAlQuraYear + 1).
459         **      From here, we can get the correct UmAlQura year.
460         ============================================================================*/
461 
GetDatePart(DateTime time, int part)462         internal virtual int GetDatePart(DateTime time, int part)
463         {
464             int UmAlQuraYear = 0;              // UmAlQura year
465             int UmAlQuraMonth = 0;             // UmAlQura month
466             int UmAlQuraDay = 0;               // UmAlQura day
467             long ticks = time.Ticks;
468             CheckTicksRange(ticks);
469 
470             ConvertGregorianToHijri(time, ref UmAlQuraYear, ref UmAlQuraMonth, ref UmAlQuraDay);
471 
472             if (part == DatePartYear)
473                 return (UmAlQuraYear);
474 
475             if (part == DatePartMonth)
476                 return (UmAlQuraMonth);
477 
478             if (part == DatePartDay)
479                 return (UmAlQuraDay);
480 
481             if (part == DatePartDayOfYear)
482                 return (int)(GetAbsoluteDateUmAlQura(UmAlQuraYear, UmAlQuraMonth, UmAlQuraDay) - GetAbsoluteDateUmAlQura(UmAlQuraYear, 1, 1) + 1);
483 
484             // Incorrect part value.
485             throw new InvalidOperationException(SR.InvalidOperation_DateTimeParsing);
486         }
487 
488         // Returns the DateTime resulting from adding the given number of
489         // months to the specified DateTime. The result is computed by incrementing
490         // (or decrementing) the year and month parts of the specified DateTime by
491         // value months, and, if required, adjusting the day part of the
492         // resulting date downwards to the last day of the resulting month in the
493         // resulting year. The time-of-day part of the result is the same as the
494         // time-of-day part of the specified DateTime.
495         //
496         // In more precise terms, considering the specified DateTime to be of the
497         // form y / m / d + t, where y is the
498         // year, m is the month, d is the day, and t is the
499         // time-of-day, the result is y1 / m1 / d1 + t,
500         // where y1 and m1 are computed by adding value months
501         // to y and m, and d1 is the largest value less than
502         // or equal to d that denotes a valid day in month m1 of year
503         // y1.
504         //
505 
506 
AddMonths(DateTime time, int months)507         public override DateTime AddMonths(DateTime time, int months)
508         {
509             if (months < -120000 || months > 120000)
510             {
511                 throw new ArgumentOutOfRangeException(
512                             nameof(months),
513                             String.Format(
514                                 CultureInfo.CurrentCulture,
515                                 SR.ArgumentOutOfRange_Range,
516                                 -120000,
517                                 120000));
518             }
519             // Get the date in UmAlQura calendar.
520             int y = GetDatePart(time, DatePartYear);
521             int m = GetDatePart(time, DatePartMonth);
522             int d = GetDatePart(time, DatePartDay);
523             int i = m - 1 + months;
524 
525             if (i >= 0)
526             {
527                 m = i % 12 + 1;
528                 y = y + i / 12;
529             }
530             else
531             {
532                 m = 12 + (i + 1) % 12;
533                 y = y + (i - 11) / 12;
534             }
535 
536             if (d > 29)
537             {
538                 int days = GetDaysInMonth(y, m);
539                 if (d > days)
540                 {
541                     d = days;
542                 }
543             }
544             CheckYearRange(y, UmAlQuraEra);
545             DateTime dt = new DateTime(GetAbsoluteDateUmAlQura(y, m, d) * TicksPerDay + time.Ticks % TicksPerDay);
546             Calendar.CheckAddResult(dt.Ticks, MinSupportedDateTime, MaxSupportedDateTime);
547             return (dt);
548         }
549 
550         // Returns the DateTime resulting from adding the given number of
551         // years to the specified DateTime. The result is computed by incrementing
552         // (or decrementing) the year part of the specified DateTime by value
553         // years. If the month and day of the specified DateTime is 2/29, and if the
554         // resulting year is not a leap year, the month and day of the resulting
555         // DateTime becomes 2/28. Otherwise, the month, day, and time-of-day
556         // parts of the result are the same as those of the specified DateTime.
557         //
558 
559 
AddYears(DateTime time, int years)560         public override DateTime AddYears(DateTime time, int years)
561         {
562             return (AddMonths(time, years * 12));
563         }
564 
565         // Returns the day-of-month part of the specified DateTime. The returned
566         // value is an integer between 1 and 31.
567         //
568 
569 
GetDayOfMonth(DateTime time)570         public override int GetDayOfMonth(DateTime time)
571         {
572             return (GetDatePart(time, DatePartDay));
573         }
574 
575         // Returns the day-of-week part of the specified DateTime. The returned value
576         // is an integer between 0 and 6, where 0 indicates Sunday, 1 indicates
577         // Monday, 2 indicates Tuesday, 3 indicates Wednesday, 4 indicates
578         // Thursday, 5 indicates Friday, and 6 indicates Saturday.
579         //
580 
581 
GetDayOfWeek(DateTime time)582         public override DayOfWeek GetDayOfWeek(DateTime time)
583         {
584             return ((DayOfWeek)((int)(time.Ticks / TicksPerDay + 1) % 7));
585         }
586 
587         // Returns the day-of-year part of the specified DateTime. The returned value
588         // is an integer between 1 and 354 or 355.
589         //
590 
591 
GetDayOfYear(DateTime time)592         public override int GetDayOfYear(DateTime time)
593         {
594             return (GetDatePart(time, DatePartDayOfYear));
595         }
596 
597         /*
598         internal bool CouldBeLeapYear(int year)
599         {
600             return ((((year * 11) + 14) % 30) < 11);
601         }
602         */
603 
604         // Returns the number of days in the month given by the year and
605         // month arguments.
606         //
607 
608 
GetDaysInMonth(int year, int month, int era)609         public override int GetDaysInMonth(int year, int month, int era)
610         {
611             CheckYearMonthRange(year, month, era);
612 
613             if ((s_hijriYearInfo[year - MinCalendarYear].HijriMonthsLengthFlags & (1 << month - 1)) == 0)
614                 return 29;
615             else
616                 return 30;
617         }
618 
RealGetDaysInYear(int year)619         internal static int RealGetDaysInYear(int year)
620         {
621             int days = 0, b;
622 
623             Debug.Assert((year >= MinCalendarYear) && (year <= MaxCalendarYear), "Hijri year is out of range.");
624 
625             b = s_hijriYearInfo[year - MinCalendarYear].HijriMonthsLengthFlags;
626 
627             for (int m = 1; m <= 12; m++)
628             {
629                 days = days + 29 + (b & 1);   /* Add the months lengths before mh */
630                 b = b >> 1;
631             }
632             Debug.Assert((days == 354) || (days == 355), "Hijri year has to be 354 or 355 days.");
633             return days;
634         }
635 
636         // Returns the number of days in the year given by the year argument for the current era.
637         //
638 
639 
GetDaysInYear(int year, int era)640         public override int GetDaysInYear(int year, int era)
641         {
642             CheckYearRange(year, era);
643             return (RealGetDaysInYear(year));
644         }
645 
646         // Returns the era for the specified DateTime value.
647 
648 
GetEra(DateTime time)649         public override int GetEra(DateTime time)
650         {
651             CheckTicksRange(time.Ticks);
652             return (UmAlQuraEra);
653         }
654 
655 
656 
657         public override int[] Eras
658         {
659             get
660             {
661                 return (new int[] { UmAlQuraEra });
662             }
663         }
664 
665         // Returns the month part of the specified DateTime. The returned value is an
666         // integer between 1 and 12.
667         //
668 
669 
GetMonth(DateTime time)670         public override int GetMonth(DateTime time)
671         {
672             return (GetDatePart(time, DatePartMonth));
673         }
674 
675         // Returns the number of months in the specified year and era.
676 
677 
GetMonthsInYear(int year, int era)678         public override int GetMonthsInYear(int year, int era)
679         {
680             CheckYearRange(year, era);
681             return (12);
682         }
683 
684         // Returns the year part of the specified DateTime. The returned value is an
685         // integer between MinCalendarYear and MaxCalendarYear.
686         //
687 
688 
GetYear(DateTime time)689         public override int GetYear(DateTime time)
690         {
691             return (GetDatePart(time, DatePartYear));
692         }
693 
694         // Checks whether a given day in the specified era is a leap day. This method returns true if
695         // the date is a leap day, or false if not.
696         //
697 
698 
IsLeapDay(int year, int month, int day, int era)699         public override bool IsLeapDay(int year, int month, int day, int era)
700         {
701             if (day >= 1 && day <= 29)
702             {
703                 CheckYearMonthRange(year, month, era);
704                 return (false);
705             }
706 
707             // The year/month/era value checking is done in GetDaysInMonth().
708             int daysInMonth = GetDaysInMonth(year, month, era);
709             if (day < 1 || day > daysInMonth)
710             {
711                 throw new ArgumentOutOfRangeException(
712                             nameof(day),
713                             String.Format(
714                                 CultureInfo.CurrentCulture,
715                                 SR.ArgumentOutOfRange_Day,
716                                 daysInMonth,
717                                 month));
718             }
719             return (false);
720         }
721 
722         // Returns  the leap month in a calendar year of the specified era. This method returns 0
723         // if this calendar does not have leap month, or this year is not a leap year.
724         //
725 
726 
GetLeapMonth(int year, int era)727         public override int GetLeapMonth(int year, int era)
728         {
729             CheckYearRange(year, era);
730             return (0);
731         }
732 
733         // Checks whether a given month in the specified era is a leap month. This method returns true if
734         // month is a leap month, or false if not.
735         //
736 
737 
IsLeapMonth(int year, int month, int era)738         public override bool IsLeapMonth(int year, int month, int era)
739         {
740             CheckYearMonthRange(year, month, era);
741             return (false);
742         }
743 
744         // Checks whether a given year in the specified era is a leap year. This method returns true if
745         // year is a leap year, or false if not.
746         //
747 
748 
IsLeapYear(int year, int era)749         public override bool IsLeapYear(int year, int era)
750         {
751             CheckYearRange(year, era);
752             if (RealGetDaysInYear(year) == 355)
753                 return true;
754             else
755                 return false;
756         }
757 
758         // Returns the date and time converted to a DateTime value.  Throws an exception if the n-tuple is invalid.
759         //
760 
761 
ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era)762         public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era)
763         {
764             if (day >= 1 && day <= 29)
765             {
766                 CheckYearMonthRange(year, month, era);
767                 goto DayInRang;
768             }
769 
770             // The year/month/era value checking is done in GetDaysInMonth().
771             int daysInMonth = GetDaysInMonth(year, month, era);
772 
773             if (day < 1 || day > daysInMonth)
774             {
775                 throw new ArgumentOutOfRangeException(
776                            nameof(day),
777                            String.Format(
778                                CultureInfo.CurrentCulture,
779                                SR.ArgumentOutOfRange_Day,
780                                daysInMonth,
781                                month));
782             }
783         DayInRang:
784             long lDate = GetAbsoluteDateUmAlQura(year, month, day);
785 
786             if (lDate >= 0)
787             {
788                 return (new DateTime(lDate * GregorianCalendar.TicksPerDay + TimeToTicks(hour, minute, second, millisecond)));
789             }
790             else
791             {
792                 throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadYearMonthDay);
793             }
794         }
795 
796         private const int DEFAULT_TWO_DIGIT_YEAR_MAX = 1451;
797 
798 
799 
800         public override int TwoDigitYearMax
801         {
802             get
803             {
804                 if (twoDigitYearMax == -1)
805                 {
806                     twoDigitYearMax = GetSystemTwoDigitYearSetting(ID, DEFAULT_TWO_DIGIT_YEAR_MAX);
807                 }
808                 return (twoDigitYearMax);
809             }
810 
811             set
812             {
813                 if (value != 99 && (value < MinCalendarYear || value > MaxCalendarYear))
814                 {
815                     throw new ArgumentOutOfRangeException(
816                                 nameof(value),
817                                 String.Format(
818                                     CultureInfo.CurrentCulture,
819                                     SR.ArgumentOutOfRange_Range,
820                                     MinCalendarYear,
821                                     MaxCalendarYear));
822                 }
823                 VerifyWritable();
824                 // We allow year 99 to be set so that one can make ToFourDigitYearMax a no-op by setting TwoDigitYearMax to 99.
825                 twoDigitYearMax = value;
826             }
827         }
828 
829 
830 
ToFourDigitYear(int year)831         public override int ToFourDigitYear(int year)
832         {
833             if (year < 0)
834             {
835                 throw new ArgumentOutOfRangeException(nameof(year),
836                     SR.ArgumentOutOfRange_NeedNonNegNum);
837             }
838 
839             if (year < 100)
840             {
841                 return (base.ToFourDigitYear(year));
842             }
843 
844             if ((year < MinCalendarYear) || (year > MaxCalendarYear))
845             {
846                 throw new ArgumentOutOfRangeException(
847                             nameof(year),
848                             String.Format(
849                                     CultureInfo.CurrentCulture,
850                                     SR.ArgumentOutOfRange_Range,
851                                     MinCalendarYear,
852                                     MaxCalendarYear));
853             }
854             return (year);
855         }
856     }
857 }
858 
859