The Labs \ Source Viewer \ SSCLI \ System.Globalization \ PersianCalendar

  1. // ==++==
  2. //
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. //
  14. // ==--==
  15. namespace System.Globalization
  16. {
  17.     using System;
  18.    
  19.     ////////////////////////////////////////////////////////////////////////////
  20.     //
  21.     // Notes about PersianCalendar
  22.     //
  23.     ////////////////////////////////////////////////////////////////////////////
  24. /*
  25.     **  Calendar support range:
  26.     **      Calendar    Minimum    Maximum
  27.     **      ==========  ==========  ==========
  28.     **      Gregorian  0622/03/21  9999/12/31
  29.     **      Persian    0001/01/01  9378/10/10
  30.     */   
  31.    
  32.     [Serializable()]
  33.     public class PersianCalendar : Calendar
  34.     {
  35.        
  36.        
  37.         public static readonly int PersianEra = 1;
  38.        
  39.         internal const int DateCycle = 33;
  40.         internal const int DatePartYear = 0;
  41.         internal const int DatePartDayOfYear = 1;
  42.         internal const int DatePartMonth = 2;
  43.         internal const int DatePartDay = 3;
  44.        
  45.         static internal int[] DaysToMonth = {0, 31, 62, 93, 124, 155, 186, 216, 246, 276,
  46.         306, 336};
  47.         //Leap years, if Y%33 is 1,5,9,13,17,22,26,30
  48.         static internal int[] LeapYears33 = {0, 1, 0, 0, 0, 1, 0, 0, 0, 1,
  49.         0, 0, 0, 1, 0, 0, 0, 1, 0, 0,
  50.         0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
  51.             // 0 [1] 2 3 4
  52.             // [5] 6 7 8 [9]
  53.             // 10 11 12 [13] 14
  54.             // 15 16 [17] 18 19
  55.             // 20 21 [22] 23 24
  56.             // 25 [26] 27 28 29
  57.         1, 0, 0};
  58.         //[30] 31 32
  59.         internal const int LeapYearsPerCycle = 8;
  60.         internal const long GregorianOffset = 226894;
  61.         //GregorianCalendar.GetAbsoluteDate(622, 3, 21);
  62.         internal const long DaysPerCycle = DateCycle * 365 + LeapYearsPerCycle;
  63.        
  64.         //internal static Calendar m_defaultInstance;
  65.        
  66.         // DateTime.MaxValue = Persian calendar (year:9378, month: 10, day: 10).
  67.         internal const int MaxCalendarYear = 9378;
  68.         internal const int MaxCalendarMonth = 10;
  69.         internal const int MaxCalendarDay = 10;
  70.         // Persian calendar (year: 1, month: 1, day:1 ) = Gregorian (year: 622, month: 3, day: 21)
  71.         // This is the minimal Gregorian date that we support in the PersianCalendar.
  72.         static internal DateTime minDate = new DateTime(622, 3, 21);
  73.         static internal DateTime maxDate = DateTime.MaxValue;
  74.        
  75. /*=================================GetDefaultInstance==========================
  76.         **Action: Internal method to provide a default intance of PersianCalendar.  Used by NLS+ implementation
  77.         **      and other calendars.
  78.         **Returns:
  79.         **Arguments:
  80.         **Exceptions:
  81.         ============================================================================*/       
  82. /*
  83.         internal static Calendar GetDefaultInstance() {
  84.             if (m_defaultInstance == null) {
  85.                 m_defaultInstance = new PersianCalendar();
  86.             }
  87.             return (m_defaultInstance);
  88.         }
  89.         */       
  90.        
  91.        
  92.        
  93.         public override DateTime MinSupportedDateTime {
  94.             get { return (minDate); }
  95.         }
  96.        
  97.        
  98.         public override DateTime MaxSupportedDateTime {
  99.             get { return (maxDate); }
  100.         }
  101.        
  102.         // Return the type of the Persian calendar.
  103.         //
  104.        
  105.        
  106.         public override CalendarAlgorithmType AlgorithmType {
  107.             get { return CalendarAlgorithmType.SolarCalendar; }
  108.         }
  109.        
  110.         // Construct an instance of Persian calendar.
  111.        
  112.         public PersianCalendar()
  113.         {
  114.         }
  115.        
  116.        
  117.         internal override int BaseCalendarID {
  118.             get { return (CAL_GREGORIAN); }
  119.         }
  120.        
  121.         internal override int ID {
  122.             get { return (CAL_PERSIAN); }
  123.         }
  124.        
  125. /*=================================GetAbsoluteDatePersian==========================
  126.         **Action: Gets the Absolute date for the given Persian date.  The absolute date means
  127.         **      the number of days from January 1st, 1 A.D.
  128.         **Returns:
  129.         **Arguments:
  130.         **Exceptions:
  131.         ============================================================================*/       
  132.        
  133.         long GetAbsoluteDatePersian(int year, int month, int day)
  134.         {
  135.             if (year >= 1 && year <= MaxCalendarYear && month >= 1 && month <= 12) {
  136.                 return DaysUpToPersianYear(year) + DaysToMonth[month - 1] + day - 1;
  137.             }
  138.             throw new ArgumentOutOfRangeException(null, Environment.GetResourceString("ArgumentOutOfRange_BadYearMonthDay"));
  139.         }
  140.        
  141. /*=================================DaysUpToPersianYear==========================
  142.         **Action: Gets the total number of days (absolute date) up to the given Persian Year.
  143.         **      The absolute date means the number of days from January 1st, 1 A.D.
  144.         **Returns: Gets the total number of days (absolute date) up to the given Persian Year.
  145.         **Arguments: PersianYear year value in Persian calendar.
  146.         **Exceptions: None
  147.         **Notes:
  148.         ============================================================================*/       
  149.        
  150.         long DaysUpToPersianYear(int PersianYear)
  151.         {
  152.            
  153.             long NumDays;
  154.             // number of absolute days
  155.             int NumCycles;
  156.             // number of 33 year cycles
  157.             int NumYearsLeft;
  158.             // number of years into 33 year cycle
  159.             //
  160.             // Compute the number of 33 years cycles.
  161.             //
  162.             NumCycles = (PersianYear - 1) / DateCycle;
  163.            
  164.             //
  165.             // Compute the number of years left. This is the number of years
  166.             // into the 33 year cycle for the given year.
  167.             //
  168.             NumYearsLeft = (PersianYear - 1) % DateCycle;
  169.            
  170.             //
  171.             // Compute the number of absolute days up to the given year.
  172.             //
  173.             NumDays = NumCycles * DaysPerCycle + GregorianOffset;
  174.             while (NumYearsLeft > 0) {
  175.                 NumDays += 365;
  176.                 // Common year is 365 days, and leap year is 366 days.
  177.                 if (IsLeapYear(NumYearsLeft, CurrentEra)) {
  178.                     NumDays++;
  179.                 }
  180.                 NumYearsLeft--;
  181.             }
  182.            
  183.             //
  184.             // Return the number of absolute days.
  185.             //
  186.             return (NumDays);
  187.         }
  188.        
  189.        
  190.         internal void CheckTicksRange(long ticks)
  191.         {
  192.             if (ticks < minDate.Ticks || ticks > maxDate.Ticks) {
  193.                 throw new ArgumentOutOfRangeException("time", String.Format(CultureInfo.InvariantCulture, Environment.GetResourceString("ArgumentOutOfRange_CalendarRange"), minDate, maxDate));
  194.             }
  195.         }
  196.        
  197.         internal void CheckEraRange(int era)
  198.         {
  199.             if (era != CurrentEra && era != PersianEra) {
  200.                 throw new ArgumentOutOfRangeException("era", Environment.GetResourceString("ArgumentOutOfRange_InvalidEraValue"));
  201.             }
  202.         }
  203.        
  204.         internal void CheckYearRange(int year, int era)
  205.         {
  206.             CheckEraRange(era);
  207.             if (year < 1 || year > MaxCalendarYear) {
  208.                 throw new ArgumentOutOfRangeException("year", String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("ArgumentOutOfRange_Range"), 1, MaxCalendarYear));
  209.             }
  210.         }
  211.        
  212.         internal void CheckYearMonthRange(int year, int month, int era)
  213.         {
  214.             CheckYearRange(year, era);
  215.             if (year == MaxCalendarYear) {
  216.                 if (month > MaxCalendarMonth) {
  217.                     throw new ArgumentOutOfRangeException("month", String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("ArgumentOutOfRange_Range"), 1, MaxCalendarMonth));
  218.                 }
  219.             }
  220.            
  221.             if (month < 1 || month > 12) {
  222.                 throw new ArgumentOutOfRangeException("month", Environment.GetResourceString("ArgumentOutOfRange_Month"));
  223.             }
  224.         }
  225.        
  226. /*=================================GetDatePart==========================
  227.         **Action: Returns a given date part of this <i>DateTime</i>. This method is used
  228.         **      to compute the year, day-of-year, month, or day part.
  229.         **Returns:
  230.         **Arguments:
  231.         **Exceptions:  ArgumentException if part is incorrect.
  232.         **Notes:
  233.         **      First, we get the absolute date (the number of days from January 1st, 1 A.C) for the given ticks.
  234.         **      Use the formula (((AbsoluteDate - 226894) * 33) / (33 * 365 + 8)) + 1, we can a rough value for the Persian year.
  235.         **      In order to get the exact Persian year, we compare the exact absolute date for PersianYear and (PersianYear + 1).
  236.         **      From here, we can get the correct Persian year.
  237.         ============================================================================*/       
  238.        
  239.         internal int GetDatePart(long ticks, int part)
  240.         {
  241.             int PersianYear;
  242.             // Persian year
  243.             int PersianMonth;
  244.             // Persian month
  245.             int PersianDay;
  246.             // Persian day
  247.             long NumDays;
  248.             // The calculation buffer in number of days.
  249.             CheckTicksRange(ticks);
  250.            
  251.             //
  252.             // Get the absolute date. The absolute date is the number of days from January 1st, 1 A.D.
  253.             // 1/1/0001 is absolute date 1.
  254.             //
  255.             NumDays = ticks / GregorianCalendar.TicksPerDay + 1;
  256.            
  257.            
  258.             //
  259.             // Calculate the appromixate Persian Year from this magic formula.
  260.             //
  261.             PersianYear = (int)(((NumDays - GregorianOffset) * DateCycle) / DaysPerCycle) + 1;
  262.            
  263.             long daysToPersianYear = DaysUpToPersianYear(PersianYear);
  264.             // The absoulte date for PersianYear
  265.             long daysOfPersianYear = GetDaysInYear(PersianYear, CurrentEra);
  266.             // The number of days for (PersianYear+1) year.
  267.             if (NumDays < daysToPersianYear) {
  268.                 daysToPersianYear -= daysOfPersianYear;
  269.                 PersianYear--;
  270.             }
  271.             else if (NumDays == daysToPersianYear) {
  272.                 PersianYear--;
  273.                 daysToPersianYear -= GetDaysInYear(PersianYear, CurrentEra);
  274.             }
  275.             else {
  276.                 if (NumDays > daysToPersianYear + daysOfPersianYear) {
  277.                     daysToPersianYear += daysOfPersianYear;
  278.                     PersianYear++;
  279.                 }
  280.             }
  281.             if (part == DatePartYear) {
  282.                 return (PersianYear);
  283.             }
  284.            
  285.             //
  286.             // Calculate the Persian Month.
  287.             //
  288.            
  289.             NumDays -= daysToPersianYear;
  290.            
  291.             if (part == DatePartDayOfYear) {
  292.                 return ((int)NumDays);
  293.             }
  294.            
  295.             PersianMonth = 0;
  296.             while ((PersianMonth < 12) && (NumDays > DaysToMonth[PersianMonth])) {
  297.                 PersianMonth++;
  298.             }
  299.            
  300.             if (part == DatePartMonth) {
  301.                 return (PersianMonth);
  302.             }
  303.            
  304.             //
  305.             // Calculate the Persian Day.
  306.             //
  307.             PersianDay = (int)(NumDays - DaysToMonth[PersianMonth - 1]);
  308.            
  309.             if (part == DatePartDay) {
  310.                 return (PersianDay);
  311.             }
  312.             // Incorrect part value.
  313.             throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_DateTimeParsing"));
  314.         }
  315.        
  316.         // Returns the DateTime resulting from adding the given number of
  317.         // months to the specified DateTime. The result is computed by incrementing
  318.         // (or decrementing) the year and month parts of the specified DateTime by
  319.         // value months, and, if required, adjusting the day part of the
  320.         // resulting date downwards to the last day of the resulting month in the
  321.         // resulting year. The time-of-day part of the result is the same as the
  322.         // time-of-day part of the specified DateTime.
  323.         //
  324.         // In more precise terms, considering the specified DateTime to be of the
  325.         // form y / m / d + t, where y is the
  326.         // year, m is the month, d is the day, and t is the
  327.         // time-of-day, the result is y1 / m1 / d1 + t,
  328.         // where y1 and m1 are computed by adding value months
  329.         // to y and m, and d1 is the largest value less than
  330.         // or equal to d that denotes a valid day in month m1 of year
  331.         // y1.
  332.         //
  333.        
  334.        
  335.         public override DateTime AddMonths(DateTime time, int months)
  336.         {
  337.             if (months < -120000 || months > 120000) {
  338.                 throw new ArgumentOutOfRangeException("months", String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("ArgumentOutOfRange_Range"), -120000, 120000));
  339.             }
  340.             // Get the date in Persian calendar.
  341.             int y = GetDatePart(time.Ticks, DatePartYear);
  342.             int m = GetDatePart(time.Ticks, DatePartMonth);
  343.             int d = GetDatePart(time.Ticks, DatePartDay);
  344.             int i = m - 1 + months;
  345.             if (i >= 0) {
  346.                 m = i % 12 + 1;
  347.                 y = y + i / 12;
  348.             }
  349.             else {
  350.                 m = 12 + (i + 1) % 12;
  351.                 y = y + (i - 11) / 12;
  352.             }
  353.             int days = GetDaysInMonth(y, m);
  354.             if (d > days) {
  355.                 d = days;
  356.             }
  357.             long ticks = GetAbsoluteDatePersian(y, m, d) * TicksPerDay + time.Ticks % TicksPerDay;
  358.             Calendar.CheckAddResult(ticks, MinSupportedDateTime, MaxSupportedDateTime);
  359.             return (new DateTime(ticks));
  360.         }
  361.        
  362.         // Returns the DateTime resulting from adding the given number of
  363.         // years to the specified DateTime. The result is computed by incrementing
  364.         // (or decrementing) the year part of the specified DateTime by value
  365.         // years. If the month and day of the specified DateTime is 2/29, and if the
  366.         // resulting year is not a leap year, the month and day of the resulting
  367.         // DateTime becomes 2/28. Otherwise, the month, day, and time-of-day
  368.         // parts of the result are the same as those of the specified DateTime.
  369.         //
  370.        
  371.        
  372.         public override DateTime AddYears(DateTime time, int years)
  373.         {
  374.             return (AddMonths(time, years * 12));
  375.         }
  376.        
  377.         // Returns the day-of-month part of the specified DateTime. The returned
  378.         // value is an integer between 1 and 31.
  379.         //
  380.        
  381.        
  382.         public override int GetDayOfMonth(DateTime time)
  383.         {
  384.             return (GetDatePart(time.Ticks, DatePartDay));
  385.         }
  386.        
  387.         // Returns the day-of-week part of the specified DateTime. The returned value
  388.         // is an integer between 0 and 6, where 0 indicates Sunday, 1 indicates
  389.         // Monday, 2 indicates Tuesday, 3 indicates Wednesday, 4 indicates
  390.         // Thursday, 5 indicates Friday, and 6 indicates Saturday.
  391.         //
  392.        
  393.        
  394.         public override DayOfWeek GetDayOfWeek(DateTime time)
  395.         {
  396.             return ((DayOfWeek)((int)(time.Ticks / TicksPerDay + 1) % 7));
  397.         }
  398.        
  399.         // Returns the day-of-year part of the specified DateTime. The returned value
  400.         // is an integer between 1 and 366.
  401.         //
  402.        
  403.        
  404.         public override int GetDayOfYear(DateTime time)
  405.         {
  406.             return (GetDatePart(time.Ticks, DatePartDayOfYear));
  407.         }
  408.        
  409.         // Returns the number of days in the month given by the year and
  410.         // month arguments.
  411.         //
  412.        
  413.        
  414.         public override int GetDaysInMonth(int year, int month, int era)
  415.         {
  416.             CheckYearMonthRange(year, month, era);
  417.            
  418.             if ((month == MaxCalendarMonth) && (year == MaxCalendarYear)) {
  419.                 return MaxCalendarDay;
  420.             }
  421.            
  422.             if (month == 12) {
  423.                 // For the 12th month, leap year has 30 days, and common year has 29 days.
  424.                 return (IsLeapYear(year, CurrentEra) ? 30 : 29);
  425.             }
  426.             // Other months first 6 months are 31 and the reset are 30 days.
  427.             return ((month > 6) ? 30 : 31);
  428.         }
  429.        
  430.         // Returns the number of days in the year given by the year argument for the current era.
  431.         //
  432.        
  433.        
  434.         public override int GetDaysInYear(int year, int era)
  435.         {
  436.             CheckYearRange(year, era);
  437.             if (year == MaxCalendarYear) {
  438.                 return DaysToMonth[MaxCalendarMonth - 1] + MaxCalendarDay;
  439.             }
  440.             // Common years have 365 days. Leap years have 366 days.
  441.             return (IsLeapYear(year, CurrentEra) ? 366 : 365);
  442.         }
  443.        
  444.         // Returns the era for the specified DateTime value.
  445.        
  446.        
  447.         public override int GetEra(DateTime time)
  448.         {
  449.             CheckTicksRange(time.Ticks);
  450.             return (PersianEra);
  451.         }
  452.        
  453.        
  454.        
  455.         public override int[] Eras {
  456.             get { return (new int[] {PersianEra}); }
  457.         }
  458.        
  459.         // Returns the month part of the specified DateTime. The returned value is an
  460.         // integer between 1 and 12.
  461.         //
  462.        
  463.        
  464.         public override int GetMonth(DateTime time)
  465.         {
  466.             return (GetDatePart(time.Ticks, DatePartMonth));
  467.         }
  468.        
  469.         // Returns the number of months in the specified year and era.
  470.        
  471.        
  472.         public override int GetMonthsInYear(int year, int era)
  473.         {
  474.             CheckYearRange(year, era);
  475.             if (year == MaxCalendarYear) {
  476.                 return MaxCalendarMonth;
  477.             }
  478.             return (12);
  479.         }
  480.        
  481.         // Returns the year part of the specified DateTime. The returned value is an
  482.         // integer between 1 and MaxCalendarYear.
  483.         //
  484.        
  485.        
  486.         public override int GetYear(DateTime time)
  487.         {
  488.             return (GetDatePart(time.Ticks, DatePartYear));
  489.         }
  490.        
  491.         // Checks whether a given day in the specified era is a leap day. This method returns true if
  492.         // the date is a leap day, or false if not.
  493.         //
  494.        
  495.        
  496.         public override bool IsLeapDay(int year, int month, int day, int era)
  497.         {
  498.             // The year/month/era value checking is done in GetDaysInMonth().
  499.             int daysInMonth = GetDaysInMonth(year, month, era);
  500.             if (day < 1 || day > daysInMonth) {
  501.                 throw new ArgumentOutOfRangeException("day", String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("ArgumentOutOfRange_Day"), daysInMonth, month));
  502.             }
  503.             return (IsLeapYear(year, era) && month == 12 && day == 30);
  504.         }
  505.        
  506.         // Returns the leap month in a calendar year of the specified era. This method returns 0
  507.         // if this calendar does not have leap month, or this year is not a leap year.
  508.         //
  509.        
  510.        
  511.         public override int GetLeapMonth(int year, int era)
  512.         {
  513.             CheckYearRange(year, era);
  514.             return (0);
  515.         }
  516.        
  517.         // Checks whether a given month in the specified era is a leap month. This method returns true if
  518.         // month is a leap month, or false if not.
  519.         //
  520.        
  521.        
  522.         public override bool IsLeapMonth(int year, int month, int era)
  523.         {
  524.             CheckYearMonthRange(year, month, era);
  525.             return (false);
  526.         }
  527.        
  528.         // Checks whether a given year in the specified era is a leap year. This method returns true if
  529.         // year is a leap year, or false if not.
  530.         //
  531.        
  532.        
  533.         public override bool IsLeapYear(int year, int era)
  534.         {
  535.             CheckYearRange(year, era);
  536.             return (LeapYears33[year % DateCycle] == 1);
  537.         }
  538.        
  539.         // Returns the date and time converted to a DateTime value. Throws an exception if the n-tuple is invalid.
  540.         //
  541.        
  542.        
  543.         public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era)
  544.         {
  545.             // The year/month/era checking is done in GetDaysInMonth().
  546.             int daysInMonth = GetDaysInMonth(year, month, era);
  547.             if (day < 1 || day > daysInMonth) {
  548.                 BCLDebug.Log("year = " + year + ", month = " + month + ", day = " + day);
  549.                 throw new ArgumentOutOfRangeException("day", String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("ArgumentOutOfRange_Day"), daysInMonth, month));
  550.             }
  551.            
  552.             long lDate = GetAbsoluteDatePersian(year, month, day);
  553.            
  554.             if (lDate >= 0) {
  555.                 return (new DateTime(lDate * GregorianCalendar.TicksPerDay + TimeToTicks(hour, minute, second, millisecond)));
  556.             }
  557.             else {
  558.                 throw new ArgumentOutOfRangeException(null, Environment.GetResourceString("ArgumentOutOfRange_BadYearMonthDay"));
  559.             }
  560.         }
  561.        
  562.         private const int DEFAULT_TWO_DIGIT_YEAR_MAX = 1410;
  563.        
  564.        
  565.        
  566.         public override int TwoDigitYearMax {
  567.             get {
  568.                 if (twoDigitYearMax == -1) {
  569.                     twoDigitYearMax = GetSystemTwoDigitYearSetting(ID, DEFAULT_TWO_DIGIT_YEAR_MAX);
  570.                 }
  571.                 return (twoDigitYearMax);
  572.             }
  573.            
  574.             set {
  575.                 VerifyWritable();
  576.                 if (value < 99 || value > MaxCalendarYear) {
  577.                     throw new ArgumentOutOfRangeException("value", String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("ArgumentOutOfRange_Range"), 99, MaxCalendarYear));
  578.                 }
  579.                 twoDigitYearMax = value;
  580.             }
  581.         }
  582.        
  583.        
  584.        
  585.         public override int ToFourDigitYear(int year)
  586.         {
  587.             if (year < 100) {
  588.                 return (base.ToFourDigitYear(year));
  589.             }
  590.            
  591.             if (year > MaxCalendarYear) {
  592.                 throw new ArgumentOutOfRangeException("year", String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("ArgumentOutOfRange_Range"), 1, MaxCalendarYear));
  593.             }
  594.             return (year);
  595.         }
  596.     }
  597. }

Developer Fusion