// Calendar.cs // // (C) Ulrich Kunitz 2002 // // // Copyright (C) 2004 Novell, Inc (http://www.novell.com) // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // namespace System.Globalization { using System; using System.IO; /// /// The class serves as a base class for calendar classes. /// [Serializable] public abstract class Calendar { /// An protected integer property that gives the number of /// days in a week. It might be overridden. internal virtual int M_DaysInWeek { get { return 7; } } /// /// The protected method creates the string used in the /// /// /// An object that represents the smallest /// allowable value. /// An object that represents the greatest allowable /// value. /// The string used in the /// /// internal string M_ValidValues(object a, object b) { StringWriter sw = new StringWriter(); sw.Write("Valid values are between {0} and {1}, inclusive.", a, b); return sw.ToString(); } /// /// The protected method checks wether the parameter /// is in the allowed range. /// /// A string that gives the name of the /// parameter to check. /// An integer that gives the value to check. /// /// An integer that represents the smallest allowed /// value. /// An integer that represents the greatest allowed /// value. /// /// The exception is thrown, if the is outside /// the allowed range. /// internal void M_ArgumentInRange(string param, int arg, int a, int b) { if (a <= arg && arg <= b) return; throw new ArgumentOutOfRangeException(param, M_ValidValues(a, b)); } /// /// The protected method, that checks whether /// , , /// , and /// are in their valid ranges /// /// An integer that represents a hour, /// should be between 0 and 23. /// An integer that represents a minute, /// should be between 0 and 59. /// An integer that represents a second, /// should be between 0 and 59. /// An integer that represents a number /// of milliseconds, should be between 0 and 999999. /// /// The Exception is thrown, if one of the parameter is outside the /// allowed the range. /// internal void M_CheckHMSM(int hour, int minute, int second, int milliseconds) { M_ArgumentInRange("hour", hour, 0, 23); M_ArgumentInRange("minute", minute, 0, 59); M_ArgumentInRange("second", second, 0, 59); M_ArgumentInRange("milliseconds", milliseconds, 0, 999999); } /// /// A represantation of the CurrentEra. /// public const int CurrentEra = 0; /// When overridden gives the eras supported by the /// calendar as an array of integers. /// public abstract int[] Eras { get; } /// /// The protected member stores the value for the /// /// property. /// internal int M_TwoDigitYearMax; /// /// Private field containing the maximum year for the calendar. /// private int M_MaxYearValue = 0; /// /// Get-only property returing the maximum allowed year for this /// class. /// internal virtual int M_MaxYear { get { if (M_MaxYearValue == 0) { M_MaxYearValue = GetYear(DateTime.MaxValue); } return M_MaxYearValue; } } /// /// Checks whether the year is the era is valid, if era = CurrentEra /// the right value is set. /// /// The year to check. /// The era to check. /// /// The exception will be thrown, if the year is not valid. /// internal abstract void M_CheckYE(int year, ref int era); /// /// The property gives the maximum value for years with two /// digits. If the property has the value 2029, than the two-digit /// integer 29 results in the year 2029 and 30 in the /// year 1930. /// It might be overridden. /// public virtual int TwoDigitYearMax { get { return M_TwoDigitYearMax; } set { M_ArgumentInRange("year", value, 100, M_MaxYear); int era = CurrentEra; M_CheckYE(value, ref era); M_TwoDigitYearMax = value; } } /// /// The virtual method adds days to a given date. /// /// The /// to which to add /// days. /// /// The number of days to add. /// A new value, that /// results from adding to the specified /// DateTime. public virtual DateTime AddDays(DateTime time, int days) { return time.Add(TimeSpan.FromDays(days)); } /// /// The virtual method adds hours to a given date. /// /// The /// to which to add /// hours. /// /// The number of hours to add. /// A new value, that /// results from adding to the specified /// DateTime. public virtual DateTime AddHours(DateTime time, int hours) { return time.Add(TimeSpan.FromHours(hours)); } /// /// The virtual method adds milliseconds to a given date. /// /// The /// to which to add /// milliseconds. /// /// The number of milliseconds given as /// double to add. Keep in mind the 100 nanosecond resolution of /// . /// /// A new value, that /// results from adding to the specified /// DateTime. public virtual DateTime AddMilliseconds(DateTime time, double milliseconds) { return time.Add(TimeSpan.FromMilliseconds(milliseconds)); } /// /// The virtual method adds minutes to a given date. /// /// The /// to which to add /// minutes. /// /// The number of minutes to add. /// A new value, that /// results from adding to the specified /// DateTime. public virtual DateTime AddMinutes(DateTime time, int minutes) { return time.Add(TimeSpan.FromMinutes(minutes)); } /// /// When overrideden adds months to a given date. /// /// The /// to which to add /// months. /// /// The number of months to add. /// A new value, that /// results from adding to the specified /// DateTime. public abstract DateTime AddMonths(DateTime time, int months); /// /// The virtual method adds seconds to a given date. /// /// The /// to which to add /// seconds. /// /// The number of seconds to add. /// A new value, that /// results from adding to the specified /// DateTime. public virtual DateTime AddSeconds(DateTime time, int seconds) { return time.Add(TimeSpan.FromSeconds(seconds)); } /// /// A wirtual method that adds weeks to a given date. /// /// The /// to which to add /// weeks. /// /// The number of weeks to add. /// A new value, that /// results from adding to the specified /// DateTime. public virtual DateTime AddWeeks(DateTime time, int weeks) { return time.AddDays(weeks * M_DaysInWeek); } /// /// When overrideden adds years to a given date. /// /// The /// to which to add /// years. /// /// The number of years to add. /// A new value, that /// results from adding to the specified /// DateTime. public abstract DateTime AddYears(DateTime time, int years); /// /// When overriden gets the day of the month from /// . /// /// The /// that specifies a /// date. /// /// An integer giving the day of months, starting with 1. /// public abstract int GetDayOfMonth(DateTime time); /// /// When overriden gets the day of the week from the specified date. /// /// The /// that specifies a /// date. /// /// An integer giving the day of months, starting with 1. /// public abstract DayOfWeek GetDayOfWeek(DateTime time); /// /// When overridden gives the number of the day in the year. /// /// The /// that specifies a /// date. /// /// An integer representing the day of the year, /// starting with 1. public abstract int GetDayOfYear(DateTime time); /// /// A virtual method that gives the number of days of the specified /// month of the and the /// . /// /// An integer that gives the year in the current /// era. /// An integer that gives the month, starting /// with 1. /// An integer that gives the number of days of the /// specified month. /// /// The exception is thrown, if or /// is outside the allowed range. /// public virtual int GetDaysInMonth(int year, int month) { return GetDaysInMonth(year, month, CurrentEra); } /// /// When overridden gives the number of days in the specified month /// of the given year and era. /// /// An integer that gives the year. /// /// An integer that gives the month, starting /// with 1. /// An intger that gives the era of the specified /// year. /// An integer that gives the number of days of the /// specified month. /// /// The exception is thrown, if , /// ,or is outside /// the allowed range. /// public abstract int GetDaysInMonth(int year, int month, int era); /// /// A virtual method that gives the number of days of the specified /// year of the . /// /// An integer that gives the year in the current /// era. /// An integer that gives the number of days of the /// specified year. /// /// The exception is thrown, if /// is outside the allowed range. /// public virtual int GetDaysInYear(int year) { return GetDaysInYear(year, CurrentEra); } /// /// When overridden gives the number of days of the specified /// year of the given era.. /// /// An integer that specifies the year. /// /// An ineger that specifies the era. /// /// An integer that gives the number of days of the /// specified year. /// /// The exception is thrown, if /// is outside the allowed range. /// public abstract int GetDaysInYear(int year, int era); /// /// When overridden gives the era of the specified date. /// /// The /// that specifies a /// date. /// /// An integer representing the era of the calendar. /// public abstract int GetEra(DateTime time); /// /// Virtual method that gives the hour of the specified time. /// /// The /// that specifies the /// time. /// /// An integer that gives the hour of the specified time, /// starting with 0. public virtual int GetHour(DateTime time) { return time.TimeOfDay.Hours; } /// /// Virtual method that gives the milliseconds in the current second /// of the specified time. /// /// The /// that specifies the /// time. /// /// An integer that gives the milliseconds in the seconds /// of the specified time, starting with 0. public virtual double GetMilliseconds(DateTime time) { return time.TimeOfDay.Milliseconds; } /// /// Virtual method that gives the minute of the specified time. /// /// The /// that specifies the /// time. /// /// An integer that gives the minute of the specified time, /// starting with 0. public virtual int GetMinute(DateTime time) { return time.TimeOfDay.Minutes; } /// /// When overridden gives the number of the month of the specified /// date. /// /// The /// that specifies a /// date. /// /// An integer representing the month, /// starting with 1. public abstract int GetMonth(DateTime time); /// /// Virtual method that gives the number of months of the specified /// year of the . /// /// An integer that specifies the year in the /// current era. /// /// An integer that gives the number of the months in the /// specified year. /// /// The exception is thrown, if the year is not allowed in the /// current era. /// public virtual int GetMonthsInYear(int year) { return GetMonthsInYear(year, CurrentEra); } /// /// When overridden gives the number of months in the specified year /// and era. /// /// An integer that specifies the year. /// /// An integer that specifies the era. /// /// An integer that gives the number of the months in the /// specified year. /// /// The exception is thrown, if the year or the era are not valid. /// public abstract int GetMonthsInYear(int year, int era); /// /// Virtual method that gives the second of the specified time. /// /// The /// that specifies the /// time. /// /// An integer that gives the second of the specified time, /// starting with 0. public virtual int GetSecond(DateTime time) { return time.TimeOfDay.Seconds; } /// /// A protected method to calculate the number of days between two /// dates. /// /// A /// representing the first date. /// /// A /// representing the second date. /// /// An integer that represents the difference of days /// between and . /// internal int M_DiffDays(DateTime timeA, DateTime timeB) { long diff = timeA.Ticks - timeB.Ticks; if (diff >= 0) { return (int)(diff/TimeSpan.TicksPerDay); } diff += 1; return -1 + (int)(diff/TimeSpan.TicksPerDay); } /// /// A protected method that gives the first day of the second week of /// the year. /// /// An integer that represents the year. /// The /// /// to be used for the calculation. /// /// /// The /// specifying the first day in a week. /// /// The representing /// the first day of the second week of the year. /// internal DateTime M_GetFirstDayOfSecondWeekOfYear( int year, CalendarWeekRule rule, DayOfWeek firstDayOfWeek) { DateTime d1 = ToDateTime(year, 1, 1, 0, 0, 0, 0); int dow1 = (int)GetDayOfWeek(d1); int fdow = (int)firstDayOfWeek; int d = 0; switch (rule) { case CalendarWeekRule.FirstDay: if (fdow > dow1) { d += fdow - dow1; } else { d += fdow + M_DaysInWeek - dow1; } break; case CalendarWeekRule.FirstFullWeek: d = M_DaysInWeek; if (fdow >= dow1) { d += fdow - dow1; } else { d += fdow + M_DaysInWeek - dow1; } break; case CalendarWeekRule.FirstFourDayWeek: int dow4 = (dow1 + 3)%M_DaysInWeek; d = 3; if (fdow > dow4) { d += fdow - dow4; } else { d += fdow + M_DaysInWeek - dow4; } break; } return AddDays(d1, d); } /// /// A virtual method that gives the number of the week in the year. /// /// A /// representing the date. /// /// The /// /// to be used for the calculation. /// /// /// The /// specifying the first day in a week. /// /// An integer representing the number of the week in the /// year, starting with 1. /// public virtual int GetWeekOfYear(DateTime time, CalendarWeekRule rule, DayOfWeek firstDayOfWeek) { if (firstDayOfWeek < DayOfWeek.Sunday || DayOfWeek.Saturday < firstDayOfWeek) { throw new ArgumentOutOfRangeException("firstDayOfWeek", "Value is not a valid day of week."); } int year = GetYear(time); int days; while (true) { DateTime secondWeek = M_GetFirstDayOfSecondWeekOfYear( year, rule, firstDayOfWeek); days = M_DiffDays(time, secondWeek) + M_DaysInWeek; if (days >= 0) break; year -= 1; } return 1 + days/M_DaysInWeek; } /// /// When overridden gives the number of the year of the specified /// date. /// /// The /// that specifies a /// date. /// /// An integer representing the year, /// starting with 1. public abstract int GetYear(DateTime time); /// /// A virtual method that tells whether the given day in the /// is a leap day. /// /// An integer that specifies the year in the /// current era. /// /// An integer that specifies the month. /// /// An integer that specifies the day. /// /// A boolean that tells whether the given day is a leap /// day. /// /// /// The exception is thrown, if the year, month or day is not valid /// the current era. /// public virtual bool IsLeapDay(int year, int month, int day) { return IsLeapDay(year, month, day, CurrentEra); } /// /// Tells when overridden whether the given day /// is a leap day. /// /// An integer that specifies the year in the /// given era. /// /// An integer that specifies the month. /// /// An integer that specifies the day. /// /// An integer that specifies the era. /// /// A boolean that tells whether the given day is a leap /// day. /// /// /// The exception is thrown, if the year, month, day, or era is not /// valid. /// public abstract bool IsLeapDay(int year, int month, int day, int era); /// /// A virtual method that tells whether the given month of the /// specified year in the /// is a leap month. /// /// An integer that specifies the year in the /// current era. /// /// An integer that specifies the month. /// /// A boolean that tells whether the given month is a leap /// month. /// /// /// The exception is thrown, if the year or month is not valid /// the current era. /// public virtual bool IsLeapMonth(int year, int month) { return IsLeapMonth(year, month, CurrentEra); } /// /// Tells when overridden whether the given month /// is a leap month. /// /// An integer that specifies the year in the /// given era. /// /// An integer that specifies the month. /// /// An integer that specifies the era. /// /// A boolean that tells whether the given month is a leap /// month. /// /// /// The exception is thrown, if the year, month, or era is not /// valid. /// public abstract bool IsLeapMonth(int year, int month, int era); /// /// A virtual method that tells whether the given year /// in the /// is a leap year. /// /// An integer that specifies the year in the /// current era. /// /// A boolean that tells whether the given year is a leap /// year. /// /// /// The exception is thrown, if the year is not valid /// the current era. /// public virtual bool IsLeapYear(int year) { return IsLeapYear(year, CurrentEra); } /// /// Tells when overridden whether the given year /// is a leap year. /// /// An integer that specifies the year in the /// given era. /// /// An integer that specifies the era. /// /// A boolean that tells whether the given year is a leap /// year. /// /// /// The exception is thrown, if the year or era is not /// valid. /// public abstract bool IsLeapYear(int year, int era); /// /// A virtual method that creates the /// from the parameters. /// /// An integer that gives the year in the /// . /// /// An integer that specifies the month. /// /// An integer that specifies the day. /// /// An integer that specifies the hour. /// /// An integer that specifies the minute. /// /// An integer that gives the second. /// /// An integer that gives the /// milliseconds. /// /// A /// representig the date and time. /// /// /// The exception is thrown, if at least one of the parameters /// is out of range. /// public virtual DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int milliseconds) { return ToDateTime(year, month, day, hour, minute, second, milliseconds, CurrentEra); } /// /// When overridden creates the /// from the parameters. /// /// An integer that gives the year in the /// . /// /// An integer that specifies the month. /// /// An integer that specifies the day. /// /// An integer that specifies the hour. /// /// An integer that specifies the minute. /// /// An integer that gives the second. /// /// An integer that gives the /// milliseconds. /// /// An integer that specifies the era. /// /// A /// representig the date and time. /// /// /// The exception is thrown, if at least one of the parameters /// is out of range. /// public abstract DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int milliseconds, int era); /// /// A virtual method that converts a two-digit year to a four-digit /// year. It uses the property. /// /// An integer that gives the two-digit year. /// /// An integer giving the four digit year. /// /// /// The exception is thrown if the year is negative or the resulting /// year is invalid. /// public virtual int ToFourDigitYear(int year) { if (year < 0) throw new ArgumentOutOfRangeException( "year", "Non-negative number required."); /* seems not to be the right thing to do, but .NET is * doing it this way. */ if (year <= 99) { int year2 = TwoDigitYearMax%100; int d = year - year2; year = TwoDigitYearMax + d + (d <= 0 ? 0 : -100); } int era = CurrentEra; M_CheckYE(year, ref era); return year; } // TwoDigitYearMax: Windows reads it from the Registry, we // should have an XML file with the defaults /// /// The default constructor, is sets the TwoDigitYearMax to 2029. /// /// /// The .NET framework reads the value from the registry. /// We should implement it here. Currently I set the default values /// in the ctors of the derived classes, if it is 99. /// protected Calendar() { M_TwoDigitYearMax = 99; } /// Protected field storing the abbreviated era names. /// internal string[] M_AbbrEraNames; /// Protected field storing the era names. /// internal string[] M_EraNames; /// /// The property stores the era names. It might be overwritten by /// CultureInfo. /// internal string[] AbbreviatedEraNames { get { if (M_AbbrEraNames == null || M_AbbrEraNames.Length != Eras.Length) throw new Exception( "Internal: M_AbbrEraNames " + "wrong initialized!"); return (string[])M_AbbrEraNames.Clone(); } set { if (value.Length != Eras.Length) { StringWriter sw = new StringWriter(); sw.Write("Array length must be equal Eras " + "length {0}.", Eras.Length); throw new ArgumentException( sw.ToString()); } M_AbbrEraNames = (string[])value.Clone(); } } /// /// The property stores the era names. It might be overwritten by /// CultureInfo. /// internal string[] EraNames { get { if (M_EraNames == null || M_EraNames.Length != Eras.Length) throw new Exception( "Internal: M_EraNames " + "not initialized!"); return (string[])M_EraNames.Clone(); } set { if (value.Length != Eras.Length) { StringWriter sw = new StringWriter(); sw.Write("Array length must be equal Eras " + "length {0}.", Eras.Length); throw new ArgumentException( sw.ToString()); } M_EraNames = (string[])value.Clone(); } } } // class Calendar } // namespace System.Globalization