2 // PersianCalendar.cs: Implements the Persian calendar
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.
34 namespace System.Globalization {
40 /// This is the Persian calendar of Iran, also known as the Iranian
41 /// calendar or the Jalaali calendar. The Afghan calendar may or may
42 /// not be different, as different sources disagree about it.
45 /// <para>The implemented algorithm is the simple 33-year arithmetic
46 /// calendar, which is not the same as the official Iranian calendar.
47 /// But this arithmetic calendar has been confirmed to produce the
48 /// same results as the official Iranian calendar at least
49 /// from 1925 C.E., when the calendar was officially introduced,
50 /// to 2088 C.E. This is the same algorithm that is used in .NET.
52 /// <para>The Iranian law explicitly mentions that the true solar year
53 /// should be used, which requires astronomical calculations of the
54 /// March equinox and the solar apparent noon. The exact locale for
55 /// observation of the apparent noon is not mentioned in the 1925 law,
56 /// but the current practice is using the 52.5 E meridian, which is
57 /// the meridian defining the official timezone of Iran.
59 /// <para>Also, please note that implementing the Persian calendar
60 /// using the 2820-year arithmetic algorithm, as suggested by
61 /// Ahmad Birashk and others, is less accurate than the 33-year
62 /// calendar: first, it fails earlier than the 33-year cycle in
63 /// matching the official astronomical calendar (first failure is
64 /// in 2025 C.E.), and second, the 2820-year suggested rule is based
65 /// 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);
501 /// Overridden. Gives the number of the month of the specified
504 /// <param name="time">The
505 /// <see cref="T:System.DateTime"/> that specifies a
508 /// <returns>An integer representing the month,
509 /// starting with 1.</returns>
510 /// <exception cref="T:System.ArgumentOutOfRangeException">
511 /// The exception is thrown if the
512 /// <see cref="T:System.DateTime"/> parameter is not in the years
513 /// between 1 A.P. and 9999 C.E., inclusive.
515 public override int GetMonth(DateTime time)
517 M_CheckDateTime(time);
518 int rd = CCFixed.FromDateTime(time);
520 my_from_fixed(out month, out year, rd);
525 /// Overridden. Gives the number of months in the specified year
528 /// <param name="year">An integer that specifies the year.
530 /// <param name="era">An integer that specifies the era.
532 /// <returns>An integer that gives the number of the months in the
533 /// specified year.</returns>
534 /// <exception cref="T:System.ArgumentOutOfRangeException">
535 /// The exception is thrown, if the year or the era are not valid.
537 public override int GetMonthsInYear(int year, int era)
539 M_CheckYE(year, ref era);
544 /// Overridden. Gives the number of the year of the specified
547 /// <param name="time">The
548 /// <see cref="T:System.DateTime"/> that specifies a
551 /// <returns>An integer representing the year,
552 /// starting with 1.</returns>
553 /// <exception cref="T:System.ArgumentOutOfRangeException">
554 /// The exception is thrown if the
555 /// <see cref="T:System.DateTime"/> parameter is not in the years
556 /// between 1 A.P. and 9999 C.E., inclusive.
558 public override int GetYear(DateTime time)
560 M_CheckDateTime(time);
561 int rd = CCFixed.FromDateTime(time);
562 return year_from_fixed(rd);
566 /// Overridden. Tells whether the given day
569 /// <param name="year">An integer that specifies the year in the
572 /// <param name="month">An integer that specifies the month.
574 /// <param name="day">An integer that specifies the day.
576 /// <param name="era">An integer that specifies the era.
578 /// <returns>A boolean that tells whether the given day is a leap
581 /// <exception cref="T:System.ArgumentOutOfRangeException">
582 /// The exception is thrown, if the year, month, day, or era is not
585 public override bool IsLeapDay(int year, int month, int day,
588 M_CheckYMDE(year, month, day, ref era);
589 return is_leap_year(year) && month == 12 && day == 30;
593 /// Overridden. Tells whether the given month
596 /// <param name="year">An integer that specifies the year in the
599 /// <param name="month">An integer that specifies the month.
601 /// <param name="era">An integer that specifies the era.
603 /// <returns>A boolean that tells whether the given month is a leap
606 /// <exception cref="T:System.ArgumentOutOfRangeException">
607 /// The exception is thrown, if the year, month, or era is not
610 public override bool IsLeapMonth(int year, int month, int era)
612 M_CheckYME(year, month, ref era);
617 /// Overridden. Tells whether the given year
620 /// <param name="year">An integer that specifies the year in the
623 /// <param name="era">An integer that specifies the era.
625 /// <returns>A boolean that tells whether the given year is a leap
628 /// <exception cref="T:System.ArgumentOutOfRangeException">
629 /// The exception is thrown, if the year or era is not
632 public override bool IsLeapYear(int year, int era)
634 M_CheckYE(year, ref era);
635 return is_leap_year(year);
639 /// Overridden. Creates the
640 /// <see cref="T:System.DateTime"/> from the parameters.
642 /// <param name="year">An integer that gives the year in the
643 /// <paramref name="era"/>.
645 /// <param name="month">An integer that specifies the month.
647 /// <param name="day">An integer that specifies the day.
649 /// <param name="hour">An integer that specifies the hour.
651 /// <param name="minute">An integer that specifies the minute.
653 /// <param name="second">An integer that gives the second.
655 /// <param name="milliseconds">An integer that gives the
658 /// <param name="era">An integer that specifies the era.
661 /// <see cref="T:system.DateTime"/> representig the date and time.
663 /// <exception cref="T:System.ArgumentOutOfRangeException">
664 /// The exception is thrown, if at least one of the parameters
667 public override DateTime ToDateTime(int year, int month, int day,
668 int hour, int minute,
669 int second, int milliseconds,
672 M_CheckYMDE(year, month, day, ref era);
673 M_CheckHMSM(hour, minute, second, milliseconds);
674 int rd = fixed_from_dmy(day, month, year);
675 return CCFixed.ToDateTime(rd,
676 hour, minute, second, milliseconds);
679 // FIXME: Calendar.cs and HebrewCalendar.cs are different in
680 // how they handle this. I have randomly chosen the
681 // HebrewCalendar.cs implementation.
682 public override int ToFourDigitYear (int year)
684 M_ArgumentInRange ("year", year, 0, 99);
686 int baseExtra = this.twoDigitYearMax % 100;
687 int baseCentury = this.twoDigitYearMax - baseExtra;
689 if (year <= baseExtra)
690 return baseCentury + year;
692 return baseCentury + year - 100;
695 } // class PersianCalendar
697 } // namespace System.Globalization