// CalendricalCalculations.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.Collections; /// A class that provides mathematical functions. /// /// /// We are breaking the .Net /// naming conventions to be compatible to the "Calendrical Calculations" /// bool. /// /// static class CCMath { /// /// A static method which rounds a double value. /// /// The double value to round. /// The rounded double. public static double round(double x) { return System.Math.Floor(x+0.5); } /// /// A static method that computes the remainder of the division /// of two doubles. /// /// The double value which is divided. /// The divisor. /// The remainder as double value. public static double mod(double x, double y) { return x - y * System.Math.Floor(x/y); } /// /// The static method divides two integers. /// /// The integer x value. /// The integer y value. /// The qotient of x and y defined by floor(x/y). /// /// /// Please notify that the function is not compatible to the standard /// integer divide operation /. /// public static int div(int x, int y) { return (int)System.Math.Floor((double)x/(double)y); } /// /// The static method computes the remainder of two integers. /// /// The integer value which will be divided. /// The divisor integer value. /// The remainder as integer value. /// /// Please notify that the method is not compatible to the C# /// remainder operation %. /// public static int mod(int x, int y) { return x - y * div(x, y); } /// /// A static method that combines integer division and remainder /// computation. /// /// Remainder integer output value. /// /// Integer to be divided. /// Divisor integer value. /// The quotient as integer. /// /// public static int div_mod(out int remainder, int x, int y) { int d = div(x, y); remainder = x - y * d; return d; } /// /// A static method returning the sign of the argument. /// /// The double argument. /// An integer value: -1 for a negative argument; /// 0 for a zero argument, and 1 for a positive argument. /// public static int signum(double x) { if (x < 0.0) return -1; if (x == 0.0) return 0; return 1; } /// /// A static method returning the sign of the integer /// argument. /// /// The integer argument. /// An integer value: -1 for a negative argument; /// 0 for a zero argument, and 1 for a positive argument. /// public static int signum(int x) { if (x < 0) return -1; if (x == 0) return 0; return 1; } /// /// An adjusted remainder function as defined in "Calendrical /// Calculations". /// /// The double x argument. /// The double y argument, the divisor. /// A double value representing remainder; but instead 0.0 /// the divisor y is returned. /// public static double amod(double x, double y) { double d = mod(x, y); return (d == 0.0) ? y : d; } /// /// The adjusted remainder functions for integers as defined in /// "Calendrical Calculations". /// /// The integer argument to be divided. /// The integer divisor argument. /// The remainder as an integer; however instead 0 /// is the divisor y returned. /// public static int amod(int x, int y) { int i = mod(x, y); return (i == 0) ? y : i; } } /// The class implements methods to handle the fixed date value from /// the "Calendrical Calculations" books. /// /// /// /// For implementing the Calendar classes I used the algorithms from the /// book "Calendrical Calculations" by Nachum Dershowitz and Edward M. /// Rheingold, second reprint 1998. Trying to prevent the introduction of new /// bugs, I implemented their algorithms in the /// /// namespace and wrapped it in the calendar classes. /// /// /// The fixed day number is also known as R.D. - rata die. /// Midnight at the onset of Monday, /// January 1, year 1 (Gregorian) is R.D. 1. /// /// Here are all my references: /// /// /// [1] Nachum Dershowitz and Edward M. Rheingold: "Calendrical Calculations"; /// Cambridge University Press; second reprint 1998. /// /// /// [2] P. Kenneth Seidelmann (ed.): "Explanatory Supplement to the Astronomical /// Almanac"; University Science Books, Sausalito; 1992 /// /// /// [3] F. Richard Stephenson: "Historical Eclipses and Earth Rotation"; /// Cambridge University Press; 1997 /// /// /// static class CCFixed { /// The method computes the /// /// from a fixed day number. /// /// A integer representing the fixed day number. /// /// The representing /// the date. /// public static System.DateTime ToDateTime(int date) { long ticks = (date - 1) * System.TimeSpan.TicksPerDay; return new System.DateTime(ticks); } /// The method computes the /// /// from a fixed day number and time arguments. /// /// An integer representing the fixed day number. /// /// An integer argument specifying the hour. /// /// An integer argument specifying the minute. /// /// An integer argument giving the second. /// /// An double argument specifying /// the milliseconds. Notice that /// has 100 nanosecond resolution. /// /// The representing /// the date. /// public static System.DateTime ToDateTime(int date, int hour, int minute, int second, double milliseconds) { System.DateTime time = ToDateTime(date); time = time.AddHours(hour); time = time.AddMinutes(minute); time = time.AddSeconds(second); return time.AddMilliseconds(milliseconds); } /// /// A static method computing the fixed day number from a /// value. /// /// A /// value representing the date. /// /// The fixed day number as integer representing the date. /// public static int FromDateTime(System.DateTime time) { return 1 + (int)(time.Ticks / System.TimeSpan.TicksPerDay); } /// /// The static method computes the . /// /// An integer representing the fixed day number. /// /// The day of week. public static DayOfWeek day_of_week(int date) { return (DayOfWeek)CCMath.mod(date, 7); } /// /// The static method computes the date of a day of week on or before /// a particular date. /// /// An integer representing the date as /// fixed day number. /// /// An integer representing the day of the week, /// starting with 0 for sunday. /// /// The fixed day number of the day of week specified by k /// on or before the given date. /// public static int kday_on_or_before(int date, int k) { return date - (int)day_of_week(date-k); } /// /// The static method computes the date of a day of week on or after /// a particular date. /// /// An integer representing the date as /// fixed day number. /// /// An integer representing the day of the week, /// starting with 0 for sunday. /// /// The fixed day number of the day of week specified by k /// on or after the given date. /// public static int kday_on_or_after(int date, int k) { return kday_on_or_before(date+6, k); } /// /// The static method computes the date of a day of week that is /// nearest to a particular date. /// /// An integer representing the date as /// fixed day number. /// /// An integer representing the day of the week, /// starting with 0 for sunday. /// /// The fixed day number of the day of week neares to the /// given date. /// public static int kd_nearest(int date, int k) { return kday_on_or_before(date+3, k); } /// /// The static method computes the date of a day of week after /// a particular date. /// /// An integer representing the date as /// fixed day number. /// /// An integer representing the day of the week, /// starting with 0 for sunday. /// /// The fixed day number of the day of week specified by k /// after the given date. /// public static int kday_after(int date, int k) { return kday_on_or_before(date+7, k); } /// /// The static method computes the date of a day of week before /// a particular date. /// /// An integer representing the date as /// fixed day number. /// /// An integer representing the day of the week, /// starting with 0 for sunday. /// /// The fixed day number of the day of week specified by k /// before the given date. /// public static int kday_before(int date, int k) { return kday_on_or_before(date-1, k); } } // class CCFixed /// /// A class encapsulating the functions of the Gregorian calendar as static /// methods. /// /// /// /// This class is not compatible to /// . /// /// /// The fixed day number is also known as R.D. - rata die. /// Midnight at the onset of Monday, /// January 1, year 1 (Gregorian) is R.D. 1. /// /// /// static class CCGregorianCalendar { /// An integer defining the epoch of the Gregorian calendar /// as fixed day number. /// The epoch is January 3, 1 C.E. (Julian). const int epoch = 1; /// The enumeration defines the months of the Gregorian /// calendar. /// public enum Month { /// /// January. /// january = 1, /// /// February. /// february, /// /// March. /// march, /// /// April. /// april, /// /// May. /// may, /// /// June. /// june, /// /// July. /// july, /// /// August. /// august, /// /// September. /// september, /// /// October. /// october, /// /// November. /// november, /// /// December. /// december }; /// /// The method tells whether the year is a leap year. /// /// An integer representing the Gregorian year. /// /// A boolean which is true if is /// a leap year. /// public static bool is_leap_year(int year) { if (CCMath.mod(year, 4) != 0) return false; switch (CCMath.mod(year, 400)) { case 100: return false; case 200: return false; case 300: return false; } return true; } /// /// The method returns the fixed day number of the given Gregorian /// date. /// /// An integer representing the day of the month, /// counting from 1. /// /// An integer representing the month in the /// Gregorian year. /// /// An integer representing the Gregorian year. /// Non-positive values are allowed also. /// /// An integer value representing the fixed day number. /// public static int fixed_from_dmy(int day, int month, int year) { int k = epoch - 1; k += 365 * (year-1); k += CCMath.div(year-1, 4); k -= CCMath.div(year-1, 100); k += CCMath.div(year-1, 400); k += CCMath.div(367*month-362, 12); if (month > 2) { k += is_leap_year(year) ? -1 : -2; } k += day; return k; } /// /// The method computes the Gregorian year from a fixed day number. /// /// The fixed day number. /// /// An integer value giving the Gregorian year of the date. /// public static int year_from_fixed(int date) { int d = date - epoch; int n_400 = CCMath.div_mod(out d, d, 146097); int n_100 = CCMath.div_mod(out d, d, 36524); int n_4 = CCMath.div_mod(out d, d, 1461); int n_1 = CCMath.div(d, 365); int year = 400*n_400 + 100*n_100 + 4*n_4 + n_1; return (n_100 == 4 || n_1 == 4) ? year : year + 1; } /// /// The method computes the Gregorian year and month from a fixed day /// number. /// /// The output value giving the Gregorian month. /// /// The output value giving the Gregorian year. /// /// An integer value specifying the fixed day /// number. public static void my_from_fixed(out int month, out int year, int date) { year = year_from_fixed(date); int prior_days = date - fixed_from_dmy(1, (int)Month.january, year); int correction; if (date < fixed_from_dmy(1, (int)Month.march, year)) { correction = 0; } else if (is_leap_year(year)) { correction = 1; } else { correction = 2; } month = CCMath.div(12 * (prior_days + correction) + 373, 367); } /// /// The method computes the Gregorian year, month, and day from a /// fixed day number. /// /// The output value returning the day of the /// month. /// /// The output value giving the Gregorian month. /// /// The output value giving the Gregorian year. /// /// An integer value specifying the fixed day /// number. public static void dmy_from_fixed(out int day, out int month, out int year, int date) { my_from_fixed(out month, out year, date); day = date - fixed_from_dmy(1, month, year) + 1; } /// A method computing the Gregorian month from a fixed /// day number. /// /// An integer specifying the fixed day number. /// /// An integer value representing the Gregorian month. /// public static int month_from_fixed(int date) { int month, year; my_from_fixed(out month, out year, date); return month; } /// /// A method computing the day of the month from a fixed day number. /// /// An integer specifying the fixed day number. /// /// An integer value representing the day of the month. /// public static int day_from_fixed(int date) { int day, month, year; dmy_from_fixed(out day, out month, out year, date); return day; } /// /// The method computes the difference between two Gregorian dates. /// /// The integer parameter gives the day of month /// of the first date. /// /// The integer parameter gives the Gregorian /// month of the first date. /// /// The integer parameter gives the Gregorian /// year of the first date. /// /// The integer parameter gives the day of month /// of the second date. /// /// The integer parameter gives the Gregorian /// month of the second date. /// /// The integer parameter gives the Gregorian /// year of the second date. /// /// An integer giving the difference of days from the first /// the second date. /// public static int date_difference(int dayA, int monthA, int yearA, int dayB, int monthB, int yearB) { return fixed_from_dmy(dayB, monthB, yearB) - fixed_from_dmy(dayA, monthA, yearA); } /// /// The method computes the number of the day in the year from /// a Gregorian date. /// /// An integer representing the day of the month, /// counting from 1. /// /// An integer representing the month in the /// Gregorian year. /// /// An integer representing the Gregorian year. /// Non-positive values are allowed also. /// /// An integer value giving the number of the day in the /// Gregorian year, counting from 1. /// public static int day_number(int day, int month, int year) { return date_difference(31, (int)Month.december, year-1, day, month, year); } /// /// The method computes the days remaining in the given Gregorian /// year from a Gregorian date. /// /// An integer representing the day of the month, /// counting from 1. /// /// An integer representing the month in the /// Gregorian year. /// /// An integer representing the Gregorian year. /// Non-positive values are allowed also. /// /// An integer value giving the number of days remaining in /// the Gregorian year. /// public static int days_remaining(int day, int month, int year) { return date_difference(day, month, year, 31, (int)Month.december, year); } // Helper functions for the Gregorian calendars. /// /// Adds months to the 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 static System.DateTime AddMonths(System.DateTime time, int months) { int rd = CCFixed.FromDateTime(time); int day, month, year; dmy_from_fixed(out day, out month, out year, rd); month += months; year += CCMath.div_mod(out month, month, 12); int maxday = GetDaysInMonth (year, month); if (day > maxday) day = maxday; rd = fixed_from_dmy(day, month, year); System.DateTime t = CCFixed.ToDateTime(rd); return t.Add(time.TimeOfDay); } /// /// Adds years to the given date. /// /// The /// to which to add /// months. /// /// The number of years to add. /// A new value, that /// results from adding to the specified /// DateTime. public static System.DateTime AddYears(System.DateTime time, int years) { int rd = CCFixed.FromDateTime(time); int day, month, year; dmy_from_fixed(out day, out month, out year, rd); year += years; int maxday = GetDaysInMonth (year, month); if (day > maxday) day = maxday; rd = fixed_from_dmy(day, month, year); System.DateTime t = CCFixed.ToDateTime(rd); return t.Add(time.TimeOfDay); } /// /// Gets the of the month from . /// /// The /// that specifies a /// date. /// /// An integer giving the day of months, starting with 1. /// public static int GetDayOfMonth(System.DateTime time) { return day_from_fixed(CCFixed.FromDateTime(time)); } /// /// The method 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 static int GetDayOfYear(System.DateTime time) { int rd = CCFixed.FromDateTime(time); int year = year_from_fixed(rd); int rd1_1 = fixed_from_dmy(1, 1, year); return rd - rd1_1 + 1; } /// /// A method that gives the number of days of the specified /// month of 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. public static int GetDaysInMonth(int year, int month) { int rd1 = fixed_from_dmy(1, month, year); int rd2 = fixed_from_dmy(1, month+1, year); return rd2 - rd1; } /// /// The method gives the number of days in the specified year. /// /// An integer that gives the year. /// /// An integer that gives the number of days of the /// specified year. public static int GetDaysInYear(int year) { int rd1 = fixed_from_dmy(1, 1, year); int rd2 = fixed_from_dmy(1, 1, year+1); return rd2 - rd1; } /// /// The method gives the number of the month of the specified /// date. /// /// The /// that specifies a /// date. /// /// An integer representing the month, /// starting with 1. public static int GetMonth(System.DateTime time) { return month_from_fixed(CCFixed.FromDateTime(time)); } /// /// The method gives the number of the year of the specified /// date. /// /// The /// that specifies a /// date. /// /// An integer representing the year. /// public static int GetYear(System.DateTime time) { return year_from_fixed(CCFixed.FromDateTime(time)); } /// /// A virtual method that tells whether the given day /// is a leap day. /// /// An integer that specifies the year. /// /// An integer that specifies the month. /// /// An integer that specifies the day. /// /// A boolean that tells whether the given day is a leap /// day. /// public static bool IsLeapDay(int year, int month, int day) { return is_leap_year(year) && month == 2 && day == 29; } /// /// A method that creates the /// from the parameters. /// /// An integer that gives the year /// /// 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. /// public static System.DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int milliseconds) { return CCFixed.ToDateTime(fixed_from_dmy(day, month, year), hour, minute, second, milliseconds); } } // class CCGregorianCalendar /// /// A class encapsulating the functions of the Julian calendar as static /// methods. /// /// /// The algorithms don't support a year 0. Years before Common Era /// (B.C.E. or B.C.) are negative and years of Common Era (C.E. or A.D.) /// are positive. /// /// /// This class is not compatible to /// . /// /// /// static class CCJulianCalendar { /// An integer defining the epoch of the Julian calendar /// as fixed day number. /// The epoch is December 30, 0 (Gregorian). const int epoch = -1; // 30. 12. 0 Gregorian /// The enumeration defines the months of the Julian /// calendar. /// public enum Month { /// /// January. /// january = 1, /// /// February. /// february, /// /// March. /// march, /// /// April. /// april, /// /// May. /// may, /// /// June. /// june, /// /// July. /// july, /// /// August. /// august, /// /// September. /// september, /// /// October. /// october, /// /// November. /// november, /// /// December. /// december }; /// /// The method tells whether the year is a leap year. /// /// An integer representing the Julian year. /// /// A boolean which is true if is /// a leap year. /// public static bool is_leap_year(int year) { return CCMath.mod(year, 4) == (year > 0 ? 0 : 3); } /// /// The method returns the fixed day number of the given Julian /// date. /// /// An integer representing the day of the month, /// counting from 1. /// /// An integer representing the month in the /// Julian year. /// /// An integer representing the Julian year. /// Positive and Negative values are allowed. /// /// An integer value representing the fixed day number. /// public static int fixed_from_dmy(int day, int month, int year) { int y = year < 0 ? year+1 : year; int k = epoch - 1; k += 365 * (y-1); k += CCMath.div(y-1, 4); k += CCMath.div(367*month-362, 12); if (month > 2) { k += is_leap_year(year) ? -1 : -2; } k += day; return k; } /// /// The method computes the Julian year from a fixed day number. /// /// The fixed day number. /// /// An integer value giving the Julian year of the date. /// public static int year_from_fixed(int date) { int approx = CCMath.div(4*(date-epoch)+1464, 1461); return approx <= 0 ? approx - 1 : approx; } /// /// The method computes the Julian year and month from a fixed day /// number. /// /// The output value giving the Julian month. /// /// The output value giving the Julian year. /// /// An integer value specifying the fixed day /// number. public static void my_from_fixed(out int month, out int year, int date) { year = year_from_fixed(date); int prior_days = date - fixed_from_dmy(1, (int)Month.january, year); int correction; if (date < fixed_from_dmy(1, (int)Month.march, year)) { correction = 0; } else if (is_leap_year(year)) { correction = 1; } else { correction = 2; } month = CCMath.div(12 * (prior_days + correction) + 373, 367); } /// /// The method computes the Julian year, month, and day from a /// fixed day number. /// /// The output value returning the day of the /// month. /// /// The output value giving the Julian month. /// /// The output value giving the Julian year. /// /// An integer value specifying the fixed day /// number. public static void dmy_from_fixed(out int day, out int month, out int year, int date) { my_from_fixed(out month, out year, date); day = date - fixed_from_dmy(1, month, year) + 1; } /// A method computing the Julian month from a fixed /// day number. /// /// An integer specifying the fixed day number. /// /// An integer value representing the Julian month. /// public static int month_from_fixed(int date) { int month, year; my_from_fixed(out month, out year, date); return month; } /// /// A method computing the day of the month from a fixed day number. /// /// An integer specifying the fixed day number. /// /// An integer value representing the day of the month. /// public static int day_from_fixed(int date) { int day; int month; int year; dmy_from_fixed(out day, out month, out year, date); return day; } /// /// The method computes the difference between two Julian dates. /// /// The integer parameter gives the day of month /// of the first date. /// /// The integer parameter gives the Julian /// month of the first date. /// /// The integer parameter gives the Julian /// year of the first date. /// /// The integer parameter gives the day of month /// of the second date. /// /// The integer parameter gives the Julian /// month of the second date. /// /// The integer parameter gives the Julian /// year of the second date. /// /// An integer giving the difference of days from the first /// the second date. /// public static int date_difference(int dayA, int monthA, int yearA, int dayB, int monthB, int yearB) { return fixed_from_dmy(dayB, monthB, yearB) - fixed_from_dmy(dayA, monthA, yearA); } /// /// The method computes the number of the day in the year from /// a Julian date. /// /// An integer representing the day of the month, /// counting from 1. /// /// An integer representing the month in the /// Julian year. /// /// An integer representing the Julian year. /// Negative values are allowed also. /// /// An integer value giving the number of the day in the /// Julian year, counting from 1. /// public static int day_number(int day, int month, int year) { return date_difference(31, (int)Month.december, year-1, day, month, year); } /// /// The method computes the days remaining in the given Julian /// year from a Julian date. /// /// An integer representing the day of the month, /// counting from 1. /// /// An integer representing the month in the /// Julian year. /// /// An integer representing the Julian year. /// Negative values are allowed also. /// /// An integer value giving the number of days remaining in /// the Julian year. /// public static int days_remaining(int day, int month, int year) { return date_difference(day, month, year, 31, (int)Month.december, year); } } // class CCJulianCalendar /// /// A class encapsulating the functions of the Hebrew calendar as static /// methods. /// /// /// /// This class is not compatible to /// . /// /// /// static class CCHebrewCalendar { /// An integer defining the epoch of the Hebrew calendar /// as fixed day number. /// The epoch is October 10, 3761 B.C.E. (Julian). const int epoch = -1373427; /// The enumeration defines the months of the Gregorian /// calendar. /// /// /// The enumaration differs from .NET which defines Tishri as month 1. /// public enum Month { /// /// Nisan. /// nisan = 1, /// /// Iyyar. /// iyyar, /// /// Sivan. /// sivan, /// /// Tammuz. /// tammuz, /// /// Av. /// av, /// /// Elul. /// elul, /// /// Tishri. /// tishri, /// /// Heshvan. /// heshvan, /// /// Kislev. /// kislev, /// /// Teveth. /// teveth, /// /// Shevat. /// shevat, /// /// Adar. /// adar, /// /// Adar I. Only in years with Adar II. /// adar_I = 12, /// /// Adar II. Only in years wirh Adar I. /// adar_II = 13, }; /// /// The method tells whether the year is a leap year. /// /// An integer representing the Hebrew year. /// /// A boolean which is true if is /// a leap year. /// public static bool is_leap_year(int year) { return CCMath.mod(7*year+1, 19) < 7; } /// /// The Method gives the number of the last month in a year, which /// is equal with the number of month in a Hebrew year. /// /// An integer representing the Hebrew year. /// /// An integer giving the number of the last month of the /// Hebrew year, which is the same as the numbers of month in the /// year. /// public static int last_month_of_year(int year) { return is_leap_year(year) ? 13 : 12; } /// The method is a helper function. /// An integer specifying the Hebrew year. /// /// An integer representing the number of elapsed days /// until the Hebrew year. public static int elapsed_days(int year) { int months_elapsed = CCMath.div(235*year-234, 19); int r; int d = CCMath.div_mod(out r, months_elapsed, 1080); int parts_elapsed = 204 + 793 * r; int hours_elapsed = 11 + 12 * months_elapsed + 793 * d + CCMath.div(parts_elapsed, 1080); int day = 29*months_elapsed + CCMath.div(hours_elapsed, 24); if (CCMath.mod(3*(day+1), 7) < 3) { day += 1; } return day; } /// A method computing the delay of new year for the given /// Hebrew year. /// /// An integer that gives the Hebrew year. /// /// The new year delay in days of the given Hebrew year. /// public static int new_year_delay(int year) { int ny1 = elapsed_days(year); int ny2 = elapsed_days(year+1); if (ny2 - ny1 == 356) { return 2; } int ny0 = elapsed_days(year-1); if (ny1 - ny0 == 382) { return 1; } return 0; } /// /// The method computes the last day of month (nummer of days in a /// month) of the given Hebrew year. /// /// The Hebrew month, allowed value between /// One and Thirteen. /// /// An integer that gives the Hebrew year. /// /// The number of the last day of the month of the given /// Hebrew year, which gives automatically the number of days in the /// month. /// /// /// The exception is thrown if month not between One and Thirteen. /// public static int last_day_of_month(int month, int year) { if (month < 1 || month > 13) throw new System.ArgumentOutOfRangeException("month", "Month should be between One and Thirteen."); switch (month) { case 2: return 29; case 4: return 29; case 6: return 29; case 8: if (!long_heshvan(year)) return 29; break; case 9: if (short_kislev(year)) return 29; break; case 10: return 29; case 12: if (!is_leap_year(year)) return 29; break; case 13: return 29; } return 30; } /// /// The functions checks whether the month Heshvan is a long one /// in the given Hebrew year. /// /// An integer that gives the Hebrew year. /// /// A boolean value: true if there is a long Heshvan /// in the given Hebrew year; false otherwise. /// public static bool long_heshvan(int year) { return CCMath.mod(days_in_year(year), 10) == 5; } /// /// The functions checks whether the month Kislev is a short one /// in the given Hebrew year. /// /// An integer that gives the Hebrew year. /// /// A boolean value: true if there is a short Kislev /// in the given Hebrew year; false otherwise. /// public static bool short_kislev(int year) { return CCMath.mod(days_in_year(year), 10) == 3; } /// /// The functions gives the number of days in the specified Hebrew /// year. /// /// An integer that gives the Hebrew year. /// /// The days of the Hebrew year as integer. /// public static int days_in_year(int year) { return fixed_from_dmy(1, 7, year+1) - fixed_from_dmy(1, 7, year); } /// /// The method returns the fixed day number of the given Hebrew /// date. /// /// An integer representing the day of the month, /// counting from 1. /// /// An integer representing the month in the /// Hebrew year. /// /// An integer representing the Hebrew year. /// Non-positive values are allowed also. /// /// An integer value representing the fixed day number. /// public static int fixed_from_dmy(int day, int month, int year) { int m; int k = epoch-1; k += elapsed_days(year); k += new_year_delay(year); if (month < 7) { int l = last_month_of_year(year); for (m = 7; m <= l; m++) { k += last_day_of_month(m, year); } for (m = 1; m < month; m++) { k += last_day_of_month(m, year); } } else { for (m = 7; m < month; m++) { k += last_day_of_month(m, year); } } k += day; return k; } /// /// The method computes the Hebrew year from a fixed day number. /// /// The fixed day number. /// /// An integer value giving the Hebrew year of the date. /// public static int year_from_fixed(int date) { int approx = (int)System.Math.Floor( ((double)(date - epoch))/(35975351.0/98496.0)); int y; for (y = approx; date >= fixed_from_dmy(1, 7, y); y++) {} return y-1; } /// /// The method computes the Hebrew year and month from a fixed day /// number. /// /// The output value giving the Hebrew month. /// /// The output value giving the Hebrew year. /// /// An integer value specifying the fixed day /// number. public static void my_from_fixed(out int month, out int year, int date) { year = year_from_fixed(date); int start = date < fixed_from_dmy(1, 1, year) ? 7 : 1; for (month = start; date > fixed_from_dmy(last_day_of_month(month, year), month, year); month++) {} } /// /// The method computes the Hebrew year, month, and day from a /// fixed day number. /// /// The output value returning the day of the /// month. /// /// The output value giving the Hebrew month. /// /// The output value giving the Hebrew year. /// /// An integer value specifying the fixed day /// number. public static void dmy_from_fixed(out int day, out int month, out int year, int date) { my_from_fixed(out month, out year, date); day = date - fixed_from_dmy(1, month, year) + 1; } /// A method computing the Hebrew month from a fixed /// day number. /// /// An integer specifying the fixed day number. /// /// An integer value representing the Hebrew month. /// public static int month_from_fixed(int date) { int month, year; my_from_fixed(out month, out year, date); return month; } /// /// A method computing the day of the month from a fixed day number. /// /// An integer specifying the fixed day number. /// /// An integer value representing the day of the month. /// public static int day_from_fixed(int date) { int day, month, year; dmy_from_fixed(out day, out month, out year, date); return day; } /// /// The method computes the difference between two Hebrew dates. /// /// The integer parameter gives the day of month /// of the first date. /// /// The integer parameter gives the Hebrew /// month of the first date. /// /// The integer parameter gives the Hebrew /// year of the first date. /// /// The integer parameter gives the day of month /// of the second date. /// /// The integer parameter gives the Hebrew /// month of the second date. /// /// The integer parameter gives the Hebrew /// year of the second date. /// /// An integer giving the difference of days from the first /// the second date. /// public static int date_difference(int dayA, int monthA, int yearA, int dayB, int monthB, int yearB) { return fixed_from_dmy(dayB, monthB, yearB) - fixed_from_dmy(dayA, monthA, yearA); } /// /// The method computes the number of the day in the year from /// a Hebrew date. /// /// An integer representing the day of the month, /// counting from 1. /// /// An integer representing the month in the /// Hebrew year. /// /// An integer representing the Hebrew year. /// /// An integer value giving the number of the day in the /// Hebrew year, counting from 1. /// public static int day_number(int day, int month, int year) { return date_difference(1, 7, year, day, month, year) + 1; } /// /// The method computes the days remaining in the given Hebrew /// year from a Hebrew date. /// /// An integer representing the day of the month, /// counting from 1. /// /// An integer representing the month in the /// Hebrew year. /// /// An integer representing the Hebrew year. /// /// An integer value giving the number of days remaining in /// the Hebrew year. /// public static int days_remaining(int day, int month, int year) { return date_difference(day, month, year, 1, 7, year+1)-1; } } // class HebrewCalendar /// /// A class encapsulating the functions of the Islamic calendar as static /// methods. /// /// /// There is no difference here in using Hijri or Islamic calendar. /// /// The epoch of the Islamic calendar isn't fixed, because we cannot /// surely say today, when the crescent of the new moon has been observed /// around the July 16, 622 C.E. Julian. Even today the start and end of /// the month Ramadan is defined by religous authorities. So the calendar /// can be offset by two days. /// /// /// We don't support the offset here, however we changed the epoch from /// "Calendrical Calculations" to value, that .Net seems to be using. /// /// /// This class is not compatible to /// . /// /// /// static class CCHijriCalendar { /// An integer defining the epoch of the Gregorian calendar /// as fixed day number. /// /// /// The epoch is given as 16 July 622 C.E. Julian (R.D. 227015) /// in Calendrical Calculations, the approximate date of /// the emigration of /// Muhammed to Medina. However there is no way to determine today /// the observation of the crescent of the new moon in July 622 C.E. /// (Julian). So there is some variability in the epoch. /// Religous authorities determine the epoch by observing the /// crescent of the new moon for the month Ramadan, so there might /// be an offsets by two days of the epoch. /// /// Windows /// supports an AddHijriDate parameter in the registry to adapt /// for it. It seems that the .NET implementation of /// HijriCalendar uses an epoch of 227014, so we use it here. The /// ArgumentOutOfRangeException gives July, 18 622 as epoch, /// which is 227014 supporting our theory. /// /// const int epoch = 227014; /// The enumeration defines the months of the Islamic /// calendar. /// public enum Month { /// /// Muharram. /// muharram = 1, /// /// Safar. /// safar, /// /// Rabi I. /// rabi_I, /// /// Rabi II. /// rabi_II, /// /// Jumada I. /// jumada_I, /// /// Jumada II. /// jumada_II, /// /// Rajab. /// rajab, /// /// Shaban. /// shaban, /// /// Ramadan. /// ramadan, /// /// Shawwal. /// shawwal, /// /// Dhu Al-Quada. /// dhu_al_quada, /// /// Dhu Al-Hijja. /// dhu_al_hijja, }; /// /// The method tells whether the year is a leap year. /// /// An integer representing the Islamic year. /// /// A boolean which is true if is /// a leap year. /// public static bool is_leap_year(int year) { return CCMath.mod(14+11*year, 30) < 11; } /// /// The method returns the fixed day number of the given Islamic /// date. /// /// An integer representing the day of the month, /// counting from 1. /// /// An integer representing the month in the /// Islamic year. /// /// An integer representing the Islamic year. /// Non-positive values are allowed also. /// /// An integer value representing the fixed day number. /// public static int fixed_from_dmy(int day, int month, int year) { int k = epoch - 1; k += 354 * (year-1); k += CCMath.div(3+11*year, 30); k += (int)System.Math.Ceiling(29.5 * (double)(month-1)); k += day; return k; } /// /// The method computes the Islamic year from a fixed day number. /// /// The fixed day number. /// /// An integer value giving the Islamic year of the date. /// public static int year_from_fixed(int date) { return CCMath.div(30*(date-epoch)+10646, 10631); } /// /// The method computes the Islamic year and month from a fixed day /// number. /// /// The output value giving the Islamic month. /// /// The output value giving the Islamic year. /// /// An integer value specifying the fixed day /// number. public static void my_from_fixed(out int month, out int year, int date) { year = year_from_fixed(date); int m = 1+(int)System.Math.Ceiling( ((double)(date-29-fixed_from_dmy(1,1,year)))/29.5); month = m < 12 ? m : 12; } /// /// The method computes the Islamic year, month, and day from a /// fixed day number. /// /// The output value returning the day of the /// month. /// /// The output value giving the Islamic month. /// /// The output value giving the Islamic year. /// /// An integer value specifying the fixed day /// number. public static void dmy_from_fixed(out int day, out int month, out int year, int date) { my_from_fixed(out month, out year, date); day = date - fixed_from_dmy(1, month, year) + 1; } /// A method computing the Islamic month from a fixed /// day number. /// /// An integer specifying the fixed day number. /// /// An integer value representing the Islamic month. /// public static int month_from_fixed(int date) { int month, year; my_from_fixed(out month, out year, date); return month; } /// /// A method computing the day of the month from a fixed day number. /// /// An integer specifying the fixed day number. /// /// An integer value representing the day of the month. /// public static int day_from_fixed(int date) { int day; int month; int year; dmy_from_fixed(out day, out month, out year, date); return day; } /// /// The method computes the difference between two Islamic dates. /// /// The integer parameter gives the day of month /// of the first date. /// /// The integer parameter gives the Islamic /// month of the first date. /// /// The integer parameter gives the Islamic /// year of the first date. /// /// The integer parameter gives the day of month /// of the second date. /// /// The integer parameter gives the Islamic /// month of the second date. /// /// The integer parameter gives the Islamic /// year of the second date. /// /// An integer giving the difference of days from the first /// the second date. /// public static int date_difference(int dayA, int monthA, int yearA, int dayB, int monthB, int yearB) { return fixed_from_dmy(dayB, monthB, yearB) - fixed_from_dmy(dayA, monthA, yearA); } /// /// The method computes the number of the day in the year from /// a Islamic date. /// /// An integer representing the day of the month, /// counting from 1. /// /// An integer representing the month in the /// Islamic year. /// /// An integer representing the Islamic year. /// /// An integer value giving the number of the day in the /// Islamic year, counting from 1. /// public static int day_number(int day, int month, int year) { return date_difference(31, 12, year-1, day, month, year); } /// /// The method computes the days remaining in the given Islamic /// year from a Islamic date. /// /// An integer representing the day of the month, /// counting from 1. /// /// An integer representing the month in the /// Islamic year. /// /// An integer representing the Islamic year. /// Non-positive values are allowed also. /// /// An integer value giving the number of days remaining in /// the Islamic year. /// public static int days_remaining(int day, int month, int year) { return date_difference(day, month, year,31, 12, year); } } // class CCHijriCalendar internal class CCEastAsianLunisolarCalendar { public static int fixed_from_dmy (int day, int month, int year) { /* int k = epoch - 1; k += 354 * (year - 1); k += CCMath.div (3+11*year, 30); k += (int) Math.Ceiling(29.53 * (double)(month-1)); k += day; return k; */ throw new Exception ("fixed_from_dmy"); } public static int year_from_fixed (int date) { throw new Exception ("year_from_fixed"); } public static void my_from_fixed(out int month, out int year, int date) { /* year = year_from_fixed (date); int m = 1+(int)System.Math.Ceiling( ((double)(date-29-fixed_from_dmy(1,1,year)))/29.5); month = m < 12 ? m : 12; */ throw new Exception ("my_from_fixed"); } public static void dmy_from_fixed(out int day, out int month, out int year, int date) { /* my_from_fixed (out month, out year, date); day = date - fixed_from_dmy (1, month, year) + 1; */ throw new Exception ("dmy_from_fixed"); } public static DateTime AddMonths (DateTime date, int months) { throw new Exception ("AddMonths"); } public static DateTime AddYears (DateTime date, int years) { throw new Exception ("AddYears"); } public static int GetDayOfMonth (DateTime date) { throw new Exception ("GetDayOfMonth"); } public static int GetDayOfYear (DateTime date) { throw new Exception ("GetDayOfYear"); } public static int GetDaysInMonth (int gyear, int month) { throw new Exception ("GetDaysInMonth"); } public static int GetDaysInYear (int year) { throw new Exception ("GetDaysInYear"); } public static int GetMonth (DateTime date) { throw new Exception ("GetMonth"); } public static bool IsLeapMonth (int gyear, int month) { int goldenNumber = gyear % 19; bool chu = false; bool leap = false; double s = 0; for (int y = 0; y < goldenNumber; y++) { for (int l = 0, m = 1; m <= month; m++) { if (leap) { l += 30; leap = false; if (y == goldenNumber && m == month) return true; } else { l += chu ? 30 : 29; chu = !chu; s += 30.44; if (s - l > 29) leap = true; } } } return false; } public static bool IsLeapYear (int gyear) { // FIXME: it is still wrong. int d = gyear % 19; switch (d) { case 0: case 3: case 6: case 9: case 11: case 14: case 17: return true; default: return false; } /* int goldenNumber = (gyear - 1900) % 19; int epact = 29; bool leap = false; while (goldenNumber-- >= 0) { epact += 11; leap = epact > 30; if (epact > 30) epact -= 30; } return leap; */ } public static DateTime ToDateTime (int year, int month, int day, int hour, int minute, int second, int millisecond) { throw new Exception ("ToDateTime"); } } /// /// A class that supports the Gregorian based calendars with other eras /// (e.g. ). /// [System.Serializable] internal class CCGregorianEraHandler { /// /// A struct that represents a single era. /// [System.Serializable] struct Era { /// /// The integer number identifying the era. /// private int _nr; /// /// A get-only property that gives the era integer number. /// public int Nr { get { return _nr; } } /// This integer gives the first day of the era as /// fixed day number. /// private int _start; // inclusive /// /// This integer gives the gregorian year of the /// value. /// private int _gregorianYearStart; /// /// This integer gives the last day of the era as fixed day /// number. /// private int _end; // inclusive /// /// This integer gives the largest year number of this era. /// private int _maxYear; /// /// This constructor creates the era structure. /// /// The integer number of the era. /// /// The fixed day number defining the /// first day of the era. /// /// The fixed day number that defines the /// last day of the era. /// public Era(int nr, int start, int end) { if (nr == 0) throw new System.ArgumentException( "Era number shouldn't be zero."); _nr = nr; if (start > end) { throw new System.ArgumentException( "Era should start before end."); } _start = start; _end = end; _gregorianYearStart = CCGregorianCalendar.year_from_fixed(_start); int gregorianYearEnd = CCGregorianCalendar.year_from_fixed(_end); _maxYear = gregorianYearEnd - _gregorianYearStart + 1; } /// /// This method computes the Gregorian year from the year /// of this era. /// /// An integer giving the year in the /// era. /// /// /// The Gregorian year as integer. /// /// /// The exception is thrown if the year isn't valid in this /// era. /// public int GregorianYear(int year) { if (year < 1 || year > _maxYear) { System.IO.StringWriter sw = new System.IO.StringWriter(); sw.Write( "Valid Values are between " + "{0} and {1}, inclusive.", 1, _maxYear); throw new System.ArgumentOutOfRangeException( "year", sw.ToString()); } return year + _gregorianYearStart - 1; } /// /// This function checks wether the given fixed day number is /// ion the time span of the era. /// /// An integer giving the fixed day /// number. /// /// A boolean: true if the argument is in the time /// span of the era. /// public bool Covers(int date) { return _start <= date && date <= _end; } /// /// This function returns the year of the era and sets /// the era in an output parameter. /// /// An output parameter returning the /// era number. /// /// An integer giving the fixed day /// number. /// /// An integer giving the year of the era. /// /// /// The exception is thrown if date is outside of the time /// span of the era. /// public int EraYear(out int era, int date) { if (!Covers(date)) throw new System.ArgumentOutOfRangeException( "date", "Time was out of Era range."); int gregorianYear = CCGregorianCalendar.year_from_fixed(date); era = _nr; return gregorianYear - _gregorianYearStart + 1; } } // struct Era /// /// A private member storing the eras in a /// . /// private SortedList _Eras; /// /// The property returns the era numbers as an array of integers. /// public int[] Eras { get { int[] a = new int[_Eras.Count]; for (int i = 0; i < _Eras.Count; i++) { Era e = (Era)_Eras.GetByIndex(i); a[i] = e.Nr; } return a; } } /// /// Constructor. /// public CCGregorianEraHandler() { _Eras = new SortedList(); } /// /// Method adds an era to the GregorianEraHandler instance. /// /// The integer number of the era. /// /// The fixed day number defining the /// first day of the era. /// /// The fixed day number that defines the /// last day of the era. /// public void appendEra(int nr, int rd_start, int rd_end) { Era era = new Era(nr, rd_start, rd_end); _Eras[(System.Object)nr] = era; } /// /// Method adds a yet not-ended era to the GregorianEraHandler /// instance. /// /// The integer number of the era. /// /// The fixed day number defining the /// first day of the era. /// public void appendEra(int nr, int rd_start) { appendEra(nr, rd_start, CCFixed.FromDateTime(DateTime.MaxValue)); } /// /// This method computes the Gregorian year from the year /// of the given era. /// /// An integer giving the year in the /// era. /// /// An integer giving the era number. /// /// /// The Gregorian year as integer. /// /// /// The exception is thrown if the year isn't valid in this /// era. /// public int GregorianYear(int year, int era) { Era e = (Era)_Eras[(System.Object)era]; return e.GregorianYear(year); } /// /// This function returns the year of the era and sets /// the era in an output parameter. /// /// An output parameter returning the /// era number. /// /// An integer giving the fixed day /// number. /// /// An integer giving the year of the era. /// /// /// The exception is thrown if the fixed day number is outside of the /// time spans of all eras. /// public int EraYear(out int era, int date) { IList list = _Eras.GetValueList(); foreach (Era e in list) { if (e.Covers(date)) return e.EraYear(out era, date); } throw new System.ArgumentOutOfRangeException("date", "Time value was out of era range."); } /// /// The method checks whether a given /// is covered by any era. /// /// A /// giving the date and time. /// /// /// The exception is thrown if the argument isn't inside the time /// span of any era. /// public void CheckDateTime(System.DateTime time) { int date = CCFixed.FromDateTime(time); if (!ValidDate(date)) throw new System.ArgumentOutOfRangeException("time", "Time value was out of era range."); } /// /// The method tests whether a given /// fixed day number is covered by any era. /// /// An integer representing the fixed day number. /// /// A boolean is returned: true if the argument is inside /// the time span of one era; false otherwise. /// public bool ValidDate(int date) { IList list = _Eras.GetValueList(); foreach (Era e in list) { if (e.Covers(date)) return true; } return false; } /// /// The method tests, whether the era number does exist. /// /// An integer giving the era number. /// /// A boole value: True if the era number does exist; /// false otherwise. /// public bool ValidEra(int era) { return _Eras.Contains((System.Object)era); } } // class CCGregorianEraHandler // FIXME: remove this class. It should be identical to CCGregorianEraHandler [System.Serializable] internal class CCEastAsianLunisolarEraHandler { [Serializable] struct Era { private int _nr; // era index public int Nr { get { return _nr; } } private int _start; // inclusive private int _gregorianYearStart; private int _end; // inclusive private int _maxYear; public Era (int nr, int start, int end) { if (nr == 0) throw new ArgumentException ("Era number shouldn't be zero."); _nr = nr; if (start > end) throw new ArgumentException ("Era should start before end."); _start = start; _end = end; _gregorianYearStart = CCGregorianCalendar.year_from_fixed (_start); int gregorianYearEnd = CCGregorianCalendar.year_from_fixed (_end); _maxYear = gregorianYearEnd - _gregorianYearStart + 1; } public int GregorianYear (int year) { if (year < 1 || year > _maxYear) throw new ArgumentOutOfRangeException ("year", String.Format ("Valid Values are between {0} and {1}, inclusive.", 1, _maxYear)); return year + _gregorianYearStart - 1; } public bool Covers (int date) { return _start <= date && date <= _end; } public int EraYear (out int era, int date) { if (!Covers (date)) throw new ArgumentOutOfRangeException ("date", "Time was out of Era range."); int gregorianYear = CCGregorianCalendar.year_from_fixed (date); era = _nr; return gregorianYear - _gregorianYearStart + 1; } } private SortedList _Eras; public int [] Eras { get { int[] a = new int [_Eras.Count]; for (int i = 0; i < _Eras.Count; i++) { Era e = (Era) _Eras.GetByIndex (i); a[i] = e.Nr; } return a; } } public CCEastAsianLunisolarEraHandler () { _Eras = new SortedList (); } public void appendEra (int nr, int rd_start, int rd_end) { Era era = new Era (nr, rd_start, rd_end); _Eras [nr] = era; } public void appendEra (int nr, int rd_start) { appendEra (nr, rd_start, CCFixed.FromDateTime (DateTime.MaxValue)); } public int GregorianYear (int year, int era) { Era e = (Era) _Eras [era]; return e.GregorianYear (year); } public int EraYear (out int era, int date) { foreach (Era e in _Eras.Values) if (e.Covers (date)) return e.EraYear (out era, date); throw new ArgumentOutOfRangeException ("date", "Time value was out of era range."); } public void CheckDateTime (DateTime time) { int date = CCFixed.FromDateTime (time); if (!ValidDate (date)) throw new ArgumentOutOfRangeException ("time", "Time value was out of era range."); } public bool ValidDate (int date) { foreach (Era e in _Eras.Values) { if (e.Covers (date)) return true; } return false; } public bool ValidEra (int era) { return _Eras.Contains (era); } } } // namespace System.Globalization