* roottypes.cs: Rename from tree.cs.
[mono.git] / mcs / class / corlib / System.Globalization / PersianCalendar.cs
1 //
2 // PersianCalendar.cs: Implements the Persian calendar
3 //
4 // Authors:
5 //   Roozbeh Pournader (roozbeh@farsiweb.info)
6 //   Ulrich Kunitz
7 //
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)
11 //
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:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
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.
30 //
31
32 #if NET_2_0
33
34 namespace System.Globalization {
35
36 using System;
37 using System.IO;
38
39 /// <summary>
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.
43 /// </summary>
44 /// <remarks>
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.
51 /// </para>
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.
58 /// </para>
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.
66 /// </para>
67 /// </remarks>
68 public class PersianCalendar : Calendar {
69         /// <summary>
70         /// Constructor.
71         /// </summary>
72         public PersianCalendar() {
73                 M_AbbrEraNames = new string[] {"A.P."};
74                 M_EraNames = new string[] {"Anno Persico"};
75                 if (M_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                         M_TwoDigitYearMax = 1410;
79         }
80
81         /// <summary>
82         /// The era number for the Anno Persico (A.P.) era, called
83         /// plain PersianEra.
84         /// </summary>
85         public static readonly int PersianEra = 1;
86
87         /// <summary>
88         /// The
89         /// <see cref="T:System.DateTime"/> ticks for first day of
90         /// year 1 A.P.
91         /// </summary>
92         internal const long M_MinTicks = 196036416000000000L;
93
94         /// <summary>
95         /// The minimum year in the A.P. era supported.
96         /// </summary>
97         internal const int M_MinYear = 1;
98
99         /// <value>Overridden. Gives the eras supported by the Persian
100         /// calendar as an array of integers.
101         /// </value>
102         public override int[] Eras {
103                 get {
104                         return new int[] { PersianEra }; 
105                 }
106         }
107
108         // FIXME: the .NET documentation does not mention the default value,
109         // This is the value mentioned in the .NET documentation example result.
110         int twoDigitYearMax = 1410;
111         
112         public override int TwoDigitYearMax {
113                 get {
114                         return twoDigitYearMax;
115                 }
116                 set {
117                         CheckReadOnly ();
118                         M_ArgumentInRange ("value", value, 100, M_MaxYear);
119
120                         twoDigitYearMax = value;
121                 }
122         }
123
124         /// <summary>
125         /// A protected member checking a
126         /// <see cref="T:System.DateTime"/> value.
127         /// </summary>
128         /// <param name="time">The
129         /// <see cref="T:System.DateTime"/>
130         /// to check.
131         /// </param>
132         /// <exception cref="T:System.ArgumentOutOfRangeException">
133         /// The exception is thrown if the
134         /// <see cref="T:System.DateTime"/> parameter is before the
135         /// year 1 A.P.
136         /// </exception>
137         internal void M_CheckDateTime(DateTime time)
138         {
139                 if (time.Ticks < M_MinTicks)
140                         throw new ArgumentOutOfRangeException(
141                                 "time",
142                                 "Only positive Persian years are supported.");
143         }
144
145         /// <summary>
146         /// A protected method checking the era number.
147         /// </summary>
148         /// <param name="era">The era number.</param>
149         /// <exception name="T:System.ArgumentException">
150         /// The exception is thrown if the era is not equal
151         /// <see cref="F:PersianEra"/>.
152         /// </exception>
153         internal void M_CheckEra(ref int era)
154         {
155                 if (era == CurrentEra)
156                         era = PersianEra;
157                 if (era != PersianEra)
158                         throw new ArgumentException("Era value was not valid.");
159         }
160
161         /// <summary>
162         /// A protected method checking calendar year and the era number.
163         /// </summary>
164         /// <param name="year">An integer representing the calendar year.
165         /// </param>
166         /// <param name="era">The era number.</param>
167         /// <exception cref="T:System.ArgumentException">
168         /// The exception is thrown if the era is not equal
169         /// <see cref="F:PersianEra"/>.
170         /// </exception>
171         /// <exception cref="T:System.ArgumentOutOfRangeException">
172         /// The exception is thrown if the calendar year is outside of
173         /// the allowed range.
174         /// </exception>
175         internal override void M_CheckYE(int year, ref int era)
176         {
177                 M_CheckEra(ref era);
178                 if (year < M_MinYear || year > M_MaxYear)
179                         throw new ArgumentOutOfRangeException(
180                                 "year",
181                                 "Only Persian years between 1 and 9378," +
182                                 " inclusive, are supported.");
183         }
184
185         /// <summary>
186         /// A protected method checking the calendar year, month, and
187         /// era number.
188         /// </summary>
189         /// <param name="year">An integer representing the calendar year.
190         /// </param>
191         /// <param name="month">An integer giving the calendar month.
192         /// </param>
193         /// <param name="era">The era number.</param>
194         /// <exception cref="T:System.ArgumentException">
195         /// The exception is thrown if the era is not equal
196         /// <see cref="F:PersianEra"/>.
197         /// </exception>
198         /// <exception cref="T:System.ArgumentOutOfRangeException">
199         /// The exception is thrown if the calendar year or month is
200         /// outside of the allowed range.
201         /// </exception>
202         internal void M_CheckYME(int year, int month, ref int era)
203         {
204                 M_CheckYE(year, ref era);
205                 if (month < 1 || month > 12)
206                         throw new ArgumentOutOfRangeException("month",
207                                 "Month must be between one and twelve.");
208                 else if (year == M_MaxYear && month > 10)
209                         throw new ArgumentOutOfRangeException("month",
210                                 "Months in year 9378 must be between one and ten.");
211         }
212
213         /// <summary>
214         /// A protected method checking the calendar day, month, and year
215         /// and the era number.
216         /// </summary>
217         /// <param name="year">An integer representing the calendar year.
218         /// </param>
219         /// <param name="month">An integer giving the calendar month.
220         /// </param>
221         /// <param name="day">An integer giving the calendar day.
222         /// </param>
223         /// <param name="era">The era number.</param>
224         /// <exception cref="T:System.ArgumentException">
225         /// The exception is thrown if the era is not equal
226         /// <see cref="F:PersianEra"/>.
227         /// </exception>
228         /// <exception cref="T:System.ArgumentOutOfRangeException">
229         /// The exception is thrown if the calendar year, month, or day is
230         /// outside of the allowed range.
231         /// </exception>
232         internal void M_CheckYMDE(int year, int month, int day,
233                                   ref int era)
234         {
235                 M_CheckYME(year, month, ref era);
236                 M_ArgumentInRange("day",
237                         day, 1, GetDaysInMonth(year, month, era));
238                 if (year == M_MaxYear && month == 10 && day > 10)
239                         throw new ArgumentOutOfRangeException("day",
240                                 "Days in month 10 of year 9378 must" +
241                                 " be between one and ten.");
242         }
243
244         internal const int epoch = 226895;
245
246         // FIXME: this may need a "static". I don't know enough C#.
247         internal int fixed_from_dmy(int day, int month, int year)
248         {
249                 int k = epoch - 1;
250
251                 k += 365 * (year - 1);
252                 k += (8 * year + 21) / 33;
253                 if (month <= 7)
254                         k += 31 * (month - 1);
255                 else
256                         k += 30 * (month - 1) + 6;
257                 k += day;
258                 
259                 return k;
260         }
261         
262         // FIXME: this may need a "static". I don't know enough C#.
263         internal int year_from_fixed(int date)
264         {
265                 return (33 * (date - epoch) + 3) / 12053 + 1;
266         }
267
268         // FIXME: this may need a "static". I don't know enough C#.
269         internal void my_from_fixed(out int month, out int year,
270                 int date)
271         {
272                 int day;
273                 
274                 year = year_from_fixed(date);
275                 day = date - fixed_from_dmy (1, 1, year);
276                 if (day < 216)
277                         month = day / 31 + 1;
278                 else
279                         month = (day - 6) / 30 + 1;
280         }
281
282         // FIXME: this may need a "static". I don't know enough C#.
283         internal void dmy_from_fixed(out int day, out int month,
284                                      out int year, int date)
285         {
286                 year = year_from_fixed(date);
287                 day = date - fixed_from_dmy (1, 1, year);
288                 if (day < 216) {
289                         month = day / 31 + 1;
290                         day = day % 31 + 1;
291                 } else {
292                         month = (day-6) / 30 + 1;
293                         day = (day-6) % 30 + 1;
294                 }
295         }
296
297         // FIXME: this may need a "static". I don't know enough C#.
298         internal bool is_leap_year(int year)
299         {
300                 return (25 * year + 11) % 33 < 8;
301         }
302
303         /// <summary>
304         /// Overrideden. Adds months to a given date.
305         /// </summary>
306         /// <param name="time">The
307         /// <see cref="T:System.DateTime"/> to which to add
308         /// months.
309         /// </param>
310         /// <param name="months">The number of months to add.</param>
311         /// <returns>A new <see cref="T:System.DateTime"/> value, that
312         /// results from adding <paramref name="months"/> to the specified
313         /// DateTime.</returns>
314         /// <exception cref="T:System.ArgumentOutOfRangeException">
315         /// The exception is thrown if the
316         /// <see cref="T:System.DateTime"/> return value is not in the years
317         /// between 1 A.P. and 9999 C.E., inclusive.
318         /// </exception>
319         public override DateTime AddMonths(DateTime time, int months)
320         {
321                 int rd = CCFixed.FromDateTime(time);
322                 int day, month, year;
323                 
324                 dmy_from_fixed(out day, out month, out year, rd);
325                 month += months;
326                 year += CCMath.div_mod(out month, month, 12);
327                 rd = fixed_from_dmy(day, month, year);
328                 DateTime t = CCFixed.ToDateTime(rd);
329                 t = t.Add(time.TimeOfDay);
330                 M_CheckDateTime(t);
331                 return t;
332         }
333
334         /// <summary>
335         /// Overridden. Adds years to a given date.
336         /// </summary>
337         /// <param name="time">The
338         /// <see cref="T:System.DateTime"/> to which to add
339         /// years.
340         /// </param>
341         /// <param name="years">The number of years to add.</param>
342         /// <returns>A new <see cref="T:System.DateTime"/> value, that
343         /// results from adding <paramref name="years"/> to the specified
344         /// DateTime.</returns>
345         /// <exception cref="T:System.ArgumentOutOfRangeException">
346         /// The exception is thrown if the
347         /// <see cref="T:System.DateTime"/> return value is not in the years
348         /// between 1 A.P. and 9999 C.E., inclusive.
349         /// </exception>
350         public override DateTime AddYears(DateTime time, int years)
351         {
352                 int rd = CCFixed.FromDateTime(time);
353                 int day, month, year;
354
355                 dmy_from_fixed(out day, out month, out year, rd);
356                 year += years;
357                 rd = fixed_from_dmy(day, month, year);
358                 DateTime t = CCFixed.ToDateTime(rd);
359                 t = t.Add(time.TimeOfDay);
360                 M_CheckDateTime(t);
361                 return t;
362         }
363                 
364         /// <summary>
365         /// Overriden. Gets the day of the month from
366         /// <paramref name="time"/>.
367         /// </summary>
368         /// <param name="time">The
369         /// <see cref="T:System.DateTime"/> that specifies a
370         /// date.
371         /// </param>
372         /// <returns>An integer giving the day of months, starting with 1.
373         /// </returns>
374         /// <exception cref="T:System.ArgumentOutOfRangeException">
375         /// The exception is thrown if the
376         /// <see cref="T:System.DateTime"/> parameter is not in the years
377         /// between 1 A.P. and 9999 C.E., inclusive.
378         /// </exception>
379         public override int GetDayOfMonth(DateTime time)
380         {
381                 int day, month, year;
382                 
383                 M_CheckDateTime(time);
384                 int rd = CCFixed.FromDateTime(time);
385                 dmy_from_fixed(out day, out month, out year, rd);
386                 return day;
387         }
388
389         /// <summary>
390         /// Overriden. Gets the day of the week from the specified date.
391         /// </summary>
392         /// <param name="time">The
393         /// <see cref="T:System.DateTime"/> that specifies a
394         /// date.
395         /// </param>
396         /// <returns>An integer giving the day of months, starting with 1.
397         /// </returns>
398         /// <exception cref="T:System.ArgumentOutOfRangeException">
399         /// The exception is thrown if the
400         /// <see cref="T:System.DateTime"/> parameter is not in the years
401         /// between 1 A.P. and 9999 C.E., inclusive.
402         /// </exception>
403         public override DayOfWeek GetDayOfWeek(DateTime time)
404         {
405                 M_CheckDateTime(time);
406                 int rd = CCFixed.FromDateTime(time);
407                 return (DayOfWeek)CCFixed.day_of_week(rd);
408         }
409
410         /// <summary>
411         /// Overridden. Gives the number of the day in the year.
412         /// </summary>
413         /// <param name="time">The
414         /// <see cref="T:System.DateTime"/> that specifies a
415         /// date.
416         /// </param>
417         /// <returns>An integer representing the day of the year,
418         /// starting with 1.</returns>
419         /// <exception cref="T:System.ArgumentOutOfRangeException">
420         /// The exception is thrown if the
421         /// <see cref="T:System.DateTime"/> parameter is not in the years
422         /// between 1 A.P. and 9999 C.E., inclusive.
423         /// </exception>
424         public override int GetDayOfYear(DateTime time)
425         {
426                 M_CheckDateTime(time);
427                 int rd = CCFixed.FromDateTime(time);
428                 int year = year_from_fixed(rd);
429                 int rd1_1 = fixed_from_dmy(1, 1, year);
430                 return rd - rd1_1 + 1;
431         }
432
433         /// <summary>
434         /// Overridden. Gives the number of days in the specified month
435         /// of the given year and era.
436         /// </summary>
437         /// <param name="year">An integer that gives the year.
438         /// </param>
439         /// <param name="month">An integer that gives the month, starting
440         /// with 1.</param>
441         /// <param name="era">An integer that gives the era of the specified
442         /// year.</param>
443         /// <returns>An integer that gives the number of days of the
444         /// specified month.</returns>
445         /// <exception cref="T:System.ArgumentOutOfRangeException">
446         /// The exception is thrown, if <paramref name="month"/>,
447         /// <paramref name="year"/> ,or <paramref name="era"/> is outside
448         /// the allowed range.
449         /// </exception>
450         public override int GetDaysInMonth(int year, int month, int era)
451         {
452                 M_CheckYME(year, month, ref era);
453                 if (month <= 6) {
454                         return 31;
455                 } else if (month == 12 && !is_leap_year(year)) {
456                         return 29;
457                 } else {
458                         return 30;
459                 }
460         }
461
462         /// <summary>
463         /// Overridden. Gives the number of days of the specified
464         /// year of the given era. 
465         /// </summary>
466         /// <param name="year">An integer that specifies the year. 
467         /// </param>
468         /// <param name="era">An ineger that specifies the era.
469         /// </param>
470         /// <returns>An integer that gives the number of days of the
471         /// specified year.</returns>
472         /// <exception cref="T:System.ArgumentOutOfRangeExceiption">
473         /// The exception is thrown, if
474         /// <paramref name="year"/> or <paramref name="era"/> are outside the
475         /// allowed range.
476         /// </exception>
477         public override int GetDaysInYear(int year, int era)
478         {
479                 M_CheckYE(year, ref era);
480                 return is_leap_year(year) ? 366 : 365;
481         }
482                 
483
484         /// <summary>
485         /// Overridden. Gives the era of the specified date.
486         /// </summary>
487         /// <param name="time">The
488         /// <see cref="T:System.DateTime"/> that specifies a
489         /// date.
490         /// </param>
491         /// <returns>An integer representing the era of the calendar.
492         /// </returns>
493         /// <exception cref="T:System.ArgumentOutOfRangeException">
494         /// The exception is thrown if the
495         /// <see cref="T:System.DateTime"/> parameter is not in the years
496         /// between 1 A.P. and 9999 C.E., inclusive.
497         /// </exception>
498         public override int GetEra(DateTime time)
499         {
500                 M_CheckDateTime(time);
501                 return PersianEra;
502         }
503
504         /// <summary>
505         /// Overridden. Gives the number of the month of the specified
506         /// date.
507         /// </summary>
508         /// <param name="time">The
509         /// <see cref="T:System.DateTime"/> that specifies a
510         /// date.
511         /// </param>
512         /// <returns>An integer representing the month, 
513         /// starting with 1.</returns>
514         /// <exception cref="T:System.ArgumentOutOfRangeException">
515         /// The exception is thrown if the
516         /// <see cref="T:System.DateTime"/> parameter is not in the years
517         /// between 1 A.P. and 9999 C.E., inclusive.
518         /// </exception>
519         public override int GetMonth(DateTime time)
520         {
521                 M_CheckDateTime(time);
522                 int rd = CCFixed.FromDateTime(time);
523                 int month, year;
524                 my_from_fixed(out month, out year, rd);
525                 return month;
526         }
527
528         /// <summary>
529         /// Overridden. Gives the number of months in the specified year 
530         /// and era.
531         /// </summary>
532         /// <param name="year">An integer that specifies the year.
533         /// </param>
534         /// <param name="era">An integer that specifies the era.
535         /// </param>
536         /// <returns>An integer that gives the number of the months in the
537         /// specified year.</returns>
538         /// <exception cref="T:System.ArgumentOutOfRangeException">
539         /// The exception is thrown, if the year or the era are not valid.
540         /// </exception>
541         public override int GetMonthsInYear(int year, int era)
542         {
543                 M_CheckYE(year, ref era);
544                 return 12;
545         }
546
547         /// <summary>
548         /// Overridden. Gives the number of the year of the specified
549         /// date.
550         /// </summary>
551         /// <param name="time">The
552         /// <see cref="T:System.DateTime"/> that specifies a
553         /// date.
554         /// </param>
555         /// <returns>An integer representing the year, 
556         /// starting with 1.</returns>
557         /// <exception cref="T:System.ArgumentOutOfRangeException">
558         /// The exception is thrown if the
559         /// <see cref="T:System.DateTime"/> parameter is not in the years
560         /// between 1 A.P. and 9999 C.E., inclusive.
561         /// </exception>
562         public override int GetYear(DateTime time)
563         {
564                 M_CheckDateTime(time);
565                 int rd = CCFixed.FromDateTime(time);
566                 return year_from_fixed(rd);
567         }
568
569         /// <summary>
570         /// Overridden. Tells whether the given day 
571         /// is a leap day.
572         /// </summary>
573         /// <param name="year">An integer that specifies the year in the
574         /// given era.
575         /// </param>
576         /// <param name="month">An integer that specifies the month.
577         /// </param>
578         /// <param name="day">An integer that specifies the day.
579         /// </param>
580         /// <param name="era">An integer that specifies the era.
581         /// </param>
582         /// <returns>A boolean that tells whether the given day is a leap
583         /// day.
584         /// </returns>
585         /// <exception cref="T:System.ArgumentOutOfRangeException">
586         /// The exception is thrown, if the year, month, day, or era is not
587         /// valid.
588         /// </exception>
589         public override bool IsLeapDay(int year, int month, int day,
590                                        int era)
591         {
592                 M_CheckYMDE(year, month, day, ref era);
593                 return is_leap_year(year) && month == 12 && day == 30;
594         }
595
596         /// <summary>
597         /// Overridden. Tells whether the given month 
598         /// is a leap month.
599         /// </summary>
600         /// <param name="year">An integer that specifies the year in the
601         /// given era.
602         /// </param>
603         /// <param name="month">An integer that specifies the month.
604         /// </param>
605         /// <param name="era">An integer that specifies the era.
606         /// </param>
607         /// <returns>A boolean that tells whether the given month is a leap
608         /// month.
609         /// </returns>
610         /// <exception cref="T:System.ArgumentOutOfRangeException">
611         /// The exception is thrown, if the year, month, or era is not
612         /// valid.
613         /// </exception>
614         public override bool IsLeapMonth(int year, int month, int era)
615         {
616                 M_CheckYME(year, month, ref era);
617                 return false;
618         }
619
620         /// <summary>
621         /// Overridden. Tells whether the given year
622         /// is a leap year.
623         /// </summary>
624         /// <param name="year">An integer that specifies the year in the
625         /// given era.
626         /// </param>
627         /// <param name="era">An integer that specifies the era.
628         /// </param>
629         /// <returns>A boolean that tells whether the given year is a leap
630         /// year.
631         /// </returns>
632         /// <exception cref="T:System.ArgumentOutOfRangeException">
633         /// The exception is thrown, if the year or era is not
634         /// valid.
635         /// </exception>
636         public override bool IsLeapYear(int year, int era)
637         {
638                 M_CheckYE(year, ref era);
639                 return is_leap_year(year);
640         }
641
642         /// <summary>
643         /// Overridden. Creates the
644         /// <see cref="T:System.DateTime"/> from the parameters.
645         /// </summary>
646         /// <param name="year">An integer that gives the year in the
647         /// <paramref name="era"/>.
648         /// </param>
649         /// <param name="month">An integer that specifies the month.
650         /// </param>
651         /// <param name="day">An integer that specifies the day.
652         /// </param>
653         /// <param name="hour">An integer that specifies the hour.
654         /// </param>
655         /// <param name="minute">An integer that specifies the minute.
656         /// </param>
657         /// <param name="second">An integer that gives the second.
658         /// </param>
659         /// <param name="milliseconds">An integer that gives the
660         /// milliseconds.
661         /// </param>
662         /// <param name="era">An integer that specifies the era.
663         /// </param>
664         /// <returns>A
665         /// <see cref="T:system.DateTime"/> representig the date and time.
666         /// </returns>
667         /// <exception cref="T:System.ArgumentOutOfRangeException">
668         /// The exception is thrown, if at least one of the parameters
669         /// is out of range.
670         /// </exception>
671         public override DateTime ToDateTime(int year, int month, int day,
672                                             int hour, int minute,
673                                             int second, int milliseconds,
674                                             int era)
675         {
676                 M_CheckYMDE(year, month, day, ref era);
677                 M_CheckHMSM(hour, minute, second, milliseconds);
678                 int rd = fixed_from_dmy(day, month, year);
679                 return CCFixed.ToDateTime(rd,
680                         hour, minute, second, milliseconds);
681         }
682
683         // FIXME: Calendar.cs and HebrewCalendar.cs are different in
684         // how they handle this. I have randomly chosen the
685         // HebrewCalendar.cs implementation.
686         public override int ToFourDigitYear (int year)
687         {
688                 M_ArgumentInRange ("year", year, 0, 99);
689                 
690                 int baseExtra = this.twoDigitYearMax % 100;
691                 int baseCentury = this.twoDigitYearMax - baseExtra;
692                 
693                 if (year <= baseExtra)
694                         return baseCentury + year;
695                 else
696                         return baseCentury + year - 100;
697         }
698
699 } // class PersianCalendar
700         
701 } // namespace System.Globalization
702
703 #endif