Merge pull request #913 from Daniel15/fix-catchall-routing-master
[mono.git] / mcs / class / corlib / System.Globalization / PersianCalendar.cs
1 //
2 // System.Globalization.PersianCalendar.cs
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 namespace System.Globalization {
33
34 using System;
35 using System.IO;
36 using System.Runtime.InteropServices;
37
38 /// <summary>
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.
42 /// </summary>
43 /// <remarks>
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.
50 /// </para>
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.
57 /// </para>
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.
65 /// </para>
66 /// </remarks>
67 [Serializable]
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 (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;
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         public override int TwoDigitYearMax {
109                 get {
110                         return twoDigitYearMax;
111                 }
112                 set {
113                         CheckReadOnly ();
114                         M_ArgumentInRange ("value", value, 100, M_MaxYear);
115
116                         twoDigitYearMax = value;
117                 }
118         }
119
120         /// <summary>
121         /// A protected member checking a
122         /// <see cref="T:System.DateTime"/> value.
123         /// </summary>
124         /// <param name="time">The
125         /// <see cref="T:System.DateTime"/>
126         /// to check.
127         /// </param>
128         /// <exception cref="T:System.ArgumentOutOfRangeException">
129         /// The exception is thrown if the
130         /// <see cref="T:System.DateTime"/> parameter is before the
131         /// year 1 A.P.
132         /// </exception>
133         internal void M_CheckDateTime(DateTime time)
134         {
135                 if (time.Ticks < M_MinTicks)
136                         throw new ArgumentOutOfRangeException(
137                                 "time",
138                                 "Only positive Persian years are supported.");
139         }
140
141         /// <summary>
142         /// A protected method checking the era number.
143         /// </summary>
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"/>.
148         /// </exception>
149         internal void M_CheckEra(ref int era)
150         {
151                 if (era == CurrentEra)
152                         era = PersianEra;
153                 if (era != PersianEra)
154                         throw new ArgumentException("Era value was not valid.");
155         }
156
157         /// <summary>
158         /// A protected method checking calendar year and the era number.
159         /// </summary>
160         /// <param name="year">An integer representing the calendar year.
161         /// </param>
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"/>.
166         /// </exception>
167         /// <exception cref="T:System.ArgumentOutOfRangeException">
168         /// The exception is thrown if the calendar year is outside of
169         /// the allowed range.
170         /// </exception>
171         internal override void M_CheckYE(int year, ref int era)
172         {
173                 M_CheckEra(ref era);
174                 if (year < M_MinYear || year > M_MaxYear)
175                         throw new ArgumentOutOfRangeException(
176                                 "year",
177                                 "Only Persian years between 1 and 9378," +
178                                 " inclusive, are supported.");
179         }
180
181         /// <summary>
182         /// A protected method checking the calendar year, month, and
183         /// era number.
184         /// </summary>
185         /// <param name="year">An integer representing the calendar year.
186         /// </param>
187         /// <param name="month">An integer giving the calendar month.
188         /// </param>
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"/>.
193         /// </exception>
194         /// <exception cref="T:System.ArgumentOutOfRangeException">
195         /// The exception is thrown if the calendar year or month is
196         /// outside of the allowed range.
197         /// </exception>
198         internal void M_CheckYME(int year, int month, ref int era)
199         {
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.");
207         }
208
209         /// <summary>
210         /// A protected method checking the calendar day, month, and year
211         /// and the era number.
212         /// </summary>
213         /// <param name="year">An integer representing the calendar year.
214         /// </param>
215         /// <param name="month">An integer giving the calendar month.
216         /// </param>
217         /// <param name="day">An integer giving the calendar day.
218         /// </param>
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"/>.
223         /// </exception>
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.
227         /// </exception>
228         internal void M_CheckYMDE(int year, int month, int day,
229                                   ref int era)
230         {
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.");
238         }
239
240         internal const int epoch = 226895;
241
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)
244         {
245                 int k = epoch - 1;
246
247                 k += 365 * (year - 1);
248                 k += (8 * year + 21) / 33;
249                 if (month <= 7)
250                         k += 31 * (month - 1);
251                 else
252                         k += 30 * (month - 1) + 6;
253                 k += day;
254                 
255                 return k;
256         }
257         
258         // FIXME: this may need a "static". I don't know enough C#.
259         internal int year_from_fixed(int date)
260         {
261                 return (33 * (date - epoch) + 3) / 12053 + 1;
262         }
263
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,
266                 int date)
267         {
268                 int day;
269                 
270                 year = year_from_fixed(date);
271                 day = date - fixed_from_dmy (1, 1, year);
272                 if (day < 216)
273                         month = day / 31 + 1;
274                 else
275                         month = (day - 6) / 30 + 1;
276         }
277
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)
281         {
282                 year = year_from_fixed(date);
283                 day = date - fixed_from_dmy (1, 1, year);
284                 if (day < 216) {
285                         month = day / 31 + 1;
286                         day = day % 31 + 1;
287                 } else {
288                         month = (day-6) / 30 + 1;
289                         day = (day-6) % 30 + 1;
290                 }
291         }
292
293         // FIXME: this may need a "static". I don't know enough C#.
294         internal bool is_leap_year(int year)
295         {
296                 return (25 * year + 11) % 33 < 8;
297         }
298
299         /// <summary>
300         /// Overrideden. Adds months to a given date.
301         /// </summary>
302         /// <param name="time">The
303         /// <see cref="T:System.DateTime"/> to which to add
304         /// months.
305         /// </param>
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.
314         /// </exception>
315         public override DateTime AddMonths(DateTime time, int months)
316         {
317                 int rd = CCFixed.FromDateTime(time);
318                 int day, month, year;
319                 
320                 dmy_from_fixed(out day, out month, out year, rd);
321                 month += months;
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);
326                 M_CheckDateTime(t);
327                 return t;
328         }
329
330         /// <summary>
331         /// Overridden. Adds years to a given date.
332         /// </summary>
333         /// <param name="time">The
334         /// <see cref="T:System.DateTime"/> to which to add
335         /// years.
336         /// </param>
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.
345         /// </exception>
346         public override DateTime AddYears(DateTime time, int years)
347         {
348                 int rd = CCFixed.FromDateTime(time);
349                 int day, month, year;
350
351                 dmy_from_fixed(out day, out month, out year, rd);
352                 year += years;
353                 rd = fixed_from_dmy(day, month, year);
354                 DateTime t = CCFixed.ToDateTime(rd);
355                 t = t.Add(time.TimeOfDay);
356                 M_CheckDateTime(t);
357                 return t;
358         }
359                 
360         /// <summary>
361         /// Overriden. Gets the day of the month from
362         /// <paramref name="time"/>.
363         /// </summary>
364         /// <param name="time">The
365         /// <see cref="T:System.DateTime"/> that specifies a
366         /// date.
367         /// </param>
368         /// <returns>An integer giving the day of months, starting with 1.
369         /// </returns>
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.
374         /// </exception>
375         public override int GetDayOfMonth(DateTime time)
376         {
377                 int day, month, year;
378                 
379                 M_CheckDateTime(time);
380                 int rd = CCFixed.FromDateTime(time);
381                 dmy_from_fixed(out day, out month, out year, rd);
382                 return day;
383         }
384
385         /// <summary>
386         /// Overriden. Gets the day of the week from the specified date.
387         /// </summary>
388         /// <param name="time">The
389         /// <see cref="T:System.DateTime"/> that specifies a
390         /// date.
391         /// </param>
392         /// <returns>An integer giving the day of months, starting with 1.
393         /// </returns>
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.
398         /// </exception>
399         public override DayOfWeek GetDayOfWeek(DateTime time)
400         {
401                 M_CheckDateTime(time);
402                 int rd = CCFixed.FromDateTime(time);
403                 return (DayOfWeek)CCFixed.day_of_week(rd);
404         }
405
406         /// <summary>
407         /// Overridden. Gives the number of the day in the year.
408         /// </summary>
409         /// <param name="time">The
410         /// <see cref="T:System.DateTime"/> that specifies a
411         /// date.
412         /// </param>
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.
419         /// </exception>
420         public override int GetDayOfYear(DateTime time)
421         {
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;
427         }
428
429         /// <summary>
430         /// Overridden. Gives the number of days in the specified month
431         /// of the given year and era.
432         /// </summary>
433         /// <param name="year">An integer that gives the year.
434         /// </param>
435         /// <param name="month">An integer that gives the month, starting
436         /// with 1.</param>
437         /// <param name="era">An integer that gives the era of the specified
438         /// year.</param>
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.
445         /// </exception>
446         public override int GetDaysInMonth(int year, int month, int era)
447         {
448                 M_CheckYME(year, month, ref era);
449                 if (month <= 6) {
450                         return 31;
451                 } else if (month == 12 && !is_leap_year(year)) {
452                         return 29;
453                 } else {
454                         return 30;
455                 }
456         }
457
458         /// <summary>
459         /// Overridden. Gives the number of days of the specified
460         /// year of the given era. 
461         /// </summary>
462         /// <param name="year">An integer that specifies the year. 
463         /// </param>
464         /// <param name="era">An ineger that specifies the era.
465         /// </param>
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
471         /// allowed range.
472         /// </exception>
473         public override int GetDaysInYear(int year, int era)
474         {
475                 M_CheckYE(year, ref era);
476                 return is_leap_year(year) ? 366 : 365;
477         }
478                 
479
480         /// <summary>
481         /// Overridden. Gives the era of the specified date.
482         /// </summary>
483         /// <param name="time">The
484         /// <see cref="T:System.DateTime"/> that specifies a
485         /// date.
486         /// </param>
487         /// <returns>An integer representing the era of the calendar.
488         /// </returns>
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.
493         /// </exception>
494         public override int GetEra(DateTime time)
495         {
496                 M_CheckDateTime(time);
497                 return PersianEra;
498         }
499
500         public override int GetLeapMonth (int year, int era)
501         {
502                 return 0;
503         }
504
505         /// <summary>
506         /// Overridden. Gives the number of the month of the specified
507         /// date.
508         /// </summary>
509         /// <param name="time">The
510         /// <see cref="T:System.DateTime"/> that specifies a
511         /// date.
512         /// </param>
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.
519         /// </exception>
520         public override int GetMonth(DateTime time)
521         {
522                 M_CheckDateTime(time);
523                 int rd = CCFixed.FromDateTime(time);
524                 int month, year;
525                 my_from_fixed(out month, out year, rd);
526                 return month;
527         }
528
529         /// <summary>
530         /// Overridden. Gives the number of months in the specified year 
531         /// and era.
532         /// </summary>
533         /// <param name="year">An integer that specifies the year.
534         /// </param>
535         /// <param name="era">An integer that specifies the era.
536         /// </param>
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.
541         /// </exception>
542         public override int GetMonthsInYear(int year, int era)
543         {
544                 M_CheckYE(year, ref era);
545                 return 12;
546         }
547
548         /// <summary>
549         /// Overridden. Gives the number of the year of the specified
550         /// date.
551         /// </summary>
552         /// <param name="time">The
553         /// <see cref="T:System.DateTime"/> that specifies a
554         /// date.
555         /// </param>
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.
562         /// </exception>
563         public override int GetYear(DateTime time)
564         {
565                 M_CheckDateTime(time);
566                 int rd = CCFixed.FromDateTime(time);
567                 return year_from_fixed(rd);
568         }
569
570         /// <summary>
571         /// Overridden. Tells whether the given day 
572         /// is a leap day.
573         /// </summary>
574         /// <param name="year">An integer that specifies the year in the
575         /// given era.
576         /// </param>
577         /// <param name="month">An integer that specifies the month.
578         /// </param>
579         /// <param name="day">An integer that specifies the day.
580         /// </param>
581         /// <param name="era">An integer that specifies the era.
582         /// </param>
583         /// <returns>A boolean that tells whether the given day is a leap
584         /// day.
585         /// </returns>
586         /// <exception cref="T:System.ArgumentOutOfRangeException">
587         /// The exception is thrown, if the year, month, day, or era is not
588         /// valid.
589         /// </exception>
590         public override bool IsLeapDay(int year, int month, int day,
591                                        int era)
592         {
593                 M_CheckYMDE(year, month, day, ref era);
594                 return is_leap_year(year) && month == 12 && day == 30;
595         }
596
597         /// <summary>
598         /// Overridden. Tells whether the given month 
599         /// is a leap month.
600         /// </summary>
601         /// <param name="year">An integer that specifies the year in the
602         /// given era.
603         /// </param>
604         /// <param name="month">An integer that specifies the month.
605         /// </param>
606         /// <param name="era">An integer that specifies the era.
607         /// </param>
608         /// <returns>A boolean that tells whether the given month is a leap
609         /// month.
610         /// </returns>
611         /// <exception cref="T:System.ArgumentOutOfRangeException">
612         /// The exception is thrown, if the year, month, or era is not
613         /// valid.
614         /// </exception>
615         public override bool IsLeapMonth(int year, int month, int era)
616         {
617                 M_CheckYME(year, month, ref era);
618                 return false;
619         }
620
621         /// <summary>
622         /// Overridden. Tells whether the given year
623         /// is a leap year.
624         /// </summary>
625         /// <param name="year">An integer that specifies the year in the
626         /// given era.
627         /// </param>
628         /// <param name="era">An integer that specifies the era.
629         /// </param>
630         /// <returns>A boolean that tells whether the given year is a leap
631         /// year.
632         /// </returns>
633         /// <exception cref="T:System.ArgumentOutOfRangeException">
634         /// The exception is thrown, if the year or era is not
635         /// valid.
636         /// </exception>
637         public override bool IsLeapYear(int year, int era)
638         {
639                 M_CheckYE(year, ref era);
640                 return is_leap_year(year);
641         }
642
643         /// <summary>
644         /// Overridden. Creates the
645         /// <see cref="T:System.DateTime"/> from the parameters.
646         /// </summary>
647         /// <param name="year">An integer that gives the year in the
648         /// <paramref name="era"/>.
649         /// </param>
650         /// <param name="month">An integer that specifies the month.
651         /// </param>
652         /// <param name="day">An integer that specifies the day.
653         /// </param>
654         /// <param name="hour">An integer that specifies the hour.
655         /// </param>
656         /// <param name="minute">An integer that specifies the minute.
657         /// </param>
658         /// <param name="second">An integer that gives the second.
659         /// </param>
660         /// <param name="milliseconds">An integer that gives the
661         /// milliseconds.
662         /// </param>
663         /// <param name="era">An integer that specifies the era.
664         /// </param>
665         /// <returns>A
666         /// <see cref="T:system.DateTime"/> representig the date and time.
667         /// </returns>
668         /// <exception cref="T:System.ArgumentOutOfRangeException">
669         /// The exception is thrown, if at least one of the parameters
670         /// is out of range.
671         /// </exception>
672         public override DateTime ToDateTime(int year, int month, int day,
673                                             int hour, int minute,
674                                             int second, int millisecond,
675                                             int era)
676         {
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);
682         }
683
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)
688         {
689                 M_ArgumentInRange ("year", year, 0, 99);
690                 
691                 int baseExtra = this.twoDigitYearMax % 100;
692                 int baseCentury = this.twoDigitYearMax - baseExtra;
693                 
694                 if (year <= baseExtra)
695                         return baseCentury + year;
696                 else
697                         return baseCentury + year - 100;
698         }
699
700         public override CalendarAlgorithmType AlgorithmType {
701                 get {
702                         return CalendarAlgorithmType.SolarCalendar;
703                 }
704         }
705
706         static DateTime PersianMin = new DateTime (622, 3, 21, 0, 0, 0);
707         static DateTime PersianMax = new DateTime (9999, 12, 31, 11, 59, 59);
708                 
709         public override DateTime MinSupportedDateTime {
710                 get {
711                         return PersianMin;
712                 }
713         }
714
715         public override DateTime MaxSupportedDateTime {
716                 get {
717                         return PersianMax;
718                 }
719         }
720 } // class PersianCalendar
721         
722 } // namespace System.Globalization