HebrewCalendar.cs 46 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124
  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. using System.Diagnostics;
  5. namespace System.Globalization
  6. {
  7. ////////////////////////////////////////////////////////////////////////////
  8. //
  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, & 19th years of a
  14. // 19-year cycle. Year = leap iff ((7y+1) mod 19 < 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. //
  49. ////////////////////////////////////////////////////////////////////////////
  50. /*
  51. ** Calendar support range:
  52. ** Calendar Minimum Maximum
  53. ** ========== ========== ==========
  54. ** Gregorian 1583/01/01 2239/09/29
  55. ** Hebrew 5343/04/07 5999/13/29
  56. */
  57. // Includes CHebrew implemetation;i.e All the code necessary for converting
  58. // Gregorian to Hebrew Lunar from 1583 to 2239.
  59. public class HebrewCalendar : Calendar
  60. {
  61. public static readonly int HebrewEra = 1;
  62. internal const int DatePartYear = 0;
  63. internal const int DatePartDayOfYear = 1;
  64. internal const int DatePartMonth = 2;
  65. internal const int DatePartDay = 3;
  66. internal const int DatePartDayOfWeek = 4;
  67. //
  68. // Hebrew Translation Table.
  69. //
  70. // This table is used to get the following Hebrew calendar information for a
  71. // given Gregorian year:
  72. // 1. The day of the Hebrew month corresponding to Gregorian January 1st
  73. // for a given Gregorian year.
  74. // 2. The month of the Hebrew month corresponding to Gregorian January 1st
  75. // for a given Gregorian year.
  76. // The information is not directly in the table. Instead, the info is decoded
  77. // by special values (numbers above 29 and below 1).
  78. // 3. The type of the Hebrew year for a given Gregorian year.
  79. //
  80. /*
  81. More notes:
  82. This table includes 2 numbers for each year.
  83. The offset into the table determines the year. (offset 0 is Gregorian year 1500)
  84. 1st number determines the day of the Hebrew month coresponeds to January 1st.
  85. 2nd number determines the type of the Hebrew year. (the type determines how
  86. many days are there in the year.)
  87. normal years : 1 = 353 days 2 = 354 days 3 = 355 days.
  88. Leap years : 4 = 383 5 384 6 = 385 days.
  89. A 99 means the year is not supported for translation.
  90. for convenience the table was defined for 750 year,
  91. but only 640 years are supported. (from 1583 to 2239)
  92. the years before 1582 (starting of Georgian calendar)
  93. and after 2239, are filled with 99.
  94. Greogrian January 1st falls usually in Tevet (4th month). Tevet has always 29 days.
  95. That's why, there no nead to specify the lunar month in the table.
  96. There are exceptions, these are coded by giving numbers above 29 and below 1.
  97. Actual decoding is takenig place whenever fetching information from the table.
  98. The function for decoding is in GetLunarMonthDay().
  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. For year 2000, we know it has a Hebrew year type 6, which means it has 385 days.
  103. And 1/1/2000 A.D. is Hebrew year 5760, 23rd day of 4th month.
  104. */
  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. // The first Gregorian year in HebrewTable.
  113. private const int FirstGregorianTableYear = 1583; // == Hebrew Year 5343
  114. // The last Gregorian year in HebrewTable.
  115. private const int LastGregorianTableYear = 2239; // == Hebrew Year 5999
  116. private const int TABLESIZE = (LastGregorianTableYear - FirstGregorianTableYear);
  117. private const int MinHebrewYear = HebrewYearOf1AD + FirstGregorianTableYear; // == 5343
  118. private const int MaxHebrewYear = HebrewYearOf1AD + LastGregorianTableYear; // == 5999
  119. private static readonly byte[] s_hebrewTable = {
  120. 7,3,17,3, // 1583-1584 (Hebrew year: 5343 - 5344)
  121. 0,4,11,2,21,6,1,3,13,2, // 1585-1589
  122. 25,4,5,3,16,2,27,6,9,1, // 1590-1594
  123. 20,2,0,6,11,3,23,4,4,2, // 1595-1599
  124. 14,3,27,4,8,2,18,3,28,6, // 1600
  125. 11,1,22,5,2,3,12,3,25,4, // 1605
  126. 6,2,16,3,26,6,8,2,20,1, // 1610
  127. 0,6,11,2,24,4,4,3,15,2, // 1615
  128. 25,6,8,1,19,2,29,6,9,3, // 1620
  129. 22,4,3,2,13,3,25,4,6,3, // 1625
  130. 17,2,27,6,7,3,19,2,31,4, // 1630
  131. 11,3,23,4,5,2,15,3,25,6, // 1635
  132. 6,2,19,1,29,6,10,2,22,4, // 1640
  133. 3,3,14,2,24,6,6,1,17,3, // 1645
  134. 28,5,8,3,20,1,32,5,12,3, // 1650
  135. 22,6,4,1,16,2,26,6,6,3, // 1655
  136. 17,2,0,4,10,3,22,4,3,2, // 1660
  137. 14,3,24,6,5,2,17,1,28,6, // 1665
  138. 9,2,19,3,31,4,13,2,23,6, // 1670
  139. 3,3,15,1,27,5,7,3,17,3, // 1675
  140. 29,4,11,2,21,6,3,1,14,2, // 1680
  141. 25,6,5,3,16,2,28,4,9,3, // 1685
  142. 20,2,0,6,12,1,23,6,4,2, // 1690
  143. 14,3,26,4,8,2,18,3,0,4, // 1695
  144. 10,3,21,5,1,3,13,1,24,5, // 1700
  145. 5,3,15,3,27,4,8,2,19,3, // 1705
  146. 29,6,10,2,22,4,3,3,14,2, // 1710
  147. 26,4,6,3,18,2,28,6,10,1, // 1715
  148. 20,6,2,2,12,3,24,4,5,2, // 1720
  149. 16,3,28,4,8,3,19,2,0,6, // 1725
  150. 12,1,23,5,3,3,14,3,26,4, // 1730
  151. 7,2,17,3,28,6,9,2,21,4, // 1735
  152. 1,3,13,2,25,4,5,3,16,2, // 1740
  153. 27,6,9,1,19,3,0,5,11,3, // 1745
  154. 23,4,4,2,14,3,25,6,7,1, // 1750
  155. 18,2,28,6,9,3,21,4,2,2, // 1755
  156. 12,3,25,4,6,2,16,3,26,6, // 1760
  157. 8,2,20,1,0,6,11,2,22,6, // 1765
  158. 4,1,15,2,25,6,6,3,18,1, // 1770
  159. 29,5,9,3,22,4,2,3,13,2, // 1775
  160. 23,6,4,3,15,2,27,4,7,3, // 1780
  161. 19,2,31,4,11,3,21,6,3,2, // 1785
  162. 15,1,25,6,6,2,17,3,29,4, // 1790
  163. 10,2,20,6,3,1,13,3,24,5, // 1795
  164. 4,3,16,1,27,5,7,3,17,3, // 1800
  165. 0,4,11,2,21,6,1,3,13,2, // 1805
  166. 25,4,5,3,16,2,29,4,9,3, // 1810
  167. 19,6,30,2,13,1,23,6,4,2, // 1815
  168. 14,3,27,4,8,2,18,3,0,4, // 1820
  169. 11,3,22,5,2,3,14,1,26,5, // 1825
  170. 6,3,16,3,28,4,10,2,20,6, // 1830
  171. 30,3,11,2,24,4,4,3,15,2, // 1835
  172. 25,6,8,1,19,2,29,6,9,3, // 1840
  173. 22,4,3,2,13,3,25,4,7,2, // 1845
  174. 17,3,27,6,9,1,21,5,1,3, // 1850
  175. 11,3,23,4,5,2,15,3,25,6, // 1855
  176. 6,2,19,1,29,6,10,2,22,4, // 1860
  177. 3,3,14,2,24,6,6,1,18,2, // 1865
  178. 28,6,8,3,20,4,2,2,12,3, // 1870
  179. 24,4,4,3,16,2,26,6,6,3, // 1875
  180. 17,2,0,4,10,3,22,4,3,2, // 1880
  181. 14,3,24,6,5,2,17,1,28,6, // 1885
  182. 9,2,21,4,1,3,13,2,23,6, // 1890
  183. 5,1,15,3,27,5,7,3,19,1, // 1895
  184. 0,5,10,3,22,4,2,3,13,2, // 1900
  185. 24,6,4,3,15,2,27,4,8,3, // 1905
  186. 20,4,1,2,11,3,22,6,3,2, // 1910
  187. 15,1,25,6,7,2,17,3,29,4, // 1915
  188. 10,2,21,6,1,3,13,1,24,5, // 1920
  189. 5,3,15,3,27,4,8,2,19,6, // 1925
  190. 1,1,12,2,22,6,3,3,14,2, // 1930
  191. 26,4,6,3,18,2,28,6,10,1, // 1935
  192. 20,6,2,2,12,3,24,4,5,2, // 1940
  193. 16,3,28,4,9,2,19,6,30,3, // 1945
  194. 12,1,23,5,3,3,14,3,26,4, // 1950
  195. 7,2,17,3,28,6,9,2,21,4, // 1955
  196. 1,3,13,2,25,4,5,3,16,2, // 1960
  197. 27,6,9,1,19,6,30,2,11,3, // 1965
  198. 23,4,4,2,14,3,27,4,7,3, // 1970
  199. 18,2,28,6,11,1,22,5,2,3, // 1975
  200. 12,3,25,4,6,2,16,3,26,6, // 1980
  201. 8,2,20,4,30,3,11,2,24,4, // 1985
  202. 4,3,15,2,25,6,8,1,18,3, // 1990
  203. 29,5,9,3,22,4,3,2,13,3, // 1995
  204. 23,6,6,1,17,2,27,6,7,3, // 2000 - 2004
  205. 20,4,1,2,11,3,23,4,5,2, // 2005 - 2009
  206. 15,3,25,6,6,2,19,1,29,6, // 2010
  207. 10,2,20,6,3,1,14,2,24,6, // 2015
  208. 4,3,17,1,28,5,8,3,20,4, // 2020
  209. 1,3,12,2,22,6,2,3,14,2, // 2025
  210. 26,4,6,3,17,2,0,4,10,3, // 2030
  211. 20,6,1,2,14,1,24,6,5,2, // 2035
  212. 15,3,28,4,9,2,19,6,1,1, // 2040
  213. 12,3,23,5,3,3,15,1,27,5, // 2045
  214. 7,3,17,3,29,4,11,2,21,6, // 2050
  215. 1,3,12,2,25,4,5,3,16,2, // 2055
  216. 28,4,9,3,19,6,30,2,12,1, // 2060
  217. 23,6,4,2,14,3,26,4,8,2, // 2065
  218. 18,3,0,4,10,3,22,5,2,3, // 2070
  219. 14,1,25,5,6,3,16,3,28,4, // 2075
  220. 9,2,20,6,30,3,11,2,23,4, // 2080
  221. 4,3,15,2,27,4,7,3,19,2, // 2085
  222. 29,6,11,1,21,6,3,2,13,3, // 2090
  223. 25,4,6,2,17,3,27,6,9,1, // 2095
  224. 20,5,30,3,10,3,22,4,3,2, // 2100
  225. 14,3,24,6,5,2,17,1,28,6, // 2105
  226. 9,2,21,4,1,3,13,2,23,6, // 2110
  227. 5,1,16,2,27,6,7,3,19,4, // 2115
  228. 30,2,11,3,23,4,3,3,14,2, // 2120
  229. 25,6,5,3,16,2,28,4,9,3, // 2125
  230. 21,4,2,2,12,3,23,6,4,2, // 2130
  231. 16,1,26,6,8,2,20,4,30,3, // 2135
  232. 11,2,22,6,4,1,14,3,25,5, // 2140
  233. 6,3,18,1,29,5,9,3,22,4, // 2145
  234. 2,3,13,2,23,6,4,3,15,2, // 2150
  235. 27,4,7,3,20,4,1,2,11,3, // 2155
  236. 21,6,3,2,15,1,25,6,6,2, // 2160
  237. 17,3,29,4,10,2,20,6,3,1, // 2165
  238. 13,3,24,5,4,3,17,1,28,5, // 2170
  239. 8,3,18,6,1,1,12,2,22,6, // 2175
  240. 2,3,14,2,26,4,6,3,17,2, // 2180
  241. 28,6,10,1,20,6,1,2,12,3, // 2185
  242. 24,4,5,2,15,3,28,4,9,2, // 2190
  243. 19,6,33,3,12,1,23,5,3,3, // 2195
  244. 13,3,25,4,6,2,16,3,26,6, // 2200
  245. 8,2,20,4,30,3,11,2,24,4, // 2205
  246. 4,3,15,2,25,6,8,1,18,6, // 2210
  247. 33,2,9,3,22,4,3,2,13,3, // 2215
  248. 25,4,6,3,17,2,27,6,9,1, // 2220
  249. 21,5,1,3,11,3,23,4,5,2, // 2225
  250. 15,3,25,6,6,2,19,4,33,3, // 2230
  251. 10,2,22,4,3,3,14,2,24,6, // 2235
  252. 6,1 // 2240 (Hebrew year: 6000)
  253. };
  254. private const int MaxMonthPlusOne = 14;
  255. //
  256. // The lunar calendar has 6 different variations of month lengths
  257. // within a year.
  258. //
  259. private static readonly byte[] s_lunarMonthLen = {
  260. 0,00,00,00,00,00,00,00,00,00,00,00,00,0,
  261. 0,30,29,29,29,30,29,30,29,30,29,30,29,0, // 3 common year variations
  262. 0,30,29,30,29,30,29,30,29,30,29,30,29,0,
  263. 0,30,30,30,29,30,29,30,29,30,29,30,29,0,
  264. 0,30,29,29,29,30,30,29,30,29,30,29,30,29, // 3 leap year variations
  265. 0,30,29,30,29,30,30,29,30,29,30,29,30,29,
  266. 0,30,30,30,29,30,30,29,30,29,30,29,30,29
  267. };
  268. internal static readonly DateTime calendarMinValue = new DateTime(1583, 1, 1);
  269. // Gregorian 2239/9/29 = Hebrew 5999/13/29 (last day in Hebrew year 5999).
  270. // We can only format/parse Hebrew numbers up to 999, so we limit the max range to Hebrew year 5999.
  271. internal static readonly DateTime calendarMaxValue = new DateTime((new DateTime(2239, 9, 29, 23, 59, 59, 999)).Ticks + 9999);
  272. public override DateTime MinSupportedDateTime
  273. {
  274. get
  275. {
  276. return (calendarMinValue);
  277. }
  278. }
  279. public override DateTime MaxSupportedDateTime
  280. {
  281. get
  282. {
  283. return (calendarMaxValue);
  284. }
  285. }
  286. public override CalendarAlgorithmType AlgorithmType
  287. {
  288. get
  289. {
  290. return CalendarAlgorithmType.LunisolarCalendar;
  291. }
  292. }
  293. public HebrewCalendar()
  294. {
  295. }
  296. internal override CalendarId ID
  297. {
  298. get
  299. {
  300. return (CalendarId.HEBREW);
  301. }
  302. }
  303. /*=================================CheckHebrewYearValue==========================
  304. **Action: Check if the Hebrew year value is supported in this class.
  305. **Returns: None.
  306. **Arguments: y Hebrew year value
  307. ** ear Hebrew era value
  308. **Exceptions: ArgumentOutOfRange_Range if the year value is not supported.
  309. **Note:
  310. ** We use a table for the Hebrew calendar calculation, so the year supported is limited.
  311. ============================================================================*/
  312. private static void CheckHebrewYearValue(int y, int era, string varName)
  313. {
  314. CheckEraRange(era);
  315. if (y > MaxHebrewYear || y < MinHebrewYear)
  316. {
  317. throw new ArgumentOutOfRangeException(
  318. varName,
  319. string.Format(
  320. CultureInfo.CurrentCulture,
  321. SR.ArgumentOutOfRange_Range,
  322. MinHebrewYear,
  323. MaxHebrewYear));
  324. }
  325. }
  326. /*=================================CheckHebrewMonthValue==========================
  327. **Action: Check if the Hebrew month value is valid.
  328. **Returns: None.
  329. **Arguments: year Hebrew year value
  330. ** month Hebrew month value
  331. **Exceptions: ArgumentOutOfRange_Range if the month value is not valid.
  332. **Note:
  333. ** Call CheckHebrewYearValue() before calling this to verify the year value is supported.
  334. ============================================================================*/
  335. private void CheckHebrewMonthValue(int year, int month, int era)
  336. {
  337. int monthsInYear = GetMonthsInYear(year, era);
  338. if (month < 1 || month > monthsInYear)
  339. {
  340. throw new ArgumentOutOfRangeException(
  341. nameof(month),
  342. string.Format(
  343. CultureInfo.CurrentCulture,
  344. SR.ArgumentOutOfRange_Range,
  345. 1,
  346. monthsInYear));
  347. }
  348. }
  349. /*=================================CheckHebrewDayValue==========================
  350. **Action: Check if the Hebrew day value is valid.
  351. **Returns: None.
  352. **Arguments: year Hebrew year value
  353. ** month Hebrew month value
  354. ** day Hebrew day value.
  355. **Exceptions: ArgumentOutOfRange_Range if the day value is not valid.
  356. **Note:
  357. ** Call CheckHebrewYearValue()/CheckHebrewMonthValue() before calling this to verify the year/month values are valid.
  358. ============================================================================*/
  359. private void CheckHebrewDayValue(int year, int month, int day, int era)
  360. {
  361. int daysInMonth = GetDaysInMonth(year, month, era);
  362. if (day < 1 || day > daysInMonth)
  363. {
  364. throw new ArgumentOutOfRangeException(
  365. nameof(day),
  366. string.Format(
  367. CultureInfo.CurrentCulture,
  368. SR.ArgumentOutOfRange_Range,
  369. 1,
  370. daysInMonth));
  371. }
  372. }
  373. internal static void CheckEraRange(int era)
  374. {
  375. if (era != CurrentEra && era != HebrewEra)
  376. {
  377. throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue);
  378. }
  379. }
  380. private static void CheckTicksRange(long ticks)
  381. {
  382. if (ticks < calendarMinValue.Ticks || ticks > calendarMaxValue.Ticks)
  383. {
  384. throw new ArgumentOutOfRangeException(
  385. "time",
  386. // Print out the date in Gregorian using InvariantCulture since the DateTime is based on GreograinCalendar.
  387. string.Format(
  388. CultureInfo.InvariantCulture,
  389. SR.ArgumentOutOfRange_CalendarRange,
  390. calendarMinValue,
  391. calendarMaxValue));
  392. }
  393. }
  394. internal static int GetResult(__DateBuffer result, int part)
  395. {
  396. switch (part)
  397. {
  398. case DatePartYear:
  399. return (result.year);
  400. case DatePartMonth:
  401. return (result.month);
  402. case DatePartDay:
  403. return (result.day);
  404. }
  405. throw new InvalidOperationException(SR.InvalidOperation_DateTimeParsing);
  406. }
  407. /*=================================GetLunarMonthDay==========================
  408. **Action: Using the Hebrew table (HebrewTable) to get the Hebrew month/day value for Gregorian January 1st
  409. ** in a given Gregorian year.
  410. ** Greogrian January 1st falls usually in Tevet (4th month). Tevet has always 29 days.
  411. ** That's why, there no nead to specify the lunar month in the table. There are exceptions, and these
  412. ** are coded by giving numbers above 29 and below 1.
  413. ** Actual decoding is takenig place in the switch statement below.
  414. **Returns:
  415. ** The Hebrew year type. The value is from 1 to 6.
  416. ** normal years : 1 = 353 days 2 = 354 days 3 = 355 days.
  417. ** Leap years : 4 = 383 5 384 6 = 385 days.
  418. **Arguments:
  419. ** gregorianYear The year value in Gregorian calendar. The value should be between 1500 and 2239.
  420. ** lunarDate Object to take the result of the Hebrew year/month/day.
  421. **Exceptions:
  422. ============================================================================*/
  423. internal static int GetLunarMonthDay(int gregorianYear, __DateBuffer lunarDate)
  424. {
  425. //
  426. // Get the offset into the LunarMonthLen array and the lunar day
  427. // for January 1st.
  428. //
  429. int index = gregorianYear - FirstGregorianTableYear;
  430. if (index < 0 || index > TABLESIZE)
  431. {
  432. throw new ArgumentOutOfRangeException(nameof(gregorianYear));
  433. }
  434. index *= 2;
  435. lunarDate.day = s_hebrewTable[index];
  436. // Get the type of the year. The value is from 1 to 6
  437. int LunarYearType = s_hebrewTable[index + 1];
  438. //
  439. // Get the Lunar Month.
  440. //
  441. switch (lunarDate.day)
  442. {
  443. case (0): // 1/1 is on Shvat 1
  444. lunarDate.month = 5;
  445. lunarDate.day = 1;
  446. break;
  447. case (30): // 1/1 is on Kislev 30
  448. lunarDate.month = 3;
  449. break;
  450. case (31): // 1/1 is on Shvat 2
  451. lunarDate.month = 5;
  452. lunarDate.day = 2;
  453. break;
  454. case (32): // 1/1 is on Shvat 3
  455. lunarDate.month = 5;
  456. lunarDate.day = 3;
  457. break;
  458. case (33): // 1/1 is on Kislev 29
  459. lunarDate.month = 3;
  460. lunarDate.day = 29;
  461. break;
  462. default: // 1/1 is on Tevet (This is the general case)
  463. lunarDate.month = 4;
  464. break;
  465. }
  466. return (LunarYearType);
  467. }
  468. // Returns a given date part of this DateTime. This method is used
  469. // to compute the year, day-of-year, month, or day part.
  470. internal virtual int GetDatePart(long ticks, int part)
  471. {
  472. // The Gregorian year, month, day value for ticks.
  473. int gregorianYear, gregorianMonth, gregorianDay;
  474. int hebrewYearType; // lunar year type
  475. long AbsoluteDate; // absolute date - absolute date 1/1/1600
  476. //
  477. // Make sure we have a valid Gregorian date that will fit into our
  478. // Hebrew conversion limits.
  479. //
  480. CheckTicksRange(ticks);
  481. DateTime time = new DateTime(ticks);
  482. //
  483. // Save the Gregorian date values.
  484. //
  485. time.GetDatePart(out gregorianYear, out gregorianMonth, out gregorianDay);
  486. __DateBuffer lunarDate = new __DateBuffer(); // lunar month and day for Jan 1
  487. // From the table looking-up value of HebrewTable[index] (stored in lunarDate.day), we get the the
  488. // lunar month and lunar day where the Gregorian date 1/1 falls.
  489. lunarDate.year = gregorianYear + HebrewYearOf1AD;
  490. hebrewYearType = GetLunarMonthDay(gregorianYear, lunarDate);
  491. // This is the buffer used to store the result Hebrew date.
  492. __DateBuffer result = new __DateBuffer();
  493. //
  494. // Store the values for the start of the new year - 1/1.
  495. //
  496. result.year = lunarDate.year;
  497. result.month = lunarDate.month;
  498. result.day = lunarDate.day;
  499. //
  500. // Get the absolute date from 1/1/1600.
  501. //
  502. AbsoluteDate = GregorianCalendar.GetAbsoluteDate(gregorianYear, gregorianMonth, gregorianDay);
  503. //
  504. // If the requested date was 1/1, then we're done.
  505. //
  506. if ((gregorianMonth == 1) && (gregorianDay == 1))
  507. {
  508. return (GetResult(result, part));
  509. }
  510. //
  511. // Calculate the number of days between 1/1 and the requested date.
  512. //
  513. long NumDays; // number of days since 1/1
  514. NumDays = AbsoluteDate - GregorianCalendar.GetAbsoluteDate(gregorianYear, 1, 1);
  515. //
  516. // If the requested date is within the current lunar month, then
  517. // we're done.
  518. //
  519. if ((NumDays + (long)lunarDate.day) <= (long)(s_lunarMonthLen[hebrewYearType * MaxMonthPlusOne + lunarDate.month]))
  520. {
  521. result.day += (int)NumDays;
  522. return (GetResult(result, part));
  523. }
  524. //
  525. // Adjust for the current partial month.
  526. //
  527. result.month++;
  528. result.day = 1;
  529. //
  530. // Adjust the Lunar Month and Year (if necessary) based on the number
  531. // of days between 1/1 and the requested date.
  532. //
  533. // Assumes Jan 1 can never translate to the last Lunar month, which
  534. // is true.
  535. //
  536. NumDays -= (long)(s_lunarMonthLen[hebrewYearType * MaxMonthPlusOne + lunarDate.month] - lunarDate.day);
  537. Debug.Assert(NumDays >= 1, "NumDays >= 1");
  538. // If NumDays is 1, then we are done. Otherwise, find the correct Hebrew month
  539. // and day.
  540. if (NumDays > 1)
  541. {
  542. //
  543. // See if we're on the correct Lunar month.
  544. //
  545. while (NumDays > (long)(s_lunarMonthLen[hebrewYearType * MaxMonthPlusOne + result.month]))
  546. {
  547. //
  548. // Adjust the number of days and move to the next month.
  549. //
  550. NumDays -= (long)(s_lunarMonthLen[hebrewYearType * MaxMonthPlusOne + result.month++]);
  551. //
  552. // See if we need to adjust the Year.
  553. // Must handle both 12 and 13 month years.
  554. //
  555. if ((result.month > 13) || (s_lunarMonthLen[hebrewYearType * MaxMonthPlusOne + result.month] == 0))
  556. {
  557. //
  558. // Adjust the Year.
  559. //
  560. result.year++;
  561. hebrewYearType = s_hebrewTable[(gregorianYear + 1 - FirstGregorianTableYear) * 2 + 1];
  562. //
  563. // Adjust the Month.
  564. //
  565. result.month = 1;
  566. }
  567. }
  568. //
  569. // Found the right Lunar month.
  570. //
  571. result.day += (int)(NumDays - 1);
  572. }
  573. return (GetResult(result, part));
  574. }
  575. // Returns the DateTime resulting from adding the given number of
  576. // months to the specified DateTime. The result is computed by incrementing
  577. // (or decrementing) the year and month parts of the specified DateTime by
  578. // value months, and, if required, adjusting the day part of the
  579. // resulting date downwards to the last day of the resulting month in the
  580. // resulting year. The time-of-day part of the result is the same as the
  581. // time-of-day part of the specified DateTime.
  582. //
  583. // In more precise terms, considering the specified DateTime to be of the
  584. // form y / m / d + t, where y is the
  585. // year, m is the month, d is the day, and t is the
  586. // time-of-day, the result is y1 / m1 / d1 + t,
  587. // where y1 and m1 are computed by adding value months
  588. // to y and m, and d1 is the largest value less than
  589. // or equal to d that denotes a valid day in month m1 of year
  590. // y1.
  591. //
  592. public override DateTime AddMonths(DateTime time, int months)
  593. {
  594. try
  595. {
  596. int y = GetDatePart(time.Ticks, DatePartYear);
  597. int m = GetDatePart(time.Ticks, DatePartMonth);
  598. int d = GetDatePart(time.Ticks, DatePartDay);
  599. int monthsInYear;
  600. int i;
  601. if (months >= 0)
  602. {
  603. i = m + months;
  604. while (i > (monthsInYear = GetMonthsInYear(y, CurrentEra)))
  605. {
  606. y++;
  607. i -= monthsInYear;
  608. }
  609. }
  610. else
  611. {
  612. if ((i = m + months) <= 0)
  613. {
  614. months = -months;
  615. months -= m;
  616. y--;
  617. while (months > (monthsInYear = GetMonthsInYear(y, CurrentEra)))
  618. {
  619. y--;
  620. months -= monthsInYear;
  621. }
  622. monthsInYear = GetMonthsInYear(y, CurrentEra);
  623. i = monthsInYear - months;
  624. }
  625. }
  626. int days = GetDaysInMonth(y, i);
  627. if (d > days)
  628. {
  629. d = days;
  630. }
  631. return (new DateTime(ToDateTime(y, i, d, 0, 0, 0, 0).Ticks + (time.Ticks % TicksPerDay)));
  632. }
  633. // We expect ArgumentException and ArgumentOutOfRangeException (which is subclass of ArgumentException)
  634. // If exception is thrown in the calls above, we are out of the supported range of this calendar.
  635. catch (ArgumentException)
  636. {
  637. throw new ArgumentOutOfRangeException(
  638. nameof(months),
  639. string.Format(
  640. CultureInfo.CurrentCulture,
  641. SR.ArgumentOutOfRange_AddValue));
  642. }
  643. }
  644. // Returns the DateTime resulting from adding the given number of
  645. // years to the specified DateTime. The result is computed by incrementing
  646. // (or decrementing) the year part of the specified DateTime by value
  647. // years. If the month and day of the specified DateTime is 2/29, and if the
  648. // resulting year is not a leap year, the month and day of the resulting
  649. // DateTime becomes 2/28. Otherwise, the month, day, and time-of-day
  650. // parts of the result are the same as those of the specified DateTime.
  651. //
  652. public override DateTime AddYears(DateTime time, int years)
  653. {
  654. int y = GetDatePart(time.Ticks, DatePartYear);
  655. int m = GetDatePart(time.Ticks, DatePartMonth);
  656. int d = GetDatePart(time.Ticks, DatePartDay);
  657. y += years;
  658. CheckHebrewYearValue(y, Calendar.CurrentEra, nameof(years));
  659. int months = GetMonthsInYear(y, CurrentEra);
  660. if (m > months)
  661. {
  662. m = months;
  663. }
  664. int days = GetDaysInMonth(y, m);
  665. if (d > days)
  666. {
  667. d = days;
  668. }
  669. long ticks = ToDateTime(y, m, d, 0, 0, 0, 0).Ticks + (time.Ticks % TicksPerDay);
  670. Calendar.CheckAddResult(ticks, MinSupportedDateTime, MaxSupportedDateTime);
  671. return (new DateTime(ticks));
  672. }
  673. // Returns the day-of-month part of the specified DateTime. The returned
  674. // value is an integer between 1 and 31.
  675. //
  676. public override int GetDayOfMonth(DateTime time)
  677. {
  678. return (GetDatePart(time.Ticks, DatePartDay));
  679. }
  680. // Returns the day-of-week part of the specified DateTime. The returned value
  681. // is an integer between 0 and 6, where 0 indicates Sunday, 1 indicates
  682. // Monday, 2 indicates Tuesday, 3 indicates Wednesday, 4 indicates
  683. // Thursday, 5 indicates Friday, and 6 indicates Saturday.
  684. //
  685. public override DayOfWeek GetDayOfWeek(DateTime time)
  686. {
  687. // If we calculate back, the Hebrew day of week for Gregorian 0001/1/1 is Monday (1).
  688. // Therfore, the fomula is:
  689. return ((DayOfWeek)((int)(time.Ticks / TicksPerDay + 1) % 7));
  690. }
  691. internal static int GetHebrewYearType(int year, int era)
  692. {
  693. CheckHebrewYearValue(year, era, nameof(year));
  694. // The HebrewTable is indexed by Gregorian year and starts from FirstGregorianYear.
  695. // So we need to convert year (Hebrew year value) to Gregorian Year below.
  696. return (s_hebrewTable[(year - HebrewYearOf1AD - FirstGregorianTableYear) * 2 + 1]);
  697. }
  698. // Returns the day-of-year part of the specified DateTime. The returned value
  699. // is an integer between 1 and 366.
  700. //
  701. public override int GetDayOfYear(DateTime time)
  702. {
  703. // Get Hebrew year value of the specified time.
  704. int year = GetYear(time);
  705. DateTime beginOfYearDate;
  706. if (year == 5343)
  707. {
  708. // Gregorian 1583/01/01 corresponds to Hebrew 5343/04/07 (MinSupportedDateTime)
  709. // To figure out the Gregorian date associated with Hebrew 5343/01/01, we need to
  710. // count the days from 5343/01/01 to 5343/04/07 and subtract that from Gregorian
  711. // 1583/01/01.
  712. // 1. Tishri (30 days)
  713. // 2. Heshvan (30 days since 5343 has 355 days)
  714. // 3. Kislev (30 days since 5343 has 355 days)
  715. // 96 days to get from 5343/01/01 to 5343/04/07
  716. // Gregorian 1583/01/01 - 96 days = 1582/9/27
  717. // the beginning of Hebrew year 5343 corresponds to Gregorian September 27, 1582.
  718. beginOfYearDate = new DateTime(1582, 9, 27);
  719. }
  720. else
  721. {
  722. // following line will fail when year is 5343 (first supported year)
  723. beginOfYearDate = ToDateTime(year, 1, 1, 0, 0, 0, 0, CurrentEra);
  724. }
  725. return ((int)((time.Ticks - beginOfYearDate.Ticks) / TicksPerDay) + 1);
  726. }
  727. // Returns the number of days in the month given by the year and
  728. // month arguments.
  729. //
  730. public override int GetDaysInMonth(int year, int month, int era)
  731. {
  732. CheckEraRange(era);
  733. int hebrewYearType = GetHebrewYearType(year, era);
  734. CheckHebrewMonthValue(year, month, era);
  735. Debug.Assert(hebrewYearType >= 1 && hebrewYearType <= 6,
  736. "hebrewYearType should be from 1 to 6, but now hebrewYearType = " + hebrewYearType + " for hebrew year " + year);
  737. int monthDays = s_lunarMonthLen[hebrewYearType * MaxMonthPlusOne + month];
  738. if (monthDays == 0)
  739. {
  740. throw new ArgumentOutOfRangeException(nameof(month), SR.ArgumentOutOfRange_Month);
  741. }
  742. return (monthDays);
  743. }
  744. // Returns the number of days in the year given by the year argument for the current era.
  745. //
  746. public override int GetDaysInYear(int year, int era)
  747. {
  748. CheckEraRange(era);
  749. // normal years : 1 = 353 days 2 = 354 days 3 = 355 days.
  750. // Leap years : 4 = 383 5 384 6 = 385 days.
  751. // LunarYearType is from 1 to 6
  752. int LunarYearType = GetHebrewYearType(year, era);
  753. if (LunarYearType < 4)
  754. {
  755. // common year: LunarYearType = 1, 2, 3
  756. return (352 + LunarYearType);
  757. }
  758. return (382 + (LunarYearType - 3));
  759. }
  760. // Returns the era for the specified DateTime value.
  761. public override int GetEra(DateTime time)
  762. {
  763. return (HebrewEra);
  764. }
  765. public override int[] Eras
  766. {
  767. get
  768. {
  769. return (new int[] { HebrewEra });
  770. }
  771. }
  772. // Returns the month part of the specified DateTime. The returned value is an
  773. // integer between 1 and 12.
  774. //
  775. public override int GetMonth(DateTime time)
  776. {
  777. return (GetDatePart(time.Ticks, DatePartMonth));
  778. }
  779. // Returns the number of months in the specified year and era.
  780. public override int GetMonthsInYear(int year, int era)
  781. {
  782. return (IsLeapYear(year, era) ? 13 : 12);
  783. }
  784. // Returns the year part of the specified DateTime. The returned value is an
  785. // integer between 1 and 9999.
  786. //
  787. public override int GetYear(DateTime time)
  788. {
  789. return (GetDatePart(time.Ticks, DatePartYear));
  790. }
  791. // Checks whether a given day in the specified era is a leap day. This method returns true if
  792. // the date is a leap day, or false if not.
  793. //
  794. public override bool IsLeapDay(int year, int month, int day, int era)
  795. {
  796. if (IsLeapMonth(year, month, era))
  797. {
  798. // Every day in a leap month is a leap day.
  799. CheckHebrewDayValue(year, month, day, era);
  800. return (true);
  801. }
  802. else if (IsLeapYear(year, Calendar.CurrentEra))
  803. {
  804. // There is an additional day in the 6th month in the leap year (the extra day is the 30th day in the 6th month),
  805. // so we should return true for 6/30 if that's in a leap year.
  806. if (month == 6 && day == 30)
  807. {
  808. return (true);
  809. }
  810. }
  811. CheckHebrewDayValue(year, month, day, era);
  812. return (false);
  813. }
  814. // Returns the leap month in a calendar year of the specified era. This method returns 0
  815. // if this calendar does not have leap month, or this year is not a leap year.
  816. //
  817. public override int GetLeapMonth(int year, int era)
  818. {
  819. // Year/era values are checked in IsLeapYear().
  820. if (IsLeapYear(year, era))
  821. {
  822. // The 7th month in a leap year is a leap month.
  823. return (7);
  824. }
  825. return (0);
  826. }
  827. // Checks whether a given month in the specified era is a leap month. This method returns true if
  828. // month is a leap month, or false if not.
  829. //
  830. public override bool IsLeapMonth(int year, int month, int era)
  831. {
  832. // Year/era values are checked in IsLeapYear().
  833. bool isLeapYear = IsLeapYear(year, era);
  834. CheckHebrewMonthValue(year, month, era);
  835. // The 7th month in a leap year is a leap month.
  836. if (isLeapYear)
  837. {
  838. if (month == 7)
  839. {
  840. return (true);
  841. }
  842. }
  843. return (false);
  844. }
  845. // Checks whether a given year in the specified era is a leap year. This method returns true if
  846. // year is a leap year, or false if not.
  847. //
  848. public override bool IsLeapYear(int year, int era)
  849. {
  850. CheckHebrewYearValue(year, era, nameof(year));
  851. return (((7 * (long)year + 1) % 19) < 7);
  852. }
  853. // (month1, day1) - (month2, day2)
  854. private static int GetDayDifference(int lunarYearType, int month1, int day1, int month2, int day2)
  855. {
  856. if (month1 == month2)
  857. {
  858. return (day1 - day2);
  859. }
  860. // Make sure that (month1, day1) < (month2, day2)
  861. bool swap = (month1 > month2);
  862. if (swap)
  863. {
  864. // (month1, day1) < (month2, day2). Swap the values.
  865. // The result will be a negative number.
  866. int tempMonth, tempDay;
  867. tempMonth = month1; tempDay = day1;
  868. month1 = month2; day1 = day2;
  869. month2 = tempMonth; day2 = tempDay;
  870. }
  871. // Get the number of days from (month1,day1) to (month1, end of month1)
  872. int days = s_lunarMonthLen[lunarYearType * MaxMonthPlusOne + month1] - day1;
  873. // Move to next month.
  874. month1++;
  875. // Add up the days.
  876. while (month1 < month2)
  877. {
  878. days += s_lunarMonthLen[lunarYearType * MaxMonthPlusOne + month1++];
  879. }
  880. days += day2;
  881. return (swap ? days : -days);
  882. }
  883. /*=================================HebrewToGregorian==========================
  884. **Action: Convert Hebrew date to Gregorian date.
  885. **Returns:
  886. **Arguments:
  887. **Exceptions:
  888. ** The algorithm is like this:
  889. ** The hebrew year has an offset to the Gregorian year, so we can guess the Gregorian year for
  890. ** the specified Hebrew year. That is, GreogrianYear = HebrewYear - FirstHebrewYearOf1AD.
  891. **
  892. ** From the Gregorian year and HebrewTable, we can get the Hebrew month/day value
  893. ** of the Gregorian date January 1st. Let's call this month/day value [hebrewDateForJan1]
  894. **
  895. ** If the requested Hebrew month/day is less than [hebrewDateForJan1], we know the result
  896. ** Gregorian date falls in previous year. So we decrease the Gregorian year value, and
  897. ** retrieve the Hebrew month/day value of the Gregorian date january 1st again.
  898. **
  899. ** Now, we get the answer of the Gregorian year.
  900. **
  901. ** The next step is to get the number of days between the requested Hebrew month/day
  902. ** and [hebrewDateForJan1]. When we get that, we can create the DateTime by adding/subtracting
  903. ** the ticks value of the number of days.
  904. **
  905. ============================================================================*/
  906. private static DateTime HebrewToGregorian(int hebrewYear, int hebrewMonth, int hebrewDay, int hour, int minute, int second, int millisecond)
  907. {
  908. // Get the rough Gregorian year for the specified hebrewYear.
  909. //
  910. int gregorianYear = hebrewYear - HebrewYearOf1AD;
  911. __DateBuffer hebrewDateOfJan1 = new __DateBuffer(); // year value is unused.
  912. int lunarYearType = GetLunarMonthDay(gregorianYear, hebrewDateOfJan1);
  913. if ((hebrewMonth == hebrewDateOfJan1.month) && (hebrewDay == hebrewDateOfJan1.day))
  914. {
  915. return (new DateTime(gregorianYear, 1, 1, hour, minute, second, millisecond));
  916. }
  917. int days = GetDayDifference(lunarYearType, hebrewMonth, hebrewDay, hebrewDateOfJan1.month, hebrewDateOfJan1.day);
  918. DateTime gregorianNewYear = new DateTime(gregorianYear, 1, 1);
  919. return (new DateTime(gregorianNewYear.Ticks + days * TicksPerDay
  920. + TimeToTicks(hour, minute, second, millisecond)));
  921. }
  922. // Returns the date and time converted to a DateTime value. Throws an exception if the n-tuple is invalid.
  923. //
  924. public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era)
  925. {
  926. CheckHebrewYearValue(year, era, nameof(year));
  927. CheckHebrewMonthValue(year, month, era);
  928. CheckHebrewDayValue(year, month, day, era);
  929. DateTime dt = HebrewToGregorian(year, month, day, hour, minute, second, millisecond);
  930. CheckTicksRange(dt.Ticks);
  931. return (dt);
  932. }
  933. private const int DEFAULT_TWO_DIGIT_YEAR_MAX = 5790;
  934. public override int TwoDigitYearMax
  935. {
  936. get
  937. {
  938. if (twoDigitYearMax == -1)
  939. {
  940. twoDigitYearMax = GetSystemTwoDigitYearSetting(ID, DEFAULT_TWO_DIGIT_YEAR_MAX);
  941. }
  942. return (twoDigitYearMax);
  943. }
  944. set
  945. {
  946. VerifyWritable();
  947. if (value == 99)
  948. {
  949. // Do nothing here. Year 99 is allowed so that TwoDitYearMax is disabled.
  950. }
  951. else
  952. {
  953. CheckHebrewYearValue(value, HebrewEra, nameof(value));
  954. }
  955. twoDigitYearMax = value;
  956. }
  957. }
  958. public override int ToFourDigitYear(int year)
  959. {
  960. if (year < 0)
  961. {
  962. throw new ArgumentOutOfRangeException(nameof(year),
  963. SR.ArgumentOutOfRange_NeedNonNegNum);
  964. }
  965. if (year < 100)
  966. {
  967. return (base.ToFourDigitYear(year));
  968. }
  969. if (year > MaxHebrewYear || year < MinHebrewYear)
  970. {
  971. throw new ArgumentOutOfRangeException(
  972. nameof(year),
  973. string.Format(
  974. CultureInfo.CurrentCulture,
  975. SR.ArgumentOutOfRange_Range,
  976. MinHebrewYear,
  977. MaxHebrewYear));
  978. }
  979. return (year);
  980. }
  981. internal class __DateBuffer
  982. {
  983. internal int year;
  984. internal int month;
  985. internal int day;
  986. }
  987. }
  988. }