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