Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / mscorlib / system / globalization / calendar.cs
1 // ==++==
2 //
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 //
5 // ==--==
6 namespace System.Globalization {
7     using System;
8     using System.Runtime.CompilerServices;
9     using System.Globalization;
10     using System.Runtime.Versioning;
11     using System.Diagnostics.Contracts;
12
13     // This abstract class represents a calendar. A calendar reckons time in
14     // divisions such as weeks, months and years. The number, length and start of
15     // the divisions vary in each calendar.
16     //
17     // Any instant in time can be represented as an n-tuple of numeric values using
18     // a particular calendar. For example, the next vernal equinox occurs at (0.0, 0
19     // , 46, 8, 20, 3, 1999) in the Gregorian calendar. An  implementation of
20     // Calendar can map any DateTime value to such an n-tuple and vice versa. The
21     // DateTimeFormat class can map between such n-tuples and a textual
22     // representation such as "8:46 AM [....] 20th 1999 AD".
23     //
24     // Most calendars identify a year which begins the current era. There may be any
25     // number of previous eras. The Calendar class identifies the eras as enumerated
26     // integers where the current era (CurrentEra) has the value zero.
27     //
28     // For consistency, the first unit in each interval, e.g. the first month, is
29     // assigned the value one.
30     // The calculation of hour/minute/second is moved to Calendar from GregorianCalendar,
31     // since most of the calendars (or all?) have the same way of calcuating hour/minute/second.
32
33     [Serializable]
34     [System.Runtime.InteropServices.ComVisible(true)]
35     public abstract class Calendar : ICloneable
36     {
37
38         // Number of 100ns (10E-7 second) ticks per time unit
39         internal const long TicksPerMillisecond   = 10000;
40         internal const long TicksPerSecond        = TicksPerMillisecond * 1000;
41         internal const long TicksPerMinute        = TicksPerSecond * 60;
42         internal const long TicksPerHour          = TicksPerMinute * 60;
43         internal const long TicksPerDay           = TicksPerHour * 24;
44
45         // Number of milliseconds per time unit
46         internal const int MillisPerSecond        = 1000;
47         internal const int MillisPerMinute        = MillisPerSecond * 60;
48         internal const int MillisPerHour          = MillisPerMinute * 60;
49         internal const int MillisPerDay           = MillisPerHour * 24;
50
51         // Number of days in a non-leap year
52         internal const int DaysPerYear            = 365;
53         // Number of days in 4 years
54         internal const int DaysPer4Years          = DaysPerYear * 4 + 1;
55         // Number of days in 100 years
56         internal const int DaysPer100Years        = DaysPer4Years * 25 - 1;
57         // Number of days in 400 years
58         internal const int DaysPer400Years        = DaysPer100Years * 4 + 1;
59
60         // Number of days from 1/1/0001 to 1/1/10000
61         internal const int DaysTo10000            = DaysPer400Years * 25 - 366;
62
63         internal const long MaxMillis             = (long)DaysTo10000 * MillisPerDay;
64
65         //
66         //  Calendar ID Values.  This is used to get data from calendar.nlp.
67         //  The order of calendar ID means the order of data items in the table.
68         //
69
70         internal const int CAL_GREGORIAN                  = 1 ;     // Gregorian (localized) calendar
71         internal const int CAL_GREGORIAN_US               = 2 ;     // Gregorian (U.S.) calendar
72         internal const int CAL_JAPAN                      = 3 ;     // Japanese Emperor Era calendar
73         internal const int CAL_TAIWAN                     = 4 ;     // Taiwan Era calendar
74         internal const int CAL_KOREA                      = 5 ;     // Korean Tangun Era calendar
75         internal const int CAL_HIJRI                      = 6 ;     // Hijri (Arabic Lunar) calendar
76         internal const int CAL_THAI                       = 7 ;     // Thai calendar
77         internal const int CAL_HEBREW                     = 8 ;     // Hebrew (Lunar) calendar
78         internal const int CAL_GREGORIAN_ME_FRENCH        = 9 ;     // Gregorian Middle East French calendar
79         internal const int CAL_GREGORIAN_ARABIC           = 10;     // Gregorian Arabic calendar
80         internal const int CAL_GREGORIAN_XLIT_ENGLISH     = 11;     // Gregorian Transliterated English calendar
81         internal const int CAL_GREGORIAN_XLIT_FRENCH      = 12;
82         internal const int CAL_JULIAN                     = 13;
83         internal const int CAL_JAPANESELUNISOLAR          = 14;
84         internal const int CAL_CHINESELUNISOLAR           = 15;
85         internal const int CAL_SAKA                       = 16;     // reserved to match Office but not implemented in our code
86         internal const int CAL_LUNAR_ETO_CHN              = 17;     // reserved to match Office but not implemented in our code
87         internal const int CAL_LUNAR_ETO_KOR              = 18;     // reserved to match Office but not implemented in our code
88         internal const int CAL_LUNAR_ETO_ROKUYOU          = 19;     // reserved to match Office but not implemented in our code
89         internal const int CAL_KOREANLUNISOLAR            = 20;
90         internal const int CAL_TAIWANLUNISOLAR            = 21;
91         internal const int CAL_PERSIAN                    = 22;
92         internal const int CAL_UMALQURA                   = 23;
93
94         internal int m_currentEraValue = -1;
95
96         [System.Runtime.Serialization.OptionalField(VersionAdded = 2)]
97         private bool m_isReadOnly = false;
98
99         // The minimum supported DateTime range for the calendar.
100
101         [System.Runtime.InteropServices.ComVisible(false)]
102         public virtual DateTime MinSupportedDateTime
103         {
104             get
105             {
106                 return (DateTime.MinValue);
107             }
108         }
109
110         // The maximum supported DateTime range for the calendar.
111
112         [System.Runtime.InteropServices.ComVisible(false)]
113         public virtual DateTime MaxSupportedDateTime
114         {
115             get
116             {
117                 return (DateTime.MaxValue);
118             }
119         }
120
121
122
123
124         protected Calendar() {
125             //Do-nothing constructor.
126         }
127
128         ///
129         // This can not be abstract, otherwise no one can create a subclass of Calendar.
130         //
131         internal virtual int ID {
132             get {
133                 return (-1);
134             }
135         }
136
137         ///
138         // Return the Base calendar ID for calendars that didn't have defined data in calendarData
139         //
140
141         internal virtual int BaseCalendarID
142         {
143             get { return ID; }
144         }
145
146         // Returns  the type of the calendar.
147         //
148         [System.Runtime.InteropServices.ComVisible(false)]
149         public virtual CalendarAlgorithmType AlgorithmType
150         {
151             get
152             {
153                 return CalendarAlgorithmType.Unknown;
154             }
155         }
156
157         ////////////////////////////////////////////////////////////////////////
158         //
159         //  IsReadOnly
160         //
161         //  Detect if the object is readonly.
162         //
163         ////////////////////////////////////////////////////////////////////////
164         [System.Runtime.InteropServices.ComVisible(false)]
165         public bool IsReadOnly
166         {
167             get { return (m_isReadOnly); }
168         }
169
170         ////////////////////////////////////////////////////////////////////////
171         //
172         //  Clone
173         //
174         //  Is the implementation of IColnable.
175         //
176         ////////////////////////////////////////////////////////////////////////
177         [System.Runtime.InteropServices.ComVisible(false)]
178         public virtual Object Clone()
179         {
180             object o = MemberwiseClone();
181             ((Calendar) o).SetReadOnlyState(false);
182             return (o);
183         }
184         
185         ////////////////////////////////////////////////////////////////////////
186         //
187         //  ReadOnly
188         //
189         //  Create a cloned readonly instance or return the input one if it is 
190         //  readonly.
191         //
192         ////////////////////////////////////////////////////////////////////////
193         [System.Runtime.InteropServices.ComVisible(false)]
194         public static Calendar ReadOnly(Calendar calendar) 
195         {
196             if (calendar == null)       { throw new ArgumentNullException("calendar"); }
197             Contract.EndContractBlock();
198             if (calendar.IsReadOnly)    { return (calendar); }
199             
200             Calendar clonedCalendar = (Calendar)(calendar.MemberwiseClone());
201             clonedCalendar.SetReadOnlyState(true);
202             
203             return (clonedCalendar);
204         }
205
206         internal void VerifyWritable()
207         {
208             if (m_isReadOnly)
209             {
210                 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ReadOnly"));
211             }
212         }
213
214         internal void SetReadOnlyState(bool readOnly)
215         {
216             m_isReadOnly = readOnly;
217         }
218
219
220         /*=================================CurrentEraValue==========================
221         **Action: This is used to convert CurretEra(0) to an appropriate era value.
222         **Returns:
223         **Arguments:
224         **Exceptions:
225         **Notes:
226         ** The value is from calendar.nlp.
227         ============================================================================*/
228
229         internal virtual int CurrentEraValue {
230             get {
231                 // The following code assumes that the current era value can not be -1.
232                 if (m_currentEraValue == -1) {
233                     Contract.Assert(BaseCalendarID > 0, "[Calendar.CurrentEraValue] Expected ID > 0");
234                     m_currentEraValue = CalendarData.GetCalendarData(BaseCalendarID).iCurrentEra;
235                 }
236                 return (m_currentEraValue);
237             }
238         }
239
240         // The current era for a calendar.
241
242         public const int CurrentEra = 0;
243
244         internal int twoDigitYearMax = -1;
245
246         internal static void CheckAddResult(long ticks, DateTime minValue, DateTime maxValue) {
247             if (ticks < minValue.Ticks || ticks > maxValue.Ticks) {
248                 throw new ArgumentException(
249                     String.Format(CultureInfo.InvariantCulture, Environment.GetResourceString("Argument_ResultCalendarRange"),
250                         minValue, maxValue));
251             }
252             Contract.EndContractBlock();
253         }
254
255         internal DateTime Add(DateTime time, double value, int scale) {
256             // From ECMA CLI spec, Partition III, section 3.27:
257             //
258             // If overflow occurs converting a floating-point type to an integer, or if the floating-point value 
259             // being converted to an integer is a NaN, the value returned is unspecified. 
260             //
261             // Based upon this, this method should be performing the comparison against the double
262             // before attempting a cast. Otherwise, the result is undefined.
263             double tempMillis = (value * scale + (value >= 0 ? 0.5 : -0.5));
264             if (!((tempMillis > -(double)MaxMillis) && (tempMillis < (double)MaxMillis)))
265             {
266                 throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_AddValue"));
267             }
268
269             long millis = (long)tempMillis;
270             long ticks = time.Ticks + millis * TicksPerMillisecond;
271             CheckAddResult(ticks, MinSupportedDateTime, MaxSupportedDateTime);
272             return (new DateTime(ticks));
273         }
274
275         // Returns the DateTime resulting from adding the given number of
276         // milliseconds to the specified DateTime. The result is computed by rounding
277         // the number of milliseconds given by value to the nearest integer,
278         // and adding that interval to the specified DateTime. The value
279         // argument is permitted to be negative.
280         //
281
282         public virtual DateTime AddMilliseconds(DateTime time, double milliseconds) {
283             return (Add(time, milliseconds, 1));
284         }
285
286
287         // Returns the DateTime resulting from adding a fractional number of
288         // days to the specified DateTime. The result is computed by rounding the
289         // fractional number of days given by value to the nearest
290         // millisecond, and adding that interval to the specified DateTime. The
291         // value argument is permitted to be negative.
292         //
293
294         public virtual DateTime AddDays(DateTime time, int days) {
295             return (Add(time, days, MillisPerDay));
296         }
297
298         // Returns the DateTime resulting from adding a fractional number of
299         // hours to the specified DateTime. The result is computed by rounding the
300         // fractional number of hours given by value to the nearest
301         // millisecond, and adding that interval to the specified DateTime. The
302         // value argument is permitted to be negative.
303         //
304
305         public virtual DateTime AddHours(DateTime time, int hours) {
306             return (Add(time, hours, MillisPerHour));
307         }
308
309
310         // Returns the DateTime resulting from adding a fractional number of
311         // minutes to the specified DateTime. The result is computed by rounding the
312         // fractional number of minutes given by value to the nearest
313         // millisecond, and adding that interval to the specified DateTime. The
314         // value argument is permitted to be negative.
315         //
316
317         public virtual DateTime AddMinutes(DateTime time, int minutes) {
318             return (Add(time, minutes, MillisPerMinute));
319         }
320
321
322         // Returns the DateTime resulting from adding the given number of
323         // months to the specified DateTime. The result is computed by incrementing
324         // (or decrementing) the year and month parts of the specified DateTime by
325         // value months, and, if required, adjusting the day part of the
326         // resulting date downwards to the last day of the resulting month in the
327         // resulting year. The time-of-day part of the result is the same as the
328         // time-of-day part of the specified DateTime.
329         //
330         // In more precise terms, considering the specified DateTime to be of the
331         // form y / m / d + t, where y is the
332         // year, m is the month, d is the day, and t is the
333         // time-of-day, the result is y1 / m1 / d1 + t,
334         // where y1 and m1 are computed by adding value months
335         // to y and m, and d1 is the largest value less than
336         // or equal to d that denotes a valid day in month m1 of year
337         // y1.
338         //
339
340         public abstract DateTime AddMonths(DateTime time, int months);
341
342         // Returns the DateTime resulting from adding a number of
343         // seconds to the specified DateTime. The result is computed by rounding the
344         // fractional number of seconds given by value to the nearest
345         // millisecond, and adding that interval to the specified DateTime. The
346         // value argument is permitted to be negative.
347         //
348
349         public virtual DateTime AddSeconds(DateTime time, int seconds) {
350             return Add(time, seconds, MillisPerSecond);
351         }
352
353         // Returns the DateTime resulting from adding a number of
354         // weeks to the specified DateTime. The
355         // value argument is permitted to be negative.
356         //
357
358         public virtual DateTime AddWeeks(DateTime time, int weeks) {
359             return (AddDays(time, weeks * 7));
360         }
361
362
363         // Returns the DateTime resulting from adding the given number of
364         // years to the specified DateTime. The result is computed by incrementing
365         // (or decrementing) the year part of the specified DateTime by value
366         // years. If the month and day of the specified DateTime is 2/29, and if the
367         // resulting year is not a leap year, the month and day of the resulting
368         // DateTime becomes 2/28. Otherwise, the month, day, and time-of-day
369         // parts of the result are the same as those of the specified DateTime.
370         //
371
372         public abstract DateTime AddYears(DateTime time, int years);
373
374         // Returns the day-of-month part of the specified DateTime. The returned
375         // value is an integer between 1 and 31.
376         //
377
378         public abstract int GetDayOfMonth(DateTime time);
379
380         // Returns the day-of-week part of the specified DateTime. The returned value
381         // is an integer between 0 and 6, where 0 indicates Sunday, 1 indicates
382         // Monday, 2 indicates Tuesday, 3 indicates Wednesday, 4 indicates
383         // Thursday, 5 indicates Friday, and 6 indicates Saturday.
384         //
385
386         public abstract DayOfWeek GetDayOfWeek(DateTime time);
387
388         // Returns the day-of-year part of the specified DateTime. The returned value
389         // is an integer between 1 and 366.
390         //
391
392         public abstract int GetDayOfYear(DateTime time);
393
394         // Returns the number of days in the month given by the year and
395         // month arguments.
396         //
397
398         public virtual int GetDaysInMonth(int year, int month)
399         {
400             return (GetDaysInMonth(year, month, CurrentEra));
401         }
402
403         // Returns the number of days in the month given by the year and
404         // month arguments for the specified era.
405         //
406
407         public abstract int GetDaysInMonth(int year, int month, int era);
408
409         // Returns the number of days in the year given by the year argument for the current era.
410         //
411
412         public virtual int GetDaysInYear(int year)
413         {
414             return (GetDaysInYear(year, CurrentEra));
415         }
416
417         // Returns the number of days in the year given by the year argument for the current era.
418         //
419
420         public abstract int GetDaysInYear(int year, int era);
421
422         // Returns the era for the specified DateTime value.
423
424         public abstract int GetEra(DateTime time);
425
426         /*=================================Eras==========================
427         **Action: Get the list of era values.
428         **Returns: The int array of the era names supported in this calendar.
429         **      null if era is not used.
430         **Arguments: None.
431         **Exceptions: None.
432         ============================================================================*/
433
434
435         public abstract int[] Eras {
436             get;
437         }
438
439
440         // Returns the hour part of the specified DateTime. The returned value is an
441         // integer between 0 and 23.
442         //
443
444         public virtual int GetHour(DateTime time) {
445             return ((int)((time.Ticks / TicksPerHour) % 24));
446         }
447
448         // Returns the millisecond part of the specified DateTime. The returned value
449         // is an integer between 0 and 999.
450         //
451
452         public virtual double GetMilliseconds(DateTime time) {
453             return (double)((time.Ticks / TicksPerMillisecond) % 1000);
454         }
455
456         // Returns the minute part of the specified DateTime. The returned value is
457         // an integer between 0 and 59.
458         //
459
460         public virtual int GetMinute(DateTime time) {
461             return ((int)((time.Ticks / TicksPerMinute) % 60));
462         }
463
464         // Returns the month part of the specified DateTime. The returned value is an
465         // integer between 1 and 12.
466         //
467
468         public abstract int GetMonth(DateTime time);
469
470         // Returns the number of months in the specified year in the current era.
471
472         public virtual int GetMonthsInYear(int year)
473         {
474             return (GetMonthsInYear(year, CurrentEra));
475         }
476
477         // Returns the number of months in the specified year and era.
478
479         public abstract int GetMonthsInYear(int year, int era);
480
481         // Returns the second part of the specified DateTime. The returned value is
482         // an integer between 0 and 59.
483         //
484
485         public virtual int GetSecond(DateTime time) {
486             return ((int)((time.Ticks / TicksPerSecond) % 60));
487         }
488
489         /*=================================GetFirstDayWeekOfYear==========================
490         **Action: Get the week of year using the FirstDay rule.
491         **Returns:  the week of year.
492         **Arguments:
493         **  time
494         **  firstDayOfWeek  the first day of week (0=Sunday, 1=Monday, ... 6=Saturday)
495         **Notes:
496         **  The CalendarWeekRule.FirstDay rule: Week 1 begins on the first day of the year.
497         **  Assume f is the specifed firstDayOfWeek,
498         **  and n is the day of week for January 1 of the specified year.
499         **  Assign offset = n - f;
500         **  Case 1: offset = 0
501         **      E.g.
502         **                     f=1
503         **          weekday 0  1  2  3  4  5  6  0  1
504         **          date       1/1
505         **          week#      1                    2
506         **      then week of year = (GetDayOfYear(time) - 1) / 7 + 1
507         **
508         **  Case 2: offset < 0
509         **      e.g.
510         **                     n=1   f=3
511         **          weekday 0  1  2  3  4  5  6  0
512         **          date       1/1
513         **          week#      1     2
514         **      This means that the first week actually starts 5 days before 1/1.
515         **      So week of year = (GetDayOfYear(time) + (7 + offset) - 1) / 7 + 1
516         **  Case 3: offset > 0
517         **      e.g.
518         **                  f=0   n=2
519         **          weekday 0  1  2  3  4  5  6  0  1  2
520         **          date          1/1
521         **          week#         1                    2
522         **      This means that the first week actually starts 2 days before 1/1.
523         **      So Week of year = (GetDayOfYear(time) + offset - 1) / 7 + 1
524         ============================================================================*/
525
526         internal int GetFirstDayWeekOfYear(DateTime time, int firstDayOfWeek) {
527             int dayOfYear = GetDayOfYear(time) - 1;   // Make the day of year to be 0-based, so that 1/1 is day 0.
528             // Calculate the day of week for the first day of the year.
529             // dayOfWeek - (dayOfYear % 7) is the day of week for the first day of this year.  Note that
530             // this value can be less than 0.  It's fine since we are making it positive again in calculating offset.
531             int dayForJan1 = (int)GetDayOfWeek(time) - (dayOfYear % 7);
532             int offset = (dayForJan1 - firstDayOfWeek + 14) % 7;
533             Contract.Assert(offset >= 0, "Calendar.GetFirstDayWeekOfYear(): offset >= 0");
534             return ((dayOfYear + offset) / 7 + 1);
535         }
536
537         private int GetWeekOfYearFullDays(DateTime time, int firstDayOfWeek, int fullDays) {
538             int dayForJan1;
539             int offset;
540             int day;
541
542             int dayOfYear = GetDayOfYear(time) - 1; // Make the day of year to be 0-based, so that 1/1 is day 0.
543             //
544             // Calculate the number of days between the first day of year (1/1) and the first day of the week.
545             // This value will be a positive value from 0 ~ 6.  We call this value as "offset".
546             //
547             // If offset is 0, it means that the 1/1 is the start of the first week.
548             //     Assume the first day of the week is Monday, it will look like this:
549             //     Sun      Mon     Tue     Wed     Thu     Fri     Sat
550             //     12/31    1/1     1/2     1/3     1/4     1/5     1/6
551             //              +--> First week starts here.
552             //
553             // If offset is 1, it means that the first day of the week is 1 day ahead of 1/1.
554             //     Assume the first day of the week is Monday, it will look like this:
555             //     Sun      Mon     Tue     Wed     Thu     Fri     Sat
556             //     1/1      1/2     1/3     1/4     1/5     1/6     1/7
557             //              +--> First week starts here.
558             //
559             // If offset is 2, it means that the first day of the week is 2 days ahead of 1/1.
560             //     Assume the first day of the week is Monday, it will look like this:
561             //     Sat      Sun     Mon     Tue     Wed     Thu     Fri     Sat
562             //     1/1      1/2     1/3     1/4     1/5     1/6     1/7     1/8
563             //                      +--> First week starts here.
564
565
566
567             // Day of week is 0-based.
568             // Get the day of week for 1/1.  This can be derived from the day of week of the target day.
569             // Note that we can get a negative value.  It's ok since we are going to make it a positive value when calculating the offset.
570             dayForJan1 = (int)GetDayOfWeek(time) - (dayOfYear % 7);
571
572             // Now, calculate the offset.  Subtract the first day of week from the dayForJan1.  And make it a positive value.
573             offset = (firstDayOfWeek - dayForJan1 + 14) % 7;
574             if (offset != 0 && offset >= fullDays)
575             {
576                 //
577                 // If the offset is greater than the value of fullDays, it means that
578                 // the first week of the year starts on the week where Jan/1 falls on.
579                 //
580                 offset -= 7;
581             }
582             //
583             // Calculate the day of year for specified time by taking offset into account.
584             //
585             day = dayOfYear - offset;
586             if (day >= 0) {
587                 //
588                 // If the day of year value is greater than zero, get the week of year.
589                 //
590                 return (day/7 + 1);
591             }
592             //
593             // Otherwise, the specified time falls on the week of previous year.
594             // Call this method again by passing the last day of previous year.
595             //
596             // the last day of the previous year may "underflow" to no longer be a valid date time for
597             // this calendar if we just subtract so we need the subclass to provide us with 
598             // that information
599             if (time <= MinSupportedDateTime.AddDays(dayOfYear))
600             {
601                 return GetWeekOfYearOfMinSupportedDateTime(firstDayOfWeek, fullDays);
602             }
603             return (GetWeekOfYearFullDays(time.AddDays(-(dayOfYear + 1)), firstDayOfWeek, fullDays));
604         }
605
606         private int GetWeekOfYearOfMinSupportedDateTime(int firstDayOfWeek, int minimumDaysInFirstWeek)
607         {
608             int dayOfYear = GetDayOfYear(MinSupportedDateTime) - 1;  // Make the day of year to be 0-based, so that 1/1 is day 0.
609             int dayOfWeekOfFirstOfYear = (int)GetDayOfWeek(MinSupportedDateTime) - dayOfYear % 7;
610
611             // Calculate the offset (how many days from the start of the year to the start of the week)
612             int offset = (firstDayOfWeek + 7 - dayOfWeekOfFirstOfYear) % 7;
613             if (offset == 0 || offset >= minimumDaysInFirstWeek)
614             {
615                 // First of year falls in the first week of the year
616                 return 1;
617             }
618
619             int daysInYearBeforeMinSupportedYear = DaysInYearBeforeMinSupportedYear - 1; // Make the day of year to be 0-based, so that 1/1 is day 0.
620             int dayOfWeekOfFirstOfPreviousYear = dayOfWeekOfFirstOfYear - 1 - (daysInYearBeforeMinSupportedYear % 7);
621
622             // starting from first day of the year, how many days do you have to go forward
623             // before getting to the first day of the week?
624             int daysInInitialPartialWeek = (firstDayOfWeek - dayOfWeekOfFirstOfPreviousYear + 14) % 7;
625             int day = daysInYearBeforeMinSupportedYear - daysInInitialPartialWeek;
626             if (daysInInitialPartialWeek >= minimumDaysInFirstWeek)
627             {
628                 // If the offset is greater than the minimum Days in the first week, it means that
629                 // First of year is part of the first week of the year even though it is only a partial week
630                 // add another week
631                 day += 7;
632             }
633
634             return (day / 7 + 1);
635         }
636
637         // it would be nice to make this abstract but we can't since that would break previous implementations
638         protected virtual int DaysInYearBeforeMinSupportedYear
639         {
640             get
641             {
642                 return 365;
643             }
644         }
645
646
647         // Returns the week of year for the specified DateTime. The returned value is an
648         // integer between 1 and 53.
649         //
650
651         public virtual int GetWeekOfYear(DateTime time, CalendarWeekRule rule, DayOfWeek firstDayOfWeek)
652         {
653             if ((int)firstDayOfWeek < 0 || (int)firstDayOfWeek > 6) {
654                 throw new ArgumentOutOfRangeException(
655                     "firstDayOfWeek", Environment.GetResourceString("ArgumentOutOfRange_Range",
656                     DayOfWeek.Sunday, DayOfWeek.Saturday));
657             }
658             Contract.EndContractBlock();
659             switch (rule) {
660                 case CalendarWeekRule.FirstDay:
661                     return (GetFirstDayWeekOfYear(time, (int)firstDayOfWeek));
662                 case CalendarWeekRule.FirstFullWeek:
663                     return (GetWeekOfYearFullDays(time, (int)firstDayOfWeek, 7));
664                 case CalendarWeekRule.FirstFourDayWeek:
665                     return (GetWeekOfYearFullDays(time, (int)firstDayOfWeek, 4));
666             }
667             throw new ArgumentOutOfRangeException(
668                 "rule", Environment.GetResourceString("ArgumentOutOfRange_Range",
669                 CalendarWeekRule.FirstDay, CalendarWeekRule.FirstFourDayWeek));
670
671         }
672
673         // Returns the year part of the specified DateTime. The returned value is an
674         // integer between 1 and 9999.
675         //
676
677         public abstract int GetYear(DateTime time);
678
679         // Checks whether a given day in the current era is a leap day. This method returns true if
680         // the date is a leap day, or false if not.
681         //
682
683         public virtual bool IsLeapDay(int year, int month, int day)
684         {
685             return (IsLeapDay(year, month, day, CurrentEra));
686         }
687
688         // Checks whether a given day in the specified era is a leap day. This method returns true if
689         // the date is a leap day, or false if not.
690         //
691
692         public abstract bool IsLeapDay(int year, int month, int day, int era);
693
694         // Checks whether a given month in the current era is a leap month. This method returns true if
695         // month is a leap month, or false if not.
696         //
697
698         public virtual bool IsLeapMonth(int year, int month) {
699             return (IsLeapMonth(year, month, CurrentEra));
700         }
701
702         // Checks whether a given month in the specified era is a leap month. This method returns true if
703         // month is a leap month, or false if not.
704         //
705
706         public abstract bool IsLeapMonth(int year, int month, int era);
707
708         // Returns  the leap month in a calendar year of the current era. This method returns 0
709         // if this calendar does not have leap month, or this year is not a leap year.
710         //
711
712         [System.Runtime.InteropServices.ComVisible(false)]
713         public virtual int GetLeapMonth(int year)
714         {
715             return (GetLeapMonth(year, CurrentEra));
716         }
717
718         // Returns  the leap month in a calendar year of the specified era. This method returns 0
719         // if this calendar does not have leap month, or this year is not a leap year.
720         //
721
722         [System.Runtime.InteropServices.ComVisible(false)]
723         public virtual int GetLeapMonth(int year, int era)
724         {
725             if (!IsLeapYear(year, era))
726                 return 0;
727
728             int monthsCount = GetMonthsInYear(year, era);
729             for (int month=1; month<=monthsCount; month++)
730             {
731                 if (IsLeapMonth(year, month, era))
732                     return month;
733             }
734
735             return 0;
736         }
737
738         // Checks whether a given year in the current era is a leap year. This method returns true if
739         // year is a leap year, or false if not.
740         //
741
742         public virtual bool IsLeapYear(int year)
743         {
744             return (IsLeapYear(year, CurrentEra));
745         }
746
747         // Checks whether a given year in the specified era is a leap year. This method returns true if
748         // year is a leap year, or false if not.
749         //
750
751         public abstract bool IsLeapYear(int year, int era);
752
753         // Returns the date and time converted to a DateTime value.  Throws an exception if the n-tuple is invalid.
754         //
755
756         public virtual DateTime ToDateTime(int year, int month,  int day, int hour, int minute, int second, int millisecond)
757         {
758             return (ToDateTime(year, month, day, hour, minute, second, millisecond, CurrentEra));
759         }
760
761         // Returns the date and time converted to a DateTime value.  Throws an exception if the n-tuple is invalid.
762         //
763
764         public abstract DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era);
765
766         internal virtual Boolean TryToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era, out DateTime result) {
767             result = DateTime.MinValue;
768             try {
769                 result = ToDateTime(year, month, day, hour, minute, second, millisecond, era);
770                 return true;
771             }
772             catch (ArgumentException) {
773                 return false;
774             }
775         }
776         
777         internal virtual bool IsValidYear(int year, int era) {
778             return (year >= GetYear(MinSupportedDateTime) && year <= GetYear(MaxSupportedDateTime));
779         }
780         
781         internal virtual bool IsValidMonth(int year, int month, int era) {
782             return (IsValidYear(year, era) && month >= 1 && month <= GetMonthsInYear(year, era));
783         }
784         
785         internal virtual bool IsValidDay(int year, int month, int day, int era)
786         {
787             return (IsValidMonth(year, month, era) && day >= 1 && day <= GetDaysInMonth(year, month, era));
788         }
789         
790
791         // Returns and assigns the maximum value to represent a two digit year.  This
792         // value is the upper boundary of a 100 year range that allows a two digit year
793         // to be properly translated to a four digit year.  For example, if 2029 is the
794         // upper boundary, then a two digit value of 30 should be interpreted as 1930
795         // while a two digit value of 29 should be interpreted as 2029.  In this example
796         // , the 100 year range would be from 1930-2029.  See ToFourDigitYear().
797
798         public virtual int TwoDigitYearMax
799         {
800             get
801             {
802                 return (twoDigitYearMax);
803             }
804
805             set
806             {
807                 VerifyWritable();
808                 twoDigitYearMax = value;
809             }
810         }
811
812         // Converts the year value to the appropriate century by using the
813         // TwoDigitYearMax property.  For example, if the TwoDigitYearMax value is 2029,
814         // then a two digit value of 30 will get converted to 1930 while a two digit
815         // value of 29 will get converted to 2029.
816
817         public virtual int ToFourDigitYear(int year) {
818             if (year < 0) {
819                 throw new ArgumentOutOfRangeException("year",
820                     Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
821             }
822             Contract.EndContractBlock();
823             if (year < 100) {
824                 return ((TwoDigitYearMax/100 - ( year > TwoDigitYearMax % 100 ? 1 : 0))*100 + year);
825             }
826             // If the year value is above 100, just return the year value.  Don't have to do
827             // the TwoDigitYearMax comparison.
828             return (year);
829         }
830
831         // Return the tick count corresponding to the given hour, minute, second.
832         // Will check the if the parameters are valid.
833         internal static long TimeToTicks(int hour, int minute, int second, int millisecond)
834         {
835             if (hour >= 0 && hour < 24 && minute >= 0 && minute < 60 && second >=0 && second < 60)
836             {
837                 if (millisecond < 0 || millisecond >= MillisPerSecond) {
838                     throw new ArgumentOutOfRangeException(
839                                 "millisecond",
840                                 String.Format(
841                                     CultureInfo.InvariantCulture,
842                                     Environment.GetResourceString("ArgumentOutOfRange_Range"), 0, MillisPerSecond - 1));
843                 }
844                 return TimeSpan.TimeToTicks(hour, minute, second) + millisecond * TicksPerMillisecond;
845             }
846             throw new ArgumentOutOfRangeException(null, Environment.GetResourceString("ArgumentOutOfRange_BadHourMinuteSecond"));
847         }
848
849         [System.Security.SecuritySafeCritical]  // auto-generated
850         [ResourceExposure(ResourceScope.None)]
851         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
852         internal static int GetSystemTwoDigitYearSetting(int CalID, int defaultYearValue)
853         {
854             // Call nativeGetTwoDigitYearMax
855             int twoDigitYearMax = CalendarData.nativeGetTwoDigitYearMax(CalID);
856             if (twoDigitYearMax < 0)
857             {
858                 twoDigitYearMax = defaultYearValue;
859             }
860             return (twoDigitYearMax);
861         }
862
863     }
864 }