* roottypes.cs: Rename from tree.cs.
[mono.git] / mcs / class / corlib / System.Globalization / Calendar.cs
1 // Calendar.cs
2 //
3 // (C) Ulrich Kunitz 2002
4 //
5
6 //
7 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29 namespace System.Globalization {
30
31 using System;
32 using System.IO;
33
34 /// <remarks>
35 /// The class serves as a base class for calendar classes.
36 /// </remarks>
37 [Serializable]
38 public abstract class Calendar
39 #if NET_2_0
40         : ICloneable
41 #endif
42 {
43         /// <value>An protected integer property that gives the number of
44         /// days in a week. It might be overridden.</value>
45         internal virtual int M_DaysInWeek
46         {
47                 get { return 7; }
48         }
49
50         /// <summary>
51         /// The protected method creates the string used in the
52         /// <see cref="T:System.ArgumentOutOfRangeException"/>
53         /// </summary>
54         /// <param name="a">An object that represents the smallest 
55         /// allowable value.</param>
56         /// <param name="b">An object that represents the greatest allowable
57         /// value.</param>
58         /// <returns>The string used in the
59         /// <see cref="T:System.ArgumentOutOfRangeException"/>
60         /// </returns>
61         internal string M_ValidValues(object a, object b)
62         {
63                 StringWriter sw = new StringWriter();
64                 sw.Write("Valid values are between {0} and {1}, inclusive.",
65                         a, b);
66                 return sw.ToString();
67         }
68
69         /// <summary>
70         /// The protected method checks wether the parameter
71         /// <paramref name="arg"/> is in the allowed range.
72         /// </summary>
73         /// <param name="param">A string that gives the name of the
74         /// parameter to check.</param>
75         /// <param name="arg">An integer that gives the value to check.
76         /// </param>
77         /// <param name="a">An integer that represents the smallest allowed
78         /// value.</param>
79         /// <param name="b">An integer that represents the greatest allowed
80         /// value.</param>
81         /// <exception cref="T:System.ArgumentOutOfRangeException">
82         /// The exception is thrown, if the <paramref name="arg"/> is outside
83         /// the allowed range.
84         /// </exception>
85         internal void M_ArgumentInRange(string param, int arg, int a, int b)
86         {
87                 if (a <= arg && arg <= b)
88                         return;
89                 throw new ArgumentOutOfRangeException(param, M_ValidValues(a, b));
90         }
91
92         /// <summary>
93         /// The protected method, that checks whether
94         /// <paramref name="hour"/>, <paramref name="minute"/>,
95         /// <paramref name="second"/>, and <parameref name="millisecond"/>
96         /// are in their valid ranges
97         /// </summary>
98         /// <param name="hour">An integer that represents a hour, 
99         /// should be between 0 and 23.</param>
100         /// <param name="minute">An integer that represents a minute,
101         /// should be between 0 and 59.</param>
102         /// <param name="second">An integer that represents a second,
103         /// should be between 0 and 59.</param>
104         /// <param name="milliseconds">An integer that represents a number
105         /// of milliseconds, should be between 0 and 999999.</param>
106         /// <exception cref="T:System.ArgumentOutOfRangeException">
107         /// The Exception is thrown, if one of the parameter is outside the
108         /// allowed the range.
109         /// </exception>
110         internal void M_CheckHMSM(int hour, int minute, int second,
111                 int milliseconds)
112         {
113                 M_ArgumentInRange("hour", hour, 0, 23);
114                 M_ArgumentInRange("minute", minute, 0, 59);
115                 M_ArgumentInRange("second", second, 0, 59);
116                 M_ArgumentInRange("milliseconds", milliseconds, 0, 999999);
117         }
118
119         /// <value>
120         /// A represantation of the CurrentEra.
121         /// </value>
122         public const int CurrentEra = 0;
123
124         /// <value>When overridden gives the eras supported by the
125         /// calendar as an array of integers.
126         /// </value>
127         public abstract int[] Eras { get; }
128
129         [NonSerialized]
130         bool is_readonly;
131
132 #if NET_2_0
133         [System.Runtime.InteropServices.ComVisible(false)]
134         public virtual CalendarAlgorithmType AlgorithmType {
135                 get {
136                         return CalendarAlgorithmType.Unknown;
137                 }
138         }
139
140         [System.Runtime.InteropServices.ComVisible(false)]
141         public virtual DateTime MaxSupportedDateTime {
142                 get {
143                         return DateTime.MaxValue;
144                 }
145         }
146
147         [System.Runtime.InteropServices.ComVisible(false)]
148         public virtual DateTime MinSupportedDateTime {
149                 get {
150                         return DateTime.MinValue;
151                 }
152         }
153
154         // LAMESPEC: huh, why not Calendar but Object?
155         public virtual object Clone ()
156         {
157                 Calendar c = (Calendar) MemberwiseClone ();
158                 c.is_readonly = false;
159                 return c;
160         }
161 #endif
162
163 #if NET_2_0
164         public bool IsReadOnly {
165                 get { return is_readonly; }
166         }
167
168         public static Calendar ReadOnly (Calendar source)
169         {
170                 if (source.is_readonly)
171                         return source;
172                 Calendar c = (Calendar) source.Clone ();
173                 c.is_readonly = true;
174                 return c;
175         }
176 #else
177         internal bool IsReadOnly {
178                 get { return false; }
179         }
180
181         internal static Calendar ReadOnly (Calendar source)
182         {
183                 return source;
184         }
185 #endif
186
187         internal void CheckReadOnly ()
188         {
189                 if (is_readonly)
190                         throw new InvalidOperationException ("This Calendar is read-only.");
191         }
192
193         /// <summary>
194         /// The protected member stores the value for the
195         /// <see cref="P:TwoDigitYearMax"/>
196         /// property.
197         /// </summary>
198         [NonSerialized]
199         internal int M_TwoDigitYearMax;
200         
201
202         /// <summary>
203         /// Private field containing the maximum year for the calendar.
204         /// </summary>
205         [NonSerialized]
206         private int M_MaxYearValue = 0;
207
208         /// <value>
209         /// Get-only property returing the maximum allowed year for this
210         /// class.
211         /// </value>
212         internal virtual int M_MaxYear {
213                 get {
214                         if (M_MaxYearValue == 0) {
215                                 M_MaxYearValue = GetYear(DateTime.MaxValue);
216                         }
217                         return M_MaxYearValue;
218                 }
219         }
220
221         /// <summary>
222         /// Checks whether the year is the era is valid, if era = CurrentEra
223         /// the right value is set.
224         /// </summary>
225         /// <param name="year">The year to check.</param>
226         /// <param name="era">The era to check.</Param>
227         /// <exception cref="T:ArgumentOutOfRangeException">
228         /// The exception will be thrown, if the year is not valid.
229         /// </exception>
230         internal abstract void M_CheckYE(int year, ref int era);
231
232         /// <value>
233         /// <para>The property gives the maximum value for years with two
234         /// digits. If the property has the value 2029, than the two-digit
235         /// integer 29 results in the year 2029 and 30 in the 
236         /// year 1930.</para>
237         /// <para>It might be overridden.</para>
238         /// </value>
239         public virtual int TwoDigitYearMax {
240                 get { return M_TwoDigitYearMax; }
241                 set {
242                         CheckReadOnly ();
243                         M_ArgumentInRange("year", value, 100, M_MaxYear);
244                         int era = CurrentEra;
245                         M_CheckYE(value, ref era);
246                         M_TwoDigitYearMax = value;
247                 }
248         }
249
250         /// <summary>
251         /// The virtual method adds days to a given date.
252         /// </summary>
253         /// <param name="time">The
254         /// <see cref="T:System.DateTime"/> to which to add
255         /// days.
256         /// </param>
257         /// <param name="days">The number of days to add.</param>
258         /// <returns>A new <see cref="T:System.DateTime"/> value, that
259         /// results from adding <paramref name="days"/> to the specified
260         /// DateTime.</returns>
261         public virtual DateTime AddDays(DateTime time, int days) {
262                 return time.Add(TimeSpan.FromDays(days));
263         }
264
265         /// <summary>
266         /// The virtual method adds hours to a given date.
267         /// </summary>
268         /// <param name="time">The
269         /// <see cref="T:System.DateTime"/> to which to add
270         /// hours.
271         /// </param>
272         /// <param name="hours">The number of hours to add.</param>
273         /// <returns>A new <see cref="T:System.DateTime"/> value, that
274         /// results from adding <paramref name="hours"/> to the specified
275         /// DateTime.</returns>
276         public virtual DateTime AddHours(DateTime time, int hours) {
277                 return time.Add(TimeSpan.FromHours(hours));
278         }
279
280         /// <summary>
281         /// The virtual method adds milliseconds to a given date.
282         /// </summary>
283         /// <param name="time">The
284         /// <see cref="T:System.DateTime"/> to which to add
285         /// milliseconds.
286         /// </param>
287         /// <param name="milliseconds">The number of milliseconds given as
288         /// double to add. Keep in mind the 100 nanosecond resolution of 
289         /// <see cref="T:System.DateTime"/>.
290         /// </param>
291         /// <returns>A new <see cref="T:System.DateTime"/> value, that
292         /// results from adding <paramref name="milliseconds"/> to the specified
293         /// DateTime.</returns>
294         public virtual DateTime AddMilliseconds(DateTime time,
295                 double milliseconds)
296         {
297                 return time.Add(TimeSpan.FromMilliseconds(milliseconds));
298         }
299
300         /// <summary>
301         /// The virtual method adds minutes to a given date.
302         /// </summary>
303         /// <param name="time">The
304         /// <see cref="T:System.DateTime"/> to which to add
305         /// minutes.
306         /// </param>
307         /// <param name="minutes">The number of minutes to add.</param>
308         /// <returns>A new <see cref="T:System.DateTime"/> value, that
309         /// results from adding <paramref name="minutes"/> to the specified
310         /// DateTime.</returns>
311         public virtual DateTime AddMinutes(DateTime time, int minutes) {
312                 return time.Add(TimeSpan.FromMinutes(minutes));
313         }
314
315         /// <summary>
316         /// When overrideden adds months to a given date.
317         /// </summary>
318         /// <param name="time">The
319         /// <see cref="T:System.DateTime"/> to which to add
320         /// months.
321         /// </param>
322         /// <param name="months">The number of months to add.</param>
323         /// <returns>A new <see cref="T:System.DateTime"/> value, that
324         /// results from adding <paramref name="months"/> to the specified
325         /// DateTime.</returns>
326         public abstract DateTime AddMonths(DateTime time, int months);
327
328         /// <summary>
329         /// The virtual method adds seconds to a given date.
330         /// </summary>
331         /// <param name="time">The
332         /// <see cref="T:System.DateTime"/> to which to add
333         /// seconds.
334         /// </param>
335         /// <param name="seconds">The number of seconds to add.</param>
336         /// <returns>A new <see cref="T:System.DateTime"/> value, that
337         /// results from adding <paramref name="seconds"/> to the specified
338         /// DateTime.</returns>
339         public virtual DateTime AddSeconds(DateTime time, int seconds) {
340                 return time.Add(TimeSpan.FromSeconds(seconds));
341         }
342
343         /// <summary>
344         /// A wirtual method that adds weeks to a given date.
345         /// </summary>
346         /// <param name="time">The
347         /// <see cref="T:System.DateTime"/> to which to add
348         /// weeks.
349         /// </param>
350         /// <param name="weeks">The number of weeks to add.</param>
351         /// <returns>A new <see cref="T:System.DateTime"/> value, that
352         /// results from adding <paramref name="weeks"/> to the specified
353         /// DateTime.</returns>
354         public virtual DateTime AddWeeks(DateTime time, int weeks) {
355                 return time.AddDays(weeks * M_DaysInWeek);
356         }
357
358         /// <summary>
359         /// When overrideden adds years to a given date.
360         /// </summary>
361         /// <param name="time">The
362         /// <see cref="T:System.DateTime"/> to which to add
363         /// years.
364         /// </param>
365         /// <param name="years">The number of years to add.</param>
366         /// <returns>A new <see cref="T:System.DateTime"/> value, that
367         /// results from adding <paramref name="years"/> to the specified
368         /// DateTime.</returns>
369         public abstract DateTime AddYears(DateTime time, int years);
370
371         /// <summary>
372         /// When overriden gets the day of the month from
373         /// <paramref name="time"/>.
374         /// </summary>
375         /// <param name="time">The
376         /// <see cref="T:System.DateTime"/> that specifies a
377         /// date.
378         /// </param>
379         /// <returns>An integer giving the day of months, starting with 1.
380         /// </returns>
381         public abstract int GetDayOfMonth(DateTime time);
382
383         /// <summary>
384         /// When overriden gets the day of the week from the specified date.
385         /// </summary>
386         /// <param name="time">The
387         /// <see cref="T:System.DateTime"/> that specifies a
388         /// date.
389         /// </param>
390         /// <returns>An integer giving the day of months, starting with 1.
391         /// </returns>
392         public abstract DayOfWeek GetDayOfWeek(DateTime time);
393
394         /// <summary>
395         /// When overridden gives the number of the day in the year.
396         /// </summary>
397         /// <param name="time">The
398         /// <see cref="T:System.DateTime"/> that specifies a
399         /// date.
400         /// </param>
401         /// <returns>An integer representing the day of the year,
402         /// starting with 1.</returns>
403         public abstract int GetDayOfYear(DateTime time);
404
405         /// <summary>
406         /// A virtual method that gives the number of days of the specified
407         /// month of the <paramref name="year"/> and the
408         /// <see cref="P:CurrentEra"/>.
409         /// </summary>
410         /// <param name="year">An integer that gives the year in the current
411         /// era.</param>
412         /// <param name="month">An integer that gives the month, starting
413         /// with 1.</param>
414         /// <returns>An integer that gives the number of days of the
415         /// specified month.</returns>
416         /// <exception cref="T:System.ArgumentOutOfRangeException">
417         /// The exception is thrown, if <paramref name="month"/> or
418         /// <paramref name="year"/> is outside the allowed range.
419         /// </exception>
420         public virtual int GetDaysInMonth(int year, int month) {
421                 return GetDaysInMonth(year, month, CurrentEra);
422         }
423
424         /// <summary>
425         /// When overridden gives the number of days in the specified month
426         /// of the given year and era.
427         /// </summary>
428         /// <param name="year">An integer that gives the year.
429         /// </param>
430         /// <param name="month">An integer that gives the month, starting
431         /// with 1.</param>
432         /// <param name="era">An intger that gives the era of the specified
433         /// year.</param>
434         /// <returns>An integer that gives the number of days of the
435         /// specified month.</returns>
436         /// <exception cref="T:System.ArgumentOutOfRangeException">
437         /// The exception is thrown, if <paramref name="month"/>,
438         /// <paramref name="year"/> ,or <paramref name="era"/> is outside
439         /// the allowed range.
440         /// </exception>
441         public abstract int GetDaysInMonth(int year, int month, int era);
442
443         /// <summary>
444         /// A virtual method that gives the number of days of the specified
445         /// year of the <see cref="P:CurrentEra"/>.
446         /// </summary>
447         /// <param name="year">An integer that gives the year in the current
448         /// era.</param>
449         /// <returns>An integer that gives the number of days of the
450         /// specified year.</returns>
451         /// <exception cref="T:System.ArgumentOutOfRangeException">
452         /// The exception is thrown, if
453         /// <paramref name="year"/> is outside the allowed range.
454         /// </exception>
455         public virtual int GetDaysInYear(int year) {
456                 return GetDaysInYear(year, CurrentEra);
457         }
458
459         /// <summary>
460         /// When overridden gives the number of days of the specified
461         /// year of the given era.. 
462         /// </summary>
463         /// <param name="year">An integer that specifies the year. 
464         /// </param>
465         /// <param name="era">An ineger that specifies the era.
466         /// </param>
467         /// <returns>An integer that gives the number of days of the
468         /// specified year.</returns>
469         /// <exception cref="T:System.ArgumentOutOfRangeExceiption">
470         /// The exception is thrown, if
471         /// <paramref name="year"/> is outside the allowed range.
472         /// </exception>
473         public abstract int GetDaysInYear(int year, int era);
474
475         /// <summary>
476         /// When overridden gives the era of the specified date.
477         /// </summary>
478         /// <param name="time">The
479         /// <see cref="T:System.DateTime"/> that specifies a
480         /// date.
481         /// </param>
482         /// <returns>An integer representing the era of the calendar.
483         /// </returns>
484         public abstract int GetEra(DateTime time);
485
486         /// <summary>
487         /// Virtual method that gives the hour of the specified time.
488         /// </summary>
489         /// <param name="time">The
490         /// <see cref="T:System.DateTime"/> that specifies the
491         /// time.
492         /// </param>
493         /// <returns>An integer that gives the hour of the specified time,
494         /// starting with 0.</returns>
495         public virtual int GetHour(DateTime time) {
496                 return time.TimeOfDay.Hours;
497         }
498
499         /// <summary>
500         /// Virtual method that gives the milliseconds in the current second
501         /// of the specified time.
502         /// </summary>
503         /// <param name="time">The
504         /// <see cref="T:System.DateTime"/> that specifies the
505         /// time.
506         /// </param>
507         /// <returns>An integer that gives the milliseconds in the seconds
508         /// of the specified time, starting with 0.</returns>
509         public virtual double GetMilliseconds(DateTime time) {
510                 return time.TimeOfDay.Milliseconds;
511         }
512
513         /// <summary>
514         /// Virtual method that gives the minute of the specified time.
515         /// </summary>
516         /// <param name="time">The
517         /// <see cref="T:System.DateTime"/> that specifies the
518         /// time.
519         /// </param>
520         /// <returns>An integer that gives the minute of the specified time,
521         /// starting with 0.</returns>
522         public virtual int GetMinute(DateTime time) {
523                 return time.TimeOfDay.Minutes;
524         }
525
526         /// <summary>
527         /// When overridden gives the number of the month of the specified
528         /// date.
529         /// </summary>
530         /// <param name="time">The
531         /// <see cref="T:System.DateTime"/> that specifies a
532         /// date.
533         /// </param>
534         /// <returns>An integer representing the month, 
535         /// starting with 1.</returns>
536         public abstract int GetMonth(DateTime time);
537
538         /// <summary>
539         /// Virtual method that gives the number of months of the specified
540         /// year of the <see cref="M:CurrentEra"/>.
541         /// </summary>
542         /// <param name="year">An integer that specifies the year in the
543         /// current era.
544         /// </param>
545         /// <returns>An integer that gives the number of the months in the
546         /// specified year.</returns>
547         /// <exception cref="T:System.ArgumentOutOfRangeException">
548         /// The exception is thrown, if the year is not allowed in the
549         /// current era.
550         /// </exception>
551         public virtual int GetMonthsInYear(int year) {
552                 return GetMonthsInYear(year, CurrentEra);
553         }
554
555         /// <summary>
556         /// When overridden gives the number of months in the specified year 
557         /// and era.
558         /// </summary>
559         /// <param name="year">An integer that specifies the year.
560         /// </param>
561         /// <param name="era">An integer that specifies the era.
562         /// </param>
563         /// <returns>An integer that gives the number of the months in the
564         /// specified year.</returns>
565         /// <exception cref="T:System.ArgumentOutOfRangeException">
566         /// The exception is thrown, if the year or the era are not valid.
567         /// </exception>
568         public abstract int GetMonthsInYear(int year, int era);
569
570         /// <summary>
571         /// Virtual method that gives the second of the specified time.
572         /// </summary>
573         /// <param name="time">The
574         /// <see cref="T:System.DateTime"/> that specifies the
575         /// time.
576         /// </param>
577         /// <returns>An integer that gives the second of the specified time,
578         /// starting with 0.</returns>
579         public virtual int GetSecond(DateTime time) {
580                 return time.TimeOfDay.Seconds;
581         }
582
583         /// <summary>
584         /// A protected method to calculate the number of days between two
585         /// dates.
586         /// </summary>
587         /// <param name="timeA">A <see cref="T:System.DateTime"/>
588         /// representing the first date.
589         /// </param>
590         /// <param name="timeB">A <see cref="T:System.DateTime"/>
591         /// representing the second date.
592         /// </param>
593         /// <returns>An integer that represents the difference of days
594         /// between <paramref name="timeA"/> and <paramref name="timeB"/>.
595         /// </returns>
596         internal int M_DiffDays(DateTime timeA, DateTime timeB) {
597                 long diff = timeA.Ticks - timeB.Ticks;
598
599                 if (diff >= 0) {
600                         return (int)(diff/TimeSpan.TicksPerDay);
601                 }
602
603                 diff += 1;
604                 return -1 + (int)(diff/TimeSpan.TicksPerDay);
605         }
606
607         /// <summary>
608         /// A protected method that gives the first day of the second week of
609         /// the year.
610         /// </summary>
611         /// <param name="year">An integer that represents the year.</param>
612         /// <param name="rule">The
613         /// <see cref="T:System.Globalization.CalendarWeekRule"/>
614         /// to be used for the calculation.
615         /// </param>
616         /// <param name="firstDayOfWeek">
617         /// The <see cref="T:System.Globalization.DayOfWeek"/>
618         /// specifying the first day in a week.
619         /// </param>
620         /// <returns>The <see cref="T:System.DateTime"/> representing 
621         /// the first day of the second week of the year.
622         /// </returns>
623         internal DateTime M_GetFirstDayOfSecondWeekOfYear(
624                 int year, CalendarWeekRule rule, DayOfWeek firstDayOfWeek)
625         {
626                 DateTime d1 = ToDateTime(year, 1, 1, 0, 0, 0, 0);
627                 int dow1 = (int)GetDayOfWeek(d1);
628                 int fdow = (int)firstDayOfWeek;
629                 int d = 0;
630
631                 switch (rule) {
632                 case CalendarWeekRule.FirstDay:
633                         if (fdow > dow1) {
634                                 d += fdow - dow1;
635                         }
636                         else {
637                                 d += fdow + M_DaysInWeek - dow1;
638                         }
639                         break;
640                 case CalendarWeekRule.FirstFullWeek:
641                         d = M_DaysInWeek;
642                         if (fdow >= dow1) {
643                                 d += fdow - dow1;
644                         }
645                         else {
646                                 d += fdow + M_DaysInWeek - dow1;
647                         }
648                         break;
649                 case CalendarWeekRule.FirstFourDayWeek:
650                         int dow4 = (dow1 + 3)%M_DaysInWeek;
651
652                         d = 3;
653                         if (fdow > dow4) {
654                                 d += fdow - dow4;
655                         }
656                         else {
657                                 d += fdow + M_DaysInWeek - dow4;
658                         }
659                         break;
660                 }
661
662                 return AddDays(d1, d);
663         }
664
665         /// <summary>
666         /// A virtual method that gives the number of the week in the year.
667         /// </summary>
668         /// <param name="time">A 
669         /// <see cref="T:System.DateTime"/> representing the date.
670         /// </param>
671         /// <param name="rule">The
672         /// <see cref="T:System.Globalization.CalendarWeekRule"/>
673         /// to be used for the calculation.
674         /// </param>
675         /// <param name="firstDayOfWeek">
676         /// The <see cref="T:System.Globalization.DayOfWeek"/>
677         /// specifying the first day in a week.
678         /// </param>
679         /// <returns>An integer representing the number of the week in the
680         /// year, starting with 1.
681         /// </returns>
682         public virtual int GetWeekOfYear(DateTime time,
683                 CalendarWeekRule rule, 
684                 DayOfWeek firstDayOfWeek)
685         {
686                 if (firstDayOfWeek < DayOfWeek.Sunday ||
687                     DayOfWeek.Saturday < firstDayOfWeek)
688                 {
689                         throw new ArgumentOutOfRangeException("firstDayOfWeek",
690                                 "Value is not a valid day of week.");
691                 }
692                 int year = GetYear(time);
693
694                 int days;
695
696                 while (true) {
697                         DateTime secondWeek = M_GetFirstDayOfSecondWeekOfYear(
698                                 year, rule, firstDayOfWeek);
699                         days = M_DiffDays(time, secondWeek) + M_DaysInWeek;
700                         if (days >= 0)
701                                 break;
702                         year -= 1;
703                 }
704
705                 return 1 + days/M_DaysInWeek;
706         }
707
708         /// <summary>
709         /// When overridden gives the number of the year of the specified
710         /// date.
711         /// </summary>
712         /// <param name="time">The
713         /// <see cref="T:System.DateTime"/> that specifies a
714         /// date.
715         /// </param>
716         /// <returns>An integer representing the year, 
717         /// starting with 1.</returns>
718         public abstract int GetYear(DateTime time);
719
720         /// <summary>
721         /// A virtual method that tells whether the given day in the
722         /// <see cref="M:CurrentEra"/> is a leap day.
723         /// </summary>
724         /// <param name="year">An integer that specifies the year in the
725         /// current era.
726         /// </param>
727         /// <param name="month">An integer that specifies the month.
728         /// </param>
729         /// <param name="day">An integer that specifies the day.
730         /// </param>
731         /// <returns>A boolean that tells whether the given day is a leap
732         /// day.
733         /// </returns>
734         /// <exception cref="T:System.ArgumentOutOfRangeException">
735         /// The exception is thrown, if the year, month or day is not valid
736         /// the current era.
737         /// </exception>
738         public virtual bool IsLeapDay(int year, int month, int day) {
739                 return IsLeapDay(year, month, day, CurrentEra);
740         }
741
742         /// <summary>
743         /// Tells when overridden whether the given day 
744         /// is a leap day.
745         /// </summary>
746         /// <param name="year">An integer that specifies the year in the
747         /// given era.
748         /// </param>
749         /// <param name="month">An integer that specifies the month.
750         /// </param>
751         /// <param name="day">An integer that specifies the day.
752         /// </param>
753         /// <param name="era">An integer that specifies the era.
754         /// </param>
755         /// <returns>A boolean that tells whether the given day is a leap
756         /// day.
757         /// </returns>
758         /// <exception cref="T:System.ArgumentOutOfRangeException">
759         /// The exception is thrown, if the year, month, day, or era is not
760         /// valid.
761         /// </exception>
762         public abstract bool IsLeapDay(int year, int month, int day, int era);
763
764         /// <summary>
765         /// A virtual method that tells whether the given month of the
766         /// specified year in the
767         /// <see cref="M:CurrentEra"/> is a leap month.
768         /// </summary>
769         /// <param name="year">An integer that specifies the year in the
770         /// current era.
771         /// </param>
772         /// <param name="month">An integer that specifies the month.
773         /// </param>
774         /// <returns>A boolean that tells whether the given month is a leap
775         /// month.
776         /// </returns>
777         /// <exception cref="T:System.ArgumentOutOfRangeException">
778         /// The exception is thrown, if the year or month is not valid
779         /// the current era.
780         /// </exception>
781         public virtual bool IsLeapMonth(int year, int month) {
782                 return IsLeapMonth(year, month, CurrentEra);
783         }
784
785         /// <summary>
786         /// Tells when overridden whether the given month 
787         /// is a leap month.
788         /// </summary>
789         /// <param name="year">An integer that specifies the year in the
790         /// given era.
791         /// </param>
792         /// <param name="month">An integer that specifies the month.
793         /// </param>
794         /// <param name="era">An integer that specifies the era.
795         /// </param>
796         /// <returns>A boolean that tells whether the given month is a leap
797         /// month.
798         /// </returns>
799         /// <exception cref="T:System.ArgumentOutOfRangeException">
800         /// The exception is thrown, if the year, month, or era is not
801         /// valid.
802         /// </exception>
803         public abstract bool IsLeapMonth(int year, int month, int era);
804
805         /// <summary>
806         /// A virtual method that tells whether the given year
807         /// in the
808         /// <see cref="M:CurrentEra"/> is a leap year.
809         /// </summary>
810         /// <param name="year">An integer that specifies the year in the
811         /// current era.
812         /// </param>
813         /// <returns>A boolean that tells whether the given year is a leap
814         /// year.
815         /// </returns>
816         /// <exception cref="T:System.ArgumentOutOfRangeException">
817         /// The exception is thrown, if the year is not valid
818         /// the current era.
819         /// </exception>
820         public virtual bool IsLeapYear(int year) {
821                 return IsLeapYear(year, CurrentEra);
822         }
823
824         /// <summary>
825         /// Tells when overridden whether the given year
826         /// is a leap year.
827         /// </summary>
828         /// <param name="year">An integer that specifies the year in the
829         /// given era.
830         /// </param>
831         /// <param name="era">An integer that specifies the era.
832         /// </param>
833         /// <returns>A boolean that tells whether the given year is a leap
834         /// year.
835         /// </returns>
836         /// <exception cref="T:System.ArgumentOutOfRangeException">
837         /// The exception is thrown, if the year or era is not
838         /// valid.
839         /// </exception>
840         public abstract bool IsLeapYear(int year, int era);
841
842         /// <summary>
843         /// A virtual method that creates the
844         /// <see cref="T:System.DateTime"/> from the parameters.
845         /// </summary>
846         /// <param name="year">An integer that gives the year in the
847         /// <see cref="M:CurrentEra"/>.
848         /// </param>
849         /// <param name="month">An integer that specifies the month.
850         /// </param>
851         /// <param name="day">An integer that specifies the day.
852         /// </param>
853         /// <param name="hour">An integer that specifies the hour.
854         /// </param>
855         /// <param name="minute">An integer that specifies the minute.
856         /// </param>
857         /// <param name="second">An integer that gives the second.
858         /// </param>
859         /// <param name="milliseconds">An integer that gives the
860         /// milliseconds.
861         /// </param>
862         /// <returns>A
863         /// <see cref="T:system.DateTime"/> representig the date and time.
864         /// </returns>
865         /// <exception cref="T:System.ArgumentOutOfRangeException">
866         /// The exception is thrown, if at least one of the parameters
867         /// is out of range.
868         /// </exception>
869         public virtual DateTime ToDateTime(int year, int month, int day,
870                 int hour, int minute, int second, int milliseconds)
871         {
872                 return ToDateTime(year, month, day, hour, minute, second, 
873                         milliseconds, CurrentEra);
874         }
875
876
877         /// <summary>
878         /// When overridden creates the
879         /// <see cref="T:System.DateTime"/> from the parameters.
880         /// </summary>
881         /// <param name="year">An integer that gives the year in the
882         /// <paramref name="era"/>.
883         /// </param>
884         /// <param name="month">An integer that specifies the month.
885         /// </param>
886         /// <param name="day">An integer that specifies the day.
887         /// </param>
888         /// <param name="hour">An integer that specifies the hour.
889         /// </param>
890         /// <param name="minute">An integer that specifies the minute.
891         /// </param>
892         /// <param name="second">An integer that gives the second.
893         /// </param>
894         /// <param name="milliseconds">An integer that gives the
895         /// milliseconds.
896         /// </param>
897         /// <param name="era">An integer that specifies the era.
898         /// </param>
899         /// <returns>A
900         /// <see cref="T:system.DateTime"/> representig the date and time.
901         /// </returns>
902         /// <exception cref="T:System.ArgumentOutOfRangeException">
903         /// The exception is thrown, if at least one of the parameters
904         /// is out of range.
905         /// </exception>
906         public abstract DateTime ToDateTime(int year, int month, int day,
907                 int hour, int minute, int second, int milliseconds,
908                 int era);
909
910         /// <summary>
911         /// A virtual method that converts a two-digit year to a four-digit
912         /// year. It uses the <see cref="M:TwoDigitYearMax"/> property.
913         /// </summary>
914         /// <param name="year">An integer that gives the two-digit year.
915         /// </param>
916         /// <returns>An integer giving the four digit year.
917         /// </returns>
918         /// <exception cref="T:System.ArgumentOutOfRangeException">
919         /// The exception is thrown if the year is negative or the resulting 
920         /// year is invalid.
921         /// </exception>
922         public virtual int ToFourDigitYear(int year) {
923                 if (year < 0)
924                         throw new ArgumentOutOfRangeException(
925                                 "year", "Non-negative number required.");
926                 /* seems not to be the right thing to do, but .NET is
927                  * doing it this way.
928                  */
929                 if (year <= 99) {
930                         int year2 = TwoDigitYearMax%100;
931                         int d = year - year2;
932                         year = TwoDigitYearMax + d + (d <= 0 ? 0 : -100); 
933                 }
934                 int era = CurrentEra;
935                 M_CheckYE(year, ref era);
936                 return year;
937         }
938
939         // TwoDigitYearMax: Windows reads it from the Registry, we
940         // should have an XML file with the defaults
941         /// <summary>
942         /// The default constructor, is sets the TwoDigitYearMax to 2029.
943         /// </summary>
944         /// <remarks>
945         /// The .NET framework reads the value from the registry.
946         /// We should implement it here. Currently I set the default values
947         /// in the ctors of the derived classes, if it is 99.
948         /// </remarks>
949         protected Calendar() {
950                 M_TwoDigitYearMax = 99;
951         }
952
953         /// <summary>Protected field storing the abbreviated era names.
954         /// </summary>
955         [NonSerialized]
956         internal string[] M_AbbrEraNames;
957         /// <summary>Protected field storing the era names.
958         /// </summary>
959         [NonSerialized]
960         internal string[] M_EraNames;
961
962         /// <value>
963         /// The property stores the era names. It might be overwritten by
964         /// CultureInfo.
965         /// </value>
966         internal string[] AbbreviatedEraNames {
967                 get {
968                         if (M_AbbrEraNames == null ||
969                             M_AbbrEraNames.Length != Eras.Length)
970                                 throw new Exception(
971                                         "Internal: M_AbbrEraNames " +
972                                         "wrong initialized!");
973                         return (string[])M_AbbrEraNames.Clone();
974                 }
975                 set {
976                         CheckReadOnly ();
977                         if (value.Length != Eras.Length) {
978                                 StringWriter sw = new StringWriter();
979                                 sw.Write("Array length must be equal Eras " +
980                                         "length {0}.", Eras.Length);
981                                 throw new ArgumentException(
982                                         sw.ToString());
983                         } 
984                         M_AbbrEraNames = (string[])value.Clone();
985                 }
986         }
987
988         /// <value>
989         /// The property stores the era names. It might be overwritten by
990         /// CultureInfo.
991         /// </value>
992         internal string[] EraNames {
993                 get {
994                         if (M_EraNames == null ||
995                             M_EraNames.Length != Eras.Length)
996                                 throw new Exception(
997                                         "Internal: M_EraNames " +
998                                         "not initialized!");
999                         return (string[])M_EraNames.Clone();
1000                 }
1001                 set {
1002                         CheckReadOnly ();
1003                         if (value.Length != Eras.Length) {
1004                                 StringWriter sw = new StringWriter();
1005                                 sw.Write("Array length must be equal Eras " +
1006                                         "length {0}.", Eras.Length);
1007                                 throw new ArgumentException(
1008                                         sw.ToString());
1009                         } 
1010                         M_EraNames = (string[])value.Clone();
1011                 }
1012         }
1013 } // class Calendar
1014         
1015 } // namespace System.Globalization