// GlobalCalendar.cs // v1.0 // by, Jeffery Walker // http://cs.oberlin.edu/~jwalker namespace System.Globalization { public class GlobalCalendar : Calendar { private int twoDigitYearMax = new GregorianCalendar().TwoDigitYearMax; private static readonly DateTime limitDate = new DateTime(9999,12, 22); public const int GlobalEra = 1; public const DayOfWeek NewYearsDay = (DayOfWeek)(-1); public const DayOfWeek LeapDay = (DayOfWeek)7; public const int CalendarOffset = 10; #region Properties public override int[] Eras { get { int[] eras = {GlobalEra}; return eras; } } public override int TwoDigitYearMax { get { return twoDigitYearMax; } set { if(value<99) throw new ArgumentOutOfRangeException("TwoDigitYearMax", value, "TwoDigitYearMax must be greater than 99 inclusive."); twoDigitYearMax = value; } } #endregion #region Manipulate DateTime Methods public override DateTime AddWeeks(DateTime time, int weeks) { int year = GetYear(time); int month = GetMonth(time); int day = GetDayOfMonth(time); if(weeks%52==0) { year += weeks / 52; if(day==29 & !IsLeapYear(year)) day=28; } else { if(day==0) day=1; else if(day==29) day=28; year += weeks / 52; weeks %= 52; month += weeks / 4; weeks %= 4; day += weeks * 7; month += (day-1) /28; day = (day-1) % 28 + 1; year += (month-1) / 13; month = (month-1) % 13 + 1; } return ToDateTime(year, month, day, time.Hour, time.Minute, time.Second, time.Millisecond); } public override DateTime AddMonths(DateTime time, int months) { int year = GetYear(time); int month = GetMonth(time); int day = GetDayOfMonth(time); if(months%13==0) { year += months / 13; if(day==29 & !IsLeapYear(year)) day=28; } else { if(day==0) day=1; else if(day==29) day=28; year += months / 13; months %= 13; month += months; year += (month-1) / 13; month = (month-1) % 13 + 1; } return ToDateTime(year, month, day, time.Hour, time.Minute, time.Second, time.Millisecond); } public override DateTime AddYears(DateTime time, int years) { int year = GetYear(time); int month = GetMonth(time); int day = GetDayOfMonth(time); year += years; if(day==29 & !IsLeapYear(year)) day=28; return ToDateTime(year, month, day, time.Hour, time.Minute, time.Second, time.Millisecond); } public override DayOfWeek GetDayOfWeek(DateTime time) { int dayOfMonth = GetDayOfMonth(time); switch(dayOfMonth) { case 0: return NewYearsDay; case 29: return LeapDay; default: return (DayOfWeek)((dayOfMonth-1) % 7 + 1); } } public override int GetDayOfMonth(DateTime time) { int dayOfYear = GetDayOfYear(time); switch(dayOfYear) { case 1: return 0; case 366: return 29; default: return (dayOfYear-2)%28+1; } } public override int GetDayOfYear(DateTime time) { return ((time.DayOfYear - 1 + CalendarOffset) % GetDaysInYear(time.Year)) + 1; } public override int GetWeekOfYear(DateTime time, CalendarWeekRule rule, DayOfWeek firstDayOfWeek) { CheckCalendarWeekRule(rule); CheckDayOfWeek(firstDayOfWeek); if(firstDayOfWeek != DayOfWeek.Monday) throw new ArgumentException("The first day of the week is always Monday in the Global Calendar.", "firstDayOfWeek"); int dayOfYear = GetDayOfYear(time); switch(dayOfYear) { case 1: return 0; case 366: return 53; default: return (dayOfYear-2)/7+1; } } public override int GetMonth(DateTime time) { int dayOfYear = GetDayOfYear(time); switch(dayOfYear) { case 1: return 1; case 366: return 13; default: return (dayOfYear-2)/28+1; } } public override int GetYear(DateTime time) { if(time >= limitDate) return 10000; else return time.AddDays(CalendarOffset).Year; } public override int GetEra(DateTime time) { return GlobalEra; } #endregion public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond) { CheckMonth(month); CheckDay(year, month, day); int dayOfYear = (month-1)*28+day; return new DateTime(year, 1, 1, hour, minute, second, millisecond).AddDays(dayOfYear-CalendarOffset); } public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era) { CheckEra(era); return ToDateTime(year, month, day, hour, minute, second, millisecond); } #region Describe Calendar Methods public override int GetDaysInMonth(int year, int month) { CheckMonth(month); switch(month) { case 1: return 29; case 13: if(IsLeapYear(year)) return 29; else return 28; default: return 28; } } public override int GetDaysInMonth(int year, int month, int era) { CheckEra(era); return GetDaysInMonth(year, month); } public override int GetDaysInYear(int year) { if(IsLeapYear(year)) return 366; else return 365; } public override int GetDaysInYear(int year, int era) { CheckEra(era); return GetDaysInYear(year); } public override int GetMonthsInYear(int year) { return 13; } public override int GetMonthsInYear(int year, int era) { CheckEra(era); return 13; } public override bool IsLeapDay(int year, int month, int day) { CheckMonth(month); CheckDay(year, month, day); return day==29; } public override bool IsLeapDay(int year, int month, int day, int era) { CheckEra(era); return IsLeapDay(year, month, day); } public override bool IsLeapMonth(int year, int month) { CheckMonth(month); return false; } public override bool IsLeapMonth(int year, int month, int era) { CheckMonth(month); CheckEra(era); return false; } public override bool IsLeapYear(int year) { return (year%4==0) && (year%100!=0 || year%400==0); } public override bool IsLeapYear(int year, int era) { CheckEra(era); return IsLeapYear(year); } #endregion #region Enforce Argument Ranges internal void CheckMonth(int month) { if(month<1 || month>13) throw new ArgumentOutOfRangeException("month", month, "Month must be between 1 and 13."); } internal void CheckEra(int era) { if(era!=GlobalEra && era!=CurrentEra) throw new ArgumentOutOfRangeException("era", "Era value was not valid."); } private void CheckDay(int year, int month, int day) { switch(month) { case 1: if(day<0 || day>28) throw new ArgumentOutOfRangeException("day", day, "Must be between 0 and 28 for January."); break; case 13: if(IsLeapYear(year)) { if(day<1 || day>29) throw new ArgumentOutOfRangeException("day", day, "Must be between 1 and 29 for December in leap years."); } else if(day<1 || day>28) throw new ArgumentOutOfRangeException("day", day, "Must be between 1 and 28 for December in non-leap years."); break; default: if(day<1 || day>28) throw new ArgumentOutOfRangeException("day", day, "Must be between 1 and 28 for normal months."); break; } } internal void CheckCalendarWeekRule(CalendarWeekRule rule) { if(!Enum.IsDefined(typeof(DayOfWeek), rule)) throw new ArgumentOutOfRangeException("day", "Valid values are between FirstDay and FirstFourDayWeek, inclusive."); } internal void CheckDayOfWeek(DayOfWeek day) { if(!Enum.IsDefined(typeof(DayOfWeek), day)) throw new ArgumentOutOfRangeException("day", "Valid values are between Sunday and Saturday, inclusive."); } #endregion } }