2 // System.Globalization.PersianCalendar.cs
5 // Roozbeh Pournader (roozbeh@farsiweb.info)
8 // Copyright (C) 2002 Ulrich Kunitz
9 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
10 // Copyright (C) 2005 Sharif FarsiWeb, Inc. (http://www.farsiweb.info)
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 namespace System.Globalization {
36 using System.Runtime.InteropServices;
39 /// This is the Persian calendar of Iran, also known as the Iranian
40 /// calendar or the Jalaali calendar. The Afghan calendar may or may
41 /// not be different, as different sources disagree about it.
44 /// <para>The implemented algorithm is the simple 33-year arithmetic
45 /// calendar, which is not the same as the official Iranian calendar.
46 /// But this arithmetic calendar has been confirmed to produce the
47 /// same results as the official Iranian calendar at least
48 /// from 1925 C.E., when the calendar was officially introduced,
49 /// to 2088 C.E. This is the same algorithm that is used in .NET.
51 /// <para>The Iranian law explicitly mentions that the true solar year
52 /// should be used, which requires astronomical calculations of the
53 /// March equinox and the solar apparent noon. The exact locale for
54 /// observation of the apparent noon is not mentioned in the 1925 law,
55 /// but the current practice is using the 52.5 E meridian, which is
56 /// the meridian defining the official timezone of Iran.
58 /// <para>Also, please note that implementing the Persian calendar
59 /// using the 2820-year arithmetic algorithm, as suggested by
60 /// Ahmad Birashk and others, is less accurate than the 33-year
61 /// calendar: first, it fails earlier than the 33-year cycle in
62 /// matching the official astronomical calendar (first failure is
63 /// in 2025 C.E.), and second, the 2820-year suggested rule is based
64 /// on the mean tropical year, not the mean March equinoctial year.
68 public class PersianCalendar : Calendar {
72 public PersianCalendar() {
73 M_AbbrEraNames = new string[] {"A.P."};
74 M_EraNames = new string[] {"Anno Persico"};
75 if (twoDigitYearMax == 99)
76 // FIXME: the .NET documentation does not mention the default value,
77 // This is the value mentioned in the .NET documentation example result.
78 twoDigitYearMax = 1410;
82 /// The era number for the Anno Persico (A.P.) era, called
85 public static readonly int PersianEra = 1;
89 /// <see cref="T:System.DateTime"/> ticks for first day of
92 internal const long M_MinTicks = 196036416000000000L;
95 /// The minimum year in the A.P. era supported.
97 internal const int M_MinYear = 1;
99 /// <value>Overridden. Gives the eras supported by the Persian
100 /// calendar as an array of integers.
102 public override int[] Eras {
104 return new int[] { PersianEra };
108 public override int TwoDigitYearMax {
110 return twoDigitYearMax;
114 M_ArgumentInRange ("value", value, 100, M_MaxYear);
116 twoDigitYearMax = value;
121 /// A protected member checking a
122 /// <see cref="T:System.DateTime"/> value.
124 /// <param name="time">The
125 /// <see cref="T:System.DateTime"/>
128 /// <exception cref="T:System.ArgumentOutOfRangeException">
129 /// The exception is thrown if the
130 /// <see cref="T:System.DateTime"/> parameter is before the
133 internal void M_CheckDateTime(DateTime time)
135 if (time.Ticks < M_MinTicks)
136 throw new ArgumentOutOfRangeException(
138 "Only positive Persian years are supported.");
142 /// A protected method checking the era number.
144 /// <param name="era">The era number.</param>
145 /// <exception name="T:System.ArgumentException">
146 /// The exception is thrown if the era is not equal
147 /// <see cref="F:PersianEra"/>.
149 internal void M_CheckEra(ref int era)
151 if (era == CurrentEra)
153 if (era != PersianEra)
154 throw new ArgumentException("Era value was not valid.");
158 /// A protected method checking calendar year and the era number.
160 /// <param name="year">An integer representing the calendar year.
162 /// <param name="era">The era number.</param>
163 /// <exception cref="T:System.ArgumentException">
164 /// The exception is thrown if the era is not equal
165 /// <see cref="F:PersianEra"/>.
167 /// <exception cref="T:System.ArgumentOutOfRangeException">
168 /// The exception is thrown if the calendar year is outside of
169 /// the allowed range.
171 internal override void M_CheckYE(int year, ref int era)
174 if (year < M_MinYear || year > M_MaxYear)
175 throw new ArgumentOutOfRangeException(
177 "Only Persian years between 1 and 9378," +
178 " inclusive, are supported.");
182 /// A protected method checking the calendar year, month, and
185 /// <param name="year">An integer representing the calendar year.
187 /// <param name="month">An integer giving the calendar month.
189 /// <param name="era">The era number.</param>
190 /// <exception cref="T:System.ArgumentException">
191 /// The exception is thrown if the era is not equal
192 /// <see cref="F:PersianEra"/>.
194 /// <exception cref="T:System.ArgumentOutOfRangeException">
195 /// The exception is thrown if the calendar year or month is
196 /// outside of the allowed range.
198 internal void M_CheckYME(int year, int month, ref int era)
200 M_CheckYE(year, ref era);
201 if (month < 1 || month > 12)
202 throw new ArgumentOutOfRangeException("month",
203 "Month must be between one and twelve.");
204 else if (year == M_MaxYear && month > 10)
205 throw new ArgumentOutOfRangeException("month",
206 "Months in year 9378 must be between one and ten.");
210 /// A protected method checking the calendar day, month, and year
211 /// and the era number.
213 /// <param name="year">An integer representing the calendar year.
215 /// <param name="month">An integer giving the calendar month.
217 /// <param name="day">An integer giving the calendar day.
219 /// <param name="era">The era number.</param>
220 /// <exception cref="T:System.ArgumentException">
221 /// The exception is thrown if the era is not equal
222 /// <see cref="F:PersianEra"/>.
224 /// <exception cref="T:System.ArgumentOutOfRangeException">
225 /// The exception is thrown if the calendar year, month, or day is
226 /// outside of the allowed range.
228 internal void M_CheckYMDE(int year, int month, int day,
231 M_CheckYME(year, month, ref era);
232 M_ArgumentInRange("day",
233 day, 1, GetDaysInMonth(year, month, era));
234 if (year == M_MaxYear && month == 10 && day > 10)
235 throw new ArgumentOutOfRangeException("day",
236 "Days in month 10 of year 9378 must" +
237 " be between one and ten.");
240 internal const int epoch = 226895;
242 // FIXME: this may need a "static". I don't know enough C#.
243 internal int fixed_from_dmy(int day, int month, int year)
247 k += 365 * (year - 1);
248 k += (8 * year + 21) / 33;
250 k += 31 * (month - 1);
252 k += 30 * (month - 1) + 6;
258 // FIXME: this may need a "static". I don't know enough C#.
259 internal int year_from_fixed(int date)
261 return (33 * (date - epoch) + 3) / 12053 + 1;
264 // FIXME: this may need a "static". I don't know enough C#.
265 internal void my_from_fixed(out int month, out int year,
270 year = year_from_fixed(date);
271 day = date - fixed_from_dmy (1, 1, year);
273 month = day / 31 + 1;
275 month = (day - 6) / 30 + 1;
278 // FIXME: this may need a "static". I don't know enough C#.
279 internal void dmy_from_fixed(out int day, out int month,
280 out int year, int date)
282 year = year_from_fixed(date);
283 day = date - fixed_from_dmy (1, 1, year);
285 month = day / 31 + 1;
288 month = (day-6) / 30 + 1;
289 day = (day-6) % 30 + 1;
293 // FIXME: this may need a "static". I don't know enough C#.
294 internal bool is_leap_year(int year)
296 return (25 * year + 11) % 33 < 8;
300 /// Overrideden. Adds months to a given date.
302 /// <param name="time">The
303 /// <see cref="T:System.DateTime"/> to which to add
306 /// <param name="months">The number of months to add.</param>
307 /// <returns>A new <see cref="T:System.DateTime"/> value, that
308 /// results from adding <paramref name="months"/> to the specified
309 /// DateTime.</returns>
310 /// <exception cref="T:System.ArgumentOutOfRangeException">
311 /// The exception is thrown if the
312 /// <see cref="T:System.DateTime"/> return value is not in the years
313 /// between 1 A.P. and 9999 C.E., inclusive.
315 public override DateTime AddMonths(DateTime time, int months)
317 int rd = CCFixed.FromDateTime(time);
318 int day, month, year;
320 dmy_from_fixed(out day, out month, out year, rd);
322 year += CCMath.div_mod(out month, month, 12);
323 rd = fixed_from_dmy(day, month, year);
324 DateTime t = CCFixed.ToDateTime(rd);
325 t = t.Add(time.TimeOfDay);
331 /// Overridden. Adds years to a given date.
333 /// <param name="time">The
334 /// <see cref="T:System.DateTime"/> to which to add
337 /// <param name="years">The number of years to add.</param>
338 /// <returns>A new <see cref="T:System.DateTime"/> value, that
339 /// results from adding <paramref name="years"/> to the specified
340 /// DateTime.</returns>
341 /// <exception cref="T:System.ArgumentOutOfRangeException">
342 /// The exception is thrown if the
343 /// <see cref="T:System.DateTime"/> return value is not in the years
344 /// between 1 A.P. and 9999 C.E., inclusive.
346 public override DateTime AddYears(DateTime time, int years)
348 int rd = CCFixed.FromDateTime(time);
349 int day, month, year;
351 dmy_from_fixed(out day, out month, out year, rd);
353 rd = fixed_from_dmy(day, month, year);
354 DateTime t = CCFixed.ToDateTime(rd);
355 t = t.Add(time.TimeOfDay);
361 /// Overriden. Gets the day of the month from
362 /// <paramref name="time"/>.
364 /// <param name="time">The
365 /// <see cref="T:System.DateTime"/> that specifies a
368 /// <returns>An integer giving the day of months, starting with 1.
370 /// <exception cref="T:System.ArgumentOutOfRangeException">
371 /// The exception is thrown if the
372 /// <see cref="T:System.DateTime"/> parameter is not in the years
373 /// between 1 A.P. and 9999 C.E., inclusive.
375 public override int GetDayOfMonth(DateTime time)
377 int day, month, year;
379 M_CheckDateTime(time);
380 int rd = CCFixed.FromDateTime(time);
381 dmy_from_fixed(out day, out month, out year, rd);
386 /// Overriden. Gets the day of the week from the specified date.
388 /// <param name="time">The
389 /// <see cref="T:System.DateTime"/> that specifies a
392 /// <returns>An integer giving the day of months, starting with 1.
394 /// <exception cref="T:System.ArgumentOutOfRangeException">
395 /// The exception is thrown if the
396 /// <see cref="T:System.DateTime"/> parameter is not in the years
397 /// between 1 A.P. and 9999 C.E., inclusive.
399 public override DayOfWeek GetDayOfWeek(DateTime time)
401 M_CheckDateTime(time);
402 int rd = CCFixed.FromDateTime(time);
403 return (DayOfWeek)CCFixed.day_of_week(rd);
407 /// Overridden. Gives the number of the day in the year.
409 /// <param name="time">The
410 /// <see cref="T:System.DateTime"/> that specifies a
413 /// <returns>An integer representing the day of the year,
414 /// starting with 1.</returns>
415 /// <exception cref="T:System.ArgumentOutOfRangeException">
416 /// The exception is thrown if the
417 /// <see cref="T:System.DateTime"/> parameter is not in the years
418 /// between 1 A.P. and 9999 C.E., inclusive.
420 public override int GetDayOfYear(DateTime time)
422 M_CheckDateTime(time);
423 int rd = CCFixed.FromDateTime(time);
424 int year = year_from_fixed(rd);
425 int rd1_1 = fixed_from_dmy(1, 1, year);
426 return rd - rd1_1 + 1;
430 /// Overridden. Gives the number of days in the specified month
431 /// of the given year and era.
433 /// <param name="year">An integer that gives the year.
435 /// <param name="month">An integer that gives the month, starting
437 /// <param name="era">An integer that gives the era of the specified
439 /// <returns>An integer that gives the number of days of the
440 /// specified month.</returns>
441 /// <exception cref="T:System.ArgumentOutOfRangeException">
442 /// The exception is thrown, if <paramref name="month"/>,
443 /// <paramref name="year"/> ,or <paramref name="era"/> is outside
444 /// the allowed range.
446 public override int GetDaysInMonth(int year, int month, int era)
448 M_CheckYME(year, month, ref era);
451 } else if (month == 12 && !is_leap_year(year)) {
459 /// Overridden. Gives the number of days of the specified
460 /// year of the given era.
462 /// <param name="year">An integer that specifies the year.
464 /// <param name="era">An ineger that specifies the era.
466 /// <returns>An integer that gives the number of days of the
467 /// specified year.</returns>
468 /// <exception cref="T:System.ArgumentOutOfRangeExceiption">
469 /// The exception is thrown, if
470 /// <paramref name="year"/> or <paramref name="era"/> are outside the
473 public override int GetDaysInYear(int year, int era)
475 M_CheckYE(year, ref era);
476 return is_leap_year(year) ? 366 : 365;
481 /// Overridden. Gives the era of the specified date.
483 /// <param name="time">The
484 /// <see cref="T:System.DateTime"/> that specifies a
487 /// <returns>An integer representing the era of the calendar.
489 /// <exception cref="T:System.ArgumentOutOfRangeException">
490 /// The exception is thrown if the
491 /// <see cref="T:System.DateTime"/> parameter is not in the years
492 /// between 1 A.P. and 9999 C.E., inclusive.
494 public override int GetEra(DateTime time)
496 M_CheckDateTime(time);
500 public override int GetLeapMonth (int year, int era)
506 /// Overridden. Gives the number of the month of the specified
509 /// <param name="time">The
510 /// <see cref="T:System.DateTime"/> that specifies a
513 /// <returns>An integer representing the month,
514 /// starting with 1.</returns>
515 /// <exception cref="T:System.ArgumentOutOfRangeException">
516 /// The exception is thrown if the
517 /// <see cref="T:System.DateTime"/> parameter is not in the years
518 /// between 1 A.P. and 9999 C.E., inclusive.
520 public override int GetMonth(DateTime time)
522 M_CheckDateTime(time);
523 int rd = CCFixed.FromDateTime(time);
525 my_from_fixed(out month, out year, rd);
530 /// Overridden. Gives the number of months in the specified year
533 /// <param name="year">An integer that specifies the year.
535 /// <param name="era">An integer that specifies the era.
537 /// <returns>An integer that gives the number of the months in the
538 /// specified year.</returns>
539 /// <exception cref="T:System.ArgumentOutOfRangeException">
540 /// The exception is thrown, if the year or the era are not valid.
542 public override int GetMonthsInYear(int year, int era)
544 M_CheckYE(year, ref era);
549 /// Overridden. Gives the number of the year of the specified
552 /// <param name="time">The
553 /// <see cref="T:System.DateTime"/> that specifies a
556 /// <returns>An integer representing the year,
557 /// starting with 1.</returns>
558 /// <exception cref="T:System.ArgumentOutOfRangeException">
559 /// The exception is thrown if the
560 /// <see cref="T:System.DateTime"/> parameter is not in the years
561 /// between 1 A.P. and 9999 C.E., inclusive.
563 public override int GetYear(DateTime time)
565 M_CheckDateTime(time);
566 int rd = CCFixed.FromDateTime(time);
567 return year_from_fixed(rd);
571 /// Overridden. Tells whether the given day
574 /// <param name="year">An integer that specifies the year in the
577 /// <param name="month">An integer that specifies the month.
579 /// <param name="day">An integer that specifies the day.
581 /// <param name="era">An integer that specifies the era.
583 /// <returns>A boolean that tells whether the given day is a leap
586 /// <exception cref="T:System.ArgumentOutOfRangeException">
587 /// The exception is thrown, if the year, month, day, or era is not
590 public override bool IsLeapDay(int year, int month, int day,
593 M_CheckYMDE(year, month, day, ref era);
594 return is_leap_year(year) && month == 12 && day == 30;
598 /// Overridden. Tells whether the given month
601 /// <param name="year">An integer that specifies the year in the
604 /// <param name="month">An integer that specifies the month.
606 /// <param name="era">An integer that specifies the era.
608 /// <returns>A boolean that tells whether the given month is a leap
611 /// <exception cref="T:System.ArgumentOutOfRangeException">
612 /// The exception is thrown, if the year, month, or era is not
615 public override bool IsLeapMonth(int year, int month, int era)
617 M_CheckYME(year, month, ref era);
622 /// Overridden. Tells whether the given year
625 /// <param name="year">An integer that specifies the year in the
628 /// <param name="era">An integer that specifies the era.
630 /// <returns>A boolean that tells whether the given year is a leap
633 /// <exception cref="T:System.ArgumentOutOfRangeException">
634 /// The exception is thrown, if the year or era is not
637 public override bool IsLeapYear(int year, int era)
639 M_CheckYE(year, ref era);
640 return is_leap_year(year);
644 /// Overridden. Creates the
645 /// <see cref="T:System.DateTime"/> from the parameters.
647 /// <param name="year">An integer that gives the year in the
648 /// <paramref name="era"/>.
650 /// <param name="month">An integer that specifies the month.
652 /// <param name="day">An integer that specifies the day.
654 /// <param name="hour">An integer that specifies the hour.
656 /// <param name="minute">An integer that specifies the minute.
658 /// <param name="second">An integer that gives the second.
660 /// <param name="milliseconds">An integer that gives the
663 /// <param name="era">An integer that specifies the era.
666 /// <see cref="T:system.DateTime"/> representig the date and time.
668 /// <exception cref="T:System.ArgumentOutOfRangeException">
669 /// The exception is thrown, if at least one of the parameters
672 public override DateTime ToDateTime(int year, int month, int day,
673 int hour, int minute,
674 int second, int millisecond,
677 M_CheckYMDE(year, month, day, ref era);
678 M_CheckHMSM(hour, minute, second, millisecond);
679 int rd = fixed_from_dmy(day, month, year);
680 return CCFixed.ToDateTime(rd,
681 hour, minute, second, millisecond);
684 // FIXME: Calendar.cs and HebrewCalendar.cs are different in
685 // how they handle this. I have randomly chosen the
686 // HebrewCalendar.cs implementation.
687 public override int ToFourDigitYear (int year)
689 M_ArgumentInRange ("year", year, 0, 99);
691 int baseExtra = this.twoDigitYearMax % 100;
692 int baseCentury = this.twoDigitYearMax - baseExtra;
694 if (year <= baseExtra)
695 return baseCentury + year;
697 return baseCentury + year - 100;
700 public override CalendarAlgorithmType AlgorithmType {
702 return CalendarAlgorithmType.SolarCalendar;
706 static DateTime PersianMin = new DateTime (622, 3, 21, 0, 0, 0);
707 static DateTime PersianMax = new DateTime (9999, 12, 31, 11, 59, 59);
709 public override DateTime MinSupportedDateTime {
715 public override DateTime MaxSupportedDateTime {
720 } // class PersianCalendar
722 } // namespace System.Globalization