< Summary

Line coverage
0%
Covered lines: 0
Uncovered lines: 190
Coverable lines: 190
Total lines: 547
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 104
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Private.CoreLib/src/System/Globalization/HijriCalendar.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4namespace System.Globalization
 5{
 6    /// <remarks>
 7    /// Rules for the Hijri calendar:
 8    ///   - The Hijri calendar is a strictly Lunar calendar.
 9    ///   - Days begin at sunset.
 10    ///   - Islamic Year 1 (Muharram 1, 1 A.H.) is equivalent to absolute date
 11    ///       227015 (Friday, July 16, 622 C.E. - Julian).
 12    ///   - Leap Years occur in the 2, 5, 7, 10, 13, 16, 18, 21, 24, 26, &amp; 29th
 13    ///       years of a 30-year cycle.  Year = leap iff ((11y+14) mod 30 &lt; 11).
 14    ///   - There are 12 months which contain alternately 30 and 29 days.
 15    ///   - The 12th month, Dhu al-Hijjah, contains 30 days instead of 29 days
 16    ///       in a leap year.
 17    ///   - Common years have 354 days.  Leap years have 355 days.
 18    ///   - There are 10,631 days in a 30-year cycle.
 19    ///   - The Islamic months are:
 20    ///       1.  Muharram   (30 days)     7.  Rajab          (30 days)
 21    ///       2.  Safar      (29 days)     8.  Sha'ban        (29 days)
 22    ///       3.  Rabi I     (30 days)     9.  Ramadan        (30 days)
 23    ///       4.  Rabi II    (29 days)     10. Shawwal        (29 days)
 24    ///       5.  Jumada I   (30 days)     11. Dhu al-Qada    (30 days)
 25    ///       6.  Jumada II  (29 days)     12. Dhu al-Hijjah  (29 days) {30}
 26    ///
 27    /// NOTENOTE
 28    ///     The calculation of the HijriCalendar is based on the absolute date.  And the
 29    ///     absolute date means the number of days from January 1st, 1 A.D.
 30    ///     Therefore, we do not support the days before the January 1st, 1 A.D.
 31    ///
 32    /// Calendar support range:
 33    ///     Calendar    Minimum     Maximum
 34    ///     ==========  ==========  ==========
 35    ///     Gregorian   0622/07/18   9999/12/31
 36    ///     Hijri       0001/01/01   9666/04/03
 37    /// </remarks>
 38
 39    public partial class HijriCalendar : Calendar
 40    {
 041        public static readonly int HijriEra = 1;
 42
 43        private const int DatePartYear = 0;
 44        private const int DatePartDayOfYear = 1;
 45        private const int DatePartMonth = 2;
 46        private const int DatePartDay = 3;
 47
 48        private const int MinAdvancedHijri = -2;
 49        private const int MaxAdvancedHijri = 2;
 50
 051        private static ReadOnlySpan<int> HijriMonthDays => [0, 30, 59, 89, 118, 148, 177, 207, 236, 266, 295, 325, 355];
 52
 053        private int _hijriAdvance = int.MinValue;
 54
 55        // DateTime.MaxValue = Hijri calendar (year:9666, month: 4, day: 3).
 56        private const int MaxCalendarYear = 9666;
 57        private const int MaxCalendarMonth = 4;
 58
 59        // Hijri calendar (year: 1, month: 1, day:1 ) = Gregorian (year: 622, month: 7, day: 18)
 60        // This is the minimal Gregorian date that we support in the HijriCalendar.
 061        private static readonly DateTime s_calendarMinValue = new DateTime(622, 7, 18);
 062        private static readonly DateTime s_calendarMaxValue = DateTime.MaxValue;
 63
 064        public override DateTime MinSupportedDateTime => s_calendarMinValue;
 65
 066        public override DateTime MaxSupportedDateTime => s_calendarMaxValue;
 67
 068        public override CalendarAlgorithmType AlgorithmType => CalendarAlgorithmType.LunarCalendar;
 69
 070        public HijriCalendar()
 71        {
 072        }
 73
 074        internal override CalendarId ID => CalendarId.HIJRI;
 75
 76        protected override int DaysInYearBeforeMinSupportedYear =>
 77            // the year before the 1st year of the cycle would have been the 30th year
 78            // of the previous cycle which is not a leap year. Common years have 354 days.
 079            354;
 80
 81        private long GetAbsoluteDateHijri(int y, int m, int d)
 82        {
 083            return (long)(DaysUpToHijriYear(y) + HijriMonthDays[m - 1] + d - 1 - HijriAdjustment);
 84        }
 85
 86        private long DaysUpToHijriYear(int HijriYear)
 87        {
 88            // Compute the number of years up to the current 30 year cycle.
 089            int numYear30 = ((HijriYear - 1) / 30) * 30;
 90
 91            // Compute the number of years left.  This is the number of years
 92            // into the 30 year cycle for the given year.
 093            int numYearsLeft = HijriYear - numYear30 - 1;
 94
 95            // Compute the number of absolute days up to the given year.
 096            long numDays = ((numYear30 * 10631L) / 30L) + 227013L;
 097            while (numYearsLeft > 0)
 98            {
 99                // Common year is 354 days, and leap year is 355 days.
 0100                numDays += 354 + (IsLeapYear(numYearsLeft, CurrentEra) ? 1 : 0);
 0101                numYearsLeft--;
 102            }
 103
 0104            return numDays;
 105        }
 106
 107        public int HijriAdjustment
 108        {
 109            get
 110            {
 0111                if (_hijriAdvance == int.MinValue)
 112                {
 113                    // Never been set before.  Use the system value from registry.
 0114                    _hijriAdvance = GetHijriDateAdjustment();
 115                }
 116
 0117                return _hijriAdvance;
 118            }
 119
 120            set
 121            {
 0122                if (value < MinAdvancedHijri || value > MaxAdvancedHijri)
 123                {
 0124                    throw new ArgumentOutOfRangeException(
 0125                        nameof(value),
 0126                        value,
 0127                        SR.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, MinAdvancedHijri, MaxAdvancedHijri));
 128                }
 129
 0130                VerifyWritable();
 0131                _hijriAdvance = value;
 0132            }
 133        }
 134
 135        internal static void CheckTicksRange(long ticks)
 136        {
 0137            if (ticks < s_calendarMinValue.Ticks || ticks > s_calendarMaxValue.Ticks)
 138            {
 0139                throw new ArgumentOutOfRangeException(
 0140                    "time",
 0141                    ticks,
 0142                    SR.Format(
 0143                        CultureInfo.InvariantCulture,
 0144                        SR.ArgumentOutOfRange_CalendarRange,
 0145                        s_calendarMinValue,
 0146                        s_calendarMaxValue));
 147            }
 0148        }
 149
 150        internal static void CheckEraRange(int era)
 151        {
 0152            if (era != CurrentEra && era != HijriEra)
 153            {
 0154                throw new ArgumentOutOfRangeException(nameof(era), era, SR.ArgumentOutOfRange_InvalidEraValue);
 155            }
 0156        }
 157
 158        internal static void CheckYearRange(int year, int era)
 159        {
 0160            CheckEraRange(era);
 0161            if (year < 1 || year > MaxCalendarYear)
 162            {
 0163                throw new ArgumentOutOfRangeException(
 0164                            nameof(year),
 0165                            SR.Format(SR.ArgumentOutOfRange_Range, 1, MaxCalendarYear));
 166            }
 0167        }
 168
 169        internal static void CheckYearMonthRange(int year, int month, int era)
 170        {
 0171            CheckYearRange(year, era);
 0172            if (year == MaxCalendarYear)
 173            {
 0174                if (month > MaxCalendarMonth)
 175                {
 0176                    throw new ArgumentOutOfRangeException(
 0177                        nameof(month),
 0178                        month,
 0179                        SR.Format(SR.ArgumentOutOfRange_Range, 1, MaxCalendarMonth));
 180                }
 181            }
 182
 0183            if (month < 1 || month > 12)
 184            {
 0185                ThrowHelper.ThrowArgumentOutOfRange_Month(month);
 186            }
 0187        }
 188
 189        /// <summary>
 190        /// First, we get the absolute date (the number of days from January 1st, 1 A.C) for the given ticks.
 191        /// Use the formula (((AbsoluteDate - 227013) * 30) / 10631) + 1, we can a rough value for the Hijri year.
 192        /// In order to get the exact Hijri year, we compare the exact absolute date for HijriYear and (HijriYear + 1).
 193        /// From here, we can get the correct Hijri year.
 194        /// </summary>
 195        private int GetDatePart(long ticks, int part)
 196        {
 0197            CheckTicksRange(ticks);
 198
 199            // Get the absolute date. The absolute date is the number of days from January 1st, 1 A.D.
 200            // 1/1/0001 is absolute date 1.
 0201            long numDays = ticks / TimeSpan.TicksPerDay + 1;
 202
 203            // See how much we need to backup or advance
 0204            numDays += HijriAdjustment;
 205
 206            // Calculate the appromixate Hijri Year from this magic formula.
 0207            int hijriYear = (int)(((numDays - 227013) * 30) / 10631) + 1;
 208
 0209            long daysToHijriYear = DaysUpToHijriYear(hijriYear);            // The absolute date for HijriYear
 0210            long daysOfHijriYear = GetDaysInYear(hijriYear, CurrentEra);    // The number of days for (HijriYear+1) year
 211
 0212            if (numDays < daysToHijriYear)
 213            {
 0214                daysToHijriYear -= daysOfHijriYear;
 0215                hijriYear--;
 216            }
 0217            else if (numDays == daysToHijriYear)
 218            {
 0219                hijriYear--;
 0220                daysToHijriYear -= GetDaysInYear(hijriYear, CurrentEra);
 221            }
 222            else
 223            {
 0224                if (numDays > daysToHijriYear + daysOfHijriYear)
 225                {
 0226                    daysToHijriYear += daysOfHijriYear;
 0227                    hijriYear++;
 228                }
 229            }
 0230            if (part == DatePartYear)
 231            {
 0232                return hijriYear;
 233            }
 234
 235            // Calculate the Hijri Month.
 0236            int hijriMonth = 1;
 0237            numDays -= daysToHijriYear;
 238
 0239            if (part == DatePartDayOfYear)
 240            {
 0241                return (int)numDays;
 242            }
 243
 0244            while ((hijriMonth <= 12) && (numDays > HijriMonthDays[hijriMonth - 1]))
 245            {
 0246                hijriMonth++;
 247            }
 0248            hijriMonth--;
 249
 0250            if (part == DatePartMonth)
 251            {
 0252                return hijriMonth;
 253            }
 254
 255            // Calculate the Hijri Day.
 0256            int hijriDay = (int)(numDays - HijriMonthDays[hijriMonth - 1]);
 257
 0258            if (part == DatePartDay)
 259            {
 0260                return hijriDay;
 261            }
 262
 263            // Incorrect part value.
 0264            throw new InvalidOperationException(SR.InvalidOperation_DateTimeParsing);
 265        }
 266
 267        public override DateTime AddMonths(DateTime time, int months)
 268        {
 0269            if (months < -120000 || months > 120000)
 270            {
 0271                throw new ArgumentOutOfRangeException(
 0272                    nameof(months),
 0273                    months,
 0274                    SR.Format(SR.ArgumentOutOfRange_Range, -120000, 120000));
 275            }
 276
 277            // Get the date in Hijri calendar.
 0278            int y = GetDatePart(time.Ticks, DatePartYear);
 0279            int m = GetDatePart(time.Ticks, DatePartMonth);
 0280            int d = GetDatePart(time.Ticks, DatePartDay);
 0281            int i = m - 1 + months;
 0282            if (i >= 0)
 283            {
 0284                m = i % 12 + 1;
 0285                y += i / 12;
 286            }
 287            else
 288            {
 0289                m = 12 + (i + 1) % 12;
 0290                y += (i - 11) / 12;
 291            }
 292
 0293            int days = GetDaysInMonth(y, m);
 0294            if (d > days)
 295            {
 0296                d = days;
 297            }
 298
 0299            long ticks = GetAbsoluteDateHijri(y, m, d) * TimeSpan.TicksPerDay + (time.Ticks % TimeSpan.TicksPerDay);
 0300            CheckAddResult(ticks, MinSupportedDateTime, MaxSupportedDateTime);
 0301            return new DateTime(ticks);
 302        }
 303
 304        public override DateTime AddYears(DateTime time, int years)
 305        {
 0306            return AddMonths(time, years * 12);
 307        }
 308
 309        public override int GetDayOfMonth(DateTime time)
 310        {
 0311            return GetDatePart(time.Ticks, DatePartDay);
 312        }
 313
 0314        public override DayOfWeek GetDayOfWeek(DateTime time) => time.DayOfWeek;
 315
 316        public override int GetDayOfYear(DateTime time)
 317        {
 0318            return GetDatePart(time.Ticks, DatePartDayOfYear);
 319        }
 320
 321        public override int GetDaysInMonth(int year, int month, int era)
 322        {
 0323            CheckYearMonthRange(year, month, era);
 0324            if (month == 12)
 325            {
 326                // For the 12th month, leap year has 30 days, and common year has 29 days.
 0327                return IsLeapYear(year, CurrentEra) ? 30 : 29;
 328            }
 329
 330            // Other months contain 30 and 29 days alternatively.  The 1st month has 30 days.
 0331            return ((month % 2) == 1) ? 30 : 29;
 332        }
 333
 334        public override int GetDaysInYear(int year, int era)
 335        {
 0336            CheckYearRange(year, era);
 337            // Common years have 354 days. Leap years have 355 days.
 0338            return IsLeapYear(year, CurrentEra) ? 355 : 354;
 339        }
 340
 341        public override int GetEra(DateTime time)
 342        {
 0343            CheckTicksRange(time.Ticks);
 0344            return HijriEra;
 345        }
 346
 0347        public override int[] Eras => [HijriEra];
 348
 349        public override int GetMonth(DateTime time)
 350        {
 0351            return GetDatePart(time.Ticks, DatePartMonth);
 352        }
 353
 354        public override int GetMonthsInYear(int year, int era)
 355        {
 0356            CheckYearRange(year, era);
 0357            return 12;
 358        }
 359
 360        public override int GetYear(DateTime time)
 361        {
 0362            return GetDatePart(time.Ticks, DatePartYear);
 363        }
 364
 365        public override bool IsLeapDay(int year, int month, int day, int era)
 366        {
 367            // The year/month/era value checking is done in GetDaysInMonth().
 0368            int daysInMonth = GetDaysInMonth(year, month, era);
 0369            if (day < 1 || day > daysInMonth)
 370            {
 0371                throw new ArgumentOutOfRangeException(
 0372                    nameof(day),
 0373                    day,
 0374                    SR.Format(SR.ArgumentOutOfRange_Day, daysInMonth, month));
 375            }
 376
 0377            return IsLeapYear(year, era) && month == 12 && day == 30;
 378        }
 379
 380        public override int GetLeapMonth(int year, int era)
 381        {
 0382            CheckYearRange(year, era);
 0383            return 0;
 384        }
 385
 386        public override bool IsLeapMonth(int year, int month, int era)
 387        {
 0388            CheckYearMonthRange(year, month, era);
 0389            return false;
 390        }
 391
 392        public override bool IsLeapYear(int year, int era)
 393        {
 0394            CheckYearRange(year, era);
 0395            return (((year * 11) + 14) % 30) < 11;
 396        }
 397
 398        public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisec
 399        {
 400            // The year/month/era checking is done in GetDaysInMonth().
 0401            int daysInMonth = GetDaysInMonth(year, month, era);
 0402            if (day < 1 || day > daysInMonth)
 403            {
 0404                throw new ArgumentOutOfRangeException(
 0405                    nameof(day),
 0406                    day,
 0407                    SR.Format(SR.ArgumentOutOfRange_Day, daysInMonth, month));
 408            }
 409
 0410            long lDate = GetAbsoluteDateHijri(year, month, day);
 0411            if (lDate < 0)
 412            {
 0413                throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadYearMonthDay);
 414            }
 415
 0416            return new DateTime(lDate * TimeSpan.TicksPerDay + TimeToTicks(hour, minute, second, millisecond));
 417        }
 418
 419        private const int DefaultTwoDigitYearMax = 1451;
 420
 421        public override int TwoDigitYearMax
 422        {
 423            get
 424            {
 0425                if (_twoDigitYearMax == -1)
 426                {
 0427                    _twoDigitYearMax = GetSystemTwoDigitYearSetting(ID, DefaultTwoDigitYearMax);
 428                }
 429
 0430                return _twoDigitYearMax;
 431            }
 432            set
 433            {
 0434                VerifyWritable();
 0435                if (value < 99 || value > MaxCalendarYear)
 436                {
 0437                    throw new ArgumentOutOfRangeException(
 0438                        nameof(value),
 0439                        value,
 0440                        SR.Format(SR.ArgumentOutOfRange_Range, 99, MaxCalendarYear));
 441                }
 442
 0443                _twoDigitYearMax = value;
 0444            }
 445        }
 446
 447        public override int ToFourDigitYear(int year)
 448        {
 0449            ArgumentOutOfRangeException.ThrowIfNegative(year);
 450
 0451            if (year < 100)
 452            {
 0453                return base.ToFourDigitYear(year);
 454            }
 455
 0456            if (year > MaxCalendarYear)
 457            {
 0458                throw new ArgumentOutOfRangeException(
 0459                    nameof(year),
 0460                    year,
 0461                    SR.Format(SR.ArgumentOutOfRange_Range, 1, MaxCalendarYear));
 462            }
 0463            return year;
 464        }
 465    }
 466}
 467

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Private.CoreLib/src/System/Globalization/HijriCalendar.Windows.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using Internal.Win32;
 5
 6namespace System.Globalization
 7{
 8    public partial class HijriCalendar : Calendar
 9    {
 10        private int GetHijriDateAdjustment()
 11        {
 012            if (_hijriAdvance == int.MinValue)
 13            {
 14                // Never been set before.  Use the system value from registry.
 015                _hijriAdvance = GetAdvanceHijriDate();
 16            }
 017            return _hijriAdvance;
 18        }
 19
 20        private const string InternationalRegKey = "Control Panel\\International";
 21        private const string HijriAdvanceRegKeyEntry = "AddHijriDate";
 22
 23        /*=================================GetAdvanceHijriDate==========================
 24        **Action: Gets the AddHijriDate value from the registry.
 25        **Returns:
 26        **Arguments:    None.
 27        **Exceptions:
 28        **Note:
 29        **  The HijriCalendar has a user-overridable calculation.  That is, use can set a value from the control
 30        **  panel, so that the calculation of the Hijri Calendar can move ahead or backwards from -2 to +2 days.
 31        **
 32        **  The valid string values in the registry are:
 33        **      "AddHijriDate-2"  =>  Add -2 days to the current calculated Hijri date.
 34        **      "AddHijriDate"    =>  Add -1 day to the current calculated Hijri date.
 35        **      ""              =>  Add 0 day to the current calculated Hijri date.
 36        **      "AddHijriDate+1"  =>  Add +1 days to the current calculated Hijri date.
 37        **      "AddHijriDate+2"  =>  Add +2 days to the current calculated Hijri date.
 38        ============================================================================*/
 39        private static int GetAdvanceHijriDate()
 40        {
 041            using (RegistryKey? key = Registry.CurrentUser.OpenSubKey(InternationalRegKey))
 42            {
 43                // Abort if we didn't find anything
 044                if (key == null)
 45                {
 046                    return 0;
 47                }
 48
 049                object? value = key.GetValue(HijriAdvanceRegKeyEntry);
 050                if (value == null)
 51                {
 052                    return 0;
 53                }
 54
 055                int hijriAdvance = 0;
 056                string? str = value.ToString();
 057                if (str is not null && str.StartsWith(HijriAdvanceRegKeyEntry, StringComparison.OrdinalIgnoreCase))
 58                {
 059                    if (str.Length == HijriAdvanceRegKeyEntry.Length)
 60                    {
 061                        hijriAdvance = -1;
 62                    }
 63                    else
 64                    {
 065                        if (int.TryParse(str.AsSpan(HijriAdvanceRegKeyEntry.Length), CultureInfo.InvariantCulture, out i
 066                            (advance >= MinAdvancedHijri) &&
 067                            (advance <= MaxAdvancedHijri))
 68                        {
 069                            hijriAdvance = advance;
 70                        }
 71                        // If parsing fails due to garbage from registry just ignore it.
 72                        // hijriAdvance = 0 because of declaraction assignment up above.
 73                    }
 74                }
 075                return hijriAdvance;
 76            }
 077        }
 78    }
 79}
 80