JulianCalendar.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  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. namespace System.Globalization
  5. {
  6. //
  7. // This class implements the Julian calendar. In 48 B.C. Julius Caesar ordered a calendar reform, and this calendar
  8. // is called Julian calendar. It consisted of a solar year of twelve months and of 365 days with an extra day
  9. // every fourth year.
  10. //*
  11. //* Calendar support range:
  12. //* Calendar Minimum Maximum
  13. //* ========== ========== ==========
  14. //* Gregorian 0001/01/01 9999/12/31
  15. //* Julia 0001/01/03 9999/10/19
  16. public class JulianCalendar : Calendar
  17. {
  18. public static readonly int JulianEra = 1;
  19. private const int DatePartYear = 0;
  20. private const int DatePartDayOfYear = 1;
  21. private const int DatePartMonth = 2;
  22. private const int DatePartDay = 3;
  23. // Number of days in a non-leap year
  24. private const int JulianDaysPerYear = 365;
  25. // Number of days in 4 years
  26. private const int JulianDaysPer4Years = JulianDaysPerYear * 4 + 1;
  27. private static readonly int[] s_daysToMonth365 =
  28. {
  29. 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
  30. };
  31. private static readonly int[] s_daysToMonth366 =
  32. {
  33. 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366
  34. };
  35. // Gregorian Calendar 9999/12/31 = Julian Calendar 9999/10/19
  36. // keep it as variable field for serialization compat.
  37. internal int MaxYear = 9999;
  38. public override DateTime MinSupportedDateTime
  39. {
  40. get
  41. {
  42. return (DateTime.MinValue);
  43. }
  44. }
  45. public override DateTime MaxSupportedDateTime
  46. {
  47. get
  48. {
  49. return (DateTime.MaxValue);
  50. }
  51. }
  52. public override CalendarAlgorithmType AlgorithmType
  53. {
  54. get
  55. {
  56. return CalendarAlgorithmType.SolarCalendar;
  57. }
  58. }
  59. public JulianCalendar()
  60. {
  61. // There is no system setting of TwoDigitYear max, so set the value here.
  62. twoDigitYearMax = 2029;
  63. }
  64. internal override CalendarId ID
  65. {
  66. get
  67. {
  68. return CalendarId.JULIAN;
  69. }
  70. }
  71. internal static void CheckEraRange(int era)
  72. {
  73. if (era != CurrentEra && era != JulianEra)
  74. {
  75. throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue);
  76. }
  77. }
  78. internal void CheckYearEraRange(int year, int era)
  79. {
  80. CheckEraRange(era);
  81. if (year <= 0 || year > MaxYear)
  82. {
  83. throw new ArgumentOutOfRangeException(
  84. nameof(year),
  85. string.Format(
  86. CultureInfo.CurrentCulture,
  87. SR.ArgumentOutOfRange_Range,
  88. 1,
  89. MaxYear));
  90. }
  91. }
  92. internal static void CheckMonthRange(int month)
  93. {
  94. if (month < 1 || month > 12)
  95. {
  96. throw new ArgumentOutOfRangeException(nameof(month), SR.ArgumentOutOfRange_Month);
  97. }
  98. }
  99. /*===================================CheckDayRange============================
  100. **Action: Check for if the day value is valid.
  101. **Returns:
  102. **Arguments:
  103. **Exceptions:
  104. **Notes:
  105. ** Before calling this method, call CheckYearEraRange()/CheckMonthRange() to make
  106. ** sure year/month values are correct.
  107. ============================================================================*/
  108. internal static void CheckDayRange(int year, int month, int day)
  109. {
  110. if (year == 1 && month == 1)
  111. {
  112. // The minimum supported Julia date is Julian 0001/01/03.
  113. if (day < 3)
  114. {
  115. throw new ArgumentOutOfRangeException(null,
  116. SR.ArgumentOutOfRange_BadYearMonthDay);
  117. }
  118. }
  119. bool isLeapYear = (year % 4) == 0;
  120. int[] days = isLeapYear ? s_daysToMonth366 : s_daysToMonth365;
  121. int monthDays = days[month] - days[month - 1];
  122. if (day < 1 || day > monthDays)
  123. {
  124. throw new ArgumentOutOfRangeException(
  125. nameof(day),
  126. string.Format(
  127. CultureInfo.CurrentCulture,
  128. SR.ArgumentOutOfRange_Range,
  129. 1,
  130. monthDays));
  131. }
  132. }
  133. // Returns a given date part of this DateTime. This method is used
  134. // to compute the year, day-of-year, month, or day part.
  135. internal static int GetDatePart(long ticks, int part)
  136. {
  137. // Gregorian 1/1/0001 is Julian 1/3/0001. Remember DateTime(0) is refered to Gregorian 1/1/0001.
  138. // The following line convert Gregorian ticks to Julian ticks.
  139. long julianTicks = ticks + TicksPerDay * 2;
  140. // n = number of days since 1/1/0001
  141. int n = (int)(julianTicks / TicksPerDay);
  142. // y4 = number of whole 4-year periods within 100-year period
  143. int y4 = n / JulianDaysPer4Years;
  144. // n = day number within 4-year period
  145. n -= y4 * JulianDaysPer4Years;
  146. // y1 = number of whole years within 4-year period
  147. int y1 = n / JulianDaysPerYear;
  148. // Last year has an extra day, so decrement result if 4
  149. if (y1 == 4) y1 = 3;
  150. // If year was requested, compute and return it
  151. if (part == DatePartYear)
  152. {
  153. return (y4 * 4 + y1 + 1);
  154. }
  155. // n = day number within year
  156. n -= y1 * JulianDaysPerYear;
  157. // If day-of-year was requested, return it
  158. if (part == DatePartDayOfYear)
  159. {
  160. return (n + 1);
  161. }
  162. // Leap year calculation looks different from IsLeapYear since y1, y4,
  163. // and y100 are relative to year 1, not year 0
  164. bool leapYear = (y1 == 3);
  165. int[] days = leapYear ? s_daysToMonth366 : s_daysToMonth365;
  166. // All months have less than 32 days, so n >> 5 is a good conservative
  167. // estimate for the month
  168. int m = (n >> 5) + 1;
  169. // m = 1-based month number
  170. while (n >= days[m]) m++;
  171. // If month was requested, return it
  172. if (part == DatePartMonth) return (m);
  173. // Return 1-based day-of-month
  174. return (n - days[m - 1] + 1);
  175. }
  176. // Returns the tick count corresponding to the given year, month, and day.
  177. internal static long DateToTicks(int year, int month, int day)
  178. {
  179. int[] days = (year % 4 == 0) ? s_daysToMonth366 : s_daysToMonth365;
  180. int y = year - 1;
  181. int n = y * 365 + y / 4 + days[month - 1] + day - 1;
  182. // Gregorian 1/1/0001 is Julian 1/3/0001. n * TicksPerDay is the ticks in JulianCalendar.
  183. // Therefore, we subtract two days in the following to convert the ticks in JulianCalendar
  184. // to ticks in Gregorian calendar.
  185. return ((n - 2) * TicksPerDay);
  186. }
  187. public override DateTime AddMonths(DateTime time, int months)
  188. {
  189. if (months < -120000 || months > 120000)
  190. {
  191. throw new ArgumentOutOfRangeException(
  192. nameof(months),
  193. string.Format(
  194. CultureInfo.CurrentCulture,
  195. SR.ArgumentOutOfRange_Range,
  196. -120000,
  197. 120000));
  198. }
  199. int y = GetDatePart(time.Ticks, DatePartYear);
  200. int m = GetDatePart(time.Ticks, DatePartMonth);
  201. int d = GetDatePart(time.Ticks, DatePartDay);
  202. int i = m - 1 + months;
  203. if (i >= 0)
  204. {
  205. m = i % 12 + 1;
  206. y = y + i / 12;
  207. }
  208. else
  209. {
  210. m = 12 + (i + 1) % 12;
  211. y = y + (i - 11) / 12;
  212. }
  213. int[] daysArray = (y % 4 == 0 && (y % 100 != 0 || y % 400 == 0)) ? s_daysToMonth366 : s_daysToMonth365;
  214. int days = (daysArray[m] - daysArray[m - 1]);
  215. if (d > days)
  216. {
  217. d = days;
  218. }
  219. long ticks = DateToTicks(y, m, d) + time.Ticks % TicksPerDay;
  220. Calendar.CheckAddResult(ticks, MinSupportedDateTime, MaxSupportedDateTime);
  221. return (new DateTime(ticks));
  222. }
  223. public override DateTime AddYears(DateTime time, int years)
  224. {
  225. return (AddMonths(time, years * 12));
  226. }
  227. public override int GetDayOfMonth(DateTime time)
  228. {
  229. return (GetDatePart(time.Ticks, DatePartDay));
  230. }
  231. public override DayOfWeek GetDayOfWeek(DateTime time)
  232. {
  233. return ((DayOfWeek)((int)(time.Ticks / TicksPerDay + 1) % 7));
  234. }
  235. public override int GetDayOfYear(DateTime time)
  236. {
  237. return (GetDatePart(time.Ticks, DatePartDayOfYear));
  238. }
  239. public override int GetDaysInMonth(int year, int month, int era)
  240. {
  241. CheckYearEraRange(year, era);
  242. CheckMonthRange(month);
  243. int[] days = (year % 4 == 0) ? s_daysToMonth366 : s_daysToMonth365;
  244. return (days[month] - days[month - 1]);
  245. }
  246. public override int GetDaysInYear(int year, int era)
  247. {
  248. // Year/Era range is done in IsLeapYear().
  249. return (IsLeapYear(year, era) ? 366 : 365);
  250. }
  251. public override int GetEra(DateTime time)
  252. {
  253. return (JulianEra);
  254. }
  255. public override int GetMonth(DateTime time)
  256. {
  257. return (GetDatePart(time.Ticks, DatePartMonth));
  258. }
  259. public override int[] Eras
  260. {
  261. get
  262. {
  263. return (new int[] { JulianEra });
  264. }
  265. }
  266. public override int GetMonthsInYear(int year, int era)
  267. {
  268. CheckYearEraRange(year, era);
  269. return (12);
  270. }
  271. public override int GetYear(DateTime time)
  272. {
  273. return (GetDatePart(time.Ticks, DatePartYear));
  274. }
  275. public override bool IsLeapDay(int year, int month, int day, int era)
  276. {
  277. CheckMonthRange(month);
  278. // Year/Era range check is done in IsLeapYear().
  279. if (IsLeapYear(year, era))
  280. {
  281. CheckDayRange(year, month, day);
  282. return (month == 2 && day == 29);
  283. }
  284. CheckDayRange(year, month, day);
  285. return (false);
  286. }
  287. // Returns the leap month in a calendar year of the specified era. This method returns 0
  288. // if this calendar does not have leap month, or this year is not a leap year.
  289. //
  290. public override int GetLeapMonth(int year, int era)
  291. {
  292. CheckYearEraRange(year, era);
  293. return (0);
  294. }
  295. public override bool IsLeapMonth(int year, int month, int era)
  296. {
  297. CheckYearEraRange(year, era);
  298. CheckMonthRange(month);
  299. return (false);
  300. }
  301. // Checks whether a given year in the specified era is a leap year. This method returns true if
  302. // year is a leap year, or false if not.
  303. //
  304. public override bool IsLeapYear(int year, int era)
  305. {
  306. CheckYearEraRange(year, era);
  307. return (year % 4 == 0);
  308. }
  309. public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era)
  310. {
  311. CheckYearEraRange(year, era);
  312. CheckMonthRange(month);
  313. CheckDayRange(year, month, day);
  314. if (millisecond < 0 || millisecond >= MillisPerSecond)
  315. {
  316. throw new ArgumentOutOfRangeException(
  317. nameof(millisecond),
  318. string.Format(
  319. CultureInfo.CurrentCulture,
  320. SR.ArgumentOutOfRange_Range,
  321. 0,
  322. MillisPerSecond - 1));
  323. }
  324. if (hour >= 0 && hour < 24 && minute >= 0 && minute < 60 && second >= 0 && second < 60)
  325. {
  326. return new DateTime(DateToTicks(year, month, day) + (new TimeSpan(0, hour, minute, second, millisecond)).Ticks);
  327. }
  328. else
  329. {
  330. throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadHourMinuteSecond);
  331. }
  332. }
  333. public override int TwoDigitYearMax
  334. {
  335. get
  336. {
  337. return (twoDigitYearMax);
  338. }
  339. set
  340. {
  341. VerifyWritable();
  342. if (value < 99 || value > MaxYear)
  343. {
  344. throw new ArgumentOutOfRangeException(
  345. "year",
  346. string.Format(
  347. CultureInfo.CurrentCulture,
  348. SR.ArgumentOutOfRange_Range,
  349. 99,
  350. MaxYear));
  351. }
  352. twoDigitYearMax = value;
  353. }
  354. }
  355. public override int ToFourDigitYear(int year)
  356. {
  357. if (year < 0)
  358. {
  359. throw new ArgumentOutOfRangeException(nameof(year),
  360. SR.ArgumentOutOfRange_NeedNonNegNum);
  361. }
  362. if (year > MaxYear)
  363. {
  364. throw new ArgumentOutOfRangeException(
  365. nameof(year),
  366. string.Format(
  367. CultureInfo.CurrentCulture,
  368. SR.ArgumentOutOfRange_Bounds_Lower_Upper,
  369. 1,
  370. MaxYear));
  371. }
  372. return (base.ToFourDigitYear(year));
  373. }
  374. }
  375. }