diff options
Diffstat (limited to 'src/mscorlib/shared/System/Globalization')
34 files changed, 24631 insertions, 0 deletions
diff --git a/src/mscorlib/shared/System/Globalization/CalendarAlgorithmType.cs b/src/mscorlib/shared/System/Globalization/CalendarAlgorithmType.cs new file mode 100644 index 0000000000..4ddc307abf --- /dev/null +++ b/src/mscorlib/shared/System/Globalization/CalendarAlgorithmType.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Globalization +{ + public enum CalendarAlgorithmType + { + Unknown = 0, // This is the default value to return in the Calendar base class. + SolarCalendar = 1, // Solar-base calendar, such as GregorianCalendar, jaoaneseCalendar, JulianCalendar, etc. + // Solar calendars are based on the solar year and seasons. + LunarCalendar = 2, // Lunar-based calendar, such as Hijri and UmAlQuraCalendar. + // Lunar calendars are based on the path of the moon. The seasons are not accurately represented. + LunisolarCalendar = 3 // Lunisolar-based calendar which use leap month rule, such as HebrewCalendar and Asian Lunisolar calendars. + // Lunisolar calendars are based on the cycle of the moon, but consider the seasons as a secondary consideration, + // so they align with the seasons as well as lunar events. + } +} diff --git a/src/mscorlib/shared/System/Globalization/CalendarWeekRule.cs b/src/mscorlib/shared/System/Globalization/CalendarWeekRule.cs new file mode 100644 index 0000000000..b381b5c544 --- /dev/null +++ b/src/mscorlib/shared/System/Globalization/CalendarWeekRule.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Globalization +{ + [Serializable] + public enum CalendarWeekRule + { + FirstDay = 0, // Week 1 begins on the first day of the year + + FirstFullWeek = 1, // Week 1 begins on first FirstDayOfWeek not before the first day of the year + + FirstFourDayWeek = 2 // Week 1 begins on first FirstDayOfWeek such that FirstDayOfWeek+3 is not before the first day of the year + }; +} diff --git a/src/mscorlib/shared/System/Globalization/CalendricalCalculationsHelper.cs b/src/mscorlib/shared/System/Globalization/CalendricalCalculationsHelper.cs new file mode 100644 index 0000000000..7de75d6aee --- /dev/null +++ b/src/mscorlib/shared/System/Globalization/CalendricalCalculationsHelper.cs @@ -0,0 +1,412 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; + +namespace System.Globalization +{ + internal class CalendricalCalculationsHelper + { + private const double FullCircleOfArc = 360.0; // 360.0; + private const int HalfCircleOfArc = 180; + private const double TwelveHours = 0.5; // half a day + private const double Noon2000Jan01 = 730120.5; + internal const double MeanTropicalYearInDays = 365.242189; + private const double MeanSpeedOfSun = MeanTropicalYearInDays / FullCircleOfArc; + private const double LongitudeSpring = 0.0; + private const double TwoDegreesAfterSpring = 2.0; + private const int SecondsPerDay = 24 * 60 * 60; // 24 hours * 60 minutes * 60 seconds + + private const int DaysInUniformLengthCentury = 36525; + private const int SecondsPerMinute = 60; + private const int MinutesPerDegree = 60; + + private static readonly long s_startOf1810 = GetNumberOfDays(new DateTime(1810, 1, 1)); + private static readonly long s_startOf1900Century = GetNumberOfDays(new DateTime(1900, 1, 1)); + + private static readonly double[] s_coefficients1900to1987 = new double[] { -0.00002, 0.000297, 0.025184, -0.181133, 0.553040, -0.861938, 0.677066, -0.212591 }; + private static readonly double[] s_coefficients1800to1899 = new double[] { -0.000009, 0.003844, 0.083563, 0.865736, 4.867575, 15.845535, 31.332267, 38.291999, 28.316289, 11.636204, 2.043794 }; + private static readonly double[] s_coefficients1700to1799 = new double[] { 8.118780842, -0.005092142, 0.003336121, -0.0000266484 }; + private static readonly double[] s_coefficients1620to1699 = new double[] { 196.58333, -4.0675, 0.0219167 }; + private static readonly double[] s_lambdaCoefficients = new double[] { 280.46645, 36000.76983, 0.0003032 }; + private static readonly double[] s_anomalyCoefficients = new double[] { 357.52910, 35999.05030, -0.0001559, -0.00000048 }; + private static readonly double[] s_eccentricityCoefficients = new double[] { 0.016708617, -0.000042037, -0.0000001236 }; + private static readonly double[] s_coefficients = new double[] { Angle(23, 26, 21.448), Angle(0, 0, -46.8150), Angle(0, 0, -0.00059), Angle(0, 0, 0.001813) }; + private static readonly double[] s_coefficientsA = new double[] { 124.90, -1934.134, 0.002063 }; + private static readonly double[] s_coefficientsB = new double[] { 201.11, 72001.5377, 0.00057 }; + + private static double RadiansFromDegrees(double degree) + { + return degree * Math.PI / 180; + } + + private static double SinOfDegree(double degree) + { + return Math.Sin(RadiansFromDegrees(degree)); + } + + private static double CosOfDegree(double degree) + { + return Math.Cos(RadiansFromDegrees(degree)); + } + private static double TanOfDegree(double degree) + { + return Math.Tan(RadiansFromDegrees(degree)); + } + + public static double Angle(int degrees, int minutes, double seconds) + { + return ((seconds / SecondsPerMinute + minutes) / MinutesPerDegree) + degrees; + } + + private static double Obliquity(double julianCenturies) + { + return PolynomialSum(s_coefficients, julianCenturies); + } + + internal static long GetNumberOfDays(DateTime date) + { + return date.Ticks / GregorianCalendar.TicksPerDay; + } + + private static int GetGregorianYear(double numberOfDays) + { + return new DateTime(Math.Min((long)(Math.Floor(numberOfDays) * GregorianCalendar.TicksPerDay), DateTime.MaxValue.Ticks)).Year; + } + + private enum CorrectionAlgorithm + { + Default, + Year1988to2019, + Year1900to1987, + Year1800to1899, + Year1700to1799, + Year1620to1699 + } + + private struct EphemerisCorrectionAlgorithmMap + { + public EphemerisCorrectionAlgorithmMap(int year, CorrectionAlgorithm algorithm) + { + _lowestYear = year; + _algorithm = algorithm; + } + + internal int _lowestYear; + internal CorrectionAlgorithm _algorithm; + }; + + private static readonly EphemerisCorrectionAlgorithmMap[] s_ephemerisCorrectionTable = new EphemerisCorrectionAlgorithmMap[] + { + // lowest year that starts algorithm, algorithm to use + new EphemerisCorrectionAlgorithmMap(2020, CorrectionAlgorithm.Default), + new EphemerisCorrectionAlgorithmMap(1988, CorrectionAlgorithm.Year1988to2019), + new EphemerisCorrectionAlgorithmMap(1900, CorrectionAlgorithm.Year1900to1987), + new EphemerisCorrectionAlgorithmMap(1800, CorrectionAlgorithm.Year1800to1899), + new EphemerisCorrectionAlgorithmMap(1700, CorrectionAlgorithm.Year1700to1799), + new EphemerisCorrectionAlgorithmMap(1620, CorrectionAlgorithm.Year1620to1699), + new EphemerisCorrectionAlgorithmMap(int.MinValue, CorrectionAlgorithm.Default) // default must be last + }; + + private static double Reminder(double divisor, double dividend) + { + double whole = Math.Floor(divisor / dividend); + return divisor - (dividend * whole); + } + + private static double NormalizeLongitude(double longitude) + { + longitude = Reminder(longitude, FullCircleOfArc); + if (longitude < 0) + { + longitude += FullCircleOfArc; + } + return longitude; + } + + public static double AsDayFraction(double longitude) + { + return longitude / FullCircleOfArc; + } + + private static double PolynomialSum(double[] coefficients, double indeterminate) + { + double sum = coefficients[0]; + double indeterminateRaised = 1; + for (int i = 1; i < coefficients.Length; i++) + { + indeterminateRaised *= indeterminate; + sum += (coefficients[i] * indeterminateRaised); + } + + return sum; + } + + private static double CenturiesFrom1900(int gregorianYear) + { + long july1stOfYear = GetNumberOfDays(new DateTime(gregorianYear, 7, 1)); + return (double)(july1stOfYear - s_startOf1900Century) / DaysInUniformLengthCentury; + } + + // the following formulas defines a polynomial function which gives us the amount that the earth is slowing down for specific year ranges + private static double DefaultEphemerisCorrection(int gregorianYear) + { + Debug.Assert(gregorianYear < 1620 || 2020 <= gregorianYear); + long january1stOfYear = GetNumberOfDays(new DateTime(gregorianYear, 1, 1)); + double daysSinceStartOf1810 = january1stOfYear - s_startOf1810; + double x = TwelveHours + daysSinceStartOf1810; + return ((Math.Pow(x, 2) / 41048480) - 15) / SecondsPerDay; + } + + private static double EphemerisCorrection1988to2019(int gregorianYear) + { + Debug.Assert(1988 <= gregorianYear && gregorianYear <= 2019); + return (double)(gregorianYear - 1933) / SecondsPerDay; + } + + private static double EphemerisCorrection1900to1987(int gregorianYear) + { + Debug.Assert(1900 <= gregorianYear && gregorianYear <= 1987); + double centuriesFrom1900 = CenturiesFrom1900(gregorianYear); + return PolynomialSum(s_coefficients1900to1987, centuriesFrom1900); + } + + private static double EphemerisCorrection1800to1899(int gregorianYear) + { + Debug.Assert(1800 <= gregorianYear && gregorianYear <= 1899); + double centuriesFrom1900 = CenturiesFrom1900(gregorianYear); + return PolynomialSum(s_coefficients1800to1899, centuriesFrom1900); + } + + private static double EphemerisCorrection1700to1799(int gregorianYear) + { + Debug.Assert(1700 <= gregorianYear && gregorianYear <= 1799); + double yearsSince1700 = gregorianYear - 1700; + return PolynomialSum(s_coefficients1700to1799, yearsSince1700) / SecondsPerDay; + } + + private static double EphemerisCorrection1620to1699(int gregorianYear) + { + Debug.Assert(1620 <= gregorianYear && gregorianYear <= 1699); + double yearsSince1600 = gregorianYear - 1600; + return PolynomialSum(s_coefficients1620to1699, yearsSince1600) / SecondsPerDay; + } + + // ephemeris-correction: correction to account for the slowing down of the rotation of the earth + private static double EphemerisCorrection(double time) + { + int year = GetGregorianYear(time); + foreach (EphemerisCorrectionAlgorithmMap map in s_ephemerisCorrectionTable) + { + if (map._lowestYear <= year) + { + switch (map._algorithm) + { + case CorrectionAlgorithm.Default: return DefaultEphemerisCorrection(year); + case CorrectionAlgorithm.Year1988to2019: return EphemerisCorrection1988to2019(year); + case CorrectionAlgorithm.Year1900to1987: return EphemerisCorrection1900to1987(year); + case CorrectionAlgorithm.Year1800to1899: return EphemerisCorrection1800to1899(year); + case CorrectionAlgorithm.Year1700to1799: return EphemerisCorrection1700to1799(year); + case CorrectionAlgorithm.Year1620to1699: return EphemerisCorrection1620to1699(year); + } + + break; // break the loop and assert eventually + } + } + + Debug.Assert(false, "Not expected to come here"); + return DefaultEphemerisCorrection(year); + } + + public static double JulianCenturies(double moment) + { + double dynamicalMoment = moment + EphemerisCorrection(moment); + return (dynamicalMoment - Noon2000Jan01) / DaysInUniformLengthCentury; + } + + private static bool IsNegative(double value) + { + return Math.Sign(value) == -1; + } + + private static double CopySign(double value, double sign) + { + return (IsNegative(value) == IsNegative(sign)) ? value : -value; + } + + // equation-of-time; approximate the difference between apparent solar time and mean solar time + // formal definition is EOT = GHA - GMHA + // GHA is the Greenwich Hour Angle of the apparent (actual) Sun + // GMHA is the Greenwich Mean Hour Angle of the mean (fictitious) Sun + // http://www.esrl.noaa.gov/gmd/grad/solcalc/ + // http://en.wikipedia.org/wiki/Equation_of_time + private static double EquationOfTime(double time) + { + double julianCenturies = JulianCenturies(time); + double lambda = PolynomialSum(s_lambdaCoefficients, julianCenturies); + double anomaly = PolynomialSum(s_anomalyCoefficients, julianCenturies); + double eccentricity = PolynomialSum(s_eccentricityCoefficients, julianCenturies); + + double epsilon = Obliquity(julianCenturies); + double tanHalfEpsilon = TanOfDegree(epsilon / 2); + double y = tanHalfEpsilon * tanHalfEpsilon; + + double dividend = ((y * SinOfDegree(2 * lambda)) + - (2 * eccentricity * SinOfDegree(anomaly)) + + (4 * eccentricity * y * SinOfDegree(anomaly) * CosOfDegree(2 * lambda)) + - (0.5 * Math.Pow(y, 2) * SinOfDegree(4 * lambda)) + - (1.25 * Math.Pow(eccentricity, 2) * SinOfDegree(2 * anomaly))); + double divisor = 2 * Math.PI; + double equation = dividend / divisor; + + // approximation of equation of time is not valid for dates that are many millennia in the past or future + // thus limited to a half day + return CopySign(Math.Min(Math.Abs(equation), TwelveHours), equation); + } + + private static double AsLocalTime(double apparentMidday, double longitude) + { + // slightly inaccurate since equation of time takes mean time not apparent time as its argument, but the difference is negligible + double universalTime = apparentMidday - AsDayFraction(longitude); + return apparentMidday - EquationOfTime(universalTime); + } + + // midday + public static double Midday(double date, double longitude) + { + return AsLocalTime(date + TwelveHours, longitude) - AsDayFraction(longitude); + } + + private static double InitLongitude(double longitude) + { + return NormalizeLongitude(longitude + HalfCircleOfArc) - HalfCircleOfArc; + } + + // midday-in-tehran + public static double MiddayAtPersianObservationSite(double date) + { + return Midday(date, InitLongitude(52.5)); // 52.5 degrees east - longitude of UTC+3:30 which defines Iranian Standard Time + } + + private static double PeriodicTerm(double julianCenturies, int x, double y, double z) + { + return x * SinOfDegree(y + z * julianCenturies); + } + + private static double SumLongSequenceOfPeriodicTerms(double julianCenturies) + { + double sum = 0.0; + sum += PeriodicTerm(julianCenturies, 403406, 270.54861, 0.9287892); + sum += PeriodicTerm(julianCenturies, 195207, 340.19128, 35999.1376958); + sum += PeriodicTerm(julianCenturies, 119433, 63.91854, 35999.4089666); + sum += PeriodicTerm(julianCenturies, 112392, 331.2622, 35998.7287385); + sum += PeriodicTerm(julianCenturies, 3891, 317.843, 71998.20261); + sum += PeriodicTerm(julianCenturies, 2819, 86.631, 71998.4403); + sum += PeriodicTerm(julianCenturies, 1721, 240.052, 36000.35726); + sum += PeriodicTerm(julianCenturies, 660, 310.26, 71997.4812); + sum += PeriodicTerm(julianCenturies, 350, 247.23, 32964.4678); + sum += PeriodicTerm(julianCenturies, 334, 260.87, -19.441); + sum += PeriodicTerm(julianCenturies, 314, 297.82, 445267.1117); + sum += PeriodicTerm(julianCenturies, 268, 343.14, 45036.884); + sum += PeriodicTerm(julianCenturies, 242, 166.79, 3.1008); + sum += PeriodicTerm(julianCenturies, 234, 81.53, 22518.4434); + sum += PeriodicTerm(julianCenturies, 158, 3.5, -19.9739); + sum += PeriodicTerm(julianCenturies, 132, 132.75, 65928.9345); + sum += PeriodicTerm(julianCenturies, 129, 182.95, 9038.0293); + sum += PeriodicTerm(julianCenturies, 114, 162.03, 3034.7684); + sum += PeriodicTerm(julianCenturies, 99, 29.8, 33718.148); + sum += PeriodicTerm(julianCenturies, 93, 266.4, 3034.448); + sum += PeriodicTerm(julianCenturies, 86, 249.2, -2280.773); + sum += PeriodicTerm(julianCenturies, 78, 157.6, 29929.992); + sum += PeriodicTerm(julianCenturies, 72, 257.8, 31556.493); + sum += PeriodicTerm(julianCenturies, 68, 185.1, 149.588); + sum += PeriodicTerm(julianCenturies, 64, 69.9, 9037.75); + sum += PeriodicTerm(julianCenturies, 46, 8.0, 107997.405); + sum += PeriodicTerm(julianCenturies, 38, 197.1, -4444.176); + sum += PeriodicTerm(julianCenturies, 37, 250.4, 151.771); + sum += PeriodicTerm(julianCenturies, 32, 65.3, 67555.316); + sum += PeriodicTerm(julianCenturies, 29, 162.7, 31556.08); + sum += PeriodicTerm(julianCenturies, 28, 341.5, -4561.54); + sum += PeriodicTerm(julianCenturies, 27, 291.6, 107996.706); + sum += PeriodicTerm(julianCenturies, 27, 98.5, 1221.655); + sum += PeriodicTerm(julianCenturies, 25, 146.7, 62894.167); + sum += PeriodicTerm(julianCenturies, 24, 110.0, 31437.369); + sum += PeriodicTerm(julianCenturies, 21, 5.2, 14578.298); + sum += PeriodicTerm(julianCenturies, 21, 342.6, -31931.757); + sum += PeriodicTerm(julianCenturies, 20, 230.9, 34777.243); + sum += PeriodicTerm(julianCenturies, 18, 256.1, 1221.999); + sum += PeriodicTerm(julianCenturies, 17, 45.3, 62894.511); + sum += PeriodicTerm(julianCenturies, 14, 242.9, -4442.039); + sum += PeriodicTerm(julianCenturies, 13, 115.2, 107997.909); + sum += PeriodicTerm(julianCenturies, 13, 151.8, 119.066); + sum += PeriodicTerm(julianCenturies, 13, 285.3, 16859.071); + sum += PeriodicTerm(julianCenturies, 12, 53.3, -4.578); + sum += PeriodicTerm(julianCenturies, 10, 126.6, 26895.292); + sum += PeriodicTerm(julianCenturies, 10, 205.7, -39.127); + sum += PeriodicTerm(julianCenturies, 10, 85.9, 12297.536); + sum += PeriodicTerm(julianCenturies, 10, 146.1, 90073.778); + return sum; + } + + private static double Aberration(double julianCenturies) + { + return (0.0000974 * CosOfDegree(177.63 + (35999.01848 * julianCenturies))) - 0.005575; + } + + private static double Nutation(double julianCenturies) + { + double a = PolynomialSum(s_coefficientsA, julianCenturies); + double b = PolynomialSum(s_coefficientsB, julianCenturies); + return (-0.004778 * SinOfDegree(a)) - (0.0003667 * SinOfDegree(b)); + } + + public static double Compute(double time) + { + double julianCenturies = JulianCenturies(time); + double lambda = 282.7771834 + + (36000.76953744 * julianCenturies) + + (0.000005729577951308232 * SumLongSequenceOfPeriodicTerms(julianCenturies)); + + double longitude = lambda + Aberration(julianCenturies) + Nutation(julianCenturies); + return InitLongitude(longitude); + } + + public static double AsSeason(double longitude) + { + return (longitude < 0) ? (longitude + FullCircleOfArc) : longitude; + } + + private static double EstimatePrior(double longitude, double time) + { + double timeSunLastAtLongitude = time - (MeanSpeedOfSun * AsSeason(InitLongitude(Compute(time) - longitude))); + double longitudeErrorDelta = InitLongitude(Compute(timeSunLastAtLongitude) - longitude); + return Math.Min(time, timeSunLastAtLongitude - (MeanSpeedOfSun * longitudeErrorDelta)); + } + + // persian-new-year-on-or-before + // number of days is the absolute date. The absolute date is the number of days from January 1st, 1 A.D. + // 1/1/0001 is absolute date 1. + internal static long PersianNewYearOnOrBefore(long numberOfDays) + { + double date = (double)numberOfDays; + + double approx = EstimatePrior(LongitudeSpring, MiddayAtPersianObservationSite(date)); + long lowerBoundNewYearDay = (long)Math.Floor(approx) - 1; + long upperBoundNewYearDay = lowerBoundNewYearDay + 3; // estimate is generally within a day of the actual occurrance (at the limits, the error expands, since the calculations rely on the mean tropical year which changes...) + long day = lowerBoundNewYearDay; + for (; day != upperBoundNewYearDay; ++day) + { + double midday = MiddayAtPersianObservationSite((double)day); + double l = Compute(midday); + if ((LongitudeSpring <= l) && (l <= TwoDegreesAfterSpring)) + { + break; + } + } + Debug.Assert(day != upperBoundNewYearDay); + + return day - 1; + } + } +} diff --git a/src/mscorlib/shared/System/Globalization/ChineseLunisolarCalendar.cs b/src/mscorlib/shared/System/Globalization/ChineseLunisolarCalendar.cs new file mode 100644 index 0000000000..404add0936 --- /dev/null +++ b/src/mscorlib/shared/System/Globalization/ChineseLunisolarCalendar.cs @@ -0,0 +1,390 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics.Contracts; + +namespace System.Globalization +{ + /* + ** Calendar support range: + ** Calendar Minimum Maximum + ** ========== ========== ========== + ** Gregorian 1901/02/19 2101/01/28 + ** ChineseLunisolar 1901/01/01 2100/12/29 + */ + + [Serializable] + public class ChineseLunisolarCalendar : EastAsianLunisolarCalendar + { + // + // The era value for the current era. + // + + public const int ChineseEra = 1; + + internal const int MIN_LUNISOLAR_YEAR = 1901; + internal const int MAX_LUNISOLAR_YEAR = 2100; + + internal const int MIN_GREGORIAN_YEAR = 1901; + internal const int MIN_GREGORIAN_MONTH = 2; + internal const int MIN_GREGORIAN_DAY = 19; + + internal const int MAX_GREGORIAN_YEAR = 2101; + internal const int MAX_GREGORIAN_MONTH = 1; + internal const int MAX_GREGORIAN_DAY = 28; + + internal static DateTime minDate = new DateTime(MIN_GREGORIAN_YEAR, MIN_GREGORIAN_MONTH, MIN_GREGORIAN_DAY); + internal static DateTime maxDate = new DateTime((new DateTime(MAX_GREGORIAN_YEAR, MAX_GREGORIAN_MONTH, MAX_GREGORIAN_DAY, 23, 59, 59, 999)).Ticks + 9999); + + public override DateTime MinSupportedDateTime + { + get + { + return (minDate); + } + } + + + public override DateTime MaxSupportedDateTime + { + get + { + return (maxDate); + } + } + + protected override int DaysInYearBeforeMinSupportedYear + { + get + { + // 1900: 1-29 2-30 3-29 4-29 5-30 6-29 7-30 8-30 Leap8-29 9-30 10-30 11-29 12-30 from Calendrical Tabulations + return 384; + } + } + + + private static readonly int[,] s_yinfo = + { + /*Y LM Lmon Lday DaysPerMonth D1 D2 D3 D4 D5 D6 D7 D8 D9 D10 D11 D12 D13 #Days + 1901 */ + { 0 , 2 , 19 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354 +1902 */{ 0 , 2 , 8 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355 +1903 */{ 5 , 1 , 29 , 21096 },/* 29 30 29 30 29 29 30 29 29 30 30 29 30 383 +1904 */{ 0 , 2 , 16 , 53856 },/* 30 30 29 30 29 29 30 29 29 30 30 29 0 354 +1905 */{ 0 , 2 , 4 , 55632 },/* 30 30 29 30 30 29 29 30 29 30 29 30 0 355 +1906 */{ 4 , 1 , 25 , 27304 },/* 29 30 30 29 30 29 30 29 30 29 30 29 30 384 +1907 */{ 0 , 2 , 13 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354 +1908 */{ 0 , 2 , 2 , 39632 },/* 30 29 29 30 30 29 30 29 30 30 29 30 0 355 +1909 */{ 2 , 1 , 22 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384 +1910 */{ 0 , 2 , 10 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354 +1911 */{ 6 , 1 , 30 , 42200 },/* 30 29 30 29 29 30 29 29 30 30 29 30 30 384 +1912 */{ 0 , 2 , 18 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354 +1913 */{ 0 , 2 , 6 , 53840 },/* 30 30 29 30 29 29 30 29 29 30 29 30 0 354 +1914 */{ 5 , 1 , 26 , 54568 },/* 30 30 29 30 29 30 29 30 29 29 30 29 30 384 +1915 */{ 0 , 2 , 14 , 46400 },/* 30 29 30 30 29 30 29 30 29 30 29 29 0 354 +1916 */{ 0 , 2 , 3 , 54944 },/* 30 30 29 30 29 30 30 29 30 29 30 29 0 355 +1917 */{ 2 , 1 , 23 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 29 384 +1918 */{ 0 , 2 , 11 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355 +1919 */{ 7 , 2 , 1 , 18872 },/* 29 30 29 29 30 29 29 30 30 29 30 30 30 384 +1920 */{ 0 , 2 , 20 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354 +1921 */{ 0 , 2 , 8 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354 +1922 */{ 5 , 1 , 28 , 45656 },/* 30 29 30 30 29 29 30 29 29 30 29 30 30 384 +1923 */{ 0 , 2 , 16 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 0 354 +1924 */{ 0 , 2 , 5 , 27968 },/* 29 30 30 29 30 30 29 30 29 30 29 29 0 354 +1925 */{ 4 , 1 , 24 , 44456 },/* 30 29 30 29 30 30 29 30 30 29 30 29 30 385 +1926 */{ 0 , 2 , 13 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354 +1927 */{ 0 , 2 , 2 , 38256 },/* 30 29 29 30 29 30 29 30 29 30 30 30 0 355 +1928 */{ 2 , 1 , 23 , 18808 },/* 29 30 29 29 30 29 29 30 29 30 30 30 30 384 +1929 */{ 0 , 2 , 10 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354 +1930 */{ 6 , 1 , 30 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 29 383 +1931 */{ 0 , 2 , 17 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354 +1932 */{ 0 , 2 , 6 , 59984 },/* 30 30 30 29 30 29 30 29 29 30 29 30 0 355 +1933 */{ 5 , 1 , 26 , 27976 },/* 29 30 30 29 30 30 29 30 29 30 29 29 30 384 +1934 */{ 0 , 2 , 14 , 23248 },/* 29 30 29 30 30 29 30 29 30 30 29 30 0 355 +1935 */{ 0 , 2 , 4 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354 +1936 */{ 3 , 1 , 24 , 37744 },/* 30 29 29 30 29 29 30 30 29 30 30 30 29 384 +1937 */{ 0 , 2 , 11 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354 +1938 */{ 7 , 1 , 31 , 51560 },/* 30 30 29 29 30 29 29 30 29 30 30 29 30 384 +1939 */{ 0 , 2 , 19 , 51536 },/* 30 30 29 29 30 29 29 30 29 30 29 30 0 354 +1940 */{ 0 , 2 , 8 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354 +1941 */{ 6 , 1 , 27 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 29 384 +1942 */{ 0 , 2 , 15 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355 +1943 */{ 0 , 2 , 5 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354 +1944 */{ 4 , 1 , 25 , 43736 },/* 30 29 30 29 30 29 30 29 30 30 29 30 30 385 +1945 */{ 0 , 2 , 13 , 9680 },/* 29 29 30 29 29 30 29 30 30 30 29 30 0 354 +1946 */{ 0 , 2 , 2 , 37584 },/* 30 29 29 30 29 29 30 29 30 30 29 30 0 354 +1947 */{ 2 , 1 , 22 , 51544 },/* 30 30 29 29 30 29 29 30 29 30 29 30 30 384 +1948 */{ 0 , 2 , 10 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354 +1949 */{ 7 , 1 , 29 , 46248 },/* 30 29 30 30 29 30 29 29 30 29 30 29 30 384 +1950 */{ 0 , 2 , 17 , 27808 },/* 29 30 30 29 30 30 29 29 30 29 30 29 0 354 +1951 */{ 0 , 2 , 6 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355 +1952 */{ 5 , 1 , 27 , 21928 },/* 29 30 29 30 29 30 29 30 30 29 30 29 30 384 +1953 */{ 0 , 2 , 14 , 19872 },/* 29 30 29 29 30 30 29 30 30 29 30 29 0 354 +1954 */{ 0 , 2 , 3 , 42416 },/* 30 29 30 29 29 30 29 30 30 29 30 30 0 355 +1955 */{ 3 , 1 , 24 , 21176 },/* 29 30 29 30 29 29 30 29 30 29 30 30 30 384 +1956 */{ 0 , 2 , 12 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354 +1957 */{ 8 , 1 , 31 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 29 383 +1958 */{ 0 , 2 , 18 , 59728 },/* 30 30 30 29 30 29 29 30 29 30 29 30 0 355 +1959 */{ 0 , 2 , 8 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354 +1960 */{ 6 , 1 , 28 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 29 384 +1961 */{ 0 , 2 , 15 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 0 355 +1962 */{ 0 , 2 , 5 , 19296 },/* 29 30 29 29 30 29 30 30 29 30 30 29 0 354 +1963 */{ 4 , 1 , 25 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 29 384 +1964 */{ 0 , 2 , 13 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355 +1965 */{ 0 , 2 , 2 , 21088 },/* 29 30 29 30 29 29 30 29 29 30 30 29 0 353 +1966 */{ 3 , 1 , 21 , 59696 },/* 30 30 30 29 30 29 29 30 29 29 30 30 29 384 +1967 */{ 0 , 2 , 9 , 55632 },/* 30 30 29 30 30 29 29 30 29 30 29 30 0 355 +1968 */{ 7 , 1 , 30 , 23208 },/* 29 30 29 30 30 29 30 29 30 29 30 29 30 384 +1969 */{ 0 , 2 , 17 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354 +1970 */{ 0 , 2 , 6 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 0 355 +1971 */{ 5 , 1 , 27 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384 +1972 */{ 0 , 2 , 15 , 19152 },/* 29 30 29 29 30 29 30 29 30 30 29 30 0 354 +1973 */{ 0 , 2 , 3 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354 +1974 */{ 4 , 1 , 23 , 53864 },/* 30 30 29 30 29 29 30 29 29 30 30 29 30 384 +1975 */{ 0 , 2 , 11 , 53840 },/* 30 30 29 30 29 29 30 29 29 30 29 30 0 354 +1976 */{ 8 , 1 , 31 , 54568 },/* 30 30 29 30 29 30 29 30 29 29 30 29 30 384 +1977 */{ 0 , 2 , 18 , 46400 },/* 30 29 30 30 29 30 29 30 29 30 29 29 0 354 +1978 */{ 0 , 2 , 7 , 46752 },/* 30 29 30 30 29 30 30 29 30 29 30 29 0 355 +1979 */{ 6 , 1 , 28 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 29 384 +1980 */{ 0 , 2 , 16 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355 +1981 */{ 0 , 2 , 5 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354 +1982 */{ 4 , 1 , 25 , 42168 },/* 30 29 30 29 29 30 29 29 30 29 30 30 30 384 +1983 */{ 0 , 2 , 13 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354 +1984 */{ 10 , 2 , 2 , 45656 },/* 30 29 30 30 29 29 30 29 29 30 29 30 30 384 +1985 */{ 0 , 2 , 20 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 0 354 +1986 */{ 0 , 2 , 9 , 27968 },/* 29 30 30 29 30 30 29 30 29 30 29 29 0 354 +1987 */{ 6 , 1 , 29 , 44448 },/* 30 29 30 29 30 30 29 30 30 29 30 29 29 384 +1988 */{ 0 , 2 , 17 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 0 355 +1989 */{ 0 , 2 , 6 , 38256 },/* 30 29 29 30 29 30 29 30 29 30 30 30 0 355 +1990 */{ 5 , 1 , 27 , 18808 },/* 29 30 29 29 30 29 29 30 29 30 30 30 30 384 +1991 */{ 0 , 2 , 15 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354 +1992 */{ 0 , 2 , 4 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 0 354 +1993 */{ 3 , 1 , 23 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 29 383 +1994 */{ 0 , 2 , 10 , 59984 },/* 30 30 30 29 30 29 30 29 29 30 29 30 0 355 +1995 */{ 8 , 1 , 31 , 27432 },/* 29 30 30 29 30 29 30 30 29 29 30 29 30 384 +1996 */{ 0 , 2 , 19 , 23232 },/* 29 30 29 30 30 29 30 29 30 30 29 29 0 354 +1997 */{ 0 , 2 , 7 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 0 355 +1998 */{ 5 , 1 , 28 , 37736 },/* 30 29 29 30 29 29 30 30 29 30 30 29 30 384 +1999 */{ 0 , 2 , 16 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354 +2000 */{ 0 , 2 , 5 , 51552 },/* 30 30 29 29 30 29 29 30 29 30 30 29 0 354 +2001 */{ 4 , 1 , 24 , 54440 },/* 30 30 29 30 29 30 29 29 30 29 30 29 30 384 +2002 */{ 0 , 2 , 12 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354 +2003 */{ 0 , 2 , 1 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 0 355 +2004 */{ 2 , 1 , 22 , 23208 },/* 29 30 29 30 30 29 30 29 30 29 30 29 30 384 +2005 */{ 0 , 2 , 9 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354 +2006 */{ 7 , 1 , 29 , 43736 },/* 30 29 30 29 30 29 30 29 30 30 29 30 30 385 +2007 */{ 0 , 2 , 18 , 9680 },/* 29 29 30 29 29 30 29 30 30 30 29 30 0 354 +2008 */{ 0 , 2 , 7 , 37584 },/* 30 29 29 30 29 29 30 29 30 30 29 30 0 354 +2009 */{ 5 , 1 , 26 , 51544 },/* 30 30 29 29 30 29 29 30 29 30 29 30 30 384 +2010 */{ 0 , 2 , 14 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354 +2011 */{ 0 , 2 , 3 , 46240 },/* 30 29 30 30 29 30 29 29 30 29 30 29 0 354 +2012 */{ 4 , 1 , 23 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 29 384 +2013 */{ 0 , 2 , 10 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355 +2014 */{ 9 , 1 , 31 , 21928 },/* 29 30 29 30 29 30 29 30 30 29 30 29 30 384 +2015 */{ 0 , 2 , 19 , 19360 },/* 29 30 29 29 30 29 30 30 30 29 30 29 0 354 +2016 */{ 0 , 2 , 8 , 42416 },/* 30 29 30 29 29 30 29 30 30 29 30 30 0 355 +2017 */{ 6 , 1 , 28 , 21176 },/* 29 30 29 30 29 29 30 29 30 29 30 30 30 384 +2018 */{ 0 , 2 , 16 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354 +2019 */{ 0 , 2 , 5 , 43312 },/* 30 29 30 29 30 29 29 30 29 29 30 30 0 354 +2020 */{ 4 , 1 , 25 , 29864 },/* 29 30 30 30 29 30 29 29 30 29 30 29 30 384 +2021 */{ 0 , 2 , 12 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354 +2022 */{ 0 , 2 , 1 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355 +2023 */{ 2 , 1 , 22 , 19880 },/* 29 30 29 29 30 30 29 30 30 29 30 29 30 384 +2024 */{ 0 , 2 , 10 , 19296 },/* 29 30 29 29 30 29 30 30 29 30 30 29 0 354 +2025 */{ 6 , 1 , 29 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 29 384 +2026 */{ 0 , 2 , 17 , 42208 },/* 30 29 30 29 29 30 29 29 30 30 30 29 0 354 +2027 */{ 0 , 2 , 6 , 53856 },/* 30 30 29 30 29 29 30 29 29 30 30 29 0 354 +2028 */{ 5 , 1 , 26 , 59696 },/* 30 30 30 29 30 29 29 30 29 29 30 30 29 384 +2029 */{ 0 , 2 , 13 , 54576 },/* 30 30 29 30 29 30 29 30 29 29 30 30 0 355 +2030 */{ 0 , 2 , 3 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354 +2031 */{ 3 , 1 , 23 , 27472 },/* 29 30 30 29 30 29 30 30 29 30 29 30 29 384 +2032 */{ 0 , 2 , 11 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 0 355 +2033 */{ 11 , 1 , 31 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384 +2034 */{ 0 , 2 , 19 , 19152 },/* 29 30 29 29 30 29 30 29 30 30 29 30 0 354 +2035 */{ 0 , 2 , 8 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354 +2036 */{ 6 , 1 , 28 , 53848 },/* 30 30 29 30 29 29 30 29 29 30 29 30 30 384 +2037 */{ 0 , 2 , 15 , 53840 },/* 30 30 29 30 29 29 30 29 29 30 29 30 0 354 +2038 */{ 0 , 2 , 4 , 54560 },/* 30 30 29 30 29 30 29 30 29 29 30 29 0 354 +2039 */{ 5 , 1 , 24 , 55968 },/* 30 30 29 30 30 29 30 29 30 29 30 29 29 384 +2040 */{ 0 , 2 , 12 , 46496 },/* 30 29 30 30 29 30 29 30 30 29 30 29 0 355 +2041 */{ 0 , 2 , 1 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 0 355 +2042 */{ 2 , 1 , 22 , 19160 },/* 29 30 29 29 30 29 30 29 30 30 29 30 30 384 +2043 */{ 0 , 2 , 10 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354 +2044 */{ 7 , 1 , 30 , 42168 },/* 30 29 30 29 29 30 29 29 30 29 30 30 30 384 +2045 */{ 0 , 2 , 17 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354 +2046 */{ 0 , 2 , 6 , 43600 },/* 30 29 30 29 30 29 30 29 29 30 29 30 0 354 +2047 */{ 5 , 1 , 26 , 46376 },/* 30 29 30 30 29 30 29 30 29 29 30 29 30 384 +2048 */{ 0 , 2 , 14 , 27936 },/* 29 30 30 29 30 30 29 30 29 29 30 29 0 354 +2049 */{ 0 , 2 , 2 , 44448 },/* 30 29 30 29 30 30 29 30 30 29 30 29 0 355 +2050 */{ 3 , 1 , 23 , 21936 },/* 29 30 29 30 29 30 29 30 30 29 30 30 29 384 +2051 */{ 0 , 2 , 11 , 37744 },/* 30 29 29 30 29 29 30 30 29 30 30 30 0 355 +2052 */{ 8 , 2 , 1 , 18808 },/* 29 30 29 29 30 29 29 30 29 30 30 30 30 384 +2053 */{ 0 , 2 , 19 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354 +2054 */{ 0 , 2 , 8 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 0 354 +2055 */{ 6 , 1 , 28 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 29 383 +2056 */{ 0 , 2 , 15 , 59984 },/* 30 30 30 29 30 29 30 29 29 30 29 30 0 355 +2057 */{ 0 , 2 , 4 , 27424 },/* 29 30 30 29 30 29 30 30 29 29 30 29 0 354 +2058 */{ 4 , 1 , 24 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 29 384 +2059 */{ 0 , 2 , 12 , 43744 },/* 30 29 30 29 30 29 30 29 30 30 30 29 0 355 +2060 */{ 0 , 2 , 2 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354 +2061 */{ 3 , 1 , 21 , 51568 },/* 30 30 29 29 30 29 29 30 29 30 30 30 29 384 +2062 */{ 0 , 2 , 9 , 51552 },/* 30 30 29 29 30 29 29 30 29 30 30 29 0 354 +2063 */{ 7 , 1 , 29 , 54440 },/* 30 30 29 30 29 30 29 29 30 29 30 29 30 384 +2064 */{ 0 , 2 , 17 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354 +2065 */{ 0 , 2 , 5 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 0 355 +2066 */{ 5 , 1 , 26 , 23208 },/* 29 30 29 30 30 29 30 29 30 29 30 29 30 384 +2067 */{ 0 , 2 , 14 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354 +2068 */{ 0 , 2 , 3 , 42704 },/* 30 29 30 29 29 30 30 29 30 30 29 30 0 355 +2069 */{ 4 , 1 , 23 , 21224 },/* 29 30 29 30 29 29 30 29 30 30 30 29 30 384 +2070 */{ 0 , 2 , 11 , 21200 },/* 29 30 29 30 29 29 30 29 30 30 29 30 0 354 +2071 */{ 8 , 1 , 31 , 43352 },/* 30 29 30 29 30 29 29 30 29 30 29 30 30 384 +2072 */{ 0 , 2 , 19 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354 +2073 */{ 0 , 2 , 7 , 46240 },/* 30 29 30 30 29 30 29 29 30 29 30 29 0 354 +2074 */{ 6 , 1 , 27 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 29 384 +2075 */{ 0 , 2 , 15 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355 +2076 */{ 0 , 2 , 5 , 21920 },/* 29 30 29 30 29 30 29 30 30 29 30 29 0 354 +2077 */{ 4 , 1 , 24 , 42448 },/* 30 29 30 29 29 30 29 30 30 30 29 30 29 384 +2078 */{ 0 , 2 , 12 , 42416 },/* 30 29 30 29 29 30 29 30 30 29 30 30 0 355 +2079 */{ 0 , 2 , 2 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354 +2080 */{ 3 , 1 , 22 , 43320 },/* 30 29 30 29 30 29 29 30 29 29 30 30 30 384 +2081 */{ 0 , 2 , 9 , 26928 },/* 29 30 30 29 30 29 29 30 29 29 30 30 0 354 +2082 */{ 7 , 1 , 29 , 29336 },/* 29 30 30 30 29 29 30 29 30 29 29 30 30 384 +2083 */{ 0 , 2 , 17 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354 +2084 */{ 0 , 2 , 6 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355 +2085 */{ 5 , 1 , 26 , 19880 },/* 29 30 29 29 30 30 29 30 30 29 30 29 30 384 +2086 */{ 0 , 2 , 14 , 19296 },/* 29 30 29 29 30 29 30 30 29 30 30 29 0 354 +2087 */{ 0 , 2 , 3 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355 +2088 */{ 4 , 1 , 24 , 21104 },/* 29 30 29 30 29 29 30 29 29 30 30 30 29 383 +2089 */{ 0 , 2 , 10 , 53856 },/* 30 30 29 30 29 29 30 29 29 30 30 29 0 354 +2090 */{ 8 , 1 , 30 , 59696 },/* 30 30 30 29 30 29 29 30 29 29 30 30 29 384 +2091 */{ 0 , 2 , 18 , 54560 },/* 30 30 29 30 29 30 29 30 29 29 30 29 0 354 +2092 */{ 0 , 2 , 7 , 55968 },/* 30 30 29 30 30 29 30 29 30 29 30 29 0 355 +2093 */{ 6 , 1 , 27 , 27472 },/* 29 30 30 29 30 29 30 30 29 30 29 30 29 384 +2094 */{ 0 , 2 , 15 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 0 355 +2095 */{ 0 , 2 , 5 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354 +2096 */{ 4 , 1 , 25 , 42216 },/* 30 29 30 29 29 30 29 29 30 30 30 29 30 384 +2097 */{ 0 , 2 , 12 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354 +2098 */{ 0 , 2 , 1 , 53584 },/* 30 30 29 30 29 29 29 30 29 30 29 30 0 354 +2099 */{ 2 , 1 , 21 , 55592 },/* 30 30 29 30 30 29 29 30 29 29 30 29 30 384 +2100 */{ 0 , 2 , 9 , 54560 },/* 30 30 29 30 29 30 29 30 29 29 30 29 0 354 + */}; + + + internal override int MinCalendarYear + { + get + { + return (MIN_LUNISOLAR_YEAR); + } + } + + internal override int MaxCalendarYear + { + get + { + return (MAX_LUNISOLAR_YEAR); + } + } + + internal override DateTime MinDate + { + get + { + return (minDate); + } + } + + internal override DateTime MaxDate + { + get + { + return (maxDate); + } + } + + internal override EraInfo[] CalEraInfo + { + get + { + return (null); + } + } + + internal override int GetYearInfo(int lunarYear, int index) + { + if ((lunarYear < MIN_LUNISOLAR_YEAR) || (lunarYear > MAX_LUNISOLAR_YEAR)) + { + throw new ArgumentOutOfRangeException( + "year", + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, MIN_LUNISOLAR_YEAR, MAX_LUNISOLAR_YEAR)); + } + Contract.EndContractBlock(); + + return s_yinfo[lunarYear - MIN_LUNISOLAR_YEAR, index]; + } + + internal override int GetYear(int year, DateTime time) + { + return year; + } + + internal override int GetGregorianYear(int year, int era) + { + if (era != CurrentEra && era != ChineseEra) + { + throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue); + } + + if (year < MIN_LUNISOLAR_YEAR || year > MAX_LUNISOLAR_YEAR) + { + throw new ArgumentOutOfRangeException( + nameof(year), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, MIN_LUNISOLAR_YEAR, MAX_LUNISOLAR_YEAR)); + } + Contract.EndContractBlock(); + + return year; + } + + public ChineseLunisolarCalendar() + { + } + + public override int GetEra(DateTime time) + { + CheckTicksRange(time.Ticks); + return (ChineseEra); + } + + internal override CalendarId ID + { + get + { + return (CalendarId.CHINESELUNISOLAR); + } + } + + internal override CalendarId BaseCalendarID + { + get + { + //Use CAL_GREGORIAN just to get CurrentEraValue as 1 since we do not have data under the ID CAL_ChineseLunisolar yet + return (CalendarId.GREGORIAN); + } + } + + + public override int[] Eras + { + get + { + return (new int[] { ChineseEra }); + } + } + } +} diff --git a/src/mscorlib/shared/System/Globalization/CultureNotFoundException.cs b/src/mscorlib/shared/System/Globalization/CultureNotFoundException.cs new file mode 100644 index 0000000000..929f4bb000 --- /dev/null +++ b/src/mscorlib/shared/System/Globalization/CultureNotFoundException.cs @@ -0,0 +1,120 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.Serialization; + +namespace System.Globalization +{ + [Serializable] + public class CultureNotFoundException : ArgumentException + { + private string _invalidCultureName; // unrecognized culture name + private int? _invalidCultureId; // unrecognized culture Lcid + + public CultureNotFoundException() + : base(DefaultMessage) + { + } + + public CultureNotFoundException(String message) + : base(message) + { + } + + public CultureNotFoundException(String paramName, String message) + : base(message, paramName) + { + } + + public CultureNotFoundException(String message, Exception innerException) + : base(message, innerException) + { + } + + public CultureNotFoundException(String paramName, string invalidCultureName, String message) + : base(message, paramName) + { + _invalidCultureName = invalidCultureName; + } + + public CultureNotFoundException(String message, string invalidCultureName, Exception innerException) + : base(message, innerException) + { + _invalidCultureName = invalidCultureName; + } + + public CultureNotFoundException(string message, int invalidCultureId, Exception innerException) + : base(message, innerException) + { + _invalidCultureId = invalidCultureId; + } + + public CultureNotFoundException(string paramName, int invalidCultureId, string message) + : base(message, paramName) + { + _invalidCultureId = invalidCultureId; + } + + protected CultureNotFoundException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + _invalidCultureId = (int?)info.GetValue("InvalidCultureId", typeof(int?)); + _invalidCultureName = (string)info.GetValue("InvalidCultureName", typeof(string)); + } + + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + info.AddValue("InvalidCultureId", _invalidCultureId, typeof(int?)); + info.AddValue("InvalidCultureName", _invalidCultureName, typeof(string)); + } + + public virtual Nullable<int> InvalidCultureId + { + get { return _invalidCultureId; } + } + + public virtual string InvalidCultureName + { + get { return _invalidCultureName; } + } + + private static String DefaultMessage + { + get + { + return SR.Argument_CultureNotSupported; + } + } + + private String FormatedInvalidCultureId + { + get + { + return InvalidCultureId != null ? + String.Format(CultureInfo.InvariantCulture, "{0} (0x{0:x4})", (int)InvalidCultureId) : + InvalidCultureName; + } + } + + public override String Message + { + get + { + String s = base.Message; + if (_invalidCultureId != null || _invalidCultureName != null) + { + String valueMessage = SR.Format(SR.Argument_CultureInvalidIdentifier, FormatedInvalidCultureId); + if (s == null) + { + return valueMessage; + } + + return s + Environment.NewLine + valueMessage; + } + return s; + } + } + } +} diff --git a/src/mscorlib/shared/System/Globalization/CultureTypes.cs b/src/mscorlib/shared/System/Globalization/CultureTypes.cs new file mode 100644 index 0000000000..f52ac82a83 --- /dev/null +++ b/src/mscorlib/shared/System/Globalization/CultureTypes.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// The enumeration constants used in CultureInfo.GetCultures(). +// On Linux platforms, the only enum values used there is NeutralCultures and SpecificCultures +// the rest are obsolete or not valid on Linux + +namespace System.Globalization +{ + [Flags] + public enum CultureTypes + { + NeutralCultures = 0x0001, // Neutral cultures are cultures like "en", "de", "zh", etc, for enumeration this includes ALL neutrals regardless of other flags + SpecificCultures = 0x0002, // Non-netural cultuers. Examples are "en-us", "zh-tw", etc., for enumeration this includes ALL specifics regardless of other flags + InstalledWin32Cultures = 0x0004, // Win32 installed cultures in the system and exists in the framework too., this is effectively all cultures + + AllCultures = NeutralCultures | SpecificCultures | InstalledWin32Cultures, + + UserCustomCulture = 0x0008, // User defined custom culture + ReplacementCultures = 0x0010, // User defined replacement custom culture. + [Obsolete("This value has been deprecated. Please use other values in CultureTypes.")] + WindowsOnlyCultures = 0x0020, // this will always return empty list. + [Obsolete("This value has been deprecated. Please use other values in CultureTypes.")] + FrameworkCultures = 0x0040, // will return only the v2 cultures marked as Framework culture. + } +} diff --git a/src/mscorlib/shared/System/Globalization/DateTimeFormat.cs b/src/mscorlib/shared/System/Globalization/DateTimeFormat.cs new file mode 100644 index 0000000000..840409f55a --- /dev/null +++ b/src/mscorlib/shared/System/Globalization/DateTimeFormat.cs @@ -0,0 +1,1206 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.Contracts; +using System.Globalization; +using System.Text; + +namespace System +{ + /* + Customized format patterns: + P.S. Format in the table below is the internal number format used to display the pattern. + + Patterns Format Description Example + ========= ========== ===================================== ======== + "h" "0" hour (12-hour clock)w/o leading zero 3 + "hh" "00" hour (12-hour clock)with leading zero 03 + "hh*" "00" hour (12-hour clock)with leading zero 03 + + "H" "0" hour (24-hour clock)w/o leading zero 8 + "HH" "00" hour (24-hour clock)with leading zero 08 + "HH*" "00" hour (24-hour clock) 08 + + "m" "0" minute w/o leading zero + "mm" "00" minute with leading zero + "mm*" "00" minute with leading zero + + "s" "0" second w/o leading zero + "ss" "00" second with leading zero + "ss*" "00" second with leading zero + + "f" "0" second fraction (1 digit) + "ff" "00" second fraction (2 digit) + "fff" "000" second fraction (3 digit) + "ffff" "0000" second fraction (4 digit) + "fffff" "00000" second fraction (5 digit) + "ffffff" "000000" second fraction (6 digit) + "fffffff" "0000000" second fraction (7 digit) + + "F" "0" second fraction (up to 1 digit) + "FF" "00" second fraction (up to 2 digit) + "FFF" "000" second fraction (up to 3 digit) + "FFFF" "0000" second fraction (up to 4 digit) + "FFFFF" "00000" second fraction (up to 5 digit) + "FFFFFF" "000000" second fraction (up to 6 digit) + "FFFFFFF" "0000000" second fraction (up to 7 digit) + + "t" first character of AM/PM designator A + "tt" AM/PM designator AM + "tt*" AM/PM designator PM + + "d" "0" day w/o leading zero 1 + "dd" "00" day with leading zero 01 + "ddd" short weekday name (abbreviation) Mon + "dddd" full weekday name Monday + "dddd*" full weekday name Monday + + + "M" "0" month w/o leading zero 2 + "MM" "00" month with leading zero 02 + "MMM" short month name (abbreviation) Feb + "MMMM" full month name Febuary + "MMMM*" full month name Febuary + + "y" "0" two digit year (year % 100) w/o leading zero 0 + "yy" "00" two digit year (year % 100) with leading zero 00 + "yyy" "D3" year 2000 + "yyyy" "D4" year 2000 + "yyyyy" "D5" year 2000 + ... + + "z" "+0;-0" timezone offset w/o leading zero -8 + "zz" "+00;-00" timezone offset with leading zero -08 + "zzz" "+00;-00" for hour offset, "00" for minute offset full timezone offset -07:30 + "zzz*" "+00;-00" for hour offset, "00" for minute offset full timezone offset -08:00 + + "K" -Local "zzz", e.g. -08:00 + -Utc "'Z'", representing UTC + -Unspecified "" + -DateTimeOffset "zzzzz" e.g -07:30:15 + + "g*" the current era name A.D. + + ":" time separator : -- DEPRECATED - Insert separator directly into pattern (eg: "H.mm.ss") + "/" date separator /-- DEPRECATED - Insert separator directly into pattern (eg: "M-dd-yyyy") + "'" quoted string 'ABC' will insert ABC into the formatted string. + '"' quoted string "ABC" will insert ABC into the formatted string. + "%" used to quote a single pattern characters E.g.The format character "%y" is to print two digit year. + "\" escaped character E.g. '\d' insert the character 'd' into the format string. + other characters insert the character into the format string. + + Pre-defined format characters: + (U) to indicate Universal time is used. + (G) to indicate Gregorian calendar is used. + + Format Description Real format Example + ========= ================================= ====================== ======================= + "d" short date culture-specific 10/31/1999 + "D" long data culture-specific Sunday, October 31, 1999 + "f" full date (long date + short time) culture-specific Sunday, October 31, 1999 2:00 AM + "F" full date (long date + long time) culture-specific Sunday, October 31, 1999 2:00:00 AM + "g" general date (short date + short time) culture-specific 10/31/1999 2:00 AM + "G" general date (short date + long time) culture-specific 10/31/1999 2:00:00 AM + "m"/"M" Month/Day date culture-specific October 31 +(G) "o"/"O" Round Trip XML "yyyy-MM-ddTHH:mm:ss.fffffffK" 1999-10-31 02:00:00.0000000Z +(G) "r"/"R" RFC 1123 date, "ddd, dd MMM yyyy HH':'mm':'ss 'GMT'" Sun, 31 Oct 1999 10:00:00 GMT +(G) "s" Sortable format, based on ISO 8601. "yyyy-MM-dd'T'HH:mm:ss" 1999-10-31T02:00:00 + ('T' for local time) + "t" short time culture-specific 2:00 AM + "T" long time culture-specific 2:00:00 AM +(G) "u" Universal time with sortable format, "yyyy'-'MM'-'dd HH':'mm':'ss'Z'" 1999-10-31 10:00:00Z + based on ISO 8601. +(U) "U" Universal time with full culture-specific Sunday, October 31, 1999 10:00:00 AM + (long date + long time) format + "y"/"Y" Year/Month day culture-specific October, 1999 + + */ + + //This class contains only static members and does not require the serializable attribute. + internal static + class DateTimeFormat + { + internal const int MaxSecondsFractionDigits = 7; + internal static readonly TimeSpan NullOffset = TimeSpan.MinValue; + + internal static char[] allStandardFormats = + { + 'd', 'D', 'f', 'F', 'g', 'G', + 'm', 'M', 'o', 'O', 'r', 'R', + 's', 't', 'T', 'u', 'U', 'y', 'Y', + }; + + internal const String RoundtripFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.fffffffK"; + internal const String RoundtripDateTimeUnfixed = "yyyy'-'MM'-'ddTHH':'mm':'ss zzz"; + + private const int DEFAULT_ALL_DATETIMES_SIZE = 132; + + internal static readonly DateTimeFormatInfo InvariantFormatInfo = CultureInfo.InvariantCulture.DateTimeFormat; + internal static readonly string[] InvariantAbbreviatedMonthNames = InvariantFormatInfo.AbbreviatedMonthNames; + internal static readonly string[] InvariantAbbreviatedDayNames = InvariantFormatInfo.AbbreviatedDayNames; + internal const string Gmt = "GMT"; + + internal static String[] fixedNumberFormats = new String[] { + "0", + "00", + "000", + "0000", + "00000", + "000000", + "0000000", + }; + + //////////////////////////////////////////////////////////////////////////// + // + // Format the positive integer value to a string and perfix with assigned + // length of leading zero. + // + // Parameters: + // value: The value to format + // len: The maximum length for leading zero. + // If the digits of the value is greater than len, no leading zero is added. + // + // Notes: + // The function can format to Int32.MaxValue. + // + //////////////////////////////////////////////////////////////////////////// + internal static void FormatDigits(StringBuilder outputBuffer, int value, int len) + { + Debug.Assert(value >= 0, "DateTimeFormat.FormatDigits(): value >= 0"); + FormatDigits(outputBuffer, value, len, false); + } + + internal unsafe static void FormatDigits(StringBuilder outputBuffer, int value, int len, bool overrideLengthLimit) + { + Debug.Assert(value >= 0, "DateTimeFormat.FormatDigits(): value >= 0"); + + // Limit the use of this function to be two-digits, so that we have the same behavior + // as RTM bits. + if (!overrideLengthLimit && len > 2) + { + len = 2; + } + + char* buffer = stackalloc char[16]; + char* p = buffer + 16; + int n = value; + do + { + *--p = (char)(n % 10 + '0'); + n /= 10; + } while ((n != 0) && (p > buffer)); + + int digits = (int)(buffer + 16 - p); + + //If the repeat count is greater than 0, we're trying + //to emulate the "00" format, so we have to prepend + //a zero if the string only has one character. + while ((digits < len) && (p > buffer)) + { + *--p = '0'; + digits++; + } + outputBuffer.Append(p, digits); + } + + private static void HebrewFormatDigits(StringBuilder outputBuffer, int digits) + { + outputBuffer.Append(HebrewNumber.ToString(digits)); + } + + internal static int ParseRepeatPattern(String format, int pos, char patternChar) + { + int len = format.Length; + int index = pos + 1; + while ((index < len) && (format[index] == patternChar)) + { + index++; + } + return (index - pos); + } + + private static String FormatDayOfWeek(int dayOfWeek, int repeat, DateTimeFormatInfo dtfi) + { + Debug.Assert(dayOfWeek >= 0 && dayOfWeek <= 6, "dayOfWeek >= 0 && dayOfWeek <= 6"); + if (repeat == 3) + { + return (dtfi.GetAbbreviatedDayName((DayOfWeek)dayOfWeek)); + } + // Call dtfi.GetDayName() here, instead of accessing DayNames property, because we don't + // want a clone of DayNames, which will hurt perf. + return (dtfi.GetDayName((DayOfWeek)dayOfWeek)); + } + + private static String FormatMonth(int month, int repeatCount, DateTimeFormatInfo dtfi) + { + Debug.Assert(month >= 1 && month <= 12, "month >=1 && month <= 12"); + if (repeatCount == 3) + { + return (dtfi.GetAbbreviatedMonthName(month)); + } + // Call GetMonthName() here, instead of accessing MonthNames property, because we don't + // want a clone of MonthNames, which will hurt perf. + return (dtfi.GetMonthName(month)); + } + + // + // FormatHebrewMonthName + // + // Action: Return the Hebrew month name for the specified DateTime. + // Returns: The month name string for the specified DateTime. + // Arguments: + // time the time to format + // month The month is the value of HebrewCalendar.GetMonth(time). + // repeat Return abbreviated month name if repeat=3, or full month name if repeat=4 + // dtfi The DateTimeFormatInfo which uses the Hebrew calendars as its calendar. + // Exceptions: None. + // + + /* Note: + If DTFI is using Hebrew calendar, GetMonthName()/GetAbbreviatedMonthName() will return month names like this: + 1 Hebrew 1st Month + 2 Hebrew 2nd Month + .. ... + 6 Hebrew 6th Month + 7 Hebrew 6th Month II (used only in a leap year) + 8 Hebrew 7th Month + 9 Hebrew 8th Month + 10 Hebrew 9th Month + 11 Hebrew 10th Month + 12 Hebrew 11th Month + 13 Hebrew 12th Month + + Therefore, if we are in a regular year, we have to increment the month name if moth is greater or eqaul to 7. + */ + private static String FormatHebrewMonthName(DateTime time, int month, int repeatCount, DateTimeFormatInfo dtfi) + { + Debug.Assert(repeatCount != 3 || repeatCount != 4, "repeateCount should be 3 or 4"); + if (dtfi.Calendar.IsLeapYear(dtfi.Calendar.GetYear(time))) + { + // This month is in a leap year + return (dtfi.internalGetMonthName(month, MonthNameStyles.LeapYear, (repeatCount == 3))); + } + // This is in a regular year. + if (month >= 7) + { + month++; + } + if (repeatCount == 3) + { + return (dtfi.GetAbbreviatedMonthName(month)); + } + return (dtfi.GetMonthName(month)); + } + + // + // The pos should point to a quote character. This method will + // append to the result StringBuilder the string encloed by the quote character. + // + internal static int ParseQuoteString(String format, int pos, StringBuilder result) + { + // + // NOTE : pos will be the index of the quote character in the 'format' string. + // + int formatLen = format.Length; + int beginPos = pos; + char quoteChar = format[pos++]; // Get the character used to quote the following string. + + bool foundQuote = false; + while (pos < formatLen) + { + char ch = format[pos++]; + if (ch == quoteChar) + { + foundQuote = true; + break; + } + else if (ch == '\\') + { + // The following are used to support escaped character. + // Escaped character is also supported in the quoted string. + // Therefore, someone can use a format like "'minute:' mm\"" to display: + // minute: 45" + // because the second double quote is escaped. + if (pos < formatLen) + { + result.Append(format[pos++]); + } + else + { + // + // This means that '\' is at the end of the formatting string. + // + throw new FormatException(SR.Format_InvalidString); + } + } + else + { + result.Append(ch); + } + } + + if (!foundQuote) + { + // Here we can't find the matching quote. + throw new FormatException( + String.Format( + CultureInfo.CurrentCulture, + SR.Format_BadQuote, quoteChar)); + } + + // + // Return the character count including the begin/end quote characters and enclosed string. + // + return (pos - beginPos); + } + + // + // Get the next character at the index of 'pos' in the 'format' string. + // Return value of -1 means 'pos' is already at the end of the 'format' string. + // Otherwise, return value is the int value of the next character. + // + internal static int ParseNextChar(String format, int pos) + { + if (pos >= format.Length - 1) + { + return (-1); + } + return ((int)format[pos + 1]); + } + + // + // IsUseGenitiveForm + // + // Actions: Check the format to see if we should use genitive month in the formatting. + // Starting at the position (index) in the (format) string, look back and look ahead to + // see if there is "d" or "dd". In the case like "d MMMM" or "MMMM dd", we can use + // genitive form. Genitive form is not used if there is more than two "d". + // Arguments: + // format The format string to be scanned. + // index Where we should start the scanning. This is generally where "M" starts. + // tokenLen The len of the current pattern character. This indicates how many "M" that we have. + // patternToMatch The pattern that we want to search. This generally uses "d" + // + private static bool IsUseGenitiveForm(String format, int index, int tokenLen, char patternToMatch) + { + int i; + int repeat = 0; + // + // Look back to see if we can find "d" or "ddd" + // + + // Find first "d". + for (i = index - 1; i >= 0 && format[i] != patternToMatch; i--) { /*Do nothing here */ }; + + if (i >= 0) + { + // Find a "d", so look back to see how many "d" that we can find. + while (--i >= 0 && format[i] == patternToMatch) + { + repeat++; + } + // + // repeat == 0 means that we have one (patternToMatch) + // repeat == 1 means that we have two (patternToMatch) + // + if (repeat <= 1) + { + return (true); + } + // Note that we can't just stop here. We may find "ddd" while looking back, and we have to look + // ahead to see if there is "d" or "dd". + } + + // + // If we can't find "d" or "dd" by looking back, try look ahead. + // + + // Find first "d" + for (i = index + tokenLen; i < format.Length && format[i] != patternToMatch; i++) { /* Do nothing here */ }; + + if (i < format.Length) + { + repeat = 0; + // Find a "d", so contine the walk to see how may "d" that we can find. + while (++i < format.Length && format[i] == patternToMatch) + { + repeat++; + } + // + // repeat == 0 means that we have one (patternToMatch) + // repeat == 1 means that we have two (patternToMatch) + // + if (repeat <= 1) + { + return (true); + } + } + return (false); + } + + + // + // FormatCustomized + // + // Actions: Format the DateTime instance using the specified format. + // + private static String FormatCustomized(DateTime dateTime, String format, DateTimeFormatInfo dtfi, TimeSpan offset) + { + Calendar cal = dtfi.Calendar; + StringBuilder result = StringBuilderCache.Acquire(); + // This is a flag to indicate if we are format the dates using Hebrew calendar. + + bool isHebrewCalendar = (cal.ID == CalendarId.HEBREW); + // This is a flag to indicate if we are formating hour/minute/second only. + bool bTimeOnly = true; + + int i = 0; + int tokenLen, hour12; + + while (i < format.Length) + { + char ch = format[i]; + int nextChar; + switch (ch) + { + case 'g': + tokenLen = ParseRepeatPattern(format, i, ch); + result.Append(dtfi.GetEraName(cal.GetEra(dateTime))); + break; + case 'h': + tokenLen = ParseRepeatPattern(format, i, ch); + hour12 = dateTime.Hour % 12; + if (hour12 == 0) + { + hour12 = 12; + } + FormatDigits(result, hour12, tokenLen); + break; + case 'H': + tokenLen = ParseRepeatPattern(format, i, ch); + FormatDigits(result, dateTime.Hour, tokenLen); + break; + case 'm': + tokenLen = ParseRepeatPattern(format, i, ch); + FormatDigits(result, dateTime.Minute, tokenLen); + break; + case 's': + tokenLen = ParseRepeatPattern(format, i, ch); + FormatDigits(result, dateTime.Second, tokenLen); + break; + case 'f': + case 'F': + tokenLen = ParseRepeatPattern(format, i, ch); + if (tokenLen <= MaxSecondsFractionDigits) + { + long fraction = (dateTime.Ticks % Calendar.TicksPerSecond); + fraction = fraction / (long)Math.Pow(10, 7 - tokenLen); + if (ch == 'f') + { + result.Append(((int)fraction).ToString(fixedNumberFormats[tokenLen - 1], CultureInfo.InvariantCulture)); + } + else + { + int effectiveDigits = tokenLen; + while (effectiveDigits > 0) + { + if (fraction % 10 == 0) + { + fraction = fraction / 10; + effectiveDigits--; + } + else + { + break; + } + } + if (effectiveDigits > 0) + { + result.Append(((int)fraction).ToString(fixedNumberFormats[effectiveDigits - 1], CultureInfo.InvariantCulture)); + } + else + { + // No fraction to emit, so see if we should remove decimal also. + if (result.Length > 0 && result[result.Length - 1] == '.') + { + result.Remove(result.Length - 1, 1); + } + } + } + } + else + { + throw new FormatException(SR.Format_InvalidString); + } + break; + case 't': + tokenLen = ParseRepeatPattern(format, i, ch); + if (tokenLen == 1) + { + if (dateTime.Hour < 12) + { + if (dtfi.AMDesignator.Length >= 1) + { + result.Append(dtfi.AMDesignator[0]); + } + } + else + { + if (dtfi.PMDesignator.Length >= 1) + { + result.Append(dtfi.PMDesignator[0]); + } + } + } + else + { + result.Append((dateTime.Hour < 12 ? dtfi.AMDesignator : dtfi.PMDesignator)); + } + break; + case 'd': + // + // tokenLen == 1 : Day of month as digits with no leading zero. + // tokenLen == 2 : Day of month as digits with leading zero for single-digit months. + // tokenLen == 3 : Day of week as a three-leter abbreviation. + // tokenLen >= 4 : Day of week as its full name. + // + tokenLen = ParseRepeatPattern(format, i, ch); + if (tokenLen <= 2) + { + int day = cal.GetDayOfMonth(dateTime); + if (isHebrewCalendar) + { + // For Hebrew calendar, we need to convert numbers to Hebrew text for yyyy, MM, and dd values. + HebrewFormatDigits(result, day); + } + else + { + FormatDigits(result, day, tokenLen); + } + } + else + { + int dayOfWeek = (int)cal.GetDayOfWeek(dateTime); + result.Append(FormatDayOfWeek(dayOfWeek, tokenLen, dtfi)); + } + bTimeOnly = false; + break; + case 'M': + // + // tokenLen == 1 : Month as digits with no leading zero. + // tokenLen == 2 : Month as digits with leading zero for single-digit months. + // tokenLen == 3 : Month as a three-letter abbreviation. + // tokenLen >= 4 : Month as its full name. + // + tokenLen = ParseRepeatPattern(format, i, ch); + int month = cal.GetMonth(dateTime); + if (tokenLen <= 2) + { + if (isHebrewCalendar) + { + // For Hebrew calendar, we need to convert numbers to Hebrew text for yyyy, MM, and dd values. + HebrewFormatDigits(result, month); + } + else + { + FormatDigits(result, month, tokenLen); + } + } + else + { + if (isHebrewCalendar) + { + result.Append(FormatHebrewMonthName(dateTime, month, tokenLen, dtfi)); + } + else + { + if ((dtfi.FormatFlags & DateTimeFormatFlags.UseGenitiveMonth) != 0 && tokenLen >= 4) + { + result.Append( + dtfi.internalGetMonthName( + month, + IsUseGenitiveForm(format, i, tokenLen, 'd') ? MonthNameStyles.Genitive : MonthNameStyles.Regular, + false)); + } + else + { + result.Append(FormatMonth(month, tokenLen, dtfi)); + } + } + } + bTimeOnly = false; + break; + case 'y': + // Notes about OS behavior: + // y: Always print (year % 100). No leading zero. + // yy: Always print (year % 100) with leading zero. + // yyy/yyyy/yyyyy/... : Print year value. No leading zero. + + int year = cal.GetYear(dateTime); + tokenLen = ParseRepeatPattern(format, i, ch); + if (dtfi.HasForceTwoDigitYears) + { + FormatDigits(result, year, tokenLen <= 2 ? tokenLen : 2); + } + else if (cal.ID == CalendarId.HEBREW) + { + HebrewFormatDigits(result, year); + } + else + { + if (tokenLen <= 2) + { + FormatDigits(result, year % 100, tokenLen); + } + else + { + String fmtPattern = "D" + tokenLen.ToString(); + result.Append(year.ToString(fmtPattern, CultureInfo.InvariantCulture)); + } + } + bTimeOnly = false; + break; + case 'z': + tokenLen = ParseRepeatPattern(format, i, ch); + FormatCustomizedTimeZone(dateTime, offset, format, tokenLen, bTimeOnly, result); + break; + case 'K': + tokenLen = 1; + FormatCustomizedRoundripTimeZone(dateTime, offset, result); + break; + case ':': + result.Append(dtfi.TimeSeparator); + tokenLen = 1; + break; + case '/': + result.Append(dtfi.DateSeparator); + tokenLen = 1; + break; + case '\'': + case '\"': + tokenLen = ParseQuoteString(format, i, result); + break; + case '%': + // Optional format character. + // For example, format string "%d" will print day of month + // without leading zero. Most of the cases, "%" can be ignored. + nextChar = ParseNextChar(format, i); + // nextChar will be -1 if we already reach the end of the format string. + // Besides, we will not allow "%%" appear in the pattern. + if (nextChar >= 0 && nextChar != (int)'%') + { + result.Append(FormatCustomized(dateTime, ((char)nextChar).ToString(), dtfi, offset)); + tokenLen = 2; + } + else + { + // + // This means that '%' is at the end of the format string or + // "%%" appears in the format string. + // + throw new FormatException(SR.Format_InvalidString); + } + break; + case '\\': + // Escaped character. Can be used to insert character into the format string. + // For exmple, "\d" will insert the character 'd' into the string. + // + // NOTENOTE : we can remove this format character if we enforce the enforced quote + // character rule. + // That is, we ask everyone to use single quote or double quote to insert characters, + // then we can remove this character. + // + nextChar = ParseNextChar(format, i); + if (nextChar >= 0) + { + result.Append(((char)nextChar)); + tokenLen = 2; + } + else + { + // + // This means that '\' is at the end of the formatting string. + // + throw new FormatException(SR.Format_InvalidString); + } + break; + default: + // NOTENOTE : we can remove this rule if we enforce the enforced quote + // character rule. + // That is, if we ask everyone to use single quote or double quote to insert characters, + // then we can remove this default block. + result.Append(ch); + tokenLen = 1; + break; + } + i += tokenLen; + } + return StringBuilderCache.GetStringAndRelease(result); + } + + + // output the 'z' famliy of formats, which output a the offset from UTC, e.g. "-07:30" + private static void FormatCustomizedTimeZone(DateTime dateTime, TimeSpan offset, String format, Int32 tokenLen, Boolean timeOnly, StringBuilder result) + { + // See if the instance already has an offset + Boolean dateTimeFormat = (offset == NullOffset); + if (dateTimeFormat) + { + // No offset. The instance is a DateTime and the output should be the local time zone + + if (timeOnly && dateTime.Ticks < Calendar.TicksPerDay) + { + // For time only format and a time only input, the time offset on 0001/01/01 is less + // accurate than the system's current offset because of daylight saving time. + offset = TimeZoneInfo.GetLocalUtcOffset(DateTime.Now, TimeZoneInfoOptions.NoThrowOnInvalidTime); + } + else if (dateTime.Kind == DateTimeKind.Utc) + { + offset = TimeSpan.Zero; + } + else + { + offset = TimeZoneInfo.GetLocalUtcOffset(dateTime, TimeZoneInfoOptions.NoThrowOnInvalidTime); + } + } + if (offset >= TimeSpan.Zero) + { + result.Append('+'); + } + else + { + result.Append('-'); + // get a positive offset, so that you don't need a separate code path for the negative numbers. + offset = offset.Negate(); + } + + if (tokenLen <= 1) + { + // 'z' format e.g "-7" + result.AppendFormat(CultureInfo.InvariantCulture, "{0:0}", offset.Hours); + } + else + { + // 'zz' or longer format e.g "-07" + result.AppendFormat(CultureInfo.InvariantCulture, "{0:00}", offset.Hours); + if (tokenLen >= 3) + { + // 'zzz*' or longer format e.g "-07:30" + result.AppendFormat(CultureInfo.InvariantCulture, ":{0:00}", offset.Minutes); + } + } + } + + // output the 'K' format, which is for round-tripping the data + private static void FormatCustomizedRoundripTimeZone(DateTime dateTime, TimeSpan offset, StringBuilder result) + { + // The objective of this format is to round trip the data in the type + // For DateTime it should round-trip the Kind value and preserve the time zone. + // DateTimeOffset instance, it should do so by using the internal time zone. + + if (offset == NullOffset) + { + // source is a date time, so behavior depends on the kind. + switch (dateTime.Kind) + { + case DateTimeKind.Local: + // This should output the local offset, e.g. "-07:30" + offset = TimeZoneInfo.GetLocalUtcOffset(dateTime, TimeZoneInfoOptions.NoThrowOnInvalidTime); + // fall through to shared time zone output code + break; + case DateTimeKind.Utc: + // The 'Z' constant is a marker for a UTC date + result.Append("Z"); + return; + default: + // If the kind is unspecified, we output nothing here + return; + } + } + if (offset >= TimeSpan.Zero) + { + result.Append('+'); + } + else + { + result.Append('-'); + // get a positive offset, so that you don't need a separate code path for the negative numbers. + offset = offset.Negate(); + } + + AppendNumber(result, offset.Hours, 2); + result.Append(':'); + AppendNumber(result, offset.Minutes, 2); + } + + + internal static String GetRealFormat(String format, DateTimeFormatInfo dtfi) + { + String realFormat = null; + + switch (format[0]) + { + case 'd': // Short Date + realFormat = dtfi.ShortDatePattern; + break; + case 'D': // Long Date + realFormat = dtfi.LongDatePattern; + break; + case 'f': // Full (long date + short time) + realFormat = dtfi.LongDatePattern + " " + dtfi.ShortTimePattern; + break; + case 'F': // Full (long date + long time) + realFormat = dtfi.FullDateTimePattern; + break; + case 'g': // General (short date + short time) + realFormat = dtfi.GeneralShortTimePattern; + break; + case 'G': // General (short date + long time) + realFormat = dtfi.GeneralLongTimePattern; + break; + case 'm': + case 'M': // Month/Day Date + realFormat = dtfi.MonthDayPattern; + break; + case 'o': + case 'O': + realFormat = RoundtripFormat; + break; + case 'r': + case 'R': // RFC 1123 Standard + realFormat = dtfi.RFC1123Pattern; + break; + case 's': // Sortable without Time Zone Info + realFormat = dtfi.SortableDateTimePattern; + break; + case 't': // Short Time + realFormat = dtfi.ShortTimePattern; + break; + case 'T': // Long Time + realFormat = dtfi.LongTimePattern; + break; + case 'u': // Universal with Sortable format + realFormat = dtfi.UniversalSortableDateTimePattern; + break; + case 'U': // Universal with Full (long date + long time) format + realFormat = dtfi.FullDateTimePattern; + break; + case 'y': + case 'Y': // Year/Month Date + realFormat = dtfi.YearMonthPattern; + break; + default: + throw new FormatException(SR.Format_InvalidString); + } + return (realFormat); + } + + + // Expand a pre-defined format string (like "D" for long date) to the real format that + // we are going to use in the date time parsing. + // This method also convert the dateTime if necessary (e.g. when the format is in Universal time), + // and change dtfi if necessary (e.g. when the format should use invariant culture). + // + private static String ExpandPredefinedFormat(String format, ref DateTime dateTime, ref DateTimeFormatInfo dtfi, ref TimeSpan offset) + { + switch (format[0]) + { + case 'o': + case 'O': // Round trip format + dtfi = DateTimeFormatInfo.InvariantInfo; + break; + case 'r': + case 'R': // RFC 1123 Standard + if (offset != NullOffset) + { + // Convert to UTC invariants mean this will be in range + dateTime = dateTime - offset; + } + else if (dateTime.Kind == DateTimeKind.Local) + { + InvalidFormatForLocal(format, dateTime); + } + dtfi = DateTimeFormatInfo.InvariantInfo; + break; + case 's': // Sortable without Time Zone Info + dtfi = DateTimeFormatInfo.InvariantInfo; + break; + case 'u': // Universal time in sortable format. + if (offset != NullOffset) + { + // Convert to UTC invariants mean this will be in range + dateTime = dateTime - offset; + } + else if (dateTime.Kind == DateTimeKind.Local) + { + InvalidFormatForLocal(format, dateTime); + } + dtfi = DateTimeFormatInfo.InvariantInfo; + break; + case 'U': // Universal time in culture dependent format. + if (offset != NullOffset) + { + // This format is not supported by DateTimeOffset + throw new FormatException(SR.Format_InvalidString); + } + // Universal time is always in Greogrian calendar. + // + // Change the Calendar to be Gregorian Calendar. + // + dtfi = (DateTimeFormatInfo)dtfi.Clone(); + if (dtfi.Calendar.GetType() != typeof(GregorianCalendar)) + { + dtfi.Calendar = GregorianCalendar.GetDefaultInstance(); + } + dateTime = dateTime.ToUniversalTime(); + break; + } + format = GetRealFormat(format, dtfi); + return (format); + } + + internal static String Format(DateTime dateTime, String format, DateTimeFormatInfo dtfi) + { + return Format(dateTime, format, dtfi, NullOffset); + } + + + internal static String Format(DateTime dateTime, String format, DateTimeFormatInfo dtfi, TimeSpan offset) + { + Contract.Requires(dtfi != null); + if (format == null || format.Length == 0) + { + Boolean timeOnlySpecialCase = false; + if (dateTime.Ticks < Calendar.TicksPerDay) + { + // If the time is less than 1 day, consider it as time of day. + // Just print out the short time format. + // + // This is a workaround for VB, since they use ticks less then one day to be + // time of day. In cultures which use calendar other than Gregorian calendar, these + // alternative calendar may not support ticks less than a day. + // For example, Japanese calendar only supports date after 1868/9/8. + // This will pose a problem when people in VB get the time of day, and use it + // to call ToString(), which will use the general format (short date + long time). + // Since Japanese calendar does not support Gregorian year 0001, an exception will be + // thrown when we try to get the Japanese year for Gregorian year 0001. + // Therefore, the workaround allows them to call ToString() for time of day from a DateTime by + // formatting as ISO 8601 format. + switch (dtfi.Calendar.ID) + { + case CalendarId.JAPAN: + case CalendarId.TAIWAN: + case CalendarId.HIJRI: + case CalendarId.HEBREW: + case CalendarId.JULIAN: + case CalendarId.UMALQURA: + case CalendarId.PERSIAN: + timeOnlySpecialCase = true; + dtfi = DateTimeFormatInfo.InvariantInfo; + break; + } + } + if (offset == NullOffset) + { + // Default DateTime.ToString case. + if (timeOnlySpecialCase) + { + format = "s"; + } + else + { + format = "G"; + } + } + else + { + // Default DateTimeOffset.ToString case. + if (timeOnlySpecialCase) + { + format = RoundtripDateTimeUnfixed; + } + else + { + format = dtfi.DateTimeOffsetPattern; + } + } + } + + if (format.Length == 1) + { + switch (format[0]) + { + case 'O': + case 'o': + return FastFormatRoundtrip(dateTime, offset); + case 'R': + case 'r': + return FastFormatRfc1123(dateTime, offset, dtfi); + } + + format = ExpandPredefinedFormat(format, ref dateTime, ref dtfi, ref offset); + } + + return (FormatCustomized(dateTime, format, dtfi, offset)); + } + + internal static string FastFormatRfc1123(DateTime dateTime, TimeSpan offset, DateTimeFormatInfo dtfi) + { + // ddd, dd MMM yyyy HH:mm:ss GMT + const int Rfc1123FormatLength = 29; + StringBuilder result = StringBuilderCache.Acquire(Rfc1123FormatLength); + + if (offset != NullOffset) + { + // Convert to UTC invariants + dateTime = dateTime - offset; + } + + result.Append(InvariantAbbreviatedDayNames[(int)dateTime.DayOfWeek]); + result.Append(','); + result.Append(' '); + AppendNumber(result, dateTime.Day, 2); + result.Append(' '); + result.Append(InvariantAbbreviatedMonthNames[dateTime.Month - 1]); + result.Append(' '); + AppendNumber(result, dateTime.Year, 4); + result.Append(' '); + AppendHHmmssTimeOfDay(result, dateTime); + result.Append(' '); + result.Append(Gmt); + + return StringBuilderCache.GetStringAndRelease(result); + } + + internal static string FastFormatRoundtrip(DateTime dateTime, TimeSpan offset) + { + // yyyy-MM-ddTHH:mm:ss.fffffffK + const int roundTripFormatLength = 28; + StringBuilder result = StringBuilderCache.Acquire(roundTripFormatLength); + + AppendNumber(result, dateTime.Year, 4); + result.Append('-'); + AppendNumber(result, dateTime.Month, 2); + result.Append('-'); + AppendNumber(result, dateTime.Day, 2); + result.Append('T'); + AppendHHmmssTimeOfDay(result, dateTime); + result.Append('.'); + + long fraction = dateTime.Ticks % TimeSpan.TicksPerSecond; + AppendNumber(result, fraction, 7); + + FormatCustomizedRoundripTimeZone(dateTime, offset, result); + + return StringBuilderCache.GetStringAndRelease(result); + } + + private static void AppendHHmmssTimeOfDay(StringBuilder result, DateTime dateTime) + { + // HH:mm:ss + AppendNumber(result, dateTime.Hour, 2); + result.Append(':'); + AppendNumber(result, dateTime.Minute, 2); + result.Append(':'); + AppendNumber(result, dateTime.Second, 2); + } + + internal static void AppendNumber(StringBuilder builder, long val, int digits) + { + for (int i = 0; i < digits; i++) + { + builder.Append('0'); + } + + int index = 1; + while (val > 0 && index <= digits) + { + builder[builder.Length - index] = (char)('0' + (val % 10)); + val = val / 10; + index++; + } + + Debug.Assert(val == 0, "DateTimeFormat.AppendNumber(): digits less than size of val"); + } + + internal static String[] GetAllDateTimes(DateTime dateTime, char format, DateTimeFormatInfo dtfi) + { + Contract.Requires(dtfi != null); + String[] allFormats = null; + String[] results = null; + + switch (format) + { + case 'd': + case 'D': + case 'f': + case 'F': + case 'g': + case 'G': + case 'm': + case 'M': + case 't': + case 'T': + case 'y': + case 'Y': + allFormats = dtfi.GetAllDateTimePatterns(format); + results = new String[allFormats.Length]; + for (int i = 0; i < allFormats.Length; i++) + { + results[i] = Format(dateTime, allFormats[i], dtfi); + } + break; + case 'U': + DateTime universalTime = dateTime.ToUniversalTime(); + allFormats = dtfi.GetAllDateTimePatterns(format); + results = new String[allFormats.Length]; + for (int i = 0; i < allFormats.Length; i++) + { + results[i] = Format(universalTime, allFormats[i], dtfi); + } + break; + // + // The following ones are special cases because these patterns are read-only in + // DateTimeFormatInfo. + // + case 'r': + case 'R': + case 'o': + case 'O': + case 's': + case 'u': + results = new String[] { Format(dateTime, new String(format, 1), dtfi) }; + break; + default: + throw new FormatException(SR.Format_InvalidString); + } + return (results); + } + + internal static String[] GetAllDateTimes(DateTime dateTime, DateTimeFormatInfo dtfi) + { + List<String> results = new List<String>(DEFAULT_ALL_DATETIMES_SIZE); + + for (int i = 0; i < allStandardFormats.Length; i++) + { + String[] strings = GetAllDateTimes(dateTime, allStandardFormats[i], dtfi); + for (int j = 0; j < strings.Length; j++) + { + results.Add(strings[j]); + } + } + String[] value = new String[results.Count]; + results.CopyTo(0, value, 0, results.Count); + return (value); + } + + // This is a placeholder for an MDA to detect when the user is using a + // local DateTime with a format that will be interpreted as UTC. + internal static void InvalidFormatForLocal(String format, DateTime dateTime) + { + } + } +} diff --git a/src/mscorlib/shared/System/Globalization/DateTimeFormatInfo.cs b/src/mscorlib/shared/System/Globalization/DateTimeFormatInfo.cs new file mode 100644 index 0000000000..d3f1ea9a45 --- /dev/null +++ b/src/mscorlib/shared/System/Globalization/DateTimeFormatInfo.cs @@ -0,0 +1,3028 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.Contracts; +using System.Runtime.Serialization; + +namespace System.Globalization +{ + // + // Flags used to indicate different styles of month names. + // This is an internal flag used by internalGetMonthName(). + // Use flag here in case that we need to provide a combination of these styles + // (such as month name of a leap year in genitive form. Not likely for now, + // but would like to keep the option open). + // + + [Flags] + internal enum MonthNameStyles + { + Regular = 0x00000000, + Genitive = 0x00000001, + LeapYear = 0x00000002, + } + + // + // Flags used to indicate special rule used in parsing/formatting + // for a specific DateTimeFormatInfo instance. + // This is an internal flag. + // + // This flag is different from MonthNameStyles because this flag + // can be expanded to accomodate parsing behaviors like CJK month names + // or alternative month names, etc. + + [Flags] + internal enum DateTimeFormatFlags + { + None = 0x00000000, + UseGenitiveMonth = 0x00000001, + UseLeapYearMonth = 0x00000002, + UseSpacesInMonthNames = 0x00000004, // Has spaces or non-breaking space in the month names. + UseHebrewRule = 0x00000008, // Format/Parse using the Hebrew calendar rule. + UseSpacesInDayNames = 0x00000010, // Has spaces or non-breaking space in the day names. + UseDigitPrefixInTokens = 0x00000020, // Has token starting with numbers. + + NotInitialized = -1, + } + + + [Serializable] + public sealed class DateTimeFormatInfo : IFormatProvider, ICloneable + { + // cache for the invariant culture. + // invariantInfo is constant irrespective of your current culture. + private static volatile DateTimeFormatInfo s_invariantInfo; + + // an index which points to a record in Culture Data Table. + [NonSerialized] + private CultureData _cultureData; + + // The culture name used to create this DTFI. + + [OptionalField(VersionAdded = 2)] + private String _name = null; + + // The language name of the culture used to create this DTFI. + [NonSerialized] + private String _langName = null; + + // CompareInfo usually used by the parser. + [NonSerialized] + private CompareInfo _compareInfo = null; + + // Culture matches current DTFI. mainly used for string comparisons during parsing. + [NonSerialized] + private CultureInfo _cultureInfo = null; + + // + // Caches for various properties. + // + + private String amDesignator = null; + private String pmDesignator = null; + + private String dateSeparator = null; // derived from short date (whidbey expects, arrowhead doesn't) + + private String generalShortTimePattern = null; // short date + short time (whidbey expects, arrowhead doesn't) + + private String generalLongTimePattern = null; // short date + long time (whidbey expects, arrowhead doesn't) + + private String timeSeparator = null; // derived from long time (whidbey expects, arrowhead doesn't) + private String monthDayPattern = null; + // added in .NET Framework Release {2.0SP1/3.0SP1/3.5RTM} + private String dateTimeOffsetPattern = null; + + // + // The following are constant values. + // + private const String rfc1123Pattern = "ddd, dd MMM yyyy HH':'mm':'ss 'GMT'"; + + // The sortable pattern is based on ISO 8601. + private const String sortableDateTimePattern = "yyyy'-'MM'-'dd'T'HH':'mm':'ss"; + private const String universalSortableDateTimePattern = "yyyy'-'MM'-'dd HH':'mm':'ss'Z'"; + + // + // The following are affected by calendar settings. + // + private Calendar calendar = null; + + private int firstDayOfWeek = -1; + private int calendarWeekRule = -1; + + + private String fullDateTimePattern = null; // long date + long time (whidbey expects, arrowhead doesn't) + + private String[] abbreviatedDayNames = null; + + + private String[] m_superShortDayNames = null; + + private String[] dayNames = null; + private String[] abbreviatedMonthNames = null; + private String[] monthNames = null; + // Cache the genitive month names that we retrieve from the data table. + + private String[] genitiveMonthNames = null; + + // Cache the abbreviated genitive month names that we retrieve from the data table. + + private String[] m_genitiveAbbreviatedMonthNames = null; + + // Cache the month names of a leap year that we retrieve from the data table. + + private String[] leapYearMonthNames = null; + + // For our "patterns" arrays we have 2 variables, a string and a string[] + // + // The string[] contains the list of patterns, EXCEPT the default may not be included. + // The string contains the default pattern. + // When we initially construct our string[], we set the string to string[0] + + // The "default" Date/time patterns + private String longDatePattern = null; + private String shortDatePattern = null; + private String yearMonthPattern = null; + private String longTimePattern = null; + private String shortTimePattern = null; + + [OptionalField(VersionAdded = 3)] + private String[] allYearMonthPatterns = null; + + private String[] allShortDatePatterns = null; + private String[] allLongDatePatterns = null; + private String[] allShortTimePatterns = null; + private String[] allLongTimePatterns = null; + + // Cache the era names for this DateTimeFormatInfo instance. + private String[] m_eraNames = null; + private String[] m_abbrevEraNames = null; + private String[] m_abbrevEnglishEraNames = null; + + private CalendarId[] optionalCalendars = null; + + private const int DEFAULT_ALL_DATETIMES_SIZE = 132; + + // CultureInfo updates this + internal bool _isReadOnly = false; + + // This flag gives hints about if formatting/parsing should perform special code path for things like + // genitive form or leap year month names. + + private DateTimeFormatFlags formatFlags = DateTimeFormatFlags.NotInitialized; + + private String CultureName + { + get + { + if (_name == null) + { + _name = _cultureData.CultureName; + } + return (_name); + } + } + + private CultureInfo Culture + { + get + { + if (_cultureInfo == null) + { + _cultureInfo = CultureInfo.GetCultureInfo(this.CultureName); + } + return _cultureInfo; + } + } + + // TODO: This ignores other cultures that might want to do something similar + private String LanguageName + { + get + { + if (_langName == null) + { + _langName = _cultureData.SISO639LANGNAME; + } + return (_langName); + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Create an array of string which contains the abbreviated day names. + // + //////////////////////////////////////////////////////////////////////////// + + private String[] internalGetAbbreviatedDayOfWeekNames() + { + if (this.abbreviatedDayNames == null) + { + // Get the abbreviated day names for our current calendar + this.abbreviatedDayNames = _cultureData.AbbreviatedDayNames(Calendar.ID); + Debug.Assert(this.abbreviatedDayNames.Length == 7, "[DateTimeFormatInfo.GetAbbreviatedDayOfWeekNames] Expected 7 day names in a week"); + } + return (this.abbreviatedDayNames); + } + + + //////////////////////////////////////////////////////////////////////// + // + // Action: Returns the string array of the one-letter day of week names. + // Returns: + // an array of one-letter day of week names + // Arguments: + // None + // Exceptions: + // None + // + //////////////////////////////////////////////////////////////////////// + + private String[] internalGetSuperShortDayNames() + { + if (this.m_superShortDayNames == null) + { + // Get the super short day names for our current calendar + this.m_superShortDayNames = _cultureData.SuperShortDayNames(Calendar.ID); + Debug.Assert(this.m_superShortDayNames.Length == 7, "[DateTimeFormatInfo.internalGetSuperShortDayNames] Expected 7 day names in a week"); + } + return (this.m_superShortDayNames); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Create an array of string which contains the day names. + // + //////////////////////////////////////////////////////////////////////////// + + private String[] internalGetDayOfWeekNames() + { + if (this.dayNames == null) + { + // Get the day names for our current calendar + this.dayNames = _cultureData.DayNames(Calendar.ID); + Debug.Assert(this.dayNames.Length == 7, "[DateTimeFormatInfo.GetDayOfWeekNames] Expected 7 day names in a week"); + } + return (this.dayNames); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Create an array of string which contains the abbreviated month names. + // + //////////////////////////////////////////////////////////////////////////// + + private String[] internalGetAbbreviatedMonthNames() + { + if (this.abbreviatedMonthNames == null) + { + // Get the month names for our current calendar + this.abbreviatedMonthNames = _cultureData.AbbreviatedMonthNames(Calendar.ID); + Debug.Assert(this.abbreviatedMonthNames.Length == 12 || this.abbreviatedMonthNames.Length == 13, + "[DateTimeFormatInfo.GetAbbreviatedMonthNames] Expected 12 or 13 month names in a year"); + } + return (this.abbreviatedMonthNames); + } + + + //////////////////////////////////////////////////////////////////////////// + // + // Create an array of string which contains the month names. + // + //////////////////////////////////////////////////////////////////////////// + + private String[] internalGetMonthNames() + { + if (this.monthNames == null) + { + // Get the month names for our current calendar + this.monthNames = _cultureData.MonthNames(Calendar.ID); + Debug.Assert(this.monthNames.Length == 12 || this.monthNames.Length == 13, + "[DateTimeFormatInfo.GetMonthNames] Expected 12 or 13 month names in a year"); + } + + return (this.monthNames); + } + + + // + // Invariant DateTimeFormatInfo doesn't have user-overriden values + // Default calendar is gregorian + public DateTimeFormatInfo() + : this(CultureInfo.InvariantCulture._cultureData, GregorianCalendar.GetDefaultInstance()) + { + } + + internal DateTimeFormatInfo(CultureData cultureData, Calendar cal) + { + Debug.Assert(cultureData != null); + Debug.Assert(cal != null); + + // Remember our culture + _cultureData = cultureData; + + this.Calendar = cal; + } + + private void InitializeOverridableProperties(CultureData cultureData, CalendarId calendarId) + { + Debug.Assert(cultureData != null); + Debug.Assert(calendarId != CalendarId.UNINITIALIZED_VALUE, "[DateTimeFormatInfo.Populate] Expected initalized calendarId"); + + if (this.firstDayOfWeek == -1) { this.firstDayOfWeek = cultureData.IFIRSTDAYOFWEEK; } + if (this.calendarWeekRule == -1) { this.calendarWeekRule = cultureData.IFIRSTWEEKOFYEAR; } + + if (this.amDesignator == null) { this.amDesignator = cultureData.SAM1159; } + if (this.pmDesignator == null) { this.pmDesignator = cultureData.SPM2359; } + if (this.timeSeparator == null) { this.timeSeparator = cultureData.TimeSeparator; } + if (this.dateSeparator == null) { this.dateSeparator = cultureData.DateSeparator(calendarId); } + + this.allLongTimePatterns = _cultureData.LongTimes; + Debug.Assert(this.allLongTimePatterns.Length > 0, "[DateTimeFormatInfo.Populate] Expected some long time patterns"); + + this.allShortTimePatterns = _cultureData.ShortTimes; + Debug.Assert(this.allShortTimePatterns.Length > 0, "[DateTimeFormatInfo.Populate] Expected some short time patterns"); + + this.allLongDatePatterns = cultureData.LongDates(calendarId); + Debug.Assert(this.allLongDatePatterns.Length > 0, "[DateTimeFormatInfo.Populate] Expected some long date patterns"); + + this.allShortDatePatterns = cultureData.ShortDates(calendarId); + Debug.Assert(this.allShortDatePatterns.Length > 0, "[DateTimeFormatInfo.Populate] Expected some short date patterns"); + + this.allYearMonthPatterns = cultureData.YearMonths(calendarId); + Debug.Assert(this.allYearMonthPatterns.Length > 0, "[DateTimeFormatInfo.Populate] Expected some year month patterns"); + } + + [OptionalField(VersionAdded = 1)] + private bool _useUserOverride; + + // This was synthesized by Whidbey so we knew what words might appear in the middle of a date string + // Now we always synthesize so its not helpful + + internal String[] m_dateWords = null; + + [OnSerializing] + private void OnSerializing(StreamingContext ctx) + { + _name = this.CultureName; // make sure the _name is initialized. + _useUserOverride = _cultureData.UseUserOverride; + + // Important to initialize these fields otherwise we may run into exception when deserializing on Whidbey + // because Whidbey try to initialize some of these fields using calendar data which could be null values + // and then we get exceptions. So we call the accessors to force the caches to get loaded. + Object o; + o = this.LongTimePattern; + o = this.LongDatePattern; + o = this.ShortTimePattern; + o = this.ShortDatePattern; + o = this.YearMonthPattern; + o = this.AllLongTimePatterns; + o = this.AllLongDatePatterns; + o = this.AllShortTimePatterns; + o = this.AllShortDatePatterns; + o = this.AllYearMonthPatterns; + } + + [OnDeserialized] + private void OnDeserialized(StreamingContext ctx) + { + if (_name != null) + { + _cultureData = CultureData.GetCultureData(_name, _useUserOverride); + if (_cultureData == null) + { + throw new CultureNotFoundException("_name", _name, SR.Argument_CultureNotSupported); + } + } + + if (calendar == null) + { + calendar = (Calendar)GregorianCalendar.GetDefaultInstance().Clone(); + calendar.SetReadOnlyState(_isReadOnly); + } + + InitializeOverridableProperties(_cultureData, calendar.ID); + + // + // turn off read only state till we finish initializing all fields and then store read only state after we are done. + // + bool isReadOnly = _isReadOnly; + _isReadOnly = false; + + // If we deserialized defaults ala Whidbey, make sure they're still defaults + // Whidbey's arrays could get a bit mixed up. + if (longDatePattern != null) this.LongDatePattern = longDatePattern; + if (shortDatePattern != null) this.ShortDatePattern = shortDatePattern; + if (yearMonthPattern != null) this.YearMonthPattern = yearMonthPattern; + if (longTimePattern != null) this.LongTimePattern = longTimePattern; + if (shortTimePattern != null) this.ShortTimePattern = shortTimePattern; + + _isReadOnly = isReadOnly; + } + + // Returns a default DateTimeFormatInfo that will be universally + // supported and constant irrespective of the current culture. + // Used by FromString methods. + // + + public static DateTimeFormatInfo InvariantInfo + { + get + { + Contract.Ensures(Contract.Result<DateTimeFormatInfo>() != null); + if (s_invariantInfo == null) + { + DateTimeFormatInfo info = new DateTimeFormatInfo(); + info.Calendar.SetReadOnlyState(true); + info._isReadOnly = true; + s_invariantInfo = info; + } + return (s_invariantInfo); + } + } + + // Returns the current culture's DateTimeFormatInfo. Used by Parse methods. + // + + public static DateTimeFormatInfo CurrentInfo + { + get + { + Contract.Ensures(Contract.Result<DateTimeFormatInfo>() != null); + System.Globalization.CultureInfo culture = System.Globalization.CultureInfo.CurrentCulture; + if (!culture._isInherited) + { + DateTimeFormatInfo info = culture.dateTimeInfo; + if (info != null) + { + return info; + } + } + return (DateTimeFormatInfo)culture.GetFormat(typeof(DateTimeFormatInfo)); + } + } + + + public static DateTimeFormatInfo GetInstance(IFormatProvider provider) + { + // Fast case for a regular CultureInfo + DateTimeFormatInfo info; + CultureInfo cultureProvider = provider as CultureInfo; + if (cultureProvider != null && !cultureProvider._isInherited) + { + return cultureProvider.DateTimeFormat; + } + // Fast case for a DTFI; + info = provider as DateTimeFormatInfo; + if (info != null) + { + return info; + } + // Wasn't cultureInfo or DTFI, do it the slower way + if (provider != null) + { + info = provider.GetFormat(typeof(DateTimeFormatInfo)) as DateTimeFormatInfo; + if (info != null) + { + return info; + } + } + // Couldn't get anything, just use currentInfo as fallback + return CurrentInfo; + } + + + public Object GetFormat(Type formatType) + { + return (formatType == typeof(DateTimeFormatInfo) ? this : null); + } + + + public Object Clone() + { + DateTimeFormatInfo n = (DateTimeFormatInfo)MemberwiseClone(); + // We can use the data member calendar in the setter, instead of the property Calendar, + // since the cloned copy should have the same state as the original copy. + n.calendar = (Calendar)this.Calendar.Clone(); + n._isReadOnly = false; + return n; + } + + + public String AMDesignator + { + get + { + if (this.amDesignator == null) + { + this.amDesignator = _cultureData.SAM1159; + } + Debug.Assert(this.amDesignator != null, "DateTimeFormatInfo.AMDesignator, amDesignator != null"); + return (this.amDesignator); + } + + set + { + if (IsReadOnly) + throw new InvalidOperationException(SR.InvalidOperation_ReadOnly); + if (value == null) + { + throw new ArgumentNullException(nameof(value), + SR.ArgumentNull_String); + } + Contract.EndContractBlock(); + ClearTokenHashTable(); + amDesignator = value; + } + } + + + public Calendar Calendar + { + get + { + Contract.Ensures(Contract.Result<Calendar>() != null); + + Debug.Assert(this.calendar != null, "DateTimeFormatInfo.Calendar: calendar != null"); + return (this.calendar); + } + + set + { + if (IsReadOnly) + throw new InvalidOperationException(SR.InvalidOperation_ReadOnly); + if (value == null) + { + throw new ArgumentNullException(nameof(value), SR.ArgumentNull_Obj); + } + Contract.EndContractBlock(); + if (value == calendar) + { + return; + } + + for (int i = 0; i < this.OptionalCalendars.Length; i++) + { + if (this.OptionalCalendars[i] == value.ID) + { + // We can use this one, so do so. + + // Clean related properties if we already had a calendar set + if (calendar != null) + { + // clean related properties which are affected by the calendar setting, + // so that they will be refreshed when they are accessed next time. + // + + // These properites are in the order as appearing in calendar.xml. + m_eraNames = null; + m_abbrevEraNames = null; + m_abbrevEnglishEraNames = null; + + monthDayPattern = null; + + dayNames = null; + abbreviatedDayNames = null; + m_superShortDayNames = null; + monthNames = null; + abbreviatedMonthNames = null; + genitiveMonthNames = null; + m_genitiveAbbreviatedMonthNames = null; + leapYearMonthNames = null; + formatFlags = DateTimeFormatFlags.NotInitialized; + + allShortDatePatterns = null; + allLongDatePatterns = null; + allYearMonthPatterns = null; + dateTimeOffsetPattern = null; + + // The defaults need reset as well: + longDatePattern = null; + shortDatePattern = null; + yearMonthPattern = null; + + // These properies are not in the OS data, but they are dependent on the values like shortDatePattern. + fullDateTimePattern = null; // Long date + long time + generalShortTimePattern = null; // short date + short time + generalLongTimePattern = null; // short date + long time + + // Derived item that changes + dateSeparator = null; + + // We don't need to do these because they are not changed by changing calendar + // amDesignator + // pmDesignator + // timeSeparator + // longTimePattern + // firstDayOfWeek + // calendarWeekRule + + // remember to reload tokens + ClearTokenHashTable(); + } + + // Remember the new calendar + calendar = value; + InitializeOverridableProperties(_cultureData, calendar.ID); + + // We succeeded, return + return; + } + } + + // The assigned calendar is not a valid calendar for this culture, throw + throw new ArgumentOutOfRangeException(nameof(value), SR.Argument_InvalidCalendar); + } + } + + private CalendarId[] OptionalCalendars + { + get + { + if (this.optionalCalendars == null) + { + this.optionalCalendars = _cultureData.CalendarIds; + } + return (this.optionalCalendars); + } + } + + /*=================================GetEra========================== + **Action: Get the era value by parsing the name of the era. + **Returns: The era value for the specified era name. + ** -1 if the name of the era is not valid or not supported. + **Arguments: eraName the name of the era. + **Exceptions: None. + ============================================================================*/ + + + public int GetEra(String eraName) + { + if (eraName == null) + { + throw new ArgumentNullException(nameof(eraName), + SR.ArgumentNull_String); + } + Contract.EndContractBlock(); + + // The Era Name and Abbreviated Era Name + // for Taiwan Calendar on non-Taiwan SKU returns empty string (which + // would be matched below) but we don't want the empty string to give + // us an Era number + // confer 85900 DTFI.GetEra("") should fail on all cultures + if (eraName.Length == 0) + { + return (-1); + } + + // The following is based on the assumption that the era value is starting from 1, and has a + // serial values. + // If that ever changes, the code has to be changed. + + // The calls to String.Compare should use the current culture for the string comparisons, but the + // invariant culture when comparing against the english names. + for (int i = 0; i < EraNames.Length; i++) + { + // Compare the era name in a case-insensitive way for the appropriate culture. + if (m_eraNames[i].Length > 0) + { + if (this.Culture.CompareInfo.Compare(eraName, m_eraNames[i], CompareOptions.IgnoreCase) == 0) + { + return (i + 1); + } + } + } + for (int i = 0; i < AbbreviatedEraNames.Length; i++) + { + // Compare the abbreviated era name in a case-insensitive way for the appropriate culture. + if (this.Culture.CompareInfo.Compare(eraName, m_abbrevEraNames[i], CompareOptions.IgnoreCase) == 0) + { + return (i + 1); + } + } + for (int i = 0; i < AbbreviatedEnglishEraNames.Length; i++) + { + // this comparison should use the InvariantCulture. The English name could have linguistically + // interesting characters. + if (CultureInfo.InvariantCulture.CompareInfo.Compare(eraName, m_abbrevEnglishEraNames[i], CompareOptions.IgnoreCase) == 0) + { + return (i + 1); + } + } + return (-1); + } + + + internal String[] EraNames + { + get + { + if (this.m_eraNames == null) + { + this.m_eraNames = _cultureData.EraNames(Calendar.ID); ; + } + return (this.m_eraNames); + } + } + + /*=================================GetEraName========================== + **Action: Get the name of the era for the specified era value. + **Returns: The name of the specified era. + **Arguments: + ** era the era value. + **Exceptions: + ** ArguementException if the era valie is invalid. + ============================================================================*/ + + // Era names are 1 indexed + public String GetEraName(int era) + { + if (era == Calendar.CurrentEra) + { + era = Calendar.CurrentEraValue; + } + + // The following is based on the assumption that the era value is starting from 1, and has a + // serial values. + // If that ever changes, the code has to be changed. + if ((--era) < EraNames.Length && (era >= 0)) + { + return (m_eraNames[era]); + } + throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue); + } + + internal String[] AbbreviatedEraNames + { + get + { + if (this.m_abbrevEraNames == null) + { + this.m_abbrevEraNames = _cultureData.AbbrevEraNames(Calendar.ID); + } + return (this.m_abbrevEraNames); + } + } + + // Era names are 1 indexed + public String GetAbbreviatedEraName(int era) + { + if (AbbreviatedEraNames.Length == 0) + { + // If abbreviation era name is not used in this culture, + // return the full era name. + return (GetEraName(era)); + } + if (era == Calendar.CurrentEra) + { + era = Calendar.CurrentEraValue; + } + if ((--era) < m_abbrevEraNames.Length && (era >= 0)) + { + return (m_abbrevEraNames[era]); + } + throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue); + } + + internal String[] AbbreviatedEnglishEraNames + { + get + { + if (this.m_abbrevEnglishEraNames == null) + { + Debug.Assert(Calendar.ID > 0, "[DateTimeFormatInfo.AbbreviatedEnglishEraNames] Expected Calendar.ID > 0"); + this.m_abbrevEnglishEraNames = _cultureData.AbbreviatedEnglishEraNames(Calendar.ID); + } + return (this.m_abbrevEnglishEraNames); + } + } + + // Note that cultureData derives this from the short date format (unless someone's set this previously) + // Note that this property is quite undesirable. + public string DateSeparator + { + get + { + if (dateSeparator == null) + { + dateSeparator = _cultureData.DateSeparator(Calendar.ID); + } + Debug.Assert(this.dateSeparator != null, "DateTimeFormatInfo.DateSeparator, dateSeparator != null"); + return dateSeparator; + } + set + { + if (IsReadOnly) + throw new InvalidOperationException(SR.InvalidOperation_ReadOnly); + + if (value == null) + { + throw new ArgumentNullException(nameof(value), SR.ArgumentNull_String); + } + Contract.EndContractBlock(); + ClearTokenHashTable(); + dateSeparator = value; + } + } + + public DayOfWeek FirstDayOfWeek + { + get + { + if (this.firstDayOfWeek == -1) + { + this.firstDayOfWeek = _cultureData.IFIRSTDAYOFWEEK; + } + Debug.Assert(this.firstDayOfWeek != -1, "DateTimeFormatInfo.FirstDayOfWeek, firstDayOfWeek != -1"); + + return ((DayOfWeek)this.firstDayOfWeek); + } + + set + { + if (IsReadOnly) + throw new InvalidOperationException(SR.InvalidOperation_ReadOnly); + if (value >= DayOfWeek.Sunday && value <= DayOfWeek.Saturday) + { + firstDayOfWeek = (int)value; + } + else + { + throw new ArgumentOutOfRangeException( + nameof(value), SR.Format(SR.ArgumentOutOfRange_Range, + DayOfWeek.Sunday, DayOfWeek.Saturday)); + } + } + } + + public CalendarWeekRule CalendarWeekRule + { + get + { + if (this.calendarWeekRule == -1) + { + this.calendarWeekRule = _cultureData.IFIRSTWEEKOFYEAR; + } + Debug.Assert(this.calendarWeekRule != -1, "DateTimeFormatInfo.CalendarWeekRule, calendarWeekRule != -1"); + return ((CalendarWeekRule)this.calendarWeekRule); + } + + set + { + if (IsReadOnly) + throw new InvalidOperationException(SR.InvalidOperation_ReadOnly); + if (value >= CalendarWeekRule.FirstDay && value <= CalendarWeekRule.FirstFourDayWeek) + { + calendarWeekRule = (int)value; + } + else + { + throw new ArgumentOutOfRangeException( + nameof(value), SR.Format(SR.ArgumentOutOfRange_Range, + CalendarWeekRule.FirstDay, CalendarWeekRule.FirstFourDayWeek)); + } + } + } + + public String FullDateTimePattern + { + get + { + if (fullDateTimePattern == null) + { + fullDateTimePattern = LongDatePattern + " " + LongTimePattern; + } + return (fullDateTimePattern); + } + + set + { + if (IsReadOnly) + throw new InvalidOperationException(SR.InvalidOperation_ReadOnly); + if (value == null) + { + throw new ArgumentNullException(nameof(value), + SR.ArgumentNull_String); + } + Contract.EndContractBlock(); + fullDateTimePattern = value; + } + } + + + // For our "patterns" arrays we have 2 variables, a string and a string[] + // + // The string[] contains the list of patterns, EXCEPT the default may not be included. + // The string contains the default pattern. + // When we initially construct our string[], we set the string to string[0] + public String LongDatePattern + { + get + { + // Initialize our long date pattern from the 1st array value if not set + if (this.longDatePattern == null) + { + // Initialize our data + this.longDatePattern = this.UnclonedLongDatePatterns[0]; + } + + return this.longDatePattern; + } + + set + { + if (IsReadOnly) + throw new InvalidOperationException(SR.InvalidOperation_ReadOnly); + if (value == null) + { + throw new ArgumentNullException(nameof(value), + SR.ArgumentNull_String); + } + Contract.EndContractBlock(); + + // Remember the new string + this.longDatePattern = value; + + // Clear the token hash table + ClearTokenHashTable(); + + // Clean up cached values that will be affected by this property. + this.fullDateTimePattern = null; + } + } + + // For our "patterns" arrays we have 2 variables, a string and a string[] + // + // The string[] contains the list of patterns, EXCEPT the default may not be included. + // The string contains the default pattern. + // When we initially construct our string[], we set the string to string[0] + public String LongTimePattern + { + get + { + // Initialize our long time pattern from the 1st array value if not set + if (this.longTimePattern == null) + { + // Initialize our data + this.longTimePattern = this.UnclonedLongTimePatterns[0]; + } + + return this.longTimePattern; + } + + set + { + if (IsReadOnly) + throw new InvalidOperationException(SR.InvalidOperation_ReadOnly); + if (value == null) + { + throw new ArgumentNullException(nameof(value), + SR.ArgumentNull_String); + } + Contract.EndContractBlock(); + + // Remember the new string + this.longTimePattern = value; + + // Clear the token hash table + ClearTokenHashTable(); + + // Clean up cached values that will be affected by this property. + this.fullDateTimePattern = null; // Full date = long date + long Time + this.generalLongTimePattern = null; // General long date = short date + long Time + this.dateTimeOffsetPattern = null; + } + } + + + // Note: just to be confusing there's only 1 month day pattern, not a whole list + public String MonthDayPattern + { + get + { + if (this.monthDayPattern == null) + { + Debug.Assert(Calendar.ID > 0, "[DateTimeFormatInfo.MonthDayPattern] Expected calID > 0"); + this.monthDayPattern = _cultureData.MonthDay(Calendar.ID); + } + Debug.Assert(this.monthDayPattern != null, "DateTimeFormatInfo.MonthDayPattern, monthDayPattern != null"); + return (this.monthDayPattern); + } + + set + { + if (IsReadOnly) + throw new InvalidOperationException(SR.InvalidOperation_ReadOnly); + if (value == null) + { + throw new ArgumentNullException(nameof(value), + SR.ArgumentNull_String); + } + Contract.EndContractBlock(); + + this.monthDayPattern = value; + } + } + + + public String PMDesignator + { + get + { + if (this.pmDesignator == null) + { + this.pmDesignator = _cultureData.SPM2359; + } + Debug.Assert(this.pmDesignator != null, "DateTimeFormatInfo.PMDesignator, pmDesignator != null"); + return (this.pmDesignator); + } + + set + { + if (IsReadOnly) + throw new InvalidOperationException(SR.InvalidOperation_ReadOnly); + if (value == null) + { + throw new ArgumentNullException(nameof(value), + SR.ArgumentNull_String); + } + Contract.EndContractBlock(); + ClearTokenHashTable(); + + pmDesignator = value; + } + } + + + public String RFC1123Pattern + { + get + { + return (rfc1123Pattern); + } + } + + // For our "patterns" arrays we have 2 variables, a string and a string[] + // + // The string[] contains the list of patterns, EXCEPT the default may not be included. + // The string contains the default pattern. + // When we initially construct our string[], we set the string to string[0] + public String ShortDatePattern + { + get + { + // Initialize our short date pattern from the 1st array value if not set + if (this.shortDatePattern == null) + { + // Initialize our data + this.shortDatePattern = this.UnclonedShortDatePatterns[0]; + } + + return this.shortDatePattern; + } + + set + { + if (IsReadOnly) + throw new InvalidOperationException(SR.InvalidOperation_ReadOnly); + if (value == null) + throw new ArgumentNullException(nameof(value), + SR.ArgumentNull_String); + Contract.EndContractBlock(); + + // Remember the new string + this.shortDatePattern = value; + + // Clear the token hash table, note that even short dates could require this + ClearTokenHashTable(); + + // Clean up cached values that will be affected by this property. + generalLongTimePattern = null; // General long time = short date + long time + generalShortTimePattern = null; // General short time = short date + short Time + dateTimeOffsetPattern = null; + } + } + + + // For our "patterns" arrays we have 2 variables, a string and a string[] + // + // The string[] contains the list of patterns, EXCEPT the default may not be included. + // The string contains the default pattern. + // When we initially construct our string[], we set the string to string[0] + public String ShortTimePattern + { + get + { + // Initialize our short time pattern from the 1st array value if not set + if (this.shortTimePattern == null) + { + // Initialize our data + this.shortTimePattern = this.UnclonedShortTimePatterns[0]; + } + return this.shortTimePattern; + } + + set + { + if (IsReadOnly) + throw new InvalidOperationException(SR.InvalidOperation_ReadOnly); + if (value == null) + { + throw new ArgumentNullException(nameof(value), + SR.ArgumentNull_String); + } + Contract.EndContractBlock(); + + // Remember the new string + this.shortTimePattern = value; + + // Clear the token hash table, note that even short times could require this + ClearTokenHashTable(); + + // Clean up cached values that will be affected by this property. + generalShortTimePattern = null; // General short date = short date + short time. + } + } + + + public String SortableDateTimePattern + { + get + { + return (sortableDateTimePattern); + } + } + + /*=================================GeneralShortTimePattern===================== + **Property: Return the pattern for 'g' general format: shortDate + short time + **Note: This is used by DateTimeFormat.cs to get the pattern for 'g' + ** We put this internal property here so that we can avoid doing the + ** concatation every time somebody asks for the general format. + ==============================================================================*/ + + internal String GeneralShortTimePattern + { + get + { + if (generalShortTimePattern == null) + { + generalShortTimePattern = ShortDatePattern + " " + ShortTimePattern; + } + return (generalShortTimePattern); + } + } + + /*=================================GeneralLongTimePattern===================== + **Property: Return the pattern for 'g' general format: shortDate + Long time + **Note: This is used by DateTimeFormat.cs to get the pattern for 'g' + ** We put this internal property here so that we can avoid doing the + ** concatation every time somebody asks for the general format. + ==============================================================================*/ + + internal String GeneralLongTimePattern + { + get + { + if (generalLongTimePattern == null) + { + generalLongTimePattern = ShortDatePattern + " " + LongTimePattern; + } + return (generalLongTimePattern); + } + } + + /*=================================DateTimeOffsetPattern========================== + **Property: Return the default pattern DateTimeOffset : shortDate + long time + time zone offset + **Note: This is used by DateTimeFormat.cs to get the pattern for short Date + long time + time zone offset + ** We put this internal property here so that we can avoid doing the + ** concatation every time somebody uses this form + ==============================================================================*/ + + /*=================================DateTimeOffsetPattern========================== + **Property: Return the default pattern DateTimeOffset : shortDate + long time + time zone offset + **Note: This is used by DateTimeFormat.cs to get the pattern for short Date + long time + time zone offset + ** We put this internal property here so that we can avoid doing the + ** concatation every time somebody uses this form + ==============================================================================*/ + + internal String DateTimeOffsetPattern + { + get + { + if (dateTimeOffsetPattern == null) + { + string dateTimePattern = ShortDatePattern + " " + LongTimePattern; + + /* LongTimePattern might contain a "z" as part of the format string in which case we don't want to append a time zone offset */ + + bool foundZ = false; + bool inQuote = false; + char quote = '\''; + for (int i = 0; !foundZ && i < LongTimePattern.Length; i++) + { + switch (LongTimePattern[i]) + { + case 'z': + /* if we aren't in a quote, we've found a z */ + foundZ = !inQuote; + /* we'll fall out of the loop now because the test includes !foundZ */ + break; + case '\'': + case '\"': + if (inQuote && (quote == LongTimePattern[i])) + { + /* we were in a quote and found a matching exit quote, so we are outside a quote now */ + inQuote = false; + } + else if (!inQuote) + { + quote = LongTimePattern[i]; + inQuote = true; + } + else + { + /* we were in a quote and saw the other type of quote character, so we are still in a quote */ + } + break; + case '%': + case '\\': + i++; /* skip next character that is escaped by this backslash */ + break; + default: + break; + } + } + + if (!foundZ) + { + dateTimePattern = dateTimePattern + " zzz"; + } + + dateTimeOffsetPattern = dateTimePattern; + } + return (dateTimeOffsetPattern); + } + } + + // Note that cultureData derives this from the long time format (unless someone's set this previously) + // Note that this property is quite undesirable. + public string TimeSeparator + { + get + { + if (timeSeparator == null) + { + timeSeparator = _cultureData.TimeSeparator; + } + Debug.Assert(this.timeSeparator != null, "DateTimeFormatInfo.TimeSeparator, timeSeparator != null"); + return (timeSeparator); + } + + set + { + if (IsReadOnly) + throw new InvalidOperationException(SR.InvalidOperation_ReadOnly); + + if (value == null) + { + throw new ArgumentNullException(nameof(value), SR.ArgumentNull_String); + } + + Contract.EndContractBlock(); + ClearTokenHashTable(); + + timeSeparator = value; + } + } + + public String UniversalSortableDateTimePattern + { + get + { + return (universalSortableDateTimePattern); + } + } + + // For our "patterns" arrays we have 2 variables, a string and a string[] + // + // The string[] contains the list of patterns, EXCEPT the default may not be included. + // The string contains the default pattern. + // When we initially construct our string[], we set the string to string[0] + public String YearMonthPattern + { + get + { + // Initialize our year/month pattern from the 1st array value if not set + if (this.yearMonthPattern == null) + { + // Initialize our data + this.yearMonthPattern = this.UnclonedYearMonthPatterns[0]; + } + return this.yearMonthPattern; + } + + set + { + if (IsReadOnly) + throw new InvalidOperationException(SR.InvalidOperation_ReadOnly); + if (value == null) + { + throw new ArgumentNullException(nameof(value), + SR.ArgumentNull_String); + } + Contract.EndContractBlock(); + + // Remember the new string + this.yearMonthPattern = value; + + // Clear the token hash table, note that even short times could require this + ClearTokenHashTable(); + } + } + + // + // Check if a string array contains a null value, and throw ArgumentNullException with parameter name "value" + // + private static void CheckNullValue(String[] values, int length) + { + Debug.Assert(values != null, "value != null"); + Debug.Assert(values.Length >= length); + for (int i = 0; i < length; i++) + { + if (values[i] == null) + { + throw new ArgumentNullException("value", + SR.ArgumentNull_ArrayValue); + } + } + } + + + public String[] AbbreviatedDayNames + { + get + { + return ((String[])internalGetAbbreviatedDayOfWeekNames().Clone()); + } + + set + { + if (IsReadOnly) + throw new InvalidOperationException(SR.InvalidOperation_ReadOnly); + if (value == null) + { + throw new ArgumentNullException(nameof(value), + SR.ArgumentNull_Array); + } + if (value.Length != 7) + { + throw new ArgumentException(SR.Format(SR.Argument_InvalidArrayLength, 7), nameof(value)); + } + Contract.EndContractBlock(); + CheckNullValue(value, value.Length); + ClearTokenHashTable(); + + abbreviatedDayNames = value; + } + } + + // Returns the string array of the one-letter day of week names. + public String[] ShortestDayNames + { + get + { + return ((String[])internalGetSuperShortDayNames().Clone()); + } + + set + { + if (IsReadOnly) + throw new InvalidOperationException(SR.InvalidOperation_ReadOnly); + if (value == null) + { + throw new ArgumentNullException(nameof(value), + SR.ArgumentNull_Array); + } + if (value.Length != 7) + { + throw new ArgumentException(SR.Format(SR.Argument_InvalidArrayLength, 7), nameof(value)); + } + Contract.EndContractBlock(); + CheckNullValue(value, value.Length); + this.m_superShortDayNames = value; + } + } + + + public String[] DayNames + { + get + { + return ((String[])internalGetDayOfWeekNames().Clone()); + } + + set + { + if (IsReadOnly) + throw new InvalidOperationException(SR.InvalidOperation_ReadOnly); + if (value == null) + { + throw new ArgumentNullException(nameof(value), + SR.ArgumentNull_Array); + } + if (value.Length != 7) + { + throw new ArgumentException(SR.Format(SR.Argument_InvalidArrayLength, 7), nameof(value)); + } + Contract.EndContractBlock(); + CheckNullValue(value, value.Length); + ClearTokenHashTable(); + + dayNames = value; + } + } + + + public String[] AbbreviatedMonthNames + { + get + { + return ((String[])internalGetAbbreviatedMonthNames().Clone()); + } + + set + { + if (IsReadOnly) + throw new InvalidOperationException(SR.InvalidOperation_ReadOnly); + if (value == null) + { + throw new ArgumentNullException(nameof(value), + SR.ArgumentNull_Array); + } + if (value.Length != 13) + { + throw new ArgumentException(SR.Format(SR.Argument_InvalidArrayLength, 13), nameof(value)); + } + Contract.EndContractBlock(); + CheckNullValue(value, value.Length - 1); + ClearTokenHashTable(); + abbreviatedMonthNames = value; + } + } + + + public String[] MonthNames + { + get + { + return ((String[])internalGetMonthNames().Clone()); + } + + set + { + if (IsReadOnly) + throw new InvalidOperationException(SR.InvalidOperation_ReadOnly); + if (value == null) + { + throw new ArgumentNullException(nameof(value), + SR.ArgumentNull_Array); + } + if (value.Length != 13) + { + throw new ArgumentException(SR.Format(SR.Argument_InvalidArrayLength, 13), nameof(value)); + } + Contract.EndContractBlock(); + CheckNullValue(value, value.Length - 1); + monthNames = value; + ClearTokenHashTable(); + } + } + + // Whitespaces that we allow in the month names. + // U+00a0 is non-breaking space. + private static readonly char[] s_monthSpaces = { ' ', '\u00a0' }; + + internal bool HasSpacesInMonthNames + { + get + { + return (FormatFlags & DateTimeFormatFlags.UseSpacesInMonthNames) != 0; + } + } + + internal bool HasSpacesInDayNames + { + get + { + return (FormatFlags & DateTimeFormatFlags.UseSpacesInDayNames) != 0; + } + } + + + // + // internalGetMonthName + // + // Actions: Return the month name using the specified MonthNameStyles in either abbreviated form + // or full form. + // Arguments: + // month + // style To indicate a form like regular/genitive/month name in a leap year. + // abbreviated When true, return abbreviated form. Otherwise, return a full form. + // Exceptions: + // ArgumentOutOfRangeException When month name is invalid. + // + internal String internalGetMonthName(int month, MonthNameStyles style, bool abbreviated) + { + // + // Right now, style is mutual exclusive, but I make the style to be flag so that + // maybe we can combine flag if there is such a need. + // + String[] monthNamesArray = null; + switch (style) + { + case MonthNameStyles.Genitive: + monthNamesArray = internalGetGenitiveMonthNames(abbreviated); + break; + case MonthNameStyles.LeapYear: + monthNamesArray = internalGetLeapYearMonthNames(/*abbreviated*/); + break; + default: + monthNamesArray = (abbreviated ? internalGetAbbreviatedMonthNames() : internalGetMonthNames()); + break; + } + // The month range is from 1 ~ this.m_monthNames.Length + // (actually is 13 right now for all cases) + if ((month < 1) || (month > monthNamesArray.Length)) + { + throw new ArgumentOutOfRangeException( + nameof(month), SR.Format(SR.ArgumentOutOfRange_Range, + 1, monthNamesArray.Length)); + } + return (monthNamesArray[month - 1]); + } + + // + // internalGetGenitiveMonthNames + // + // Action: Retrieve the array which contains the month names in genitive form. + // If this culture does not use the gentive form, the normal month name is returned. + // Arguments: + // abbreviated When true, return abbreviated form. Otherwise, return a full form. + // + private String[] internalGetGenitiveMonthNames(bool abbreviated) + { + if (abbreviated) + { + if (this.m_genitiveAbbreviatedMonthNames == null) + { + this.m_genitiveAbbreviatedMonthNames = _cultureData.AbbreviatedGenitiveMonthNames(this.Calendar.ID); + Debug.Assert(this.m_genitiveAbbreviatedMonthNames.Length == 13, + "[DateTimeFormatInfo.GetGenitiveMonthNames] Expected 13 abbreviated genitive month names in a year"); + } + return (this.m_genitiveAbbreviatedMonthNames); + } + + if (this.genitiveMonthNames == null) + { + this.genitiveMonthNames = _cultureData.GenitiveMonthNames(this.Calendar.ID); + Debug.Assert(this.genitiveMonthNames.Length == 13, + "[DateTimeFormatInfo.GetGenitiveMonthNames] Expected 13 genitive month names in a year"); + } + return (this.genitiveMonthNames); + } + + // + // internalGetLeapYearMonthNames + // + // Actions: Retrieve the month names used in a leap year. + // If this culture does not have different month names in a leap year, the normal month name is returned. + // Agruments: None. (can use abbreviated later if needed) + // + internal String[] internalGetLeapYearMonthNames(/*bool abbreviated*/) + { + if (this.leapYearMonthNames == null) + { + Debug.Assert(Calendar.ID > 0, "[DateTimeFormatInfo.internalGetLeapYearMonthNames] Expected Calendar.ID > 0"); + this.leapYearMonthNames = _cultureData.LeapYearMonthNames(Calendar.ID); + Debug.Assert(this.leapYearMonthNames.Length == 13, + "[DateTimeFormatInfo.internalGetLeapYearMonthNames] Expepcted 13 leap year month names"); + } + return (leapYearMonthNames); + } + + + public String GetAbbreviatedDayName(DayOfWeek dayofweek) + { + if ((int)dayofweek < 0 || (int)dayofweek > 6) + { + throw new ArgumentOutOfRangeException( + nameof(dayofweek), SR.Format(SR.ArgumentOutOfRange_Range, + DayOfWeek.Sunday, DayOfWeek.Saturday)); + } + Contract.EndContractBlock(); + // + // Don't call the public property AbbreviatedDayNames here since a clone is needed in that + // property, so it will be slower. Instead, use GetAbbreviatedDayOfWeekNames() directly. + // + return (internalGetAbbreviatedDayOfWeekNames()[(int)dayofweek]); + } + + // Returns the super short day of week names for the specified day of week. + public string GetShortestDayName(DayOfWeek dayOfWeek) + { + if ((int)dayOfWeek < 0 || (int)dayOfWeek > 6) + { + throw new ArgumentOutOfRangeException( + nameof(dayOfWeek), SR.Format(SR.ArgumentOutOfRange_Range, + DayOfWeek.Sunday, DayOfWeek.Saturday)); + } + Contract.EndContractBlock(); + // + // Don't call the public property SuperShortDayNames here since a clone is needed in that + // property, so it will be slower. Instead, use internalGetSuperShortDayNames() directly. + // + return (internalGetSuperShortDayNames()[(int)dayOfWeek]); + } + + // Get all possible combination of inputs + private static String[] GetCombinedPatterns(String[] patterns1, String[] patterns2, String connectString) + { + Debug.Assert(patterns1 != null); + Debug.Assert(patterns2 != null); + + // Get array size + String[] result = new String[patterns1.Length * patterns2.Length]; + + // Counter of actual results + int k = 0; + for (int i = 0; i < patterns1.Length; i++) + { + for (int j = 0; j < patterns2.Length; j++) + { + // Can't combine if null or empty + result[k++] = patterns1[i] + connectString + patterns2[j]; + } + } + + // Return the combinations + return (result); + } + + public string[] GetAllDateTimePatterns() + { + List<String> results = new List<String>(DEFAULT_ALL_DATETIMES_SIZE); + + for (int i = 0; i < DateTimeFormat.allStandardFormats.Length; i++) + { + String[] strings = GetAllDateTimePatterns(DateTimeFormat.allStandardFormats[i]); + for (int j = 0; j < strings.Length; j++) + { + results.Add(strings[j]); + } + } + return results.ToArray(); + } + + public string[] GetAllDateTimePatterns(char format) + { + Contract.Ensures(Contract.Result<String[]>() != null); + String[] result = null; + + switch (format) + { + case 'd': + result = this.AllShortDatePatterns; + break; + case 'D': + result = this.AllLongDatePatterns; + break; + case 'f': + result = GetCombinedPatterns(AllLongDatePatterns, AllShortTimePatterns, " "); + break; + case 'F': + case 'U': + result = GetCombinedPatterns(AllLongDatePatterns, AllLongTimePatterns, " "); + break; + case 'g': + result = GetCombinedPatterns(AllShortDatePatterns, AllShortTimePatterns, " "); + break; + case 'G': + result = GetCombinedPatterns(AllShortDatePatterns, AllLongTimePatterns, " "); + break; + case 'm': + case 'M': + result = new String[] { MonthDayPattern }; + break; + case 'o': + case 'O': + result = new String[] { RoundtripFormat }; + break; + case 'r': + case 'R': + result = new String[] { rfc1123Pattern }; + break; + case 's': + result = new String[] { sortableDateTimePattern }; + break; + case 't': + result = this.AllShortTimePatterns; + break; + case 'T': + result = this.AllLongTimePatterns; + break; + case 'u': + result = new String[] { UniversalSortableDateTimePattern }; + break; + case 'y': + case 'Y': + result = this.AllYearMonthPatterns; + break; + default: + throw new ArgumentException(SR.Format_BadFormatSpecifier, nameof(format)); + } + return (result); + } + + + public String GetDayName(DayOfWeek dayofweek) + { + if ((int)dayofweek < 0 || (int)dayofweek > 6) + { + throw new ArgumentOutOfRangeException( + nameof(dayofweek), SR.Format(SR.ArgumentOutOfRange_Range, + DayOfWeek.Sunday, DayOfWeek.Saturday)); + } + Contract.EndContractBlock(); + + // Use the internal one so that we don't clone the array unnecessarily + return (internalGetDayOfWeekNames()[(int)dayofweek]); + } + + + + public String GetAbbreviatedMonthName(int month) + { + if (month < 1 || month > 13) + { + throw new ArgumentOutOfRangeException( + nameof(month), SR.Format(SR.ArgumentOutOfRange_Range, + 1, 13)); + } + Contract.EndContractBlock(); + // Use the internal one so we don't clone the array unnecessarily + return (internalGetAbbreviatedMonthNames()[month - 1]); + } + + + public String GetMonthName(int month) + { + if (month < 1 || month > 13) + { + throw new ArgumentOutOfRangeException( + nameof(month), SR.Format(SR.ArgumentOutOfRange_Range, + 1, 13)); + } + Contract.EndContractBlock(); + // Use the internal one so we don't clone the array unnecessarily + return (internalGetMonthNames()[month - 1]); + } + + // For our "patterns" arrays we have 2 variables, a string and a string[] + // + // The string[] contains the list of patterns, EXCEPT the default may not be included. + // The string contains the default pattern. + // When we initially construct our string[], we set the string to string[0] + // + // The resulting [] can get returned to the calling app, so clone it. + private static string[] GetMergedPatterns(string[] patterns, string defaultPattern) + { + Debug.Assert(patterns != null && patterns.Length > 0, + "[DateTimeFormatInfo.GetMergedPatterns]Expected array of at least one pattern"); + Debug.Assert(defaultPattern != null, + "[DateTimeFormatInfo.GetMergedPatterns]Expected non null default string"); + + // If the default happens to be the first in the list just return (a cloned) copy + if (defaultPattern == patterns[0]) + { + return (string[])patterns.Clone(); + } + + // We either need a bigger list, or the pattern from the list. + int i; + for (i = 0; i < patterns.Length; i++) + { + // Stop if we found it + if (defaultPattern == patterns[i]) + break; + } + + // Either way we're going to need a new array + string[] newPatterns; + + // Did we find it + if (i < patterns.Length) + { + // Found it, output will be same size + newPatterns = (string[])patterns.Clone(); + + // Have to move [0] item to [i] so we can re-write default at [0] + // (remember defaultPattern == [i] so this is OK) + newPatterns[i] = newPatterns[0]; + } + else + { + // Not found, make room for it + newPatterns = new String[patterns.Length + 1]; + + // Copy existing array + Array.Copy(patterns, 0, newPatterns, 1, patterns.Length); + } + + // Remember the default + newPatterns[0] = defaultPattern; + + // Return the reconstructed list + return newPatterns; + } + + // Needed by DateTimeFormatInfo and DateTimeFormat + internal const String RoundtripFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.fffffffK"; + internal const String RoundtripDateTimeUnfixed = "yyyy'-'MM'-'ddTHH':'mm':'ss zzz"; + + // Default string isn't necessarily in our string array, so get the + // merged patterns of both + private String[] AllYearMonthPatterns + { + get + { + return GetMergedPatterns(this.UnclonedYearMonthPatterns, this.YearMonthPattern); + } + } + + private String[] AllShortDatePatterns + { + get + { + return GetMergedPatterns(this.UnclonedShortDatePatterns, this.ShortDatePattern); + } + } + + private String[] AllShortTimePatterns + { + get + { + return GetMergedPatterns(this.UnclonedShortTimePatterns, this.ShortTimePattern); + } + } + + private String[] AllLongDatePatterns + { + get + { + return GetMergedPatterns(this.UnclonedLongDatePatterns, this.LongDatePattern); + } + } + + private String[] AllLongTimePatterns + { + get + { + return GetMergedPatterns(this.UnclonedLongTimePatterns, this.LongTimePattern); + } + } + + // NOTE: Clone this string array if you want to return it to user. Otherwise, you are returning a writable cache copy. + // This won't include default, call AllYearMonthPatterns + private String[] UnclonedYearMonthPatterns + { + get + { + if (allYearMonthPatterns == null) + { + Debug.Assert(Calendar.ID > 0, "[DateTimeFormatInfo.UnclonedYearMonthPatterns] Expected Calendar.ID > 0"); + this.allYearMonthPatterns = _cultureData.YearMonths(this.Calendar.ID); + Debug.Assert(this.allYearMonthPatterns.Length > 0, + "[DateTimeFormatInfo.UnclonedYearMonthPatterns] Expected some year month patterns"); + } + + return allYearMonthPatterns; + } + } + + + // NOTE: Clone this string array if you want to return it to user. Otherwise, you are returning a writable cache copy. + // This won't include default, call AllShortDatePatterns + private String[] UnclonedShortDatePatterns + { + get + { + if (allShortDatePatterns == null) + { + Debug.Assert(Calendar.ID > 0, "[DateTimeFormatInfo.UnclonedShortDatePatterns] Expected Calendar.ID > 0"); + this.allShortDatePatterns = _cultureData.ShortDates(this.Calendar.ID); + Debug.Assert(this.allShortDatePatterns.Length > 0, + "[DateTimeFormatInfo.UnclonedShortDatePatterns] Expected some short date patterns"); + } + + return this.allShortDatePatterns; + } + } + + // NOTE: Clone this string array if you want to return it to user. Otherwise, you are returning a writable cache copy. + // This won't include default, call AllLongDatePatterns + private String[] UnclonedLongDatePatterns + { + get + { + if (allLongDatePatterns == null) + { + Debug.Assert(Calendar.ID > 0, "[DateTimeFormatInfo.UnclonedLongDatePatterns] Expected Calendar.ID > 0"); + this.allLongDatePatterns = _cultureData.LongDates(this.Calendar.ID); + Debug.Assert(this.allLongDatePatterns.Length > 0, + "[DateTimeFormatInfo.UnclonedLongDatePatterns] Expected some long date patterns"); + } + + return this.allLongDatePatterns; + } + } + + // NOTE: Clone this string array if you want to return it to user. Otherwise, you are returning a writable cache copy. + // This won't include default, call AllShortTimePatterns + private String[] UnclonedShortTimePatterns + { + get + { + if (this.allShortTimePatterns == null) + { + this.allShortTimePatterns = _cultureData.ShortTimes; + Debug.Assert(this.allShortTimePatterns.Length > 0, + "[DateTimeFormatInfo.UnclonedShortTimePatterns] Expected some short time patterns"); + } + + return this.allShortTimePatterns; + } + } + + // NOTE: Clone this string array if you want to return it to user. Otherwise, you are returning a writable cache copy. + // This won't include default, call AllLongTimePatterns + private String[] UnclonedLongTimePatterns + { + get + { + if (this.allLongTimePatterns == null) + { + this.allLongTimePatterns = _cultureData.LongTimes; + Debug.Assert(this.allLongTimePatterns.Length > 0, + "[DateTimeFormatInfo.UnclonedLongTimePatterns] Expected some long time patterns"); + } + + return this.allLongTimePatterns; + } + } + + public static DateTimeFormatInfo ReadOnly(DateTimeFormatInfo dtfi) + { + if (dtfi == null) + { + throw new ArgumentNullException(nameof(dtfi), + SR.ArgumentNull_Obj); + } + Contract.EndContractBlock(); + if (dtfi.IsReadOnly) + { + return (dtfi); + } + DateTimeFormatInfo newInfo = (DateTimeFormatInfo)(dtfi.MemberwiseClone()); + // We can use the data member calendar in the setter, instead of the property Calendar, + // since the cloned copy should have the same state as the original copy. + newInfo.calendar = Calendar.ReadOnly(dtfi.Calendar); + newInfo._isReadOnly = true; + return (newInfo); + } + + public bool IsReadOnly + { + get + { + return (_isReadOnly); + } + } + + // Return the native name for the calendar in DTFI.Calendar. The native name is referred to + // the culture used to create the DTFI. E.g. in the following example, the native language is Japanese. + // DateTimeFormatInfo dtfi = new CultureInfo("ja-JP", false).DateTimeFormat.Calendar = new JapaneseCalendar(); + // String nativeName = dtfi.NativeCalendarName; // Get the Japanese name for the Japanese calendar. + // DateTimeFormatInfo dtfi = new CultureInfo("ja-JP", false).DateTimeFormat.Calendar = new GregorianCalendar(GregorianCalendarTypes.Localized); + // String nativeName = dtfi.NativeCalendarName; // Get the Japanese name for the Gregorian calendar. + public string NativeCalendarName + { + get + { + return _cultureData.CalendarName(Calendar.ID); + } + } + + // + // Used by custom cultures and others to set the list of available formats. Note that none of them are + // explicitly used unless someone calls GetAllDateTimePatterns and subsequently uses one of the items + // from the list. + // + // Most of the format characters that can be used in GetAllDateTimePatterns are + // not really needed since they are one of the following: + // + // r/R/s/u locale-independent constants -- cannot be changed! + // m/M/y/Y fields with a single string in them -- that can be set through props directly + // f/F/g/G/U derived fields based on combinations of various of the below formats + // + // NOTE: No special validation is done here beyond what is done when the actual respective fields + // are used (what would be the point of disallowing here what we allow in the appropriate property?) + // + // WARNING: If more validation is ever done in one place, it should be done in the other. + // + public void SetAllDateTimePatterns(String[] patterns, char format) + { + if (IsReadOnly) + throw new InvalidOperationException(SR.InvalidOperation_ReadOnly); + + if (patterns == null) + { + throw new ArgumentNullException(nameof(patterns), SR.ArgumentNull_Array); + } + + if (patterns.Length == 0) + { + throw new ArgumentException(SR.Arg_ArrayZeroError, nameof(patterns)); + } + + Contract.EndContractBlock(); + + for (int i = 0; i < patterns.Length; i++) + { + if (patterns[i] == null) + { + throw new ArgumentNullException("patterns[" + i + "]", SR.ArgumentNull_ArrayValue); + } + } + + // Remember the patterns, and use the 1st as default + switch (format) + { + case 'd': + allShortDatePatterns = patterns; + shortDatePattern = allShortDatePatterns[0]; + break; + + case 'D': + allLongDatePatterns = patterns; + longDatePattern = allLongDatePatterns[0]; + break; + + case 't': + allShortTimePatterns = patterns; + shortTimePattern = allShortTimePatterns[0]; + break; + + case 'T': + allLongTimePatterns = patterns; + longTimePattern = allLongTimePatterns[0]; + break; + + case 'y': + case 'Y': + allYearMonthPatterns = patterns; + yearMonthPattern = allYearMonthPatterns[0]; + break; + + default: + throw new ArgumentException(SR.Format_BadFormatSpecifier, nameof(format)); + } + + // Clear the token hash table, note that even short dates could require this + ClearTokenHashTable(); + } + + public String[] AbbreviatedMonthGenitiveNames + { + get + { + return ((String[])internalGetGenitiveMonthNames(true).Clone()); + } + + set + { + if (IsReadOnly) + throw new InvalidOperationException(SR.InvalidOperation_ReadOnly); + if (value == null) + { + throw new ArgumentNullException(nameof(value), + SR.ArgumentNull_Array); + } + if (value.Length != 13) + { + throw new ArgumentException(SR.Format(SR.Argument_InvalidArrayLength, 13), nameof(value)); + } + Contract.EndContractBlock(); + CheckNullValue(value, value.Length - 1); + ClearTokenHashTable(); + this.m_genitiveAbbreviatedMonthNames = value; + } + } + + public String[] MonthGenitiveNames + { + get + { + return ((String[])internalGetGenitiveMonthNames(false).Clone()); + } + + set + { + if (IsReadOnly) + throw new InvalidOperationException(SR.InvalidOperation_ReadOnly); + if (value == null) + { + throw new ArgumentNullException(nameof(value), + SR.ArgumentNull_Array); + } + if (value.Length != 13) + { + throw new ArgumentException(SR.Format(SR.Argument_InvalidArrayLength, 13), nameof(value)); + } + Contract.EndContractBlock(); + CheckNullValue(value, value.Length - 1); + genitiveMonthNames = value; + ClearTokenHashTable(); + } + } + + // + // Positive TimeSpan Pattern + // + [NonSerialized] + private string _fullTimeSpanPositivePattern; + internal String FullTimeSpanPositivePattern + { + get + { + if (_fullTimeSpanPositivePattern == null) + { + CultureData cultureDataWithoutUserOverrides; + if (_cultureData.UseUserOverride) + cultureDataWithoutUserOverrides = CultureData.GetCultureData(_cultureData.CultureName, false); + else + cultureDataWithoutUserOverrides = _cultureData; + String decimalSeparator = new NumberFormatInfo(cultureDataWithoutUserOverrides).NumberDecimalSeparator; + + _fullTimeSpanPositivePattern = "d':'h':'mm':'ss'" + decimalSeparator + "'FFFFFFF"; + } + return _fullTimeSpanPositivePattern; + } + } + + // + // Negative TimeSpan Pattern + // + [NonSerialized] + private string _fullTimeSpanNegativePattern; + internal String FullTimeSpanNegativePattern + { + get + { + if (_fullTimeSpanNegativePattern == null) + _fullTimeSpanNegativePattern = "'-'" + FullTimeSpanPositivePattern; + return _fullTimeSpanNegativePattern; + } + } + + // + // Get suitable CompareInfo from current DTFI object. + // + internal CompareInfo CompareInfo + { + get + { + if (_compareInfo == null) + { + // We use the regular GetCompareInfo here to make sure the created CompareInfo object is stored in the + // CompareInfo cache. otherwise we would just create CompareInfo using _cultureData. + _compareInfo = CompareInfo.GetCompareInfo(_cultureData.SCOMPAREINFO); + } + + return _compareInfo; + } + } + + + internal const DateTimeStyles InvalidDateTimeStyles = ~(DateTimeStyles.AllowLeadingWhite | DateTimeStyles.AllowTrailingWhite + | DateTimeStyles.AllowInnerWhite | DateTimeStyles.NoCurrentDateDefault + | DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeLocal + | DateTimeStyles.AssumeUniversal | DateTimeStyles.RoundtripKind); + + internal static void ValidateStyles(DateTimeStyles style, String parameterName) + { + if ((style & InvalidDateTimeStyles) != 0) + { + throw new ArgumentException(SR.Argument_InvalidDateTimeStyles, parameterName); + } + if (((style & (DateTimeStyles.AssumeLocal)) != 0) && ((style & (DateTimeStyles.AssumeUniversal)) != 0)) + { + throw new ArgumentException(SR.Argument_ConflictingDateTimeStyles, parameterName); + } + Contract.EndContractBlock(); + if (((style & DateTimeStyles.RoundtripKind) != 0) + && ((style & (DateTimeStyles.AssumeLocal | DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal)) != 0)) + { + throw new ArgumentException(SR.Argument_ConflictingDateTimeRoundtripStyles, parameterName); + } + } + + // + // Actions: Return the internal flag used in formatting and parsing. + // The flag can be used to indicate things like if genitive forms is used in this DTFi, or if leap year gets different month names. + // + internal DateTimeFormatFlags FormatFlags + { + get + { + if (formatFlags == DateTimeFormatFlags.NotInitialized) + { + // Build the format flags from the data in this DTFI + formatFlags = DateTimeFormatFlags.None; + formatFlags |= (DateTimeFormatFlags)DateTimeFormatInfoScanner.GetFormatFlagGenitiveMonth( + MonthNames, internalGetGenitiveMonthNames(false), AbbreviatedMonthNames, internalGetGenitiveMonthNames(true)); + formatFlags |= (DateTimeFormatFlags)DateTimeFormatInfoScanner.GetFormatFlagUseSpaceInMonthNames( + MonthNames, internalGetGenitiveMonthNames(false), AbbreviatedMonthNames, internalGetGenitiveMonthNames(true)); + formatFlags |= (DateTimeFormatFlags)DateTimeFormatInfoScanner.GetFormatFlagUseSpaceInDayNames(DayNames, AbbreviatedDayNames); + formatFlags |= (DateTimeFormatFlags)DateTimeFormatInfoScanner.GetFormatFlagUseHebrewCalendar((int)Calendar.ID); + } + return (formatFlags); + } + } + + internal Boolean HasForceTwoDigitYears + { + get + { + switch (calendar.ID) + { + // Handle Japanese and Taiwan cases. + // If is y/yy, do not get (year % 100). "y" will print + // year without leading zero. "yy" will print year with two-digit in leading zero. + // If pattern is yyy/yyyy/..., print year value with two-digit in leading zero. + // So year 5 is "05", and year 125 is "125". + // The reason for not doing (year % 100) is for Taiwan calendar. + // If year 125, then output 125 and not 25. + // Note: OS uses "yyyy" for Taiwan calendar by default. + case (CalendarId.JAPAN): + case (CalendarId.TAIWAN): + return true; + } + return false; + } + } + + // Returns whether the YearMonthAdjustment function has any fix-up work to do for this culture/calendar. + internal Boolean HasYearMonthAdjustment + { + get + { + return ((FormatFlags & DateTimeFormatFlags.UseHebrewRule) != 0); + } + } + + // This is a callback that the parser can make back into the DTFI to let it fiddle with special + // cases associated with that culture or calendar. Currently this only has special cases for + // the Hebrew calendar, but this could be extended to other cultures. + // + // The return value is whether the year and month are actually valid for this calendar. + internal Boolean YearMonthAdjustment(ref int year, ref int month, Boolean parsedMonthName) + { + if ((FormatFlags & DateTimeFormatFlags.UseHebrewRule) != 0) + { + // Special rules to fix up the Hebrew year/month + + // When formatting, we only format up to the hundred digit of the Hebrew year, although Hebrew year is now over 5000. + // E.g. if the year is 5763, we only format as 763. + if (year < 1000) + { + year += 5000; + } + + // Because we need to calculate leap year, we should fall out now for an invalid year. + if (year < Calendar.GetYear(Calendar.MinSupportedDateTime) || year > Calendar.GetYear(Calendar.MaxSupportedDateTime)) + { + return false; + } + + // To handle leap months, the set of month names in the symbol table does not always correspond to the numbers. + // For non-leap years, month 7 (Adar Bet) is not present, so we need to make using this month invalid and + // shuffle the other months down. + if (parsedMonthName) + { + if (!Calendar.IsLeapYear(year)) + { + if (month >= 8) + { + month--; + } + else if (month == 7) + { + return false; + } + } + } + } + return true; + } + + // + // DateTimeFormatInfo tokenizer. This is used by DateTime.Parse() to break input string into tokens. + // + [NonSerialized] + private TokenHashValue[] _dtfiTokenHash; + + private const int TOKEN_HASH_SIZE = 199; + private const int SECOND_PRIME = 197; + private const String dateSeparatorOrTimeZoneOffset = "-"; + private const String invariantDateSeparator = "/"; + private const String invariantTimeSeparator = ":"; + + // + // Common Ignorable Symbols + // + internal const String IgnorablePeriod = "."; + internal const String IgnorableComma = ","; + + // + // Year/Month/Day suffixes + // + internal const String CJKYearSuff = "\u5e74"; + internal const String CJKMonthSuff = "\u6708"; + internal const String CJKDaySuff = "\u65e5"; + + internal const String KoreanYearSuff = "\ub144"; + internal const String KoreanMonthSuff = "\uc6d4"; + internal const String KoreanDaySuff = "\uc77c"; + + internal const String KoreanHourSuff = "\uc2dc"; + internal const String KoreanMinuteSuff = "\ubd84"; + internal const String KoreanSecondSuff = "\ucd08"; + + internal const String CJKHourSuff = "\u6642"; + internal const String ChineseHourSuff = "\u65f6"; + + internal const String CJKMinuteSuff = "\u5206"; + internal const String CJKSecondSuff = "\u79d2"; + + internal const String LocalTimeMark = "T"; + + internal const String GMTName = "GMT"; + internal const String ZuluName = "Z"; + + internal const String KoreanLangName = "ko"; + internal const String JapaneseLangName = "ja"; + internal const String EnglishLangName = "en"; + + private static volatile DateTimeFormatInfo s_jajpDTFI; + private static volatile DateTimeFormatInfo s_zhtwDTFI; + + // + // Create a Japanese DTFI which uses JapaneseCalendar. This is used to parse + // date string with Japanese era name correctly even when the supplied DTFI + // does not use Japanese calendar. + // The created instance is stored in global s_jajpDTFI. + // + internal static DateTimeFormatInfo GetJapaneseCalendarDTFI() + { + DateTimeFormatInfo temp = s_jajpDTFI; + if (temp == null) + { + temp = new CultureInfo("ja-JP", false).DateTimeFormat; + temp.Calendar = JapaneseCalendar.GetDefaultInstance(); + s_jajpDTFI = temp; + } + return (temp); + } + + // Create a Taiwan DTFI which uses TaiwanCalendar. This is used to parse + // date string with era name correctly even when the supplied DTFI + // does not use Taiwan calendar. + // The created instance is stored in global s_zhtwDTFI. + internal static DateTimeFormatInfo GetTaiwanCalendarDTFI() + { + DateTimeFormatInfo temp = s_zhtwDTFI; + if (temp == null) + { + temp = new CultureInfo("zh-TW", false).DateTimeFormat; + temp.Calendar = TaiwanCalendar.GetDefaultInstance(); + s_zhtwDTFI = temp; + } + return (temp); + } + + + // DTFI properties should call this when the setter are called. + private void ClearTokenHashTable() + { + _dtfiTokenHash = null; + formatFlags = DateTimeFormatFlags.NotInitialized; + } + + internal TokenHashValue[] CreateTokenHashTable() + { + TokenHashValue[] temp = _dtfiTokenHash; + if (temp == null) + { + temp = new TokenHashValue[TOKEN_HASH_SIZE]; + + bool koreanLanguage = LanguageName.Equals(KoreanLangName); + + string sep = this.TimeSeparator.Trim(); + if (IgnorableComma != sep) InsertHash(temp, IgnorableComma, TokenType.IgnorableSymbol, 0); + if (IgnorablePeriod != sep) InsertHash(temp, IgnorablePeriod, TokenType.IgnorableSymbol, 0); + + if (KoreanHourSuff != sep && CJKHourSuff != sep && ChineseHourSuff != sep) + { + // + // On the Macintosh, the default TimeSeparator is identical to the KoreanHourSuff, CJKHourSuff, or ChineseHourSuff for some cultures like + // ja-JP and ko-KR. In these cases having the same symbol inserted into the hash table with multiple TokenTypes causes undesirable + // DateTime.Parse behavior. For instance, the DateTimeFormatInfo.Tokenize() method might return SEP_DateOrOffset for KoreanHourSuff + // instead of SEP_HourSuff. + // + InsertHash(temp, this.TimeSeparator, TokenType.SEP_Time, 0); + } + + InsertHash(temp, this.AMDesignator, TokenType.SEP_Am | TokenType.Am, 0); + InsertHash(temp, this.PMDesignator, TokenType.SEP_Pm | TokenType.Pm, 1); + + // TODO: This ignores similar custom cultures + if (LanguageName.Equals("sq")) + { + // Albanian allows time formats like "12:00.PD" + InsertHash(temp, IgnorablePeriod + this.AMDesignator, TokenType.SEP_Am | TokenType.Am, 0); + InsertHash(temp, IgnorablePeriod + this.PMDesignator, TokenType.SEP_Pm | TokenType.Pm, 1); + } + + // CJK suffix + InsertHash(temp, CJKYearSuff, TokenType.SEP_YearSuff, 0); + InsertHash(temp, KoreanYearSuff, TokenType.SEP_YearSuff, 0); + InsertHash(temp, CJKMonthSuff, TokenType.SEP_MonthSuff, 0); + InsertHash(temp, KoreanMonthSuff, TokenType.SEP_MonthSuff, 0); + InsertHash(temp, CJKDaySuff, TokenType.SEP_DaySuff, 0); + InsertHash(temp, KoreanDaySuff, TokenType.SEP_DaySuff, 0); + + InsertHash(temp, CJKHourSuff, TokenType.SEP_HourSuff, 0); + InsertHash(temp, ChineseHourSuff, TokenType.SEP_HourSuff, 0); + InsertHash(temp, CJKMinuteSuff, TokenType.SEP_MinuteSuff, 0); + InsertHash(temp, CJKSecondSuff, TokenType.SEP_SecondSuff, 0); + + // TODO: This ignores other custom cultures that might want to do something similar + if (koreanLanguage) + { + // Korean suffix + InsertHash(temp, KoreanHourSuff, TokenType.SEP_HourSuff, 0); + InsertHash(temp, KoreanMinuteSuff, TokenType.SEP_MinuteSuff, 0); + InsertHash(temp, KoreanSecondSuff, TokenType.SEP_SecondSuff, 0); + } + + if (LanguageName.Equals("ky")) + { + // For some cultures, the date separator works more like a comma, being allowed before or after any date part + InsertHash(temp, dateSeparatorOrTimeZoneOffset, TokenType.IgnorableSymbol, 0); + } + else + { + InsertHash(temp, dateSeparatorOrTimeZoneOffset, TokenType.SEP_DateOrOffset, 0); + } + + String[] dateWords = null; + DateTimeFormatInfoScanner scanner = null; + + // We need to rescan the date words since we're always synthetic + scanner = new DateTimeFormatInfoScanner(); + m_dateWords = dateWords = scanner.GetDateWordsOfDTFI(this); + // Ensure the formatflags is initialized. + DateTimeFormatFlags flag = FormatFlags; + + // For some cultures, the date separator works more like a comma, being allowed before or after any date part. + // In these cultures, we do not use normal date separator since we disallow date separator after a date terminal state. + // This is determined in DateTimeFormatInfoScanner. Use this flag to determine if we should treat date separator as ignorable symbol. + bool useDateSepAsIgnorableSymbol = false; + + String monthPostfix = null; + if (dateWords != null) + { + // There are DateWords. It could be a real date word (such as "de"), or a monthPostfix. + // The monthPostfix starts with '\xfffe' (MonthPostfixChar), followed by the real monthPostfix. + for (int i = 0; i < dateWords.Length; i++) + { + switch (dateWords[i][0]) + { + // This is a month postfix + case DateTimeFormatInfoScanner.MonthPostfixChar: + // Get the real month postfix. + monthPostfix = dateWords[i].Substring(1); + // Add the month name + postfix into the token. + AddMonthNames(temp, monthPostfix); + break; + case DateTimeFormatInfoScanner.IgnorableSymbolChar: + String symbol = dateWords[i].Substring(1); + InsertHash(temp, symbol, TokenType.IgnorableSymbol, 0); + if (this.DateSeparator.Trim(null).Equals(symbol)) + { + // The date separator is the same as the ignorable symbol. + useDateSepAsIgnorableSymbol = true; + } + break; + default: + InsertHash(temp, dateWords[i], TokenType.DateWordToken, 0); + // TODO: This ignores similar custom cultures + if (LanguageName.Equals("eu")) + { + // Basque has date words with leading dots + InsertHash(temp, IgnorablePeriod + dateWords[i], TokenType.DateWordToken, 0); + } + break; + } + } + } + + if (!useDateSepAsIgnorableSymbol) + { + // Use the normal date separator. + InsertHash(temp, this.DateSeparator, TokenType.SEP_Date, 0); + } + // Add the regular month names. + AddMonthNames(temp, null); + + // Add the abbreviated month names. + for (int i = 1; i <= 13; i++) + { + InsertHash(temp, GetAbbreviatedMonthName(i), TokenType.MonthToken, i); + } + + + if ((FormatFlags & DateTimeFormatFlags.UseGenitiveMonth) != 0) + { + for (int i = 1; i <= 13; i++) + { + String str; + str = internalGetMonthName(i, MonthNameStyles.Genitive, false); + InsertHash(temp, str, TokenType.MonthToken, i); + } + } + + if ((FormatFlags & DateTimeFormatFlags.UseLeapYearMonth) != 0) + { + for (int i = 1; i <= 13; i++) + { + String str; + str = internalGetMonthName(i, MonthNameStyles.LeapYear, false); + InsertHash(temp, str, TokenType.MonthToken, i); + } + } + + for (int i = 0; i < 7; i++) + { + //String str = GetDayOfWeekNames()[i]; + // We have to call public methods here to work with inherited DTFI. + String str = GetDayName((DayOfWeek)i); + InsertHash(temp, str, TokenType.DayOfWeekToken, i); + + str = GetAbbreviatedDayName((DayOfWeek)i); + InsertHash(temp, str, TokenType.DayOfWeekToken, i); + } + + int[] eras = calendar.Eras; + for (int i = 1; i <= eras.Length; i++) + { + InsertHash(temp, GetEraName(i), TokenType.EraToken, i); + InsertHash(temp, GetAbbreviatedEraName(i), TokenType.EraToken, i); + } + + // TODO: This ignores other cultures that might want to do something similar + if (LanguageName.Equals(JapaneseLangName)) + { + // Japanese allows day of week forms like: "(Tue)" + for (int i = 0; i < 7; i++) + { + String specialDayOfWeek = "(" + GetAbbreviatedDayName((DayOfWeek)i) + ")"; + InsertHash(temp, specialDayOfWeek, TokenType.DayOfWeekToken, i); + } + if (this.Calendar.GetType() != typeof(JapaneseCalendar)) + { + // Special case for Japanese. If this is a Japanese DTFI, and the calendar is not Japanese calendar, + // we will check Japanese Era name as well when the calendar is Gregorian. + DateTimeFormatInfo jaDtfi = GetJapaneseCalendarDTFI(); + for (int i = 1; i <= jaDtfi.Calendar.Eras.Length; i++) + { + InsertHash(temp, jaDtfi.GetEraName(i), TokenType.JapaneseEraToken, i); + InsertHash(temp, jaDtfi.GetAbbreviatedEraName(i), TokenType.JapaneseEraToken, i); + // m_abbrevEnglishEraNames[0] contains the name for era 1, so the token value is i+1. + InsertHash(temp, jaDtfi.AbbreviatedEnglishEraNames[i - 1], TokenType.JapaneseEraToken, i); + } + } + } + // TODO: This prohibits similar custom cultures, but we hard coded the name + else if (CultureName.Equals("zh-TW")) + { + DateTimeFormatInfo twDtfi = GetTaiwanCalendarDTFI(); + for (int i = 1; i <= twDtfi.Calendar.Eras.Length; i++) + { + if (twDtfi.GetEraName(i).Length > 0) + { + InsertHash(temp, twDtfi.GetEraName(i), TokenType.TEraToken, i); + } + } + } + + InsertHash(temp, InvariantInfo.AMDesignator, TokenType.SEP_Am | TokenType.Am, 0); + InsertHash(temp, InvariantInfo.PMDesignator, TokenType.SEP_Pm | TokenType.Pm, 1); + + // Add invariant month names and day names. + for (int i = 1; i <= 12; i++) + { + String str; + // We have to call public methods here to work with inherited DTFI. + // Insert the month name first, so that they are at the front of abbrevaited + // month names. + str = InvariantInfo.GetMonthName(i); + InsertHash(temp, str, TokenType.MonthToken, i); + str = InvariantInfo.GetAbbreviatedMonthName(i); + InsertHash(temp, str, TokenType.MonthToken, i); + } + + for (int i = 0; i < 7; i++) + { + // We have to call public methods here to work with inherited DTFI. + String str = InvariantInfo.GetDayName((DayOfWeek)i); + InsertHash(temp, str, TokenType.DayOfWeekToken, i); + + str = InvariantInfo.GetAbbreviatedDayName((DayOfWeek)i); + InsertHash(temp, str, TokenType.DayOfWeekToken, i); + } + + for (int i = 0; i < AbbreviatedEnglishEraNames.Length; i++) + { + // m_abbrevEnglishEraNames[0] contains the name for era 1, so the token value is i+1. + InsertHash(temp, AbbreviatedEnglishEraNames[i], TokenType.EraToken, i + 1); + } + + InsertHash(temp, LocalTimeMark, TokenType.SEP_LocalTimeMark, 0); + InsertHash(temp, GMTName, TokenType.TimeZoneToken, 0); + InsertHash(temp, ZuluName, TokenType.TimeZoneToken, 0); + + InsertHash(temp, invariantDateSeparator, TokenType.SEP_Date, 0); + InsertHash(temp, invariantTimeSeparator, TokenType.SEP_Time, 0); + + _dtfiTokenHash = temp; + } + return (temp); + } + + private void AddMonthNames(TokenHashValue[] temp, String monthPostfix) + { + for (int i = 1; i <= 13; i++) + { + String str; + //str = internalGetMonthName(i, MonthNameStyles.Regular, false); + // We have to call public methods here to work with inherited DTFI. + // Insert the month name first, so that they are at the front of abbrevaited + // month names. + str = GetMonthName(i); + if (str.Length > 0) + { + if (monthPostfix != null) + { + // Insert the month name with the postfix first, so it can be matched first. + InsertHash(temp, str + monthPostfix, TokenType.MonthToken, i); + } + else + { + InsertHash(temp, str, TokenType.MonthToken, i); + } + } + str = GetAbbreviatedMonthName(i); + InsertHash(temp, str, TokenType.MonthToken, i); + } + } + + //////////////////////////////////////////////////////////////////////// + // + // Actions: + // Try to parse the current word to see if it is a Hebrew number. + // Tokens will be updated accordingly. + // This is called by the Lexer of DateTime.Parse(). + // + // Unlike most of the functions in this class, the return value indicates + // whether or not it started to parse. The badFormat parameter indicates + // if parsing began, but the format was bad. + // + //////////////////////////////////////////////////////////////////////// + + private static bool TryParseHebrewNumber( + ref __DTString str, + out Boolean badFormat, + out int number) + { + number = -1; + badFormat = false; + + int i = str.Index; + if (!HebrewNumber.IsDigit(str.Value[i])) + { + // If the current character is not a Hebrew digit, just return false. + // There is no chance that we can parse a valid Hebrew number from here. + return (false); + } + // The current character is a Hebrew digit. Try to parse this word as a Hebrew number. + HebrewNumberParsingContext context = new HebrewNumberParsingContext(0); + HebrewNumberParsingState state; + + do + { + state = HebrewNumber.ParseByChar(str.Value[i++], ref context); + switch (state) + { + case HebrewNumberParsingState.InvalidHebrewNumber: // Not a valid Hebrew number. + case HebrewNumberParsingState.NotHebrewDigit: // The current character is not a Hebrew digit character. + // Break out so that we don't continue to try parse this as a Hebrew number. + return (false); + } + } while (i < str.Value.Length && (state != HebrewNumberParsingState.FoundEndOfHebrewNumber)); + + // When we are here, we are either at the end of the string, or we find a valid Hebrew number. + Debug.Assert(state == HebrewNumberParsingState.ContinueParsing || state == HebrewNumberParsingState.FoundEndOfHebrewNumber, + "Invalid returned state from HebrewNumber.ParseByChar()"); + + if (state != HebrewNumberParsingState.FoundEndOfHebrewNumber) + { + // We reach end of the string but we can't find a terminal state in parsing Hebrew number. + return (false); + } + + // We have found a valid Hebrew number. Update the index. + str.Advance(i - str.Index); + + // Get the final Hebrew number value from the HebrewNumberParsingContext. + number = context.result; + + return (true); + } + + private static bool IsHebrewChar(char ch) + { + return (ch >= '\x0590' && ch <= '\x05ff'); + } + + internal bool Tokenize(TokenType TokenMask, out TokenType tokenType, out int tokenValue, + ref __DTString str) + { + tokenType = TokenType.UnknownToken; + tokenValue = 0; + + TokenHashValue value; + Debug.Assert(str.Index < str.Value.Length, "DateTimeFormatInfo.Tokenize(): start < value.Length"); + + char ch = str.m_current; + bool isLetter = Char.IsLetter(ch); + if (isLetter) + { + ch = this.Culture.TextInfo.ToLower(ch); + if (IsHebrewChar(ch) && TokenMask == TokenType.RegularTokenMask) + { + bool badFormat; + if (TryParseHebrewNumber(ref str, out badFormat, out tokenValue)) + { + if (badFormat) + { + tokenType = TokenType.UnknownToken; + return (false); + } + // This is a Hebrew number. + // Do nothing here. TryParseHebrewNumber() will update token accordingly. + tokenType = TokenType.HebrewNumber; + return (true); + } + } + } + + + int hashcode = ch % TOKEN_HASH_SIZE; + int hashProbe = 1 + ch % SECOND_PRIME; + int remaining = str.len - str.Index; + int i = 0; + + TokenHashValue[] hashTable = _dtfiTokenHash; + if (hashTable == null) + { + hashTable = CreateTokenHashTable(); + } + do + { + value = hashTable[hashcode]; + if (value == null) + { + // Not found. + break; + } + // Check this value has the right category (regular token or separator token) that we are looking for. + if (((int)value.tokenType & (int)TokenMask) > 0 && value.tokenString.Length <= remaining) + { + bool compareStrings = true; + if (isLetter) + { + // If this token starts with a letter, make sure that we won't allow partial match. So you can't tokenize "MarchWed" separately. + // Also an optimization to avoid string comparison + int nextCharIndex = str.Index + value.tokenString.Length; + if (nextCharIndex > str.len) + { + compareStrings = false; + } + else if (nextCharIndex < str.len) + { + // Check word boundary. The next character should NOT be a letter. + char nextCh = str.Value[nextCharIndex]; + compareStrings = !(Char.IsLetter(nextCh)); + } + } + if (compareStrings && CompareStringIgnoreCaseOptimized(str.Value, str.Index, value.tokenString.Length, value.tokenString, 0, value.tokenString.Length)) + { + tokenType = value.tokenType & TokenMask; + tokenValue = value.tokenValue; + str.Advance(value.tokenString.Length); + return (true); + } + else if ((value.tokenType == TokenType.MonthToken && HasSpacesInMonthNames) || + (value.tokenType == TokenType.DayOfWeekToken && HasSpacesInDayNames)) + { + // For month or day token, we will match the names which have spaces. + int matchStrLen = 0; + if (str.MatchSpecifiedWords(value.tokenString, true, ref matchStrLen)) + { + tokenType = value.tokenType & TokenMask; + tokenValue = value.tokenValue; + str.Advance(matchStrLen); + return (true); + } + } + } + i++; + hashcode += hashProbe; + if (hashcode >= TOKEN_HASH_SIZE) hashcode -= TOKEN_HASH_SIZE; + } while (i < TOKEN_HASH_SIZE); + + return (false); + } + + private void InsertAtCurrentHashNode(TokenHashValue[] hashTable, String str, char ch, TokenType tokenType, int tokenValue, int pos, int hashcode, int hashProbe) + { + // Remember the current slot. + TokenHashValue previousNode = hashTable[hashcode]; + + //// Console.WriteLine(" Insert Key: {0} in {1}", str, slotToInsert); + // Insert the new node into the current slot. + hashTable[hashcode] = new TokenHashValue(str, tokenType, tokenValue); ; + + while (++pos < TOKEN_HASH_SIZE) + { + hashcode += hashProbe; + if (hashcode >= TOKEN_HASH_SIZE) hashcode -= TOKEN_HASH_SIZE; + // Remember this slot + TokenHashValue temp = hashTable[hashcode]; + + if (temp != null && this.Culture.TextInfo.ToLower(temp.tokenString[0]) != ch) + { + continue; + } + // Put the previous slot into this slot. + hashTable[hashcode] = previousNode; + //// Console.WriteLine(" Move {0} to slot {1}", previousNode.tokenString, hashcode); + if (temp == null) + { + // Done + return; + } + previousNode = temp; + }; + Debug.Assert(false, "The hashtable is full. This should not happen."); + } + + private void InsertHash(TokenHashValue[] hashTable, String str, TokenType tokenType, int tokenValue) + { + // The month of the 13th month is allowed to be null, so make sure that we ignore null value here. + if (str == null || str.Length == 0) + { + return; + } + TokenHashValue value; + int i = 0; + // If there is whitespace characters in the beginning and end of the string, trim them since whitespaces are skipped by + // DateTime.Parse(). + if (Char.IsWhiteSpace(str[0]) || Char.IsWhiteSpace(str[str.Length - 1])) + { + str = str.Trim(null); // Trim white space characters. + // Could have space for separators + if (str.Length == 0) + return; + } + char ch = this.Culture.TextInfo.ToLower(str[0]); + int hashcode = ch % TOKEN_HASH_SIZE; + int hashProbe = 1 + ch % SECOND_PRIME; + do + { + value = hashTable[hashcode]; + if (value == null) + { + //// Console.WriteLine(" Put Key: {0} in {1}", str, hashcode); + hashTable[hashcode] = new TokenHashValue(str, tokenType, tokenValue); + return; + } + else + { + // Collision happens. Find another slot. + if (str.Length >= value.tokenString.Length) + { + // If there are two tokens with the same prefix, we have to make sure that the longer token should be at the front of + // the shorter ones. + if (this.CompareStringIgnoreCaseOptimized(str, 0, value.tokenString.Length, value.tokenString, 0, value.tokenString.Length)) + { + if (str.Length > value.tokenString.Length) + { + // The str to be inserted has the same prefix as the current token, and str is longer. + // Insert str into this node, and shift every node behind it. + InsertAtCurrentHashNode(hashTable, str, ch, tokenType, tokenValue, i, hashcode, hashProbe); + return; + } + else + { + // Same token. If they have different types (regular token vs separator token). Add them. + // If we have the same regular token or separator token in the hash already, do NOT update the hash. + // Therefore, the order of inserting token is significant here regarding what tokenType will be kept in the hash. + + + // + // Check the current value of RegularToken (stored in the lower 8-bit of tokenType) , and insert the tokenType into the hash ONLY when we don't have a RegularToken yet. + // Also check the current value of SeparatorToken (stored in the upper 8-bit of token), and insert the tokenType into the hash ONLY when we don't have the SeparatorToken yet. + // + + int nTokenType = (int)tokenType; + int nCurrentTokenTypeInHash = (int)value.tokenType; + + // + // The folowing is the fix for the issue of throwing FormatException when "mar" is passed in string of the short date format dd/MMM/yyyy for es-MX + // + + if (((nCurrentTokenTypeInHash & (int)TokenType.RegularTokenMask) == 0) && ((nTokenType & (int)TokenType.RegularTokenMask) != 0) || + ((nCurrentTokenTypeInHash & (int)TokenType.SeparatorTokenMask) == 0) && ((nTokenType & (int)TokenType.SeparatorTokenMask) != 0)) + { + value.tokenType |= tokenType; + if (tokenValue != 0) + { + value.tokenValue = tokenValue; + } + } + // The token to be inserted is already in the table. Skip it. + return; + } + } + } + } + //// Console.WriteLine(" COLLISION. Old Key: {0}, New Key: {1}", hashTable[hashcode].tokenString, str); + i++; + hashcode += hashProbe; + if (hashcode >= TOKEN_HASH_SIZE) hashcode -= TOKEN_HASH_SIZE; + } while (i < TOKEN_HASH_SIZE); + Debug.Assert(false, "The hashtable is full. This should not happen."); + } + + private bool CompareStringIgnoreCaseOptimized(string string1, int offset1, int length1, string string2, int offset2, int length2) + { + // Optimize for one character cases which are common due to date and time separators (/ and :) + if (length1 == 1 && length2 == 1 && string1[offset1] == string2[offset2]) + { + return true; + } + + return (this.Culture.CompareInfo.Compare(string1, offset1, length1, string2, offset2, length2, CompareOptions.IgnoreCase) == 0); + } + + // class DateTimeFormatInfo + + internal class TokenHashValue + { + internal String tokenString; + internal TokenType tokenType; + internal int tokenValue; + + internal TokenHashValue(String tokenString, TokenType tokenType, int tokenValue) + { + this.tokenString = tokenString; + this.tokenType = tokenType; + this.tokenValue = tokenValue; + } + } + } +} diff --git a/src/mscorlib/shared/System/Globalization/DateTimeFormatInfoScanner.cs b/src/mscorlib/shared/System/Globalization/DateTimeFormatInfoScanner.cs new file mode 100644 index 0000000000..15af1b7d84 --- /dev/null +++ b/src/mscorlib/shared/System/Globalization/DateTimeFormatInfoScanner.cs @@ -0,0 +1,739 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +//////////////////////////////////////////////////////////////////////////// +// +// DateTimeFormatInfoScanner +// +// Scan a specified DateTimeFormatInfo to search for data used in DateTime.Parse() +// +// The data includes: +// +// DateWords: such as "de" used in es-ES (Spanish) LongDatePattern. +// Postfix: such as "ta" used in fi-FI after the month name. +// +// This class is shared among mscorlib.dll and sysglobl.dll. +// Use conditional CULTURE_AND_REGIONINFO_BUILDER_ONLY to differentiate between +// methods for mscorlib.dll and sysglobl.dll. +// +//////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Text; + +namespace System.Globalization +{ + +#if CORECLR + using StringStringDictionary = Dictionary<string, string>; + using StringList = List<string>; +#else + using StringStringDictionary = LowLevelDictionary<string, string>; + using StringList = LowLevelList<string>; +#endif + + // + // from LocaleEx.txt header + // + //; IFORMATFLAGS + //; Parsing/formatting flags. + internal enum FORMATFLAGS + { + None = 0x00000000, + UseGenitiveMonth = 0x00000001, + UseLeapYearMonth = 0x00000002, + UseSpacesInMonthNames = 0x00000004, + UseHebrewParsing = 0x00000008, + UseSpacesInDayNames = 0x00000010, // Has spaces or non-breaking space in the day names. + UseDigitPrefixInTokens = 0x00000020, // Has token starting with numbers. + } + + internal enum CalendarId : ushort + { + UNINITIALIZED_VALUE = 0, + GREGORIAN = 1, // Gregorian (localized) calendar + GREGORIAN_US = 2, // Gregorian (U.S.) calendar + JAPAN = 3, // Japanese Emperor Era calendar + /* SSS_WARNINGS_OFF */ + TAIWAN = 4, // Taiwan Era calendar /* SSS_WARNINGS_ON */ + KOREA = 5, // Korean Tangun Era calendar + HIJRI = 6, // Hijri (Arabic Lunar) calendar + THAI = 7, // Thai calendar + HEBREW = 8, // Hebrew (Lunar) calendar + GREGORIAN_ME_FRENCH = 9, // Gregorian Middle East French calendar + GREGORIAN_ARABIC = 10, // Gregorian Arabic calendar + GREGORIAN_XLIT_ENGLISH = 11, // Gregorian Transliterated English calendar + GREGORIAN_XLIT_FRENCH = 12, + // Note that all calendars after this point are MANAGED ONLY for now. + JULIAN = 13, + JAPANESELUNISOLAR = 14, + CHINESELUNISOLAR = 15, + SAKA = 16, // reserved to match Office but not implemented in our code + LUNAR_ETO_CHN = 17, // reserved to match Office but not implemented in our code + LUNAR_ETO_KOR = 18, // reserved to match Office but not implemented in our code + LUNAR_ETO_ROKUYOU = 19, // reserved to match Office but not implemented in our code + KOREANLUNISOLAR = 20, + TAIWANLUNISOLAR = 21, + PERSIAN = 22, + UMALQURA = 23, + LAST_CALENDAR = 23 // Last calendar ID + } + + internal class DateTimeFormatInfoScanner + { + // Special prefix-like flag char in DateWord array. + + // Use char in PUA area since we won't be using them in real data. + // The char used to tell a read date word or a month postfix. A month postfix + // is "ta" in the long date pattern like "d. MMMM'ta 'yyyy" for fi-FI. + // In this case, it will be stored as "\xfffeta" in the date word array. + internal const char MonthPostfixChar = '\xe000'; + + // Add ignorable symbol in a DateWord array. + + // hu-HU has: + // shrot date pattern: yyyy. MM. dd.;yyyy-MM-dd;yy-MM-dd + // long date pattern: yyyy. MMMM d. + // Here, "." is the date separator (derived from short date pattern). However, + // "." also appear at the end of long date pattern. In this case, we just + // "." as ignorable symbol so that the DateTime.Parse() state machine will not + // treat the additional date separator at the end of y,m,d pattern as an error + // condition. + internal const char IgnorableSymbolChar = '\xe001'; + + // Known CJK suffix + internal const String CJKYearSuff = "\u5e74"; + internal const String CJKMonthSuff = "\u6708"; + internal const String CJKDaySuff = "\u65e5"; + + internal const String KoreanYearSuff = "\ub144"; + internal const String KoreanMonthSuff = "\uc6d4"; + internal const String KoreanDaySuff = "\uc77c"; + + internal const String KoreanHourSuff = "\uc2dc"; + internal const String KoreanMinuteSuff = "\ubd84"; + internal const String KoreanSecondSuff = "\ucd08"; + + internal const String CJKHourSuff = "\u6642"; + internal const String ChineseHourSuff = "\u65f6"; + + internal const String CJKMinuteSuff = "\u5206"; + internal const String CJKSecondSuff = "\u79d2"; + + // The collection fo date words & postfix. + internal StringList m_dateWords = new StringList(); + // Hashtable for the known words. + private static volatile StringStringDictionary s_knownWords; + + static StringStringDictionary KnownWords + { + get + { + if (s_knownWords == null) + { + StringStringDictionary temp = new StringStringDictionary(); + // Add known words into the hash table. + + // Skip these special symbols. + temp.Add("/", String.Empty); + temp.Add("-", String.Empty); + temp.Add(".", String.Empty); + // Skip known CJK suffixes. + temp.Add(CJKYearSuff, String.Empty); + temp.Add(CJKMonthSuff, String.Empty); + temp.Add(CJKDaySuff, String.Empty); + temp.Add(KoreanYearSuff, String.Empty); + temp.Add(KoreanMonthSuff, String.Empty); + temp.Add(KoreanDaySuff, String.Empty); + temp.Add(KoreanHourSuff, String.Empty); + temp.Add(KoreanMinuteSuff, String.Empty); + temp.Add(KoreanSecondSuff, String.Empty); + temp.Add(CJKHourSuff, String.Empty); + temp.Add(ChineseHourSuff, String.Empty); + temp.Add(CJKMinuteSuff, String.Empty); + temp.Add(CJKSecondSuff, String.Empty); + + s_knownWords = temp; + } + return (s_knownWords); + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Parameters: + // pattern: The pattern to be scanned. + // currentIndex: the current index to start the scan. + // + // Returns: + // Return the index with the first character that is a letter, which will + // be the start of a date word. + // Note that the index can be pattern.Length if we reach the end of the string. + // + //////////////////////////////////////////////////////////////////////////// + internal static int SkipWhiteSpacesAndNonLetter(String pattern, int currentIndex) + { + while (currentIndex < pattern.Length) + { + char ch = pattern[currentIndex]; + if (ch == '\\') + { + // Escaped character. Look ahead one character. + currentIndex++; + if (currentIndex < pattern.Length) + { + ch = pattern[currentIndex]; + if (ch == '\'') + { + // Skip the leading single quote. We will + // stop at the first letter. + continue; + } + // Fall thru to check if this is a letter. + } + else + { + // End of string + break; + } + } + if (Char.IsLetter(ch) || ch == '\'' || ch == '.') + { + break; + } + // Skip the current char since it is not a letter. + currentIndex++; + } + return (currentIndex); + } + + //////////////////////////////////////////////////////////////////////////// + // + // A helper to add the found date word or month postfix into ArrayList for date words. + // + // Parameters: + // formatPostfix: What kind of postfix this is. + // Possible values: + // null: This is a regular date word + // "MMMM": month postfix + // word: The date word or postfix to be added. + // + //////////////////////////////////////////////////////////////////////////// + internal void AddDateWordOrPostfix(String formatPostfix, String str) + { + if (str.Length > 0) + { + // Some cultures use . like an abbreviation + if (str.Equals(".")) + { + AddIgnorableSymbols("."); + return; + } + String words; + if (KnownWords.TryGetValue(str, out words) == false) + { + if (m_dateWords == null) + { + m_dateWords = new StringList(); + } + if (formatPostfix == "MMMM") + { + // Add the word into the ArrayList as "\xfffe" + real month postfix. + String temp = MonthPostfixChar + str; + if (!m_dateWords.Contains(temp)) + { + m_dateWords.Add(temp); + } + } + else + { + if (!m_dateWords.Contains(str)) + { + m_dateWords.Add(str); + } + if (str[str.Length - 1] == '.') + { + // Old version ignore the trialing dot in the date words. Support this as well. + String strWithoutDot = str.Substring(0, str.Length - 1); + if (!m_dateWords.Contains(strWithoutDot)) + { + m_dateWords.Add(strWithoutDot); + } + } + } + } + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Scan the pattern from the specified index and add the date word/postfix + // when appropriate. + // + // Parameters: + // pattern: The pattern to be scanned. + // index: The starting index to be scanned. + // formatPostfix: The kind of postfix to be scanned. + // Possible values: + // null: This is a regular date word + // "MMMM": month postfix + // + // + //////////////////////////////////////////////////////////////////////////// + internal int AddDateWords(String pattern, int index, String formatPostfix) + { + // Skip any whitespaces so we will start from a letter. + int newIndex = SkipWhiteSpacesAndNonLetter(pattern, index); + if (newIndex != index && formatPostfix != null) + { + // There are whitespaces. This will not be a postfix. + formatPostfix = null; + } + index = newIndex; + + // This is the first char added into dateWord. + // Skip all non-letter character. We will add the first letter into DateWord. + StringBuilder dateWord = new StringBuilder(); + // We assume that date words should start with a letter. + // Skip anything until we see a letter. + + while (index < pattern.Length) + { + char ch = pattern[index]; + if (ch == '\'') + { + // We have seen the end of quote. Add the word if we do not see it before, + // and break the while loop. + AddDateWordOrPostfix(formatPostfix, dateWord.ToString()); + index++; + break; + } + else if (ch == '\\') + { + // + // Escaped character. Look ahead one character + // + + // Skip escaped backslash. + index++; + if (index < pattern.Length) + { + dateWord.Append(pattern[index]); + index++; + } + } + else if (Char.IsWhiteSpace(ch)) + { + // Found a whitespace. We have to add the current date word/postfix. + AddDateWordOrPostfix(formatPostfix, dateWord.ToString()); + if (formatPostfix != null) + { + // Done with postfix. The rest will be regular date word. + formatPostfix = null; + } + // Reset the dateWord. + dateWord.Length = 0; + index++; + } + else + { + dateWord.Append(ch); + index++; + } + } + return (index); + } + + //////////////////////////////////////////////////////////////////////////// + // + // A simple helper to find the repeat count for a specified char. + // + //////////////////////////////////////////////////////////////////////////// + internal static int ScanRepeatChar(String pattern, char ch, int index, out int count) + { + count = 1; + while (++index < pattern.Length && pattern[index] == ch) + { + count++; + } + // Return the updated position. + return (index); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Add the text that is a date separator but is treated like ignroable symbol. + // E.g. + // hu-HU has: + // shrot date pattern: yyyy. MM. dd.;yyyy-MM-dd;yy-MM-dd + // long date pattern: yyyy. MMMM d. + // Here, "." is the date separator (derived from short date pattern). However, + // "." also appear at the end of long date pattern. In this case, we just + // "." as ignorable symbol so that the DateTime.Parse() state machine will not + // treat the additional date separator at the end of y,m,d pattern as an error + // condition. + // + //////////////////////////////////////////////////////////////////////////// + + internal void AddIgnorableSymbols(String text) + { + if (m_dateWords == null) + { + // Create the date word array. + m_dateWords = new StringList(); + } + // Add the ignorable symbol into the ArrayList. + String temp = IgnorableSymbolChar + text; + if (!m_dateWords.Contains(temp)) + { + m_dateWords.Add(temp); + } + } + + + // + // Flag used to trace the date patterns (yy/yyyyy/M/MM/MMM/MMM/d/dd) that we have seen. + // + private enum FoundDatePattern + { + None = 0x0000, + FoundYearPatternFlag = 0x0001, + FoundMonthPatternFlag = 0x0002, + FoundDayPatternFlag = 0x0004, + FoundYMDPatternFlag = 0x0007, // FoundYearPatternFlag | FoundMonthPatternFlag | FoundDayPatternFlag; + } + + // Check if we have found all of the year/month/day pattern. + private FoundDatePattern _ymdFlags = FoundDatePattern.None; + + + //////////////////////////////////////////////////////////////////////////// + // + // Given a date format pattern, scan for date word or postfix. + // + // A date word should be always put in a single quoted string. And it will + // start from a letter, so whitespace and symbols will be ignored before + // the first letter. + // + // Examples of date word: + // 'de' in es-SP: dddd, dd' de 'MMMM' de 'yyyy + // "\x0443." in bg-BG: dd.M.yyyy '\x0433.' + // + // Example of postfix: + // month postfix: + // "ta" in fi-FI: d. MMMM'ta 'yyyy + // Currently, only month postfix is supported. + // + // Usage: + // Always call this with Framework-style pattern, instead of Windows style pattern. + // Windows style pattern uses '' for single quote, while .NET uses \' + // + //////////////////////////////////////////////////////////////////////////// + internal void ScanDateWord(String pattern) + { + // Check if we have found all of the year/month/day pattern. + _ymdFlags = FoundDatePattern.None; + + int i = 0; + while (i < pattern.Length) + { + char ch = pattern[i]; + int chCount; + + switch (ch) + { + case '\'': + // Find a beginning quote. Search until the end quote. + i = AddDateWords(pattern, i + 1, null); + break; + case 'M': + i = ScanRepeatChar(pattern, 'M', i, out chCount); + if (chCount >= 4) + { + if (i < pattern.Length && pattern[i] == '\'') + { + i = AddDateWords(pattern, i + 1, "MMMM"); + } + } + _ymdFlags |= FoundDatePattern.FoundMonthPatternFlag; + break; + case 'y': + i = ScanRepeatChar(pattern, 'y', i, out chCount); + _ymdFlags |= FoundDatePattern.FoundYearPatternFlag; + break; + case 'd': + i = ScanRepeatChar(pattern, 'd', i, out chCount); + if (chCount <= 2) + { + // Only count "d" & "dd". + // ddd, dddd are day names. Do not count them. + _ymdFlags |= FoundDatePattern.FoundDayPatternFlag; + } + break; + case '\\': + // Found a escaped char not in a quoted string. Skip the current backslash + // and its next character. + i += 2; + break; + case '.': + if (_ymdFlags == FoundDatePattern.FoundYMDPatternFlag) + { + // If we find a dot immediately after the we have seen all of the y, m, d pattern. + // treat it as a ignroable symbol. Check for comments in AddIgnorableSymbols for + // more details. + AddIgnorableSymbols("."); + _ymdFlags = FoundDatePattern.None; + } + i++; + break; + default: + if (_ymdFlags == FoundDatePattern.FoundYMDPatternFlag && !Char.IsWhiteSpace(ch)) + { + // We are not seeing "." after YMD. Clear the flag. + _ymdFlags = FoundDatePattern.None; + } + // We are not in quote. Skip the current character. + i++; + break; + } + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Given a DTFI, get all of the date words from date patterns and time patterns. + // + //////////////////////////////////////////////////////////////////////////// + + internal String[] GetDateWordsOfDTFI(DateTimeFormatInfo dtfi) + { + // Enumarate all LongDatePatterns, and get the DateWords and scan for month postfix. + String[] datePatterns = dtfi.GetAllDateTimePatterns('D'); + int i; + + // Scan the long date patterns + for (i = 0; i < datePatterns.Length; i++) + { + ScanDateWord(datePatterns[i]); + } + + // Scan the short date patterns + datePatterns = dtfi.GetAllDateTimePatterns('d'); + for (i = 0; i < datePatterns.Length; i++) + { + ScanDateWord(datePatterns[i]); + } + // Scan the YearMonth patterns. + datePatterns = dtfi.GetAllDateTimePatterns('y'); + for (i = 0; i < datePatterns.Length; i++) + { + ScanDateWord(datePatterns[i]); + } + + // Scan the month/day pattern + ScanDateWord(dtfi.MonthDayPattern); + + // Scan the long time patterns. + datePatterns = dtfi.GetAllDateTimePatterns('T'); + for (i = 0; i < datePatterns.Length; i++) + { + ScanDateWord(datePatterns[i]); + } + + // Scan the short time patterns. + datePatterns = dtfi.GetAllDateTimePatterns('t'); + for (i = 0; i < datePatterns.Length; i++) + { + ScanDateWord(datePatterns[i]); + } + + String[] result = null; + if (m_dateWords != null && m_dateWords.Count > 0) + { + result = new String[m_dateWords.Count]; + for (i = 0; i < m_dateWords.Count; i++) + { + result[i] = m_dateWords[i]; + } + } + return (result); + } + + + //////////////////////////////////////////////////////////////////////////// + // + // Scan the month names to see if genitive month names are used, and return + // the format flag. + // + //////////////////////////////////////////////////////////////////////////// + internal static FORMATFLAGS GetFormatFlagGenitiveMonth(String[] monthNames, String[] genitveMonthNames, String[] abbrevMonthNames, String[] genetiveAbbrevMonthNames) + { + // If we have different names in regular and genitive month names, use genitive month flag. + return ((!EqualStringArrays(monthNames, genitveMonthNames) || !EqualStringArrays(abbrevMonthNames, genetiveAbbrevMonthNames)) + ? FORMATFLAGS.UseGenitiveMonth : 0); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Scan the month names to see if spaces are used or start with a digit, and return the format flag + // + //////////////////////////////////////////////////////////////////////////// + internal static FORMATFLAGS GetFormatFlagUseSpaceInMonthNames(String[] monthNames, String[] genitveMonthNames, String[] abbrevMonthNames, String[] genetiveAbbrevMonthNames) + { + FORMATFLAGS formatFlags = 0; + formatFlags |= (ArrayElementsBeginWithDigit(monthNames) || + ArrayElementsBeginWithDigit(genitveMonthNames) || + ArrayElementsBeginWithDigit(abbrevMonthNames) || + ArrayElementsBeginWithDigit(genetiveAbbrevMonthNames) + ? FORMATFLAGS.UseDigitPrefixInTokens : 0); + + formatFlags |= (ArrayElementsHaveSpace(monthNames) || + ArrayElementsHaveSpace(genitveMonthNames) || + ArrayElementsHaveSpace(abbrevMonthNames) || + ArrayElementsHaveSpace(genetiveAbbrevMonthNames) + ? FORMATFLAGS.UseSpacesInMonthNames : 0); + return (formatFlags); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Scan the day names and set the correct format flag. + // + //////////////////////////////////////////////////////////////////////////// + internal static FORMATFLAGS GetFormatFlagUseSpaceInDayNames(String[] dayNames, String[] abbrevDayNames) + { + return ((ArrayElementsHaveSpace(dayNames) || + ArrayElementsHaveSpace(abbrevDayNames)) + ? FORMATFLAGS.UseSpacesInDayNames : 0); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Check the calendar to see if it is HebrewCalendar and set the Hebrew format flag if necessary. + // + //////////////////////////////////////////////////////////////////////////// + internal static FORMATFLAGS GetFormatFlagUseHebrewCalendar(int calID) + { + return (calID == (int)CalendarId.HEBREW ? + FORMATFLAGS.UseHebrewParsing | FORMATFLAGS.UseLeapYearMonth : 0); + } + + + //----------------------------------------------------------------------------- + // EqualStringArrays + // compares two string arrays and return true if all elements of the first + // array equals to all elmentsof the second array. + // otherwise it returns false. + //----------------------------------------------------------------------------- + + private static bool EqualStringArrays(string[] array1, string[] array2) + { + // Shortcut if they're the same array + if (array1 == array2) + { + return true; + } + + // This is effectively impossible + if (array1.Length != array2.Length) + { + return false; + } + + // Check each string + for (int i = 0; i < array1.Length; i++) + { + if (!array1[i].Equals(array2[i])) + { + return false; + } + } + + return true; + } + + //----------------------------------------------------------------------------- + // ArrayElementsHaveSpace + // It checks all input array elements if any of them has space character + // returns true if found space character in one of the array elements. + // otherwise returns false. + //----------------------------------------------------------------------------- + + private static bool ArrayElementsHaveSpace(string[] array) + { + for (int i = 0; i < array.Length; i++) + { + // it is faster to check for space character manually instead of calling IndexOf + // so we don't have to go to native code side. + for (int j = 0; j < array[i].Length; j++) + { + if (Char.IsWhiteSpace(array[i][j])) + { + return true; + } + } + } + + return false; + } + + + //////////////////////////////////////////////////////////////////////////// + // + // Check if any element of the array start with a digit. + // + //////////////////////////////////////////////////////////////////////////// + private static bool ArrayElementsBeginWithDigit(string[] array) + { + for (int i = 0; i < array.Length; i++) + { + // it is faster to check for space character manually instead of calling IndexOf + // so we don't have to go to native code side. + if (array[i].Length > 0 && + array[i][0] >= '0' && array[i][0] <= '9') + { + int index = 1; + while (index < array[i].Length && array[i][index] >= '0' && array[i][index] <= '9') + { + // Skip other digits. + index++; + } + if (index == array[i].Length) + { + return (false); + } + + if (index == array[i].Length - 1) + { + // Skip known CJK month suffix. + // CJK uses month name like "1\x6708", since \x6708 is a known month suffix, + // we don't need the UseDigitPrefixInTokens since it is slower. + switch (array[i][index]) + { + case '\x6708': // CJKMonthSuff + case '\xc6d4': // KoreanMonthSuff + return (false); + } + } + + if (index == array[i].Length - 4) + { + // Skip known CJK month suffix. + // Starting with Windows 8, the CJK months for some cultures looks like: "1' \x6708'" + // instead of just "1\x6708" + if (array[i][index] == '\'' && array[i][index + 1] == ' ' && + array[i][index + 2] == '\x6708' && array[i][index + 3] == '\'') + { + return (false); + } + } + return (true); + } + } + + return false; + } + } +} + diff --git a/src/mscorlib/shared/System/Globalization/DateTimeParse.cs b/src/mscorlib/shared/System/Globalization/DateTimeParse.cs new file mode 100644 index 0000000000..910fbf2ff0 --- /dev/null +++ b/src/mscorlib/shared/System/Globalization/DateTimeParse.cs @@ -0,0 +1,5672 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Diagnostics.Contracts; +using System.Globalization; +using System.Text; + +namespace System +{ + internal static class DateTimeParse + { + internal const Int32 MaxDateTimeNumberDigits = 8; + + internal delegate bool MatchNumberDelegate(ref __DTString str, int digitLen, out int result); + + internal static MatchNumberDelegate m_hebrewNumberParser = new MatchNumberDelegate(DateTimeParse.MatchHebrewDigits); + + internal static DateTime ParseExact(String s, String format, DateTimeFormatInfo dtfi, DateTimeStyles style) + { + DateTimeResult result = new DateTimeResult(); // The buffer to store the parsing result. + result.Init(); + if (TryParseExact(s, format, dtfi, style, ref result)) + { + return result.parsedDate; + } + else + { + throw GetDateTimeParseException(ref result); + } + } + + internal static DateTime ParseExact(String s, String format, DateTimeFormatInfo dtfi, DateTimeStyles style, out TimeSpan offset) + { + DateTimeResult result = new DateTimeResult(); // The buffer to store the parsing result. + offset = TimeSpan.Zero; + result.Init(); + result.flags |= ParseFlags.CaptureOffset; + if (TryParseExact(s, format, dtfi, style, ref result)) + { + offset = result.timeZoneOffset; + return result.parsedDate; + } + else + { + throw GetDateTimeParseException(ref result); + } + } + + internal static bool TryParseExact(String s, String format, DateTimeFormatInfo dtfi, DateTimeStyles style, out DateTime result) + { + result = DateTime.MinValue; + DateTimeResult resultData = new DateTimeResult(); // The buffer to store the parsing result. + resultData.Init(); + if (TryParseExact(s, format, dtfi, style, ref resultData)) + { + result = resultData.parsedDate; + return true; + } + return false; + } + + internal static bool TryParseExact(String s, String format, DateTimeFormatInfo dtfi, DateTimeStyles style, out DateTime result, out TimeSpan offset) + { + result = DateTime.MinValue; + offset = TimeSpan.Zero; + DateTimeResult resultData = new DateTimeResult(); // The buffer to store the parsing result. + resultData.Init(); + resultData.flags |= ParseFlags.CaptureOffset; + if (TryParseExact(s, format, dtfi, style, ref resultData)) + { + result = resultData.parsedDate; + offset = resultData.timeZoneOffset; + return true; + } + return false; + } + + internal static bool TryParseExact(String s, String format, DateTimeFormatInfo dtfi, DateTimeStyles style, ref DateTimeResult result) + { + if (s == null) + { + result.SetFailure(ParseFailureKind.ArgumentNull, "ArgumentNull_String", null, nameof(s)); + return false; + } + if (format == null) + { + result.SetFailure(ParseFailureKind.ArgumentNull, "ArgumentNull_String", null, nameof(format)); + return false; + } + if (s.Length == 0) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + + if (format.Length == 0) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadFormatSpecifier", null); + return false; + } + + Debug.Assert(dtfi != null, "dtfi == null"); + + return DoStrictParse(s, format, style, dtfi, ref result); + } + + internal static DateTime ParseExactMultiple(String s, String[] formats, + DateTimeFormatInfo dtfi, DateTimeStyles style) + { + DateTimeResult result = new DateTimeResult(); // The buffer to store the parsing result. + result.Init(); + if (TryParseExactMultiple(s, formats, dtfi, style, ref result)) + { + return result.parsedDate; + } + else + { + throw GetDateTimeParseException(ref result); + } + } + + + internal static DateTime ParseExactMultiple(String s, String[] formats, + DateTimeFormatInfo dtfi, DateTimeStyles style, out TimeSpan offset) + { + DateTimeResult result = new DateTimeResult(); // The buffer to store the parsing result. + offset = TimeSpan.Zero; + result.Init(); + result.flags |= ParseFlags.CaptureOffset; + if (TryParseExactMultiple(s, formats, dtfi, style, ref result)) + { + offset = result.timeZoneOffset; + return result.parsedDate; + } + else + { + throw GetDateTimeParseException(ref result); + } + } + + internal static bool TryParseExactMultiple(String s, String[] formats, + DateTimeFormatInfo dtfi, DateTimeStyles style, out DateTime result, out TimeSpan offset) + { + result = DateTime.MinValue; + offset = TimeSpan.Zero; + DateTimeResult resultData = new DateTimeResult(); // The buffer to store the parsing result. + resultData.Init(); + resultData.flags |= ParseFlags.CaptureOffset; + if (TryParseExactMultiple(s, formats, dtfi, style, ref resultData)) + { + result = resultData.parsedDate; + offset = resultData.timeZoneOffset; + return true; + } + return false; + } + + + internal static bool TryParseExactMultiple(String s, String[] formats, + DateTimeFormatInfo dtfi, DateTimeStyles style, out DateTime result) + { + result = DateTime.MinValue; + DateTimeResult resultData = new DateTimeResult(); // The buffer to store the parsing result. + resultData.Init(); + if (TryParseExactMultiple(s, formats, dtfi, style, ref resultData)) + { + result = resultData.parsedDate; + return true; + } + return false; + } + + internal static bool TryParseExactMultiple(String s, String[] formats, + DateTimeFormatInfo dtfi, DateTimeStyles style, ref DateTimeResult result) + { + if (s == null) + { + result.SetFailure(ParseFailureKind.ArgumentNull, "ArgumentNull_String", null, nameof(s)); + return false; + } + if (formats == null) + { + result.SetFailure(ParseFailureKind.ArgumentNull, "ArgumentNull_String", null, nameof(formats)); + return false; + } + + if (s.Length == 0) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + + if (formats.Length == 0) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadFormatSpecifier", null); + return false; + } + + Debug.Assert(dtfi != null, "dtfi == null"); + + // + // Do a loop through the provided formats and see if we can parse succesfully in + // one of the formats. + // + for (int i = 0; i < formats.Length; i++) + { + if (formats[i] == null || formats[i].Length == 0) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadFormatSpecifier", null); + return false; + } + // Create a new result each time to ensure the runs are independent. Carry through + // flags from the caller and return the result. + DateTimeResult innerResult = new DateTimeResult(); // The buffer to store the parsing result. + innerResult.Init(); + innerResult.flags = result.flags; + if (TryParseExact(s, formats[i], dtfi, style, ref innerResult)) + { + result.parsedDate = innerResult.parsedDate; + result.timeZoneOffset = innerResult.timeZoneOffset; + return (true); + } + } + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return (false); + } + + //////////////////////////////////////////////////////////////////////////// + // Date Token Types + // + // Following is the set of tokens that can be generated from a date + // string. Notice that the legal set of trailing separators have been + // folded in with the date number, and month name tokens. This set + // of tokens is chosen to reduce the number of date parse states. + // + //////////////////////////////////////////////////////////////////////////// + + internal enum DTT : int + { + End = 0, // '\0' + NumEnd = 1, // Num[ ]*[\0] + NumAmpm = 2, // Num[ ]+AmPm + NumSpace = 3, // Num[ ]+^[Dsep|Tsep|'0\'] + NumDatesep = 4, // Num[ ]*Dsep + NumTimesep = 5, // Num[ ]*Tsep + MonthEnd = 6, // Month[ ]*'\0' + MonthSpace = 7, // Month[ ]+^[Dsep|Tsep|'\0'] + MonthDatesep = 8, // Month[ ]*Dsep + NumDatesuff = 9, // Month[ ]*DSuff + NumTimesuff = 10, // Month[ ]*TSuff + DayOfWeek = 11, // Day of week name + YearSpace = 12, // Year+^[Dsep|Tsep|'0\'] + YearDateSep = 13, // Year+Dsep + YearEnd = 14, // Year+['\0'] + TimeZone = 15, // timezone name + Era = 16, // era name + NumUTCTimeMark = 17, // Num + 'Z' + // When you add a new token which will be in the + // state table, add it after NumLocalTimeMark. + Unk = 18, // unknown + NumLocalTimeMark = 19, // Num + 'T' + Max = 20, // marker + } + + internal enum TM + { + NotSet = -1, + AM = 0, + PM = 1, + } + + + //////////////////////////////////////////////////////////////////////////// + // + // DateTime parsing state enumeration (DS.*) + // + //////////////////////////////////////////////////////////////////////////// + + internal enum DS + { + BEGIN = 0, + N = 1, // have one number + NN = 2, // have two numbers + + // The following are known to be part of a date + + D_Nd = 3, // date string: have number followed by date separator + D_NN = 4, // date string: have two numbers + D_NNd = 5, // date string: have two numbers followed by date separator + + D_M = 6, // date string: have a month + D_MN = 7, // date string: have a month and a number + D_NM = 8, // date string: have a number and a month + D_MNd = 9, // date string: have a month and number followed by date separator + D_NDS = 10, // date string: have one number followed a date suffix. + + D_Y = 11, // date string: have a year. + D_YN = 12, // date string: have a year and a number + D_YNd = 13, // date string: have a year and a number and a date separator + D_YM = 14, // date string: have a year and a month + D_YMd = 15, // date string: have a year and a month and a date separator + D_S = 16, // have numbers followed by a date suffix. + T_S = 17, // have numbers followed by a time suffix. + + // The following are known to be part of a time + + T_Nt = 18, // have num followed by time separator + T_NNt = 19, // have two numbers followed by time separator + + + ERROR = 20, + + // The following are terminal states. These all have an action + // associated with them; and transition back to BEGIN. + + DX_NN = 21, // day from two numbers + DX_NNN = 22, // day from three numbers + DX_MN = 23, // day from month and one number + DX_NM = 24, // day from month and one number + DX_MNN = 25, // day from month and two numbers + DX_DS = 26, // a set of date suffixed numbers. + DX_DSN = 27, // day from date suffixes and one number. + DX_NDS = 28, // day from one number and date suffixes . + DX_NNDS = 29, // day from one number and date suffixes . + + DX_YNN = 30, // date string: have a year and two number + DX_YMN = 31, // date string: have a year, a month, and a number. + DX_YN = 32, // date string: have a year and one number + DX_YM = 33, // date string: have a year, a month. + TX_N = 34, // time from one number (must have ampm) + TX_NN = 35, // time from two numbers + TX_NNN = 36, // time from three numbers + TX_TS = 37, // a set of time suffixed numbers. + DX_NNY = 38, + } + + //////////////////////////////////////////////////////////////////////////// + // + // NOTE: The following state machine table is dependent on the order of the + // DS and DTT enumerations. + // + // For each non terminal state, the following table defines the next state + // for each given date token type. + // + //////////////////////////////////////////////////////////////////////////// + + // End NumEnd NumAmPm NumSpace NumDaySep NumTimesep MonthEnd MonthSpace MonthDSep NumDateSuff NumTimeSuff DayOfWeek YearSpace YearDateSep YearEnd TimeZone Era UTCTimeMark + private static DS[][] dateParsingStates = { +// DS.BEGIN // DS.BEGIN +new DS[] { DS.BEGIN, DS.ERROR, DS.TX_N, DS.N, DS.D_Nd, DS.T_Nt, DS.ERROR, DS.D_M, DS.D_M, DS.D_S, DS.T_S, DS.BEGIN, DS.D_Y, DS.D_Y, DS.ERROR, DS.BEGIN, DS.BEGIN, DS.ERROR}, + +// DS.N // DS.N +new DS[] { DS.ERROR, DS.DX_NN, DS.ERROR, DS.NN, DS.D_NNd, DS.ERROR, DS.DX_NM, DS.D_NM, DS.D_MNd, DS.D_NDS, DS.ERROR, DS.N, DS.D_YN, DS.D_YNd, DS.DX_YN, DS.N, DS.N, DS.ERROR}, + +// DS.NN // DS.NN +new DS[] { DS.DX_NN, DS.DX_NNN, DS.TX_N, DS.DX_NNN, DS.ERROR, DS.T_Nt, DS.DX_MNN, DS.DX_MNN, DS.ERROR, DS.ERROR, DS.T_S, DS.NN, DS.DX_NNY, DS.ERROR, DS.DX_NNY, DS.NN, DS.NN, DS.ERROR}, + +// DS.D_Nd // DS.D_Nd +new DS[] { DS.ERROR, DS.DX_NN, DS.ERROR, DS.D_NN, DS.D_NNd, DS.ERROR, DS.DX_NM, DS.D_MN, DS.D_MNd, DS.ERROR, DS.ERROR, DS.D_Nd, DS.D_YN, DS.D_YNd, DS.DX_YN, DS.ERROR, DS.D_Nd, DS.ERROR}, + +// DS.D_NN // DS.D_NN +new DS[] { DS.DX_NN, DS.DX_NNN, DS.TX_N, DS.DX_NNN, DS.ERROR, DS.T_Nt, DS.DX_MNN, DS.DX_MNN, DS.ERROR, DS.DX_DS, DS.T_S, DS.D_NN, DS.DX_NNY, DS.ERROR, DS.DX_NNY, DS.ERROR, DS.D_NN, DS.ERROR}, + +// DS.D_NNd // DS.D_NNd +new DS[] { DS.ERROR, DS.DX_NNN, DS.DX_NNN, DS.DX_NNN, DS.ERROR, DS.ERROR, DS.DX_MNN, DS.DX_MNN, DS.ERROR, DS.DX_DS, DS.ERROR, DS.D_NNd, DS.DX_NNY, DS.ERROR, DS.DX_NNY, DS.ERROR, DS.D_NNd, DS.ERROR}, + +// DS.D_M // DS.D_M +new DS[] { DS.ERROR, DS.DX_MN, DS.ERROR, DS.D_MN, DS.D_MNd, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.D_M, DS.D_YM, DS.D_YMd, DS.DX_YM, DS.ERROR, DS.D_M, DS.ERROR}, + +// DS.D_MN // DS.D_MN +new DS[] { DS.DX_MN, DS.DX_MNN, DS.DX_MNN, DS.DX_MNN, DS.ERROR, DS.T_Nt, DS.ERROR, DS.ERROR, DS.ERROR, DS.DX_DS, DS.T_S, DS.D_MN, DS.DX_YMN, DS.ERROR, DS.DX_YMN, DS.ERROR, DS.D_MN, DS.ERROR}, + +// DS.D_NM // DS.D_NM +new DS[] { DS.DX_NM, DS.DX_MNN, DS.DX_MNN, DS.DX_MNN, DS.ERROR, DS.T_Nt, DS.ERROR, DS.ERROR, DS.ERROR, DS.DX_DS, DS.T_S, DS.D_NM, DS.DX_YMN, DS.ERROR, DS.DX_YMN, DS.ERROR, DS.D_NM, DS.ERROR}, + +// DS.D_MNd // DS.D_MNd +new DS[] { DS.ERROR, DS.DX_MNN, DS.ERROR, DS.DX_MNN, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.D_MNd, DS.DX_YMN, DS.ERROR, DS.DX_YMN, DS.ERROR, DS.D_MNd, DS.ERROR}, + +// DS.D_NDS, // DS.D_NDS, +new DS[] { DS.DX_NDS,DS.DX_NNDS, DS.DX_NNDS, DS.DX_NNDS, DS.ERROR, DS.T_Nt, DS.ERROR, DS.ERROR, DS.ERROR, DS.D_NDS, DS.T_S, DS.D_NDS, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.D_NDS, DS.ERROR}, + +// DS.D_Y // DS.D_Y +new DS[] { DS.ERROR, DS.DX_YN, DS.ERROR, DS.D_YN, DS.D_YNd, DS.ERROR, DS.DX_YM, DS.D_YM, DS.D_YMd, DS.D_YM, DS.ERROR, DS.D_Y, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.D_Y, DS.ERROR}, + +// DS.D_YN // DS.D_YN +new DS[] { DS.DX_YN, DS.DX_YNN, DS.DX_YNN, DS.DX_YNN, DS.ERROR, DS.ERROR, DS.DX_YMN, DS.DX_YMN, DS.ERROR, DS.ERROR, DS.ERROR, DS.D_YN, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.D_YN, DS.ERROR}, + +// DS.D_YNd // DS.D_YNd +new DS[] { DS.ERROR, DS.DX_YNN, DS.DX_YNN, DS.DX_YNN, DS.ERROR, DS.ERROR, DS.DX_YMN, DS.DX_YMN, DS.ERROR, DS.ERROR, DS.ERROR, DS.D_YN, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.D_YN, DS.ERROR}, + +// DS.D_YM // DS.D_YM +new DS[] { DS.DX_YM, DS.DX_YMN, DS.DX_YMN, DS.DX_YMN, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.D_YM, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.D_YM, DS.ERROR}, + +// DS.D_YMd // DS.D_YMd +new DS[] { DS.ERROR, DS.DX_YMN, DS.DX_YMN, DS.DX_YMN, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.D_YM, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.D_YM, DS.ERROR}, + +// DS.D_S // DS.D_S +new DS[] { DS.DX_DS, DS.DX_DSN, DS.TX_N, DS.T_Nt, DS.ERROR, DS.T_Nt, DS.ERROR, DS.ERROR, DS.ERROR, DS.D_S, DS.T_S, DS.D_S, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.D_S, DS.ERROR}, + +// DS.T_S // DS.T_S +new DS[] { DS.TX_TS, DS.TX_TS, DS.TX_TS, DS.T_Nt, DS.D_Nd, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.D_S, DS.T_S, DS.T_S, DS.ERROR, DS.ERROR, DS.ERROR, DS.T_S, DS.T_S, DS.ERROR}, + +// DS.T_Nt // DS.T_Nt +new DS[] { DS.ERROR, DS.TX_NN, DS.TX_NN, DS.TX_NN, DS.ERROR, DS.T_NNt, DS.DX_NM, DS.D_NM, DS.ERROR, DS.ERROR, DS.T_S, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.T_Nt, DS.T_Nt, DS.TX_NN}, + +// DS.T_NNt // DS.T_NNt +new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.T_S, DS.T_NNt, DS.ERROR, DS.ERROR, DS.ERROR, DS.T_NNt, DS.T_NNt, DS.TX_NNN}, +}; + // End NumEnd NumAmPm NumSpace NumDaySep NumTimesep MonthEnd MonthSpace MonthDSep NumDateSuff NumTimeSuff DayOfWeek YearSpace YearDateSep YearEnd TimeZone Era UTCMark + + internal const String GMTName = "GMT"; + internal const String ZuluName = "Z"; + + // + // Search from the index of str at str.Index to see if the target string exists in the str. + // + private static bool MatchWord(ref __DTString str, String target) + { + int length = target.Length; + if (length > (str.Value.Length - str.Index)) + { + return false; + } + + if (str.CompareInfo.Compare(str.Value, str.Index, length, + target, 0, length, CompareOptions.IgnoreCase) != 0) + { + return (false); + } + + int nextCharIndex = str.Index + target.Length; + + if (nextCharIndex < str.Value.Length) + { + char nextCh = str.Value[nextCharIndex]; + if (Char.IsLetter(nextCh)) + { + return (false); + } + } + str.Index = nextCharIndex; + if (str.Index < str.len) + { + str.m_current = str.Value[str.Index]; + } + + return (true); + } + + + // + // Check the word at the current index to see if it matches GMT name or Zulu name. + // + private static bool GetTimeZoneName(ref __DTString str) + { + if (MatchWord(ref str, GMTName)) + { + return (true); + } + + if (MatchWord(ref str, ZuluName)) + { + return (true); + } + + return (false); + } + + internal static bool IsDigit(char ch) + { + return (ch >= '0' && ch <= '9'); + } + + + /*=================================ParseFraction========================== + **Action: Starting at the str.Index, which should be a decimal symbol. + ** if the current character is a digit, parse the remaining + ** numbers as fraction. For example, if the sub-string starting at str.Index is "123", then + ** the method will return 0.123 + **Returns: The fraction number. + **Arguments: + ** str the parsing string + **Exceptions: + ============================================================================*/ + + private static bool ParseFraction(ref __DTString str, out double result) + { + result = 0; + double decimalBase = 0.1; + int digits = 0; + char ch; + while (str.GetNext() + && IsDigit(ch = str.m_current)) + { + result += (ch - '0') * decimalBase; + decimalBase *= 0.1; + digits++; + } + return (digits > 0); + } + + /*=================================ParseTimeZone========================== + **Action: Parse the timezone offset in the following format: + ** "+8", "+08", "+0800", "+0800" + ** This method is used by DateTime.Parse(). + **Returns: The TimeZone offset. + **Arguments: + ** str the parsing string + **Exceptions: + ** FormatException if invalid timezone format is found. + ============================================================================*/ + + private static bool ParseTimeZone(ref __DTString str, ref TimeSpan result) + { + // The hour/minute offset for timezone. + int hourOffset = 0; + int minuteOffset = 0; + DTSubString sub; + + // Consume the +/- character that has already been read + sub = str.GetSubString(); + if (sub.length != 1) + { + return false; + } + char offsetChar = sub[0]; + if (offsetChar != '+' && offsetChar != '-') + { + return false; + } + str.ConsumeSubString(sub); + + sub = str.GetSubString(); + if (sub.type != DTSubStringType.Number) + { + return false; + } + int value = sub.value; + int length = sub.length; + if (length == 1 || length == 2) + { + // Parsing "+8" or "+08" + hourOffset = value; + str.ConsumeSubString(sub); + // See if we have minutes + sub = str.GetSubString(); + if (sub.length == 1 && sub[0] == ':') + { + // Parsing "+8:00" or "+08:00" + str.ConsumeSubString(sub); + sub = str.GetSubString(); + if (sub.type != DTSubStringType.Number || sub.length < 1 || sub.length > 2) + { + return false; + } + minuteOffset = sub.value; + str.ConsumeSubString(sub); + } + } + else if (length == 3 || length == 4) + { + // Parsing "+800" or "+0800" + hourOffset = value / 100; + minuteOffset = value % 100; + str.ConsumeSubString(sub); + } + else + { + // Wrong number of digits + return false; + } + Debug.Assert(hourOffset >= 0 && hourOffset <= 99, "hourOffset >= 0 && hourOffset <= 99"); + Debug.Assert(minuteOffset >= 0 && minuteOffset <= 99, "minuteOffset >= 0 && minuteOffset <= 99"); + if (minuteOffset < 0 || minuteOffset >= 60) + { + return false; + } + + result = new TimeSpan(hourOffset, minuteOffset, 0); + if (offsetChar == '-') + { + result = result.Negate(); + } + return true; + } + + // This is the helper function to handle timezone in string in the format like +/-0800 + private static bool HandleTimeZone(ref __DTString str, ref DateTimeResult result) + { + if ((str.Index < str.len - 1)) + { + char nextCh = str.Value[str.Index]; + // Skip whitespace, but don't update the index unless we find a time zone marker + int whitespaceCount = 0; + while (Char.IsWhiteSpace(nextCh) && str.Index + whitespaceCount < str.len - 1) + { + whitespaceCount++; + nextCh = str.Value[str.Index + whitespaceCount]; + } + if (nextCh == '+' || nextCh == '-') + { + str.Index += whitespaceCount; + if ((result.flags & ParseFlags.TimeZoneUsed) != 0) + { + // Should not have two timezone offsets. + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + result.flags |= ParseFlags.TimeZoneUsed; + if (!ParseTimeZone(ref str, ref result.timeZoneOffset)) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + } + } + return true; + } + + // + // This is the lexer. Check the character at the current index, and put the found token in dtok and + // some raw date/time information in raw. + // + private static Boolean Lex(DS dps, ref __DTString str, ref DateTimeToken dtok, ref DateTimeRawInfo raw, ref DateTimeResult result, ref DateTimeFormatInfo dtfi, DateTimeStyles styles) + { + TokenType tokenType; + int tokenValue; + int indexBeforeSeparator; + char charBeforeSeparator; + + TokenType sep; + dtok.dtt = DTT.Unk; // Assume the token is unkown. + + str.GetRegularToken(out tokenType, out tokenValue, dtfi); + +#if _LOGGING + // Builds with _LOGGING defined (x86dbg, amd64chk, etc) support tracing + // Set the following internal-only/unsupported environment variables to enable DateTime tracing to the console: + // + // COMPlus_LogEnable=1 + // COMPlus_LogToConsole=1 + // COMPlus_LogLevel=9 + // COMPlus_ManagedLogFacility=0x00001000 + if (_tracingEnabled) + { + BCLDebug.Trace("DATETIME", "[DATETIME] Lex({0})\tpos:{1}({2}), {3}, DS.{4}", Hex(str.Value), + str.Index, Hex(str.m_current), tokenType, dps); + } +#endif // _LOGGING + + // Look at the regular token. + switch (tokenType) + { + case TokenType.NumberToken: + case TokenType.YearNumberToken: + if (raw.numCount == 3 || tokenValue == -1) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + LexTraceExit("0010", dps); + return false; + } + // + // This is a digit. + // + // If the previous parsing state is DS.T_NNt (like 12:01), and we got another number, + // so we will have a terminal state DS.TX_NNN (like 12:01:02). + // If the previous parsing state is DS.T_Nt (like 12:), and we got another number, + // so we will have a terminal state DS.TX_NN (like 12:01). + // + // Look ahead to see if the following character is a decimal point or timezone offset. + // This enables us to parse time in the forms of: + // "11:22:33.1234" or "11:22:33-08". + if (dps == DS.T_NNt) + { + if ((str.Index < str.len - 1)) + { + char nextCh = str.Value[str.Index]; + if (nextCh == '.') + { + // While ParseFraction can fail, it just means that there were no digits after + // the dot. In this case ParseFraction just removes the dot. This is actually + // valid for cultures like Albanian, that join the time marker to the time with + // with a dot: e.g. "9:03.MD" + ParseFraction(ref str, out raw.fraction); + } + } + } + if (dps == DS.T_NNt || dps == DS.T_Nt) + { + if ((str.Index < str.len - 1)) + { + if (false == HandleTimeZone(ref str, ref result)) + { + LexTraceExit("0020 (value like \"12:01\" or \"12:\" followed by a non-TZ number", dps); + return false; + } + } + } + + dtok.num = tokenValue; + if (tokenType == TokenType.YearNumberToken) + { + if (raw.year == -1) + { + raw.year = tokenValue; + // + // If we have number which has 3 or more digits (like "001" or "0001"), + // we assume this number is a year. Save the currnet raw.numCount in + // raw.year. + // + switch (sep = str.GetSeparatorToken(dtfi, out indexBeforeSeparator, out charBeforeSeparator)) + { + case TokenType.SEP_End: + dtok.dtt = DTT.YearEnd; + break; + case TokenType.SEP_Am: + case TokenType.SEP_Pm: + if (raw.timeMark == TM.NotSet) + { + raw.timeMark = (sep == TokenType.SEP_Am ? TM.AM : TM.PM); + dtok.dtt = DTT.YearSpace; + } + else + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + LexTraceExit("0030 (TM.AM/TM.PM Happened more than 1x)", dps); + } + break; + case TokenType.SEP_Space: + dtok.dtt = DTT.YearSpace; + break; + case TokenType.SEP_Date: + dtok.dtt = DTT.YearDateSep; + break; + case TokenType.SEP_Time: + if (!raw.hasSameDateAndTimeSeparators) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + LexTraceExit("0040 (Invalid separator after number)", dps); + return false; + } + + // we have the date and time separators are same and getting a year number, then change the token to YearDateSep as + // we are sure we are not parsing time. + dtok.dtt = DTT.YearDateSep; + break; + case TokenType.SEP_DateOrOffset: + // The separator is either a date separator or the start of a time zone offset. If the token will complete the date then + // process just the number and roll back the index so that the outer loop can attempt to parse the time zone offset. + if ((dateParsingStates[(int)dps][(int)DTT.YearDateSep] == DS.ERROR) + && (dateParsingStates[(int)dps][(int)DTT.YearSpace] > DS.ERROR)) + { + str.Index = indexBeforeSeparator; + str.m_current = charBeforeSeparator; + dtok.dtt = DTT.YearSpace; + } + else + { + dtok.dtt = DTT.YearDateSep; + } + break; + case TokenType.SEP_YearSuff: + case TokenType.SEP_MonthSuff: + case TokenType.SEP_DaySuff: + dtok.dtt = DTT.NumDatesuff; + dtok.suffix = sep; + break; + case TokenType.SEP_HourSuff: + case TokenType.SEP_MinuteSuff: + case TokenType.SEP_SecondSuff: + dtok.dtt = DTT.NumTimesuff; + dtok.suffix = sep; + break; + default: + // Invalid separator after number number. + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + LexTraceExit("0040 (Invalid separator after number)", dps); + return false; + } + // + // Found the token already. Return now. + // + LexTraceExit("0050 (success)", dps); + return true; + } + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + LexTraceExit("0060", dps); + return false; + } + switch (sep = str.GetSeparatorToken(dtfi, out indexBeforeSeparator, out charBeforeSeparator)) + { + // + // Note here we check if the numCount is less than three. + // When we have more than three numbers, it will be caught as error in the state machine. + // + case TokenType.SEP_End: + dtok.dtt = DTT.NumEnd; + raw.AddNumber(dtok.num); + break; + case TokenType.SEP_Am: + case TokenType.SEP_Pm: + if (raw.timeMark == TM.NotSet) + { + raw.timeMark = (sep == TokenType.SEP_Am ? TM.AM : TM.PM); + dtok.dtt = DTT.NumAmpm; + // Fix AM/PM parsing case, e.g. "1/10 5 AM" + if (dps == DS.D_NN) + { + if (!ProcessTerminaltState(DS.DX_NN, ref result, ref styles, ref raw, dtfi)) + { + return false; + } + } + + raw.AddNumber(dtok.num); + } + else + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + break; + } + if (dps == DS.T_NNt || dps == DS.T_Nt) + { + if (false == HandleTimeZone(ref str, ref result)) + { + LexTraceExit("0070 (HandleTimeZone returned false)", dps); + return false; + } + } + break; + case TokenType.SEP_Space: + dtok.dtt = DTT.NumSpace; + raw.AddNumber(dtok.num); + break; + case TokenType.SEP_Date: + dtok.dtt = DTT.NumDatesep; + raw.AddNumber(dtok.num); + break; + case TokenType.SEP_DateOrOffset: + // The separator is either a date separator or the start of a time zone offset. If the token will complete the date then + // process just the number and roll back the index so that the outer loop can attempt to parse the time zone offset. + if ((dateParsingStates[(int)dps][(int)DTT.NumDatesep] == DS.ERROR) + && (dateParsingStates[(int)dps][(int)DTT.NumSpace] > DS.ERROR)) + { + str.Index = indexBeforeSeparator; + str.m_current = charBeforeSeparator; + dtok.dtt = DTT.NumSpace; + } + else + { + dtok.dtt = DTT.NumDatesep; + } + raw.AddNumber(dtok.num); + break; + case TokenType.SEP_Time: + if (raw.hasSameDateAndTimeSeparators && + (dps == DS.D_Y || dps == DS.D_YN || dps == DS.D_YNd || dps == DS.D_YM || dps == DS.D_YMd)) + { + // we are parsing a date and we have the time separator same as date separator, so we mark the token as date separator + dtok.dtt = DTT.NumDatesep; + raw.AddNumber(dtok.num); + break; + } + dtok.dtt = DTT.NumTimesep; + raw.AddNumber(dtok.num); + break; + case TokenType.SEP_YearSuff: + try + { + dtok.num = dtfi.Calendar.ToFourDigitYear(tokenValue); + } + catch (ArgumentOutOfRangeException e) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", e); + LexTraceExit("0075 (Calendar.ToFourDigitYear failed)", dps); + return false; + } + dtok.dtt = DTT.NumDatesuff; + dtok.suffix = sep; + break; + case TokenType.SEP_MonthSuff: + case TokenType.SEP_DaySuff: + dtok.dtt = DTT.NumDatesuff; + dtok.suffix = sep; + break; + case TokenType.SEP_HourSuff: + case TokenType.SEP_MinuteSuff: + case TokenType.SEP_SecondSuff: + dtok.dtt = DTT.NumTimesuff; + dtok.suffix = sep; + break; + case TokenType.SEP_LocalTimeMark: + dtok.dtt = DTT.NumLocalTimeMark; + raw.AddNumber(dtok.num); + break; + default: + // Invalid separator after number number. + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + LexTraceExit("0080", dps); + return false; + } + break; + case TokenType.HebrewNumber: + if (tokenValue >= 100) + { + // This is a year number + if (raw.year == -1) + { + raw.year = tokenValue; + // + // If we have number which has 3 or more digits (like "001" or "0001"), + // we assume this number is a year. Save the currnet raw.numCount in + // raw.year. + // + switch (sep = str.GetSeparatorToken(dtfi, out indexBeforeSeparator, out charBeforeSeparator)) + { + case TokenType.SEP_End: + dtok.dtt = DTT.YearEnd; + break; + case TokenType.SEP_Space: + dtok.dtt = DTT.YearSpace; + break; + case TokenType.SEP_DateOrOffset: + // The separator is either a date separator or the start of a time zone offset. If the token will complete the date then + // process just the number and roll back the index so that the outer loop can attempt to parse the time zone offset. + if (dateParsingStates[(int)dps][(int)DTT.YearSpace] > DS.ERROR) + { + str.Index = indexBeforeSeparator; + str.m_current = charBeforeSeparator; + dtok.dtt = DTT.YearSpace; + break; + } + goto default; + default: + // Invalid separator after number number. + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + LexTraceExit("0090", dps); + return false; + } + } + else + { + // Invalid separator after number number. + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + LexTraceExit("0100", dps); + return false; + } + } + else + { + // This is a day number + dtok.num = tokenValue; + raw.AddNumber(dtok.num); + + switch (sep = str.GetSeparatorToken(dtfi, out indexBeforeSeparator, out charBeforeSeparator)) + { + // + // Note here we check if the numCount is less than three. + // When we have more than three numbers, it will be caught as error in the state machine. + // + case TokenType.SEP_End: + dtok.dtt = DTT.NumEnd; + break; + case TokenType.SEP_Space: + case TokenType.SEP_Date: + dtok.dtt = DTT.NumDatesep; + break; + case TokenType.SEP_DateOrOffset: + // The separator is either a date separator or the start of a time zone offset. If the token will complete the date then + // process just the number and roll back the index so that the outer loop can attempt to parse the time zone offset. + if ((dateParsingStates[(int)dps][(int)DTT.NumDatesep] == DS.ERROR) + && (dateParsingStates[(int)dps][(int)DTT.NumSpace] > DS.ERROR)) + { + str.Index = indexBeforeSeparator; + str.m_current = charBeforeSeparator; + dtok.dtt = DTT.NumSpace; + } + else + { + dtok.dtt = DTT.NumDatesep; + } + break; + default: + // Invalid separator after number number. + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + LexTraceExit("0110", dps); + return false; + } + } + break; + case TokenType.DayOfWeekToken: + if (raw.dayOfWeek == -1) + { + // + // This is a day of week name. + // + raw.dayOfWeek = tokenValue; + dtok.dtt = DTT.DayOfWeek; + } + else + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + LexTraceExit("0120 (DayOfWeek seen more than 1x)", dps); + return false; + } + break; + case TokenType.MonthToken: + if (raw.month == -1) + { + // + // This is a month name + // + switch (sep = str.GetSeparatorToken(dtfi, out indexBeforeSeparator, out charBeforeSeparator)) + { + case TokenType.SEP_End: + dtok.dtt = DTT.MonthEnd; + break; + case TokenType.SEP_Space: + dtok.dtt = DTT.MonthSpace; + break; + case TokenType.SEP_Date: + dtok.dtt = DTT.MonthDatesep; + break; + case TokenType.SEP_Time: + if (!raw.hasSameDateAndTimeSeparators) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + LexTraceExit("0130 (Invalid separator after month name)", dps); + return false; + } + + // we have the date and time separators are same and getting a Month name, then change the token to MonthDatesep as + // we are sure we are not parsing time. + dtok.dtt = DTT.MonthDatesep; + break; + case TokenType.SEP_DateOrOffset: + // The separator is either a date separator or the start of a time zone offset. If the token will complete the date then + // process just the number and roll back the index so that the outer loop can attempt to parse the time zone offset. + if ((dateParsingStates[(int)dps][(int)DTT.MonthDatesep] == DS.ERROR) + && (dateParsingStates[(int)dps][(int)DTT.MonthSpace] > DS.ERROR)) + { + str.Index = indexBeforeSeparator; + str.m_current = charBeforeSeparator; + dtok.dtt = DTT.MonthSpace; + } + else + { + dtok.dtt = DTT.MonthDatesep; + } + break; + default: + //Invalid separator after month name + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + LexTraceExit("0130 (Invalid separator after month name)", dps); + return false; + } + raw.month = tokenValue; + } + else + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + LexTraceExit("0140 (MonthToken seen more than 1x)", dps); + return false; + } + break; + case TokenType.EraToken: + if (result.era != -1) + { + result.era = tokenValue; + dtok.dtt = DTT.Era; + } + else + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + LexTraceExit("0150 (EraToken seen when result.era already set)", dps); + return false; + } + break; + case TokenType.JapaneseEraToken: + // Special case for Japanese. We allow Japanese era name to be used even if the calendar is not Japanese Calendar. + result.calendar = JapaneseCalendar.GetDefaultInstance(); + dtfi = DateTimeFormatInfo.GetJapaneseCalendarDTFI(); + if (result.era != -1) + { + result.era = tokenValue; + dtok.dtt = DTT.Era; + } + else + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + LexTraceExit("0160 (JapaneseEraToken seen when result.era already set)", dps); + return false; + } + break; + case TokenType.TEraToken: + result.calendar = TaiwanCalendar.GetDefaultInstance(); + dtfi = DateTimeFormatInfo.GetTaiwanCalendarDTFI(); + if (result.era != -1) + { + result.era = tokenValue; + dtok.dtt = DTT.Era; + } + else + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + LexTraceExit("0170 (TEraToken seen when result.era already set)", dps); + return false; + } + break; + case TokenType.TimeZoneToken: + // + // This is a timezone designator + // + // NOTENOTE : for now, we only support "GMT" and "Z" (for Zulu time). + // + if ((result.flags & ParseFlags.TimeZoneUsed) != 0) + { + // Should not have two timezone offsets. + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + LexTraceExit("0180 (seen GMT or Z more than 1x)", dps); + return false; + } + dtok.dtt = DTT.TimeZone; + result.flags |= ParseFlags.TimeZoneUsed; + result.timeZoneOffset = new TimeSpan(0); + result.flags |= ParseFlags.TimeZoneUtc; + break; + case TokenType.EndOfString: + dtok.dtt = DTT.End; + break; + case TokenType.DateWordToken: + case TokenType.IgnorableSymbol: + // Date words and ignorable symbols can just be skipped over + break; + case TokenType.Am: + case TokenType.Pm: + if (raw.timeMark == TM.NotSet) + { + raw.timeMark = (TM)tokenValue; + } + else + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + LexTraceExit("0190 (AM/PM timeMark already set)", dps); + return false; + } + break; + case TokenType.UnknownToken: + if (Char.IsLetter(str.m_current)) + { + result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_UnknowDateTimeWord", str.Index); + LexTraceExit("0200", dps); + return (false); + } + + if ((str.m_current == '-' || str.m_current == '+') && ((result.flags & ParseFlags.TimeZoneUsed) == 0)) + { + Int32 originalIndex = str.Index; + if (ParseTimeZone(ref str, ref result.timeZoneOffset)) + { + result.flags |= ParseFlags.TimeZoneUsed; + LexTraceExit("0220 (success)", dps); + return true; + } + else + { + // Time zone parse attempt failed. Fall through to punctuation handling. + str.Index = originalIndex; + } + } + + // Visual Basic implements string to date conversions on top of DateTime.Parse: + // CDate("#10/10/95#") + // + if (VerifyValidPunctuation(ref str)) + { + LexTraceExit("0230 (success)", dps); + return true; + } + + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + LexTraceExit("0240", dps); + return false; + } + + LexTraceExit("0250 (success)", dps); + return true; + } + + private static Boolean VerifyValidPunctuation(ref __DTString str) + { + // Compatability Behavior. Allow trailing nulls and surrounding hashes + Char ch = str.Value[str.Index]; + if (ch == '#') + { + bool foundStart = false; + bool foundEnd = false; + for (int i = 0; i < str.len; i++) + { + ch = str.Value[i]; + if (ch == '#') + { + if (foundStart) + { + if (foundEnd) + { + // Having more than two hashes is invalid + return false; + } + else + { + foundEnd = true; + } + } + else + { + foundStart = true; + } + } + else if (ch == '\0') + { + // Allow nulls only at the end + if (!foundEnd) + { + return false; + } + } + else if ((!Char.IsWhiteSpace(ch))) + { + // Anthyhing other than whitespace outside hashes is invalid + if (!foundStart || foundEnd) + { + return false; + } + } + } + if (!foundEnd) + { + // The has was un-paired + return false; + } + // Valid Hash usage: eat the hash and continue. + str.GetNext(); + return true; + } + else if (ch == '\0') + { + for (int i = str.Index; i < str.len; i++) + { + if (str.Value[i] != '\0') + { + // Nulls are only valid if they are the only trailing character + return false; + } + } + // Move to the end of the string + str.Index = str.len; + return true; + } + return false; + } + + private const int ORDER_YMD = 0; // The order of date is Year/Month/Day. + private const int ORDER_MDY = 1; // The order of date is Month/Day/Year. + private const int ORDER_DMY = 2; // The order of date is Day/Month/Year. + private const int ORDER_YDM = 3; // The order of date is Year/Day/Month + private const int ORDER_YM = 4; // Year/Month order. + private const int ORDER_MY = 5; // Month/Year order. + private const int ORDER_MD = 6; // Month/Day order. + private const int ORDER_DM = 7; // Day/Month order. + + // + // Decide the year/month/day order from the datePattern. + // + // Return 0 for YMD, 1 for MDY, 2 for DMY, otherwise -1. + // + private static Boolean GetYearMonthDayOrder(String datePattern, DateTimeFormatInfo dtfi, out int order) + { + int yearOrder = -1; + int monthOrder = -1; + int dayOrder = -1; + int orderCount = 0; + + bool inQuote = false; + + for (int i = 0; i < datePattern.Length && orderCount < 3; i++) + { + char ch = datePattern[i]; + if (ch == '\\' || ch == '%') + { + i++; + continue; // Skip next character that is escaped by this backslash + } + + if (ch == '\'' || ch == '"') + { + inQuote = !inQuote; + } + + if (!inQuote) + { + if (ch == 'y') + { + yearOrder = orderCount++; + + // + // Skip all year pattern charaters. + // + for (; i + 1 < datePattern.Length && datePattern[i + 1] == 'y'; i++) + { + // Do nothing here. + } + } + else if (ch == 'M') + { + monthOrder = orderCount++; + // + // Skip all month pattern characters. + // + for (; i + 1 < datePattern.Length && datePattern[i + 1] == 'M'; i++) + { + // Do nothing here. + } + } + else if (ch == 'd') + { + int patternCount = 1; + // + // Skip all day pattern characters. + // + for (; i + 1 < datePattern.Length && datePattern[i + 1] == 'd'; i++) + { + patternCount++; + } + // + // Make sure this is not "ddd" or "dddd", which means day of week. + // + if (patternCount <= 2) + { + dayOrder = orderCount++; + } + } + } + } + + if (yearOrder == 0 && monthOrder == 1 && dayOrder == 2) + { + order = ORDER_YMD; + return true; + } + if (monthOrder == 0 && dayOrder == 1 && yearOrder == 2) + { + order = ORDER_MDY; + return true; + } + if (dayOrder == 0 && monthOrder == 1 && yearOrder == 2) + { + order = ORDER_DMY; + return true; + } + if (yearOrder == 0 && dayOrder == 1 && monthOrder == 2) + { + order = ORDER_YDM; + return true; + } + order = -1; + return false; + } + + // + // Decide the year/month order from the pattern. + // + // Return 0 for YM, 1 for MY, otherwise -1. + // + private static Boolean GetYearMonthOrder(String pattern, DateTimeFormatInfo dtfi, out int order) + { + int yearOrder = -1; + int monthOrder = -1; + int orderCount = 0; + + bool inQuote = false; + for (int i = 0; i < pattern.Length && orderCount < 2; i++) + { + char ch = pattern[i]; + if (ch == '\\' || ch == '%') + { + i++; + continue; // Skip next character that is escaped by this backslash + } + + if (ch == '\'' || ch == '"') + { + inQuote = !inQuote; + } + + if (!inQuote) + { + if (ch == 'y') + { + yearOrder = orderCount++; + + // + // Skip all year pattern charaters. + // + for (; i + 1 < pattern.Length && pattern[i + 1] == 'y'; i++) + { + } + } + else if (ch == 'M') + { + monthOrder = orderCount++; + // + // Skip all month pattern characters. + // + for (; i + 1 < pattern.Length && pattern[i + 1] == 'M'; i++) + { + } + } + } + } + + if (yearOrder == 0 && monthOrder == 1) + { + order = ORDER_YM; + return true; + } + if (monthOrder == 0 && yearOrder == 1) + { + order = ORDER_MY; + return true; + } + order = -1; + return false; + } + + // + // Decide the month/day order from the pattern. + // + // Return 0 for MD, 1 for DM, otherwise -1. + // + private static Boolean GetMonthDayOrder(String pattern, DateTimeFormatInfo dtfi, out int order) + { + int monthOrder = -1; + int dayOrder = -1; + int orderCount = 0; + + bool inQuote = false; + for (int i = 0; i < pattern.Length && orderCount < 2; i++) + { + char ch = pattern[i]; + if (ch == '\\' || ch == '%') + { + i++; + continue; // Skip next character that is escaped by this backslash + } + + if (ch == '\'' || ch == '"') + { + inQuote = !inQuote; + } + + if (!inQuote) + { + if (ch == 'd') + { + int patternCount = 1; + // + // Skip all day pattern charaters. + // + for (; i + 1 < pattern.Length && pattern[i + 1] == 'd'; i++) + { + patternCount++; + } + + // + // Make sure this is not "ddd" or "dddd", which means day of week. + // + if (patternCount <= 2) + { + dayOrder = orderCount++; + } + } + else if (ch == 'M') + { + monthOrder = orderCount++; + // + // Skip all month pattern characters. + // + for (; i + 1 < pattern.Length && pattern[i + 1] == 'M'; i++) + { + } + } + } + } + + if (monthOrder == 0 && dayOrder == 1) + { + order = ORDER_MD; + return true; + } + if (dayOrder == 0 && monthOrder == 1) + { + order = ORDER_DM; + return true; + } + order = -1; + return false; + } + + // + // Adjust the two-digit year if necessary. + // + private static bool TryAdjustYear(ref DateTimeResult result, int year, out int adjustedYear) + { + if (year < 100) + { + try + { + // the Calendar classes need some real work. Many of the calendars that throw + // don't implement a fast/non-allocating (and non-throwing) IsValid{Year|Day|Month} method. + // we are making a targeted try/catch fix in the in-place release but will revisit this code + // in the next side-by-side release. + year = result.calendar.ToFourDigitYear(year); + } + catch (ArgumentOutOfRangeException) + { + adjustedYear = -1; + return false; + } + } + adjustedYear = year; + return true; + } + + private static bool SetDateYMD(ref DateTimeResult result, int year, int month, int day) + { + // Note, longer term these checks should be done at the end of the parse. This current + // way of checking creates order dependence with parsing the era name. + if (result.calendar.IsValidDay(year, month, day, result.era)) + { + result.SetDate(year, month, day); // YMD + return (true); + } + return (false); + } + + private static bool SetDateMDY(ref DateTimeResult result, int month, int day, int year) + { + return (SetDateYMD(ref result, year, month, day)); + } + + private static bool SetDateDMY(ref DateTimeResult result, int day, int month, int year) + { + return (SetDateYMD(ref result, year, month, day)); + } + + private static bool SetDateYDM(ref DateTimeResult result, int year, int day, int month) + { + return (SetDateYMD(ref result, year, month, day)); + } + + private static void GetDefaultYear(ref DateTimeResult result, ref DateTimeStyles styles) + { + result.Year = result.calendar.GetYear(GetDateTimeNow(ref result, ref styles)); + result.flags |= ParseFlags.YearDefault; + } + + // Processing teriminal case: DS.DX_NN + private static Boolean GetDayOfNN(ref DateTimeResult result, ref DateTimeStyles styles, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) + { + if ((result.flags & ParseFlags.HaveDate) != 0) + { + // Multiple dates in the input string + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + + int n1 = raw.GetNumber(0); + int n2 = raw.GetNumber(1); + + GetDefaultYear(ref result, ref styles); + + int order; + if (!GetMonthDayOrder(dtfi.MonthDayPattern, dtfi, out order)) + { + result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.MonthDayPattern); + return false; + } + + if (order == ORDER_MD) + { + if (SetDateYMD(ref result, result.Year, n1, n2)) // MD + { + result.flags |= ParseFlags.HaveDate; + return true; + } + } + else + { + // ORDER_DM + if (SetDateYMD(ref result, result.Year, n2, n1)) // DM + { + result.flags |= ParseFlags.HaveDate; + return true; + } + } + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + + // Processing teriminal case: DS.DX_NNN + private static Boolean GetDayOfNNN(ref DateTimeResult result, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) + { + if ((result.flags & ParseFlags.HaveDate) != 0) + { + // Multiple dates in the input string + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + + int n1 = raw.GetNumber(0); + int n2 = raw.GetNumber(1); ; + int n3 = raw.GetNumber(2); + + int order; + if (!GetYearMonthDayOrder(dtfi.ShortDatePattern, dtfi, out order)) + { + result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.ShortDatePattern); + return false; + } + int year; + + if (order == ORDER_YMD) + { + if (TryAdjustYear(ref result, n1, out year) && SetDateYMD(ref result, year, n2, n3)) // YMD + { + result.flags |= ParseFlags.HaveDate; + return true; + } + } + else if (order == ORDER_MDY) + { + if (TryAdjustYear(ref result, n3, out year) && SetDateMDY(ref result, n1, n2, year)) // MDY + { + result.flags |= ParseFlags.HaveDate; + return true; + } + } + else if (order == ORDER_DMY) + { + if (TryAdjustYear(ref result, n3, out year) && SetDateDMY(ref result, n1, n2, year)) // DMY + { + result.flags |= ParseFlags.HaveDate; + return true; + } + } + else if (order == ORDER_YDM) + { + if (TryAdjustYear(ref result, n1, out year) && SetDateYDM(ref result, year, n2, n3)) // YDM + { + result.flags |= ParseFlags.HaveDate; + return true; + } + } + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + + private static Boolean GetDayOfMN(ref DateTimeResult result, ref DateTimeStyles styles, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) + { + if ((result.flags & ParseFlags.HaveDate) != 0) + { + // Multiple dates in the input string + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + + // The interpretation is based on the MonthDayPattern and YearMonthPattern + // + // MonthDayPattern YearMonthPattern Interpretation + // --------------- ---------------- --------------- + // MMMM dd MMMM yyyy Day + // MMMM dd yyyy MMMM Day + // dd MMMM MMMM yyyy Year + // dd MMMM yyyy MMMM Day + // + // In the first and last cases, it could be either or neither, but a day is a better default interpretation + // than a 2 digit year. + + int monthDayOrder; + if (!GetMonthDayOrder(dtfi.MonthDayPattern, dtfi, out monthDayOrder)) + { + result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.MonthDayPattern); + return false; + } + if (monthDayOrder == ORDER_DM) + { + int yearMonthOrder; + if (!GetYearMonthOrder(dtfi.YearMonthPattern, dtfi, out yearMonthOrder)) + { + result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.YearMonthPattern); + return false; + } + if (yearMonthOrder == ORDER_MY) + { + int year; + if (!TryAdjustYear(ref result, raw.GetNumber(0), out year) || !SetDateYMD(ref result, year, raw.month, 1)) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + return true; + } + } + + GetDefaultYear(ref result, ref styles); + if (!SetDateYMD(ref result, result.Year, raw.month, raw.GetNumber(0))) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + return true; + } + + //////////////////////////////////////////////////////////////////////// + // Actions: + // Deal with the terminal state for Hebrew Month/Day pattern + // + //////////////////////////////////////////////////////////////////////// + + private static Boolean GetHebrewDayOfNM(ref DateTimeResult result, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) + { + int monthDayOrder; + if (!GetMonthDayOrder(dtfi.MonthDayPattern, dtfi, out monthDayOrder)) + { + result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.MonthDayPattern); + return false; + } + result.Month = raw.month; + if (monthDayOrder == ORDER_DM || monthDayOrder == ORDER_MD) + { + if (result.calendar.IsValidDay(result.Year, result.Month, raw.GetNumber(0), result.era)) + { + result.Day = raw.GetNumber(0); + return true; + } + } + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + + private static Boolean GetDayOfNM(ref DateTimeResult result, ref DateTimeStyles styles, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) + { + if ((result.flags & ParseFlags.HaveDate) != 0) + { + // Multiple dates in the input string + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + + // The interpretation is based on the MonthDayPattern and YearMonthPattern + // + // MonthDayPattern YearMonthPattern Interpretation + // --------------- ---------------- --------------- + // MMMM dd MMMM yyyy Day + // MMMM dd yyyy MMMM Year + // dd MMMM MMMM yyyy Day + // dd MMMM yyyy MMMM Day + // + // In the first and last cases, it could be either or neither, but a day is a better default interpretation + // than a 2 digit year. + + int monthDayOrder; + if (!GetMonthDayOrder(dtfi.MonthDayPattern, dtfi, out monthDayOrder)) + { + result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.MonthDayPattern); + return false; + } + if (monthDayOrder == ORDER_MD) + { + int yearMonthOrder; + if (!GetYearMonthOrder(dtfi.YearMonthPattern, dtfi, out yearMonthOrder)) + { + result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.YearMonthPattern); + return false; + } + if (yearMonthOrder == ORDER_YM) + { + int year; + if (!TryAdjustYear(ref result, raw.GetNumber(0), out year) || !SetDateYMD(ref result, year, raw.month, 1)) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + return true; + } + } + + GetDefaultYear(ref result, ref styles); + if (!SetDateYMD(ref result, result.Year, raw.month, raw.GetNumber(0))) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + return true; + } + + private static Boolean GetDayOfMNN(ref DateTimeResult result, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) + { + if ((result.flags & ParseFlags.HaveDate) != 0) + { + // Multiple dates in the input string + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + + int n1 = raw.GetNumber(0); + int n2 = raw.GetNumber(1); + + int order; + if (!GetYearMonthDayOrder(dtfi.ShortDatePattern, dtfi, out order)) + { + result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.ShortDatePattern); + return false; + } + int year; + + if (order == ORDER_MDY) + { + if (TryAdjustYear(ref result, n2, out year) && result.calendar.IsValidDay(year, raw.month, n1, result.era)) + { + result.SetDate(year, raw.month, n1); // MDY + result.flags |= ParseFlags.HaveDate; + return true; + } + else if (TryAdjustYear(ref result, n1, out year) && result.calendar.IsValidDay(year, raw.month, n2, result.era)) + { + result.SetDate(year, raw.month, n2); // YMD + result.flags |= ParseFlags.HaveDate; + return true; + } + } + else if (order == ORDER_YMD) + { + if (TryAdjustYear(ref result, n1, out year) && result.calendar.IsValidDay(year, raw.month, n2, result.era)) + { + result.SetDate(year, raw.month, n2); // YMD + result.flags |= ParseFlags.HaveDate; + return true; + } + else if (TryAdjustYear(ref result, n2, out year) && result.calendar.IsValidDay(year, raw.month, n1, result.era)) + { + result.SetDate(year, raw.month, n1); // DMY + result.flags |= ParseFlags.HaveDate; + return true; + } + } + else if (order == ORDER_DMY) + { + if (TryAdjustYear(ref result, n2, out year) && result.calendar.IsValidDay(year, raw.month, n1, result.era)) + { + result.SetDate(year, raw.month, n1); // DMY + result.flags |= ParseFlags.HaveDate; + return true; + } + else if (TryAdjustYear(ref result, n1, out year) && result.calendar.IsValidDay(year, raw.month, n2, result.era)) + { + result.SetDate(year, raw.month, n2); // YMD + result.flags |= ParseFlags.HaveDate; + return true; + } + } + + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + + private static Boolean GetDayOfYNN(ref DateTimeResult result, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) + { + if ((result.flags & ParseFlags.HaveDate) != 0) + { + // Multiple dates in the input string + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + + int n1 = raw.GetNumber(0); + int n2 = raw.GetNumber(1); + String pattern = dtfi.ShortDatePattern; + + // For compatibility, don't throw if we can't determine the order, but default to YMD instead + int order; + if (GetYearMonthDayOrder(pattern, dtfi, out order) && order == ORDER_YDM) + { + if (SetDateYMD(ref result, raw.year, n2, n1)) + { + result.flags |= ParseFlags.HaveDate; + return true; // Year + DM + } + } + else + { + if (SetDateYMD(ref result, raw.year, n1, n2)) + { + result.flags |= ParseFlags.HaveDate; + return true; // Year + MD + } + } + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + + private static Boolean GetDayOfNNY(ref DateTimeResult result, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) + { + if ((result.flags & ParseFlags.HaveDate) != 0) + { + // Multiple dates in the input string + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + + int n1 = raw.GetNumber(0); + int n2 = raw.GetNumber(1); + + int order; + if (!GetYearMonthDayOrder(dtfi.ShortDatePattern, dtfi, out order)) + { + result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.ShortDatePattern); + return false; + } + + if (order == ORDER_MDY || order == ORDER_YMD) + { + if (SetDateYMD(ref result, raw.year, n1, n2)) + { + result.flags |= ParseFlags.HaveDate; + return true; // MD + Year + } + } + else + { + if (SetDateYMD(ref result, raw.year, n2, n1)) + { + result.flags |= ParseFlags.HaveDate; + return true; // DM + Year + } + } + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + + + private static Boolean GetDayOfYMN(ref DateTimeResult result, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) + { + if ((result.flags & ParseFlags.HaveDate) != 0) + { + // Multiple dates in the input string + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + + if (SetDateYMD(ref result, raw.year, raw.month, raw.GetNumber(0))) + { + result.flags |= ParseFlags.HaveDate; + return true; + } + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + + private static Boolean GetDayOfYN(ref DateTimeResult result, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) + { + if ((result.flags & ParseFlags.HaveDate) != 0) + { + // Multiple dates in the input string + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + + if (SetDateYMD(ref result, raw.year, raw.GetNumber(0), 1)) + { + result.flags |= ParseFlags.HaveDate; + return true; + } + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + + private static Boolean GetDayOfYM(ref DateTimeResult result, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) + { + if ((result.flags & ParseFlags.HaveDate) != 0) + { + // Multiple dates in the input string + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + + if (SetDateYMD(ref result, raw.year, raw.month, 1)) + { + result.flags |= ParseFlags.HaveDate; + return true; + } + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + + private static void AdjustTimeMark(DateTimeFormatInfo dtfi, ref DateTimeRawInfo raw) + { + // Specail case for culture which uses AM as empty string. + // E.g. af-ZA (0x0436) + // S1159 \x0000 + // S2359 nm + // In this case, if we are parsing a string like "2005/09/14 12:23", we will assume this is in AM. + + if (raw.timeMark == TM.NotSet) + { + if (dtfi.AMDesignator != null && dtfi.PMDesignator != null) + { + if (dtfi.AMDesignator.Length == 0 && dtfi.PMDesignator.Length != 0) + { + raw.timeMark = TM.AM; + } + if (dtfi.PMDesignator.Length == 0 && dtfi.AMDesignator.Length != 0) + { + raw.timeMark = TM.PM; + } + } + } + } + + // + // Adjust hour according to the time mark. + // + private static Boolean AdjustHour(ref int hour, TM timeMark) + { + if (timeMark != TM.NotSet) + { + if (timeMark == TM.AM) + { + if (hour < 0 || hour > 12) + { + return false; + } + hour = (hour == 12) ? 0 : hour; + } + else + { + if (hour < 0 || hour > 23) + { + return false; + } + if (hour < 12) + { + hour += 12; + } + } + } + return true; + } + + private static Boolean GetTimeOfN(DateTimeFormatInfo dtfi, ref DateTimeResult result, ref DateTimeRawInfo raw) + { + if ((result.flags & ParseFlags.HaveTime) != 0) + { + // Multiple times in the input string + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + // + // In this case, we need a time mark. Check if so. + // + if (raw.timeMark == TM.NotSet) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + result.Hour = raw.GetNumber(0); + result.flags |= ParseFlags.HaveTime; + return true; + } + + private static Boolean GetTimeOfNN(DateTimeFormatInfo dtfi, ref DateTimeResult result, ref DateTimeRawInfo raw) + { + Debug.Assert(raw.numCount >= 2, "raw.numCount >= 2"); + if ((result.flags & ParseFlags.HaveTime) != 0) + { + // Multiple times in the input string + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + + result.Hour = raw.GetNumber(0); + result.Minute = raw.GetNumber(1); + result.flags |= ParseFlags.HaveTime; + return true; + } + + private static Boolean GetTimeOfNNN(DateTimeFormatInfo dtfi, ref DateTimeResult result, ref DateTimeRawInfo raw) + { + if ((result.flags & ParseFlags.HaveTime) != 0) + { + // Multiple times in the input string + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + Debug.Assert(raw.numCount >= 3, "raw.numCount >= 3"); + result.Hour = raw.GetNumber(0); + result.Minute = raw.GetNumber(1); + result.Second = raw.GetNumber(2); + result.flags |= ParseFlags.HaveTime; + return true; + } + + // + // Processing terminal state: A Date suffix followed by one number. + // + private static Boolean GetDateOfDSN(ref DateTimeResult result, ref DateTimeRawInfo raw) + { + if (raw.numCount != 1 || result.Day != -1) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + result.Day = raw.GetNumber(0); + return true; + } + + private static Boolean GetDateOfNDS(ref DateTimeResult result, ref DateTimeRawInfo raw) + { + if (result.Month == -1) + { + //Should have a month suffix + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + if (result.Year != -1) + { + // Aleady has a year suffix + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + if (!TryAdjustYear(ref result, raw.GetNumber(0), out result.Year)) + { + // the year value is out of range + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + result.Day = 1; + return true; + } + + private static Boolean GetDateOfNNDS(ref DateTimeResult result, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) + { + // For partial CJK Dates, the only valid formats are with a specified year, followed by two numbers, which + // will be the Month and Day, and with a specified Month, when the numbers are either the year and day or + // day and year, depending on the short date pattern. + + if ((result.flags & ParseFlags.HaveYear) != 0) + { + if (((result.flags & ParseFlags.HaveMonth) == 0) && ((result.flags & ParseFlags.HaveDay) == 0)) + { + if (TryAdjustYear(ref result, raw.year, out result.Year) && SetDateYMD(ref result, result.Year, raw.GetNumber(0), raw.GetNumber(1))) + { + return true; + } + } + } + else if ((result.flags & ParseFlags.HaveMonth) != 0) + { + if (((result.flags & ParseFlags.HaveYear) == 0) && ((result.flags & ParseFlags.HaveDay) == 0)) + { + int order; + if (!GetYearMonthDayOrder(dtfi.ShortDatePattern, dtfi, out order)) + { + result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.ShortDatePattern); + return false; + } + int year; + if (order == ORDER_YMD) + { + if (TryAdjustYear(ref result, raw.GetNumber(0), out year) && SetDateYMD(ref result, year, result.Month, raw.GetNumber(1))) + { + return true; + } + } + else + { + if (TryAdjustYear(ref result, raw.GetNumber(1), out year) && SetDateYMD(ref result, year, result.Month, raw.GetNumber(0))) + { + return true; + } + } + } + } + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + + // + // A date suffix is found, use this method to put the number into the result. + // + private static bool ProcessDateTimeSuffix(ref DateTimeResult result, ref DateTimeRawInfo raw, ref DateTimeToken dtok) + { + switch (dtok.suffix) + { + case TokenType.SEP_YearSuff: + if ((result.flags & ParseFlags.HaveYear) != 0) + { + return false; + } + result.flags |= ParseFlags.HaveYear; + result.Year = raw.year = dtok.num; + break; + case TokenType.SEP_MonthSuff: + if ((result.flags & ParseFlags.HaveMonth) != 0) + { + return false; + } + result.flags |= ParseFlags.HaveMonth; + result.Month = raw.month = dtok.num; + break; + case TokenType.SEP_DaySuff: + if ((result.flags & ParseFlags.HaveDay) != 0) + { + return false; + } + result.flags |= ParseFlags.HaveDay; + result.Day = dtok.num; + break; + case TokenType.SEP_HourSuff: + if ((result.flags & ParseFlags.HaveHour) != 0) + { + return false; + } + result.flags |= ParseFlags.HaveHour; + result.Hour = dtok.num; + break; + case TokenType.SEP_MinuteSuff: + if ((result.flags & ParseFlags.HaveMinute) != 0) + { + return false; + } + result.flags |= ParseFlags.HaveMinute; + result.Minute = dtok.num; + break; + case TokenType.SEP_SecondSuff: + if ((result.flags & ParseFlags.HaveSecond) != 0) + { + return false; + } + result.flags |= ParseFlags.HaveSecond; + result.Second = dtok.num; + break; + } + return true; + } + + //////////////////////////////////////////////////////////////////////// + // + // Actions: + // This is used by DateTime.Parse(). + // Process the terminal state for the Hebrew calendar parsing. + // + //////////////////////////////////////////////////////////////////////// + + internal static Boolean ProcessHebrewTerminalState(DS dps, ref DateTimeResult result, ref DateTimeStyles styles, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) + { + // The following are accepted terminal state for Hebrew date. + switch (dps) + { + case DS.DX_MNN: + // Deal with the default long/short date format when the year number is ambigous (i.e. year < 100). + raw.year = raw.GetNumber(1); + if (!dtfi.YearMonthAdjustment(ref raw.year, ref raw.month, true)) + { + result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null); + return false; + } + if (!GetDayOfMNN(ref result, ref raw, dtfi)) + { + return false; + } + break; + case DS.DX_YMN: + // Deal with the default long/short date format when the year number is NOT ambigous (i.e. year >= 100). + if (!dtfi.YearMonthAdjustment(ref raw.year, ref raw.month, true)) + { + result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null); + return false; + } + if (!GetDayOfYMN(ref result, ref raw, dtfi)) + { + return false; + } + break; + case DS.DX_NM: + case DS.DX_MN: + // Deal with Month/Day pattern. + GetDefaultYear(ref result, ref styles); + if (!dtfi.YearMonthAdjustment(ref result.Year, ref raw.month, true)) + { + result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null); + return false; + } + if (!GetHebrewDayOfNM(ref result, ref raw, dtfi)) + { + return false; + } + break; + case DS.DX_YM: + // Deal with Year/Month pattern. + if (!dtfi.YearMonthAdjustment(ref raw.year, ref raw.month, true)) + { + result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null); + return false; + } + if (!GetDayOfYM(ref result, ref raw, dtfi)) + { + return false; + } + break; + case DS.TX_N: + // Deal hour + AM/PM + if (!GetTimeOfN(dtfi, ref result, ref raw)) + { + return false; + } + break; + case DS.TX_NN: + if (!GetTimeOfNN(dtfi, ref result, ref raw)) + { + return false; + } + break; + case DS.TX_NNN: + if (!GetTimeOfNNN(dtfi, ref result, ref raw)) + { + return false; + } + break; + default: + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + if (dps > DS.ERROR) + { + // + // We have reached a terminal state. Reset the raw num count. + // + raw.numCount = 0; + } + return true; + } + + // + // A terminal state has been reached, call the appropriate function to fill in the parsing result. + // Return true if the state is a terminal state. + // + internal static Boolean ProcessTerminaltState(DS dps, ref DateTimeResult result, ref DateTimeStyles styles, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi) + { + bool passed = true; + switch (dps) + { + case DS.DX_NN: + passed = GetDayOfNN(ref result, ref styles, ref raw, dtfi); + break; + case DS.DX_NNN: + passed = GetDayOfNNN(ref result, ref raw, dtfi); + break; + case DS.DX_MN: + passed = GetDayOfMN(ref result, ref styles, ref raw, dtfi); + break; + case DS.DX_NM: + passed = GetDayOfNM(ref result, ref styles, ref raw, dtfi); + break; + case DS.DX_MNN: + passed = GetDayOfMNN(ref result, ref raw, dtfi); + break; + case DS.DX_DS: + // The result has got the correct value. No need to process. + passed = true; + break; + case DS.DX_YNN: + passed = GetDayOfYNN(ref result, ref raw, dtfi); + break; + case DS.DX_NNY: + passed = GetDayOfNNY(ref result, ref raw, dtfi); + break; + case DS.DX_YMN: + passed = GetDayOfYMN(ref result, ref raw, dtfi); + break; + case DS.DX_YN: + passed = GetDayOfYN(ref result, ref raw, dtfi); + break; + case DS.DX_YM: + passed = GetDayOfYM(ref result, ref raw, dtfi); + break; + case DS.TX_N: + passed = GetTimeOfN(dtfi, ref result, ref raw); + break; + case DS.TX_NN: + passed = GetTimeOfNN(dtfi, ref result, ref raw); + break; + case DS.TX_NNN: + passed = GetTimeOfNNN(dtfi, ref result, ref raw); + break; + case DS.TX_TS: + // The result has got the correct value. Nothing to do. + passed = true; + break; + case DS.DX_DSN: + passed = GetDateOfDSN(ref result, ref raw); + break; + case DS.DX_NDS: + passed = GetDateOfNDS(ref result, ref raw); + break; + case DS.DX_NNDS: + passed = GetDateOfNNDS(ref result, ref raw, dtfi); + break; + } + + PTSTraceExit(dps, passed); + if (!passed) + { + return false; + } + + if (dps > DS.ERROR) + { + // + // We have reached a terminal state. Reset the raw num count. + // + raw.numCount = 0; + } + return true; + } + + internal static DateTime Parse(String s, DateTimeFormatInfo dtfi, DateTimeStyles styles) + { + DateTimeResult result = new DateTimeResult(); // The buffer to store the parsing result. + result.Init(); + if (TryParse(s, dtfi, styles, ref result)) + { + return result.parsedDate; + } + else + { + throw GetDateTimeParseException(ref result); + } + } + + internal static DateTime Parse(String s, DateTimeFormatInfo dtfi, DateTimeStyles styles, out TimeSpan offset) + { + DateTimeResult result = new DateTimeResult(); // The buffer to store the parsing result. + result.Init(); + result.flags |= ParseFlags.CaptureOffset; + if (TryParse(s, dtfi, styles, ref result)) + { + offset = result.timeZoneOffset; + return result.parsedDate; + } + else + { + throw GetDateTimeParseException(ref result); + } + } + + + internal static bool TryParse(String s, DateTimeFormatInfo dtfi, DateTimeStyles styles, out DateTime result) + { + result = DateTime.MinValue; + DateTimeResult resultData = new DateTimeResult(); // The buffer to store the parsing result. + resultData.Init(); + if (TryParse(s, dtfi, styles, ref resultData)) + { + result = resultData.parsedDate; + return true; + } + return false; + } + + internal static bool TryParse(String s, DateTimeFormatInfo dtfi, DateTimeStyles styles, out DateTime result, out TimeSpan offset) + { + result = DateTime.MinValue; + offset = TimeSpan.Zero; + DateTimeResult parseResult = new DateTimeResult(); // The buffer to store the parsing result. + parseResult.Init(); + parseResult.flags |= ParseFlags.CaptureOffset; + if (TryParse(s, dtfi, styles, ref parseResult)) + { + result = parseResult.parsedDate; + offset = parseResult.timeZoneOffset; + return true; + } + return false; + } + + + // + // This is the real method to do the parsing work. + // + internal static bool TryParse(String s, DateTimeFormatInfo dtfi, DateTimeStyles styles, ref DateTimeResult result) + { + if (s == null) + { + result.SetFailure(ParseFailureKind.ArgumentNull, "ArgumentNull_String", null, nameof(s)); + return false; + } + if (s.Length == 0) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + + Debug.Assert(dtfi != null, "dtfi == null"); + +#if _LOGGING + DTFITrace(dtfi); +#endif + + DateTime time; + // + // First try the predefined format. + // + + DS dps = DS.BEGIN; // Date Parsing State. + bool reachTerminalState = false; + + DateTimeToken dtok = new DateTimeToken(); // The buffer to store the parsing token. + dtok.suffix = TokenType.SEP_Unk; + DateTimeRawInfo raw = new DateTimeRawInfo(); // The buffer to store temporary parsing information. + unsafe + { + Int32* numberPointer = stackalloc Int32[3]; + raw.Init(numberPointer); + } + raw.hasSameDateAndTimeSeparators = dtfi.DateSeparator.Equals(dtfi.TimeSeparator, StringComparison.Ordinal); + + result.calendar = dtfi.Calendar; + result.era = Calendar.CurrentEra; + + // + // The string to be parsed. Use a __DTString wrapper so that we can trace the index which + // indicates the begining of next token. + // + __DTString str = new __DTString(s, dtfi); + + str.GetNext(); + + // + // The following loop will break out when we reach the end of the str. + // + do + { + // + // Call the lexer to get the next token. + // + // If we find a era in Lex(), the era value will be in raw.era. + if (!Lex(dps, ref str, ref dtok, ref raw, ref result, ref dtfi, styles)) + { + TPTraceExit("0000", dps); + return false; + } + + // + // If the token is not unknown, process it. + // Otherwise, just discard it. + // + if (dtok.dtt != DTT.Unk) + { + // + // Check if we got any CJK Date/Time suffix. + // Since the Date/Time suffix tells us the number belongs to year/month/day/hour/minute/second, + // store the number in the appropriate field in the result. + // + if (dtok.suffix != TokenType.SEP_Unk) + { + if (!ProcessDateTimeSuffix(ref result, ref raw, ref dtok)) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + TPTraceExit("0010", dps); + return false; + } + + dtok.suffix = TokenType.SEP_Unk; // Reset suffix to SEP_Unk; + } + + if (dtok.dtt == DTT.NumLocalTimeMark) + { + if (dps == DS.D_YNd || dps == DS.D_YN) + { + // Consider this as ISO 8601 format: + // "yyyy-MM-dd'T'HH:mm:ss" 1999-10-31T02:00:00 + TPTraceExit("0020", dps); + return (ParseISO8601(ref raw, ref str, styles, ref result)); + } + else + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + TPTraceExit("0030", dps); + return false; + } + } + + if (raw.hasSameDateAndTimeSeparators) + { + if (dtok.dtt == DTT.YearEnd || dtok.dtt == DTT.YearSpace || dtok.dtt == DTT.YearDateSep) + { + // When time and date separators are same and we are hitting a year number while the first parsed part of the string was recognized + // as part of time (and not a date) DS.T_Nt, DS.T_NNt then change the state to be a date so we try to parse it as a date instead + if (dps == DS.T_Nt) + { + dps = DS.D_Nd; + } + if (dps == DS.T_NNt) + { + dps = DS.D_NNd; + } + } + + bool atEnd = str.AtEnd(); + if (dateParsingStates[(int)dps][(int)dtok.dtt] == DS.ERROR || atEnd) + { + switch (dtok.dtt) + { + // we have the case of Serbia have dates in forms 'd.M.yyyy.' so we can expect '.' after the date parts. + // changing the token to end with space instead of Date Separator will avoid failing the parsing. + + case DTT.YearDateSep: dtok.dtt = atEnd ? DTT.YearEnd : DTT.YearSpace; break; + case DTT.NumDatesep: dtok.dtt = atEnd ? DTT.NumEnd : DTT.NumSpace; break; + case DTT.NumTimesep: dtok.dtt = atEnd ? DTT.NumEnd : DTT.NumSpace; break; + case DTT.MonthDatesep: dtok.dtt = atEnd ? DTT.MonthEnd : DTT.MonthSpace; break; + } + } + } + + // + // Advance to the next state, and continue + // + dps = dateParsingStates[(int)dps][(int)dtok.dtt]; + + if (dps == DS.ERROR) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + TPTraceExit("0040 (invalid state transition)", dps); + return false; + } + else if (dps > DS.ERROR) + { + if ((dtfi.FormatFlags & DateTimeFormatFlags.UseHebrewRule) != 0) + { + if (!ProcessHebrewTerminalState(dps, ref result, ref styles, ref raw, dtfi)) + { + TPTraceExit("0050 (ProcessHebrewTerminalState)", dps); + return false; + } + } + else + { + if (!ProcessTerminaltState(dps, ref result, ref styles, ref raw, dtfi)) + { + TPTraceExit("0060 (ProcessTerminaltState)", dps); + return false; + } + } + reachTerminalState = true; + + // + // If we have reached a terminal state, start over from DS.BEGIN again. + // For example, when we parsed "1999-12-23 13:30", we will reach a terminal state at "1999-12-23", + // and we start over so we can continue to parse "12:30". + // + dps = DS.BEGIN; + } + } + } while (dtok.dtt != DTT.End && dtok.dtt != DTT.NumEnd && dtok.dtt != DTT.MonthEnd); + + if (!reachTerminalState) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + TPTraceExit("0070 (did not reach terminal state)", dps); + return false; + } + + AdjustTimeMark(dtfi, ref raw); + if (!AdjustHour(ref result.Hour, raw.timeMark)) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + TPTraceExit("0080 (AdjustHour)", dps); + return false; + } + + // Check if the parased string only contains hour/minute/second values. + bool bTimeOnly = (result.Year == -1 && result.Month == -1 && result.Day == -1); + + // + // Check if any year/month/day is missing in the parsing string. + // If yes, get the default value from today's date. + // + if (!CheckDefaultDateTime(ref result, ref result.calendar, styles)) + { + TPTraceExit("0090 (failed to fill in missing year/month/day defaults)", dps); + return false; + } + + if (!result.calendar.TryToDateTime(result.Year, result.Month, result.Day, + result.Hour, result.Minute, result.Second, 0, result.era, out time)) + { + result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null); + TPTraceExit("0100 (result.calendar.TryToDateTime)", dps); + return false; + } + if (raw.fraction > 0) + { + time = time.AddTicks((long)Math.Round(raw.fraction * Calendar.TicksPerSecond)); + } + + // + // We have to check day of week before we adjust to the time zone. + // Otherwise, the value of day of week may change after adjustting to the time zone. + // + if (raw.dayOfWeek != -1) + { + // + // Check if day of week is correct. + // + if (raw.dayOfWeek != (int)result.calendar.GetDayOfWeek(time)) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDayOfWeek", null); + TPTraceExit("0110 (dayOfWeek check)", dps); + return false; + } + } + + result.parsedDate = time; + + if (!DetermineTimeZoneAdjustments(ref result, styles, bTimeOnly)) + { + TPTraceExit("0120 (DetermineTimeZoneAdjustments)", dps); + return false; + } + TPTraceExit("0130 (success)", dps); + return true; + } + + + // Handles time zone adjustments and sets DateTimeKind values as required by the styles + private static Boolean DetermineTimeZoneAdjustments(ref DateTimeResult result, DateTimeStyles styles, Boolean bTimeOnly) + { + if ((result.flags & ParseFlags.CaptureOffset) != 0) + { + // This is a DateTimeOffset parse, so the offset will actually be captured directly, and + // no adjustment is required in most cases + return DateTimeOffsetTimeZonePostProcessing(ref result, styles); + } + else + { + Int64 offsetTicks = result.timeZoneOffset.Ticks; + + // the DateTime offset must be within +- 14:00 hours. + if (offsetTicks < DateTimeOffset.MinOffset || offsetTicks > DateTimeOffset.MaxOffset) + { + result.SetFailure(ParseFailureKind.Format, "Format_OffsetOutOfRange", null); + return false; + } + } + + // The flags AssumeUniveral and AssumeLocal only apply when the input does not have a time zone + if ((result.flags & ParseFlags.TimeZoneUsed) == 0) + { + // If AssumeLocal or AssumeLocal is used, there will always be a kind specified. As in the + // case when a time zone is present, it will default to being local unless AdjustToUniversal + // is present. These comparisons determine whether setting the kind is sufficient, or if a + // time zone adjustment is required. For consistentcy with the rest of parsing, it is desirable + // to fall through to the Adjust methods below, so that there is consist handling of boundary + // cases like wrapping around on time-only dates and temporarily allowing an adjusted date + // to exceed DateTime.MaxValue + if ((styles & DateTimeStyles.AssumeLocal) != 0) + { + if ((styles & DateTimeStyles.AdjustToUniversal) != 0) + { + result.flags |= ParseFlags.TimeZoneUsed; + result.timeZoneOffset = TimeZoneInfo.GetLocalUtcOffset(result.parsedDate, TimeZoneInfoOptions.NoThrowOnInvalidTime); + } + else + { + result.parsedDate = DateTime.SpecifyKind(result.parsedDate, DateTimeKind.Local); + return true; + } + } + else if ((styles & DateTimeStyles.AssumeUniversal) != 0) + { + if ((styles & DateTimeStyles.AdjustToUniversal) != 0) + { + result.parsedDate = DateTime.SpecifyKind(result.parsedDate, DateTimeKind.Utc); + return true; + } + else + { + result.flags |= ParseFlags.TimeZoneUsed; + result.timeZoneOffset = TimeSpan.Zero; + } + } + else + { + // No time zone and no Assume flags, so DateTimeKind.Unspecified is fine + Debug.Assert(result.parsedDate.Kind == DateTimeKind.Unspecified, "result.parsedDate.Kind == DateTimeKind.Unspecified"); + return true; + } + } + + if (((styles & DateTimeStyles.RoundtripKind) != 0) && ((result.flags & ParseFlags.TimeZoneUtc) != 0)) + { + result.parsedDate = DateTime.SpecifyKind(result.parsedDate, DateTimeKind.Utc); + return true; + } + + if ((styles & DateTimeStyles.AdjustToUniversal) != 0) + { + return (AdjustTimeZoneToUniversal(ref result)); + } + return (AdjustTimeZoneToLocal(ref result, bTimeOnly)); + } + + // Apply validation and adjustments specific to DateTimeOffset + private static Boolean DateTimeOffsetTimeZonePostProcessing(ref DateTimeResult result, DateTimeStyles styles) + { + // For DateTimeOffset, default to the Utc or Local offset when an offset was not specified by + // the input string. + if ((result.flags & ParseFlags.TimeZoneUsed) == 0) + { + if ((styles & DateTimeStyles.AssumeUniversal) != 0) + { + // AssumeUniversal causes the offset to default to zero (0) + result.timeZoneOffset = TimeSpan.Zero; + } + else + { + // AssumeLocal causes the offset to default to Local. This flag is on by default for DateTimeOffset. + result.timeZoneOffset = TimeZoneInfo.GetLocalUtcOffset(result.parsedDate, TimeZoneInfoOptions.NoThrowOnInvalidTime); + } + } + + Int64 offsetTicks = result.timeZoneOffset.Ticks; + + // there should be no overflow, because the offset can be no more than -+100 hours and the date already + // fits within a DateTime. + Int64 utcTicks = result.parsedDate.Ticks - offsetTicks; + + // For DateTimeOffset, both the parsed time and the corresponding UTC value must be within the boundaries + // of a DateTime instance. + if (utcTicks < DateTime.MinTicks || utcTicks > DateTime.MaxTicks) + { + result.SetFailure(ParseFailureKind.Format, "Format_UTCOutOfRange", null); + return false; + } + + // the offset must be within +- 14:00 hours. + if (offsetTicks < DateTimeOffset.MinOffset || offsetTicks > DateTimeOffset.MaxOffset) + { + result.SetFailure(ParseFailureKind.Format, "Format_OffsetOutOfRange", null); + return false; + } + + // DateTimeOffset should still honor the AdjustToUniversal flag for consistency with DateTime. It means you + // want to return an adjusted UTC value, so store the utcTicks in the DateTime and set the offset to zero + if ((styles & DateTimeStyles.AdjustToUniversal) != 0) + { + if (((result.flags & ParseFlags.TimeZoneUsed) == 0) && ((styles & DateTimeStyles.AssumeUniversal) == 0)) + { + // Handle the special case where the timeZoneOffset was defaulted to Local + Boolean toUtcResult = AdjustTimeZoneToUniversal(ref result); + result.timeZoneOffset = TimeSpan.Zero; + return toUtcResult; + } + + // The constructor should always succeed because of the range check earlier in the function + // Althought it is UTC, internally DateTimeOffset does not use this flag + result.parsedDate = new DateTime(utcTicks, DateTimeKind.Utc); + result.timeZoneOffset = TimeSpan.Zero; + } + + return true; + } + + + // + // Adjust the specified time to universal time based on the supplied timezone. + // E.g. when parsing "2001/06/08 14:00-07:00", + // the time is 2001/06/08 14:00, and timeZoneOffset = -07:00. + // The result will be "2001/06/08 21:00" + // + private static Boolean AdjustTimeZoneToUniversal(ref DateTimeResult result) + { + long resultTicks = result.parsedDate.Ticks; + resultTicks -= result.timeZoneOffset.Ticks; + if (resultTicks < 0) + { + resultTicks += Calendar.TicksPerDay; + } + + if (resultTicks < DateTime.MinTicks || resultTicks > DateTime.MaxTicks) + { + result.SetFailure(ParseFailureKind.Format, "Format_DateOutOfRange", null); + return false; + } + result.parsedDate = new DateTime(resultTicks, DateTimeKind.Utc); + return true; + } + + // + // Adjust the specified time to universal time based on the supplied timezone, + // and then convert to local time. + // E.g. when parsing "2001/06/08 14:00-04:00", and local timezone is GMT-7. + // the time is 2001/06/08 14:00, and timeZoneOffset = -05:00. + // The result will be "2001/06/08 11:00" + // + private static Boolean AdjustTimeZoneToLocal(ref DateTimeResult result, bool bTimeOnly) + { + long resultTicks = result.parsedDate.Ticks; + // Convert to local ticks + TimeZoneInfo tz = TimeZoneInfo.Local; + Boolean isAmbiguousLocalDst = false; + if (resultTicks < Calendar.TicksPerDay) + { + // + // This is time of day. + // + + // Adjust timezone. + resultTicks -= result.timeZoneOffset.Ticks; + // If the time is time of day, use the current timezone offset. + resultTicks += tz.GetUtcOffset(bTimeOnly ? DateTime.Now : result.parsedDate, TimeZoneInfoOptions.NoThrowOnInvalidTime).Ticks; + + if (resultTicks < 0) + { + resultTicks += Calendar.TicksPerDay; + } + } + else + { + // Adjust timezone to GMT. + resultTicks -= result.timeZoneOffset.Ticks; + if (resultTicks < DateTime.MinTicks || resultTicks > DateTime.MaxTicks) + { + // If the result ticks is greater than DateTime.MaxValue, we can not create a DateTime from this ticks. + // In this case, keep using the old code. + resultTicks += tz.GetUtcOffset(result.parsedDate, TimeZoneInfoOptions.NoThrowOnInvalidTime).Ticks; + } + else + { + // Convert the GMT time to local time. + DateTime utcDt = new DateTime(resultTicks, DateTimeKind.Utc); + Boolean isDaylightSavings = false; + resultTicks += TimeZoneInfo.GetUtcOffsetFromUtc(utcDt, TimeZoneInfo.Local, out isDaylightSavings, out isAmbiguousLocalDst).Ticks; + } + } + if (resultTicks < DateTime.MinTicks || resultTicks > DateTime.MaxTicks) + { + result.parsedDate = DateTime.MinValue; + result.SetFailure(ParseFailureKind.Format, "Format_DateOutOfRange", null); + return false; + } + result.parsedDate = new DateTime(resultTicks, DateTimeKind.Local, isAmbiguousLocalDst); + return true; + } + + // + // Parse the ISO8601 format string found during Parse(); + // + // + private static bool ParseISO8601(ref DateTimeRawInfo raw, ref __DTString str, DateTimeStyles styles, ref DateTimeResult result) + { + if (raw.year < 0 || raw.GetNumber(0) < 0 || raw.GetNumber(1) < 0) + { + } + str.Index--; + int hour, minute; + int second = 0; + double partSecond = 0; + + str.SkipWhiteSpaces(); + if (!ParseDigits(ref str, 2, out hour)) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + str.SkipWhiteSpaces(); + if (!str.Match(':')) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + str.SkipWhiteSpaces(); + if (!ParseDigits(ref str, 2, out minute)) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + str.SkipWhiteSpaces(); + if (str.Match(':')) + { + str.SkipWhiteSpaces(); + if (!ParseDigits(ref str, 2, out second)) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + if (str.Match('.')) + { + if (!ParseFraction(ref str, out partSecond)) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + str.Index--; + } + str.SkipWhiteSpaces(); + } + if (str.GetNext()) + { + char ch = str.GetChar(); + if (ch == '+' || ch == '-') + { + result.flags |= ParseFlags.TimeZoneUsed; + if (!ParseTimeZone(ref str, ref result.timeZoneOffset)) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + } + else if (ch == 'Z' || ch == 'z') + { + result.flags |= ParseFlags.TimeZoneUsed; + result.timeZoneOffset = TimeSpan.Zero; + result.flags |= ParseFlags.TimeZoneUtc; + } + else + { + str.Index--; + } + str.SkipWhiteSpaces(); + if (str.Match('#')) + { + if (!VerifyValidPunctuation(ref str)) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + str.SkipWhiteSpaces(); + } + if (str.Match('\0')) + { + if (!VerifyValidPunctuation(ref str)) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + } + if (str.GetNext()) + { + // If this is true, there were non-white space characters remaining in the DateTime + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + } + + DateTime time; + Calendar calendar = GregorianCalendar.GetDefaultInstance(); + if (!calendar.TryToDateTime(raw.year, raw.GetNumber(0), raw.GetNumber(1), + hour, minute, second, 0, result.era, out time)) + { + result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null); + return false; + } + + time = time.AddTicks((long)Math.Round(partSecond * Calendar.TicksPerSecond)); + result.parsedDate = time; + if (!DetermineTimeZoneAdjustments(ref result, styles, false)) + { + return false; + } + return true; + } + + + //////////////////////////////////////////////////////////////////////// + // + // Actions: + // Parse the current word as a Hebrew number. + // This is used by DateTime.ParseExact(). + // + //////////////////////////////////////////////////////////////////////// + + internal static bool MatchHebrewDigits(ref __DTString str, int digitLen, out int number) + { + number = 0; + + // Create a context object so that we can parse the Hebrew number text character by character. + HebrewNumberParsingContext context = new HebrewNumberParsingContext(0); + + // Set this to ContinueParsing so that we will run the following while loop in the first time. + HebrewNumberParsingState state = HebrewNumberParsingState.ContinueParsing; + + while (state == HebrewNumberParsingState.ContinueParsing && str.GetNext()) + { + state = HebrewNumber.ParseByChar(str.GetChar(), ref context); + } + + if (state == HebrewNumberParsingState.FoundEndOfHebrewNumber) + { + // If we have reached a terminal state, update the result and returns. + number = context.result; + return (true); + } + + // If we run out of the character before reaching FoundEndOfHebrewNumber, or + // the state is InvalidHebrewNumber or ContinueParsing, we fail to match a Hebrew number. + // Return an error. + return false; + } + + /*=================================ParseDigits================================== + **Action: Parse the number string in __DTString that are formatted using + ** the following patterns: + ** "0", "00", and "000..0" + **Returns: the integer value + **Arguments: str: a __DTString. The parsing will start from the + ** next character after str.Index. + **Exceptions: FormatException if error in parsing number. + ==============================================================================*/ + + internal static bool ParseDigits(ref __DTString str, int digitLen, out int result) + { + if (digitLen == 1) + { + // 1 really means 1 or 2 for this call + return ParseDigits(ref str, 1, 2, out result); + } + else + { + return ParseDigits(ref str, digitLen, digitLen, out result); + } + } + + internal static bool ParseDigits(ref __DTString str, int minDigitLen, int maxDigitLen, out int result) + { + Debug.Assert(minDigitLen > 0, "minDigitLen > 0"); + Debug.Assert(maxDigitLen < 9, "maxDigitLen < 9"); + Debug.Assert(minDigitLen <= maxDigitLen, "minDigitLen <= maxDigitLen"); + result = 0; + int startingIndex = str.Index; + int tokenLength = 0; + while (tokenLength < maxDigitLen) + { + if (!str.GetNextDigit()) + { + str.Index--; + break; + } + result = result * 10 + str.GetDigit(); + tokenLength++; + } + if (tokenLength < minDigitLen) + { + str.Index = startingIndex; + return false; + } + return true; + } + + /*=================================ParseFractionExact================================== + **Action: Parse the number string in __DTString that are formatted using + ** the following patterns: + ** "0", "00", and "000..0" + **Returns: the fraction value + **Arguments: str: a __DTString. The parsing will start from the + ** next character after str.Index. + **Exceptions: FormatException if error in parsing number. + ==============================================================================*/ + + private static bool ParseFractionExact(ref __DTString str, int maxDigitLen, ref double result) + { + if (!str.GetNextDigit()) + { + str.Index--; + return false; + } + result = str.GetDigit(); + + int digitLen = 1; + for (; digitLen < maxDigitLen; digitLen++) + { + if (!str.GetNextDigit()) + { + str.Index--; + break; + } + result = result * 10 + str.GetDigit(); + } + + result = ((double)result / Math.Pow(10, digitLen)); + return (digitLen == maxDigitLen); + } + + /*=================================ParseSign================================== + **Action: Parse a positive or a negative sign. + **Returns: true if postive sign. flase if negative sign. + **Arguments: str: a __DTString. The parsing will start from the + ** next character after str.Index. + **Exceptions: FormatException if end of string is encountered or a sign + ** symbol is not found. + ==============================================================================*/ + + private static bool ParseSign(ref __DTString str, ref bool result) + { + if (!str.GetNext()) + { + // A sign symbol ('+' or '-') is expected. However, end of string is encountered. + return false; + } + char ch = str.GetChar(); + if (ch == '+') + { + result = true; + return (true); + } + else if (ch == '-') + { + result = false; + return (true); + } + // A sign symbol ('+' or '-') is expected. + return false; + } + + /*=================================ParseTimeZoneOffset================================== + **Action: Parse the string formatted using "z", "zz", "zzz" in DateTime.Format(). + **Returns: the TimeSpan for the parsed timezone offset. + **Arguments: str: a __DTString. The parsing will start from the + ** next character after str.Index. + ** len: the repeated number of the "z" + **Exceptions: FormatException if errors in parsing. + ==============================================================================*/ + + private static bool ParseTimeZoneOffset(ref __DTString str, int len, ref TimeSpan result) + { + bool isPositive = true; + int hourOffset; + int minuteOffset = 0; + + switch (len) + { + case 1: + case 2: + if (!ParseSign(ref str, ref isPositive)) + { + return (false); + } + if (!ParseDigits(ref str, len, out hourOffset)) + { + return (false); + } + break; + default: + if (!ParseSign(ref str, ref isPositive)) + { + return (false); + } + + // Parsing 1 digit will actually parse 1 or 2. + if (!ParseDigits(ref str, 1, out hourOffset)) + { + return (false); + } + // ':' is optional. + if (str.Match(":")) + { + // Found ':' + if (!ParseDigits(ref str, 2, out minuteOffset)) + { + return (false); + } + } + else + { + // Since we can not match ':', put the char back. + str.Index--; + if (!ParseDigits(ref str, 2, out minuteOffset)) + { + return (false); + } + } + break; + } + if (minuteOffset < 0 || minuteOffset >= 60) + { + return false; + } + + result = (new TimeSpan(hourOffset, minuteOffset, 0)); + if (!isPositive) + { + result = result.Negate(); + } + return (true); + } + + /*=================================MatchAbbreviatedMonthName================================== + **Action: Parse the abbreviated month name from string starting at str.Index. + **Returns: A value from 1 to 12 for the first month to the twelveth month. + **Arguments: str: a __DTString. The parsing will start from the + ** next character after str.Index. + **Exceptions: FormatException if an abbreviated month name can not be found. + ==============================================================================*/ + + private static bool MatchAbbreviatedMonthName(ref __DTString str, DateTimeFormatInfo dtfi, ref int result) + { + int maxMatchStrLen = 0; + result = -1; + if (str.GetNext()) + { + // + // Scan the month names (note that some calendars has 13 months) and find + // the matching month name which has the max string length. + // We need to do this because some cultures (e.g. "cs-CZ") which have + // abbreviated month names with the same prefix. + // + int monthsInYear = (dtfi.GetMonthName(13).Length == 0 ? 12 : 13); + for (int i = 1; i <= monthsInYear; i++) + { + String searchStr = dtfi.GetAbbreviatedMonthName(i); + int matchStrLen = searchStr.Length; + if (dtfi.HasSpacesInMonthNames + ? str.MatchSpecifiedWords(searchStr, false, ref matchStrLen) + : str.MatchSpecifiedWord(searchStr)) + { + if (matchStrLen > maxMatchStrLen) + { + maxMatchStrLen = matchStrLen; + result = i; + } + } + } + + // Search leap year form. + if ((dtfi.FormatFlags & DateTimeFormatFlags.UseLeapYearMonth) != 0) + { + int tempResult = str.MatchLongestWords(dtfi.internalGetLeapYearMonthNames(), ref maxMatchStrLen); + // We found a longer match in the leap year month name. Use this as the result. + // The result from MatchLongestWords is 0 ~ length of word array. + // So we increment the result by one to become the month value. + if (tempResult >= 0) + { + result = tempResult + 1; + } + } + } + if (result > 0) + { + str.Index += (maxMatchStrLen - 1); + return (true); + } + return false; + } + + /*=================================MatchMonthName================================== + **Action: Parse the month name from string starting at str.Index. + **Returns: A value from 1 to 12 indicating the first month to the twelveth month. + **Arguments: str: a __DTString. The parsing will start from the + ** next character after str.Index. + **Exceptions: FormatException if a month name can not be found. + ==============================================================================*/ + + private static bool MatchMonthName(ref __DTString str, DateTimeFormatInfo dtfi, ref int result) + { + int maxMatchStrLen = 0; + result = -1; + if (str.GetNext()) + { + // + // Scan the month names (note that some calendars has 13 months) and find + // the matching month name which has the max string length. + // We need to do this because some cultures (e.g. "vi-VN") which have + // month names with the same prefix. + // + int monthsInYear = (dtfi.GetMonthName(13).Length == 0 ? 12 : 13); + for (int i = 1; i <= monthsInYear; i++) + { + String searchStr = dtfi.GetMonthName(i); + int matchStrLen = searchStr.Length; + if (dtfi.HasSpacesInMonthNames + ? str.MatchSpecifiedWords(searchStr, false, ref matchStrLen) + : str.MatchSpecifiedWord(searchStr)) + { + if (matchStrLen > maxMatchStrLen) + { + maxMatchStrLen = matchStrLen; + result = i; + } + } + } + + // Search genitive form. + if ((dtfi.FormatFlags & DateTimeFormatFlags.UseGenitiveMonth) != 0) + { + int tempResult = str.MatchLongestWords(dtfi.MonthGenitiveNames, ref maxMatchStrLen); + // We found a longer match in the genitive month name. Use this as the result. + // The result from MatchLongestWords is 0 ~ length of word array. + // So we increment the result by one to become the month value. + if (tempResult >= 0) + { + result = tempResult + 1; + } + } + + // Search leap year form. + if ((dtfi.FormatFlags & DateTimeFormatFlags.UseLeapYearMonth) != 0) + { + int tempResult = str.MatchLongestWords(dtfi.internalGetLeapYearMonthNames(), ref maxMatchStrLen); + // We found a longer match in the leap year month name. Use this as the result. + // The result from MatchLongestWords is 0 ~ length of word array. + // So we increment the result by one to become the month value. + if (tempResult >= 0) + { + result = tempResult + 1; + } + } + } + + if (result > 0) + { + str.Index += (maxMatchStrLen - 1); + return (true); + } + return false; + } + + /*=================================MatchAbbreviatedDayName================================== + **Action: Parse the abbreviated day of week name from string starting at str.Index. + **Returns: A value from 0 to 6 indicating Sunday to Saturday. + **Arguments: str: a __DTString. The parsing will start from the + ** next character after str.Index. + **Exceptions: FormatException if a abbreviated day of week name can not be found. + ==============================================================================*/ + + private static bool MatchAbbreviatedDayName(ref __DTString str, DateTimeFormatInfo dtfi, ref int result) + { + int maxMatchStrLen = 0; + result = -1; + if (str.GetNext()) + { + for (DayOfWeek i = DayOfWeek.Sunday; i <= DayOfWeek.Saturday; i++) + { + String searchStr = dtfi.GetAbbreviatedDayName(i); + int matchStrLen = searchStr.Length; + if (dtfi.HasSpacesInDayNames + ? str.MatchSpecifiedWords(searchStr, false, ref matchStrLen) + : str.MatchSpecifiedWord(searchStr)) + { + if (matchStrLen > maxMatchStrLen) + { + maxMatchStrLen = matchStrLen; + result = (int)i; + } + } + } + } + if (result >= 0) + { + str.Index += maxMatchStrLen - 1; + return (true); + } + return false; + } + + /*=================================MatchDayName================================== + **Action: Parse the day of week name from string starting at str.Index. + **Returns: A value from 0 to 6 indicating Sunday to Saturday. + **Arguments: str: a __DTString. The parsing will start from the + ** next character after str.Index. + **Exceptions: FormatException if a day of week name can not be found. + ==============================================================================*/ + + private static bool MatchDayName(ref __DTString str, DateTimeFormatInfo dtfi, ref int result) + { + // Turkish (tr-TR) got day names with the same prefix. + int maxMatchStrLen = 0; + result = -1; + if (str.GetNext()) + { + for (DayOfWeek i = DayOfWeek.Sunday; i <= DayOfWeek.Saturday; i++) + { + String searchStr = dtfi.GetDayName(i); + int matchStrLen = searchStr.Length; + if (dtfi.HasSpacesInDayNames + ? str.MatchSpecifiedWords(searchStr, false, ref matchStrLen) + : str.MatchSpecifiedWord(searchStr)) + { + if (matchStrLen > maxMatchStrLen) + { + maxMatchStrLen = matchStrLen; + result = (int)i; + } + } + } + } + if (result >= 0) + { + str.Index += maxMatchStrLen - 1; + return (true); + } + return false; + } + + /*=================================MatchEraName================================== + **Action: Parse era name from string starting at str.Index. + **Returns: An era value. + **Arguments: str: a __DTString. The parsing will start from the + ** next character after str.Index. + **Exceptions: FormatException if an era name can not be found. + ==============================================================================*/ + + private static bool MatchEraName(ref __DTString str, DateTimeFormatInfo dtfi, ref int result) + { + if (str.GetNext()) + { + int[] eras = dtfi.Calendar.Eras; + + if (eras != null) + { + for (int i = 0; i < eras.Length; i++) + { + String searchStr = dtfi.GetEraName(eras[i]); + if (str.MatchSpecifiedWord(searchStr)) + { + str.Index += (searchStr.Length - 1); + result = eras[i]; + return (true); + } + searchStr = dtfi.GetAbbreviatedEraName(eras[i]); + if (str.MatchSpecifiedWord(searchStr)) + { + str.Index += (searchStr.Length - 1); + result = eras[i]; + return (true); + } + } + } + } + return false; + } + + /*=================================MatchTimeMark================================== + **Action: Parse the time mark (AM/PM) from string starting at str.Index. + **Returns: TM_AM or TM_PM. + **Arguments: str: a __DTString. The parsing will start from the + ** next character after str.Index. + **Exceptions: FormatException if a time mark can not be found. + ==============================================================================*/ + + private static bool MatchTimeMark(ref __DTString str, DateTimeFormatInfo dtfi, ref TM result) + { + result = TM.NotSet; + // In some cultures have empty strings in AM/PM mark. E.g. af-ZA (0x0436), the AM mark is "", and PM mark is "nm". + if (dtfi.AMDesignator.Length == 0) + { + result = TM.AM; + } + if (dtfi.PMDesignator.Length == 0) + { + result = TM.PM; + } + + if (str.GetNext()) + { + String searchStr = dtfi.AMDesignator; + if (searchStr.Length > 0) + { + if (str.MatchSpecifiedWord(searchStr)) + { + // Found an AM timemark with length > 0. + str.Index += (searchStr.Length - 1); + result = TM.AM; + return (true); + } + } + searchStr = dtfi.PMDesignator; + if (searchStr.Length > 0) + { + if (str.MatchSpecifiedWord(searchStr)) + { + // Found a PM timemark with length > 0. + str.Index += (searchStr.Length - 1); + result = TM.PM; + return (true); + } + } + str.Index--; // Undo the GetNext call. + } + if (result != TM.NotSet) + { + // If one of the AM/PM marks is empty string, return the result. + return (true); + } + return false; + } + + /*=================================MatchAbbreviatedTimeMark================================== + **Action: Parse the abbreviated time mark (AM/PM) from string starting at str.Index. + **Returns: TM_AM or TM_PM. + **Arguments: str: a __DTString. The parsing will start from the + ** next character after str.Index. + **Exceptions: FormatException if a abbreviated time mark can not be found. + ==============================================================================*/ + + private static bool MatchAbbreviatedTimeMark(ref __DTString str, DateTimeFormatInfo dtfi, ref TM result) + { + // NOTENOTE : the assumption here is that abbreviated time mark is the first + // character of the AM/PM designator. If this invariant changes, we have to + // change the code below. + if (str.GetNext()) + { + if (str.GetChar() == dtfi.AMDesignator[0]) + { + result = TM.AM; + return (true); + } + if (str.GetChar() == dtfi.PMDesignator[0]) + { + result = TM.PM; + return (true); + } + } + return false; + } + + /*=================================CheckNewValue================================== + **Action: Check if currentValue is initialized. If not, return the newValue. + ** If yes, check if the current value is equal to newValue. Return false + ** if they are not equal. This is used to check the case like "d" and "dd" are both + ** used to format a string. + **Returns: the correct value for currentValue. + **Arguments: + **Exceptions: + ==============================================================================*/ + + private static bool CheckNewValue(ref int currentValue, int newValue, char patternChar, ref DateTimeResult result) + { + if (currentValue == -1) + { + currentValue = newValue; + return (true); + } + else + { + if (newValue != currentValue) + { + result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_RepeatDateTimePattern", patternChar); + return (false); + } + } + return (true); + } + + private static DateTime GetDateTimeNow(ref DateTimeResult result, ref DateTimeStyles styles) + { + if ((result.flags & ParseFlags.CaptureOffset) != 0) + { + if ((result.flags & ParseFlags.TimeZoneUsed) != 0) + { + // use the supplied offset to calculate 'Now' + return new DateTime(DateTime.UtcNow.Ticks + result.timeZoneOffset.Ticks, DateTimeKind.Unspecified); + } + else if ((styles & DateTimeStyles.AssumeUniversal) != 0) + { + // assume the offset is Utc + return DateTime.UtcNow; + } + } + + // assume the offset is Local + return DateTime.Now; + } + + private static bool CheckDefaultDateTime(ref DateTimeResult result, ref Calendar cal, DateTimeStyles styles) + { + if ((result.flags & ParseFlags.CaptureOffset) != 0) + { + // DateTimeOffset.Parse should allow dates without a year, but only if there is also no time zone marker; + // e.g. "May 1 5pm" is OK, but "May 1 5pm -08:30" is not. This is somewhat pragmatic, since we would + // have to rearchitect parsing completely to allow this one case to correctly handle things like leap + // years and leap months. Is is an extremely corner case, and DateTime is basically incorrect in that + // case today. + // + // values like "11:00Z" or "11:00 -3:00" are also acceptable + // + // if ((month or day is set) and (year is not set and time zone is set)) + // + if (((result.Month != -1) || (result.Day != -1)) + && ((result.Year == -1 || ((result.flags & ParseFlags.YearDefault) != 0)) && (result.flags & ParseFlags.TimeZoneUsed) != 0)) + { + result.SetFailure(ParseFailureKind.Format, "Format_MissingIncompleteDate", null); + return false; + } + } + + + if ((result.Year == -1) || (result.Month == -1) || (result.Day == -1)) + { + /* + The following table describes the behaviors of getting the default value + when a certain year/month/day values are missing. + + An "X" means that the value exists. And "--" means that value is missing. + + Year Month Day => ResultYear ResultMonth ResultDay Note + + X X X Parsed year Parsed month Parsed day + X X -- Parsed Year Parsed month First day If we have year and month, assume the first day of that month. + X -- X Parsed year First month Parsed day If the month is missing, assume first month of that year. + X -- -- Parsed year First month First day If we have only the year, assume the first day of that year. + + -- X X CurrentYear Parsed month Parsed day If the year is missing, assume the current year. + -- X -- CurrentYear Parsed month First day If we have only a month value, assume the current year and current day. + -- -- X CurrentYear First month Parsed day If we have only a day value, assume current year and first month. + -- -- -- CurrentYear Current month Current day So this means that if the date string only contains time, you will get current date. + + */ + + DateTime now = GetDateTimeNow(ref result, ref styles); + if (result.Month == -1 && result.Day == -1) + { + if (result.Year == -1) + { + if ((styles & DateTimeStyles.NoCurrentDateDefault) != 0) + { + // If there is no year/month/day values, and NoCurrentDateDefault flag is used, + // set the year/month/day value to the beginning year/month/day of DateTime(). + // Note we should be using Gregorian for the year/month/day. + cal = GregorianCalendar.GetDefaultInstance(); + result.Year = result.Month = result.Day = 1; + } + else + { + // Year/Month/Day are all missing. + result.Year = cal.GetYear(now); + result.Month = cal.GetMonth(now); + result.Day = cal.GetDayOfMonth(now); + } + } + else + { + // Month/Day are both missing. + result.Month = 1; + result.Day = 1; + } + } + else + { + if (result.Year == -1) + { + result.Year = cal.GetYear(now); + } + if (result.Month == -1) + { + result.Month = 1; + } + if (result.Day == -1) + { + result.Day = 1; + } + } + } + // Set Hour/Minute/Second to zero if these value are not in str. + if (result.Hour == -1) result.Hour = 0; + if (result.Minute == -1) result.Minute = 0; + if (result.Second == -1) result.Second = 0; + if (result.era == -1) result.era = Calendar.CurrentEra; + return true; + } + + // Expand a pre-defined format string (like "D" for long date) to the real format that + // we are going to use in the date time parsing. + // This method also set the dtfi according/parseInfo to some special pre-defined + // formats. + // + private static String ExpandPredefinedFormat(String format, ref DateTimeFormatInfo dtfi, ref ParsingInfo parseInfo, ref DateTimeResult result) + { + // + // Check the format to see if we need to override the dtfi to be InvariantInfo, + // and see if we need to set up the userUniversalTime flag. + // + switch (format[0]) + { + case 'o': + case 'O': // Round Trip Format + parseInfo.calendar = GregorianCalendar.GetDefaultInstance(); + dtfi = DateTimeFormatInfo.InvariantInfo; + break; + case 'r': + case 'R': // RFC 1123 Standard. (in Universal time) + parseInfo.calendar = GregorianCalendar.GetDefaultInstance(); + dtfi = DateTimeFormatInfo.InvariantInfo; + + if ((result.flags & ParseFlags.CaptureOffset) != 0) + { + result.flags |= ParseFlags.Rfc1123Pattern; + } + break; + case 's': // Sortable format (in local time) + dtfi = DateTimeFormatInfo.InvariantInfo; + parseInfo.calendar = GregorianCalendar.GetDefaultInstance(); + break; + case 'u': // Universal time format in sortable format. + parseInfo.calendar = GregorianCalendar.GetDefaultInstance(); + dtfi = DateTimeFormatInfo.InvariantInfo; + + if ((result.flags & ParseFlags.CaptureOffset) != 0) + { + result.flags |= ParseFlags.UtcSortPattern; + } + break; + case 'U': // Universal time format with culture-dependent format. + parseInfo.calendar = GregorianCalendar.GetDefaultInstance(); + result.flags |= ParseFlags.TimeZoneUsed; + result.timeZoneOffset = new TimeSpan(0); + result.flags |= ParseFlags.TimeZoneUtc; + if (dtfi.Calendar.GetType() != typeof(GregorianCalendar)) + { + dtfi = (DateTimeFormatInfo)dtfi.Clone(); + dtfi.Calendar = GregorianCalendar.GetDefaultInstance(); + } + break; + } + + // + // Expand the pre-defined format character to the real format from DateTimeFormatInfo. + // + return (DateTimeFormat.GetRealFormat(format, dtfi)); + } + + + + + + // Given a specified format character, parse and update the parsing result. + // + private static bool ParseByFormat( + ref __DTString str, + ref __DTString format, + ref ParsingInfo parseInfo, + DateTimeFormatInfo dtfi, + ref DateTimeResult result) + { + int tokenLen = 0; + int tempYear = 0, tempMonth = 0, tempDay = 0, tempDayOfWeek = 0, tempHour = 0, tempMinute = 0, tempSecond = 0; + double tempFraction = 0; + TM tempTimeMark = 0; + + char ch = format.GetChar(); + + switch (ch) + { + case 'y': + tokenLen = format.GetRepeatCount(); + bool parseResult; + if (dtfi.HasForceTwoDigitYears) + { + parseResult = ParseDigits(ref str, 1, 4, out tempYear); + } + else + { + if (tokenLen <= 2) + { + parseInfo.fUseTwoDigitYear = true; + } + parseResult = ParseDigits(ref str, tokenLen, out tempYear); + } + if (!parseResult && parseInfo.fCustomNumberParser) + { + parseResult = parseInfo.parseNumberDelegate(ref str, tokenLen, out tempYear); + } + if (!parseResult) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return (false); + } + if (!CheckNewValue(ref result.Year, tempYear, ch, ref result)) + { + return (false); + } + break; + case 'M': + tokenLen = format.GetRepeatCount(); + if (tokenLen <= 2) + { + if (!ParseDigits(ref str, tokenLen, out tempMonth)) + { + if (!parseInfo.fCustomNumberParser || + !parseInfo.parseNumberDelegate(ref str, tokenLen, out tempMonth)) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return (false); + } + } + } + else + { + if (tokenLen == 3) + { + if (!MatchAbbreviatedMonthName(ref str, dtfi, ref tempMonth)) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return (false); + } + } + else + { + if (!MatchMonthName(ref str, dtfi, ref tempMonth)) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return (false); + } + } + result.flags |= ParseFlags.ParsedMonthName; + } + if (!CheckNewValue(ref result.Month, tempMonth, ch, ref result)) + { + return (false); + } + break; + case 'd': + // Day & Day of week + tokenLen = format.GetRepeatCount(); + if (tokenLen <= 2) + { + // "d" & "dd" + + if (!ParseDigits(ref str, tokenLen, out tempDay)) + { + if (!parseInfo.fCustomNumberParser || + !parseInfo.parseNumberDelegate(ref str, tokenLen, out tempDay)) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return (false); + } + } + if (!CheckNewValue(ref result.Day, tempDay, ch, ref result)) + { + return (false); + } + } + else + { + if (tokenLen == 3) + { + // "ddd" + if (!MatchAbbreviatedDayName(ref str, dtfi, ref tempDayOfWeek)) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return (false); + } + } + else + { + // "dddd*" + if (!MatchDayName(ref str, dtfi, ref tempDayOfWeek)) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return (false); + } + } + if (!CheckNewValue(ref parseInfo.dayOfWeek, tempDayOfWeek, ch, ref result)) + { + return (false); + } + } + break; + case 'g': + tokenLen = format.GetRepeatCount(); + // Put the era value in result.era. + if (!MatchEraName(ref str, dtfi, ref result.era)) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return (false); + } + break; + case 'h': + parseInfo.fUseHour12 = true; + tokenLen = format.GetRepeatCount(); + if (!ParseDigits(ref str, (tokenLen < 2 ? 1 : 2), out tempHour)) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return (false); + } + if (!CheckNewValue(ref result.Hour, tempHour, ch, ref result)) + { + return (false); + } + break; + case 'H': + tokenLen = format.GetRepeatCount(); + if (!ParseDigits(ref str, (tokenLen < 2 ? 1 : 2), out tempHour)) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return (false); + } + if (!CheckNewValue(ref result.Hour, tempHour, ch, ref result)) + { + return (false); + } + break; + case 'm': + tokenLen = format.GetRepeatCount(); + if (!ParseDigits(ref str, (tokenLen < 2 ? 1 : 2), out tempMinute)) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return (false); + } + if (!CheckNewValue(ref result.Minute, tempMinute, ch, ref result)) + { + return (false); + } + break; + case 's': + tokenLen = format.GetRepeatCount(); + if (!ParseDigits(ref str, (tokenLen < 2 ? 1 : 2), out tempSecond)) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return (false); + } + if (!CheckNewValue(ref result.Second, tempSecond, ch, ref result)) + { + return (false); + } + break; + case 'f': + case 'F': + tokenLen = format.GetRepeatCount(); + if (tokenLen <= DateTimeFormat.MaxSecondsFractionDigits) + { + if (!ParseFractionExact(ref str, tokenLen, ref tempFraction)) + { + if (ch == 'f') + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return (false); + } + } + if (result.fraction < 0) + { + result.fraction = tempFraction; + } + else + { + if (tempFraction != result.fraction) + { + result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_RepeatDateTimePattern", ch); + return (false); + } + } + } + else + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return (false); + } + break; + case 't': + // AM/PM designator + tokenLen = format.GetRepeatCount(); + if (tokenLen == 1) + { + if (!MatchAbbreviatedTimeMark(ref str, dtfi, ref tempTimeMark)) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return (false); + } + } + else + { + if (!MatchTimeMark(ref str, dtfi, ref tempTimeMark)) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return (false); + } + } + + if (parseInfo.timeMark == TM.NotSet) + { + parseInfo.timeMark = tempTimeMark; + } + else + { + if (parseInfo.timeMark != tempTimeMark) + { + result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_RepeatDateTimePattern", ch); + return (false); + } + } + break; + case 'z': + // timezone offset + tokenLen = format.GetRepeatCount(); + { + TimeSpan tempTimeZoneOffset = new TimeSpan(0); + if (!ParseTimeZoneOffset(ref str, tokenLen, ref tempTimeZoneOffset)) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return (false); + } + if ((result.flags & ParseFlags.TimeZoneUsed) != 0 && tempTimeZoneOffset != result.timeZoneOffset) + { + result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_RepeatDateTimePattern", 'z'); + return (false); + } + result.timeZoneOffset = tempTimeZoneOffset; + result.flags |= ParseFlags.TimeZoneUsed; + } + break; + case 'Z': + if ((result.flags & ParseFlags.TimeZoneUsed) != 0 && result.timeZoneOffset != TimeSpan.Zero) + { + result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_RepeatDateTimePattern", 'Z'); + return (false); + } + + result.flags |= ParseFlags.TimeZoneUsed; + result.timeZoneOffset = new TimeSpan(0); + result.flags |= ParseFlags.TimeZoneUtc; + + // The updating of the indexes is to reflect that ParseExact MatchXXX methods assume that + // they need to increment the index and Parse GetXXX do not. Since we are calling a Parse + // method from inside ParseExact we need to adjust this. Long term, we should try to + // eliminate this discrepancy. + str.Index++; + if (!GetTimeZoneName(ref str)) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + str.Index--; + break; + case 'K': + // This should parse either as a blank, the 'Z' character or a local offset like "-07:00" + if (str.Match('Z')) + { + if ((result.flags & ParseFlags.TimeZoneUsed) != 0 && result.timeZoneOffset != TimeSpan.Zero) + { + result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_RepeatDateTimePattern", 'K'); + return (false); + } + + result.flags |= ParseFlags.TimeZoneUsed; + result.timeZoneOffset = new TimeSpan(0); + result.flags |= ParseFlags.TimeZoneUtc; + } + else if (str.Match('+') || str.Match('-')) + { + str.Index--; // Put the character back for the parser + TimeSpan tempTimeZoneOffset = new TimeSpan(0); + if (!ParseTimeZoneOffset(ref str, 3, ref tempTimeZoneOffset)) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return (false); + } + if ((result.flags & ParseFlags.TimeZoneUsed) != 0 && tempTimeZoneOffset != result.timeZoneOffset) + { + result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_RepeatDateTimePattern", 'K'); + return (false); + } + result.timeZoneOffset = tempTimeZoneOffset; + result.flags |= ParseFlags.TimeZoneUsed; + } + // Otherwise it is unspecified and we consume no characters + break; + case ':': + // We match the separator in time pattern with the character in the time string if both equal to ':' or the date separator is matching the characters in the date string + // We have to exclude the case when the time separator is more than one character and starts with ':' something like "::" for instance. + if (((dtfi.TimeSeparator.Length > 1 && dtfi.TimeSeparator[0] == ':') || !str.Match(':')) && + !str.Match(dtfi.TimeSeparator)) + { + // A time separator is expected. + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + break; + case '/': + // We match the separator in date pattern with the character in the date string if both equal to '/' or the date separator is matching the characters in the date string + // We have to exclude the case when the date separator is more than one character and starts with '/' something like "//" for instance. + if (((dtfi.DateSeparator.Length > 1 && dtfi.DateSeparator[0] == '/') || !str.Match('/')) && + !str.Match(dtfi.DateSeparator)) + { + // A date separator is expected. + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + break; + case '\"': + case '\'': + StringBuilder enquotedString = new StringBuilder(); + // Use ParseQuoteString so that we can handle escape characters within the quoted string. + if (!TryParseQuoteString(format.Value, format.Index, enquotedString, out tokenLen)) + { + result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadQuote", ch); + return (false); + } + format.Index += tokenLen - 1; + + // Some cultures uses space in the quoted string. E.g. Spanish has long date format as: + // "dddd, dd' de 'MMMM' de 'yyyy". When inner spaces flag is set, we should skip whitespaces if there is space + // in the quoted string. + String quotedStr = enquotedString.ToString(); + + for (int i = 0; i < quotedStr.Length; i++) + { + if (quotedStr[i] == ' ' && parseInfo.fAllowInnerWhite) + { + str.SkipWhiteSpaces(); + } + else if (!str.Match(quotedStr[i])) + { + // Can not find the matching quoted string. + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + } + + // The "r" and "u" formats incorrectly quoted 'GMT' and 'Z', respectively. We cannot + // correct this mistake for DateTime.ParseExact for compatibility reasons, but we can + // fix it for DateTimeOffset.ParseExact as DateTimeOffset has not been publically released + // with this issue. + if ((result.flags & ParseFlags.CaptureOffset) != 0) + { + if ((result.flags & ParseFlags.Rfc1123Pattern) != 0 && quotedStr == GMTName) + { + result.flags |= ParseFlags.TimeZoneUsed; + result.timeZoneOffset = TimeSpan.Zero; + } + else if ((result.flags & ParseFlags.UtcSortPattern) != 0 && quotedStr == ZuluName) + { + result.flags |= ParseFlags.TimeZoneUsed; + result.timeZoneOffset = TimeSpan.Zero; + } + } + + break; + case '%': + // Skip this so we can get to the next pattern character. + // Used in case like "%d", "%y" + + // Make sure the next character is not a '%' again. + if (format.Index >= format.Value.Length - 1 || format.Value[format.Index + 1] == '%') + { + result.SetFailure(ParseFailureKind.Format, "Format_BadFormatSpecifier", null); + return false; + } + break; + case '\\': + // Escape character. For example, "\d". + // Get the next character in format, and see if we can + // find a match in str. + if (format.GetNext()) + { + if (!str.Match(format.GetChar())) + { + // Can not find a match for the escaped character. + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + } + else + { + result.SetFailure(ParseFailureKind.Format, "Format_BadFormatSpecifier", null); + return false; + } + break; + case '.': + if (!str.Match(ch)) + { + if (format.GetNext()) + { + // If we encounter the pattern ".F", and the dot is not present, it is an optional + // second fraction and we can skip this format. + if (format.Match('F')) + { + format.GetRepeatCount(); + break; + } + } + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + break; + default: + if (ch == ' ') + { + if (parseInfo.fAllowInnerWhite) + { + // Skip whitespaces if AllowInnerWhite. + // Do nothing here. + } + else + { + if (!str.Match(ch)) + { + // If the space does not match, and trailing space is allowed, we do + // one more step to see if the next format character can lead to + // successful parsing. + // This is used to deal with special case that a empty string can match + // a specific pattern. + // The example here is af-ZA, which has a time format like "hh:mm:ss tt". However, + // its AM symbol is "" (empty string). If fAllowTrailingWhite is used, and time is in + // the AM, we will trim the whitespaces at the end, which will lead to a failure + // when we are trying to match the space before "tt". + if (parseInfo.fAllowTrailingWhite) + { + if (format.GetNext()) + { + if (ParseByFormat(ref str, ref format, ref parseInfo, dtfi, ref result)) + { + return (true); + } + } + } + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + // Found a macth. + } + } + else + { + if (format.MatchSpecifiedWord(GMTName)) + { + format.Index += (GMTName.Length - 1); + // Found GMT string in format. This means the DateTime string + // is in GMT timezone. + result.flags |= ParseFlags.TimeZoneUsed; + result.timeZoneOffset = TimeSpan.Zero; + if (!str.Match(GMTName)) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + } + else if (!str.Match(ch)) + { + // ch is expected. + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + } + break; + } // switch + return (true); + } + + // + // The pos should point to a quote character. This method will + // get the string enclosed by the quote character. + // + internal static bool TryParseQuoteString(String format, int pos, StringBuilder result, out int returnValue) + { + // + // NOTE : pos will be the index of the quote character in the 'format' string. + // + returnValue = 0; + int formatLen = format.Length; + int beginPos = pos; + char quoteChar = format[pos++]; // Get the character used to quote the following string. + + bool foundQuote = false; + while (pos < formatLen) + { + char ch = format[pos++]; + if (ch == quoteChar) + { + foundQuote = true; + break; + } + else if (ch == '\\') + { + // The following are used to support escaped character. + // Escaped character is also supported in the quoted string. + // Therefore, someone can use a format like "'minute:' mm\"" to display: + // minute: 45" + // because the second double quote is escaped. + if (pos < formatLen) + { + result.Append(format[pos++]); + } + else + { + // + // This means that '\' is at the end of the formatting string. + // + return false; + } + } + else + { + result.Append(ch); + } + } + + if (!foundQuote) + { + // Here we can't find the matching quote. + return false; + } + + // + // Return the character count including the begin/end quote characters and enclosed string. + // + returnValue = (pos - beginPos); + return true; + } + + + + + /*=================================DoStrictParse================================== + **Action: Do DateTime parsing using the format in formatParam. + **Returns: The parsed DateTime. + **Arguments: + **Exceptions: + ** + **Notes: + ** When the following general formats are used, InvariantInfo is used in dtfi: + ** 'r', 'R', 's'. + ** When the following general formats are used, the time is assumed to be in Universal time. + ** + **Limitations: + ** Only GregarianCalendar is supported for now. + ** Only support GMT timezone. + ==============================================================================*/ + + private static bool DoStrictParse( + String s, + String formatParam, + DateTimeStyles styles, + DateTimeFormatInfo dtfi, + ref DateTimeResult result) + { + ParsingInfo parseInfo = new ParsingInfo(); + parseInfo.Init(); + + parseInfo.calendar = dtfi.Calendar; + parseInfo.fAllowInnerWhite = ((styles & DateTimeStyles.AllowInnerWhite) != 0); + parseInfo.fAllowTrailingWhite = ((styles & DateTimeStyles.AllowTrailingWhite) != 0); + + // We need the original values of the following two below. + String originalFormat = formatParam; + + if (formatParam.Length == 1) + { + if (((result.flags & ParseFlags.CaptureOffset) != 0) && formatParam[0] == 'U') + { + // The 'U' format is not allowed for DateTimeOffset + result.SetFailure(ParseFailureKind.Format, "Format_BadFormatSpecifier", null); + return false; + } + formatParam = ExpandPredefinedFormat(formatParam, ref dtfi, ref parseInfo, ref result); + } + + bool bTimeOnly = false; + result.calendar = parseInfo.calendar; + + if (parseInfo.calendar.ID == CalendarId.HEBREW) + { + parseInfo.parseNumberDelegate = m_hebrewNumberParser; + parseInfo.fCustomNumberParser = true; + } + + // Reset these values to negative one so that we could throw exception + // if we have parsed every item twice. + result.Hour = result.Minute = result.Second = -1; + + __DTString format = new __DTString(formatParam, dtfi, false); + __DTString str = new __DTString(s, dtfi, false); + + if (parseInfo.fAllowTrailingWhite) + { + // Trim trailing spaces if AllowTrailingWhite. + format.TrimTail(); + format.RemoveTrailingInQuoteSpaces(); + str.TrimTail(); + } + + if ((styles & DateTimeStyles.AllowLeadingWhite) != 0) + { + format.SkipWhiteSpaces(); + format.RemoveLeadingInQuoteSpaces(); + str.SkipWhiteSpaces(); + } + + // + // Scan every character in format and match the pattern in str. + // + while (format.GetNext()) + { + // We trim inner spaces here, so that we will not eat trailing spaces when + // AllowTrailingWhite is not used. + if (parseInfo.fAllowInnerWhite) + { + str.SkipWhiteSpaces(); + } + if (!ParseByFormat(ref str, ref format, ref parseInfo, dtfi, ref result)) + { + return (false); + } + } + + if (str.Index < str.Value.Length - 1) + { + // There are still remaining character in str. + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + + if (parseInfo.fUseTwoDigitYear && ((dtfi.FormatFlags & DateTimeFormatFlags.UseHebrewRule) == 0)) + { + // A two digit year value is expected. Check if the parsed year value is valid. + if (result.Year >= 100) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + try + { + result.Year = parseInfo.calendar.ToFourDigitYear(result.Year); + } + catch (ArgumentOutOfRangeException e) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", e); + return false; + } + } + + if (parseInfo.fUseHour12) + { + if (parseInfo.timeMark == TM.NotSet) + { + // hh is used, but no AM/PM designator is specified. + // Assume the time is AM. + // Don't throw exceptions in here becasue it is very confusing for the caller. + // I always got confused myself when I use "hh:mm:ss" to parse a time string, + // and ParseExact() throws on me (because I didn't use the 24-hour clock 'HH'). + parseInfo.timeMark = TM.AM; + } + if (result.Hour > 12) + { + // AM/PM is used, but the value for HH is too big. + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + if (parseInfo.timeMark == TM.AM) + { + if (result.Hour == 12) + { + result.Hour = 0; + } + } + else + { + result.Hour = (result.Hour == 12) ? 12 : result.Hour + 12; + } + } + else + { + // Military (24-hour time) mode + // + // AM cannot be set with a 24-hour time like 17:15. + // PM cannot be set with a 24-hour time like 03:15. + if ((parseInfo.timeMark == TM.AM && result.Hour >= 12) + || (parseInfo.timeMark == TM.PM && result.Hour < 12)) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null); + return false; + } + } + + + // Check if the parased string only contains hour/minute/second values. + bTimeOnly = (result.Year == -1 && result.Month == -1 && result.Day == -1); + if (!CheckDefaultDateTime(ref result, ref parseInfo.calendar, styles)) + { + return false; + } + + if (!bTimeOnly && dtfi.HasYearMonthAdjustment) + { + if (!dtfi.YearMonthAdjustment(ref result.Year, ref result.Month, ((result.flags & ParseFlags.ParsedMonthName) != 0))) + { + result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null); + return false; + } + } + if (!parseInfo.calendar.TryToDateTime(result.Year, result.Month, result.Day, + result.Hour, result.Minute, result.Second, 0, result.era, out result.parsedDate)) + { + result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null); + return false; + } + if (result.fraction > 0) + { + result.parsedDate = result.parsedDate.AddTicks((long)Math.Round(result.fraction * Calendar.TicksPerSecond)); + } + + // + // We have to check day of week before we adjust to the time zone. + // It is because the value of day of week may change after adjusting + // to the time zone. + // + if (parseInfo.dayOfWeek != -1) + { + // + // Check if day of week is correct. + // + if (parseInfo.dayOfWeek != (int)parseInfo.calendar.GetDayOfWeek(result.parsedDate)) + { + result.SetFailure(ParseFailureKind.Format, "Format_BadDayOfWeek", null); + return false; + } + } + + + if (!DetermineTimeZoneAdjustments(ref result, styles, bTimeOnly)) + { + return false; + } + return true; + } + + private static Exception GetDateTimeParseException(ref DateTimeResult result) + { + switch (result.failure) + { + case ParseFailureKind.ArgumentNull: + return new ArgumentNullException(result.failureArgumentName, SR.GetResourceString(result.failureMessageID)); + case ParseFailureKind.Format: + return new FormatException(SR.GetResourceString(result.failureMessageID)); + case ParseFailureKind.FormatWithParameter: + return new FormatException(SR.Format(SR.GetResourceString(result.failureMessageID), result.failureMessageFormatArgument)); + case ParseFailureKind.FormatBadDateTimeCalendar: + return new FormatException(SR.Format(SR.GetResourceString(result.failureMessageID), result.calendar)); + default: + Debug.Assert(false, "Unkown DateTimeParseFailure: " + result); + return null; + } + } + + // Builds with _LOGGING defined (x86dbg, amd64chk, etc) support tracing + // Set the following internal-only/unsupported environment variables to enable DateTime tracing to the console: + // + // COMPlus_LogEnable=1 + // COMPlus_LogToConsole=1 + // COMPlus_LogLevel=9 + // COMPlus_ManagedLogFacility=0x00001000 + [Pure] + [Conditional("_LOGGING")] + internal static void LexTraceExit(string message, DS dps) + { +#if _LOGGING + if (!_tracingEnabled) + return; + BCLDebug.Trace("DATETIME", "[DATETIME] Lex return {0}, DS.{1}", message, dps); +#endif // _LOGGING + } + [Pure] + [Conditional("_LOGGING")] + internal static void PTSTraceExit(DS dps, bool passed) + { +#if _LOGGING + if (!_tracingEnabled) + return; + BCLDebug.Trace("DATETIME", "[DATETIME] ProcessTerminalState {0} @ DS.{1}", passed ? "passed" : "failed", dps); +#endif // _LOGGING + } + [Pure] + [Conditional("_LOGGING")] + internal static void TPTraceExit(string message, DS dps) + { +#if _LOGGING + if (!_tracingEnabled) + return; + BCLDebug.Trace("DATETIME", "[DATETIME] TryParse return {0}, DS.{1}", message, dps); +#endif // _LOGGING + } + [Pure] + [Conditional("_LOGGING")] + internal static void DTFITrace(DateTimeFormatInfo dtfi) + { +#if _LOGGING + if (!_tracingEnabled) + return; + + BCLDebug.Trace("DATETIME", "[DATETIME] DateTimeFormatInfo Properties"); +#if !FEATURE_COREFX_GLOBALIZATION + BCLDebug.Trace("DATETIME", " NativeCalendarName {0}", Hex(dtfi.NativeCalendarName)); +#endif + BCLDebug.Trace("DATETIME", " AMDesignator {0}", Hex(dtfi.AMDesignator)); + BCLDebug.Trace("DATETIME", " PMDesignator {0}", Hex(dtfi.PMDesignator)); + BCLDebug.Trace("DATETIME", " TimeSeparator {0}", Hex(dtfi.TimeSeparator)); + BCLDebug.Trace("DATETIME", " AbbrvDayNames {0}", Hex(dtfi.AbbreviatedDayNames)); + BCLDebug.Trace("DATETIME", " ShortestDayNames {0}", Hex(dtfi.ShortestDayNames)); + BCLDebug.Trace("DATETIME", " DayNames {0}", Hex(dtfi.DayNames)); + BCLDebug.Trace("DATETIME", " AbbrvMonthNames {0}", Hex(dtfi.AbbreviatedMonthNames)); + BCLDebug.Trace("DATETIME", " MonthNames {0}", Hex(dtfi.MonthNames)); + BCLDebug.Trace("DATETIME", " AbbrvMonthGenNames {0}", Hex(dtfi.AbbreviatedMonthGenitiveNames)); + BCLDebug.Trace("DATETIME", " MonthGenNames {0}", Hex(dtfi.MonthGenitiveNames)); +#endif // _LOGGING + } +#if _LOGGING + [Pure] + // return a string in the form: "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" + internal static string Hex(string[] strs) + { + if (strs == null || strs.Length == 0) + return String.Empty; + if (strs.Length == 1) + return Hex(strs[0]); + + int curLineLength = 0; + int maxLineLength = 55; + int newLinePadding = 20; + + + //invariant: strs.Length >= 2 + StringBuilder buffer = new StringBuilder(); + buffer.Append(Hex(strs[0])); + curLineLength = buffer.Length; + String s; + + for (int i = 1; i < strs.Length - 1; i++) + { + s = Hex(strs[i]); + + if (s.Length > maxLineLength || (curLineLength + s.Length + 2) > maxLineLength) + { + buffer.Append(','); + buffer.Append(Environment.NewLine); + buffer.Append(' ', newLinePadding); + curLineLength = 0; + } + else + { + buffer.Append(", "); + curLineLength += 2; + } + buffer.Append(s); + curLineLength += s.Length; + } + + buffer.Append(','); + s = Hex(strs[strs.Length - 1]); + if (s.Length > maxLineLength || (curLineLength + s.Length + 6) > maxLineLength) + { + buffer.Append(Environment.NewLine); + buffer.Append(' ', newLinePadding); + } + else + { + buffer.Append(' '); + } + buffer.Append(s); + return buffer.ToString(); + } + [Pure] + // return a string in the form: "Sun" + internal static string Hex(string str) + { + StringBuilder buffer = new StringBuilder(); + buffer.Append("\""); + for (int i = 0; i < str.Length; i++) + { + if (str[i] <= '\x007f') + buffer.Append(str[i]); + else + buffer.Append("\\u" + ((int)str[i]).ToString("x4", CultureInfo.InvariantCulture)); + } + buffer.Append("\""); + return buffer.ToString(); + } + [Pure] + // return an unicode escaped string form of char c + internal static String Hex(char c) + { + if (c <= '\x007f') + return c.ToString(CultureInfo.InvariantCulture); + else + return "\\u" + ((int)c).ToString("x4", CultureInfo.InvariantCulture); + } + + internal static bool _tracingEnabled = BCLDebug.CheckEnabled("DATETIME"); +#endif // _LOGGING + } + + + // + // This is a string parsing helper which wraps a String object. + // It has a Index property which tracks + // the current parsing pointer of the string. + // + internal + struct __DTString + { + // + // Value propery: stores the real string to be parsed. + // + internal String Value; + + // + // Index property: points to the character that we are currently parsing. + // + internal int Index; + + // The length of Value string. + internal int len; + + // The current chracter to be looked at. + internal char m_current; + + private CompareInfo m_info; + // Flag to indicate if we encouter an digit, we should check for token or not. + // In some cultures, such as mn-MN, it uses "\x0031\x00a0\x0434\x04af\x0433\x044d\x044d\x0440\x00a0\x0441\x0430\x0440" in month names. + private bool m_checkDigitToken; + + internal __DTString(String str, DateTimeFormatInfo dtfi, bool checkDigitToken) : this(str, dtfi) + { + m_checkDigitToken = checkDigitToken; + } + + internal __DTString(String str, DateTimeFormatInfo dtfi) + { + Index = -1; + Value = str; + len = Value.Length; + + m_current = '\0'; + if (dtfi != null) + { + m_info = dtfi.CompareInfo; + m_checkDigitToken = ((dtfi.FormatFlags & DateTimeFormatFlags.UseDigitPrefixInTokens) != 0); + } + else + { + m_info = CultureInfo.CurrentCulture.CompareInfo; + m_checkDigitToken = false; + } + } + + internal CompareInfo CompareInfo + { + get { return m_info; } + } + + // + // Advance the Index. + // Return true if Index is NOT at the end of the string. + // + // Typical usage: + // while (str.GetNext()) + // { + // char ch = str.GetChar() + // } + internal bool GetNext() + { + Index++; + if (Index < len) + { + m_current = Value[Index]; + return (true); + } + return (false); + } + + internal bool AtEnd() + { + return Index < len ? false : true; + } + + internal bool Advance(int count) + { + Debug.Assert(Index + count <= len, "__DTString::Advance: Index + count <= len"); + Index += count; + if (Index < len) + { + m_current = Value[Index]; + return (true); + } + return (false); + } + + + // Used by DateTime.Parse() to get the next token. + internal void GetRegularToken(out TokenType tokenType, out int tokenValue, DateTimeFormatInfo dtfi) + { + tokenValue = 0; + if (Index >= len) + { + tokenType = TokenType.EndOfString; + return; + } + + tokenType = TokenType.UnknownToken; + + Start: + if (DateTimeParse.IsDigit(m_current)) + { + // This is a digit. + tokenValue = m_current - '0'; + int value; + int start = Index; + + // + // Collect other digits. + // + while (++Index < len) + { + m_current = Value[Index]; + value = m_current - '0'; + if (value >= 0 && value <= 9) + { + tokenValue = tokenValue * 10 + value; + } + else + { + break; + } + } + if (Index - start > DateTimeParse.MaxDateTimeNumberDigits) + { + tokenType = TokenType.NumberToken; + tokenValue = -1; + } + else if (Index - start < 3) + { + tokenType = TokenType.NumberToken; + } + else + { + // If there are more than 3 digits, assume that it's a year value. + tokenType = TokenType.YearNumberToken; + } + if (m_checkDigitToken) + { + int save = Index; + char saveCh = m_current; + // Re-scan using the staring Index to see if this is a token. + Index = start; // To include the first digit. + m_current = Value[Index]; + TokenType tempType; + int tempValue; + // This DTFI has tokens starting with digits. + // E.g. mn-MN has month name like "\x0031\x00a0\x0434\x04af\x0433\x044d\x044d\x0440\x00a0\x0441\x0430\x0440" + if (dtfi.Tokenize(TokenType.RegularTokenMask, out tempType, out tempValue, ref this)) + { + tokenType = tempType; + tokenValue = tempValue; + // This is a token, so the Index has been advanced propertly in DTFI.Tokenizer(). + } + else + { + // Use the number token value. + // Restore the index. + Index = save; + m_current = saveCh; + } + } + } + else if (Char.IsWhiteSpace(m_current)) + { + // Just skip to the next character. + while (++Index < len) + { + m_current = Value[Index]; + if (!(Char.IsWhiteSpace(m_current))) + { + goto Start; + } + } + // We have reached the end of string. + tokenType = TokenType.EndOfString; + } + else + { + dtfi.Tokenize(TokenType.RegularTokenMask, out tokenType, out tokenValue, ref this); + } + } + + internal TokenType GetSeparatorToken(DateTimeFormatInfo dtfi, out int indexBeforeSeparator, out char charBeforeSeparator) + { + indexBeforeSeparator = Index; + charBeforeSeparator = m_current; + TokenType tokenType; + if (!SkipWhiteSpaceCurrent()) + { + // Reach the end of the string. + return (TokenType.SEP_End); + } + if (!DateTimeParse.IsDigit(m_current)) + { + // Not a digit. Tokenize it. + int tokenValue; + bool found = dtfi.Tokenize(TokenType.SeparatorTokenMask, out tokenType, out tokenValue, ref this); + if (!found) + { + tokenType = TokenType.SEP_Space; + } + } + else + { + // Do nothing here. If we see a number, it will not be a separator. There is no need wasting time trying to find the + // separator token. + tokenType = TokenType.SEP_Space; + } + return (tokenType); + } + + internal bool MatchSpecifiedWord(String target) + { + return MatchSpecifiedWord(target, target.Length + Index); + } + + internal bool MatchSpecifiedWord(String target, int endIndex) + { + int count = endIndex - Index; + + if (count != target.Length) + { + return false; + } + + if (Index + count > len) + { + return false; + } + + return (m_info.Compare(Value, Index, count, target, 0, count, CompareOptions.IgnoreCase) == 0); + } + + private static Char[] WhiteSpaceChecks = new Char[] { ' ', '\u00A0' }; + + internal bool MatchSpecifiedWords(String target, bool checkWordBoundary, ref int matchLength) + { + int valueRemaining = Value.Length - Index; + matchLength = target.Length; + + if (matchLength > valueRemaining || m_info.Compare(Value, Index, matchLength, target, 0, matchLength, CompareOptions.IgnoreCase) != 0) + { + // Check word by word + int targetPosition = 0; // Where we are in the target string + int thisPosition = Index; // Where we are in this string + int wsIndex = target.IndexOfAny(WhiteSpaceChecks, targetPosition); + if (wsIndex == -1) + { + return false; + } + do + { + int segmentLength = wsIndex - targetPosition; + if (thisPosition >= Value.Length - segmentLength) + { // Subtraction to prevent overflow. + return false; + } + if (segmentLength == 0) + { + // If segmentLength == 0, it means that we have leading space in the target string. + // In that case, skip the leading spaces in the target and this string. + matchLength--; + } + else + { + // Make sure we also have whitespace in the input string + if (!Char.IsWhiteSpace(Value[thisPosition + segmentLength])) + { + return false; + } + if (m_info.Compare(Value, thisPosition, segmentLength, target, targetPosition, segmentLength, CompareOptions.IgnoreCase) != 0) + { + return false; + } + // Advance the input string + thisPosition = thisPosition + segmentLength + 1; + } + // Advance our target string + targetPosition = wsIndex + 1; + + + // Skip past multiple whitespace + while (thisPosition < Value.Length && Char.IsWhiteSpace(Value[thisPosition])) + { + thisPosition++; + matchLength++; + } + } while ((wsIndex = target.IndexOfAny(WhiteSpaceChecks, targetPosition)) >= 0); + // now check the last segment; + if (targetPosition < target.Length) + { + int segmentLength = target.Length - targetPosition; + if (thisPosition > Value.Length - segmentLength) + { + return false; + } + if (m_info.Compare(Value, thisPosition, segmentLength, target, targetPosition, segmentLength, CompareOptions.IgnoreCase) != 0) + { + return false; + } + } + } + + if (checkWordBoundary) + { + int nextCharIndex = Index + matchLength; + if (nextCharIndex < Value.Length) + { + if (Char.IsLetter(Value[nextCharIndex])) + { + return (false); + } + } + } + return (true); + } + + // + // Check to see if the string starting from Index is a prefix of + // str. + // If a match is found, true value is returned and Index is updated to the next character to be parsed. + // Otherwise, Index is unchanged. + // + internal bool Match(String str) + { + if (++Index >= len) + { + return (false); + } + + if (str.Length > (Value.Length - Index)) + { + return false; + } + + if (m_info.Compare(Value, Index, str.Length, str, 0, str.Length, CompareOptions.Ordinal) == 0) + { + // Update the Index to the end of the matching string. + // So the following GetNext()/Match() opeartion will get + // the next character to be parsed. + Index += (str.Length - 1); + return (true); + } + return (false); + } + + internal bool Match(char ch) + { + if (++Index >= len) + { + return (false); + } + if (Value[Index] == ch) + { + m_current = ch; + return (true); + } + Index--; + return (false); + } + + // + // Actions: From the current position, try matching the longest word in the specified string array. + // E.g. words[] = {"AB", "ABC", "ABCD"}, if the current position points to a substring like "ABC DEF", + // MatchLongestWords(words, ref MaxMatchStrLen) will return 1 (the index), and maxMatchLen will be 3. + // Returns: + // The index that contains the longest word to match + // Arguments: + // words The string array that contains words to search. + // maxMatchStrLen [in/out] the initailized maximum length. This parameter can be used to + // find the longest match in two string arrays. + // + internal int MatchLongestWords(String[] words, ref int maxMatchStrLen) + { + int result = -1; + for (int i = 0; i < words.Length; i++) + { + String word = words[i]; + int matchLength = word.Length; + if (MatchSpecifiedWords(word, false, ref matchLength)) + { + if (matchLength > maxMatchStrLen) + { + maxMatchStrLen = matchLength; + result = i; + } + } + } + + return (result); + } + + // + // Get the number of repeat character after the current character. + // For a string "hh:mm:ss" at Index of 3. GetRepeatCount() = 2, and Index + // will point to the second ':'. + // + internal int GetRepeatCount() + { + char repeatChar = Value[Index]; + int pos = Index + 1; + while ((pos < len) && (Value[pos] == repeatChar)) + { + pos++; + } + int repeatCount = (pos - Index); + // Update the Index to the end of the repeated characters. + // So the following GetNext() opeartion will get + // the next character to be parsed. + Index = pos - 1; + return (repeatCount); + } + + // Return false when end of string is encountered or a non-digit character is found. + internal bool GetNextDigit() + { + if (++Index >= len) + { + return (false); + } + return (DateTimeParse.IsDigit(Value[Index])); + } + + // + // Get the current character. + // + internal char GetChar() + { + Debug.Assert(Index >= 0 && Index < len, "Index >= 0 && Index < len"); + return (Value[Index]); + } + + // + // Convert the current character to a digit, and return it. + // + internal int GetDigit() + { + Debug.Assert(Index >= 0 && Index < len, "Index >= 0 && Index < len"); + Debug.Assert(DateTimeParse.IsDigit(Value[Index]), "IsDigit(Value[Index])"); + return (Value[Index] - '0'); + } + + // + // Eat White Space ahead of the current position + // + // Return false if end of string is encountered. + // + internal void SkipWhiteSpaces() + { + // Look ahead to see if the next character + // is a whitespace. + while (Index + 1 < len) + { + char ch = Value[Index + 1]; + if (!Char.IsWhiteSpace(ch)) + { + return; + } + Index++; + } + return; + } + + // + // Skip white spaces from the current position + // + // Return false if end of string is encountered. + // + internal bool SkipWhiteSpaceCurrent() + { + if (Index >= len) + { + return (false); + } + + if (!Char.IsWhiteSpace(m_current)) + { + return (true); + } + + while (++Index < len) + { + m_current = Value[Index]; + if (!Char.IsWhiteSpace(m_current)) + { + return (true); + } + // Nothing here. + } + return (false); + } + + internal void TrimTail() + { + int i = len - 1; + while (i >= 0 && Char.IsWhiteSpace(Value[i])) + { + i--; + } + Value = Value.Substring(0, i + 1); + len = Value.Length; + } + + // Trim the trailing spaces within a quoted string. + // Call this after TrimTail() is done. + internal void RemoveTrailingInQuoteSpaces() + { + int i = len - 1; + if (i <= 1) + { + return; + } + char ch = Value[i]; + // Check if the last character is a quote. + if (ch == '\'' || ch == '\"') + { + if (Char.IsWhiteSpace(Value[i - 1])) + { + i--; + while (i >= 1 && Char.IsWhiteSpace(Value[i - 1])) + { + i--; + } + Value = Value.Remove(i, Value.Length - 1 - i); + len = Value.Length; + } + } + } + + // Trim the leading spaces within a quoted string. + // Call this after the leading spaces before quoted string are trimmed. + internal void RemoveLeadingInQuoteSpaces() + { + if (len <= 2) + { + return; + } + int i = 0; + char ch = Value[i]; + // Check if the last character is a quote. + if (ch == '\'' || ch == '\"') + { + while ((i + 1) < len && Char.IsWhiteSpace(Value[i + 1])) + { + i++; + } + if (i != 0) + { + Value = Value.Remove(1, i); + len = Value.Length; + } + } + } + + internal DTSubString GetSubString() + { + DTSubString sub = new DTSubString(); + sub.index = Index; + sub.s = Value; + while (Index + sub.length < len) + { + DTSubStringType currentType; + Char ch = Value[Index + sub.length]; + if (ch >= '0' && ch <= '9') + { + currentType = DTSubStringType.Number; + } + else + { + currentType = DTSubStringType.Other; + } + + if (sub.length == 0) + { + sub.type = currentType; + } + else + { + if (sub.type != currentType) + { + break; + } + } + sub.length++; + if (currentType == DTSubStringType.Number) + { + // Incorporate the number into the value + // Limit the digits to prevent overflow + if (sub.length > DateTimeParse.MaxDateTimeNumberDigits) + { + sub.type = DTSubStringType.Invalid; + return sub; + } + int number = ch - '0'; + Debug.Assert(number >= 0 && number <= 9, "number >= 0 && number <= 9"); + sub.value = sub.value * 10 + number; + } + else + { + // For non numbers, just return this length 1 token. This should be expanded + // to more types of thing if this parsing approach is used for things other + // than numbers and single characters + break; + } + } + if (sub.length == 0) + { + sub.type = DTSubStringType.End; + return sub; + } + + return sub; + } + + internal void ConsumeSubString(DTSubString sub) + { + Debug.Assert(sub.index == Index, "sub.index == Index"); + Debug.Assert(sub.index + sub.length <= len, "sub.index + sub.length <= len"); + Index = sub.index + sub.length; + if (Index < len) + { + m_current = Value[Index]; + } + } + } + + internal enum DTSubStringType + { + Unknown = 0, + Invalid = 1, + Number = 2, + End = 3, + Other = 4, + } + + internal struct DTSubString + { + internal String s; + internal Int32 index; + internal Int32 length; + internal DTSubStringType type; + internal Int32 value; + + internal Char this[Int32 relativeIndex] + { + get + { + return s[index + relativeIndex]; + } + } + } + + // + // The buffer to store the parsing token. + // + internal + struct DateTimeToken + { + internal DateTimeParse.DTT dtt; // Store the token + internal TokenType suffix; // Store the CJK Year/Month/Day suffix (if any) + internal int num; // Store the number that we are parsing (if any) + } + + // + // The buffer to store temporary parsing information. + // + internal + unsafe struct DateTimeRawInfo + { + private int* num; + internal int numCount; + internal int month; + internal int year; + internal int dayOfWeek; + internal int era; + internal DateTimeParse.TM timeMark; + internal double fraction; + internal bool hasSameDateAndTimeSeparators; + + internal void Init(int* numberBuffer) + { + month = -1; + year = -1; + dayOfWeek = -1; + era = -1; + timeMark = DateTimeParse.TM.NotSet; + fraction = -1; + num = numberBuffer; + } + internal unsafe void AddNumber(int value) + { + num[numCount++] = value; + } + internal unsafe int GetNumber(int index) + { + return num[index]; + } + } + + internal enum ParseFailureKind + { + None = 0, + ArgumentNull = 1, + Format = 2, + FormatWithParameter = 3, + FormatBadDateTimeCalendar = 4, // FormatException when ArgumentOutOfRange is thrown by a Calendar.TryToDateTime(). + }; + + [Flags] + internal enum ParseFlags + { + HaveYear = 0x00000001, + HaveMonth = 0x00000002, + HaveDay = 0x00000004, + HaveHour = 0x00000008, + HaveMinute = 0x00000010, + HaveSecond = 0x00000020, + HaveTime = 0x00000040, + HaveDate = 0x00000080, + TimeZoneUsed = 0x00000100, + TimeZoneUtc = 0x00000200, + ParsedMonthName = 0x00000400, + CaptureOffset = 0x00000800, + YearDefault = 0x00001000, + Rfc1123Pattern = 0x00002000, + UtcSortPattern = 0x00004000, + } + + // + // This will store the result of the parsing. And it will be eventually + // used to construct a DateTime instance. + // + internal + struct DateTimeResult + { + internal int Year; + internal int Month; + internal int Day; + // + // Set time defualt to 00:00:00. + // + internal int Hour; + internal int Minute; + internal int Second; + internal double fraction; + + internal int era; + + internal ParseFlags flags; + + internal TimeSpan timeZoneOffset; + + internal Calendar calendar; + + internal DateTime parsedDate; + + internal ParseFailureKind failure; + internal string failureMessageID; + internal object failureMessageFormatArgument; + internal string failureArgumentName; + + internal void Init() + { + Year = -1; + Month = -1; + Day = -1; + fraction = -1; + era = -1; + } + + internal void SetDate(int year, int month, int day) + { + Year = year; + Month = month; + Day = day; + } + internal void SetFailure(ParseFailureKind failure, string failureMessageID, object failureMessageFormatArgument) + { + this.failure = failure; + this.failureMessageID = failureMessageID; + this.failureMessageFormatArgument = failureMessageFormatArgument; + } + + internal void SetFailure(ParseFailureKind failure, string failureMessageID, object failureMessageFormatArgument, string failureArgumentName) + { + this.failure = failure; + this.failureMessageID = failureMessageID; + this.failureMessageFormatArgument = failureMessageFormatArgument; + this.failureArgumentName = failureArgumentName; + } + } + + // This is the helper data structure used in ParseExact(). + internal struct ParsingInfo + { + internal Calendar calendar; + internal int dayOfWeek; + internal DateTimeParse.TM timeMark; + + internal bool fUseHour12; + internal bool fUseTwoDigitYear; + internal bool fAllowInnerWhite; + internal bool fAllowTrailingWhite; + internal bool fCustomNumberParser; + internal DateTimeParse.MatchNumberDelegate parseNumberDelegate; + + internal void Init() + { + dayOfWeek = -1; + timeMark = DateTimeParse.TM.NotSet; + } + } + + // + // The type of token that will be returned by DateTimeFormatInfo.Tokenize(). + // + internal enum TokenType + { + // The valid token should start from 1. + + // Regular tokens. The range is from 0x00 ~ 0xff. + NumberToken = 1, // The number. E.g. "12" + YearNumberToken = 2, // The number which is considered as year number, which has 3 or more digits. E.g. "2003" + Am = 3, // AM timemark. E.g. "AM" + Pm = 4, // PM timemark. E.g. "PM" + MonthToken = 5, // A word (or words) that represents a month name. E.g. "March" + EndOfString = 6, // End of string + DayOfWeekToken = 7, // A word (or words) that represents a day of week name. E.g. "Monday" or "Mon" + TimeZoneToken = 8, // A word that represents a timezone name. E.g. "GMT" + EraToken = 9, // A word that represents a era name. E.g. "A.D." + DateWordToken = 10, // A word that can appear in a DateTime string, but serves no parsing semantics. E.g. "de" in Spanish culture. + UnknownToken = 11, // An unknown word, which signals an error in parsing. + HebrewNumber = 12, // A number that is composed of Hebrew text. Hebrew calendar uses Hebrew digits for year values, month values, and day values. + JapaneseEraToken = 13, // Era name for JapaneseCalendar + TEraToken = 14, // Era name for TaiwanCalendar + IgnorableSymbol = 15, // A separator like "," that is equivalent to whitespace + + + // Separator tokens. + SEP_Unk = 0x100, // Unknown separator. + SEP_End = 0x200, // The end of the parsing string. + SEP_Space = 0x300, // Whitespace (including comma). + SEP_Am = 0x400, // AM timemark. E.g. "AM" + SEP_Pm = 0x500, // PM timemark. E.g. "PM" + SEP_Date = 0x600, // date separator. E.g. "/" + SEP_Time = 0x700, // time separator. E.g. ":" + SEP_YearSuff = 0x800, // Chinese/Japanese/Korean year suffix. + SEP_MonthSuff = 0x900, // Chinese/Japanese/Korean month suffix. + SEP_DaySuff = 0xa00, // Chinese/Japanese/Korean day suffix. + SEP_HourSuff = 0xb00, // Chinese/Japanese/Korean hour suffix. + SEP_MinuteSuff = 0xc00, // Chinese/Japanese/Korean minute suffix. + SEP_SecondSuff = 0xd00, // Chinese/Japanese/Korean second suffix. + SEP_LocalTimeMark = 0xe00, // 'T', used in ISO 8601 format. + SEP_DateOrOffset = 0xf00, // '-' which could be a date separator or start of a time zone offset + + RegularTokenMask = 0x00ff, + SeparatorTokenMask = 0xff00, + } +} diff --git a/src/mscorlib/shared/System/Globalization/DateTimeStyles.cs b/src/mscorlib/shared/System/Globalization/DateTimeStyles.cs new file mode 100644 index 0000000000..79232ff199 --- /dev/null +++ b/src/mscorlib/shared/System/Globalization/DateTimeStyles.cs @@ -0,0 +1,49 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** +** +** Purpose: Contains valid formats for DateTime recognized by +** the DateTime class' parsing code. +** +** +===========================================================*/ + +namespace System.Globalization +{ + [Flags] + public enum DateTimeStyles + { + // Bit flag indicating that leading whitespace is allowed. Character values + // 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, and 0x0020 are considered to be + // whitespace. + + + None = 0x00000000, + + AllowLeadingWhite = 0x00000001, + + AllowTrailingWhite = 0x00000002, //Bitflag indicating trailing whitespace is allowed. + + AllowInnerWhite = 0x00000004, + + AllowWhiteSpaces = AllowLeadingWhite | AllowInnerWhite | AllowTrailingWhite, + // When parsing a date/time string, if all year/month/day are missing, set the default date + // to 0001/1/1, instead of the current year/month/day. + + NoCurrentDateDefault = 0x00000008, + // When parsing a date/time string, if a timezone specifier ("GMT","Z","+xxxx", "-xxxx" exists), we will + // ajdust the parsed time based to GMT. + + AdjustToUniversal = 0x00000010, + + AssumeLocal = 0x00000020, + + AssumeUniversal = 0x00000040, + // Attempt to preserve whether the input is unspecified, local or UTC + RoundtripKind = 0x00000080, + } +} diff --git a/src/mscorlib/shared/System/Globalization/DaylightTime.cs b/src/mscorlib/shared/System/Globalization/DaylightTime.cs new file mode 100644 index 0000000000..b3c70e1d10 --- /dev/null +++ b/src/mscorlib/shared/System/Globalization/DaylightTime.cs @@ -0,0 +1,50 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Globalization +{ + // This class represents a starting/ending time for a period of daylight saving time. + [Serializable] + public class DaylightTime + { + private readonly DateTime _start; + private readonly DateTime _end; + private readonly TimeSpan _delta; + + private DaylightTime() + { + } + + public DaylightTime(DateTime start, DateTime end, TimeSpan delta) + { + _start = start; + _end = end; + _delta = delta; + } + + // The start date of a daylight saving period. + public DateTime Start => _start; + + // The end date of a daylight saving period. + public DateTime End => _end; + + // Delta to stardard offset in ticks. + public TimeSpan Delta => _delta; + } + + // Value type version of DaylightTime + internal struct DaylightTimeStruct + { + public DaylightTimeStruct(DateTime start, DateTime end, TimeSpan delta) + { + Start = start; + End = end; + Delta = delta; + } + + public readonly DateTime Start; + public readonly DateTime End; + public readonly TimeSpan Delta; + } +} diff --git a/src/mscorlib/shared/System/Globalization/DigitShapes.cs b/src/mscorlib/shared/System/Globalization/DigitShapes.cs new file mode 100644 index 0000000000..1ce45dbeb6 --- /dev/null +++ b/src/mscorlib/shared/System/Globalization/DigitShapes.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Globalization +{ + public enum DigitShapes : int + { + Context = 0x0000, // The shape depends on the previous text in the same output. + None = 0x0001, // Gives full Unicode compatibility. + NativeNational = 0x0002 // National shapes + } +} diff --git a/src/mscorlib/shared/System/Globalization/EastAsianLunisolarCalendar.cs b/src/mscorlib/shared/System/Globalization/EastAsianLunisolarCalendar.cs new file mode 100644 index 0000000000..d06b13cd7d --- /dev/null +++ b/src/mscorlib/shared/System/Globalization/EastAsianLunisolarCalendar.cs @@ -0,0 +1,710 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics.Contracts; + +namespace System.Globalization +{ + [Serializable] + public abstract class EastAsianLunisolarCalendar : Calendar + { + internal const int LeapMonth = 0; + internal const int Jan1Month = 1; + internal const int Jan1Date = 2; + internal const int nDaysPerMonth = 3; + + // # of days so far in the solar year + internal static readonly int[] DaysToMonth365 = + { + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 + }; + + internal static readonly int[] DaysToMonth366 = + { + 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 + }; + + internal const int DatePartYear = 0; + internal const int DatePartDayOfYear = 1; + internal const int DatePartMonth = 2; + internal const int DatePartDay = 3; + + public override CalendarAlgorithmType AlgorithmType + { + get + { + return CalendarAlgorithmType.LunisolarCalendar; + } + } + + // Return the year number in the 60-year cycle. + // + + public virtual int GetSexagenaryYear(DateTime time) + { + CheckTicksRange(time.Ticks); + + int year = 0, month = 0, day = 0; + TimeToLunar(time, ref year, ref month, ref day); + + return ((year - 4) % 60) + 1; + } + + // Return the celestial year from the 60-year cycle. + // The returned value is from 1 ~ 10. + // + + public int GetCelestialStem(int sexagenaryYear) + { + if ((sexagenaryYear < 1) || (sexagenaryYear > 60)) + { + throw new ArgumentOutOfRangeException( + nameof(sexagenaryYear), + SR.Format(SR.ArgumentOutOfRange_Range, 1, 60)); + } + Contract.EndContractBlock(); + + return ((sexagenaryYear - 1) % 10) + 1; + } + + // Return the Terrestial Branch from the the 60-year cycle. + // The returned value is from 1 ~ 12. + // + + public int GetTerrestrialBranch(int sexagenaryYear) + { + if ((sexagenaryYear < 1) || (sexagenaryYear > 60)) + { + throw new ArgumentOutOfRangeException( + nameof(sexagenaryYear), + SR.Format(SR.ArgumentOutOfRange_Range, 1, 60)); + } + Contract.EndContractBlock(); + + return ((sexagenaryYear - 1) % 12) + 1; + } + + internal abstract int GetYearInfo(int LunarYear, int Index); + internal abstract int GetYear(int year, DateTime time); + internal abstract int GetGregorianYear(int year, int era); + + internal abstract int MinCalendarYear { get; } + internal abstract int MaxCalendarYear { get; } + internal abstract EraInfo[] CalEraInfo { get; } + internal abstract DateTime MinDate { get; } + internal abstract DateTime MaxDate { get; } + + internal const int MaxCalendarMonth = 13; + internal const int MaxCalendarDay = 30; + + internal int MinEraCalendarYear(int era) + { + EraInfo[] mEraInfo = CalEraInfo; + //ChineseLunisolarCalendar does not has m_EraInfo it is going to retuen null + if (mEraInfo == null) + { + return MinCalendarYear; + } + + if (era == Calendar.CurrentEra) + { + era = CurrentEraValue; + } + //era has to be in the supported range otherwise we will throw exception in CheckEraRange() + if (era == GetEra(MinDate)) + { + return (GetYear(MinCalendarYear, MinDate)); + } + + for (int i = 0; i < mEraInfo.Length; i++) + { + if (era == mEraInfo[i].era) + { + return (mEraInfo[i].minEraYear); + } + } + throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue); + } + + internal int MaxEraCalendarYear(int era) + { + EraInfo[] mEraInfo = CalEraInfo; + //ChineseLunisolarCalendar does not has m_EraInfo it is going to retuen null + if (mEraInfo == null) + { + return MaxCalendarYear; + } + + if (era == Calendar.CurrentEra) + { + era = CurrentEraValue; + } + //era has to be in the supported range otherwise we will throw exception in CheckEraRange() + if (era == GetEra(MaxDate)) + { + return (GetYear(MaxCalendarYear, MaxDate)); + } + + for (int i = 0; i < mEraInfo.Length; i++) + { + if (era == mEraInfo[i].era) + { + return (mEraInfo[i].maxEraYear); + } + } + throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue); + } + + internal EastAsianLunisolarCalendar() + { + } + + internal void CheckTicksRange(long ticks) + { + if (ticks < MinSupportedDateTime.Ticks || ticks > MaxSupportedDateTime.Ticks) + { + throw new ArgumentOutOfRangeException( + "time", + String.Format(CultureInfo.InvariantCulture, SR.ArgumentOutOfRange_CalendarRange, + MinSupportedDateTime, MaxSupportedDateTime)); + } + Contract.EndContractBlock(); + } + + internal void CheckEraRange(int era) + { + if (era == Calendar.CurrentEra) + { + era = CurrentEraValue; + } + + if ((era < GetEra(MinDate)) || (era > GetEra(MaxDate))) + { + throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue); + } + } + + internal int CheckYearRange(int year, int era) + { + CheckEraRange(era); + year = GetGregorianYear(year, era); + + if ((year < MinCalendarYear) || (year > MaxCalendarYear)) + { + throw new ArgumentOutOfRangeException( + nameof(year), + SR.Format(SR.ArgumentOutOfRange_Range, MinEraCalendarYear(era), MaxEraCalendarYear(era))); + } + return year; + } + + internal int CheckYearMonthRange(int year, int month, int era) + { + year = CheckYearRange(year, era); + + if (month == 13) + { + //Reject if there is no leap month this year + if (GetYearInfo(year, LeapMonth) == 0) + throw new ArgumentOutOfRangeException(nameof(month), SR.ArgumentOutOfRange_Month); + } + + if (month < 1 || month > 13) + { + throw new ArgumentOutOfRangeException(nameof(month), SR.ArgumentOutOfRange_Month); + } + return year; + } + + internal int InternalGetDaysInMonth(int year, int month) + { + int nDays; + int mask; // mask for extracting bits + + mask = 0x8000; + // convert the lunar day into a lunar month/date + mask >>= (month - 1); + if ((GetYearInfo(year, nDaysPerMonth) & mask) == 0) + nDays = 29; + else + nDays = 30; + return nDays; + } + + // Returns the number of days in the month given by the year and + // month arguments. + // + + public override int GetDaysInMonth(int year, int month, int era) + { + year = CheckYearMonthRange(year, month, era); + return InternalGetDaysInMonth(year, month); + } + + private static int GregorianIsLeapYear(int y) + { + return ((((y) % 4) != 0) ? 0 : ((((y) % 100) != 0) ? 1 : ((((y) % 400) != 0) ? 0 : 1))); + } + + // Returns the date and time converted to a DateTime value. Throws an exception if the n-tuple is invalid. + // + + public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era) + { + year = CheckYearMonthRange(year, month, era); + int daysInMonth = InternalGetDaysInMonth(year, month); + if (day < 1 || day > daysInMonth) + { + throw new ArgumentOutOfRangeException( + nameof(day), + SR.Format(SR.ArgumentOutOfRange_Day, daysInMonth, month)); + } + + int gy = 0; int gm = 0; int gd = 0; + + if (LunarToGregorian(year, month, day, ref gy, ref gm, ref gd)) + { + return new DateTime(gy, gm, gd, hour, minute, second, millisecond); + } + else + { + throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadYearMonthDay); + } + } + + + // + // GregorianToLunar calculates lunar calendar info for the given gregorian year, month, date. + // The input date should be validated before calling this method. + // + internal void GregorianToLunar(int nSYear, int nSMonth, int nSDate, ref int nLYear, ref int nLMonth, ref int nLDate) + { + // unsigned int nLYear, nLMonth, nLDate; // lunar ymd + int nSolarDay; // day # in solar year + int nLunarDay; // day # in lunar year + int fLeap; // is it a solar leap year? + int LDpM; // lunar days/month bitfield + int mask; // mask for extracting bits + int nDays; // # days this lunar month + int nJan1Month, nJan1Date; + + // calc the solar day of year + fLeap = GregorianIsLeapYear(nSYear); + nSolarDay = (fLeap == 1) ? DaysToMonth366[nSMonth - 1] : DaysToMonth365[nSMonth - 1]; + nSolarDay += nSDate; + + // init lunar year info + nLunarDay = nSolarDay; + nLYear = nSYear; + if (nLYear == (MaxCalendarYear + 1)) + { + nLYear--; + nLunarDay += ((GregorianIsLeapYear(nLYear) == 1) ? 366 : 365); + nJan1Month = GetYearInfo(nLYear, Jan1Month); + nJan1Date = GetYearInfo(nLYear, Jan1Date); + } + else + { + nJan1Month = GetYearInfo(nLYear, Jan1Month); + nJan1Date = GetYearInfo(nLYear, Jan1Date); + + // check if this solar date is actually part of the previous + // lunar year + if ((nSMonth < nJan1Month) || + (nSMonth == nJan1Month && nSDate < nJan1Date)) + { + // the corresponding lunar day is actually part of the previous + // lunar year + nLYear--; + + // add a solar year to the lunar day # + nLunarDay += ((GregorianIsLeapYear(nLYear) == 1) ? 366 : 365); + + // update the new start of year + nJan1Month = GetYearInfo(nLYear, Jan1Month); + nJan1Date = GetYearInfo(nLYear, Jan1Date); + } + } + + // convert solar day into lunar day. + // subtract off the beginning part of the solar year which is not + // part of the lunar year. since this part is always in Jan or Feb, + // we don't need to handle Leap Year (LY only affects March + // and later). + nLunarDay -= DaysToMonth365[nJan1Month - 1]; + nLunarDay -= (nJan1Date - 1); + + // convert the lunar day into a lunar month/date + mask = 0x8000; + LDpM = GetYearInfo(nLYear, nDaysPerMonth); + nDays = ((LDpM & mask) != 0) ? 30 : 29; + nLMonth = 1; + while (nLunarDay > nDays) + { + nLunarDay -= nDays; + nLMonth++; + mask >>= 1; + nDays = ((LDpM & mask) != 0) ? 30 : 29; + } + nLDate = nLunarDay; + } + + /* + //Convert from Lunar to Gregorian + //Highly inefficient, but it works based on the forward conversion + */ + internal bool LunarToGregorian(int nLYear, int nLMonth, int nLDate, ref int nSolarYear, ref int nSolarMonth, ref int nSolarDay) + { + int numLunarDays; + + if (nLDate < 1 || nLDate > 30) + return false; + + numLunarDays = nLDate - 1; + + //Add previous months days to form the total num of days from the first of the month. + for (int i = 1; i < nLMonth; i++) + { + numLunarDays += InternalGetDaysInMonth(nLYear, i); + } + + //Get Gregorian First of year + int nJan1Month = GetYearInfo(nLYear, Jan1Month); + int nJan1Date = GetYearInfo(nLYear, Jan1Date); + + // calc the solar day of year of 1 Lunar day + int fLeap = GregorianIsLeapYear(nLYear); + int[] days = (fLeap == 1) ? DaysToMonth366 : DaysToMonth365; + + nSolarDay = nJan1Date; + + if (nJan1Month > 1) + nSolarDay += days[nJan1Month - 1]; + + // Add the actual lunar day to get the solar day we want + nSolarDay = nSolarDay + numLunarDays;// - 1; + + if (nSolarDay > (fLeap + 365)) + { + nSolarYear = nLYear + 1; + nSolarDay -= (fLeap + 365); + } + else + { + nSolarYear = nLYear; + } + + for (nSolarMonth = 1; nSolarMonth < 12; nSolarMonth++) + { + if (days[nSolarMonth] >= nSolarDay) + break; + } + + nSolarDay -= days[nSolarMonth - 1]; + return true; + } + + internal DateTime LunarToTime(DateTime time, int year, int month, int day) + { + int gy = 0; int gm = 0; int gd = 0; + LunarToGregorian(year, month, day, ref gy, ref gm, ref gd); + return (GregorianCalendar.GetDefaultInstance().ToDateTime(gy, gm, gd, time.Hour, time.Minute, time.Second, time.Millisecond)); + } + + internal void TimeToLunar(DateTime time, ref int year, ref int month, ref int day) + { + int gy = 0; int gm = 0; int gd = 0; + + Calendar Greg = GregorianCalendar.GetDefaultInstance(); + gy = Greg.GetYear(time); + gm = Greg.GetMonth(time); + gd = Greg.GetDayOfMonth(time); + + GregorianToLunar(gy, gm, gd, ref year, ref month, ref day); + } + + // Returns the DateTime resulting from adding the given number of + // months to the specified DateTime. The result is computed by incrementing + // (or decrementing) the year and month parts of the specified DateTime by + // value months, and, if required, adjusting the day part of the + // resulting date downwards to the last day of the resulting month in the + // resulting year. The time-of-day part of the result is the same as the + // time-of-day part of the specified DateTime. + // + + public override DateTime AddMonths(DateTime time, int months) + { + if (months < -120000 || months > 120000) + { + throw new ArgumentOutOfRangeException( + nameof(months), + SR.Format(SR.ArgumentOutOfRange_Range, -120000, 120000)); + } + Contract.EndContractBlock(); + + CheckTicksRange(time.Ticks); + + int y = 0; int m = 0; int d = 0; + TimeToLunar(time, ref y, ref m, ref d); + + int i = m + months; + if (i > 0) + { + int monthsInYear = InternalIsLeapYear(y) ? 13 : 12; + + while (i - monthsInYear > 0) + { + i -= monthsInYear; + y++; + monthsInYear = InternalIsLeapYear(y) ? 13 : 12; + } + m = i; + } + else + { + int monthsInYear; + while (i <= 0) + { + monthsInYear = InternalIsLeapYear(y - 1) ? 13 : 12; + i += monthsInYear; + y--; + } + m = i; + } + + int days = InternalGetDaysInMonth(y, m); + if (d > days) + { + d = days; + } + DateTime dt = LunarToTime(time, y, m, d); + + CheckAddResult(dt.Ticks, MinSupportedDateTime, MaxSupportedDateTime); + return (dt); + } + + + public override DateTime AddYears(DateTime time, int years) + { + CheckTicksRange(time.Ticks); + + int y = 0; int m = 0; int d = 0; + TimeToLunar(time, ref y, ref m, ref d); + + y += years; + + if (m == 13 && !InternalIsLeapYear(y)) + { + m = 12; + d = InternalGetDaysInMonth(y, m); + } + int DaysInMonths = InternalGetDaysInMonth(y, m); + if (d > DaysInMonths) + { + d = DaysInMonths; + } + + DateTime dt = LunarToTime(time, y, m, d); + CheckAddResult(dt.Ticks, MinSupportedDateTime, MaxSupportedDateTime); + return (dt); + } + + // Returns the day-of-year part of the specified DateTime. The returned value + // is an integer between 1 and [354|355 |383|384]. + // + + public override int GetDayOfYear(DateTime time) + { + CheckTicksRange(time.Ticks); + + int y = 0; int m = 0; int d = 0; + TimeToLunar(time, ref y, ref m, ref d); + + for (int i = 1; i < m; i++) + { + d = d + InternalGetDaysInMonth(y, i); + } + return d; + } + + // Returns the day-of-month part of the specified DateTime. The returned + // value is an integer between 1 and 29 or 30. + // + + public override int GetDayOfMonth(DateTime time) + { + CheckTicksRange(time.Ticks); + + int y = 0; int m = 0; int d = 0; + TimeToLunar(time, ref y, ref m, ref d); + + return d; + } + + // Returns the number of days in the year given by the year argument for the current era. + // + + public override int GetDaysInYear(int year, int era) + { + year = CheckYearRange(year, era); + + int Days = 0; + int monthsInYear = InternalIsLeapYear(year) ? 13 : 12; + + while (monthsInYear != 0) + Days += InternalGetDaysInMonth(year, monthsInYear--); + + return Days; + } + + // Returns the month part of the specified DateTime. The returned value is an + // integer between 1 and 13. + // + + public override int GetMonth(DateTime time) + { + CheckTicksRange(time.Ticks); + + int y = 0; int m = 0; int d = 0; + TimeToLunar(time, ref y, ref m, ref d); + + return m; + } + + // Returns the year part of the specified DateTime. The returned value is an + // integer between 1 and MaxCalendarYear. + // + + public override int GetYear(DateTime time) + { + CheckTicksRange(time.Ticks); + + int y = 0; int m = 0; int d = 0; + TimeToLunar(time, ref y, ref m, ref d); + + return GetYear(y, time); + } + + // Returns the day-of-week part of the specified DateTime. The returned value + // is an integer between 0 and 6, where 0 indicates Sunday, 1 indicates + // Monday, 2 indicates Tuesday, 3 indicates Wednesday, 4 indicates + // Thursday, 5 indicates Friday, and 6 indicates Saturday. + // + + public override DayOfWeek GetDayOfWeek(DateTime time) + { + CheckTicksRange(time.Ticks); + return ((DayOfWeek)((int)(time.Ticks / Calendar.TicksPerDay + 1) % 7)); + } + + // Returns the number of months in the specified year and era. + + public override int GetMonthsInYear(int year, int era) + { + year = CheckYearRange(year, era); + return (InternalIsLeapYear(year) ? 13 : 12); + } + + // Checks whether a given day in the specified era is a leap day. This method returns true if + // the date is a leap day, or false if not. + // + + public override bool IsLeapDay(int year, int month, int day, int era) + { + year = CheckYearMonthRange(year, month, era); + int daysInMonth = InternalGetDaysInMonth(year, month); + + if (day < 1 || day > daysInMonth) + { + throw new ArgumentOutOfRangeException( + nameof(day), + SR.Format(SR.ArgumentOutOfRange_Day, daysInMonth, month)); + } + int m = GetYearInfo(year, LeapMonth); + return ((m != 0) && (month == (m + 1))); + } + + // Checks whether a given month in the specified era is a leap month. This method returns true if + // month is a leap month, or false if not. + // + + public override bool IsLeapMonth(int year, int month, int era) + { + year = CheckYearMonthRange(year, month, era); + int m = GetYearInfo(year, LeapMonth); + return ((m != 0) && (month == (m + 1))); + } + + // Returns the leap month in a calendar year of the specified era. This method returns 0 + // if this this year is not a leap year. + // + + public override int GetLeapMonth(int year, int era) + { + year = CheckYearRange(year, era); + int month = GetYearInfo(year, LeapMonth); + if (month > 0) + { + return (month + 1); + } + return 0; + } + + internal bool InternalIsLeapYear(int year) + { + return (GetYearInfo(year, LeapMonth) != 0); + } + // Checks whether a given year in the specified era is a leap year. This method returns true if + // year is a leap year, or false if not. + // + + public override bool IsLeapYear(int year, int era) + { + year = CheckYearRange(year, era); + return InternalIsLeapYear(year); + } + + private const int DEFAULT_GREGORIAN_TWO_DIGIT_YEAR_MAX = 2029; + + + public override int TwoDigitYearMax + { + get + { + if (twoDigitYearMax == -1) + { + twoDigitYearMax = GetSystemTwoDigitYearSetting(BaseCalendarID, GetYear(new DateTime(DEFAULT_GREGORIAN_TWO_DIGIT_YEAR_MAX, 1, 1))); + } + return (twoDigitYearMax); + } + + set + { + VerifyWritable(); + if (value < 99 || value > MaxCalendarYear) + { + throw new ArgumentOutOfRangeException( + nameof(value), + SR.Format(SR.ArgumentOutOfRange_Range, 99, MaxCalendarYear)); + } + twoDigitYearMax = value; + } + } + + + public override int ToFourDigitYear(int year) + { + if (year < 0) + { + throw new ArgumentOutOfRangeException(nameof(year), + SR.ArgumentOutOfRange_NeedNonNegNum); + } + Contract.EndContractBlock(); + + year = base.ToFourDigitYear(year); + CheckYearRange(year, CurrentEra); + return (year); + } + } +} diff --git a/src/mscorlib/shared/System/Globalization/GregorianCalendarTypes.cs b/src/mscorlib/shared/System/Globalization/GregorianCalendarTypes.cs new file mode 100644 index 0000000000..1b61e5256e --- /dev/null +++ b/src/mscorlib/shared/System/Globalization/GregorianCalendarTypes.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Globalization +{ + // Note: The values of the members of this enum must match the coresponding values + // in the CalendarId enum (since we cast between GregorianCalendarTypes and CalendarId). + [Serializable] + public enum GregorianCalendarTypes + { + Localized = CalendarId.GREGORIAN, + USEnglish = CalendarId.GREGORIAN_US, + MiddleEastFrench = CalendarId.GREGORIAN_ME_FRENCH, + Arabic = CalendarId.GREGORIAN_ARABIC, + TransliteratedEnglish = CalendarId.GREGORIAN_XLIT_ENGLISH, + TransliteratedFrench = CalendarId.GREGORIAN_XLIT_FRENCH, + } +} diff --git a/src/mscorlib/shared/System/Globalization/HebrewCalendar.cs b/src/mscorlib/shared/System/Globalization/HebrewCalendar.cs new file mode 100644 index 0000000000..b4f54f8fbb --- /dev/null +++ b/src/mscorlib/shared/System/Globalization/HebrewCalendar.cs @@ -0,0 +1,1129 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Diagnostics.Contracts; + +namespace System.Globalization +{ + //////////////////////////////////////////////////////////////////////////// + // + // Rules for the Hebrew calendar: + // - The Hebrew calendar is both a Lunar (months) and Solar (years) + // calendar, but allows for a week of seven days. + // - Days begin at sunset. + // - Leap Years occur in the 3, 6, 8, 11, 14, 17, & 19th years of a + // 19-year cycle. Year = leap iff ((7y+1) mod 19 < 7). + // - There are 12 months in a common year and 13 months in a leap year. + // - In a common year, the 6th month, Adar, has 29 days. In a leap + // year, the 6th month, Adar I, has 30 days and the leap month, + // Adar II, has 29 days. + // - Common years have 353-355 days. Leap years have 383-385 days. + // - The Hebrew new year (Rosh HaShanah) begins on the 1st of Tishri, + // the 7th month in the list below. + // - The new year may not begin on Sunday, Wednesday, or Friday. + // - If the new year would fall on a Tuesday and the conjunction of + // the following year were at midday or later, the new year is + // delayed until Thursday. + // - If the new year would fall on a Monday after a leap year, the + // new year is delayed until Tuesday. + // - The length of the 8th and 9th months vary from year to year, + // depending on the overall length of the year. + // - The length of a year is determined by the dates of the new + // years (Tishri 1) preceding and following the year in question. + // - The 2th month is long (30 days) if the year has 355 or 385 days. + // - The 3th month is short (29 days) if the year has 353 or 383 days. + // - The Hebrew months are: + // 1. Tishri (30 days) + // 2. Heshvan (29 or 30 days) + // 3. Kislev (29 or 30 days) + // 4. Teveth (29 days) + // 5. Shevat (30 days) + // 6. Adar I (30 days) + // 7. Adar {II} (29 days, this only exists if that year is a leap year) + // 8. Nisan (30 days) + // 9. Iyyar (29 days) + // 10. Sivan (30 days) + // 11. Tammuz (29 days) + // 12. Av (30 days) + // 13. Elul (29 days) + // + //////////////////////////////////////////////////////////////////////////// + /* + ** Calendar support range: + ** Calendar Minimum Maximum + ** ========== ========== ========== + ** Gregorian 1583/01/01 2239/09/29 + ** Hebrew 5343/04/07 5999/13/29 + */ + + // Includes CHebrew implemetation;i.e All the code necessary for converting + // Gregorian to Hebrew Lunar from 1583 to 2239. + + + [Serializable] + public class HebrewCalendar : Calendar + { + public static readonly int HebrewEra = 1; + + internal const int DatePartYear = 0; + internal const int DatePartDayOfYear = 1; + internal const int DatePartMonth = 2; + internal const int DatePartDay = 3; + internal const int DatePartDayOfWeek = 4; + + // + // Hebrew Translation Table. + // + // This table is used to get the following Hebrew calendar information for a + // given Gregorian year: + // 1. The day of the Hebrew month corresponding to Gregorian January 1st + // for a given Gregorian year. + // 2. The month of the Hebrew month corresponding to Gregorian January 1st + // for a given Gregorian year. + // The information is not directly in the table. Instead, the info is decoded + // by special values (numbers above 29 and below 1). + // 3. The type of the Hebrew year for a given Gregorian year. + // + + /* + More notes: + + This table includes 2 numbers for each year. + The offset into the table determines the year. (offset 0 is Gregorian year 1500) + 1st number determines the day of the Hebrew month coresponeds to January 1st. + 2nd number determines the type of the Hebrew year. (the type determines how + many days are there in the year.) + + normal years : 1 = 353 days 2 = 354 days 3 = 355 days. + Leap years : 4 = 383 5 384 6 = 385 days. + + A 99 means the year is not supported for translation. + for convenience the table was defined for 750 year, + but only 640 years are supported. (from 1583 to 2239) + the years before 1582 (starting of Georgian calander) + and after 2239, are filled with 99. + + Greogrian January 1st falls usually in Tevet (4th month). Tevet has always 29 days. + That's why, there no nead to specify the lunar month in the table. + There are exceptions, these are coded by giving numbers above 29 and below 1. + Actual decoding is takenig place whenever fetching information from the table. + The function for decoding is in GetLunarMonthDay(). + + Example: + The data for 2000 - 2005 A.D. is: + + 23,6,6,1,17,2,27,6,7,3, // 2000 - 2004 + + For year 2000, we know it has a Hebrew year type 6, which means it has 385 days. + And 1/1/2000 A.D. is Hebrew year 5760, 23rd day of 4th month. + */ + + // + // Jewish Era in use today is dated from the supposed year of the + // Creation with its beginning in 3761 B.C. + // + + // The Hebrew year of Gregorian 1st year AD. + // 0001/01/01 AD is Hebrew 3760/01/01 + private const int HebrewYearOf1AD = 3760; + + // The first Gregorian year in HebrewTable. + private const int FirstGregorianTableYear = 1583; // == Hebrew Year 5343 + // The last Gregorian year in HebrewTable. + private const int LastGregorianTableYear = 2239; // == Hebrew Year 5999 + private const int TABLESIZE = (LastGregorianTableYear - FirstGregorianTableYear); + + private const int MinHebrewYear = HebrewYearOf1AD + FirstGregorianTableYear; // == 5343 + private const int MaxHebrewYear = HebrewYearOf1AD + LastGregorianTableYear; // == 5999 + + private static readonly byte[] s_hebrewTable = { + 7,3,17,3, // 1583-1584 (Hebrew year: 5343 - 5344) + 0,4,11,2,21,6,1,3,13,2, // 1585-1589 + 25,4,5,3,16,2,27,6,9,1, // 1590-1594 + 20,2,0,6,11,3,23,4,4,2, // 1595-1599 + 14,3,27,4,8,2,18,3,28,6, // 1600 + 11,1,22,5,2,3,12,3,25,4, // 1605 + 6,2,16,3,26,6,8,2,20,1, // 1610 + 0,6,11,2,24,4,4,3,15,2, // 1615 + 25,6,8,1,19,2,29,6,9,3, // 1620 + 22,4,3,2,13,3,25,4,6,3, // 1625 + 17,2,27,6,7,3,19,2,31,4, // 1630 + 11,3,23,4,5,2,15,3,25,6, // 1635 + 6,2,19,1,29,6,10,2,22,4, // 1640 + 3,3,14,2,24,6,6,1,17,3, // 1645 + 28,5,8,3,20,1,32,5,12,3, // 1650 + 22,6,4,1,16,2,26,6,6,3, // 1655 + 17,2,0,4,10,3,22,4,3,2, // 1660 + 14,3,24,6,5,2,17,1,28,6, // 1665 + 9,2,19,3,31,4,13,2,23,6, // 1670 + 3,3,15,1,27,5,7,3,17,3, // 1675 + 29,4,11,2,21,6,3,1,14,2, // 1680 + 25,6,5,3,16,2,28,4,9,3, // 1685 + 20,2,0,6,12,1,23,6,4,2, // 1690 + 14,3,26,4,8,2,18,3,0,4, // 1695 + 10,3,21,5,1,3,13,1,24,5, // 1700 + 5,3,15,3,27,4,8,2,19,3, // 1705 + 29,6,10,2,22,4,3,3,14,2, // 1710 + 26,4,6,3,18,2,28,6,10,1, // 1715 + 20,6,2,2,12,3,24,4,5,2, // 1720 + 16,3,28,4,8,3,19,2,0,6, // 1725 + 12,1,23,5,3,3,14,3,26,4, // 1730 + 7,2,17,3,28,6,9,2,21,4, // 1735 + 1,3,13,2,25,4,5,3,16,2, // 1740 + 27,6,9,1,19,3,0,5,11,3, // 1745 + 23,4,4,2,14,3,25,6,7,1, // 1750 + 18,2,28,6,9,3,21,4,2,2, // 1755 + 12,3,25,4,6,2,16,3,26,6, // 1760 + 8,2,20,1,0,6,11,2,22,6, // 1765 + 4,1,15,2,25,6,6,3,18,1, // 1770 + 29,5,9,3,22,4,2,3,13,2, // 1775 + 23,6,4,3,15,2,27,4,7,3, // 1780 + 19,2,31,4,11,3,21,6,3,2, // 1785 + 15,1,25,6,6,2,17,3,29,4, // 1790 + 10,2,20,6,3,1,13,3,24,5, // 1795 + 4,3,16,1,27,5,7,3,17,3, // 1800 + 0,4,11,2,21,6,1,3,13,2, // 1805 + 25,4,5,3,16,2,29,4,9,3, // 1810 + 19,6,30,2,13,1,23,6,4,2, // 1815 + 14,3,27,4,8,2,18,3,0,4, // 1820 + 11,3,22,5,2,3,14,1,26,5, // 1825 + 6,3,16,3,28,4,10,2,20,6, // 1830 + 30,3,11,2,24,4,4,3,15,2, // 1835 + 25,6,8,1,19,2,29,6,9,3, // 1840 + 22,4,3,2,13,3,25,4,7,2, // 1845 + 17,3,27,6,9,1,21,5,1,3, // 1850 + 11,3,23,4,5,2,15,3,25,6, // 1855 + 6,2,19,1,29,6,10,2,22,4, // 1860 + 3,3,14,2,24,6,6,1,18,2, // 1865 + 28,6,8,3,20,4,2,2,12,3, // 1870 + 24,4,4,3,16,2,26,6,6,3, // 1875 + 17,2,0,4,10,3,22,4,3,2, // 1880 + 14,3,24,6,5,2,17,1,28,6, // 1885 + 9,2,21,4,1,3,13,2,23,6, // 1890 + 5,1,15,3,27,5,7,3,19,1, // 1895 + 0,5,10,3,22,4,2,3,13,2, // 1900 + 24,6,4,3,15,2,27,4,8,3, // 1905 + 20,4,1,2,11,3,22,6,3,2, // 1910 + 15,1,25,6,7,2,17,3,29,4, // 1915 + 10,2,21,6,1,3,13,1,24,5, // 1920 + 5,3,15,3,27,4,8,2,19,6, // 1925 + 1,1,12,2,22,6,3,3,14,2, // 1930 + 26,4,6,3,18,2,28,6,10,1, // 1935 + 20,6,2,2,12,3,24,4,5,2, // 1940 + 16,3,28,4,9,2,19,6,30,3, // 1945 + 12,1,23,5,3,3,14,3,26,4, // 1950 + 7,2,17,3,28,6,9,2,21,4, // 1955 + 1,3,13,2,25,4,5,3,16,2, // 1960 + 27,6,9,1,19,6,30,2,11,3, // 1965 + 23,4,4,2,14,3,27,4,7,3, // 1970 + 18,2,28,6,11,1,22,5,2,3, // 1975 + 12,3,25,4,6,2,16,3,26,6, // 1980 + 8,2,20,4,30,3,11,2,24,4, // 1985 + 4,3,15,2,25,6,8,1,18,3, // 1990 + 29,5,9,3,22,4,3,2,13,3, // 1995 + 23,6,6,1,17,2,27,6,7,3, // 2000 - 2004 + 20,4,1,2,11,3,23,4,5,2, // 2005 - 2009 + 15,3,25,6,6,2,19,1,29,6, // 2010 + 10,2,20,6,3,1,14,2,24,6, // 2015 + 4,3,17,1,28,5,8,3,20,4, // 2020 + 1,3,12,2,22,6,2,3,14,2, // 2025 + 26,4,6,3,17,2,0,4,10,3, // 2030 + 20,6,1,2,14,1,24,6,5,2, // 2035 + 15,3,28,4,9,2,19,6,1,1, // 2040 + 12,3,23,5,3,3,15,1,27,5, // 2045 + 7,3,17,3,29,4,11,2,21,6, // 2050 + 1,3,12,2,25,4,5,3,16,2, // 2055 + 28,4,9,3,19,6,30,2,12,1, // 2060 + 23,6,4,2,14,3,26,4,8,2, // 2065 + 18,3,0,4,10,3,22,5,2,3, // 2070 + 14,1,25,5,6,3,16,3,28,4, // 2075 + 9,2,20,6,30,3,11,2,23,4, // 2080 + 4,3,15,2,27,4,7,3,19,2, // 2085 + 29,6,11,1,21,6,3,2,13,3, // 2090 + 25,4,6,2,17,3,27,6,9,1, // 2095 + 20,5,30,3,10,3,22,4,3,2, // 2100 + 14,3,24,6,5,2,17,1,28,6, // 2105 + 9,2,21,4,1,3,13,2,23,6, // 2110 + 5,1,16,2,27,6,7,3,19,4, // 2115 + 30,2,11,3,23,4,3,3,14,2, // 2120 + 25,6,5,3,16,2,28,4,9,3, // 2125 + 21,4,2,2,12,3,23,6,4,2, // 2130 + 16,1,26,6,8,2,20,4,30,3, // 2135 + 11,2,22,6,4,1,14,3,25,5, // 2140 + 6,3,18,1,29,5,9,3,22,4, // 2145 + 2,3,13,2,23,6,4,3,15,2, // 2150 + 27,4,7,3,20,4,1,2,11,3, // 2155 + 21,6,3,2,15,1,25,6,6,2, // 2160 + 17,3,29,4,10,2,20,6,3,1, // 2165 + 13,3,24,5,4,3,17,1,28,5, // 2170 + 8,3,18,6,1,1,12,2,22,6, // 2175 + 2,3,14,2,26,4,6,3,17,2, // 2180 + 28,6,10,1,20,6,1,2,12,3, // 2185 + 24,4,5,2,15,3,28,4,9,2, // 2190 + 19,6,33,3,12,1,23,5,3,3, // 2195 + 13,3,25,4,6,2,16,3,26,6, // 2200 + 8,2,20,4,30,3,11,2,24,4, // 2205 + 4,3,15,2,25,6,8,1,18,6, // 2210 + 33,2,9,3,22,4,3,2,13,3, // 2215 + 25,4,6,3,17,2,27,6,9,1, // 2220 + 21,5,1,3,11,3,23,4,5,2, // 2225 + 15,3,25,6,6,2,19,4,33,3, // 2230 + 10,2,22,4,3,3,14,2,24,6, // 2235 + 6,1 // 2240 (Hebrew year: 6000) + }; + + private const int MaxMonthPlusOne = 14; + + // + // The lunar calendar has 6 different variations of month lengths + // within a year. + // + private static readonly byte[] s_lunarMonthLen = { + 0,00,00,00,00,00,00,00,00,00,00,00,00,0, + 0,30,29,29,29,30,29,30,29,30,29,30,29,0, // 3 common year variations + 0,30,29,30,29,30,29,30,29,30,29,30,29,0, + 0,30,30,30,29,30,29,30,29,30,29,30,29,0, + 0,30,29,29,29,30,30,29,30,29,30,29,30,29, // 3 leap year variations + 0,30,29,30,29,30,30,29,30,29,30,29,30,29, + 0,30,30,30,29,30,30,29,30,29,30,29,30,29 + }; + + internal static readonly DateTime calendarMinValue = new DateTime(1583, 1, 1); + // Gregorian 2239/9/29 = Hebrew 5999/13/29 (last day in Hebrew year 5999). + // We can only format/parse Hebrew numbers up to 999, so we limit the max range to Hebrew year 5999. + internal static readonly DateTime calendarMaxValue = new DateTime((new DateTime(2239, 9, 29, 23, 59, 59, 999)).Ticks + 9999); + + + + public override DateTime MinSupportedDateTime + { + get + { + return (calendarMinValue); + } + } + + + + public override DateTime MaxSupportedDateTime + { + get + { + return (calendarMaxValue); + } + } + + public override CalendarAlgorithmType AlgorithmType + { + get + { + return CalendarAlgorithmType.LunisolarCalendar; + } + } + + public HebrewCalendar() + { + } + + internal override CalendarId ID + { + get + { + return (CalendarId.HEBREW); + } + } + + + /*=================================CheckHebrewYearValue========================== + **Action: Check if the Hebrew year value is supported in this class. + **Returns: None. + **Arguments: y Hebrew year value + ** ear Hebrew era value + **Exceptions: ArgumentOutOfRange_Range if the year value is not supported. + **Note: + ** We use a table for the Hebrew calendar calculation, so the year supported is limited. + ============================================================================*/ + + private static void CheckHebrewYearValue(int y, int era, String varName) + { + CheckEraRange(era); + if (y > MaxHebrewYear || y < MinHebrewYear) + { + throw new ArgumentOutOfRangeException( + varName, + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + MinHebrewYear, + MaxHebrewYear)); + } + } + + /*=================================CheckHebrewMonthValue========================== + **Action: Check if the Hebrew month value is valid. + **Returns: None. + **Arguments: year Hebrew year value + ** month Hebrew month value + **Exceptions: ArgumentOutOfRange_Range if the month value is not valid. + **Note: + ** Call CheckHebrewYearValue() before calling this to verify the year value is supported. + ============================================================================*/ + + private void CheckHebrewMonthValue(int year, int month, int era) + { + int monthsInYear = GetMonthsInYear(year, era); + if (month < 1 || month > monthsInYear) + { + throw new ArgumentOutOfRangeException( + nameof(month), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 1, + monthsInYear)); + } + } + + /*=================================CheckHebrewDayValue========================== + **Action: Check if the Hebrew day value is valid. + **Returns: None. + **Arguments: year Hebrew year value + ** month Hebrew month value + ** day Hebrew day value. + **Exceptions: ArgumentOutOfRange_Range if the day value is not valid. + **Note: + ** Call CheckHebrewYearValue()/CheckHebrewMonthValue() before calling this to verify the year/month values are valid. + ============================================================================*/ + + private void CheckHebrewDayValue(int year, int month, int day, int era) + { + int daysInMonth = GetDaysInMonth(year, month, era); + if (day < 1 || day > daysInMonth) + { + throw new ArgumentOutOfRangeException( + nameof(day), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 1, + daysInMonth)); + } + } + + internal static void CheckEraRange(int era) + { + if (era != CurrentEra && era != HebrewEra) + { + throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue); + } + } + + private static void CheckTicksRange(long ticks) + { + if (ticks < calendarMinValue.Ticks || ticks > calendarMaxValue.Ticks) + { + throw new ArgumentOutOfRangeException( + "time", + // Print out the date in Gregorian using InvariantCulture since the DateTime is based on GreograinCalendar. + String.Format( + CultureInfo.InvariantCulture, + SR.ArgumentOutOfRange_CalendarRange, + calendarMinValue, + calendarMaxValue)); + } + } + + internal static int GetResult(__DateBuffer result, int part) + { + switch (part) + { + case DatePartYear: + return (result.year); + case DatePartMonth: + return (result.month); + case DatePartDay: + return (result.day); + } + + throw new InvalidOperationException(SR.InvalidOperation_DateTimeParsing); + } + + /*=================================GetLunarMonthDay========================== + **Action: Using the Hebrew table (HebrewTable) to get the Hebrew month/day value for Gregorian January 1st + ** in a given Gregorian year. + ** Greogrian January 1st falls usually in Tevet (4th month). Tevet has always 29 days. + ** That's why, there no nead to specify the lunar month in the table. There are exceptions, and these + ** are coded by giving numbers above 29 and below 1. + ** Actual decoding is takenig place in the switch statement below. + **Returns: + ** The Hebrew year type. The value is from 1 to 6. + ** normal years : 1 = 353 days 2 = 354 days 3 = 355 days. + ** Leap years : 4 = 383 5 384 6 = 385 days. + **Arguments: + ** gregorianYear The year value in Gregorian calendar. The value should be between 1500 and 2239. + ** lunarDate Object to take the result of the Hebrew year/month/day. + **Exceptions: + ============================================================================*/ + + internal static int GetLunarMonthDay(int gregorianYear, __DateBuffer lunarDate) + { + // + // Get the offset into the LunarMonthLen array and the lunar day + // for January 1st. + // + int index = gregorianYear - FirstGregorianTableYear; + if (index < 0 || index > TABLESIZE) + { + throw new ArgumentOutOfRangeException(nameof(gregorianYear)); + } + + index *= 2; + lunarDate.day = s_hebrewTable[index]; + + // Get the type of the year. The value is from 1 to 6 + int LunarYearType = s_hebrewTable[index + 1]; + + // + // Get the Lunar Month. + // + switch (lunarDate.day) + { + case (0): // 1/1 is on Shvat 1 + lunarDate.month = 5; + lunarDate.day = 1; + break; + case (30): // 1/1 is on Kislev 30 + lunarDate.month = 3; + break; + case (31): // 1/1 is on Shvat 2 + lunarDate.month = 5; + lunarDate.day = 2; + break; + case (32): // 1/1 is on Shvat 3 + lunarDate.month = 5; + lunarDate.day = 3; + break; + case (33): // 1/1 is on Kislev 29 + lunarDate.month = 3; + lunarDate.day = 29; + break; + default: // 1/1 is on Tevet (This is the general case) + lunarDate.month = 4; + break; + } + return (LunarYearType); + } + + // Returns a given date part of this DateTime. This method is used + // to compute the year, day-of-year, month, or day part. + + internal virtual int GetDatePart(long ticks, int part) + { + // The Gregorian year, month, day value for ticks. + int gregorianYear, gregorianMonth, gregorianDay; + int hebrewYearType; // lunar year type + long AbsoluteDate; // absolute date - absolute date 1/1/1600 + + // + // Make sure we have a valid Gregorian date that will fit into our + // Hebrew conversion limits. + // + CheckTicksRange(ticks); + + DateTime time = new DateTime(ticks); + + // + // Save the Gregorian date values. + // + gregorianYear = time.Year; + gregorianMonth = time.Month; + gregorianDay = time.Day; + + __DateBuffer lunarDate = new __DateBuffer(); // lunar month and day for Jan 1 + + // From the table looking-up value of HebrewTable[index] (stored in lunarDate.day), we get the the + // lunar month and lunar day where the Gregorian date 1/1 falls. + lunarDate.year = gregorianYear + HebrewYearOf1AD; + hebrewYearType = GetLunarMonthDay(gregorianYear, lunarDate); + + // This is the buffer used to store the result Hebrew date. + __DateBuffer result = new __DateBuffer(); + + // + // Store the values for the start of the new year - 1/1. + // + result.year = lunarDate.year; + result.month = lunarDate.month; + result.day = lunarDate.day; + + // + // Get the absolute date from 1/1/1600. + // + AbsoluteDate = GregorianCalendar.GetAbsoluteDate(gregorianYear, gregorianMonth, gregorianDay); + + // + // If the requested date was 1/1, then we're done. + // + if ((gregorianMonth == 1) && (gregorianDay == 1)) + { + return (GetResult(result, part)); + } + + // + // Calculate the number of days between 1/1 and the requested date. + // + long NumDays; // number of days since 1/1 + NumDays = AbsoluteDate - GregorianCalendar.GetAbsoluteDate(gregorianYear, 1, 1); + + // + // If the requested date is within the current lunar month, then + // we're done. + // + if ((NumDays + (long)lunarDate.day) <= (long)(s_lunarMonthLen[hebrewYearType * MaxMonthPlusOne + lunarDate.month])) + { + result.day += (int)NumDays; + return (GetResult(result, part)); + } + + // + // Adjust for the current partial month. + // + result.month++; + result.day = 1; + + // + // Adjust the Lunar Month and Year (if necessary) based on the number + // of days between 1/1 and the requested date. + // + // Assumes Jan 1 can never translate to the last Lunar month, which + // is true. + // + NumDays -= (long)(s_lunarMonthLen[hebrewYearType * MaxMonthPlusOne + lunarDate.month] - lunarDate.day); + Debug.Assert(NumDays >= 1, "NumDays >= 1"); + + // If NumDays is 1, then we are done. Otherwise, find the correct Hebrew month + // and day. + if (NumDays > 1) + { + // + // See if we're on the correct Lunar month. + // + while (NumDays > (long)(s_lunarMonthLen[hebrewYearType * MaxMonthPlusOne + result.month])) + { + // + // Adjust the number of days and move to the next month. + // + NumDays -= (long)(s_lunarMonthLen[hebrewYearType * MaxMonthPlusOne + result.month++]); + + // + // See if we need to adjust the Year. + // Must handle both 12 and 13 month years. + // + if ((result.month > 13) || (s_lunarMonthLen[hebrewYearType * MaxMonthPlusOne + result.month] == 0)) + { + // + // Adjust the Year. + // + result.year++; + hebrewYearType = s_hebrewTable[(gregorianYear + 1 - FirstGregorianTableYear) * 2 + 1]; + + // + // Adjust the Month. + // + result.month = 1; + } + } + // + // Found the right Lunar month. + // + result.day += (int)(NumDays - 1); + } + return (GetResult(result, part)); + } + + // Returns the DateTime resulting from adding the given number of + // months to the specified DateTime. The result is computed by incrementing + // (or decrementing) the year and month parts of the specified DateTime by + // value months, and, if required, adjusting the day part of the + // resulting date downwards to the last day of the resulting month in the + // resulting year. The time-of-day part of the result is the same as the + // time-of-day part of the specified DateTime. + // + // In more precise terms, considering the specified DateTime to be of the + // form y / m / d + t, where y is the + // year, m is the month, d is the day, and t is the + // time-of-day, the result is y1 / m1 / d1 + t, + // where y1 and m1 are computed by adding value months + // to y and m, and d1 is the largest value less than + // or equal to d that denotes a valid day in month m1 of year + // y1. + // + + public override DateTime AddMonths(DateTime time, int months) + { + try + { + int y = GetDatePart(time.Ticks, DatePartYear); + int m = GetDatePart(time.Ticks, DatePartMonth); + int d = GetDatePart(time.Ticks, DatePartDay); + + + int monthsInYear; + int i; + if (months >= 0) + { + i = m + months; + while (i > (monthsInYear = GetMonthsInYear(y, CurrentEra))) + { + y++; + i -= monthsInYear; + } + } + else + { + if ((i = m + months) <= 0) + { + months = -months; + months -= m; + y--; + + while (months > (monthsInYear = GetMonthsInYear(y, CurrentEra))) + { + y--; + months -= monthsInYear; + } + monthsInYear = GetMonthsInYear(y, CurrentEra); + i = monthsInYear - months; + } + } + + int days = GetDaysInMonth(y, i); + if (d > days) + { + d = days; + } + return (new DateTime(ToDateTime(y, i, d, 0, 0, 0, 0).Ticks + (time.Ticks % TicksPerDay))); + } + // We expect ArgumentException and ArgumentOutOfRangeException (which is subclass of ArgumentException) + // If exception is thrown in the calls above, we are out of the supported range of this calendar. + catch (ArgumentException) + { + throw new ArgumentOutOfRangeException( + nameof(months), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_AddValue)); + } + } + + // Returns the DateTime resulting from adding the given number of + // years to the specified DateTime. The result is computed by incrementing + // (or decrementing) the year part of the specified DateTime by value + // years. If the month and day of the specified DateTime is 2/29, and if the + // resulting year is not a leap year, the month and day of the resulting + // DateTime becomes 2/28. Otherwise, the month, day, and time-of-day + // parts of the result are the same as those of the specified DateTime. + // + + public override DateTime AddYears(DateTime time, int years) + { + int y = GetDatePart(time.Ticks, DatePartYear); + int m = GetDatePart(time.Ticks, DatePartMonth); + int d = GetDatePart(time.Ticks, DatePartDay); + + y += years; + CheckHebrewYearValue(y, Calendar.CurrentEra, nameof(years)); + + int months = GetMonthsInYear(y, CurrentEra); + if (m > months) + { + m = months; + } + + int days = GetDaysInMonth(y, m); + if (d > days) + { + d = days; + } + + long ticks = ToDateTime(y, m, d, 0, 0, 0, 0).Ticks + (time.Ticks % TicksPerDay); + Calendar.CheckAddResult(ticks, MinSupportedDateTime, MaxSupportedDateTime); + return (new DateTime(ticks)); + } + + // Returns the day-of-month part of the specified DateTime. The returned + // value is an integer between 1 and 31. + // + + public override int GetDayOfMonth(DateTime time) + { + return (GetDatePart(time.Ticks, DatePartDay)); + } + + // Returns the day-of-week part of the specified DateTime. The returned value + // is an integer between 0 and 6, where 0 indicates Sunday, 1 indicates + // Monday, 2 indicates Tuesday, 3 indicates Wednesday, 4 indicates + // Thursday, 5 indicates Friday, and 6 indicates Saturday. + // + + public override DayOfWeek GetDayOfWeek(DateTime time) + { + // If we calculate back, the Hebrew day of week for Gregorian 0001/1/1 is Monday (1). + // Therfore, the fomula is: + return ((DayOfWeek)((int)(time.Ticks / TicksPerDay + 1) % 7)); + } + + internal static int GetHebrewYearType(int year, int era) + { + CheckHebrewYearValue(year, era, nameof(year)); + // The HebrewTable is indexed by Gregorian year and starts from FirstGregorianYear. + // So we need to convert year (Hebrew year value) to Gregorian Year below. + return (s_hebrewTable[(year - HebrewYearOf1AD - FirstGregorianTableYear) * 2 + 1]); + } + + // Returns the day-of-year part of the specified DateTime. The returned value + // is an integer between 1 and 366. + // + + public override int GetDayOfYear(DateTime time) + { + // Get Hebrew year value of the specified time. + int year = GetYear(time); + DateTime beginOfYearDate; + if (year == 5343) + { + // Gregorian 1583/01/01 corresponds to Hebrew 5343/04/07 (MinSupportedDateTime) + // To figure out the Gregorian date associated with Hebrew 5343/01/01, we need to + // count the days from 5343/01/01 to 5343/04/07 and subtract that from Gregorian + // 1583/01/01. + // 1. Tishri (30 days) + // 2. Heshvan (30 days since 5343 has 355 days) + // 3. Kislev (30 days since 5343 has 355 days) + // 96 days to get from 5343/01/01 to 5343/04/07 + // Gregorian 1583/01/01 - 96 days = 1582/9/27 + + // the beginning of Hebrew year 5343 corresponds to Gregorian September 27, 1582. + beginOfYearDate = new DateTime(1582, 9, 27); + } + else + { + // following line will fail when year is 5343 (first supported year) + beginOfYearDate = ToDateTime(year, 1, 1, 0, 0, 0, 0, CurrentEra); + } + return ((int)((time.Ticks - beginOfYearDate.Ticks) / TicksPerDay) + 1); + } + + // Returns the number of days in the month given by the year and + // month arguments. + // + + public override int GetDaysInMonth(int year, int month, int era) + { + CheckEraRange(era); + int hebrewYearType = GetHebrewYearType(year, era); + CheckHebrewMonthValue(year, month, era); + + Debug.Assert(hebrewYearType >= 1 && hebrewYearType <= 6, + "hebrewYearType should be from 1 to 6, but now hebrewYearType = " + hebrewYearType + " for hebrew year " + year); + int monthDays = s_lunarMonthLen[hebrewYearType * MaxMonthPlusOne + month]; + if (monthDays == 0) + { + throw new ArgumentOutOfRangeException(nameof(month), SR.ArgumentOutOfRange_Month); + } + return (monthDays); + } + + // Returns the number of days in the year given by the year argument for the current era. + // + + public override int GetDaysInYear(int year, int era) + { + CheckEraRange(era); + // normal years : 1 = 353 days 2 = 354 days 3 = 355 days. + // Leap years : 4 = 383 5 384 6 = 385 days. + + // LunarYearType is from 1 to 6 + int LunarYearType = GetHebrewYearType(year, era); + if (LunarYearType < 4) + { + // common year: LunarYearType = 1, 2, 3 + return (352 + LunarYearType); + } + return (382 + (LunarYearType - 3)); + } + + // Returns the era for the specified DateTime value. + + public override int GetEra(DateTime time) + { + return (HebrewEra); + } + + + public override int[] Eras + { + get + { + return (new int[] { HebrewEra }); + } + } + + // Returns the month part of the specified DateTime. The returned value is an + // integer between 1 and 12. + // + + public override int GetMonth(DateTime time) + { + return (GetDatePart(time.Ticks, DatePartMonth)); + } + + // Returns the number of months in the specified year and era. + + public override int GetMonthsInYear(int year, int era) + { + return (IsLeapYear(year, era) ? 13 : 12); + } + + // Returns the year part of the specified DateTime. The returned value is an + // integer between 1 and 9999. + // + + public override int GetYear(DateTime time) + { + return (GetDatePart(time.Ticks, DatePartYear)); + } + + // Checks whether a given day in the specified era is a leap day. This method returns true if + // the date is a leap day, or false if not. + // + + public override bool IsLeapDay(int year, int month, int day, int era) + { + if (IsLeapMonth(year, month, era)) + { + // Every day in a leap month is a leap day. + CheckHebrewDayValue(year, month, day, era); + return (true); + } + else if (IsLeapYear(year, Calendar.CurrentEra)) + { + // There is an additional day in the 6th month in the leap year (the extra day is the 30th day in the 6th month), + // so we should return true for 6/30 if that's in a leap year. + if (month == 6 && day == 30) + { + return (true); + } + } + CheckHebrewDayValue(year, month, day, era); + return (false); + } + + // Returns the leap month in a calendar year of the specified era. This method returns 0 + // if this calendar does not have leap month, or this year is not a leap year. + // + + + public override int GetLeapMonth(int year, int era) + { + // Year/era values are checked in IsLeapYear(). + if (IsLeapYear(year, era)) + { + // The 7th month in a leap year is a leap month. + return (7); + } + return (0); + } + + // Checks whether a given month in the specified era is a leap month. This method returns true if + // month is a leap month, or false if not. + // + + public override bool IsLeapMonth(int year, int month, int era) + { + // Year/era values are checked in IsLeapYear(). + bool isLeapYear = IsLeapYear(year, era); + CheckHebrewMonthValue(year, month, era); + // The 7th month in a leap year is a leap month. + if (isLeapYear) + { + if (month == 7) + { + return (true); + } + } + return (false); + } + + // Checks whether a given year in the specified era is a leap year. This method returns true if + // year is a leap year, or false if not. + // + + public override bool IsLeapYear(int year, int era) + { + CheckHebrewYearValue(year, era, nameof(year)); + return (((7 * (long)year + 1) % 19) < 7); + } + + // (month1, day1) - (month2, day2) + private static int GetDayDifference(int lunarYearType, int month1, int day1, int month2, int day2) + { + if (month1 == month2) + { + return (day1 - day2); + } + + // Make sure that (month1, day1) < (month2, day2) + bool swap = (month1 > month2); + if (swap) + { + // (month1, day1) < (month2, day2). Swap the values. + // The result will be a negative number. + int tempMonth, tempDay; + tempMonth = month1; tempDay = day1; + month1 = month2; day1 = day2; + month2 = tempMonth; day2 = tempDay; + } + + // Get the number of days from (month1,day1) to (month1, end of month1) + int days = s_lunarMonthLen[lunarYearType * MaxMonthPlusOne + month1] - day1; + + // Move to next month. + month1++; + + // Add up the days. + while (month1 < month2) + { + days += s_lunarMonthLen[lunarYearType * MaxMonthPlusOne + month1++]; + } + days += day2; + + return (swap ? days : -days); + } + + /*=================================HebrewToGregorian========================== + **Action: Convert Hebrew date to Gregorian date. + **Returns: + **Arguments: + **Exceptions: + ** The algorithm is like this: + ** The hebrew year has an offset to the Gregorian year, so we can guess the Gregorian year for + ** the specified Hebrew year. That is, GreogrianYear = HebrewYear - FirstHebrewYearOf1AD. + ** + ** From the Gregorian year and HebrewTable, we can get the Hebrew month/day value + ** of the Gregorian date January 1st. Let's call this month/day value [hebrewDateForJan1] + ** + ** If the requested Hebrew month/day is less than [hebrewDateForJan1], we know the result + ** Gregorian date falls in previous year. So we decrease the Gregorian year value, and + ** retrieve the Hebrew month/day value of the Gregorian date january 1st again. + ** + ** Now, we get the answer of the Gregorian year. + ** + ** The next step is to get the number of days between the requested Hebrew month/day + ** and [hebrewDateForJan1]. When we get that, we can create the DateTime by adding/subtracting + ** the ticks value of the number of days. + ** + ============================================================================*/ + + + private static DateTime HebrewToGregorian(int hebrewYear, int hebrewMonth, int hebrewDay, int hour, int minute, int second, int millisecond) + { + // Get the rough Gregorian year for the specified hebrewYear. + // + int gregorianYear = hebrewYear - HebrewYearOf1AD; + + __DateBuffer hebrewDateOfJan1 = new __DateBuffer(); // year value is unused. + int lunarYearType = GetLunarMonthDay(gregorianYear, hebrewDateOfJan1); + + if ((hebrewMonth == hebrewDateOfJan1.month) && (hebrewDay == hebrewDateOfJan1.day)) + { + return (new DateTime(gregorianYear, 1, 1, hour, minute, second, millisecond)); + } + + int days = GetDayDifference(lunarYearType, hebrewMonth, hebrewDay, hebrewDateOfJan1.month, hebrewDateOfJan1.day); + + DateTime gregorianNewYear = new DateTime(gregorianYear, 1, 1); + return (new DateTime(gregorianNewYear.Ticks + days * TicksPerDay + + TimeToTicks(hour, minute, second, millisecond))); + } + + // Returns the date and time converted to a DateTime value. Throws an exception if the n-tuple is invalid. + // + + public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era) + { + CheckHebrewYearValue(year, era, nameof(year)); + CheckHebrewMonthValue(year, month, era); + CheckHebrewDayValue(year, month, day, era); + DateTime dt = HebrewToGregorian(year, month, day, hour, minute, second, millisecond); + CheckTicksRange(dt.Ticks); + return (dt); + } + + private const int DEFAULT_TWO_DIGIT_YEAR_MAX = 5790; + + + public override int TwoDigitYearMax + { + get + { + if (twoDigitYearMax == -1) + { + twoDigitYearMax = GetSystemTwoDigitYearSetting(ID, DEFAULT_TWO_DIGIT_YEAR_MAX); + } + return (twoDigitYearMax); + } + + set + { + VerifyWritable(); + if (value == 99) + { + // Do nothing here. Year 99 is allowed so that TwoDitYearMax is disabled. + } + else + { + CheckHebrewYearValue(value, HebrewEra, nameof(value)); + } + twoDigitYearMax = value; + } + } + + + public override int ToFourDigitYear(int year) + { + if (year < 0) + { + throw new ArgumentOutOfRangeException(nameof(year), + SR.ArgumentOutOfRange_NeedNonNegNum); + } + Contract.EndContractBlock(); + + if (year < 100) + { + return (base.ToFourDigitYear(year)); + } + + if (year > MaxHebrewYear || year < MinHebrewYear) + { + throw new ArgumentOutOfRangeException( + nameof(year), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + MinHebrewYear, + MaxHebrewYear)); + } + return (year); + } + + internal class __DateBuffer + { + internal int year; + internal int month; + internal int day; + } + } +} + diff --git a/src/mscorlib/shared/System/Globalization/HebrewNumber.cs b/src/mscorlib/shared/System/Globalization/HebrewNumber.cs new file mode 100644 index 0000000000..1e8fff2bcb --- /dev/null +++ b/src/mscorlib/shared/System/Globalization/HebrewNumber.cs @@ -0,0 +1,457 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Text; +using System.Diagnostics; + +namespace System.Globalization +{ + //////////////////////////////////////////////////////////////////////////// + // + // Used in HebrewNumber.ParseByChar to maintain the context information ( + // the state in the state machine and current Hebrew number values, etc.) + // when parsing Hebrew number character by character. + // + //////////////////////////////////////////////////////////////////////////// + + internal struct HebrewNumberParsingContext + { + // The current state of the state machine for parsing Hebrew numbers. + internal HebrewNumber.HS state; + // The current value of the Hebrew number. + // The final value is determined when state is FoundEndOfHebrewNumber. + internal int result; + + public HebrewNumberParsingContext(int result) + { + // Set the start state of the state machine for parsing Hebrew numbers. + state = HebrewNumber.HS.Start; + this.result = result; + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Please see ParseByChar() for comments about different states defined here. + // + //////////////////////////////////////////////////////////////////////////// + + internal enum HebrewNumberParsingState + { + InvalidHebrewNumber, + NotHebrewDigit, + FoundEndOfHebrewNumber, + ContinueParsing, + } + + //////////////////////////////////////////////////////////////////////////// + // + // class HebrewNumber + // + // Provides static methods for formatting integer values into + // Hebrew text and parsing Hebrew number text. + // + // Limitations: + // Parse can only handles value 1 ~ 999. + // ToString() can only handles 1 ~ 999. If value is greater than 5000, + // 5000 will be subtracted from the value. + // + //////////////////////////////////////////////////////////////////////////// + + internal class HebrewNumber + { + // This class contains only static methods. Add a private ctor so that + // compiler won't generate a default one for us. + private HebrewNumber() + { + } + + //////////////////////////////////////////////////////////////////////////// + // + // ToString + // + // Converts the given number to Hebrew letters according to the numeric + // value of each Hebrew letter. Basically, this converts the lunar year + // and the lunar month to letters. + // + // The character of a year is described by three letters of the Hebrew + // alphabet, the first and third giving, respectively, the days of the + // weeks on which the New Year occurs and Passover begins, while the + // second is the initial of the Hebrew word for defective, normal, or + // complete. + // + // Defective Year : Both Heshvan and Kislev are defective (353 or 383 days) + // Normal Year : Heshvan is defective, Kislev is full (354 or 384 days) + // Complete Year : Both Heshvan and Kislev are full (355 or 385 days) + // + //////////////////////////////////////////////////////////////////////////// + + internal static String ToString(int Number) + { + char cTens = '\x0'; + char cUnits; // tens and units chars + int Hundreds, Tens; // hundreds and tens values + StringBuilder szHebrew = new StringBuilder(); + + + // + // Adjust the number if greater than 5000. + // + if (Number > 5000) + { + Number -= 5000; + } + + Debug.Assert(Number > 0 && Number <= 999, "Number is out of range."); ; + + // + // Get the Hundreds. + // + Hundreds = Number / 100; + + if (Hundreds > 0) + { + Number -= Hundreds * 100; + // \x05e7 = 100 + // \x05e8 = 200 + // \x05e9 = 300 + // \x05ea = 400 + // If the number is greater than 400, use the multiples of 400. + for (int i = 0; i < (Hundreds / 4); i++) + { + szHebrew.Append('\x05ea'); + } + + int remains = Hundreds % 4; + if (remains > 0) + { + szHebrew.Append((char)((int)'\x05e6' + remains)); + } + } + + // + // Get the Tens. + // + Tens = Number / 10; + Number %= 10; + + switch (Tens) + { + case (0): + cTens = '\x0'; + break; + case (1): + cTens = '\x05d9'; // Hebrew Letter Yod + break; + case (2): + cTens = '\x05db'; // Hebrew Letter Kaf + break; + case (3): + cTens = '\x05dc'; // Hebrew Letter Lamed + break; + case (4): + cTens = '\x05de'; // Hebrew Letter Mem + break; + case (5): + cTens = '\x05e0'; // Hebrew Letter Nun + break; + case (6): + cTens = '\x05e1'; // Hebrew Letter Samekh + break; + case (7): + cTens = '\x05e2'; // Hebrew Letter Ayin + break; + case (8): + cTens = '\x05e4'; // Hebrew Letter Pe + break; + case (9): + cTens = '\x05e6'; // Hebrew Letter Tsadi + break; + } + + // + // Get the Units. + // + cUnits = (char)(Number > 0 ? ((int)'\x05d0' + Number - 1) : 0); + + if ((cUnits == '\x05d4') && // Hebrew Letter He (5) + (cTens == '\x05d9')) + { // Hebrew Letter Yod (10) + cUnits = '\x05d5'; // Hebrew Letter Vav (6) + cTens = '\x05d8'; // Hebrew Letter Tet (9) + } + + if ((cUnits == '\x05d5') && // Hebrew Letter Vav (6) + (cTens == '\x05d9')) + { // Hebrew Letter Yod (10) + cUnits = '\x05d6'; // Hebrew Letter Zayin (7) + cTens = '\x05d8'; // Hebrew Letter Tet (9) + } + + // + // Copy the appropriate info to the given buffer. + // + + if (cTens != '\x0') + { + szHebrew.Append(cTens); + } + + if (cUnits != '\x0') + { + szHebrew.Append(cUnits); + } + + if (szHebrew.Length > 1) + { + szHebrew.Insert(szHebrew.Length - 1, '"'); + } + else + { + szHebrew.Append('\''); + } + + // + // Return success. + // + return (szHebrew.ToString()); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Token used to tokenize a Hebrew word into tokens so that we can use in the + // state machine. + // + //////////////////////////////////////////////////////////////////////////// + + private enum HebrewToken : short + { + Invalid = -1, + Digit400 = 0, + Digit200_300 = 1, + Digit100 = 2, + Digit10 = 3, // 10 ~ 90 + Digit1 = 4, // 1, 2, 3, 4, 5, 8, + Digit6_7 = 5, + Digit7 = 6, + Digit9 = 7, + SingleQuote = 8, + DoubleQuote = 9, + }; + + //////////////////////////////////////////////////////////////////////////// + // + // This class is used to map a token into its Hebrew digit value. + // + //////////////////////////////////////////////////////////////////////////// + + private struct HebrewValue + { + internal HebrewToken token; + internal short value; + internal HebrewValue(HebrewToken token, short value) + { + this.token = token; + this.value = value; + } + } + + // + // Map a Hebrew character from U+05D0 ~ U+05EA to its digit value. + // The value is -1 if the Hebrew character does not have a associated value. + // + private static readonly HebrewValue[] s_hebrewValues = { + new HebrewValue(HebrewToken.Digit1, 1) , // '\x05d0 + new HebrewValue(HebrewToken.Digit1, 2) , // '\x05d1 + new HebrewValue(HebrewToken.Digit1, 3) , // '\x05d2 + new HebrewValue(HebrewToken.Digit1, 4) , // '\x05d3 + new HebrewValue(HebrewToken.Digit1, 5) , // '\x05d4 + new HebrewValue(HebrewToken.Digit6_7,6) , // '\x05d5 + new HebrewValue(HebrewToken.Digit6_7,7) , // '\x05d6 + new HebrewValue(HebrewToken.Digit1, 8) , // '\x05d7 + new HebrewValue(HebrewToken.Digit9, 9) , // '\x05d8 + new HebrewValue(HebrewToken.Digit10, 10) , // '\x05d9; // Hebrew Letter Yod + new HebrewValue(HebrewToken.Invalid, -1) , // '\x05da; + new HebrewValue(HebrewToken.Digit10, 20) , // '\x05db; // Hebrew Letter Kaf + new HebrewValue(HebrewToken.Digit10, 30) , // '\x05dc; // Hebrew Letter Lamed + new HebrewValue(HebrewToken.Invalid, -1) , // '\x05dd; + new HebrewValue(HebrewToken.Digit10, 40) , // '\x05de; // Hebrew Letter Mem + new HebrewValue(HebrewToken.Invalid, -1) , // '\x05df; + new HebrewValue(HebrewToken.Digit10, 50) , // '\x05e0; // Hebrew Letter Nun + new HebrewValue(HebrewToken.Digit10, 60) , // '\x05e1; // Hebrew Letter Samekh + new HebrewValue(HebrewToken.Digit10, 70) , // '\x05e2; // Hebrew Letter Ayin + new HebrewValue(HebrewToken.Invalid, -1) , // '\x05e3; + new HebrewValue(HebrewToken.Digit10, 80) , // '\x05e4; // Hebrew Letter Pe + new HebrewValue(HebrewToken.Invalid, -1) , // '\x05e5; + new HebrewValue(HebrewToken.Digit10, 90) , // '\x05e6; // Hebrew Letter Tsadi + new HebrewValue(HebrewToken.Digit100, 100) , // '\x05e7; + new HebrewValue(HebrewToken.Digit200_300, 200) , // '\x05e8; + new HebrewValue(HebrewToken.Digit200_300, 300) , // '\x05e9; + new HebrewValue(HebrewToken.Digit400, 400) , // '\x05ea; + }; + + private const int minHebrewNumberCh = 0x05d0; + private static char s_maxHebrewNumberCh = (char)(minHebrewNumberCh + s_hebrewValues.Length - 1); + + //////////////////////////////////////////////////////////////////////////// + // + // Hebrew number parsing State + // The current state and the next token will lead to the next state in the state machine. + // DQ = Double Quote + // + //////////////////////////////////////////////////////////////////////////// + + internal enum HS : sbyte + { + _err = -1, // an error state + Start = 0, + S400 = 1, // a Hebrew digit 400 + S400_400 = 2, // Two Hebrew digit 400 + S400_X00 = 3, // Two Hebrew digit 400 and followed by 100 + S400_X0 = 4, // Hebrew digit 400 and followed by 10 ~ 90 + X00_DQ = 5, // A hundred number and followed by a double quote. + S400_X00_X0 = 6, + X0_DQ = 7, // A two-digit number and followed by a double quote. + X = 8, // A single digit Hebrew number. + X0 = 9, // A two-digit Hebrew number + X00 = 10, // A three-digit Hebrew number + S400_DQ = 11, // A Hebrew digit 400 and followed by a double quote. + S400_400_DQ = 12, + S400_400_100 = 13, + S9 = 14, // Hebrew digit 9 + X00_S9 = 15, // A hundered number and followed by a digit 9 + S9_DQ = 16, // Hebrew digit 9 and followed by a double quote + END = 100, // A terminial state is reached. + } + + // + // The state machine for Hebrew number pasing. + // + private static readonly HS[] s_numberPasingState = + { + // 400 300/200 100 90~10 8~1 6, 7, 9, ' " + /* 0 */ + HS.S400, HS.X00, HS.X00, HS.X0, HS.X, HS.X, HS.X, HS.S9, HS._err, HS._err, + /* 1: S400 */ + HS.S400_400, HS.S400_X00, HS.S400_X00, HS.S400_X0, HS._err, HS._err, HS._err, HS.X00_S9 ,HS.END, HS.S400_DQ, + /* 2: S400_400 */ + HS._err, HS._err, HS.S400_400_100,HS.S400_X0, HS._err, HS._err, HS._err, HS.X00_S9 ,HS._err, HS.S400_400_DQ, + /* 3: S400_X00 */ + HS._err, HS._err, HS._err, HS.S400_X00_X0, HS._err, HS._err, HS._err, HS.X00_S9 ,HS._err, HS.X00_DQ, + /* 4: S400_X0 */ + HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS.X0_DQ, + /* 5: X00_DQ */ + HS._err, HS._err, HS._err, HS.END, HS.END, HS.END, HS.END, HS.END, HS._err, HS._err, + /* 6: S400_X00_X0 */ + HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS.X0_DQ, + /* 7: X0_DQ */ + HS._err, HS._err, HS._err, HS._err, HS.END, HS.END, HS.END, HS.END, HS._err, HS._err, + /* 8: X */ + HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS.END, HS._err, + /* 9: X0 */ + HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS.END, HS.X0_DQ, + /* 10: X00 */ + HS._err, HS._err, HS._err, HS.S400_X0, HS._err, HS._err, HS._err, HS.X00_S9, HS.END, HS.X00_DQ, + /* 11: S400_DQ */ + HS.END, HS.END, HS.END, HS.END, HS.END, HS.END, HS.END, HS.END, HS._err, HS._err, + /* 12: S400_400_DQ*/ + HS._err, HS._err, HS.END, HS.END, HS.END, HS.END, HS.END, HS.END, HS._err, HS._err, + /* 13: S400_400_100*/ + HS._err, HS._err, HS._err, HS.S400_X00_X0, HS._err, HS._err, HS._err, HS.X00_S9, HS._err, HS.X00_DQ, + /* 14: S9 */ + HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS.END, HS.S9_DQ, + /* 15: X00_S9 */ + HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS.S9_DQ, + /* 16: S9_DQ */ + HS._err, HS._err, HS._err, HS._err, HS._err, HS.END, HS.END, HS._err, HS._err, HS._err + }; + + // Count of valid HebrewToken, column count in the NumberPasingState array + private const int HebrewTokenCount = 10; + + //////////////////////////////////////////////////////////////////////// + // + // Actions: + // Parse the Hebrew number by passing one character at a time. + // The state between characters are maintained at HebrewNumberPasingContext. + // Returns: + // Return a enum of HebrewNumberParsingState. + // NotHebrewDigit: The specified ch is not a valid Hebrew digit. + // InvalidHebrewNumber: After parsing the specified ch, it will lead into + // an invalid Hebrew number text. + // FoundEndOfHebrewNumber: A terminal state is reached. This means that + // we find a valid Hebrew number text after the specified ch is parsed. + // ContinueParsing: The specified ch is a valid Hebrew digit, and + // it will lead into a valid state in the state machine, we should + // continue to parse incoming characters. + // + //////////////////////////////////////////////////////////////////////// + + internal static HebrewNumberParsingState ParseByChar(char ch, ref HebrewNumberParsingContext context) + { + Debug.Assert(s_numberPasingState.Length == HebrewTokenCount * ((int)HS.S9_DQ + 1)); + + HebrewToken token; + if (ch == '\'') + { + token = HebrewToken.SingleQuote; + } + else if (ch == '\"') + { + token = HebrewToken.DoubleQuote; + } + else + { + int index = (int)ch - minHebrewNumberCh; + if (index >= 0 && index < s_hebrewValues.Length) + { + token = s_hebrewValues[index].token; + if (token == HebrewToken.Invalid) + { + return (HebrewNumberParsingState.NotHebrewDigit); + } + context.result += s_hebrewValues[index].value; + } + else + { + // Not in valid Hebrew digit range. + return (HebrewNumberParsingState.NotHebrewDigit); + } + } + context.state = s_numberPasingState[(int)context.state * (int)HebrewTokenCount + (int)token]; + if (context.state == HS._err) + { + // Invalid Hebrew state. This indicates an incorrect Hebrew number. + return (HebrewNumberParsingState.InvalidHebrewNumber); + } + if (context.state == HS.END) + { + // Reach a terminal state. + return (HebrewNumberParsingState.FoundEndOfHebrewNumber); + } + // We should continue to parse. + return (HebrewNumberParsingState.ContinueParsing); + } + + //////////////////////////////////////////////////////////////////////// + // + // Actions: + // Check if the ch is a valid Hebrew number digit. + // This function will return true if the specified char is a legal Hebrew + // digit character, single quote, or double quote. + // Returns: + // true if the specified character is a valid Hebrew number character. + // + //////////////////////////////////////////////////////////////////////// + + internal static bool IsDigit(char ch) + { + if (ch >= minHebrewNumberCh && ch <= s_maxHebrewNumberCh) + { + return (s_hebrewValues[ch - minHebrewNumberCh].value >= 0); + } + return (ch == '\'' || ch == '\"'); + } + } +} diff --git a/src/mscorlib/shared/System/Globalization/HijriCalendar.cs b/src/mscorlib/shared/System/Globalization/HijriCalendar.cs new file mode 100644 index 0000000000..cafde5fbb8 --- /dev/null +++ b/src/mscorlib/shared/System/Globalization/HijriCalendar.cs @@ -0,0 +1,677 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics.Contracts; + +namespace System.Globalization +{ + //////////////////////////////////////////////////////////////////////////// + // + // Rules for the Hijri calendar: + // - The Hijri calendar is a strictly Lunar calendar. + // - Days begin at sunset. + // - Islamic Year 1 (Muharram 1, 1 A.H.) is equivalent to absolute date + // 227015 (Friday, July 16, 622 C.E. - Julian). + // - Leap Years occur in the 2, 5, 7, 10, 13, 16, 18, 21, 24, 26, & 29th + // years of a 30-year cycle. Year = leap iff ((11y+14) mod 30 < 11). + // - There are 12 months which contain alternately 30 and 29 days. + // - The 12th month, Dhu al-Hijjah, contains 30 days instead of 29 days + // in a leap year. + // - Common years have 354 days. Leap years have 355 days. + // - There are 10,631 days in a 30-year cycle. + // - The Islamic months are: + // 1. Muharram (30 days) 7. Rajab (30 days) + // 2. Safar (29 days) 8. Sha'ban (29 days) + // 3. Rabi I (30 days) 9. Ramadan (30 days) + // 4. Rabi II (29 days) 10. Shawwal (29 days) + // 5. Jumada I (30 days) 11. Dhu al-Qada (30 days) + // 6. Jumada II (29 days) 12. Dhu al-Hijjah (29 days) {30} + // + // NOTENOTE + // The calculation of the HijriCalendar is based on the absolute date. And the + // absolute date means the number of days from January 1st, 1 A.D. + // Therefore, we do not support the days before the January 1st, 1 A.D. + // + //////////////////////////////////////////////////////////////////////////// + /* + ** Calendar support range: + ** Calendar Minimum Maximum + ** ========== ========== ========== + ** Gregorian 0622/07/18 9999/12/31 + ** Hijri 0001/01/01 9666/04/03 + */ + + [Serializable] + public partial class HijriCalendar : Calendar + { + public static readonly int HijriEra = 1; + + internal const int DatePartYear = 0; + internal const int DatePartDayOfYear = 1; + internal const int DatePartMonth = 2; + internal const int DatePartDay = 3; + + internal const int MinAdvancedHijri = -2; + internal const int MaxAdvancedHijri = 2; + + internal static readonly int[] HijriMonthDays = { 0, 30, 59, 89, 118, 148, 177, 207, 236, 266, 295, 325, 355 }; + + private int _hijriAdvance = Int32.MinValue; + + // DateTime.MaxValue = Hijri calendar (year:9666, month: 4, day: 3). + internal const int MaxCalendarYear = 9666; + internal const int MaxCalendarMonth = 4; + internal const int MaxCalendarDay = 3; + // Hijri calendar (year: 1, month: 1, day:1 ) = Gregorian (year: 622, month: 7, day: 18) + // This is the minimal Gregorian date that we support in the HijriCalendar. + internal static readonly DateTime calendarMinValue = new DateTime(622, 7, 18); + internal static readonly DateTime calendarMaxValue = DateTime.MaxValue; + + + public override DateTime MinSupportedDateTime + { + get + { + return (calendarMinValue); + } + } + + + public override DateTime MaxSupportedDateTime + { + get + { + return (calendarMaxValue); + } + } + + public override CalendarAlgorithmType AlgorithmType + { + get + { + return CalendarAlgorithmType.LunarCalendar; + } + } + + public HijriCalendar() + { + } + + internal override CalendarId ID + { + get + { + return CalendarId.HIJRI; + } + } + + protected override int DaysInYearBeforeMinSupportedYear + { + get + { + // the year before the 1st year of the cycle would have been the 30th year + // of the previous cycle which is not a leap year. Common years have 354 days. + return 354; + } + } + + + + /*=================================GetAbsoluteDateHijri========================== + **Action: Gets the Absolute date for the given Hijri date. The absolute date means + ** the number of days from January 1st, 1 A.D. + **Returns: + **Arguments: + **Exceptions: + ============================================================================*/ + + private long GetAbsoluteDateHijri(int y, int m, int d) + { + return (long)(DaysUpToHijriYear(y) + HijriMonthDays[m - 1] + d - 1 - HijriAdjustment); + } + + /*=================================DaysUpToHijriYear========================== + **Action: Gets the total number of days (absolute date) up to the given Hijri Year. + ** The absolute date means the number of days from January 1st, 1 A.D. + **Returns: Gets the total number of days (absolute date) up to the given Hijri Year. + **Arguments: HijriYear year value in Hijri calendar. + **Exceptions: None + **Notes: + ============================================================================*/ + + private long DaysUpToHijriYear(int HijriYear) + { + long NumDays; // number of absolute days + int NumYear30; // number of years up to current 30 year cycle + int NumYearsLeft; // number of years into 30 year cycle + + // + // Compute the number of years up to the current 30 year cycle. + // + NumYear30 = ((HijriYear - 1) / 30) * 30; + + // + // Compute the number of years left. This is the number of years + // into the 30 year cycle for the given year. + // + NumYearsLeft = HijriYear - NumYear30 - 1; + + // + // Compute the number of absolute days up to the given year. + // + NumDays = ((NumYear30 * 10631L) / 30L) + 227013L; + while (NumYearsLeft > 0) + { + // Common year is 354 days, and leap year is 355 days. + NumDays += 354 + (IsLeapYear(NumYearsLeft, CurrentEra) ? 1 : 0); + NumYearsLeft--; + } + + // + // Return the number of absolute days. + // + return (NumDays); + } + + public int HijriAdjustment + { + get + { + if (_hijriAdvance == Int32.MinValue) + { + // Never been set before. Use the system value from registry. + _hijriAdvance = GetHijriDateAdjustment(); + } + return (_hijriAdvance); + } + + set + { + // NOTE: Check the value of Min/MaxAdavncedHijri with Arabic speakers to see if the assumption is good. + if (value < MinAdvancedHijri || value > MaxAdvancedHijri) + { + throw new ArgumentOutOfRangeException( + "HijriAdjustment", + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Bounds_Lower_Upper, + MinAdvancedHijri, + MaxAdvancedHijri)); + } + Contract.EndContractBlock(); + VerifyWritable(); + + _hijriAdvance = value; + } + } + + internal static void CheckTicksRange(long ticks) + { + if (ticks < calendarMinValue.Ticks || ticks > calendarMaxValue.Ticks) + { + throw new ArgumentOutOfRangeException( + "time", + String.Format( + CultureInfo.InvariantCulture, + SR.ArgumentOutOfRange_CalendarRange, + calendarMinValue, + calendarMaxValue)); + } + } + + internal static void CheckEraRange(int era) + { + if (era != CurrentEra && era != HijriEra) + { + throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue); + } + } + + internal static void CheckYearRange(int year, int era) + { + CheckEraRange(era); + if (year < 1 || year > MaxCalendarYear) + { + throw new ArgumentOutOfRangeException( + nameof(year), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 1, + MaxCalendarYear)); + } + } + + internal static void CheckYearMonthRange(int year, int month, int era) + { + CheckYearRange(year, era); + if (year == MaxCalendarYear) + { + if (month > MaxCalendarMonth) + { + throw new ArgumentOutOfRangeException( + nameof(month), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 1, + MaxCalendarMonth)); + } + } + + if (month < 1 || month > 12) + { + throw new ArgumentOutOfRangeException(nameof(month), SR.ArgumentOutOfRange_Month); + } + } + + /*=================================GetDatePart========================== + **Action: Returns a given date part of this <i>DateTime</i>. This method is used + ** to compute the year, day-of-year, month, or day part. + **Returns: + **Arguments: + **Exceptions: ArgumentException if part is incorrect. + **Notes: + ** First, we get the absolute date (the number of days from January 1st, 1 A.C) for the given ticks. + ** Use the formula (((AbsoluteDate - 227013) * 30) / 10631) + 1, we can a rough value for the Hijri year. + ** In order to get the exact Hijri year, we compare the exact absolute date for HijriYear and (HijriYear + 1). + ** From here, we can get the correct Hijri year. + ============================================================================*/ + + internal virtual int GetDatePart(long ticks, int part) + { + int HijriYear; // Hijri year + int HijriMonth; // Hijri month + int HijriDay; // Hijri day + long NumDays; // The calculation buffer in number of days. + + CheckTicksRange(ticks); + + // + // Get the absolute date. The absolute date is the number of days from January 1st, 1 A.D. + // 1/1/0001 is absolute date 1. + // + NumDays = ticks / GregorianCalendar.TicksPerDay + 1; + + // + // See how much we need to backup or advance + // + NumDays += HijriAdjustment; + + // + // Calculate the appromixate Hijri Year from this magic formula. + // + HijriYear = (int)(((NumDays - 227013) * 30) / 10631) + 1; + + long daysToHijriYear = DaysUpToHijriYear(HijriYear); // The absoulte date for HijriYear + long daysOfHijriYear = GetDaysInYear(HijriYear, CurrentEra); // The number of days for (HijriYear+1) year. + + if (NumDays < daysToHijriYear) + { + daysToHijriYear -= daysOfHijriYear; + HijriYear--; + } + else if (NumDays == daysToHijriYear) + { + HijriYear--; + daysToHijriYear -= GetDaysInYear(HijriYear, CurrentEra); + } + else + { + if (NumDays > daysToHijriYear + daysOfHijriYear) + { + daysToHijriYear += daysOfHijriYear; + HijriYear++; + } + } + if (part == DatePartYear) + { + return (HijriYear); + } + + // + // Calculate the Hijri Month. + // + + HijriMonth = 1; + NumDays -= daysToHijriYear; + + if (part == DatePartDayOfYear) + { + return ((int)NumDays); + } + + while ((HijriMonth <= 12) && (NumDays > HijriMonthDays[HijriMonth - 1])) + { + HijriMonth++; + } + HijriMonth--; + + if (part == DatePartMonth) + { + return (HijriMonth); + } + + // + // Calculate the Hijri Day. + // + HijriDay = (int)(NumDays - HijriMonthDays[HijriMonth - 1]); + + if (part == DatePartDay) + { + return (HijriDay); + } + // Incorrect part value. + throw new InvalidOperationException(SR.InvalidOperation_DateTimeParsing); + } + + // Returns the DateTime resulting from adding the given number of + // months to the specified DateTime. The result is computed by incrementing + // (or decrementing) the year and month parts of the specified DateTime by + // value months, and, if required, adjusting the day part of the + // resulting date downwards to the last day of the resulting month in the + // resulting year. The time-of-day part of the result is the same as the + // time-of-day part of the specified DateTime. + // + // In more precise terms, considering the specified DateTime to be of the + // form y / m / d + t, where y is the + // year, m is the month, d is the day, and t is the + // time-of-day, the result is y1 / m1 / d1 + t, + // where y1 and m1 are computed by adding value months + // to y and m, and d1 is the largest value less than + // or equal to d that denotes a valid day in month m1 of year + // y1. + // + + public override DateTime AddMonths(DateTime time, int months) + { + if (months < -120000 || months > 120000) + { + throw new ArgumentOutOfRangeException( + nameof(months), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + -120000, + 120000)); + } + Contract.EndContractBlock(); + // Get the date in Hijri calendar. + int y = GetDatePart(time.Ticks, DatePartYear); + int m = GetDatePart(time.Ticks, DatePartMonth); + int d = GetDatePart(time.Ticks, DatePartDay); + int i = m - 1 + months; + if (i >= 0) + { + m = i % 12 + 1; + y = y + i / 12; + } + else + { + m = 12 + (i + 1) % 12; + y = y + (i - 11) / 12; + } + int days = GetDaysInMonth(y, m); + if (d > days) + { + d = days; + } + long ticks = GetAbsoluteDateHijri(y, m, d) * TicksPerDay + (time.Ticks % TicksPerDay); + Calendar.CheckAddResult(ticks, MinSupportedDateTime, MaxSupportedDateTime); + return (new DateTime(ticks)); + } + + // Returns the DateTime resulting from adding the given number of + // years to the specified DateTime. The result is computed by incrementing + // (or decrementing) the year part of the specified DateTime by value + // years. If the month and day of the specified DateTime is 2/29, and if the + // resulting year is not a leap year, the month and day of the resulting + // DateTime becomes 2/28. Otherwise, the month, day, and time-of-day + // parts of the result are the same as those of the specified DateTime. + // + + public override DateTime AddYears(DateTime time, int years) + { + return (AddMonths(time, years * 12)); + } + + // Returns the day-of-month part of the specified DateTime. The returned + // value is an integer between 1 and 31. + // + + public override int GetDayOfMonth(DateTime time) + { + return (GetDatePart(time.Ticks, DatePartDay)); + } + + // Returns the day-of-week part of the specified DateTime. The returned value + // is an integer between 0 and 6, where 0 indicates Sunday, 1 indicates + // Monday, 2 indicates Tuesday, 3 indicates Wednesday, 4 indicates + // Thursday, 5 indicates Friday, and 6 indicates Saturday. + // + + public override DayOfWeek GetDayOfWeek(DateTime time) + { + return ((DayOfWeek)((int)(time.Ticks / TicksPerDay + 1) % 7)); + } + + // Returns the day-of-year part of the specified DateTime. The returned value + // is an integer between 1 and 366. + // + + public override int GetDayOfYear(DateTime time) + { + return (GetDatePart(time.Ticks, DatePartDayOfYear)); + } + + // Returns the number of days in the month given by the year and + // month arguments. + // + [Pure] + public override int GetDaysInMonth(int year, int month, int era) + { + CheckYearMonthRange(year, month, era); + if (month == 12) + { + // For the 12th month, leap year has 30 days, and common year has 29 days. + return (IsLeapYear(year, CurrentEra) ? 30 : 29); + } + // Other months contain 30 and 29 days alternatively. The 1st month has 30 days. + return (((month % 2) == 1) ? 30 : 29); + } + + // Returns the number of days in the year given by the year argument for the current era. + // + + public override int GetDaysInYear(int year, int era) + { + CheckYearRange(year, era); + // Common years have 354 days. Leap years have 355 days. + return (IsLeapYear(year, CurrentEra) ? 355 : 354); + } + + // Returns the era for the specified DateTime value. + + public override int GetEra(DateTime time) + { + CheckTicksRange(time.Ticks); + return (HijriEra); + } + + + public override int[] Eras + { + get + { + return (new int[] { HijriEra }); + } + } + + // Returns the month part of the specified DateTime. The returned value is an + // integer between 1 and 12. + // + + public override int GetMonth(DateTime time) + { + return (GetDatePart(time.Ticks, DatePartMonth)); + } + + // Returns the number of months in the specified year and era. + + public override int GetMonthsInYear(int year, int era) + { + CheckYearRange(year, era); + return (12); + } + + // Returns the year part of the specified DateTime. The returned value is an + // integer between 1 and MaxCalendarYear. + // + + public override int GetYear(DateTime time) + { + return (GetDatePart(time.Ticks, DatePartYear)); + } + + // Checks whether a given day in the specified era is a leap day. This method returns true if + // the date is a leap day, or false if not. + // + + public override bool IsLeapDay(int year, int month, int day, int era) + { + // The year/month/era value checking is done in GetDaysInMonth(). + int daysInMonth = GetDaysInMonth(year, month, era); + if (day < 1 || day > daysInMonth) + { + throw new ArgumentOutOfRangeException( + nameof(day), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Day, + daysInMonth, + month)); + } + return (IsLeapYear(year, era) && month == 12 && day == 30); + } + + // Returns the leap month in a calendar year of the specified era. This method returns 0 + // if this calendar does not have leap month, or this year is not a leap year. + // + + public override int GetLeapMonth(int year, int era) + { + CheckYearRange(year, era); + return (0); + } + + // Checks whether a given month in the specified era is a leap month. This method returns true if + // month is a leap month, or false if not. + // + + public override bool IsLeapMonth(int year, int month, int era) + { + CheckYearMonthRange(year, month, era); + return (false); + } + + // Checks whether a given year in the specified era is a leap year. This method returns true if + // year is a leap year, or false if not. + // + + public override bool IsLeapYear(int year, int era) + { + CheckYearRange(year, era); + return ((((year * 11) + 14) % 30) < 11); + } + + // Returns the date and time converted to a DateTime value. Throws an exception if the n-tuple is invalid. + // + + public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era) + { + // The year/month/era checking is done in GetDaysInMonth(). + int daysInMonth = GetDaysInMonth(year, month, era); + if (day < 1 || day > daysInMonth) + { + throw new ArgumentOutOfRangeException( + nameof(day), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Day, + daysInMonth, + month)); + } + + long lDate = GetAbsoluteDateHijri(year, month, day); + + if (lDate >= 0) + { + return (new DateTime(lDate * GregorianCalendar.TicksPerDay + TimeToTicks(hour, minute, second, millisecond))); + } + else + { + throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadYearMonthDay); + } + } + + private const int DEFAULT_TWO_DIGIT_YEAR_MAX = 1451; + + + public override int TwoDigitYearMax + { + get + { + if (twoDigitYearMax == -1) + { + twoDigitYearMax = GetSystemTwoDigitYearSetting(ID, DEFAULT_TWO_DIGIT_YEAR_MAX); + } + return (twoDigitYearMax); + } + + set + { + VerifyWritable(); + if (value < 99 || value > MaxCalendarYear) + { + throw new ArgumentOutOfRangeException( + nameof(value), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 99, + MaxCalendarYear)); + } + twoDigitYearMax = value; + } + } + + + public override int ToFourDigitYear(int year) + { + if (year < 0) + { + throw new ArgumentOutOfRangeException(nameof(year), + SR.ArgumentOutOfRange_NeedNonNegNum); + } + Contract.EndContractBlock(); + + if (year < 100) + { + return (base.ToFourDigitYear(year)); + } + + if (year > MaxCalendarYear) + { + throw new ArgumentOutOfRangeException( + nameof(year), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 1, + MaxCalendarYear)); + } + return (year); + } + } +} diff --git a/src/mscorlib/shared/System/Globalization/InternalGlobalizationHelper.cs b/src/mscorlib/shared/System/Globalization/InternalGlobalizationHelper.cs new file mode 100644 index 0000000000..f5eea1b629 --- /dev/null +++ b/src/mscorlib/shared/System/Globalization/InternalGlobalizationHelper.cs @@ -0,0 +1,48 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Globalization +{ + internal class InternalGlobalizationHelper + { + // Copied from the TimeSpan to be used inside the globalization code and avoid internal dependancy on TimeSpan class + internal static long TimeToTicks(int hour, int minute, int second) + { + // totalSeconds is bounded by 2^31 * 2^12 + 2^31 * 2^8 + 2^31, + // which is less than 2^44, meaning we won't overflow totalSeconds. + long totalSeconds = (long)hour * 3600 + (long)minute * 60 + (long)second; + if (totalSeconds > MaxSeconds || totalSeconds < MinSeconds) + throw new ArgumentOutOfRangeException(null, SR.Overflow_TimeSpanTooLong); + return totalSeconds * TicksPerSecond; + } + + + // + // Define needed constants so globalization code can be independant from any other types + // + + internal const long TicksPerMillisecond = 10000; + internal const long TicksPerTenthSecond = TicksPerMillisecond * 100; + internal const long TicksPerSecond = TicksPerMillisecond * 1000; // 10,000,000 + internal const long MaxSeconds = Int64.MaxValue / TicksPerSecond; + internal const long MinSeconds = Int64.MinValue / TicksPerSecond; + private const int DaysPerYear = 365; + private const int DaysPer4Years = DaysPerYear * 4 + 1; // 1461 + private const int DaysPer100Years = DaysPer4Years * 25 - 1; // 36524 + private const int DaysPer400Years = DaysPer100Years * 4 + 1; // 146097 + private const int DaysTo10000 = DaysPer400Years * 25 - 366; // 3652059 + private const long TicksPerMinute = TicksPerSecond * 60; + private const long TicksPerHour = TicksPerMinute * 60; + private const long TicksPerDay = TicksPerHour * 24; + internal const long MaxTicks = DaysTo10000 * TicksPerDay - 1; + internal const long MinTicks = 0; + internal const long MaxMilliSeconds = Int64.MaxValue / TicksPerMillisecond; + internal const long MinMilliSeconds = Int64.MinValue / TicksPerMillisecond; + + internal const int StringBuilderDefaultCapacity = 16; + + internal const Int64 MaxOffset = TimeSpan.TicksPerHour * 14; + internal const Int64 MinOffset = -MaxOffset; + } +} diff --git a/src/mscorlib/shared/System/Globalization/JapaneseCalendar.cs b/src/mscorlib/shared/System/Globalization/JapaneseCalendar.cs new file mode 100644 index 0000000000..4655e08a4e --- /dev/null +++ b/src/mscorlib/shared/System/Globalization/JapaneseCalendar.cs @@ -0,0 +1,409 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.Contracts; + +namespace System.Globalization +{ + /*=================================JapaneseCalendar========================== + ** + ** JapaneseCalendar is based on Gregorian calendar. The month and day values are the same as + ** Gregorian calendar. However, the year value is an offset to the Gregorian + ** year based on the era. + ** + ** This system is adopted by Emperor Meiji in 1868. The year value is counted based on the reign of an emperor, + ** and the era begins on the day an emperor ascends the throne and continues until his death. + ** The era changes at 12:00AM. + ** + ** For example, the current era is Heisei. It started on 1989/1/8 A.D. Therefore, Gregorian year 1989 is also Heisei 1st. + ** 1989/1/8 A.D. is also Heisei 1st 1/8. + ** + ** Any date in the year during which era is changed can be reckoned in either era. For example, + ** 1989/1/1 can be 1/1 Heisei 1st year or 1/1 Showa 64th year. + ** + ** Note: + ** The DateTime can be represented by the JapaneseCalendar are limited to two factors: + ** 1. The min value and max value of DateTime class. + ** 2. The available era information. + ** + ** Calendar support range: + ** Calendar Minimum Maximum + ** ========== ========== ========== + ** Gregorian 1868/09/08 9999/12/31 + ** Japanese Meiji 01/01 Heisei 8011/12/31 + ============================================================================*/ + + + [Serializable] + public partial class JapaneseCalendar : Calendar + { + internal static readonly DateTime calendarMinValue = new DateTime(1868, 9, 8); + + + public override DateTime MinSupportedDateTime + { + get + { + return (calendarMinValue); + } + } + + public override DateTime MaxSupportedDateTime + { + get + { + return (DateTime.MaxValue); + } + } + + public override CalendarAlgorithmType AlgorithmType + { + get + { + return CalendarAlgorithmType.SolarCalendar; + } + } + + // + // Using a field initializer rather than a static constructor so that the whole class can be lazy + // init. + internal static volatile EraInfo[] japaneseEraInfo; + + // + // Read our era info + // + // m_EraInfo must be listed in reverse chronological order. The most recent era + // should be the first element. + // That is, m_EraInfo[0] contains the most recent era. + // + // We know about 4 built-in eras, however users may add additional era(s) from the + // registry, by adding values to HKLM\SYSTEM\CurrentControlSet\Control\Nls\Calendars\Japanese\Eras + // we don't read the registry and instead we call WinRT to get the needed informatio + // + // Registry values look like: + // yyyy.mm.dd=era_abbrev_english_englishabbrev + // + // Where yyyy.mm.dd is the registry value name, and also the date of the era start. + // yyyy, mm, and dd are the year, month & day the era begins (4, 2 & 2 digits long) + // era is the Japanese Era name + // abbrev is the Abbreviated Japanese Era Name + // english is the English name for the Era (unused) + // englishabbrev is the Abbreviated English name for the era. + // . is a delimiter, but the value of . doesn't matter. + // '_' marks the space between the japanese era name, japanese abbreviated era name + // english name, and abbreviated english names. + // + internal static EraInfo[] GetEraInfo() + { + // See if we need to build it + if (japaneseEraInfo == null) + { + japaneseEraInfo = GetJapaneseEras(); + // See if we have to use the built-in eras + if (japaneseEraInfo == null) + { + // We know about some built-in ranges + EraInfo[] defaultEraRanges = new EraInfo[4]; + defaultEraRanges[0] = new EraInfo(4, 1989, 1, 8, 1988, 1, GregorianCalendar.MaxYear - 1988, + "\x5e73\x6210", "\x5e73", "H"); // era #4 start year/month/day, yearOffset, minEraYear + defaultEraRanges[1] = new EraInfo(3, 1926, 12, 25, 1925, 1, 1989 - 1925, + "\x662d\x548c", "\x662d", "S"); // era #3,start year/month/day, yearOffset, minEraYear + defaultEraRanges[2] = new EraInfo(2, 1912, 7, 30, 1911, 1, 1926 - 1911, + "\x5927\x6b63", "\x5927", "T"); // era #2,start year/month/day, yearOffset, minEraYear + defaultEraRanges[3] = new EraInfo(1, 1868, 1, 1, 1867, 1, 1912 - 1867, + "\x660e\x6cbb", "\x660e", "M"); // era #1,start year/month/day, yearOffset, minEraYear + + // Remember the ranges we built + japaneseEraInfo = defaultEraRanges; + } + } + + // return the era we found/made + return japaneseEraInfo; + } + + internal static volatile Calendar s_defaultInstance; + internal GregorianCalendarHelper helper; + + /*=================================GetDefaultInstance========================== + **Action: Internal method to provide a default intance of JapaneseCalendar. Used by NLS+ implementation + ** and other calendars. + **Returns: + **Arguments: + **Exceptions: + ============================================================================*/ + + internal static Calendar GetDefaultInstance() + { + if (s_defaultInstance == null) + { + s_defaultInstance = new JapaneseCalendar(); + } + return (s_defaultInstance); + } + + + public JapaneseCalendar() + { + try + { + new CultureInfo("ja-JP"); + } + catch (ArgumentException e) + { + throw new TypeInitializationException(this.GetType().ToString(), e); + } + helper = new GregorianCalendarHelper(this, GetEraInfo()); + } + + internal override CalendarId ID + { + get + { + return CalendarId.JAPAN; + } + } + + + public override DateTime AddMonths(DateTime time, int months) + { + return (helper.AddMonths(time, months)); + } + + + public override DateTime AddYears(DateTime time, int years) + { + return (helper.AddYears(time, years)); + } + + /*=================================GetDaysInMonth========================== + **Action: Returns the number of days in the month given by the year and month arguments. + **Returns: The number of days in the given month. + **Arguments: + ** year The year in Japanese calendar. + ** month The month + ** era The Japanese era value. + **Exceptions + ** ArgumentException If month is less than 1 or greater * than 12. + ============================================================================*/ + + + public override int GetDaysInMonth(int year, int month, int era) + { + return (helper.GetDaysInMonth(year, month, era)); + } + + + public override int GetDaysInYear(int year, int era) + { + return (helper.GetDaysInYear(year, era)); + } + + + public override int GetDayOfMonth(DateTime time) + { + return (helper.GetDayOfMonth(time)); + } + + + public override DayOfWeek GetDayOfWeek(DateTime time) + { + return (helper.GetDayOfWeek(time)); + } + + + public override int GetDayOfYear(DateTime time) + { + return (helper.GetDayOfYear(time)); + } + + + public override int GetMonthsInYear(int year, int era) + { + return (helper.GetMonthsInYear(year, era)); + } + + + [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems. + public override int GetWeekOfYear(DateTime time, CalendarWeekRule rule, DayOfWeek firstDayOfWeek) + { + return (helper.GetWeekOfYear(time, rule, firstDayOfWeek)); + } + + /*=================================GetEra========================== + **Action: Get the era value of the specified time. + **Returns: The era value for the specified time. + **Arguments: + ** time the specified date time. + **Exceptions: ArgumentOutOfRangeException if time is out of the valid era ranges. + ============================================================================*/ + + + public override int GetEra(DateTime time) + { + return (helper.GetEra(time)); + } + + + public override int GetMonth(DateTime time) + { + return (helper.GetMonth(time)); + } + + + public override int GetYear(DateTime time) + { + return (helper.GetYear(time)); + } + + + public override bool IsLeapDay(int year, int month, int day, int era) + { + return (helper.IsLeapDay(year, month, day, era)); + } + + + public override bool IsLeapYear(int year, int era) + { + return (helper.IsLeapYear(year, era)); + } + + // Returns the leap month in a calendar year of the specified era. This method returns 0 + // if this calendar does not have leap month, or this year is not a leap year. + // + + public override int GetLeapMonth(int year, int era) + { + return (helper.GetLeapMonth(year, era)); + } + + + public override bool IsLeapMonth(int year, int month, int era) + { + return (helper.IsLeapMonth(year, month, era)); + } + + + public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era) + { + return (helper.ToDateTime(year, month, day, hour, minute, second, millisecond, era)); + } + + // For Japanese calendar, four digit year is not used. Few emperors will live for more than one hundred years. + // Therefore, for any two digit number, we just return the original number. + + public override int ToFourDigitYear(int year) + { + if (year <= 0) + { + throw new ArgumentOutOfRangeException(nameof(year), + SR.ArgumentOutOfRange_NeedPosNum); + } + Contract.EndContractBlock(); + + if (year > helper.MaxYear) + { + throw new ArgumentOutOfRangeException( + nameof(year), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 1, + helper.MaxYear)); + } + return (year); + } + + + public override int[] Eras + { + get + { + return (helper.Eras); + } + } + + // + // Return the various era strings + // Note: The arrays are backwards of the eras + // + internal static String[] EraNames() + { + EraInfo[] eras = GetEraInfo(); + String[] eraNames = new String[eras.Length]; + + for (int i = 0; i < eras.Length; i++) + { + // Strings are in chronological order, eras are backwards order. + eraNames[i] = eras[eras.Length - i - 1].eraName; + } + + return eraNames; + } + + internal static String[] AbbrevEraNames() + { + EraInfo[] eras = GetEraInfo(); + String[] erasAbbrev = new String[eras.Length]; + + for (int i = 0; i < eras.Length; i++) + { + // Strings are in chronological order, eras are backwards order. + erasAbbrev[i] = eras[eras.Length - i - 1].abbrevEraName; + } + + return erasAbbrev; + } + + internal static String[] EnglishEraNames() + { + EraInfo[] eras = GetEraInfo(); + String[] erasEnglish = new String[eras.Length]; + + for (int i = 0; i < eras.Length; i++) + { + // Strings are in chronological order, eras are backwards order. + erasEnglish[i] = eras[eras.Length - i - 1].englishEraName; + } + + return erasEnglish; + } + + private const int DEFAULT_TWO_DIGIT_YEAR_MAX = 99; + + internal override bool IsValidYear(int year, int era) + { + return helper.IsValidYear(year, era); + } + + public override int TwoDigitYearMax + { + get + { + if (twoDigitYearMax == -1) + { + twoDigitYearMax = GetSystemTwoDigitYearSetting(ID, DEFAULT_TWO_DIGIT_YEAR_MAX); + } + return (twoDigitYearMax); + } + + set + { + VerifyWritable(); + if (value < 99 || value > helper.MaxYear) + { + throw new ArgumentOutOfRangeException( + "year", + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 99, + helper.MaxYear)); + } + twoDigitYearMax = value; + } + } + } +} diff --git a/src/mscorlib/shared/System/Globalization/JapaneseLunisolarCalendar.cs b/src/mscorlib/shared/System/Globalization/JapaneseLunisolarCalendar.cs new file mode 100644 index 0000000000..95e87f85d7 --- /dev/null +++ b/src/mscorlib/shared/System/Globalization/JapaneseLunisolarCalendar.cs @@ -0,0 +1,305 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics.Contracts; + +namespace System.Globalization +{ + /* + ** Calendar support range: + ** Calendar Minimum Maximum + ** ========== ========== ========== + ** Gregorian 1960/01/28 2050/01/22 + ** JapaneseLunisolar 1960/01/01 2049/12/29 + */ + + [Serializable] + public class JapaneseLunisolarCalendar : EastAsianLunisolarCalendar + { + // + // The era value for the current era. + // + + public const int JapaneseEra = 1; + + internal GregorianCalendarHelper helper; + + internal const int MIN_LUNISOLAR_YEAR = 1960; + internal const int MAX_LUNISOLAR_YEAR = 2049; + + internal const int MIN_GREGORIAN_YEAR = 1960; + internal const int MIN_GREGORIAN_MONTH = 1; + internal const int MIN_GREGORIAN_DAY = 28; + + internal const int MAX_GREGORIAN_YEAR = 2050; + internal const int MAX_GREGORIAN_MONTH = 1; + internal const int MAX_GREGORIAN_DAY = 22; + + internal static DateTime minDate = new DateTime(MIN_GREGORIAN_YEAR, MIN_GREGORIAN_MONTH, MIN_GREGORIAN_DAY); + internal static DateTime maxDate = new DateTime((new DateTime(MAX_GREGORIAN_YEAR, MAX_GREGORIAN_MONTH, MAX_GREGORIAN_DAY, 23, 59, 59, 999)).Ticks + 9999); + + public override DateTime MinSupportedDateTime + { + get + { + return (minDate); + } + } + + + public override DateTime MaxSupportedDateTime + { + get + { + return (maxDate); + } + } + protected override int DaysInYearBeforeMinSupportedYear + { + get + { + // 1959 from ChineseLunisolarCalendar + return 354; + } + } + + private static readonly int[,] s_yinfo = + { + /*Y LM Lmon Lday DaysPerMonth D1 D2 D3 D4 D5 D6 D7 D8 D9 D10 D11 D12 D13 #Days + 1960 */ + { 6 , 1 , 28 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 29 384 +1961 */{ 0 , 2 , 15 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 0 355 +1962 */{ 0 , 2 , 5 , 19808 },/* 29 30 29 29 30 30 29 30 29 30 30 29 0 354 +1963 */{ 4 , 1 , 25 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 29 384 +1964 */{ 0 , 2 , 13 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355 +1965 */{ 0 , 2 , 2 , 21104 },/* 29 30 29 30 29 29 30 29 29 30 30 30 0 354 +1966 */{ 3 , 1 , 22 , 26928 },/* 29 30 30 29 30 29 29 30 29 29 30 30 29 383 +1967 */{ 0 , 2 , 9 , 55632 },/* 30 30 29 30 30 29 29 30 29 30 29 30 0 355 +1968 */{ 7 , 1 , 30 , 27304 },/* 29 30 30 29 30 29 30 29 30 29 30 29 30 384 +1969 */{ 0 , 2 , 17 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354 +1970 */{ 0 , 2 , 6 , 39632 },/* 30 29 29 30 30 29 30 29 30 30 29 30 0 355 +1971 */{ 5 , 1 , 27 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384 +1972 */{ 0 , 2 , 15 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354 +1973 */{ 0 , 2 , 3 , 42208 },/* 30 29 30 29 29 30 29 29 30 30 30 29 0 354 +1974 */{ 4 , 1 , 23 , 53864 },/* 30 30 29 30 29 29 30 29 29 30 30 29 30 384 +1975 */{ 0 , 2 , 11 , 53840 },/* 30 30 29 30 29 29 30 29 29 30 29 30 0 354 +1976 */{ 8 , 1 , 31 , 54600 },/* 30 30 29 30 29 30 29 30 29 30 29 29 30 384 +1977 */{ 0 , 2 , 18 , 46400 },/* 30 29 30 30 29 30 29 30 29 30 29 29 0 354 +1978 */{ 0 , 2 , 7 , 54944 },/* 30 30 29 30 29 30 30 29 30 29 30 29 0 355 +1979 */{ 6 , 1 , 28 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 29 384 +1980 */{ 0 , 2 , 16 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355 +1981 */{ 0 , 2 , 5 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354 +1982 */{ 4 , 1 , 25 , 42200 },/* 30 29 30 29 29 30 29 29 30 30 29 30 30 384 +1983 */{ 0 , 2 , 13 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354 +1984 */{ 10 , 2 , 2 , 45656 },/* 30 29 30 30 29 29 30 29 29 30 29 30 30 384 +1985 */{ 0 , 2 , 20 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 0 354 +1986 */{ 0 , 2 , 9 , 27968 },/* 29 30 30 29 30 30 29 30 29 30 29 29 0 354 +1987 */{ 6 , 1 , 29 , 46504 },/* 30 29 30 30 29 30 29 30 30 29 30 29 30 385 +1988 */{ 0 , 2 , 18 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354 +1989 */{ 0 , 2 , 6 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355 +1990 */{ 5 , 1 , 27 , 18872 },/* 29 30 29 29 30 29 29 30 30 29 30 30 30 384 +1991 */{ 0 , 2 , 15 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354 +1992 */{ 0 , 2 , 4 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 0 354 +1993 */{ 3 , 1 , 23 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 29 383 +1994 */{ 0 , 2 , 10 , 59984 },/* 30 30 30 29 30 29 30 29 29 30 29 30 0 355 +1995 */{ 8 , 1 , 31 , 27976 },/* 29 30 30 29 30 30 29 30 29 30 29 29 30 384 +1996 */{ 0 , 2 , 19 , 23248 },/* 29 30 29 30 30 29 30 29 30 30 29 30 0 355 +1997 */{ 0 , 2 , 8 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354 +1998 */{ 5 , 1 , 28 , 37744 },/* 30 29 29 30 29 29 30 30 29 30 30 30 29 384 +1999 */{ 0 , 2 , 16 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354 +2000 */{ 0 , 2 , 5 , 51552 },/* 30 30 29 29 30 29 29 30 29 30 30 29 0 354 +2001 */{ 4 , 1 , 24 , 58536 },/* 30 30 30 29 29 30 29 29 30 29 30 29 30 384 +2002 */{ 0 , 2 , 12 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354 +2003 */{ 0 , 2 , 1 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 0 355 +2004 */{ 2 , 1 , 22 , 23208 },/* 29 30 29 30 30 29 30 29 30 29 30 29 30 384 +2005 */{ 0 , 2 , 9 , 22208 },/* 29 30 29 30 29 30 30 29 30 30 29 29 0 354 +2006 */{ 7 , 1 , 29 , 43736 },/* 30 29 30 29 30 29 30 29 30 30 29 30 30 385 +2007 */{ 0 , 2 , 18 , 9680 },/* 29 29 30 29 29 30 29 30 30 30 29 30 0 354 +2008 */{ 0 , 2 , 7 , 37584 },/* 30 29 29 30 29 29 30 29 30 30 29 30 0 354 +2009 */{ 5 , 1 , 26 , 51544 },/* 30 30 29 29 30 29 29 30 29 30 29 30 30 384 +2010 */{ 0 , 2 , 14 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354 +2011 */{ 0 , 2 , 3 , 46240 },/* 30 29 30 30 29 30 29 29 30 29 30 29 0 354 +2012 */{ 3 , 1 , 23 , 47696 },/* 30 29 30 30 30 29 30 29 29 30 29 30 29 384 +2013 */{ 0 , 2 , 10 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355 +2014 */{ 9 , 1 , 31 , 21928 },/* 29 30 29 30 29 30 29 30 30 29 30 29 30 384 +2015 */{ 0 , 2 , 19 , 19360 },/* 29 30 29 29 30 29 30 30 30 29 30 29 0 354 +2016 */{ 0 , 2 , 8 , 42416 },/* 30 29 30 29 29 30 29 30 30 29 30 30 0 355 +2017 */{ 5 , 1 , 28 , 21176 },/* 29 30 29 30 29 29 30 29 30 29 30 30 30 384 +2018 */{ 0 , 2 , 16 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354 +2019 */{ 0 , 2 , 5 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354 +2020 */{ 4 , 1 , 25 , 46248 },/* 30 29 30 30 29 30 29 29 30 29 30 29 30 384 +2021 */{ 0 , 2 , 12 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354 +2022 */{ 0 , 2 , 1 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355 +2023 */{ 2 , 1 , 22 , 21928 },/* 29 30 29 30 29 30 29 30 30 29 30 29 30 384 +2024 */{ 0 , 2 , 10 , 19296 },/* 29 30 29 29 30 29 30 30 29 30 30 29 0 354 +2025 */{ 6 , 1 , 29 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 29 384 +2026 */{ 0 , 2 , 17 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355 +2027 */{ 0 , 2 , 7 , 21104 },/* 29 30 29 30 29 29 30 29 29 30 30 30 0 354 +2028 */{ 5 , 1 , 27 , 26928 },/* 29 30 30 29 30 29 29 30 29 29 30 30 29 383 +2029 */{ 0 , 2 , 13 , 55600 },/* 30 30 29 30 30 29 29 30 29 29 30 30 0 355 +2030 */{ 0 , 2 , 3 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354 +2031 */{ 3 , 1 , 23 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 29 384 +2032 */{ 0 , 2 , 11 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 0 355 +2033 */{ 11 , 1 , 31 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384 +2034 */{ 0 , 2 , 19 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354 +2035 */{ 0 , 2 , 8 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354 +2036 */{ 6 , 1 , 28 , 53864 },/* 30 30 29 30 29 29 30 29 29 30 30 29 30 384 +2037 */{ 0 , 2 , 15 , 53840 },/* 30 30 29 30 29 29 30 29 29 30 29 30 0 354 +2038 */{ 0 , 2 , 4 , 54560 },/* 30 30 29 30 29 30 29 30 29 29 30 29 0 354 +2039 */{ 5 , 1 , 24 , 55968 },/* 30 30 29 30 30 29 30 29 30 29 30 29 29 384 +2040 */{ 0 , 2 , 12 , 46752 },/* 30 29 30 30 29 30 30 29 30 29 30 29 0 355 +2041 */{ 0 , 2 , 1 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 0 355 +2042 */{ 2 , 1 , 22 , 19160 },/* 29 30 29 29 30 29 30 29 30 30 29 30 30 384 +2043 */{ 0 , 2 , 10 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354 +2044 */{ 7 , 1 , 30 , 42168 },/* 30 29 30 29 29 30 29 29 30 29 30 30 30 384 +2045 */{ 0 , 2 , 17 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354 +2046 */{ 0 , 2 , 6 , 45648 },/* 30 29 30 30 29 29 30 29 29 30 29 30 0 354 +2047 */{ 5 , 1 , 26 , 46376 },/* 30 29 30 30 29 30 29 30 29 29 30 29 30 384 +2048 */{ 0 , 2 , 14 , 27968 },/* 29 30 30 29 30 30 29 30 29 30 29 29 0 354 +2049 */{ 0 , 2 , 2 , 44448 },/* 30 29 30 29 30 30 29 30 30 29 30 29 0 355 + */ }; + + internal override int MinCalendarYear + { + get + { + return (MIN_LUNISOLAR_YEAR); + } + } + + internal override int MaxCalendarYear + { + get + { + return (MAX_LUNISOLAR_YEAR); + } + } + + internal override DateTime MinDate + { + get + { + return (minDate); + } + } + + internal override DateTime MaxDate + { + get + { + return (maxDate); + } + } + + internal override EraInfo[] CalEraInfo + { + get + { + return (JapaneseCalendar.GetEraInfo()); + } + } + + internal override int GetYearInfo(int lunarYear, int index) + { + if ((lunarYear < MIN_LUNISOLAR_YEAR) || (lunarYear > MAX_LUNISOLAR_YEAR)) + { + throw new ArgumentOutOfRangeException( + "year", + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + MIN_LUNISOLAR_YEAR, + MAX_LUNISOLAR_YEAR)); + } + Contract.EndContractBlock(); + + return s_yinfo[lunarYear - MIN_LUNISOLAR_YEAR, index]; + } + + internal override int GetYear(int year, DateTime time) + { + return helper.GetYear(year, time); + } + + internal override int GetGregorianYear(int year, int era) + { + return helper.GetGregorianYear(year, era); + } + + // Trim off the eras that are before our date range + private static EraInfo[] TrimEras(EraInfo[] baseEras) + { + EraInfo[] newEras = new EraInfo[baseEras.Length]; + int newIndex = 0; + + // Eras have most recent first, so start with that + for (int i = 0; i < baseEras.Length; i++) + { + // If this one's minimum year is bigger than our maximum year + // then we can't use it. + if (baseEras[i].yearOffset + baseEras[i].minEraYear >= MAX_LUNISOLAR_YEAR) + { + // skip this one. + continue; + } + + // If this one's maximum era is less than our minimum era + // then we've gotten too low in the era #s, so we're done + if (baseEras[i].yearOffset + baseEras[i].maxEraYear < MIN_LUNISOLAR_YEAR) + { + break; + } + + // Wasn't too large or too small, can use this one + newEras[newIndex] = baseEras[i]; + newIndex++; + } + + // If we didn't copy any then something was wrong, just return base + if (newIndex == 0) return baseEras; + + // Resize the output array + Array.Resize(ref newEras, newIndex); + return newEras; + } + + + // Construct an instance of JapaneseLunisolar calendar. + public JapaneseLunisolarCalendar() + { + helper = new GregorianCalendarHelper(this, TrimEras(JapaneseCalendar.GetEraInfo())); + } + + + public override int GetEra(DateTime time) + { + return (helper.GetEra(time)); + } + + internal override CalendarId BaseCalendarID + { + get + { + return (CalendarId.JAPAN); + } + } + + internal override CalendarId ID + { + get + { + return (CalendarId.JAPANESELUNISOLAR); + } + } + + + public override int[] Eras + { + get + { + return (helper.Eras); + } + } + } +} diff --git a/src/mscorlib/shared/System/Globalization/JulianCalendar.cs b/src/mscorlib/shared/System/Globalization/JulianCalendar.cs new file mode 100644 index 0000000000..f4678c1a85 --- /dev/null +++ b/src/mscorlib/shared/System/Globalization/JulianCalendar.cs @@ -0,0 +1,443 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics.Contracts; + +namespace System.Globalization +{ + // + // This class implements the Julian calendar. In 48 B.C. Julius Caesar ordered a calendar reform, and this calendar + // is called Julian calendar. It consisted of a solar year of twelve months and of 365 days with an extra day + // every fourth year. + //* + //* Calendar support range: + //* Calendar Minimum Maximum + //* ========== ========== ========== + //* Gregorian 0001/01/01 9999/12/31 + //* Julia 0001/01/03 9999/10/19 + + [Serializable] + public class JulianCalendar : Calendar + { + public static readonly int JulianEra = 1; + + private const int DatePartYear = 0; + private const int DatePartDayOfYear = 1; + private const int DatePartMonth = 2; + private const int DatePartDay = 3; + + // Number of days in a non-leap year + private const int JulianDaysPerYear = 365; + // Number of days in 4 years + private const int JulianDaysPer4Years = JulianDaysPerYear * 4 + 1; + + private static readonly int[] s_daysToMonth365 = + { + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 + }; + + private static readonly int[] s_daysToMonth366 = + { + 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 + }; + + // Gregorian Calendar 9999/12/31 = Julian Calendar 9999/10/19 + // keep it as variable field for serialization compat. + internal int MaxYear = 9999; + + + public override DateTime MinSupportedDateTime + { + get + { + return (DateTime.MinValue); + } + } + + public override DateTime MaxSupportedDateTime + { + get + { + return (DateTime.MaxValue); + } + } + + public override CalendarAlgorithmType AlgorithmType + { + get + { + return CalendarAlgorithmType.SolarCalendar; + } + } + + public JulianCalendar() + { + // There is no system setting of TwoDigitYear max, so set the value here. + twoDigitYearMax = 2029; + } + + internal override CalendarId ID + { + get + { + return CalendarId.JULIAN; + } + } + + internal static void CheckEraRange(int era) + { + if (era != CurrentEra && era != JulianEra) + { + throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue); + } + } + + internal void CheckYearEraRange(int year, int era) + { + CheckEraRange(era); + if (year <= 0 || year > MaxYear) + { + throw new ArgumentOutOfRangeException( + nameof(year), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 1, + MaxYear)); + } + } + + internal static void CheckMonthRange(int month) + { + if (month < 1 || month > 12) + { + throw new ArgumentOutOfRangeException(nameof(month), SR.ArgumentOutOfRange_Month); + } + } + + /*===================================CheckDayRange============================ + **Action: Check for if the day value is valid. + **Returns: + **Arguments: + **Exceptions: + **Notes: + ** Before calling this method, call CheckYearEraRange()/CheckMonthRange() to make + ** sure year/month values are correct. + ============================================================================*/ + + internal static void CheckDayRange(int year, int month, int day) + { + if (year == 1 && month == 1) + { + // The mimimum supported Julia date is Julian 0001/01/03. + if (day < 3) + { + throw new ArgumentOutOfRangeException(null, + SR.ArgumentOutOfRange_BadYearMonthDay); + } + } + bool isLeapYear = (year % 4) == 0; + int[] days = isLeapYear ? s_daysToMonth366 : s_daysToMonth365; + int monthDays = days[month] - days[month - 1]; + if (day < 1 || day > monthDays) + { + throw new ArgumentOutOfRangeException( + nameof(day), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 1, + monthDays)); + } + } + + + // Returns a given date part of this DateTime. This method is used + // to compute the year, day-of-year, month, or day part. + internal static int GetDatePart(long ticks, int part) + { + // Gregorian 1/1/0001 is Julian 1/3/0001. Remember DateTime(0) is refered to Gregorian 1/1/0001. + // The following line convert Gregorian ticks to Julian ticks. + long julianTicks = ticks + TicksPerDay * 2; + // n = number of days since 1/1/0001 + int n = (int)(julianTicks / TicksPerDay); + // y4 = number of whole 4-year periods within 100-year period + int y4 = n / JulianDaysPer4Years; + // n = day number within 4-year period + n -= y4 * JulianDaysPer4Years; + // y1 = number of whole years within 4-year period + int y1 = n / JulianDaysPerYear; + // Last year has an extra day, so decrement result if 4 + if (y1 == 4) y1 = 3; + // If year was requested, compute and return it + if (part == DatePartYear) + { + return (y4 * 4 + y1 + 1); + } + // n = day number within year + n -= y1 * JulianDaysPerYear; + // If day-of-year was requested, return it + if (part == DatePartDayOfYear) + { + return (n + 1); + } + // Leap year calculation looks different from IsLeapYear since y1, y4, + // and y100 are relative to year 1, not year 0 + bool leapYear = (y1 == 3); + int[] days = leapYear ? s_daysToMonth366 : s_daysToMonth365; + // All months have less than 32 days, so n >> 5 is a good conservative + // estimate for the month + int m = (n >> 5) + 1; + // m = 1-based month number + while (n >= days[m]) m++; + // If month was requested, return it + if (part == DatePartMonth) return (m); + // Return 1-based day-of-month + return (n - days[m - 1] + 1); + } + + // Returns the tick count corresponding to the given year, month, and day. + internal static long DateToTicks(int year, int month, int day) + { + int[] days = (year % 4 == 0) ? s_daysToMonth366 : s_daysToMonth365; + int y = year - 1; + int n = y * 365 + y / 4 + days[month - 1] + day - 1; + // Gregorian 1/1/0001 is Julian 1/3/0001. n * TicksPerDay is the ticks in JulianCalendar. + // Therefore, we subtract two days in the following to convert the ticks in JulianCalendar + // to ticks in Gregorian calendar. + return ((n - 2) * TicksPerDay); + } + + + public override DateTime AddMonths(DateTime time, int months) + { + if (months < -120000 || months > 120000) + { + throw new ArgumentOutOfRangeException( + nameof(months), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + -120000, + 120000)); + } + Contract.EndContractBlock(); + int y = GetDatePart(time.Ticks, DatePartYear); + int m = GetDatePart(time.Ticks, DatePartMonth); + int d = GetDatePart(time.Ticks, DatePartDay); + int i = m - 1 + months; + if (i >= 0) + { + m = i % 12 + 1; + y = y + i / 12; + } + else + { + m = 12 + (i + 1) % 12; + y = y + (i - 11) / 12; + } + int[] daysArray = (y % 4 == 0 && (y % 100 != 0 || y % 400 == 0)) ? s_daysToMonth366 : s_daysToMonth365; + int days = (daysArray[m] - daysArray[m - 1]); + + if (d > days) + { + d = days; + } + long ticks = DateToTicks(y, m, d) + time.Ticks % TicksPerDay; + Calendar.CheckAddResult(ticks, MinSupportedDateTime, MaxSupportedDateTime); + return (new DateTime(ticks)); + } + + + public override DateTime AddYears(DateTime time, int years) + { + return (AddMonths(time, years * 12)); + } + + + public override int GetDayOfMonth(DateTime time) + { + return (GetDatePart(time.Ticks, DatePartDay)); + } + + + public override DayOfWeek GetDayOfWeek(DateTime time) + { + return ((DayOfWeek)((int)(time.Ticks / TicksPerDay + 1) % 7)); + } + + + public override int GetDayOfYear(DateTime time) + { + return (GetDatePart(time.Ticks, DatePartDayOfYear)); + } + + + public override int GetDaysInMonth(int year, int month, int era) + { + CheckYearEraRange(year, era); + CheckMonthRange(month); + int[] days = (year % 4 == 0) ? s_daysToMonth366 : s_daysToMonth365; + return (days[month] - days[month - 1]); + } + + + public override int GetDaysInYear(int year, int era) + { + // Year/Era range is done in IsLeapYear(). + return (IsLeapYear(year, era) ? 366 : 365); + } + + + public override int GetEra(DateTime time) + { + return (JulianEra); + } + + + public override int GetMonth(DateTime time) + { + return (GetDatePart(time.Ticks, DatePartMonth)); + } + + + public override int[] Eras + { + get + { + return (new int[] { JulianEra }); + } + } + + + public override int GetMonthsInYear(int year, int era) + { + CheckYearEraRange(year, era); + return (12); + } + + + public override int GetYear(DateTime time) + { + return (GetDatePart(time.Ticks, DatePartYear)); + } + + + public override bool IsLeapDay(int year, int month, int day, int era) + { + CheckMonthRange(month); + // Year/Era range check is done in IsLeapYear(). + if (IsLeapYear(year, era)) + { + CheckDayRange(year, month, day); + return (month == 2 && day == 29); + } + CheckDayRange(year, month, day); + return (false); + } + + // Returns the leap month in a calendar year of the specified era. This method returns 0 + // if this calendar does not have leap month, or this year is not a leap year. + // + + public override int GetLeapMonth(int year, int era) + { + CheckYearEraRange(year, era); + return (0); + } + + + public override bool IsLeapMonth(int year, int month, int era) + { + CheckYearEraRange(year, era); + CheckMonthRange(month); + return (false); + } + + // Checks whether a given year in the specified era is a leap year. This method returns true if + // year is a leap year, or false if not. + // + + public override bool IsLeapYear(int year, int era) + { + CheckYearEraRange(year, era); + return (year % 4 == 0); + } + + + public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era) + { + CheckYearEraRange(year, era); + CheckMonthRange(month); + CheckDayRange(year, month, day); + if (millisecond < 0 || millisecond >= MillisPerSecond) + { + throw new ArgumentOutOfRangeException( + nameof(millisecond), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 0, + MillisPerSecond - 1)); + } + + if (hour >= 0 && hour < 24 && minute >= 0 && minute < 60 && second >= 0 && second < 60) + { + return new DateTime(DateToTicks(year, month, day) + (new TimeSpan(0, hour, minute, second, millisecond)).Ticks); + } + else + { + throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadHourMinuteSecond); + } + } + + + public override int TwoDigitYearMax + { + get + { + return (twoDigitYearMax); + } + + set + { + VerifyWritable(); + if (value < 99 || value > MaxYear) + { + throw new ArgumentOutOfRangeException( + "year", + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 99, + MaxYear)); + } + twoDigitYearMax = value; + } + } + + + public override int ToFourDigitYear(int year) + { + if (year < 0) + { + throw new ArgumentOutOfRangeException(nameof(year), + SR.ArgumentOutOfRange_NeedNonNegNum); + } + Contract.EndContractBlock(); + + if (year > MaxYear) + { + throw new ArgumentOutOfRangeException( + nameof(year), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Bounds_Lower_Upper, + 1, + MaxYear)); + } + return (base.ToFourDigitYear(year)); + } + } +} diff --git a/src/mscorlib/shared/System/Globalization/KoreanCalendar.cs b/src/mscorlib/shared/System/Globalization/KoreanCalendar.cs new file mode 100644 index 0000000000..b962b1c427 --- /dev/null +++ b/src/mscorlib/shared/System/Globalization/KoreanCalendar.cs @@ -0,0 +1,266 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.Contracts; + +namespace System.Globalization +{ + /*=================================KoreanCalendar========================== + ** + ** Korean calendar is based on the Gregorian calendar. And the year is an offset to Gregorian calendar. + ** That is, + ** Korean year = Gregorian year + 2333. So 2000/01/01 A.D. is Korean 4333/01/01 + ** + ** 0001/1/1 A.D. is Korean year 2334. + ** + ** Calendar support range: + ** Calendar Minimum Maximum + ** ========== ========== ========== + ** Gregorian 0001/01/01 9999/12/31 + ** Korean 2334/01/01 12332/12/31 + ============================================================================*/ + + + [Serializable] + public class KoreanCalendar : Calendar + { + // + // The era value for the current era. + // + + public const int KoreanEra = 1; + + // Since + // Gregorian Year = Era Year + yearOffset + // Gregorian Year 1 is Korean year 2334, so that + // 1 = 2334 + yearOffset + // So yearOffset = -2333 + // Gregorian year 2001 is Korean year 4334. + + //m_EraInfo[0] = new EraInfo(1, new DateTime(1, 1, 1).Ticks, -2333, 2334, GregorianCalendar.MaxYear + 2333); + + // Initialize our era info. + internal static EraInfo[] koreanEraInfo = new EraInfo[] { + new EraInfo( 1, 1, 1, 1, -2333, 2334, GregorianCalendar.MaxYear + 2333) // era #, start year/month/day, yearOffset, minEraYear + }; + + internal GregorianCalendarHelper helper; + + + public override DateTime MinSupportedDateTime + { + get + { + return (DateTime.MinValue); + } + } + + public override DateTime MaxSupportedDateTime + { + get + { + return (DateTime.MaxValue); + } + } + + public override CalendarAlgorithmType AlgorithmType + { + get + { + return CalendarAlgorithmType.SolarCalendar; + } + } + + public KoreanCalendar() + { + try + { + new CultureInfo("ko-KR"); + } + catch (ArgumentException e) + { + throw new TypeInitializationException(this.GetType().ToString(), e); + } + helper = new GregorianCalendarHelper(this, koreanEraInfo); + } + + internal override CalendarId ID + { + get + { + return CalendarId.KOREA; + } + } + + + public override DateTime AddMonths(DateTime time, int months) + { + return (helper.AddMonths(time, months)); + } + + + public override DateTime AddYears(DateTime time, int years) + { + return (helper.AddYears(time, years)); + } + + /*=================================GetDaysInMonth========================== + **Action: Returns the number of days in the month given by the year and month arguments. + **Returns: The number of days in the given month. + **Arguments: + ** year The year in Korean calendar. + ** month The month + ** era The Japanese era value. + **Exceptions + ** ArgumentException If month is less than 1 or greater * than 12. + ============================================================================*/ + + + public override int GetDaysInMonth(int year, int month, int era) + { + return (helper.GetDaysInMonth(year, month, era)); + } + + + public override int GetDaysInYear(int year, int era) + { + return (helper.GetDaysInYear(year, era)); + } + + + public override int GetDayOfMonth(DateTime time) + { + return (helper.GetDayOfMonth(time)); + } + + + public override DayOfWeek GetDayOfWeek(DateTime time) + { + return (helper.GetDayOfWeek(time)); + } + + + public override int GetDayOfYear(DateTime time) + { + return (helper.GetDayOfYear(time)); + } + + + public override int GetMonthsInYear(int year, int era) + { + return (helper.GetMonthsInYear(year, era)); + } + + + [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems. + public override int GetWeekOfYear(DateTime time, CalendarWeekRule rule, DayOfWeek firstDayOfWeek) + { + return (helper.GetWeekOfYear(time, rule, firstDayOfWeek)); + } + + + public override int GetEra(DateTime time) + { + return (helper.GetEra(time)); + } + + public override int GetMonth(DateTime time) + { + return (helper.GetMonth(time)); + } + + + public override int GetYear(DateTime time) + { + return (helper.GetYear(time)); + } + + + public override bool IsLeapDay(int year, int month, int day, int era) + { + return (helper.IsLeapDay(year, month, day, era)); + } + + + public override bool IsLeapYear(int year, int era) + { + return (helper.IsLeapYear(year, era)); + } + + // Returns the leap month in a calendar year of the specified era. This method returns 0 + // if this calendar does not have leap month, or this year is not a leap year. + // + + public override int GetLeapMonth(int year, int era) + { + return (helper.GetLeapMonth(year, era)); + } + + + public override bool IsLeapMonth(int year, int month, int era) + { + return (helper.IsLeapMonth(year, month, era)); + } + + + public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era) + { + return (helper.ToDateTime(year, month, day, hour, minute, second, millisecond, era)); + } + + + public override int[] Eras + { + get + { + return (helper.Eras); + } + } + + private const int DEFAULT_TWO_DIGIT_YEAR_MAX = 4362; + + + public override int TwoDigitYearMax + { + get + { + if (twoDigitYearMax == -1) + { + twoDigitYearMax = GetSystemTwoDigitYearSetting(ID, DEFAULT_TWO_DIGIT_YEAR_MAX); + } + return (twoDigitYearMax); + } + + set + { + VerifyWritable(); + if (value < 99 || value > helper.MaxYear) + { + throw new ArgumentOutOfRangeException( + "year", + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 99, + helper.MaxYear)); + } + twoDigitYearMax = value; + } + } + + + public override int ToFourDigitYear(int year) + { + if (year < 0) + { + throw new ArgumentOutOfRangeException(nameof(year), + SR.ArgumentOutOfRange_NeedNonNegNum); + } + Contract.EndContractBlock(); + + return (helper.ToFourDigitYear(year, this.TwoDigitYearMax)); + } + } +} diff --git a/src/mscorlib/shared/System/Globalization/KoreanLunisolarCalendar.cs b/src/mscorlib/shared/System/Globalization/KoreanLunisolarCalendar.cs new file mode 100644 index 0000000000..d4c71632aa --- /dev/null +++ b/src/mscorlib/shared/System/Globalization/KoreanLunisolarCalendar.cs @@ -0,0 +1,1323 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics.Contracts; + +namespace System.Globalization +{ + /* + ** Calendar support range: + ** Calendar Minimum Maximum + ** ========== ========== ========== + ** Gregorian 918/02/14 2051/02/10 + ** KoreanLunisolar 918/01/01 2050/13/29 + */ + + [Serializable] + public class KoreanLunisolarCalendar : EastAsianLunisolarCalendar + { + // + // The era value for the current era. + // + + public const int GregorianEra = 1; + + internal const int MIN_LUNISOLAR_YEAR = 918; + internal const int MAX_LUNISOLAR_YEAR = 2050; + + internal const int MIN_GREGORIAN_YEAR = 918; + internal const int MIN_GREGORIAN_MONTH = 2; + internal const int MIN_GREGORIAN_DAY = 14; + + internal const int MAX_GREGORIAN_YEAR = 2051; + internal const int MAX_GREGORIAN_MONTH = 2; + internal const int MAX_GREGORIAN_DAY = 10; + + internal static DateTime minDate = new DateTime(MIN_GREGORIAN_YEAR, MIN_GREGORIAN_MONTH, MIN_GREGORIAN_DAY); + internal static DateTime maxDate = new DateTime((new DateTime(MAX_GREGORIAN_YEAR, MAX_GREGORIAN_MONTH, MAX_GREGORIAN_DAY, 23, 59, 59, 999)).Ticks + 9999); + + public override DateTime MinSupportedDateTime + { + get + { + return (minDate); + } + } + + + + public override DateTime MaxSupportedDateTime + { + get + { + return (maxDate); + } + } + + protected override int DaysInYearBeforeMinSupportedYear + { + get + { + // 917 -- From http://emr.cs.iit.edu/home/reingold/calendar-book/Calendrica.html + // using ChineseLunisolar + return 384; + } + } + + private static readonly int[,] s_yinfo = + { + /*Y LM Lmon Lday DaysPerMonth D1 D2 D3 D4 D5 D6 D7 D8 D9 D10 D11 D12 D13 #Days + 918 */ + { 0 , 2 , 14 , 21936 },/* 29 30 29 30 29 30 29 30 30 29 30 30 0 355 +919 */{ 0 , 2 , 4 , 17872 },/* 29 30 29 29 29 30 29 30 30 30 29 30 0 354 +920 */{ 6 , 1 , 24 , 41688 },/* 30 29 30 29 29 29 30 29 30 30 29 30 30 384 +921 */{ 0 , 2 , 11 , 41648 },/* 30 29 30 29 29 29 30 29 30 29 30 30 0 354 +922 */{ 0 , 1 , 31 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354 +923 */{ 4 , 1 , 20 , 46248 },/* 30 29 30 30 29 30 29 29 30 29 30 29 30 384 +924 */{ 0 , 2 , 8 , 27936 },/* 29 30 30 29 30 30 29 30 29 29 30 29 0 354 +925 */{ 12 , 1 , 27 , 44384 },/* 30 29 30 29 30 30 29 30 29 30 30 29 29 384 +926 */{ 0 , 2 , 15 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 0 355 +927 */{ 0 , 2 , 5 , 21936 },/* 29 30 29 30 29 30 29 30 30 29 30 30 0 355 +928 */{ 8 , 1 , 26 , 17848 },/* 29 30 29 29 29 30 29 30 30 29 30 30 30 384 +929 */{ 0 , 2 , 13 , 17776 },/* 29 30 29 29 29 30 29 30 29 30 30 30 0 354 +930 */{ 0 , 2 , 2 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354 +931 */{ 5 , 1 , 22 , 26960 },/* 29 30 30 29 30 29 29 30 29 30 29 30 29 383 +932 */{ 0 , 2 , 9 , 59728 },/* 30 30 30 29 30 29 29 30 29 30 29 30 0 355 +933 */{ 0 , 1 , 29 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354 +934 */{ 1 , 1 , 18 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 29 384 +935 */{ 0 , 2 , 6 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 0 355 +936 */{ 11 , 1 , 27 , 21344 },/* 29 30 29 30 29 29 30 30 29 30 30 29 29 383 +937 */{ 0 , 2 , 13 , 51904 },/* 30 30 29 29 30 29 30 29 30 30 29 29 0 354 +938 */{ 0 , 2 , 2 , 58720 },/* 30 30 30 29 29 30 29 30 29 30 30 29 0 355 +939 */{ 7 , 1 , 23 , 53928 },/* 30 30 29 30 29 29 30 29 30 29 30 29 30 384 +940 */{ 0 , 2 , 11 , 53920 },/* 30 30 29 30 29 29 30 29 30 29 30 29 0 354 +941 */{ 0 , 1 , 30 , 55632 },/* 30 30 29 30 30 29 29 30 29 30 29 30 0 355 +942 */{ 3 , 1 , 20 , 23208 },/* 29 30 29 30 30 29 30 29 30 29 30 29 30 384 +943 */{ 0 , 2 , 8 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354 +944 */{ 12 , 1 , 28 , 42704 },/* 30 29 30 29 29 30 30 29 30 30 29 30 29 384 +945 */{ 0 , 2 , 15 , 38352 },/* 30 29 29 30 29 30 29 30 30 30 29 30 0 355 +946 */{ 0 , 2 , 5 , 19152 },/* 29 30 29 29 30 29 30 29 30 30 29 30 0 354 +947 */{ 7 , 1 , 25 , 42200 },/* 30 29 30 29 29 30 29 29 30 30 29 30 30 384 +948 */{ 0 , 2 , 13 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354 +949 */{ 0 , 2 , 1 , 45664 },/* 30 29 30 30 29 29 30 29 29 30 30 29 0 354 +950 */{ 5 , 1 , 21 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 29 384 +951 */{ 0 , 2 , 9 , 45936 },/* 30 29 30 30 29 30 29 30 29 30 29 0 0 325 +952 */{ 0 , 12 , 31 , 43728 },/* 30 29 30 29 30 29 30 29 30 30 29 30 29 384 +953 */{ 1 , 1 , 18 , 38352 },/* 30 29 29 30 29 30 29 30 30 30 29 30 29 384 +954 */{ 0 , 2 , 6 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355 +955 */{ 9 , 1 , 27 , 19128 },/* 29 30 29 29 30 29 30 29 30 29 30 30 30 384 +956 */{ 0 , 2 , 15 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354 +957 */{ 0 , 2 , 3 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354 +958 */{ 7 , 1 , 23 , 43672 },/* 30 29 30 29 30 29 30 29 30 29 29 30 30 384 +959 */{ 0 , 2 , 11 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354 +960 */{ 0 , 1 , 31 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355 +961 */{ 3 , 1 , 20 , 19880 },/* 29 30 29 29 30 30 29 30 30 29 30 29 30 384 +962 */{ 0 , 2 , 8 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354 +963 */{ 12 , 1 , 28 , 38256 },/* 30 29 29 30 29 30 29 30 29 30 30 30 29 384 +964 */{ 0 , 2 , 16 , 41840 },/* 30 29 30 29 29 29 30 30 29 30 30 30 0 355 +965 */{ 0 , 2 , 5 , 20848 },/* 29 30 29 30 29 29 29 30 29 30 30 30 0 354 +966 */{ 8 , 1 , 25 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 29 383 +967 */{ 0 , 2 , 12 , 54448 },/* 30 30 29 30 29 30 29 29 30 29 30 30 0 355 +968 */{ 0 , 2 , 2 , 23184 },/* 29 30 29 30 30 29 30 29 30 29 29 30 0 354 +969 */{ 5 , 1 , 21 , 27472 },/* 29 30 30 29 30 29 30 30 29 30 29 30 29 384 +970 */{ 0 , 2 , 9 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 0 355 +971 */{ 0 , 1 , 30 , 10976 },/* 29 29 30 29 30 29 30 29 30 30 30 29 0 354 +972 */{ 2 , 1 , 19 , 37744 },/* 30 29 29 30 29 29 30 30 29 30 30 30 29 384 +973 */{ 0 , 2 , 6 , 41696 },/* 30 29 30 29 29 29 30 29 30 30 30 29 0 354 +974 */{ 10 , 1 , 26 , 51560 },/* 30 30 29 29 30 29 29 30 29 30 30 29 30 384 +975 */{ 0 , 2 , 14 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354 +976 */{ 0 , 2 , 3 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354 +977 */{ 7 , 1 , 22 , 55952 },/* 30 30 29 30 30 29 30 29 30 29 29 30 29 384 +978 */{ 0 , 2 , 10 , 46496 },/* 30 29 30 30 29 30 29 30 30 29 30 29 0 355 +979 */{ 0 , 1 , 31 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 0 355 +980 */{ 3 , 1 , 21 , 10968 },/* 29 29 30 29 30 29 30 29 30 30 29 30 30 384 +981 */{ 0 , 2 , 8 , 9680 },/* 29 29 30 29 29 30 29 30 30 30 29 30 0 354 +982 */{ 12 , 1 , 28 , 37592 },/* 30 29 29 30 29 29 30 29 30 30 29 30 30 384 +983 */{ 0 , 2 , 16 , 37552 },/* 30 29 29 30 29 29 30 29 30 29 30 30 0 354 +984 */{ 0 , 2 , 5 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354 +985 */{ 9 , 1 , 24 , 46248 },/* 30 29 30 30 29 30 29 29 30 29 30 29 30 384 +986 */{ 0 , 2 , 12 , 44192 },/* 30 29 30 29 30 30 29 29 30 29 30 29 0 354 +987 */{ 0 , 2 , 1 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355 +988 */{ 5 , 1 , 22 , 21936 },/* 29 30 29 30 29 30 29 30 30 29 30 30 29 384 +989 */{ 0 , 2 , 9 , 19376 },/* 29 30 29 29 30 29 30 30 30 29 30 30 0 355 +990 */{ 0 , 1 , 30 , 9648 },/* 29 29 30 29 29 30 29 30 30 29 30 30 0 354 +991 */{ 2 , 1 , 19 , 37560 },/* 30 29 29 30 29 29 30 29 30 29 30 30 30 384 +992 */{ 0 , 2 , 7 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354 +993 */{ 10 , 1 , 26 , 26968 },/* 29 30 30 29 30 29 29 30 29 30 29 30 30 384 +994 */{ 0 , 2 , 14 , 22864 },/* 29 30 29 30 30 29 29 30 29 30 29 30 0 354 +995 */{ 0 , 2 , 3 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354 +996 */{ 7 , 1 , 23 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 29 384 +997 */{ 0 , 2 , 10 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 0 355 +998 */{ 0 , 1 , 31 , 19296 },/* 29 30 29 29 30 29 30 30 29 30 30 29 0 354 +999 */{ 3 , 1 , 20 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 29 384 +1000 */{ 0 , 2 , 8 , 9584 },/* 29 29 30 29 29 30 29 30 29 30 30 30 0 354 +1001 */{ 12 , 1 , 28 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 29 383 +1002 */{ 0 , 2 , 15 , 53920 },/* 30 30 29 30 29 29 30 29 30 29 30 29 0 354 +1003 */{ 0 , 2 , 4 , 54608 },/* 30 30 29 30 29 30 29 30 29 30 29 30 0 355 +1004 */{ 9 , 1 , 25 , 23208 },/* 29 30 29 30 30 29 30 29 30 29 30 29 30 384 +1005 */{ 0 , 2 , 12 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354 +1006 */{ 0 , 2 , 1 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 0 355 +1007 */{ 5 , 1 , 22 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384 +1008 */{ 0 , 2 , 10 , 19152 },/* 29 30 29 29 30 29 30 29 30 30 29 30 0 354 +1009 */{ 0 , 1 , 29 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354 +1010 */{ 2 , 1 , 18 , 53864 },/* 30 30 29 30 29 29 30 29 29 30 30 29 30 384 +1011 */{ 0 , 2 , 6 , 45664 },/* 30 29 30 30 29 29 30 29 29 30 30 29 0 354 +1012 */{ 10 , 1 , 26 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 29 384 +1013 */{ 0 , 2 , 13 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355 +1014 */{ 0 , 2 , 3 , 13728 },/* 29 29 30 30 29 30 29 30 30 29 30 29 0 354 +1015 */{ 6 , 1 , 23 , 38352 },/* 30 29 29 30 29 30 29 30 30 30 29 30 29 384 +1016 */{ 0 , 2 , 11 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355 +1017 */{ 0 , 1 , 31 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354 +1018 */{ 4 , 1 , 20 , 42200 },/* 30 29 30 29 29 30 29 29 30 30 29 30 30 384 +1019 */{ 0 , 2 , 8 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354 +1020 */{ 12 , 1 , 28 , 43608 },/* 30 29 30 29 30 29 30 29 29 30 29 30 30 384 +1021 */{ 0 , 2 , 15 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354 +1022 */{ 0 , 2 , 4 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355 +1023 */{ 9 , 1 , 25 , 11688 },/* 29 29 30 29 30 30 29 30 30 29 30 29 30 384 +1024 */{ 0 , 2 , 13 , 11088 },/* 29 29 30 29 30 29 30 30 29 30 29 30 0 354 +1025 */{ 0 , 2 , 1 , 38256 },/* 30 29 29 30 29 30 29 30 29 30 30 30 0 355 +1026 */{ 5 , 1 , 22 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 29 383 +1027 */{ 0 , 2 , 9 , 51568 },/* 30 30 29 29 30 29 29 30 29 30 30 30 0 355 +1028 */{ 0 , 1 , 30 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 0 354 +1029 */{ 2 , 1 , 18 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 29 383 +1030 */{ 0 , 2 , 5 , 55952 },/* 30 30 29 30 30 29 30 29 30 29 29 30 0 355 +1031 */{ 10 , 1 , 26 , 27472 },/* 29 30 30 29 30 29 30 30 29 30 29 30 29 384 +1032 */{ 0 , 2 , 14 , 26320 },/* 29 30 30 29 29 30 30 29 30 30 29 30 0 355 +1033 */{ 0 , 2 , 3 , 9952 },/* 29 29 30 29 29 30 30 29 30 30 30 29 0 354 +1034 */{ 6 , 1 , 23 , 37744 },/* 30 29 29 30 29 29 30 30 29 30 30 30 29 384 +1035 */{ 0 , 2 , 11 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354 +1036 */{ 0 , 1 , 31 , 51552 },/* 30 30 29 29 30 29 29 30 29 30 30 29 0 354 +1037 */{ 4 , 1 , 19 , 54440 },/* 30 30 29 30 29 30 29 29 30 29 30 29 30 384 +1038 */{ 0 , 2 , 7 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354 +1039 */{ 12 , 1 , 27 , 54928 },/* 30 30 29 30 29 30 30 29 30 29 29 30 29 384 +1040 */{ 0 , 2 , 15 , 46464 },/* 30 29 30 30 29 30 29 30 30 29 29 29 0 354 +1041 */{ 0 , 2 , 3 , 54960 },/* 30 30 29 30 29 30 30 29 30 29 30 30 0 356 +1042 */{ 9 , 1 , 25 , 9944 },/* 29 29 30 29 29 30 30 29 30 30 29 30 30 384 +1043 */{ 0 , 2 , 13 , 9680 },/* 29 29 30 29 29 30 29 30 30 30 29 30 0 354 +1044 */{ 0 , 2 , 2 , 37552 },/* 30 29 29 30 29 29 30 29 30 29 30 30 0 354 +1045 */{ 5 , 1 , 21 , 43352 },/* 30 29 30 29 30 29 29 30 29 30 29 30 30 384 +1046 */{ 0 , 2 , 9 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354 +1047 */{ 0 , 1 , 29 , 46240 },/* 30 29 30 30 29 30 29 29 30 29 30 29 0 354 +1048 */{ 1 , 1 , 18 , 46424 },/* 30 29 30 30 29 30 29 30 29 30 29 30 30 385 +1049 */{ 0 , 2 , 6 , 11600 },/* 29 29 30 29 30 30 29 30 29 30 29 30 0 354 +1050 */{ 11 , 1 , 26 , 21936 },/* 29 30 29 30 29 30 29 30 30 29 30 30 29 384 +1051 */{ 0 , 2 , 14 , 19376 },/* 29 30 29 29 30 29 30 30 30 29 30 30 0 355 +1052 */{ 0 , 2 , 4 , 9648 },/* 29 29 30 29 29 30 29 30 30 29 30 30 0 354 +1053 */{ 7 , 1 , 23 , 21176 },/* 29 30 29 30 29 29 30 29 30 29 30 30 30 384 +1054 */{ 0 , 2 , 11 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354 +1055 */{ 0 , 1 , 31 , 26960 },/* 29 30 30 29 30 29 29 30 29 30 29 30 0 354 +1056 */{ 3 , 1 , 20 , 27304 },/* 29 30 30 29 30 29 30 29 30 29 30 29 30 384 +1057 */{ 0 , 2 , 7 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354 +1058 */{ 12 , 1 , 27 , 43864 },/* 30 29 30 29 30 29 30 30 29 30 29 30 30 385 +1059 */{ 0 , 2 , 16 , 10064 },/* 29 29 30 29 29 30 30 30 29 30 29 30 0 354 +1060 */{ 0 , 2 , 5 , 19296 },/* 29 30 29 29 30 29 30 30 29 30 30 29 0 354 +1061 */{ 8 , 1 , 24 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 29 384 +1062 */{ 0 , 2 , 12 , 42336 },/* 30 29 30 29 29 30 29 30 29 30 30 29 0 354 +1063 */{ 0 , 2 , 1 , 53856 },/* 30 30 29 30 29 29 30 29 29 30 30 29 0 354 +1064 */{ 5 , 1 , 21 , 59696 },/* 30 30 30 29 30 29 29 30 29 29 30 30 29 384 +1065 */{ 0 , 2 , 8 , 54608 },/* 30 30 29 30 29 30 29 30 29 30 29 30 0 355 +1066 */{ 0 , 1 , 29 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354 +1067 */{ 1 , 1 , 18 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 29 384 +1068 */{ 0 , 2 , 6 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 0 355 +1069 */{ 11 , 1 , 26 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384 +1070 */{ 0 , 2 , 14 , 18896 },/* 29 30 29 29 30 29 29 30 30 30 29 30 0 354 +1071 */{ 0 , 2 , 3 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354 +1072 */{ 7 , 1 , 23 , 53864 },/* 30 30 29 30 29 29 30 29 29 30 30 29 30 384 +1073 */{ 0 , 2 , 10 , 43616 },/* 30 29 30 29 30 29 30 29 29 30 30 29 0 354 +1074 */{ 0 , 1 , 30 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355 +1075 */{ 4 , 1 , 20 , 22184 },/* 29 30 29 30 29 30 30 29 30 29 30 29 30 384 +1076 */{ 0 , 2 , 8 , 13728 },/* 29 29 30 30 29 30 29 30 30 29 30 29 0 354 +1077 */{ 0 , 1 , 27 , 38352 },/* 30 29 29 30 29 30 29 30 30 30 29 30 0 355 +1078 */{ 1 , 1 , 17 , 19352 },/* 29 30 29 29 30 29 30 30 30 29 29 30 30 384 +1079 */{ 0 , 2 , 5 , 17840 },/* 29 30 29 29 29 30 29 30 30 29 30 30 0 354 +1080 */{ 9 , 1 , 25 , 42168 },/* 30 29 30 29 29 30 29 29 30 29 30 30 30 384 +1081 */{ 0 , 2 , 12 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 0 354 +1082 */{ 0 , 2 , 1 , 43600 },/* 30 29 30 29 30 29 30 29 29 30 29 30 0 354 +1083 */{ 6 , 1 , 21 , 46408 },/* 30 29 30 30 29 30 29 30 29 30 29 29 30 384 +1084 */{ 0 , 2 , 9 , 27472 },/* 29 30 30 29 30 29 30 30 29 30 29 30 0 355 +1085 */{ 0 , 1 , 29 , 11680 },/* 29 29 30 29 30 30 29 30 30 29 30 29 0 354 +1086 */{ 2 , 1 , 18 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 29 384 +1087 */{ 0 , 2 , 6 , 37744 },/* 30 29 29 30 29 29 30 30 29 30 30 30 0 355 +1088 */{ 12 , 1 , 27 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 29 383 +1089 */{ 0 , 2 , 13 , 51568 },/* 30 30 29 29 30 29 29 30 29 30 30 30 0 355 +1090 */{ 0 , 2 , 3 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 0 354 +1091 */{ 8 , 1 , 23 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 29 383 +1092 */{ 0 , 2 , 10 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 0 355 +1093 */{ 0 , 1 , 30 , 23360 },/* 29 30 29 30 30 29 30 30 29 30 29 29 0 354 +1094 */{ 4 , 1 , 19 , 43880 },/* 30 29 30 29 30 29 30 30 29 30 30 29 30 385 +1095 */{ 0 , 2 , 8 , 10976 },/* 29 29 30 29 30 29 30 29 30 30 30 29 0 354 +1096 */{ 0 , 1 , 28 , 58896 },/* 30 30 30 29 29 30 30 29 29 29 29 30 0 354 +1097 */{ 2 , 1 , 16 , 51568 },/* 30 30 29 29 30 29 29 30 29 30 30 30 29 384 +1098 */{ 0 , 2 , 4 , 51552 },/* 30 30 29 29 30 29 29 30 29 30 30 29 0 354 +1099 */{ 9 , 1 , 24 , 54440 },/* 30 30 29 30 29 30 29 29 30 29 30 29 30 384 +1100 */{ 0 , 2 , 12 , 21664 },/* 29 30 29 30 29 30 29 29 30 29 30 29 0 353 +1101 */{ 0 , 1 , 31 , 54864 },/* 30 30 29 30 29 30 30 29 29 30 29 30 0 355 +1102 */{ 6 , 1 , 21 , 23208 },/* 29 30 29 30 30 29 30 29 30 29 30 29 30 384 +1103 */{ 0 , 2 , 9 , 21968 },/* 29 30 29 30 29 30 29 30 30 30 29 30 0 355 +1104 */{ 0 , 1 , 30 , 9936 },/* 29 29 30 29 29 30 30 29 30 30 29 30 0 354 +1105 */{ 2 , 1 , 18 , 37608 },/* 30 29 29 30 29 29 30 29 30 30 30 29 30 384 +1106 */{ 0 , 2 , 6 , 37552 },/* 30 29 29 30 29 29 30 29 30 29 30 30 0 354 +1107 */{ 10 , 1 , 26 , 43352 },/* 30 29 30 29 30 29 29 30 29 30 29 30 30 384 +1108 */{ 0 , 2 , 14 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354 +1109 */{ 0 , 2 , 2 , 46240 },/* 30 29 30 30 29 30 29 29 30 29 30 29 0 354 +1110 */{ 8 , 1 , 22 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 29 384 +1111 */{ 0 , 2 , 10 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355 +1112 */{ 0 , 1 , 31 , 21936 },/* 29 30 29 30 29 30 29 30 30 29 30 30 0 355 +1113 */{ 4 , 1 , 20 , 9656 },/* 29 29 30 29 29 30 29 30 30 29 30 30 30 384 +1114 */{ 0 , 2 , 8 , 17776 },/* 29 30 29 29 29 30 29 30 29 30 30 30 0 354 +1115 */{ 0 , 1 , 28 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354 +1116 */{ 1 , 1 , 17 , 43352 },/* 30 29 30 29 30 29 29 30 29 30 29 30 30 384 +1117 */{ 0 , 2 , 4 , 26960 },/* 29 30 30 29 30 29 29 30 29 30 29 30 0 354 +1118 */{ 9 , 1 , 24 , 29352 },/* 29 30 30 30 29 29 30 29 30 29 30 29 30 384 +1119 */{ 0 , 2 , 12 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354 +1120 */{ 0 , 2 , 1 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 0 355 +1121 */{ 5 , 1 , 21 , 19304 },/* 29 30 29 29 30 29 30 30 29 30 30 29 30 384 +1122 */{ 0 , 2 , 9 , 19296 },/* 29 30 29 29 30 29 30 30 29 30 30 29 0 354 +1123 */{ 0 , 1 , 29 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355 +1124 */{ 3 , 1 , 19 , 21104 },/* 29 30 29 30 29 29 30 29 29 30 30 30 29 383 +1125 */{ 0 , 2 , 5 , 53856 },/* 30 30 29 30 29 29 30 29 29 30 30 29 0 354 +1126 */{ 11 , 1 , 25 , 59696 },/* 30 30 30 29 30 29 29 30 29 29 30 30 29 384 +1127 */{ 0 , 2 , 13 , 54608 },/* 30 30 29 30 29 30 29 30 29 30 29 30 0 355 +1128 */{ 0 , 2 , 3 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354 +1129 */{ 8 , 1 , 22 , 39824 },/* 30 29 29 30 30 29 30 30 30 29 29 30 29 384 +1130 */{ 0 , 2 , 10 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 0 355 +1131 */{ 0 , 1 , 31 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354 +1132 */{ 4 , 1 , 20 , 42216 },/* 30 29 30 29 29 30 29 29 30 30 30 29 30 384 +1133 */{ 0 , 2 , 7 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354 +1134 */{ 0 , 1 , 27 , 53840 },/* 30 30 29 30 29 29 30 29 29 30 29 30 0 354 +1135 */{ 2 , 1 , 16 , 55592 },/* 30 30 29 30 30 29 29 30 29 29 30 29 30 384 +1136 */{ 0 , 2 , 4 , 46400 },/* 30 29 30 30 29 30 29 30 29 30 29 29 0 354 +1137 */{ 10 , 1 , 23 , 54952 },/* 30 30 29 30 29 30 30 29 30 29 30 29 30 385 +1138 */{ 0 , 2 , 12 , 11680 },/* 29 29 30 29 30 30 29 30 30 29 30 29 0 354 +1139 */{ 0 , 2 , 1 , 38352 },/* 30 29 29 30 29 30 29 30 30 30 29 30 0 355 +1140 */{ 6 , 1 , 22 , 19160 },/* 29 30 29 29 30 29 30 29 30 30 29 30 30 384 +1141 */{ 0 , 2 , 9 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354 +1142 */{ 0 , 1 , 29 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354 +1143 */{ 4 , 1 , 18 , 45656 },/* 30 29 30 30 29 29 30 29 29 30 29 30 30 384 +1144 */{ 0 , 2 , 6 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 0 354 +1145 */{ 11 , 1 , 25 , 46376 },/* 30 29 30 30 29 30 29 30 29 29 30 29 30 384 +1146 */{ 0 , 2 , 13 , 27456 },/* 29 30 30 29 30 29 30 30 29 30 29 29 0 354 +1147 */{ 0 , 2 , 2 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 0 355 +1148 */{ 8 , 1 , 23 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 29 384 +1149 */{ 0 , 2 , 10 , 39280 },/* 30 29 29 30 30 29 29 30 29 30 30 30 0 355 +1150 */{ 0 , 1 , 31 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354 +1151 */{ 4 , 1 , 20 , 25784 },/* 29 30 30 29 29 30 29 29 30 29 30 30 30 384 +1152 */{ 0 , 2 , 8 , 21680 },/* 29 30 29 30 29 30 29 29 30 29 30 30 0 354 +1153 */{ 12 , 1 , 27 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 29 383 +1154 */{ 0 , 2 , 14 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 0 355 +1155 */{ 0 , 2 , 4 , 23232 },/* 29 30 29 30 30 29 30 29 30 30 29 29 0 354 +1156 */{ 10 , 1 , 24 , 43880 },/* 30 29 30 29 30 29 30 30 29 30 30 29 30 385 +1157 */{ 0 , 2 , 12 , 9952 },/* 29 29 30 29 29 30 30 29 30 30 30 29 0 354 +1158 */{ 0 , 2 , 1 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354 +1159 */{ 6 , 1 , 21 , 51568 },/* 30 30 29 29 30 29 29 30 29 30 30 30 29 384 +1160 */{ 0 , 2 , 9 , 51552 },/* 30 30 29 29 30 29 29 30 29 30 30 29 0 354 +1161 */{ 0 , 1 , 28 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354 +1162 */{ 2 , 1 , 17 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 29 384 +1163 */{ 0 , 2 , 5 , 54608 },/* 30 30 29 30 29 30 29 30 29 30 29 30 0 355 +1164 */{ 11 , 1 , 26 , 22184 },/* 29 30 29 30 29 30 30 29 30 29 30 29 30 384 +1165 */{ 0 , 2 , 13 , 21936 },/* 29 30 29 30 29 30 29 30 30 29 30 30 0 355 +1166 */{ 0 , 2 , 3 , 9680 },/* 29 29 30 29 29 30 29 30 30 30 29 30 0 354 +1167 */{ 7 , 1 , 23 , 37608 },/* 30 29 29 30 29 29 30 29 30 30 30 29 30 384 +1168 */{ 0 , 2 , 11 , 37488 },/* 30 29 29 30 29 29 30 29 29 30 30 30 0 354 +1169 */{ 0 , 1 , 30 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354 +1170 */{ 5 , 1 , 19 , 54440 },/* 30 30 29 30 29 30 29 29 30 29 30 29 30 384 +1171 */{ 0 , 2 , 7 , 46240 },/* 30 29 30 30 29 30 29 29 30 29 30 29 0 354 +1172 */{ 0 , 1 , 27 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355 +1173 */{ 1 , 1 , 16 , 22184 },/* 29 30 29 30 29 30 30 29 30 29 30 29 30 384 +1174 */{ 0 , 2 , 4 , 19888 },/* 29 30 29 29 30 30 29 30 30 29 30 30 0 355 +1175 */{ 9 , 1 , 25 , 9648 },/* 29 29 30 29 29 30 29 30 30 29 30 30 29 383 +1176 */{ 0 , 2 , 12 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355 +1177 */{ 0 , 2 , 1 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354 +1178 */{ 6 , 1 , 21 , 43352 },/* 30 29 30 29 30 29 29 30 29 30 29 30 30 384 +1179 */{ 0 , 2 , 9 , 26960 },/* 29 30 30 29 30 29 29 30 29 30 29 30 0 354 +1180 */{ 0 , 1 , 29 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354 +1181 */{ 3 , 1 , 17 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 29 384 +1182 */{ 0 , 2 , 5 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 0 355 +1183 */{ 11 , 1 , 26 , 19304 },/* 29 30 29 29 30 29 30 30 29 30 30 29 30 384 +1184 */{ 0 , 2 , 14 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354 +1185 */{ 0 , 2 , 2 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355 +1186 */{ 7 , 1 , 23 , 21104 },/* 29 30 29 30 29 29 30 29 29 30 30 30 29 383 +1187 */{ 0 , 2 , 10 , 53392 },/* 30 30 29 30 29 29 30 29 29 30 30 0 0 325 +1188 */{ 0 , 1 , 1 , 29848 },/* 29 30 30 30 29 30 29 29 30 29 29 30 30 384 +1189 */{ 5 , 1 , 19 , 27304 },/* 29 30 30 29 30 29 30 29 30 29 30 29 30 384 +1190 */{ 0 , 2 , 7 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354 +1191 */{ 0 , 1 , 27 , 39760 },/* 30 29 29 30 30 29 30 30 29 30 29 30 0 355 +1192 */{ 2 , 1 , 17 , 19304 },/* 29 30 29 29 30 29 30 30 29 30 30 29 30 384 +1193 */{ 0 , 2 , 4 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354 +1194 */{ 10 , 1 , 24 , 42216 },/* 30 29 30 29 29 30 29 29 30 30 30 29 30 384 +1195 */{ 0 , 2 , 12 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354 +1196 */{ 0 , 2 , 1 , 53856 },/* 30 30 29 30 29 29 30 29 29 30 30 29 0 354 +1197 */{ 6 , 1 , 20 , 54568 },/* 30 30 29 30 29 30 29 30 29 29 30 29 30 384 +1198 */{ 0 , 2 , 8 , 46368 },/* 30 29 30 30 29 30 29 30 29 29 30 29 0 354 +1199 */{ 0 , 1 , 28 , 54944 },/* 30 30 29 30 29 30 30 29 30 29 30 29 0 355 +1200 */{ 2 , 1 , 18 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 29 384 +1201 */{ 0 , 2 , 5 , 38352 },/* 30 29 29 30 29 30 29 30 30 30 29 30 0 355 +1202 */{ 12 , 1 , 26 , 18904 },/* 29 30 29 29 30 29 29 30 30 30 29 30 30 384 +1203 */{ 0 , 2 , 14 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354 +1204 */{ 0 , 2 , 3 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354 +1205 */{ 8 , 1 , 22 , 43608 },/* 30 29 30 29 30 29 30 29 29 30 29 30 30 384 +1206 */{ 0 , 2 , 10 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 0 354 +1207 */{ 0 , 1 , 30 , 46400 },/* 30 29 30 30 29 30 29 30 29 30 29 29 0 354 +1208 */{ 4 , 1 , 19 , 46496 },/* 30 29 30 30 29 30 29 30 30 29 30 29 29 384 +1209 */{ 0 , 2 , 6 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 0 355 +1210 */{ 0 , 1 , 27 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355 +1211 */{ 2 , 1 , 17 , 18872 },/* 29 30 29 29 30 29 29 30 30 29 30 30 30 384 +1212 */{ 0 , 2 , 5 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354 +1213 */{ 9 , 1 , 24 , 25784 },/* 29 30 30 29 29 30 29 29 30 29 30 30 30 384 +1214 */{ 0 , 2 , 12 , 21680 },/* 29 30 29 30 29 30 29 29 30 29 30 30 0 354 +1215 */{ 0 , 2 , 1 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 0 354 +1216 */{ 7 , 1 , 21 , 27944 },/* 29 30 30 29 30 30 29 30 29 29 30 29 30 384 +1217 */{ 0 , 2 , 8 , 23232 },/* 29 30 29 30 30 29 30 29 30 30 29 29 0 354 +1218 */{ 0 , 1 , 28 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 0 355 +1219 */{ 3 , 1 , 18 , 37744 },/* 30 29 29 30 29 29 30 30 29 30 30 30 29 384 +1220 */{ 0 , 2 , 6 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354 +1221 */{ 12 , 1 , 25 , 51568 },/* 30 30 29 29 30 29 29 30 29 30 30 30 29 384 +1222 */{ 0 , 2 , 13 , 51552 },/* 30 30 29 29 30 29 29 30 29 30 30 29 0 354 +1223 */{ 0 , 2 , 2 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354 +1224 */{ 8 , 1 , 22 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 29 384 +1225 */{ 0 , 2 , 9 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355 +1226 */{ 0 , 1 , 30 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354 +1227 */{ 5 , 1 , 19 , 43736 },/* 30 29 30 29 30 29 30 29 30 30 29 30 30 385 +1228 */{ 0 , 2 , 8 , 9680 },/* 29 29 30 29 29 30 29 30 30 30 29 30 0 354 +1229 */{ 0 , 1 , 27 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354 +1230 */{ 2 , 1 , 16 , 51544 },/* 30 30 29 29 30 29 29 30 29 30 29 30 30 384 +1231 */{ 0 , 2 , 4 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354 +1232 */{ 9 , 1 , 24 , 54440 },/* 30 30 29 30 29 30 29 29 30 29 30 29 30 384 +1233 */{ 0 , 2 , 11 , 45728 },/* 30 29 30 30 29 29 30 29 30 29 30 29 0 354 +1234 */{ 0 , 1 , 31 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355 +1235 */{ 7 , 1 , 21 , 22184 },/* 29 30 29 30 29 30 30 29 30 29 30 29 30 384 +1236 */{ 0 , 2 , 9 , 19872 },/* 29 30 29 29 30 30 29 30 30 29 30 29 0 354 +1237 */{ 0 , 1 , 28 , 42416 },/* 30 29 30 29 29 30 29 30 30 29 30 30 0 355 +1238 */{ 4 , 1 , 18 , 21176 },/* 29 30 29 30 29 29 30 29 30 29 30 30 30 384 +1239 */{ 0 , 2 , 6 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354 +1240 */{ 12 , 1 , 26 , 43320 },/* 30 29 30 29 30 29 29 30 29 29 30 30 30 384 +1241 */{ 0 , 2 , 13 , 26928 },/* 29 30 30 29 30 29 29 30 29 29 30 30 0 354 +1242 */{ 0 , 2 , 2 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354 +1243 */{ 8 , 1 , 22 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 29 384 +1244 */{ 0 , 2 , 10 , 44624 },/* 30 29 30 29 30 30 30 29 29 30 29 30 0 355 +1245 */{ 0 , 1 , 30 , 19296 },/* 29 30 29 29 30 29 30 30 29 30 30 29 0 354 +1246 */{ 4 , 1 , 19 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 29 384 +1247 */{ 0 , 2 , 7 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355 +1248 */{ 0 , 1 , 28 , 21104 },/* 29 30 29 30 29 29 30 29 29 30 30 30 0 354 +1249 */{ 2 , 1 , 16 , 26928 },/* 29 30 30 29 30 29 29 30 29 29 30 30 29 383 +1250 */{ 0 , 2 , 3 , 58672 },/* 30 30 30 29 29 30 29 30 29 29 30 30 0 355 +1251 */{ 10 , 1 , 24 , 27800 },/* 29 30 30 29 30 30 29 29 30 29 29 30 30 384 +1252 */{ 0 , 2 , 12 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354 +1253 */{ 0 , 1 , 31 , 23248 },/* 29 30 29 30 30 29 30 29 30 30 29 30 0 355 +1254 */{ 6 , 1 , 21 , 19304 },/* 29 30 29 29 30 29 30 30 29 30 30 29 30 384 +1255 */{ 0 , 2 , 9 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354 +1256 */{ 0 , 1 , 29 , 42208 },/* 30 29 30 29 29 30 29 29 30 30 30 29 0 354 +1257 */{ 4 , 1 , 17 , 53864 },/* 30 30 29 30 29 29 30 29 29 30 30 29 30 384 +1258 */{ 0 , 2 , 5 , 53856 },/* 30 30 29 30 29 29 30 29 29 30 30 29 0 354 +1259 */{ 11 , 1 , 25 , 54568 },/* 30 30 29 30 29 30 29 30 29 29 30 29 30 384 +1260 */{ 0 , 2 , 13 , 46368 },/* 30 29 30 30 29 30 29 30 29 29 30 29 0 354 +1261 */{ 0 , 2 , 1 , 46752 },/* 30 29 30 30 29 30 30 29 30 29 30 29 0 355 +1262 */{ 9 , 1 , 22 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 29 384 +1263 */{ 0 , 2 , 10 , 21872 },/* 29 30 29 30 29 30 29 30 29 30 30 30 0 355 +1264 */{ 0 , 1 , 31 , 18896 },/* 29 30 29 29 30 29 29 30 30 30 29 30 0 354 +1265 */{ 5 , 1 , 19 , 42200 },/* 30 29 30 29 29 30 29 29 30 30 29 30 30 384 +1266 */{ 0 , 2 , 7 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354 +1267 */{ 0 , 1 , 27 , 43600 },/* 30 29 30 29 30 29 30 29 29 30 29 30 0 354 +1268 */{ 1 , 1 , 16 , 46376 },/* 30 29 30 30 29 30 29 30 29 29 30 29 30 384 +1269 */{ 0 , 2 , 3 , 46368 },/* 30 29 30 30 29 30 29 30 29 29 30 29 0 354 +1270 */{ 11 , 1 , 23 , 46528 },/* 30 29 30 30 29 30 29 30 30 30 29 29 29 384 +1271 */{ 0 , 2 , 11 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 0 355 +1272 */{ 0 , 2 , 1 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355 +1273 */{ 6 , 1 , 21 , 18872 },/* 29 30 29 29 30 29 29 30 30 29 30 30 30 384 +1274 */{ 0 , 2 , 9 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354 +1275 */{ 0 , 1 , 29 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 0 354 +1276 */{ 3 , 1 , 18 , 27224 },/* 29 30 30 29 30 29 30 29 29 30 29 30 30 384 +1277 */{ 0 , 2 , 5 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 0 354 +1278 */{ 11 , 1 , 25 , 27432 },/* 29 30 30 29 30 29 30 30 29 29 30 29 30 384 +1279 */{ 0 , 2 , 13 , 23232 },/* 29 30 29 30 30 29 30 29 30 30 29 29 0 354 +1280 */{ 0 , 2 , 2 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 0 355 +1281 */{ 8 , 1 , 22 , 10984 },/* 29 29 30 29 30 29 30 29 30 30 30 29 30 384 +1282 */{ 0 , 2 , 10 , 18912 },/* 29 30 29 29 30 29 29 30 30 30 30 29 0 354 +1283 */{ 0 , 1 , 30 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354 +1284 */{ 5 , 1 , 19 , 53848 },/* 30 30 29 30 29 29 30 29 29 30 29 30 30 384 +1285 */{ 0 , 2 , 6 , 45648 },/* 30 29 30 30 29 29 30 29 29 30 29 30 0 354 +1286 */{ 0 , 1 , 26 , 46368 },/* 30 29 30 30 29 30 29 30 29 29 30 29 0 354 +1287 */{ 2 , 1 , 15 , 62096 },/* 30 30 30 30 29 29 30 29 30 29 29 30 29 384 +1288 */{ 0 , 2 , 3 , 46496 },/* 30 29 30 30 29 30 29 30 30 29 30 29 0 355 +1289 */{ 10 , 1 , 23 , 38352 },/* 30 29 29 30 29 30 29 30 30 30 29 30 29 384 +1290 */{ 0 , 2 , 11 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355 +1291 */{ 0 , 2 , 1 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354 +1292 */{ 6 , 1 , 21 , 42168 },/* 30 29 30 29 29 30 29 29 30 29 30 30 30 384 +1293 */{ 0 , 2 , 8 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354 +1294 */{ 0 , 1 , 28 , 43600 },/* 30 29 30 29 30 29 30 29 29 30 29 30 0 354 +1295 */{ 4 , 1 , 17 , 46376 },/* 30 29 30 30 29 30 29 30 29 29 30 29 30 384 +1296 */{ 0 , 2 , 5 , 27968 },/* 29 30 30 29 30 30 29 30 29 30 29 29 0 354 +1297 */{ 12 , 1 , 24 , 44384 },/* 30 29 30 29 30 30 29 30 29 30 30 29 29 384 +1298 */{ 0 , 2 , 12 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 0 355 +1299 */{ 0 , 2 , 2 , 37744 },/* 30 29 29 30 29 29 30 30 29 30 30 30 0 355 +1300 */{ 8 , 1 , 23 , 2424 },/* 29 29 29 29 30 29 29 30 29 30 30 30 30 383 +1301 */{ 0 , 2 , 10 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354 +1302 */{ 0 , 1 , 30 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 0 354 +1303 */{ 5 , 1 , 19 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 29 383 +1304 */{ 0 , 2 , 6 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 0 355 +1305 */{ 0 , 1 , 26 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354 +1306 */{ 1 , 1 , 15 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 29 384 +1307 */{ 0 , 2 , 3 , 42720 },/* 30 29 30 29 29 30 30 29 30 30 30 29 0 355 +1308 */{ 11 , 1 , 24 , 37608 },/* 30 29 29 30 29 29 30 29 30 30 30 29 30 384 +1309 */{ 0 , 2 , 11 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354 +1310 */{ 0 , 1 , 31 , 51552 },/* 30 30 29 29 30 29 29 30 29 30 30 29 0 354 +1311 */{ 7 , 1 , 20 , 54440 },/* 30 30 29 30 29 30 29 29 30 29 30 29 30 384 +1312 */{ 0 , 2 , 8 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354 +1313 */{ 0 , 1 , 27 , 54608 },/* 30 30 29 30 29 30 29 30 29 30 29 30 0 355 +1314 */{ 3 , 1 , 17 , 23208 },/* 29 30 29 30 30 29 30 29 30 29 30 29 30 384 +1315 */{ 0 , 2 , 5 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354 +1316 */{ 0 , 1 , 25 , 42704 },/* 30 29 30 29 29 30 30 29 30 30 29 30 0 355 +1317 */{ 1 , 1 , 14 , 37608 },/* 30 29 29 30 29 29 30 29 30 30 30 29 30 384 +1318 */{ 0 , 2 , 2 , 37552 },/* 30 29 29 30 29 29 30 29 30 29 30 30 0 354 +1319 */{ 8 , 1 , 22 , 42328 },/* 30 29 30 29 29 30 29 30 29 30 29 30 30 384 +1320 */{ 0 , 2 , 10 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354 +1321 */{ 0 , 1 , 29 , 45728 },/* 30 29 30 30 29 29 30 29 30 29 30 29 0 354 +1322 */{ 5 , 1 , 18 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 29 384 +1323 */{ 0 , 2 , 6 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355 +1324 */{ 0 , 1 , 27 , 19872 },/* 29 30 29 29 30 30 29 30 30 29 30 29 0 354 +1325 */{ 1 , 1 , 15 , 42448 },/* 30 29 30 29 29 30 29 30 30 30 29 30 29 384 +1326 */{ 0 , 2 , 3 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355 +1327 */{ 9 , 1 , 24 , 21176 },/* 29 30 29 30 29 29 30 29 30 29 30 30 30 384 +1328 */{ 0 , 2 , 12 , 21104 },/* 29 30 29 30 29 29 30 29 29 30 30 30 0 354 +1329 */{ 0 , 1 , 31 , 26928 },/* 29 30 30 29 30 29 29 30 29 29 30 30 0 354 +1330 */{ 7 , 1 , 20 , 27288 },/* 29 30 30 29 30 29 30 29 30 29 29 30 30 384 +1331 */{ 0 , 2 , 8 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354 +1332 */{ 0 , 1 , 28 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 0 355 +1333 */{ 3 , 1 , 17 , 19368 },/* 29 30 29 29 30 29 30 30 30 29 30 29 30 384 +1334 */{ 0 , 2 , 5 , 19296 },/* 29 30 29 29 30 29 30 30 29 30 30 29 0 354 +1335 */{ 12 , 1 , 25 , 42608 },/* 30 29 30 29 29 30 30 29 29 30 30 30 29 384 +1336 */{ 0 , 2 , 13 , 41696 },/* 30 29 30 29 29 29 30 29 30 30 30 29 0 354 +1337 */{ 0 , 2 , 1 , 53600 },/* 30 30 29 30 29 29 29 30 29 30 30 29 0 354 +1338 */{ 8 , 1 , 21 , 59696 },/* 30 30 30 29 30 29 29 30 29 29 30 30 29 384 +1339 */{ 0 , 2 , 9 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354 +1340 */{ 0 , 1 , 29 , 55968 },/* 30 30 29 30 30 29 30 29 30 29 30 29 0 355 +1341 */{ 5 , 1 , 18 , 23376 },/* 29 30 29 30 30 29 30 30 29 30 29 30 29 384 +1342 */{ 0 , 2 , 6 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 0 355 +1343 */{ 0 , 1 , 27 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354 +1344 */{ 2 , 1 , 16 , 41704 },/* 30 29 30 29 29 29 30 29 30 30 30 29 30 384 +1345 */{ 0 , 2 , 3 , 41680 },/* 30 29 30 29 29 29 30 29 30 30 29 30 0 354 +1346 */{ 10 , 1 , 23 , 53592 },/* 30 30 29 30 29 29 29 30 29 30 29 30 30 384 +1347 */{ 0 , 2 , 11 , 43600 },/* 30 29 30 29 30 29 30 29 29 30 29 30 0 354 +1348 */{ 0 , 1 , 31 , 46368 },/* 30 29 30 30 29 30 29 30 29 29 30 29 0 354 +1349 */{ 7 , 1 , 19 , 54944 },/* 30 30 29 30 29 30 30 29 30 29 30 29 29 384 +1350 */{ 0 , 2 , 7 , 44448 },/* 30 29 30 29 30 30 29 30 30 29 30 29 0 355 +1351 */{ 0 , 1 , 28 , 21968 },/* 29 30 29 30 29 30 29 30 30 30 29 30 0 355 +1352 */{ 3 , 1 , 18 , 18904 },/* 29 30 29 29 30 29 29 30 30 30 29 30 30 384 +1353 */{ 0 , 2 , 5 , 17840 },/* 29 30 29 29 29 30 29 30 30 29 30 30 0 354 +1354 */{ 0 , 1 , 25 , 41648 },/* 30 29 30 29 29 29 30 29 30 29 30 30 0 354 +1355 */{ 1 , 1 , 14 , 53592 },/* 30 30 29 30 29 29 29 30 29 30 29 30 30 384 +1356 */{ 0 , 2 , 2 , 43600 },/* 30 29 30 29 30 29 30 29 29 30 29 30 0 354 +1357 */{ 9 , 1 , 21 , 46376 },/* 30 29 30 30 29 30 29 30 29 29 30 29 30 384 +1358 */{ 0 , 2 , 9 , 27424 },/* 29 30 30 29 30 29 30 30 29 29 30 29 0 354 +1359 */{ 0 , 1 , 29 , 44384 },/* 30 29 30 29 30 30 29 30 29 30 30 29 0 355 +1360 */{ 5 , 1 , 19 , 21936 },/* 29 30 29 30 29 30 29 30 30 29 30 30 29 384 +1361 */{ 0 , 2 , 6 , 37744 },/* 30 29 29 30 29 29 30 30 29 30 30 30 0 355 +1362 */{ 0 , 1 , 27 , 17776 },/* 29 30 29 29 29 30 29 30 29 30 30 30 0 354 +1363 */{ 3 , 1 , 16 , 41656 },/* 30 29 30 29 29 29 30 29 30 29 30 30 30 384 +1364 */{ 0 , 2 , 4 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354 +1365 */{ 10 , 1 , 23 , 43600 },/* 30 29 30 29 30 29 30 29 29 30 29 30 29 383 +1366 */{ 0 , 2 , 10 , 55632 },/* 30 30 29 30 30 29 29 30 29 30 29 30 0 355 +1367 */{ 0 , 1 , 31 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354 +1368 */{ 7 , 1 , 20 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 29 384 +1369 */{ 0 , 2 , 7 , 42720 },/* 30 29 30 29 29 30 30 29 30 30 30 29 0 355 +1370 */{ 0 , 1 , 28 , 21216 },/* 29 30 29 30 29 29 30 29 30 30 30 29 0 354 +1371 */{ 3 , 1 , 17 , 50544 },/* 30 30 29 29 29 30 29 30 29 30 30 30 29 384 +1372 */{ 0 , 2 , 5 , 42336 },/* 30 29 30 29 29 30 29 30 29 30 30 29 0 354 +1373 */{ 11 , 1 , 24 , 53928 },/* 30 30 29 30 29 29 30 29 30 29 30 29 30 384 +1374 */{ 0 , 2 , 12 , 53920 },/* 30 30 29 30 29 29 30 29 30 29 30 29 0 354 +1375 */{ 0 , 2 , 1 , 54608 },/* 30 30 29 30 29 30 29 30 29 30 29 30 0 355 +1376 */{ 9 , 1 , 22 , 23208 },/* 29 30 29 30 30 29 30 29 30 29 30 29 30 384 +1377 */{ 0 , 2 , 9 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354 +1378 */{ 0 , 1 , 29 , 42704 },/* 30 29 30 29 29 30 30 29 30 30 29 30 0 355 +1379 */{ 5 , 1 , 19 , 21224 },/* 29 30 29 30 29 29 30 29 30 30 30 29 30 384 +1380 */{ 0 , 2 , 7 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354 +1381 */{ 0 , 1 , 26 , 43216 },/* 30 29 30 29 30 29 29 29 30 30 29 30 0 354 +1382 */{ 2 , 1 , 15 , 53928 },/* 30 30 29 30 29 29 30 29 30 29 30 29 30 384 +1383 */{ 0 , 2 , 3 , 45728 },/* 30 29 30 30 29 29 30 29 30 29 30 29 0 354 +1384 */{ 10 , 1 , 23 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 29 384 +1385 */{ 0 , 2 , 10 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355 +1386 */{ 0 , 1 , 31 , 19872 },/* 29 30 29 29 30 30 29 30 30 29 30 29 0 354 +1387 */{ 6 , 1 , 20 , 42448 },/* 30 29 30 29 29 30 29 30 30 30 29 30 29 384 +1388 */{ 0 , 2 , 8 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355 +1389 */{ 0 , 1 , 28 , 20912 },/* 29 30 29 30 29 29 29 30 30 29 30 30 0 354 +1390 */{ 4 , 1 , 17 , 43192 },/* 30 29 30 29 30 29 29 29 30 29 30 30 30 384 +1391 */{ 0 , 2 , 5 , 25904 },/* 29 30 30 29 29 30 29 30 29 29 30 30 0 354 +1392 */{ 12 , 1 , 25 , 27288 },/* 29 30 30 29 30 29 30 29 30 29 29 30 30 384 +1393 */{ 0 , 2 , 12 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354 +1394 */{ 0 , 2 , 1 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 0 355 +1395 */{ 9 , 1 , 22 , 11176 },/* 29 29 30 29 30 29 30 30 30 29 30 29 30 384 +1396 */{ 0 , 2 , 10 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354 +1397 */{ 0 , 1 , 29 , 50032 },/* 30 30 29 29 29 29 30 30 29 30 30 30 0 355 +1398 */{ 5 , 1 , 19 , 20848 },/* 29 30 29 30 29 29 29 30 29 30 30 30 29 383 +1399 */{ 0 , 2 , 6 , 51552 },/* 30 30 29 29 30 29 29 30 29 30 30 29 0 354 +1400 */{ 0 , 1 , 26 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354 +1401 */{ 3 , 1 , 15 , 27280 },/* 29 30 30 29 30 29 30 29 30 29 29 30 29 383 +1402 */{ 0 , 2 , 2 , 55968 },/* 30 30 29 30 30 29 30 29 30 29 30 29 0 355 +1403 */{ 11 , 1 , 23 , 23376 },/* 29 30 29 30 30 29 30 30 29 30 29 30 29 384 +1404 */{ 0 , 2 , 11 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 0 355 +1405 */{ 0 , 1 , 31 , 10976 },/* 29 29 30 29 30 29 30 29 30 30 30 29 0 354 +1406 */{ 7 , 1 , 20 , 41704 },/* 30 29 30 29 29 29 30 29 30 30 30 29 30 384 +1407 */{ 0 , 2 , 8 , 41680 },/* 30 29 30 29 29 29 30 29 30 30 29 30 0 354 +1408 */{ 0 , 1 , 28 , 53584 },/* 30 30 29 30 29 29 29 30 29 30 29 30 0 354 +1409 */{ 4 , 1 , 16 , 54440 },/* 30 30 29 30 29 30 29 29 30 29 30 29 30 384 +1410 */{ 0 , 2 , 4 , 46368 },/* 30 29 30 30 29 30 29 30 29 29 30 29 0 354 +1411 */{ 12 , 1 , 24 , 46736 },/* 30 29 30 30 29 30 30 29 30 29 29 30 29 384 +1412 */{ 0 , 2 , 12 , 44448 },/* 30 29 30 29 30 30 29 30 30 29 30 29 0 355 +1413 */{ 0 , 2 , 1 , 21968 },/* 29 30 29 30 29 30 29 30 30 30 29 30 0 355 +1414 */{ 9 , 1 , 22 , 9688 },/* 29 29 30 29 29 30 29 30 30 30 29 30 30 384 +1415 */{ 0 , 2 , 10 , 17840 },/* 29 30 29 29 29 30 29 30 30 29 30 30 0 354 +1416 */{ 0 , 1 , 30 , 41648 },/* 30 29 30 29 29 29 30 29 30 29 30 30 0 354 +1417 */{ 5 , 1 , 18 , 43352 },/* 30 29 30 29 30 29 29 30 29 30 29 30 30 384 +1418 */{ 0 , 2 , 6 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354 +1419 */{ 0 , 1 , 26 , 46368 },/* 30 29 30 30 29 30 29 30 29 29 30 29 0 354 +1420 */{ 1 , 1 , 15 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 29 384 +1421 */{ 0 , 2 , 2 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 0 355 +1422 */{ 12 , 1 , 23 , 21936 },/* 29 30 29 30 29 30 29 30 30 29 30 30 29 384 +1423 */{ 0 , 2 , 11 , 19312 },/* 29 30 29 29 30 29 30 30 29 30 30 30 0 355 +1424 */{ 0 , 2 , 1 , 17776 },/* 29 30 29 29 29 30 29 30 29 30 30 30 0 354 +1425 */{ 7 , 1 , 20 , 21176 },/* 29 30 29 30 29 29 30 29 30 29 30 30 30 384 +1426 */{ 0 , 2 , 8 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354 +1427 */{ 0 , 1 , 28 , 26960 },/* 29 30 30 29 30 29 29 30 29 30 29 30 0 354 +1428 */{ 4 , 1 , 17 , 27816 },/* 29 30 30 29 30 30 29 29 30 29 30 29 30 384 +1429 */{ 0 , 2 , 4 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354 +1430 */{ 12 , 1 , 24 , 39760 },/* 30 29 29 30 30 29 30 30 29 30 29 30 29 384 +1431 */{ 0 , 2 , 12 , 42720 },/* 30 29 30 29 29 30 30 29 30 30 30 29 0 355 +1432 */{ 0 , 2 , 2 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354 +1433 */{ 8 , 1 , 21 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 29 384 +1434 */{ 0 , 2 , 9 , 42336 },/* 30 29 30 29 29 30 29 30 29 30 30 29 0 354 +1435 */{ 0 , 1 , 29 , 53920 },/* 30 30 29 30 29 29 30 29 30 29 30 29 0 354 +1436 */{ 6 , 1 , 18 , 59728 },/* 30 30 30 29 30 29 29 30 29 30 29 30 29 384 +1437 */{ 0 , 2 , 5 , 54608 },/* 30 30 29 30 29 30 29 30 29 30 29 30 0 355 +1438 */{ 0 , 1 , 26 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354 +1439 */{ 2 , 1 , 15 , 43728 },/* 30 29 30 29 30 29 30 29 30 30 29 30 29 384 +1440 */{ 0 , 2 , 3 , 38368 },/* 30 29 29 30 29 30 29 30 30 30 30 29 0 355 +1441 */{ 11 , 1 , 23 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384 +1442 */{ 0 , 2 , 11 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354 +1443 */{ 0 , 1 , 31 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354 +1444 */{ 7 , 1 , 20 , 53872 },/* 30 30 29 30 29 29 30 29 29 30 30 30 29 384 +1445 */{ 0 , 2 , 7 , 45728 },/* 30 29 30 30 29 29 30 29 30 29 30 29 0 354 +1446 */{ 0 , 1 , 27 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355 +1447 */{ 4 , 1 , 17 , 22184 },/* 29 30 29 30 29 30 30 29 30 29 30 29 30 384 +1448 */{ 0 , 2 , 5 , 11680 },/* 29 29 30 29 30 30 29 30 30 29 30 29 0 354 +1449 */{ 0 , 1 , 24 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355 +1450 */{ 1 , 1 , 14 , 19128 },/* 29 30 29 29 30 29 30 29 30 29 30 30 30 384 +1451 */{ 0 , 2 , 2 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354 +1452 */{ 9 , 1 , 22 , 42168 },/* 30 29 30 29 29 30 29 29 30 29 30 30 30 384 +1453 */{ 0 , 2 , 9 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 0 354 +1454 */{ 0 , 1 , 29 , 27280 },/* 29 30 30 29 30 29 30 29 30 29 29 30 0 354 +1455 */{ 6 , 1 , 18 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 29 384 +1456 */{ 0 , 2 , 6 , 27472 },/* 29 30 30 29 30 29 30 30 29 30 29 30 0 355 +1457 */{ 0 , 1 , 26 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354 +1458 */{ 2 , 1 , 15 , 38256 },/* 30 29 29 30 29 30 29 30 29 30 30 30 29 384 +1459 */{ 0 , 2 , 3 , 37744 },/* 30 29 29 30 29 29 30 30 29 30 30 30 0 355 +1460 */{ 11 , 1 , 24 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 29 383 +1461 */{ 0 , 2 , 10 , 51552 },/* 30 30 29 29 30 29 29 30 29 30 30 29 0 354 +1462 */{ 0 , 1 , 30 , 58544 },/* 30 30 30 29 29 30 29 29 30 29 30 30 0 355 +1463 */{ 7 , 1 , 20 , 27280 },/* 29 30 30 29 30 29 30 29 30 29 29 30 29 383 +1464 */{ 0 , 2 , 7 , 55968 },/* 30 30 29 30 30 29 30 29 30 29 30 29 0 355 +1465 */{ 0 , 1 , 27 , 23248 },/* 29 30 29 30 30 29 30 29 30 30 29 30 0 355 +1466 */{ 3 , 1 , 17 , 11112 },/* 29 29 30 29 30 29 30 30 29 30 30 29 30 384 +1467 */{ 0 , 2 , 5 , 9952 },/* 29 29 30 29 29 30 30 29 30 30 30 29 0 354 +1468 */{ 0 , 1 , 25 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354 +1469 */{ 2 , 1 , 13 , 51560 },/* 30 30 29 29 30 29 29 30 29 30 30 29 30 384 +1470 */{ 0 , 2 , 1 , 51536 },/* 30 30 29 29 30 29 29 30 29 30 29 30 0 354 +1471 */{ 9 , 1 , 21 , 54440 },/* 30 30 29 30 29 30 29 29 30 29 30 29 30 384 +1472 */{ 0 , 2 , 9 , 46240 },/* 30 29 30 30 29 30 29 29 30 29 30 29 0 354 +1473 */{ 0 , 1 , 28 , 46736 },/* 30 29 30 30 29 30 30 29 30 29 29 30 0 355 +1474 */{ 6 , 1 , 18 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 29 384 +1475 */{ 0 , 2 , 6 , 21936 },/* 29 30 29 30 29 30 29 30 30 29 30 30 0 355 +1476 */{ 0 , 1 , 27 , 9680 },/* 29 29 30 29 29 30 29 30 30 30 29 30 0 354 +1477 */{ 2 , 1 , 15 , 37592 },/* 30 29 29 30 29 29 30 29 30 30 29 30 30 384 +1478 */{ 0 , 2 , 3 , 37552 },/* 30 29 29 30 29 29 30 29 30 29 30 30 0 354 +1479 */{ 10 , 1 , 23 , 43352 },/* 30 29 30 29 30 29 29 30 29 30 29 30 30 384 +1480 */{ 0 , 2 , 11 , 26960 },/* 29 30 30 29 30 29 29 30 29 30 29 30 0 354 +1481 */{ 0 , 1 , 30 , 29856 },/* 29 30 30 30 29 30 29 29 30 29 30 29 0 354 +1482 */{ 8 , 1 , 19 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 29 384 +1483 */{ 0 , 2 , 7 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 0 355 +1484 */{ 0 , 1 , 28 , 21424 },/* 29 30 29 30 29 29 30 30 30 29 30 30 0 355 +1485 */{ 4 , 1 , 17 , 9656 },/* 29 29 30 29 29 30 29 30 30 29 30 30 30 384 +1486 */{ 0 , 2 , 5 , 9584 },/* 29 29 30 29 29 30 29 30 29 30 30 30 0 354 +1487 */{ 0 , 1 , 25 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354 +1488 */{ 1 , 1 , 14 , 43352 },/* 30 29 30 29 30 29 29 30 29 30 29 30 30 384 +1489 */{ 0 , 2 , 1 , 26960 },/* 29 30 30 29 30 29 29 30 29 30 29 30 0 354 +1490 */{ 9 , 1 , 21 , 27304 },/* 29 30 30 29 30 29 30 29 30 29 30 29 30 384 +1491 */{ 0 , 2 , 9 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354 +1492 */{ 0 , 1 , 29 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 0 355 +1493 */{ 5 , 1 , 18 , 19304 },/* 29 30 29 29 30 29 30 30 29 30 30 29 30 384 +1494 */{ 0 , 2 , 6 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354 +1495 */{ 0 , 1 , 26 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355 +1496 */{ 3 , 1 , 16 , 21104 },/* 29 30 29 30 29 29 30 29 29 30 30 30 29 383 +1497 */{ 0 , 2 , 2 , 53920 },/* 30 30 29 30 29 29 30 29 30 29 30 29 0 354 +1498 */{ 11 , 1 , 22 , 55632 },/* 30 30 29 30 30 29 29 30 29 30 29 30 29 384 +1499 */{ 0 , 2 , 10 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355 +1500 */{ 0 , 1 , 31 , 5792 },/* 29 29 29 30 29 30 30 29 30 29 30 29 0 353 +1501 */{ 7 , 1 , 19 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 29 384 +1502 */{ 0 , 2 , 7 , 38352 },/* 30 29 29 30 29 30 29 30 30 30 29 30 0 355 +1503 */{ 0 , 1 , 28 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354 +1504 */{ 4 , 1 , 17 , 42200 },/* 30 29 30 29 29 30 29 29 30 30 29 30 30 384 +1505 */{ 0 , 2 , 4 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354 +1506 */{ 0 , 1 , 24 , 53840 },/* 30 30 29 30 29 29 30 29 29 30 29 30 0 354 +1507 */{ 1 , 1 , 13 , 54608 },/* 30 30 29 30 29 30 29 30 29 30 29 30 29 384 +1508 */{ 0 , 2 , 1 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355 +1509 */{ 9 , 1 , 21 , 22184 },/* 29 30 29 30 29 30 30 29 30 29 30 29 30 384 +1510 */{ 0 , 2 , 9 , 11680 },/* 29 29 30 29 30 30 29 30 30 29 30 29 0 354 +1511 */{ 0 , 1 , 29 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355 +1512 */{ 5 , 1 , 19 , 18872 },/* 29 30 29 29 30 29 29 30 30 29 30 30 30 384 +1513 */{ 0 , 2 , 6 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354 +1514 */{ 0 , 1 , 26 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354 +1515 */{ 4 , 1 , 15 , 45656 },/* 30 29 30 30 29 29 30 29 29 30 29 30 30 384 +1516 */{ 0 , 2 , 3 , 27280 },/* 29 30 30 29 30 29 30 29 30 29 29 30 0 354 +1517 */{ 12 , 1 , 22 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 29 384 +1518 */{ 0 , 2 , 10 , 23376 },/* 29 30 29 30 30 29 30 30 29 30 29 30 0 355 +1519 */{ 0 , 1 , 31 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354 +1520 */{ 8 , 1 , 20 , 38256 },/* 30 29 29 30 29 30 29 30 29 30 30 30 29 384 +1521 */{ 0 , 2 , 7 , 37616 },/* 30 29 29 30 29 29 30 29 30 30 30 30 0 355 +1522 */{ 0 , 1 , 28 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354 +1523 */{ 4 , 1 , 17 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 29 383 +1524 */{ 0 , 2 , 4 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354 +1525 */{ 12 , 1 , 23 , 59984 },/* 30 30 30 29 30 29 30 29 29 30 29 30 29 384 +1526 */{ 0 , 2 , 11 , 54928 },/* 30 30 29 30 29 30 30 29 30 29 29 30 0 355 +1527 */{ 0 , 2 , 1 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 0 355 +1528 */{ 10 , 1 , 22 , 11112 },/* 29 29 30 29 30 29 30 30 29 30 30 29 30 384 +1529 */{ 0 , 2 , 9 , 9952 },/* 29 29 30 29 29 30 30 29 30 30 30 29 0 354 +1530 */{ 0 , 1 , 29 , 21216 },/* 29 30 29 30 29 29 30 29 30 30 30 29 0 354 +1531 */{ 6 , 1 , 18 , 51560 },/* 30 30 29 29 30 29 29 30 29 30 30 29 30 384 +1532 */{ 0 , 2 , 6 , 51536 },/* 30 30 29 29 30 29 29 30 29 30 29 30 0 354 +1533 */{ 0 , 1 , 25 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354 +1534 */{ 2 , 1 , 14 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 29 384 +1535 */{ 0 , 2 , 2 , 46480 },/* 30 29 30 30 29 30 29 30 30 29 29 30 0 355 +1536 */{ 12 , 1 , 23 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 29 384 +1537 */{ 0 , 2 , 10 , 21936 },/* 29 30 29 30 29 30 29 30 30 29 30 30 0 355 +1538 */{ 0 , 1 , 31 , 9680 },/* 29 29 30 29 29 30 29 30 30 30 29 30 0 354 +1539 */{ 7 , 1 , 20 , 37592 },/* 30 29 29 30 29 29 30 29 30 30 29 30 30 384 +1540 */{ 0 , 2 , 8 , 37552 },/* 30 29 29 30 29 29 30 29 30 29 30 30 0 354 +1541 */{ 0 , 1 , 27 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354 +1542 */{ 5 , 1 , 16 , 46248 },/* 30 29 30 30 29 30 29 29 30 29 30 29 30 384 +1543 */{ 0 , 2 , 4 , 27808 },/* 29 30 30 29 30 30 29 29 30 29 30 29 0 354 +1544 */{ 0 , 1 , 24 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355 +1545 */{ 1 , 1 , 13 , 21936 },/* 29 30 29 30 29 30 29 30 30 29 30 30 29 384 +1546 */{ 0 , 2 , 1 , 19376 },/* 29 30 29 29 30 29 30 30 30 29 30 30 0 355 +1547 */{ 9 , 1 , 22 , 9656 },/* 29 29 30 29 29 30 29 30 30 29 30 30 30 384 +1548 */{ 0 , 2 , 10 , 9584 },/* 29 29 30 29 29 30 29 30 29 30 30 30 0 354 +1549 */{ 0 , 1 , 29 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354 +1550 */{ 6 , 1 , 18 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 29 383 +1551 */{ 0 , 2 , 5 , 59728 },/* 30 30 30 29 30 29 29 30 29 30 29 30 0 355 +1552 */{ 0 , 1 , 26 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354 +1553 */{ 3 , 1 , 14 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 29 384 +1554 */{ 0 , 2 , 2 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 0 355 +1555 */{ 11 , 1 , 23 , 19304 },/* 29 30 29 29 30 29 30 30 29 30 30 29 30 384 +1556 */{ 0 , 2 , 11 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354 +1557 */{ 0 , 1 , 30 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355 +1558 */{ 7 , 1 , 20 , 21096 },/* 29 30 29 30 29 29 30 29 29 30 30 29 30 383 +1559 */{ 0 , 2 , 7 , 53856 },/* 30 30 29 30 29 29 30 29 29 30 30 29 0 354 +1560 */{ 0 , 1 , 27 , 55632 },/* 30 30 29 30 30 29 29 30 29 30 29 30 0 355 +1561 */{ 5 , 1 , 16 , 23208 },/* 29 30 29 30 30 29 30 29 30 29 30 29 30 384 +1562 */{ 0 , 2 , 4 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354 +1563 */{ 0 , 1 , 24 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 0 355 +1564 */{ 2 , 1 , 14 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384 +1565 */{ 0 , 2 , 1 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354 +1566 */{ 10 , 1 , 21 , 42200 },/* 30 29 30 29 29 30 29 29 30 30 29 30 30 384 +1567 */{ 0 , 2 , 9 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354 +1568 */{ 0 , 1 , 29 , 53840 },/* 30 30 29 30 29 29 30 29 29 30 29 30 0 354 +1569 */{ 6 , 1 , 17 , 54600 },/* 30 30 29 30 29 30 29 30 29 30 29 29 30 384 +1570 */{ 0 , 2 , 5 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355 +1571 */{ 0 , 1 , 26 , 13728 },/* 29 29 30 30 29 30 29 30 30 29 30 29 0 354 +1572 */{ 2 , 1 , 15 , 38352 },/* 30 29 29 30 29 30 29 30 30 30 29 30 29 384 +1573 */{ 0 , 2 , 2 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355 +1574 */{ 12 , 1 , 23 , 18872 },/* 29 30 29 29 30 29 29 30 30 29 30 30 30 384 +1575 */{ 0 , 2 , 11 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354 +1576 */{ 0 , 1 , 31 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354 +1577 */{ 8 , 1 , 19 , 45656 },/* 30 29 30 30 29 29 30 29 29 30 29 30 30 384 +1578 */{ 0 , 2 , 7 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 0 354 +1579 */{ 0 , 1 , 27 , 27968 },/* 29 30 30 29 30 30 29 30 29 30 29 29 0 354 +1580 */{ 4 , 1 , 16 , 44456 },/* 30 29 30 29 30 30 29 30 30 29 30 29 30 385 +1581 */{ 0 , 2 , 4 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354 +1582 */{ 0 , 1 , 24 , 39024 },/* 30 29 29 30 29 29 30 30 39 30 30 30 0 365 +1583 */{ 2 , 1 , 24 , 18808 },/* 29 30 29 29 30 29 29 30 29 30 30 30 30 384 +1584 */{ 0 , 2 , 12 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354 +1585 */{ 9 , 1 , 31 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 29 383 +1586 */{ 0 , 2 , 18 , 54440 },/* 30 30 29 30 29 30 29 29 30 29 30 29 30 384 +1587 */{ 0 , 3 , 9 , 53968 },/* 30 30 29 30 29 30 29 29 30 29 30 0 0 325 +1588 */{ 6 , 1 , 28 , 27464 },/* 29 30 30 29 30 29 30 30 29 30 29 29 30 384 +1589 */{ 0 , 2 , 15 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 0 355 +1590 */{ 0 , 2 , 5 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354 +1591 */{ 3 , 1 , 25 , 37616 },/* 30 29 29 30 29 29 30 29 30 30 30 30 29 384 +1592 */{ 0 , 2 , 13 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354 +1593 */{ 11 , 2 , 1 , 51560 },/* 30 30 29 29 30 29 29 30 29 30 30 29 30 384 +1594 */{ 0 , 2 , 20 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354 +1595 */{ 0 , 2 , 9 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354 +1596 */{ 8 , 1 , 29 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 29 384 +1597 */{ 0 , 2 , 16 , 46288 },/* 30 29 30 30 29 30 29 29 30 30 29 30 0 355 +1598 */{ 0 , 2 , 6 , 22192 },/* 29 30 29 30 29 30 30 29 30 29 30 30 0 355 +1599 */{ 4 , 1 , 27 , 9944 },/* 29 29 30 29 29 30 30 29 30 30 29 30 30 384 +1600 */{ 0 , 2 , 15 , 9680 },/* 29 29 30 29 29 30 29 30 30 30 29 30 0 354 +1601 */{ 0 , 2 , 3 , 37584 },/* 30 29 29 30 29 29 30 29 30 30 29 30 0 354 +1602 */{ 2 , 1 , 23 , 51608 },/* 30 30 29 29 30 29 29 30 30 29 29 30 30 384 +1603 */{ 0 , 2 , 11 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354 +1604 */{ 9 , 1 , 31 , 46248 },/* 30 29 30 30 29 30 29 29 30 29 30 29 30 384 +1605 */{ 0 , 2 , 18 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354 +1606 */{ 0 , 2 , 7 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355 +1607 */{ 6 , 1 , 28 , 21928 },/* 29 30 29 30 29 30 29 30 30 29 30 29 30 384 +1608 */{ 0 , 2 , 16 , 19376 },/* 29 30 29 29 30 29 30 30 30 29 30 30 0 355 +1609 */{ 0 , 2 , 5 , 9648 },/* 29 29 30 29 29 30 29 30 30 29 30 30 0 354 +1610 */{ 3 , 1 , 25 , 21176 },/* 29 30 29 30 29 29 30 29 30 29 30 30 30 384 +1611 */{ 0 , 2 , 13 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354 +1612 */{ 11 , 2 , 2 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 29 383 +1613 */{ 0 , 2 , 19 , 59728 },/* 30 30 30 29 30 29 29 30 29 30 29 30 0 355 +1614 */{ 0 , 2 , 9 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354 +1615 */{ 8 , 1 , 29 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 29 384 +1616 */{ 0 , 2 , 17 , 39760 },/* 30 29 29 30 30 29 30 30 29 30 29 30 0 355 +1617 */{ 0 , 2 , 6 , 19296 },/* 29 30 29 29 30 29 30 30 29 30 30 29 0 354 +1618 */{ 4 , 1 , 26 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 29 384 +1619 */{ 0 , 2 , 14 , 42224 },/* 30 29 30 29 29 30 29 29 30 30 30 30 0 355 +1620 */{ 0 , 2 , 4 , 21088 },/* 29 30 29 30 29 29 30 29 29 30 30 29 0 353 +1621 */{ 2 , 1 , 22 , 59696 },/* 30 30 30 29 30 29 29 30 29 29 30 30 29 384 +1622 */{ 0 , 2 , 10 , 54608 },/* 30 30 29 30 29 30 29 30 29 30 29 30 0 355 +1623 */{ 10 , 1 , 31 , 23208 },/* 29 30 29 30 30 29 30 29 30 29 30 29 30 384 +1624 */{ 0 , 2 , 19 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354 +1625 */{ 0 , 2 , 7 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 0 355 +1626 */{ 6 , 1 , 28 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384 +1627 */{ 0 , 2 , 16 , 18912 },/* 29 30 29 29 30 29 29 30 30 30 30 29 0 354 +1628 */{ 0 , 2 , 5 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354 +1629 */{ 4 , 1 , 24 , 53864 },/* 30 30 29 30 29 29 30 29 29 30 30 29 30 384 +1630 */{ 0 , 2 , 12 , 53840 },/* 30 30 29 30 29 29 30 29 29 30 29 30 0 354 +1631 */{ 11 , 2 , 1 , 54568 },/* 30 30 29 30 29 30 29 30 29 29 30 29 30 384 +1632 */{ 0 , 2 , 20 , 46400 },/* 30 29 30 30 29 30 29 30 29 30 29 29 0 354 +1633 */{ 0 , 2 , 8 , 46496 },/* 30 29 30 30 29 30 29 30 30 29 30 29 0 355 +1634 */{ 8 , 1 , 29 , 38352 },/* 30 29 29 30 29 30 29 30 30 30 29 30 29 384 +1635 */{ 0 , 2 , 17 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355 +1636 */{ 0 , 2 , 7 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354 +1637 */{ 4 , 1 , 26 , 42168 },/* 30 29 30 29 29 30 29 29 30 29 30 30 30 384 +1638 */{ 0 , 2 , 14 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354 +1639 */{ 0 , 2 , 3 , 43600 },/* 30 29 30 29 30 29 30 29 29 30 29 30 0 354 +1640 */{ 1 , 1 , 23 , 46376 },/* 30 29 30 30 29 30 29 30 29 29 30 29 30 384 +1641 */{ 0 , 2 , 10 , 27968 },/* 29 30 30 29 30 30 29 30 29 30 29 29 0 354 +1642 */{ 11 , 1 , 30 , 44456 },/* 30 29 30 29 30 30 29 30 30 29 30 29 30 385 +1643 */{ 0 , 2 , 19 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354 +1644 */{ 0 , 2 , 8 , 37744 },/* 30 29 29 30 29 29 30 30 29 30 30 30 0 355 +1645 */{ 6 , 1 , 28 , 18808 },/* 29 30 29 29 30 29 29 30 29 30 30 30 30 384 +1646 */{ 0 , 2 , 16 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354 +1647 */{ 0 , 2 , 5 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 0 354 +1648 */{ 3 , 1 , 24 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 29 383 +1649 */{ 0 , 2 , 11 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 0 355 +1650 */{ 11 , 2 , 1 , 27464 },/* 29 30 30 29 30 29 30 30 29 30 29 29 30 384 +1651 */{ 0 , 2 , 20 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 0 355 +1652 */{ 0 , 2 , 10 , 11168 },/* 29 29 30 29 30 29 30 30 30 29 30 29 0 354 +1653 */{ 7 , 1 , 29 , 37616 },/* 30 29 29 30 29 29 30 29 30 30 30 30 29 384 +1654 */{ 0 , 2 , 17 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354 +1655 */{ 0 , 2 , 6 , 51552 },/* 30 30 29 29 30 29 29 30 29 30 30 29 0 354 +1656 */{ 5 , 1 , 26 , 54440 },/* 30 30 29 30 29 30 29 29 30 29 30 29 30 384 +1657 */{ 0 , 2 , 13 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354 +1658 */{ 0 , 2 , 2 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 0 355 +1659 */{ 3 , 1 , 23 , 39592 },/* 30 29 29 30 30 29 30 29 30 29 30 29 30 384 +1660 */{ 0 , 2 , 11 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354 +1661 */{ 7 , 1 , 30 , 42704 },/* 30 29 30 29 29 30 30 29 30 30 29 30 29 384 +1662 */{ 0 , 2 , 18 , 42448 },/* 30 29 30 29 29 30 29 30 30 30 29 30 0 355 +1663 */{ 0 , 2 , 8 , 37584 },/* 30 29 29 30 29 29 30 29 30 30 29 30 0 354 +1664 */{ 6 , 1 , 28 , 43352 },/* 30 29 30 29 30 29 29 30 29 30 29 30 30 384 +1665 */{ 0 , 2 , 15 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354 +1666 */{ 0 , 2 , 4 , 46240 },/* 30 29 30 30 29 30 29 29 30 29 30 29 0 354 +1667 */{ 4 , 1 , 24 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 29 384 +1668 */{ 0 , 2 , 12 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355 +1669 */{ 0 , 2 , 1 , 21920 },/* 29 30 29 30 29 30 29 30 30 29 30 29 0 354 +1670 */{ 2 , 1 , 21 , 42448 },/* 30 29 30 29 29 30 29 30 30 30 29 30 29 384 +1671 */{ 0 , 2 , 9 , 42416 },/* 30 29 30 29 29 30 29 30 30 29 30 30 0 355 +1672 */{ 7 , 1 , 30 , 21176 },/* 29 30 29 30 29 29 30 29 30 29 30 30 30 384 +1673 */{ 0 , 2 , 17 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354 +1674 */{ 0 , 2 , 6 , 26928 },/* 29 30 30 29 30 29 29 30 29 29 30 30 0 354 +1675 */{ 5 , 1 , 26 , 29864 },/* 29 30 30 30 29 30 29 29 30 29 30 29 30 384 +1676 */{ 0 , 2 , 14 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354 +1677 */{ 0 , 2 , 2 , 44432 },/* 30 29 30 29 30 30 29 30 30 29 29 30 0 355 +1678 */{ 3 , 1 , 23 , 19880 },/* 29 30 29 29 30 30 29 30 30 29 30 29 30 384 +1679 */{ 0 , 2 , 11 , 19296 },/* 29 30 29 29 30 29 30 30 29 30 30 29 0 354 +1680 */{ 8 , 1 , 31 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 29 384 +1681 */{ 0 , 2 , 18 , 42208 },/* 30 29 30 29 29 30 29 29 30 30 30 29 0 354 +1682 */{ 0 , 2 , 7 , 53856 },/* 30 30 29 30 29 29 30 29 29 30 30 29 0 354 +1683 */{ 6 , 1 , 27 , 59696 },/* 30 30 30 29 30 29 29 30 29 29 30 30 29 384 +1684 */{ 0 , 2 , 15 , 54560 },/* 30 30 29 30 29 30 29 30 29 29 30 29 0 354 +1685 */{ 0 , 2 , 3 , 55968 },/* 30 30 29 30 30 29 30 29 30 29 30 29 0 355 +1686 */{ 4 , 1 , 24 , 27472 },/* 29 30 30 29 30 29 30 30 29 30 29 30 29 384 +1687 */{ 0 , 2 , 12 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 0 355 +1688 */{ 0 , 2 , 2 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354 +1689 */{ 3 , 1 , 21 , 42216 },/* 30 29 30 29 29 30 29 29 30 30 30 29 30 384 +1690 */{ 0 , 2 , 9 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354 +1691 */{ 7 , 1 , 29 , 53848 },/* 30 30 29 30 29 29 30 29 29 30 29 30 30 384 +1692 */{ 0 , 2 , 17 , 45136 },/* 30 29 29 32 29 29 29 29 29 30 29 30 0 354 +1693 */{ 0 , 2 , 5 , 54560 },/* 30 30 29 30 29 30 29 30 29 29 30 29 0 354 +1694 */{ 5 , 1 , 25 , 54944 },/* 30 30 29 30 29 30 30 29 30 29 30 29 29 384 +1695 */{ 0 , 2 , 13 , 46496 },/* 30 29 30 30 29 30 29 30 30 29 30 29 0 355 +1696 */{ 0 , 2 , 3 , 21968 },/* 29 30 29 30 29 30 29 30 30 30 29 30 0 355 +1697 */{ 3 , 1 , 23 , 19160 },/* 29 30 29 29 30 29 30 29 30 30 29 30 30 384 +1698 */{ 0 , 2 , 11 , 18896 },/* 29 30 29 29 30 29 29 30 30 30 29 30 0 354 +1699 */{ 7 , 1 , 31 , 42168 },/* 30 29 30 29 29 30 29 29 30 29 30 30 30 384 +1700 */{ 0 , 2 , 19 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354 +1701 */{ 0 , 2 , 8 , 43600 },/* 30 29 30 29 30 29 30 29 29 30 29 30 0 354 +1702 */{ 6 , 1 , 28 , 46376 },/* 30 29 30 30 29 30 29 30 29 29 30 29 30 384 +1703 */{ 0 , 2 , 16 , 27936 },/* 29 30 30 29 30 30 29 30 29 29 30 29 0 354 +1704 */{ 0 , 2 , 5 , 44448 },/* 30 29 30 29 30 30 29 30 30 29 30 29 0 355 +1705 */{ 4 , 1 , 25 , 21936 },/* 29 30 29 30 29 30 29 30 30 29 30 30 29 384 +1706 */{ 0 , 2 , 13 , 37744 },/* 30 29 29 30 29 29 30 30 29 30 30 30 0 355 +1707 */{ 0 , 2 , 3 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354 +1708 */{ 3 , 1 , 23 , 25784 },/* 29 30 30 29 29 30 29 29 30 29 30 30 30 384 +1709 */{ 0 , 2 , 10 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354 +1710 */{ 7 , 1 , 30 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 29 383 +1711 */{ 0 , 2 , 17 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 0 355 +1712 */{ 0 , 2 , 7 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354 +1713 */{ 5 , 1 , 26 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 29 384 +1714 */{ 0 , 2 , 14 , 43744 },/* 30 29 30 29 30 29 30 29 30 30 30 29 0 355 +1715 */{ 0 , 2 , 4 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354 +1716 */{ 3 , 1 , 24 , 51568 },/* 30 30 29 29 30 29 29 30 29 30 30 30 29 384 +1717 */{ 0 , 2 , 11 , 51552 },/* 30 30 29 29 30 29 29 30 29 30 30 29 0 354 +1718 */{ 8 , 1 , 31 , 54440 },/* 30 30 29 30 29 30 29 29 30 29 30 29 30 384 +1719 */{ 0 , 2 , 19 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354 +1720 */{ 0 , 2 , 8 , 54608 },/* 30 30 29 30 29 30 29 30 29 30 29 30 0 355 +1721 */{ 6 , 1 , 28 , 23208 },/* 29 30 29 30 30 29 30 29 30 29 30 29 30 384 +1722 */{ 0 , 2 , 16 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354 +1723 */{ 0 , 2 , 5 , 42704 },/* 30 29 30 29 29 30 30 29 30 30 29 30 0 355 +1724 */{ 4 , 1 , 26 , 21224 },/* 29 30 29 30 29 29 30 29 30 30 30 29 30 384 +1725 */{ 0 , 2 , 13 , 21200 },/* 29 30 29 30 29 29 30 29 30 30 29 30 0 354 +1726 */{ 0 , 2 , 2 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354 +1727 */{ 3 , 1 , 22 , 58536 },/* 30 30 30 29 29 30 29 29 30 29 30 29 30 384 +1728 */{ 0 , 2 , 10 , 46240 },/* 30 29 30 30 29 30 29 29 30 29 30 29 0 354 +1729 */{ 7 , 1 , 29 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 29 384 +1730 */{ 0 , 2 , 17 , 40272 },/* 30 29 29 30 30 30 29 30 29 30 29 30 0 355 +1731 */{ 0 , 2 , 7 , 21920 },/* 29 30 29 30 29 30 29 30 30 29 30 29 0 354 +1732 */{ 5 , 1 , 27 , 42448 },/* 30 29 30 29 29 30 29 30 30 30 29 30 29 384 +1733 */{ 0 , 2 , 14 , 42416 },/* 30 29 30 29 29 30 29 30 30 29 30 30 0 355 +1734 */{ 0 , 2 , 4 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354 +1735 */{ 4 , 1 , 24 , 43192 },/* 30 29 30 29 30 29 29 29 30 29 30 30 30 384 +1736 */{ 0 , 2 , 12 , 26928 },/* 29 30 30 29 30 29 29 30 29 29 30 30 0 354 +1737 */{ 9 , 1 , 31 , 27288 },/* 29 30 30 29 30 29 30 29 30 29 29 30 30 384 +1738 */{ 0 , 2 , 19 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354 +1739 */{ 0 , 2 , 8 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 0 355 +1740 */{ 6 , 1 , 29 , 19880 },/* 29 30 29 29 30 30 29 30 30 29 30 29 30 384 +1741 */{ 0 , 2 , 16 , 19296 },/* 29 30 29 29 30 29 30 30 29 30 30 29 0 354 +1742 */{ 0 , 2 , 5 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355 +1743 */{ 4 , 1 , 26 , 20848 },/* 29 30 29 30 29 29 29 30 29 30 30 30 29 383 +1744 */{ 0 , 2 , 13 , 53600 },/* 30 30 29 30 29 29 29 30 29 30 30 29 0 354 +1745 */{ 0 , 2 , 1 , 59696 },/* 30 30 30 29 30 29 29 30 29 29 30 30 0 355 +1746 */{ 3 , 1 , 22 , 27280 },/* 29 30 30 29 30 29 30 29 30 29 29 30 29 383 +1747 */{ 0 , 2 , 9 , 55968 },/* 30 30 29 30 30 29 30 29 30 29 30 29 0 355 +1748 */{ 7 , 1 , 30 , 23376 },/* 29 30 29 30 30 29 30 30 29 30 29 30 29 384 +1749 */{ 0 , 2 , 17 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 0 355 +1750 */{ 0 , 2 , 7 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354 +1751 */{ 5 , 1 , 27 , 42200 },/* 30 29 30 29 29 30 29 29 30 30 29 30 30 384 +1752 */{ 0 , 2 , 15 , 41680 },/* 30 29 30 29 29 29 30 29 30 30 29 30 0 354 +1753 */{ 0 , 2 , 3 , 53592 },/* 30 30 29 30 29 29 29 30 29 30 29 30 30 384 +1754 */{ 4 , 2 , 22 , 43600 },/* 30 29 30 29 30 29 30 29 29 30 29 30 0 354 +1755 */{ 0 , 2 , 11 , 46368 },/* 30 29 30 30 29 30 29 30 29 29 30 29 0 354 +1756 */{ 9 , 1 , 31 , 54928 },/* 30 30 29 30 29 30 30 29 30 29 29 30 29 384 +1757 */{ 0 , 2 , 18 , 44448 },/* 30 29 30 29 30 30 29 30 30 29 30 29 0 355 +1758 */{ 0 , 2 , 8 , 21968 },/* 29 30 29 30 29 30 29 30 30 30 29 30 0 355 +1759 */{ 6 , 1 , 29 , 10968 },/* 29 29 30 29 30 29 30 29 30 30 29 30 30 384 +1760 */{ 0 , 2 , 17 , 17840 },/* 29 30 29 29 29 30 29 30 30 29 30 30 0 354 +1761 */{ 0 , 2 , 5 , 41648 },/* 30 29 30 29 29 29 30 29 30 29 30 30 0 354 +1762 */{ 5 , 1 , 25 , 45400 },/* 30 29 30 30 29 29 29 30 29 30 29 30 30 384 +1763 */{ 0 , 2 , 13 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354 +1764 */{ 0 , 2 , 2 , 46368 },/* 30 29 30 30 29 30 29 30 29 29 30 29 0 354 +1765 */{ 2 , 1 , 21 , 46480 },/* 30 29 30 30 29 30 29 30 30 29 29 30 29 384 +1766 */{ 0 , 2 , 9 , 44384 },/* 30 29 30 29 30 30 29 30 29 30 30 29 0 355 +1767 */{ 7 , 1 , 30 , 21936 },/* 29 30 29 30 29 30 29 30 30 29 30 30 29 384 +1768 */{ 0 , 2 , 18 , 21360 },/* 29 30 29 30 29 29 30 30 29 30 30 30 0 355 +1769 */{ 0 , 2 , 7 , 17776 },/* 29 30 29 29 29 30 29 30 29 30 30 30 0 354 +1770 */{ 5 , 1 , 27 , 25272 },/* 29 30 30 29 29 29 30 29 30 29 30 30 30 384 +1771 */{ 0 , 2 , 15 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354 +1772 */{ 0 , 2 , 4 , 26960 },/* 29 30 30 29 30 29 29 30 29 30 29 30 0 354 +1773 */{ 3 , 1 , 23 , 27816 },/* 29 30 30 29 30 30 29 29 30 29 30 29 30 384 +1774 */{ 0 , 2 , 11 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354 +1775 */{ 10 , 1 , 31 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 29 384 +1776 */{ 0 , 2 , 19 , 42704 },/* 30 29 30 29 29 30 30 29 30 30 29 30 0 355 +1777 */{ 0 , 2 , 8 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354 +1778 */{ 6 , 1 , 28 , 38256 },/* 30 29 29 30 29 30 29 30 29 30 30 30 29 384 +1779 */{ 0 , 2 , 16 , 42336 },/* 30 29 30 29 29 30 29 30 29 30 30 29 0 354 +1780 */{ 0 , 2 , 5 , 53920 },/* 30 30 29 30 29 29 30 29 30 29 30 29 0 354 +1781 */{ 5 , 1 , 24 , 59728 },/* 30 30 30 29 30 29 29 30 29 30 29 30 29 384 +1782 */{ 0 , 2 , 12 , 54608 },/* 30 30 29 30 29 30 29 30 29 30 29 30 0 355 +1783 */{ 0 , 2 , 2 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354 +1784 */{ 3 , 1 , 22 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 29 384 +1785 */{ 0 , 2 , 9 , 42704 },/* 30 29 30 29 29 30 30 29 30 30 29 30 0 355 +1786 */{ 7 , 1 , 30 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384 +1787 */{ 0 , 2 , 18 , 19120 },/* 29 30 29 29 30 29 30 29 30 29 30 30 0 354 +1788 */{ 0 , 2 , 7 , 43216 },/* 30 29 30 29 30 29 29 29 30 30 29 30 0 354 +1789 */{ 5 , 1 , 26 , 53928 },/* 30 30 29 30 29 29 30 29 30 29 30 29 30 384 +1790 */{ 0 , 2 , 14 , 45728 },/* 30 29 30 30 29 29 30 29 30 29 30 29 0 354 +1791 */{ 0 , 2 , 3 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355 +1792 */{ 4 , 1 , 24 , 22184 },/* 29 30 29 30 29 30 30 29 30 29 30 29 30 384 +1793 */{ 0 , 2 , 11 , 19872 },/* 29 30 29 29 30 30 29 30 30 29 30 29 0 354 +1794 */{ 0 , 1 , 31 , 38352 },/* 30 29 29 30 29 30 29 30 30 30 29 30 0 355 +1795 */{ 2 , 1 , 21 , 19128 },/* 29 30 29 29 30 29 30 29 30 29 30 30 30 384 +1796 */{ 0 , 2 , 9 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354 +1797 */{ 6 , 1 , 28 , 43192 },/* 30 29 30 29 30 29 29 29 30 29 30 30 30 384 +1798 */{ 0 , 2 , 16 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 0 354 +1799 */{ 0 , 2 , 5 , 27280 },/* 29 30 30 29 30 29 30 29 30 29 29 30 0 354 +1800 */{ 4 , 1 , 25 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 29 384 +1801 */{ 0 , 2 , 13 , 27472 },/* 29 30 30 29 30 29 30 30 29 30 29 30 0 355 +1802 */{ 0 , 2 , 3 , 11168 },/* 29 29 30 29 30 29 30 30 30 29 30 29 0 354 +1803 */{ 2 , 1 , 23 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 29 384 +1804 */{ 0 , 2 , 11 , 37744 },/* 30 29 29 30 29 29 30 30 29 30 30 30 0 355 +1805 */{ 6 , 1 , 31 , 20848 },/* 29 30 29 30 29 29 29 30 29 30 30 30 29 383 +1806 */{ 0 , 2 , 18 , 53600 },/* 30 30 29 30 29 29 29 30 29 30 30 29 0 354 +1807 */{ 0 , 2 , 7 , 58544 },/* 30 30 30 29 29 30 29 29 30 29 30 30 0 355 +1808 */{ 5 , 1 , 28 , 27280 },/* 29 30 30 29 30 29 30 29 30 29 29 30 29 383 +1809 */{ 0 , 2 , 14 , 55952 },/* 30 30 29 30 30 29 30 29 30 29 29 30 0 355 +1810 */{ 0 , 2 , 4 , 23376 },/* 29 30 29 30 30 29 30 30 29 30 29 30 0 355 +1811 */{ 3 , 1 , 25 , 11112 },/* 29 29 30 29 30 29 30 30 29 30 30 29 30 384 +1812 */{ 0 , 2 , 13 , 10976 },/* 29 29 30 29 30 29 30 29 30 30 30 29 0 354 +1813 */{ 0 , 2 , 1 , 41696 },/* 30 29 30 29 29 29 30 29 30 30 30 29 0 354 +1814 */{ 2 , 1 , 21 , 53608 },/* 30 30 29 30 29 29 29 30 29 30 30 29 30 384 +1815 */{ 0 , 2 , 9 , 51536 },/* 30 30 29 29 30 29 29 30 29 30 29 30 0 354 +1816 */{ 6 , 1 , 29 , 54440 },/* 30 30 29 30 29 30 29 29 30 29 30 29 30 384 +1817 */{ 0 , 2 , 16 , 46368 },/* 30 29 30 30 29 30 29 30 29 29 30 29 0 354 +1818 */{ 0 , 2 , 5 , 46736 },/* 30 29 30 30 29 30 30 29 30 29 29 30 0 355 +1819 */{ 4 , 1 , 26 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 29 384 +1820 */{ 0 , 2 , 14 , 21968 },/* 29 30 29 30 29 30 29 30 30 30 29 30 0 355 +1821 */{ 0 , 2 , 3 , 9680 },/* 29 29 30 29 29 30 29 30 30 30 29 30 0 354 +1822 */{ 3 , 1 , 23 , 41688 },/* 30 29 30 29 29 29 30 29 30 30 29 30 30 384 +1823 */{ 0 , 2 , 11 , 41648 },/* 30 29 30 29 29 29 30 29 30 29 30 30 0 354 +1824 */{ 7 , 1 , 31 , 43352 },/* 30 29 30 29 30 29 29 30 29 30 29 30 30 384 +1825 */{ 0 , 2 , 18 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354 +1826 */{ 0 , 2 , 7 , 46240 },/* 30 29 30 30 29 30 29 29 30 29 30 29 0 354 +1827 */{ 5 , 1 , 27 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 29 384 +1828 */{ 0 , 2 , 15 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355 +1829 */{ 0 , 2 , 4 , 21936 },/* 29 30 29 30 29 30 29 30 30 29 30 30 0 355 +1830 */{ 4 , 1 , 25 , 9656 },/* 29 29 30 29 29 30 29 30 30 29 30 30 30 384 +1831 */{ 0 , 2 , 13 , 9584 },/* 29 29 30 29 29 30 29 30 29 30 30 30 0 354 +1832 */{ 9 , 2 , 2 , 21176 },/* 29 30 29 30 29 29 30 29 30 29 30 30 30 384 +1833 */{ 0 , 2 , 20 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354 +1834 */{ 0 , 2 , 9 , 26960 },/* 29 30 30 29 30 29 29 30 29 30 29 30 0 354 +1835 */{ 6 , 1 , 29 , 27816 },/* 29 30 30 29 30 30 29 29 30 29 30 29 30 384 +1836 */{ 0 , 2 , 17 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354 +1837 */{ 0 , 2 , 5 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 0 355 +1838 */{ 4 , 1 , 26 , 21352 },/* 29 30 29 30 29 29 30 30 29 30 30 29 30 384 +1839 */{ 0 , 2 , 14 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354 +1840 */{ 0 , 2 , 3 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355 +1841 */{ 3 , 1 , 23 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 29 383 +1842 */{ 0 , 2 , 10 , 53920 },/* 30 30 29 30 29 29 30 29 30 29 30 29 0 354 +1843 */{ 7 , 1 , 30 , 59728 },/* 30 30 30 29 30 29 29 30 29 30 29 30 29 384 +1844 */{ 0 , 2 , 18 , 54608 },/* 30 30 29 30 29 30 29 30 29 30 29 30 0 355 +1845 */{ 0 , 2 , 7 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354 +1846 */{ 5 , 1 , 27 , 43728 },/* 30 29 30 29 30 29 30 29 30 30 29 30 29 384 +1847 */{ 0 , 2 , 15 , 38352 },/* 30 29 29 30 29 30 29 30 30 30 29 30 0 355 +1848 */{ 0 , 2 , 5 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354 +1849 */{ 4 , 1 , 24 , 42328 },/* 30 29 30 29 29 30 29 30 29 30 29 30 30 384 +1850 */{ 0 , 2 , 12 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354 +1851 */{ 8 , 2 , 1 , 53848 },/* 30 30 29 30 29 29 30 29 29 30 29 30 30 384 +1852 */{ 0 , 2 , 20 , 45712 },/* 30 29 30 30 29 29 30 29 30 29 29 30 0 354 +1853 */{ 0 , 2 , 8 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355 +1854 */{ 7 , 1 , 29 , 22184 },/* 29 30 29 30 29 30 30 29 30 29 30 29 30 384 +1855 */{ 0 , 2 , 17 , 11680 },/* 29 29 30 29 30 30 29 30 30 29 30 29 0 354 +1856 */{ 0 , 2 , 6 , 38352 },/* 30 29 29 30 29 30 29 30 30 30 29 30 0 355 +1857 */{ 5 , 1 , 26 , 19128 },/* 29 30 29 29 30 29 30 29 30 29 30 30 30 384 +1858 */{ 0 , 2 , 14 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354 +1859 */{ 0 , 2 , 3 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354 +1860 */{ 3 , 1 , 23 , 45656 },/* 30 29 30 30 29 29 30 29 29 30 29 30 30 384 +1861 */{ 0 , 2 , 10 , 27280 },/* 29 30 30 29 30 29 30 29 30 29 29 30 0 354 +1862 */{ 8 , 1 , 30 , 44360 },/* 30 29 30 29 30 30 29 30 29 30 29 29 30 384 +1863 */{ 0 , 2 , 18 , 27472 },/* 29 30 30 29 30 29 30 30 29 30 29 30 0 355 +1864 */{ 0 , 2 , 8 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354 +1865 */{ 5 , 1 , 27 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 29 384 +1866 */{ 0 , 2 , 15 , 37744 },/* 30 29 29 30 29 29 30 30 29 30 30 30 0 355 +1867 */{ 0 , 2 , 5 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354 +1868 */{ 4 , 1 , 25 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 29 383 +1869 */{ 0 , 2 , 11 , 58528 },/* 30 30 30 29 29 30 29 29 30 29 30 29 0 354 +1870 */{ 10 , 1 , 31 , 59984 },/* 30 30 30 29 30 29 30 29 29 30 29 30 29 384 +1871 */{ 0 , 2 , 19 , 55952 },/* 30 30 29 30 30 29 30 29 30 29 29 30 0 355 +1872 */{ 0 , 2 , 9 , 23248 },/* 29 30 29 30 30 29 30 29 30 30 29 30 0 355 +1873 */{ 6 , 1 , 29 , 11112 },/* 29 29 30 29 30 29 30 30 29 30 30 29 30 384 +1874 */{ 0 , 2 , 17 , 10976 },/* 29 29 30 29 30 29 30 29 30 30 30 29 0 354 +1875 */{ 0 , 2 , 6 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354 +1876 */{ 5 , 1 , 26 , 51560 },/* 30 30 29 29 30 29 29 30 29 30 30 29 30 384 +1877 */{ 0 , 2 , 13 , 51536 },/* 30 30 29 29 30 29 29 30 29 30 29 30 0 354 +1878 */{ 0 , 2 , 2 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354 +1879 */{ 3 , 1 , 22 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 29 384 +1880 */{ 0 , 2 , 10 , 46736 },/* 30 29 30 30 29 30 30 29 30 29 29 30 0 355 +1881 */{ 7 , 1 , 30 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 29 384 +1882 */{ 0 , 2 , 18 , 21936 },/* 29 30 29 30 29 30 29 30 30 29 30 30 0 355 +1883 */{ 0 , 2 , 8 , 9680 },/* 29 29 30 29 29 30 29 30 30 30 29 30 0 354 +1884 */{ 5 , 1 , 28 , 37592 },/* 30 29 29 30 29 29 30 29 30 30 29 30 30 384 +1885 */{ 0 , 2 , 15 , 37552 },/* 30 29 29 30 29 29 30 29 30 29 30 30 0 354 +1886 */{ 0 , 2 , 4 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354 +1887 */{ 4 , 1 , 24 , 54440 },/* 30 30 29 30 29 30 29 29 30 29 30 29 30 384 +1888 */{ 0 , 2 , 12 , 46240 },/* 30 29 30 30 29 30 29 29 30 29 30 29 0 354 +1889 */{ 0 , 1 , 31 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355 +1890 */{ 2 , 1 , 21 , 22184 },/* 29 30 29 30 29 30 30 29 30 29 30 29 30 384 +1891 */{ 0 , 2 , 9 , 21936 },/* 29 30 29 30 29 30 29 30 30 29 30 30 0 355 +1892 */{ 6 , 1 , 30 , 9656 },/* 29 29 30 29 29 30 29 30 30 29 30 30 30 384 +1893 */{ 0 , 2 , 17 , 9584 },/* 29 29 30 29 29 30 29 30 29 30 30 30 0 354 +1894 */{ 0 , 2 , 6 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354 +1895 */{ 5 , 1 , 26 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 29 383 +1896 */{ 0 , 2 , 13 , 59728 },/* 30 30 30 29 30 29 29 30 29 30 29 30 0 355 +1897 */{ 0 , 2 , 2 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354 +1898 */{ 3 , 1 , 22 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 29 384 +1899 */{ 0 , 2 , 10 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 0 355 +1900 */{ 8 , 1 , 31 , 19304 },/* 29 30 29 29 30 29 30 30 29 30 30 29 30 384 +1901 */{ 0 , 2 , 19 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354 +1902 */{ 0 , 2 , 8 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355 +1903 */{ 5 , 1 , 29 , 21096 },/* 29 30 29 30 29 29 30 29 29 30 30 29 30 383 +1904 */{ 0 , 2 , 16 , 53856 },/* 30 30 29 30 29 29 30 29 29 30 30 29 0 354 +1905 */{ 0 , 2 , 4 , 55632 },/* 30 30 29 30 30 29 29 30 29 30 29 30 0 355 +1906 */{ 4 , 1 , 25 , 27304 },/* 29 30 30 29 30 29 30 29 30 29 30 29 30 384 +1907 */{ 0 , 2 , 13 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354 +1908 */{ 0 , 2 , 2 , 39632 },/* 30 29 29 30 30 29 30 29 30 30 29 30 0 355 +1909 */{ 2 , 1 , 22 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384 +1910 */{ 0 , 2 , 10 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354 +1911 */{ 6 , 1 , 30 , 42200 },/* 30 29 30 29 29 30 29 29 30 30 29 30 30 384 +1912 */{ 0 , 2 , 18 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354 +1913 */{ 0 , 2 , 6 , 53840 },/* 30 30 29 30 29 29 30 29 29 30 29 30 0 354 +1914 */{ 5 , 1 , 26 , 55624 },/* 30 30 29 30 30 29 29 30 29 30 29 29 30 384 +1915 */{ 0 , 2 , 14 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355 +1916 */{ 0 , 2 , 4 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354 +1917 */{ 2 , 1 , 23 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 29 384 +1918 */{ 0 , 2 , 11 , 38352 },/* 30 29 29 30 29 30 29 30 30 30 29 30 0 355 +1919 */{ 7 , 2 , 1 , 19160 },/* 29 30 29 29 30 29 30 29 30 30 29 30 30 384 +1920 */{ 0 , 2 , 20 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354 +1921 */{ 0 , 2 , 8 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354 +1922 */{ 5 , 1 , 28 , 45656 },/* 30 29 30 30 29 29 30 29 29 30 29 30 30 384 +1923 */{ 0 , 2 , 16 , 27280 },/* 29 30 30 29 30 29 30 29 30 29 29 30 0 354 +1924 */{ 0 , 2 , 5 , 44352 },/* 30 29 30 29 30 30 29 30 29 30 29 29 0 354 +1925 */{ 4 , 1 , 24 , 46504 },/* 30 29 30 30 29 30 29 30 30 29 30 29 30 385 +1926 */{ 0 , 2 , 13 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354 +1927 */{ 0 , 2 , 2 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355 +1928 */{ 2 , 1 , 23 , 18872 },/* 29 30 29 29 30 29 29 30 30 29 30 30 30 384 +1929 */{ 0 , 2 , 10 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354 +1930 */{ 6 , 1 , 30 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 29 383 +1931 */{ 0 , 2 , 17 , 58528 },/* 30 30 30 29 29 30 29 29 30 29 30 29 0 354 +1932 */{ 0 , 2 , 6 , 59984 },/* 30 30 30 29 30 29 30 29 29 30 29 30 0 355 +1933 */{ 5 , 1 , 26 , 27976 },/* 29 30 30 29 30 30 29 30 29 30 29 29 30 384 +1934 */{ 0 , 2 , 14 , 23376 },/* 29 30 29 30 30 29 30 30 29 30 29 30 0 355 +1935 */{ 0 , 2 , 4 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354 +1936 */{ 3 , 1 , 24 , 38256 },/* 30 29 29 30 29 30 29 30 29 30 30 30 29 384 +1937 */{ 0 , 2 , 11 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354 +1938 */{ 7 , 1 , 31 , 51560 },/* 30 30 29 29 30 29 29 30 29 30 30 29 30 384 +1939 */{ 0 , 2 , 19 , 51536 },/* 30 30 29 29 30 29 29 30 29 30 29 30 0 354 +1940 */{ 0 , 2 , 8 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354 +1941 */{ 6 , 1 , 27 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 29 384 +1942 */{ 0 , 2 , 15 , 46736 },/* 30 29 30 30 29 30 30 29 30 29 29 30 0 355 +1943 */{ 0 , 2 , 5 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 0 355 +1944 */{ 4 , 1 , 26 , 10968 },/* 29 29 30 29 30 29 30 29 30 30 29 30 30 384 +1945 */{ 0 , 2 , 13 , 9680 },/* 29 29 30 29 29 30 29 30 30 30 29 30 0 354 +1946 */{ 0 , 2 , 2 , 37584 },/* 30 29 29 30 29 29 30 29 30 30 29 30 0 354 +1947 */{ 2 , 1 , 22 , 51544 },/* 30 30 29 29 30 29 29 30 29 30 29 30 30 384 +1948 */{ 0 , 2 , 10 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354 +1949 */{ 7 , 1 , 29 , 54440 },/* 30 30 29 30 29 30 29 29 30 29 30 29 30 384 +1950 */{ 0 , 2 , 17 , 46240 },/* 30 29 30 30 29 30 29 29 30 29 30 29 0 354 +1951 */{ 0 , 2 , 6 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355 +1952 */{ 5 , 1 , 27 , 22184 },/* 29 30 29 30 29 30 30 29 30 29 30 29 30 384 +1953 */{ 0 , 2 , 14 , 19888 },/* 29 30 29 29 30 30 29 30 30 29 30 30 0 355 +1954 */{ 0 , 2 , 4 , 9648 },/* 29 29 30 29 29 30 29 30 30 29 30 30 0 354 +1955 */{ 3 , 1 , 24 , 37560 },/* 30 29 29 30 29 29 30 29 30 29 30 30 30 384 +1956 */{ 0 , 2 , 12 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354 +1957 */{ 8 , 1 , 31 , 43352 },/* 30 29 30 29 30 29 29 30 29 30 29 30 30 384 +1958 */{ 0 , 2 , 19 , 26960 },/* 29 30 30 29 30 29 29 30 29 30 29 30 0 354 +1959 */{ 0 , 2 , 8 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354 +1960 */{ 6 , 1 , 28 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 29 384 +1961 */{ 0 , 2 , 15 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 0 355 +1962 */{ 0 , 2 , 5 , 19296 },/* 29 30 29 29 30 29 30 30 29 30 30 29 0 354 +1963 */{ 4 , 1 , 25 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 29 384 +1964 */{ 0 , 2 , 13 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355 +1965 */{ 0 , 2 , 2 , 21104 },/* 29 30 29 30 29 29 30 29 29 30 30 30 0 354 +1966 */{ 3 , 1 , 22 , 26928 },/* 29 30 30 29 30 29 29 30 29 29 30 30 29 383 +1967 */{ 0 , 2 , 9 , 55632 },/* 30 30 29 30 30 29 29 30 29 30 29 30 0 355 +1968 */{ 7 , 1 , 30 , 27304 },/* 29 30 30 29 30 29 30 29 30 29 30 29 30 384 +1969 */{ 0 , 2 , 17 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354 +1970 */{ 0 , 2 , 6 , 39632 },/* 30 29 29 30 30 29 30 29 30 30 29 30 0 355 +1971 */{ 5 , 1 , 27 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384 +1972 */{ 0 , 2 , 15 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354 +1973 */{ 0 , 2 , 3 , 42208 },/* 30 29 30 29 29 30 29 29 30 30 30 29 0 354 +1974 */{ 4 , 1 , 23 , 53864 },/* 30 30 29 30 29 29 30 29 29 30 30 29 30 384 +1975 */{ 0 , 2 , 11 , 53840 },/* 30 30 29 30 29 29 30 29 29 30 29 30 0 354 +1976 */{ 8 , 1 , 31 , 54600 },/* 30 30 29 30 29 30 29 30 29 30 29 29 30 384 +1977 */{ 0 , 2 , 18 , 46400 },/* 30 29 30 30 29 30 29 30 29 30 29 29 0 354 +1978 */{ 0 , 2 , 7 , 54944 },/* 30 30 29 30 29 30 30 29 30 29 30 29 0 355 +1979 */{ 6 , 1 , 28 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 29 384 +1980 */{ 0 , 2 , 16 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355 +1981 */{ 0 , 2 , 5 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354 +1982 */{ 4 , 1 , 25 , 42200 },/* 30 29 30 29 29 30 29 29 30 30 29 30 30 384 +1983 */{ 0 , 2 , 13 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354 +1984 */{ 10 , 2 , 2 , 45656 },/* 30 29 30 30 29 29 30 29 29 30 29 30 30 384 +1985 */{ 0 , 2 , 20 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 0 354 +1986 */{ 0 , 2 , 9 , 27968 },/* 29 30 30 29 30 30 29 30 29 30 29 29 0 354 +1987 */{ 6 , 1 , 29 , 46504 },/* 30 29 30 30 29 30 29 30 30 29 30 29 30 385 +1988 */{ 0 , 2 , 18 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354 +1989 */{ 0 , 2 , 6 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355 +1990 */{ 5 , 1 , 27 , 18872 },/* 29 30 29 29 30 29 29 30 30 29 30 30 30 384 +1991 */{ 0 , 2 , 15 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354 +1992 */{ 0 , 2 , 4 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 0 354 +1993 */{ 3 , 1 , 23 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 29 383 +1994 */{ 0 , 2 , 10 , 59984 },/* 30 30 30 29 30 29 30 29 29 30 29 30 0 355 +1995 */{ 8 , 1 , 31 , 27976 },/* 29 30 30 29 30 30 29 30 29 30 29 29 30 384 +1996 */{ 0 , 2 , 19 , 23248 },/* 29 30 29 30 30 29 30 29 30 30 29 30 0 355 +1997 */{ 0 , 2 , 8 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354 +1998 */{ 5 , 1 , 28 , 37744 },/* 30 29 29 30 29 29 30 30 29 30 30 30 29 384 +1999 */{ 0 , 2 , 16 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354 +2000 */{ 0 , 2 , 5 , 51552 },/* 30 30 29 29 30 29 29 30 29 30 30 29 0 354 +2001 */{ 4 , 1 , 24 , 58536 },/* 30 30 30 29 29 30 29 29 30 29 30 29 30 384 +2002 */{ 0 , 2 , 12 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354 +2003 */{ 0 , 2 , 1 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 0 355 +2004 */{ 2 , 1 , 22 , 23208 },/* 29 30 29 30 30 29 30 29 30 29 30 29 30 384 +2005 */{ 0 , 2 , 9 , 22208 },/* 29 30 29 30 29 30 30 29 30 30 29 29 0 354 +2006 */{ 7 , 1 , 29 , 43736 },/* 30 29 30 29 30 29 30 29 30 30 29 30 30 385 +2007 */{ 0 , 2 , 18 , 9680 },/* 29 29 30 29 29 30 29 30 30 30 29 30 0 354 +2008 */{ 0 , 2 , 7 , 37584 },/* 30 29 29 30 29 29 30 29 30 30 29 30 0 354 +2009 */{ 5 , 1 , 26 , 51544 },/* 30 30 29 29 30 29 29 30 29 30 29 30 30 384 +2010 */{ 0 , 2 , 14 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354 +2011 */{ 0 , 2 , 3 , 46240 },/* 30 29 30 30 29 30 29 29 30 29 30 29 0 354 +2012 */{ 3 , 1 , 23 , 47696 },/* 30 29 30 30 30 29 30 29 29 30 29 30 29 384 +2013 */{ 0 , 2 , 10 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355 +2014 */{ 9 , 1 , 31 , 21928 },/* 29 30 29 30 29 30 29 30 30 29 30 29 30 384 +2015 */{ 0 , 2 , 19 , 19360 },/* 29 30 29 29 30 29 30 30 30 29 30 29 0 354 +2016 */{ 0 , 2 , 8 , 42416 },/* 30 29 30 29 29 30 29 30 30 29 30 30 0 355 +2017 */{ 5 , 1 , 28 , 21176 },/* 29 30 29 30 29 29 30 29 30 29 30 30 30 384 +2018 */{ 0 , 2 , 16 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354 +2019 */{ 0 , 2 , 5 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354 +2020 */{ 4 , 1 , 25 , 46248 },/* 30 29 30 30 29 30 29 29 30 29 30 29 30 384 +2021 */{ 0 , 2 , 12 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354 +2022 */{ 0 , 2 , 1 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355 +2023 */{ 2 , 1 , 22 , 21928 },/* 29 30 29 30 29 30 29 30 30 29 30 29 30 384 +2024 */{ 0 , 2 , 10 , 19296 },/* 29 30 29 29 30 29 30 30 29 30 30 29 0 354 +2025 */{ 6 , 1 , 29 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 29 384 +2026 */{ 0 , 2 , 17 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355 +2027 */{ 0 , 2 , 7 , 21104 },/* 29 30 29 30 29 29 30 29 29 30 30 30 0 354 +2028 */{ 5 , 1 , 27 , 26928 },/* 29 30 30 29 30 29 29 30 29 29 30 30 29 383 +2029 */{ 0 , 2 , 13 , 55600 },/* 30 30 29 30 30 29 29 30 29 29 30 30 0 355 +2030 */{ 0 , 2 , 3 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354 +2031 */{ 3 , 1 , 23 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 29 384 +2032 */{ 0 , 2 , 11 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 0 355 +2033 */{ 11 , 1 , 31 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384 +2034 */{ 0 , 2 , 19 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354 +2035 */{ 0 , 2 , 8 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354 +2036 */{ 6 , 1 , 28 , 53864 },/* 30 30 29 30 29 29 30 29 29 30 30 29 30 384 +2037 */{ 0 , 2 , 15 , 53840 },/* 30 30 29 30 29 29 30 29 29 30 29 30 0 354 +2038 */{ 0 , 2 , 4 , 54560 },/* 30 30 29 30 29 30 29 30 29 29 30 29 0 354 +2039 */{ 5 , 1 , 24 , 55968 },/* 30 30 29 30 30 29 30 29 30 29 30 29 29 384 +2040 */{ 0 , 2 , 12 , 46752 },/* 30 29 30 30 29 30 30 29 30 29 30 29 0 355 +2041 */{ 0 , 2 , 1 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 0 355 +2042 */{ 2 , 1 , 22 , 19160 },/* 29 30 29 29 30 29 30 29 30 30 29 30 30 384 +2043 */{ 0 , 2 , 10 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354 +2044 */{ 7 , 1 , 30 , 42168 },/* 30 29 30 29 29 30 29 29 30 29 30 30 30 384 +2045 */{ 0 , 2 , 17 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354 +2046 */{ 0 , 2 , 6 , 45648 },/* 30 29 30 30 29 29 30 29 29 30 29 30 0 354 +2047 */{ 5 , 1 , 26 , 46376 },/* 30 29 30 30 29 30 29 30 29 29 30 29 30 384 +2048 */{ 0 , 2 , 14 , 27968 },/* 29 30 30 29 30 30 29 30 29 30 29 29 0 354 +2049 */{ 0 , 2 , 2 , 44448 },/* 30 29 30 29 30 30 29 30 30 29 30 29 0 355 +2050 */{ 3 , 1 , 23 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 29 384 + */}; + + + internal override int MinCalendarYear + { + get + { + return (MIN_LUNISOLAR_YEAR); + } + } + + internal override int MaxCalendarYear + { + get + { + return (MAX_LUNISOLAR_YEAR); + } + } + + internal override DateTime MinDate + { + get + { + return (minDate); + } + } + + internal override DateTime MaxDate + { + get + { + return (maxDate); + } + } + + internal override EraInfo[] CalEraInfo + { + get + { + return null; + } + } + + internal override int GetYearInfo(int lunarYear, int index) + { + if ((lunarYear < MIN_LUNISOLAR_YEAR) || (lunarYear > MAX_LUNISOLAR_YEAR)) + { + throw new ArgumentOutOfRangeException( + "year", + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + MIN_LUNISOLAR_YEAR, + MAX_LUNISOLAR_YEAR)); + } + Contract.EndContractBlock(); + return s_yinfo[lunarYear - MIN_LUNISOLAR_YEAR, index]; + } + + internal override int GetYear(int year, DateTime time) + { + return year; + } + + internal override int GetGregorianYear(int year, int era) + { + if (era != CurrentEra && era != GregorianEra) + throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue); + + if (year < MIN_LUNISOLAR_YEAR || year > MAX_LUNISOLAR_YEAR) + { + throw new ArgumentOutOfRangeException( + nameof(year), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, MIN_LUNISOLAR_YEAR, MAX_LUNISOLAR_YEAR)); + } + Contract.EndContractBlock(); + + return year; + } + + public KoreanLunisolarCalendar() + { + } + + public override int GetEra(DateTime time) + { + CheckTicksRange(time.Ticks); + return (GregorianEra); + } + + internal override CalendarId BaseCalendarID + { + get + { + return (CalendarId.KOREA); + } + } + + internal override CalendarId ID + { + get + { + return (CalendarId.KOREANLUNISOLAR); + } + } + + + + public override int[] Eras + { + get + { + return (new int[] { GregorianEra }); + } + } + } +} diff --git a/src/mscorlib/shared/System/Globalization/LocaleData.Unix.cs b/src/mscorlib/shared/System/Globalization/LocaleData.Unix.cs new file mode 100644 index 0000000000..d4c58d8a3d --- /dev/null +++ b/src/mscorlib/shared/System/Globalization/LocaleData.Unix.cs @@ -0,0 +1,4572 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Diagnostics.Contracts; + +// This file contains the handling of Windows OS specific culture features. + +namespace System.Globalization +{ + internal enum LocaleDataParts + { + Lcid = 0, + AnsiCodePage = 1, + OemCodePage = 2, + MacCodePage = 3, + EbcdicCodePage = 4, + GeoId = 5, + DigitSubstitution = 6, + SpecificLocaleIndex = 7, + ConsoleLocaleIndex = 8 + } + + internal partial class LocaleData + { + // this is done rather than using a large readonly array of strings to avoid + // generating a large amount of code in the static constructor. + // Using indices from s_localeNamesIndices, we binary search this string when mapping + // an culture name to Lcid. Note that these names are all lowercase and are + // sorted alphabetically (ordinal). + private const string c_localeNames = + // culture name Lcid + "aa" + // 01000 - 0 + "aa-dj" + // 01000 - 2 + "aa-er" + // 01000 - 7 + "aa-et" + // 01000 - 12 + "af" + // 00036 - 17 + "af-na" + // 01000 - 19 + "af-za" + // 00436 - 24 + "agq" + // 01000 - 29 + "agq-cm" + // 01000 - 32 + "ak" + // 01000 - 38 + "ak-gh" + // 01000 - 40 + "am" + // 0005e - 45 + "am-et" + // 0045e - 47 + "ar" + // 00001 - 52 + "ar-001" + // 01000 - 54 + "ar-ae" + // 03801 - 60 + "ar-bh" + // 03c01 - 65 + "ar-dj" + // 01000 - 70 + "ar-dz" + // 01401 - 75 + "ar-eg" + // 00c01 - 80 + "ar-er" + // 01000 - 85 + "ar-il" + // 01000 - 90 + "ar-iq" + // 00801 - 95 + "ar-jo" + // 02c01 - 100 + "ar-km" + // 01000 - 105 + "ar-kw" + // 03401 - 110 + "ar-lb" + // 03001 - 115 + "ar-ly" + // 01001 - 120 + "ar-ma" + // 01801 - 125 + "ar-mr" + // 01000 - 130 + "ar-om" + // 02001 - 135 + "ar-ps" + // 01000 - 140 + "ar-qa" + // 04001 - 145 + "ar-sa" + // 00401 - 150 + "ar-sd" + // 01000 - 155 + "ar-so" + // 01000 - 160 + "ar-ss" + // 01000 - 165 + "ar-sy" + // 02801 - 170 + "ar-td" + // 01000 - 175 + "ar-tn" + // 01c01 - 180 + "ar-ye" + // 02401 - 185 + "arn" + // 0007a - 190 + "arn-cl" + // 0047a - 193 + "as" + // 0004d - 199 + "as-in" + // 0044d - 201 + "asa" + // 01000 - 206 + "asa-tz" + // 01000 - 209 + "ast" + // 01000 - 215 + "ast-es" + // 01000 - 218 + "az" + // 0002c - 224 + "az-cyrl" + // 0742c - 226 + "az-cyrl-az" + // 0082c - 233 + "az-latn" + // 0782c - 243 + "az-latn-az" + // 0042c - 250 + "ba" + // 0006d - 260 + "ba-ru" + // 0046d - 262 + "bas" + // 01000 - 267 + "bas-cm" + // 01000 - 270 + "be" + // 00023 - 276 + "be-by" + // 00423 - 278 + "bem" + // 01000 - 283 + "bem-zm" + // 01000 - 286 + "bez" + // 01000 - 292 + "bez-tz" + // 01000 - 295 + "bg" + // 00002 - 301 + "bg-bg" + // 00402 - 303 + "bin" + // 00066 - 308 + "bin-ng" + // 00466 - 311 + "bm" + // 01000 - 317 + "bm-latn" + // 01000 - 319 + "bm-latn-ml" + // 01000 - 326 + "bn" + // 00045 - 336 + "bn-bd" + // 00845 - 338 + "bn-in" + // 00445 - 343 + "bo" + // 00051 - 348 + "bo-cn" + // 00451 - 350 + "bo-in" + // 01000 - 355 + "br" + // 0007e - 360 + "br-fr" + // 0047e - 362 + "brx" + // 01000 - 367 + "brx-in" + // 01000 - 370 + "bs" + // 0781a - 376 + "bs-cyrl" + // 0641a - 378 + "bs-cyrl-ba" + // 0201a - 385 + "bs-latn" + // 0681a - 395 + "bs-latn-ba" + // 0141a - 402 + "byn" + // 01000 - 412 + "byn-er" + // 01000 - 415 + "ca" + // 00003 - 421 + "ca-ad" + // 01000 - 423 + "ca-es" + // 00403 - 428 + "ca-es-valencia" + // 00803 - 433 + "ca-fr" + // 01000 - 447 + "ca-it" + // 01000 - 452 + "ce" + // 01000 - 457 + "ce-ru" + // 01000 - 459 + "cgg" + // 01000 - 464 + "cgg-ug" + // 01000 - 467 + "chr" + // 0005c - 473 + "chr-cher" + // 07c5c - 476 + "chr-cher-us" + // 0045c - 484 + "co" + // 00083 - 495 + "co-fr" + // 00483 - 497 + "cs" + // 00005 - 502 + "cs-cz" + // 00405 - 504 + "cu" + // 01000 - 509 + "cu-ru" + // 01000 - 511 + "cy" + // 00052 - 516 + "cy-gb" + // 00452 - 518 + "da" + // 00006 - 523 + "da-dk" + // 00406 - 525 + "da-gl" + // 01000 - 530 + "dav" + // 01000 - 535 + "dav-ke" + // 01000 - 538 + "de" + // 00007 - 544 + "de-at" + // 00c07 - 546 + "de-be" + // 01000 - 551 + "de-ch" + // 00807 - 556 + "de-de" + // 00407 - 561 + "de-de_phoneb" + // 10407 - 566 + "de-it" + // 01000 - 578 + "de-li" + // 01407 - 583 + "de-lu" + // 01007 - 588 + "dje" + // 01000 - 593 + "dje-ne" + // 01000 - 596 + "dsb" + // 07c2e - 602 + "dsb-de" + // 0082e - 605 + "dua" + // 01000 - 611 + "dua-cm" + // 01000 - 614 + "dv" + // 00065 - 620 + "dv-mv" + // 00465 - 622 + "dyo" + // 01000 - 627 + "dyo-sn" + // 01000 - 630 + "dz" + // 01000 - 636 + "dz-bt" + // 00c51 - 638 + "ebu" + // 01000 - 643 + "ebu-ke" + // 01000 - 646 + "ee" + // 01000 - 652 + "ee-gh" + // 01000 - 654 + "ee-tg" + // 01000 - 659 + "el" + // 00008 - 664 + "el-cy" + // 01000 - 666 + "el-gr" + // 00408 - 671 + "en" + // 00009 - 676 + "en-001" + // 01000 - 678 + "en-029" + // 02409 - 684 + "en-150" + // 01000 - 690 + "en-ag" + // 01000 - 696 + "en-ai" + // 01000 - 701 + "en-as" + // 01000 - 706 + "en-at" + // 01000 - 711 + "en-au" + // 00c09 - 716 + "en-bb" + // 01000 - 721 + "en-be" + // 01000 - 726 + "en-bi" + // 01000 - 731 + "en-bm" + // 01000 - 736 + "en-bs" + // 01000 - 741 + "en-bw" + // 01000 - 746 + "en-bz" + // 02809 - 751 + "en-ca" + // 01009 - 756 + "en-cc" + // 01000 - 761 + "en-ch" + // 01000 - 766 + "en-ck" + // 01000 - 771 + "en-cm" + // 01000 - 776 + "en-cx" + // 01000 - 781 + "en-cy" + // 01000 - 786 + "en-de" + // 01000 - 791 + "en-dk" + // 01000 - 796 + "en-dm" + // 01000 - 801 + "en-er" + // 01000 - 806 + "en-fi" + // 01000 - 811 + "en-fj" + // 01000 - 816 + "en-fk" + // 01000 - 821 + "en-fm" + // 01000 - 826 + "en-gb" + // 00809 - 831 + "en-gd" + // 01000 - 836 + "en-gg" + // 01000 - 841 + "en-gh" + // 01000 - 846 + "en-gi" + // 01000 - 851 + "en-gm" + // 01000 - 856 + "en-gu" + // 01000 - 861 + "en-gy" + // 01000 - 866 + "en-hk" + // 03c09 - 871 + "en-id" + // 03809 - 876 + "en-ie" + // 01809 - 881 + "en-il" + // 01000 - 886 + "en-im" + // 01000 - 891 + "en-in" + // 04009 - 896 + "en-io" + // 01000 - 901 + "en-je" + // 01000 - 906 + "en-jm" + // 02009 - 911 + "en-ke" + // 01000 - 916 + "en-ki" + // 01000 - 921 + "en-kn" + // 01000 - 926 + "en-ky" + // 01000 - 931 + "en-lc" + // 01000 - 936 + "en-lr" + // 01000 - 941 + "en-ls" + // 01000 - 946 + "en-mg" + // 01000 - 951 + "en-mh" + // 01000 - 956 + "en-mo" + // 01000 - 961 + "en-mp" + // 01000 - 966 + "en-ms" + // 01000 - 971 + "en-mt" + // 01000 - 976 + "en-mu" + // 01000 - 981 + "en-mw" + // 01000 - 986 + "en-my" + // 04409 - 991 + "en-na" + // 01000 - 996 + "en-nf" + // 01000 - 1001 + "en-ng" + // 01000 - 1006 + "en-nl" + // 01000 - 1011 + "en-nr" + // 01000 - 1016 + "en-nu" + // 01000 - 1021 + "en-nz" + // 01409 - 1026 + "en-pg" + // 01000 - 1031 + "en-ph" + // 03409 - 1036 + "en-pk" + // 01000 - 1041 + "en-pn" + // 01000 - 1046 + "en-pr" + // 01000 - 1051 + "en-pw" + // 01000 - 1056 + "en-rw" + // 01000 - 1061 + "en-sb" + // 01000 - 1066 + "en-sc" + // 01000 - 1071 + "en-sd" + // 01000 - 1076 + "en-se" + // 01000 - 1081 + "en-sg" + // 04809 - 1086 + "en-sh" + // 01000 - 1091 + "en-si" + // 01000 - 1096 + "en-sl" + // 01000 - 1101 + "en-ss" + // 01000 - 1106 + "en-sx" + // 01000 - 1111 + "en-sz" + // 01000 - 1116 + "en-tc" + // 01000 - 1121 + "en-tk" + // 01000 - 1126 + "en-to" + // 01000 - 1131 + "en-tt" + // 02c09 - 1136 + "en-tv" + // 01000 - 1141 + "en-tz" + // 01000 - 1146 + "en-ug" + // 01000 - 1151 + "en-um" + // 01000 - 1156 + "en-us" + // 00409 - 1161 + "en-vc" + // 01000 - 1166 + "en-vg" + // 01000 - 1171 + "en-vi" + // 01000 - 1176 + "en-vu" + // 01000 - 1181 + "en-ws" + // 01000 - 1186 + "en-za" + // 01c09 - 1191 + "en-zm" + // 01000 - 1196 + "en-zw" + // 03009 - 1201 + "eo" + // 01000 - 1206 + "eo-001" + // 01000 - 1208 + "es" + // 0000a - 1214 + "es-419" + // 0580a - 1216 + "es-ar" + // 02c0a - 1222 + "es-bo" + // 0400a - 1227 + "es-br" + // 01000 - 1232 + "es-cl" + // 0340a - 1237 + "es-co" + // 0240a - 1242 + "es-cr" + // 0140a - 1247 + "es-cu" + // 05c0a - 1252 + "es-do" + // 01c0a - 1257 + "es-ec" + // 0300a - 1262 + "es-es" + // 00c0a - 1267 + "es-es_tradnl" + // 0040a - 1272 + "es-gq" + // 01000 - 1284 + "es-gt" + // 0100a - 1289 + "es-hn" + // 0480a - 1294 + "es-mx" + // 0080a - 1299 + "es-ni" + // 04c0a - 1304 + "es-pa" + // 0180a - 1309 + "es-pe" + // 0280a - 1314 + "es-ph" + // 01000 - 1319 + "es-pr" + // 0500a - 1324 + "es-py" + // 03c0a - 1329 + "es-sv" + // 0440a - 1334 + "es-us" + // 0540a - 1339 + "es-uy" + // 0380a - 1344 + "es-ve" + // 0200a - 1349 + "et" + // 00025 - 1354 + "et-ee" + // 00425 - 1356 + "eu" + // 0002d - 1361 + "eu-es" + // 0042d - 1363 + "ewo" + // 01000 - 1368 + "ewo-cm" + // 01000 - 1371 + "fa" + // 00029 - 1377 + "fa-ir" + // 00429 - 1379 + "ff" + // 00067 - 1384 + "ff-cm" + // 01000 - 1386 + "ff-gn" + // 01000 - 1391 + "ff-latn" + // 07c67 - 1396 + "ff-latn-sn" + // 00867 - 1403 + "ff-mr" + // 01000 - 1413 + "ff-ng" + // 00467 - 1418 + "fi" + // 0000b - 1423 + "fi-fi" + // 0040b - 1425 + "fil" + // 00064 - 1430 + "fil-ph" + // 00464 - 1433 + "fo" + // 00038 - 1439 + "fo-dk" + // 01000 - 1441 + "fo-fo" + // 00438 - 1446 + "fr" + // 0000c - 1451 + "fr-029" + // 01c0c - 1453 + "fr-be" + // 0080c - 1459 + "fr-bf" + // 01000 - 1464 + "fr-bi" + // 01000 - 1469 + "fr-bj" + // 01000 - 1474 + "fr-bl" + // 01000 - 1479 + "fr-ca" + // 00c0c - 1484 + "fr-cd" + // 0240c - 1489 + "fr-cf" + // 01000 - 1494 + "fr-cg" + // 01000 - 1499 + "fr-ch" + // 0100c - 1504 + "fr-ci" + // 0300c - 1509 + "fr-cm" + // 02c0c - 1514 + "fr-dj" + // 01000 - 1519 + "fr-dz" + // 01000 - 1524 + "fr-fr" + // 0040c - 1529 + "fr-ga" + // 01000 - 1534 + "fr-gf" + // 01000 - 1539 + "fr-gn" + // 01000 - 1544 + "fr-gp" + // 01000 - 1549 + "fr-gq" + // 01000 - 1554 + "fr-ht" + // 03c0c - 1559 + "fr-km" + // 01000 - 1564 + "fr-lu" + // 0140c - 1569 + "fr-ma" + // 0380c - 1574 + "fr-mc" + // 0180c - 1579 + "fr-mf" + // 01000 - 1584 + "fr-mg" + // 01000 - 1589 + "fr-ml" + // 0340c - 1594 + "fr-mq" + // 01000 - 1599 + "fr-mr" + // 01000 - 1604 + "fr-mu" + // 01000 - 1609 + "fr-nc" + // 01000 - 1614 + "fr-ne" + // 01000 - 1619 + "fr-pf" + // 01000 - 1624 + "fr-pm" + // 01000 - 1629 + "fr-re" + // 0200c - 1634 + "fr-rw" + // 01000 - 1639 + "fr-sc" + // 01000 - 1644 + "fr-sn" + // 0280c - 1649 + "fr-sy" + // 01000 - 1654 + "fr-td" + // 01000 - 1659 + "fr-tg" + // 01000 - 1664 + "fr-tn" + // 01000 - 1669 + "fr-vu" + // 01000 - 1674 + "fr-wf" + // 01000 - 1679 + "fr-yt" + // 01000 - 1684 + "fur" + // 01000 - 1689 + "fur-it" + // 01000 - 1692 + "fy" + // 00062 - 1698 + "fy-nl" + // 00462 - 1700 + "ga" + // 0003c - 1705 + "ga-ie" + // 0083c - 1707 + "gd" + // 00091 - 1712 + "gd-gb" + // 00491 - 1714 + "gl" + // 00056 - 1719 + "gl-es" + // 00456 - 1721 + "gn" + // 00074 - 1726 + "gn-py" + // 00474 - 1728 + "gsw" + // 00084 - 1733 + "gsw-ch" + // 01000 - 1736 + "gsw-fr" + // 00484 - 1742 + "gsw-li" + // 01000 - 1748 + "gu" + // 00047 - 1754 + "gu-in" + // 00447 - 1756 + "guz" + // 01000 - 1761 + "guz-ke" + // 01000 - 1764 + "gv" + // 01000 - 1770 + "gv-im" + // 01000 - 1772 + "ha" + // 00068 - 1777 + "ha-latn" + // 07c68 - 1779 + "ha-latn-gh" + // 01000 - 1786 + "ha-latn-ne" + // 01000 - 1796 + "ha-latn-ng" + // 00468 - 1806 + "haw" + // 00075 - 1816 + "haw-us" + // 00475 - 1819 + "he" + // 0000d - 1825 + "he-il" + // 0040d - 1827 + "hi" + // 00039 - 1832 + "hi-in" + // 00439 - 1834 + "hr" + // 0001a - 1839 + "hr-ba" + // 0101a - 1841 + "hr-hr" + // 0041a - 1846 + "hsb" + // 0002e - 1851 + "hsb-de" + // 0042e - 1854 + "hu" + // 0000e - 1860 + "hu-hu" + // 0040e - 1862 + "hu-hu_technl" + // 1040e - 1867 + "hy" + // 0002b - 1879 + "hy-am" + // 0042b - 1881 + "ia" + // 01000 - 1886 + "ia-001" + // 01000 - 1888 + "ia-fr" + // 01000 - 1894 + "ibb" + // 00069 - 1899 + "ibb-ng" + // 00469 - 1902 + "id" + // 00021 - 1908 + "id-id" + // 00421 - 1910 + "ig" + // 00070 - 1915 + "ig-ng" + // 00470 - 1917 + "ii" + // 00078 - 1922 + "ii-cn" + // 00478 - 1924 + "is" + // 0000f - 1929 + "is-is" + // 0040f - 1931 + "it" + // 00010 - 1936 + "it-ch" + // 00810 - 1938 + "it-it" + // 00410 - 1943 + "it-sm" + // 01000 - 1948 + "iu" + // 0005d - 1953 + "iu-cans" + // 0785d - 1955 + "iu-cans-ca" + // 0045d - 1962 + "iu-latn" + // 07c5d - 1972 + "iu-latn-ca" + // 0085d - 1979 + "ja" + // 00011 - 1989 + "ja-jp" + // 00411 - 1991 + "ja-jp_radstr" + // 40411 - 1996 + "jgo" + // 01000 - 2008 + "jgo-cm" + // 01000 - 2011 + "jmc" + // 01000 - 2017 + "jmc-tz" + // 01000 - 2020 + "jv" + // 01000 - 2026 + "jv-java" + // 01000 - 2028 + "jv-java-id" + // 01000 - 2035 + "jv-latn" + // 01000 - 2045 + "jv-latn-id" + // 01000 - 2052 + "ka" + // 00037 - 2062 + "ka-ge" + // 00437 - 2064 + "ka-ge_modern" + // 10437 - 2069 + "kab" + // 01000 - 2081 + "kab-dz" + // 01000 - 2084 + "kam" + // 01000 - 2090 + "kam-ke" + // 01000 - 2093 + "kde" + // 01000 - 2099 + "kde-tz" + // 01000 - 2102 + "kea" + // 01000 - 2108 + "kea-cv" + // 01000 - 2111 + "khq" + // 01000 - 2117 + "khq-ml" + // 01000 - 2120 + "ki" + // 01000 - 2126 + "ki-ke" + // 01000 - 2128 + "kk" + // 0003f - 2133 + "kk-kz" + // 0043f - 2135 + "kkj" + // 01000 - 2140 + "kkj-cm" + // 01000 - 2143 + "kl" + // 0006f - 2149 + "kl-gl" + // 0046f - 2151 + "kln" + // 01000 - 2156 + "kln-ke" + // 01000 - 2159 + "km" + // 00053 - 2165 + "km-kh" + // 00453 - 2167 + "kn" + // 0004b - 2172 + "kn-in" + // 0044b - 2174 + "ko" + // 00012 - 2179 + "ko-kp" + // 01000 - 2181 + "ko-kr" + // 00412 - 2186 + "kok" + // 00057 - 2191 + "kok-in" + // 00457 - 2194 + "kr" + // 00071 - 2200 + "kr-ng" + // 00471 - 2202 + "ks" + // 00060 - 2207 + "ks-arab" + // 00460 - 2209 + "ks-arab-in" + // 01000 - 2216 + "ks-deva" + // 01000 - 2226 + "ks-deva-in" + // 00860 - 2233 + "ksb" + // 01000 - 2243 + "ksb-tz" + // 01000 - 2246 + "ksf" + // 01000 - 2252 + "ksf-cm" + // 01000 - 2255 + "ksh" + // 01000 - 2261 + "ksh-de" + // 01000 - 2264 + "ku" + // 00092 - 2270 + "ku-arab" + // 07c92 - 2272 + "ku-arab-iq" + // 00492 - 2279 + "ku-arab-ir" + // 01000 - 2289 + "kw" + // 01000 - 2299 + "kw-gb" + // 01000 - 2301 + "ky" + // 00040 - 2306 + "ky-kg" + // 00440 - 2308 + "la" + // 00076 - 2313 + "la-001" + // 00476 - 2315 + "lag" + // 01000 - 2321 + "lag-tz" + // 01000 - 2324 + "lb" + // 0006e - 2330 + "lb-lu" + // 0046e - 2332 + "lg" + // 01000 - 2337 + "lg-ug" + // 01000 - 2339 + "lkt" + // 01000 - 2344 + "lkt-us" + // 01000 - 2347 + "ln" + // 01000 - 2353 + "ln-ao" + // 01000 - 2355 + "ln-cd" + // 01000 - 2360 + "ln-cf" + // 01000 - 2365 + "ln-cg" + // 01000 - 2370 + "lo" + // 00054 - 2375 + "lo-la" + // 00454 - 2377 + "lrc" + // 01000 - 2382 + "lrc-iq" + // 01000 - 2385 + "lrc-ir" + // 01000 - 2391 + "lt" + // 00027 - 2397 + "lt-lt" + // 00427 - 2399 + "lu" + // 01000 - 2404 + "lu-cd" + // 01000 - 2406 + "luo" + // 01000 - 2411 + "luo-ke" + // 01000 - 2414 + "luy" + // 01000 - 2420 + "luy-ke" + // 01000 - 2423 + "lv" + // 00026 - 2429 + "lv-lv" + // 00426 - 2431 + "mas" + // 01000 - 2436 + "mas-ke" + // 01000 - 2439 + "mas-tz" + // 01000 - 2445 + "mer" + // 01000 - 2451 + "mer-ke" + // 01000 - 2454 + "mfe" + // 01000 - 2460 + "mfe-mu" + // 01000 - 2463 + "mg" + // 01000 - 2469 + "mg-mg" + // 01000 - 2471 + "mgh" + // 01000 - 2476 + "mgh-mz" + // 01000 - 2479 + "mgo" + // 01000 - 2485 + "mgo-cm" + // 01000 - 2488 + "mi" + // 00081 - 2494 + "mi-nz" + // 00481 - 2496 + "mk" + // 0002f - 2501 + "mk-mk" + // 0042f - 2503 + "ml" + // 0004c - 2508 + "ml-in" + // 0044c - 2510 + "mn" + // 00050 - 2515 + "mn-cyrl" + // 07850 - 2517 + "mn-mn" + // 00450 - 2524 + "mn-mong" + // 07c50 - 2529 + "mn-mong-cn" + // 00850 - 2536 + "mn-mong-mn" + // 00c50 - 2546 + "mni" + // 00058 - 2556 + "mni-in" + // 00458 - 2559 + "moh" + // 0007c - 2565 + "moh-ca" + // 0047c - 2568 + "mr" + // 0004e - 2574 + "mr-in" + // 0044e - 2576 + "ms" + // 0003e - 2581 + "ms-bn" + // 0083e - 2583 + "ms-my" + // 0043e - 2588 + "ms-sg" + // 01000 - 2593 + "mt" + // 0003a - 2598 + "mt-mt" + // 0043a - 2600 + "mua" + // 01000 - 2605 + "mua-cm" + // 01000 - 2608 + "my" + // 00055 - 2614 + "my-mm" + // 00455 - 2616 + "mzn" + // 01000 - 2621 + "mzn-ir" + // 01000 - 2624 + "naq" + // 01000 - 2630 + "naq-na" + // 01000 - 2633 + "nb" + // 07c14 - 2639 + "nb-no" + // 00414 - 2641 + "nb-sj" + // 01000 - 2646 + "nd" + // 01000 - 2651 + "nd-zw" + // 01000 - 2653 + "nds" + // 01000 - 2658 + "nds-de" + // 01000 - 2661 + "nds-nl" + // 01000 - 2667 + "ne" + // 00061 - 2673 + "ne-in" + // 00861 - 2675 + "ne-np" + // 00461 - 2680 + "nl" + // 00013 - 2685 + "nl-aw" + // 01000 - 2687 + "nl-be" + // 00813 - 2692 + "nl-bq" + // 01000 - 2697 + "nl-cw" + // 01000 - 2702 + "nl-nl" + // 00413 - 2707 + "nl-sr" + // 01000 - 2712 + "nl-sx" + // 01000 - 2717 + "nmg" + // 01000 - 2722 + "nmg-cm" + // 01000 - 2725 + "nn" + // 07814 - 2731 + "nn-no" + // 00814 - 2733 + "nnh" + // 01000 - 2738 + "nnh-cm" + // 01000 - 2741 + "no" + // 00014 - 2747 + "nqo" + // 01000 - 2749 + "nqo-gn" + // 01000 - 2752 + "nr" + // 01000 - 2758 + "nr-za" + // 01000 - 2760 + "nso" + // 0006c - 2765 + "nso-za" + // 0046c - 2768 + "nus" + // 01000 - 2774 + "nus-ss" + // 01000 - 2777 + "nyn" + // 01000 - 2783 + "nyn-ug" + // 01000 - 2786 + "oc" + // 00082 - 2792 + "oc-fr" + // 00482 - 2794 + "om" + // 00072 - 2799 + "om-et" + // 00472 - 2801 + "om-ke" + // 01000 - 2806 + "or" + // 00048 - 2811 + "or-in" + // 00448 - 2813 + "os" + // 01000 - 2818 + "os-ge" + // 01000 - 2820 + "os-ru" + // 01000 - 2825 + "pa" + // 00046 - 2830 + "pa-arab" + // 07c46 - 2832 + "pa-arab-pk" + // 00846 - 2839 + "pa-in" + // 00446 - 2849 + "pap" + // 00079 - 2854 + "pap-029" + // 00479 - 2857 + "pl" + // 00015 - 2864 + "pl-pl" + // 00415 - 2866 + "prg" + // 01000 - 2871 + "prg-001" + // 01000 - 2874 + "prs" + // 0008c - 2881 + "prs-af" + // 0048c - 2884 + "ps" + // 00063 - 2890 + "ps-af" + // 00463 - 2892 + "pt" + // 00016 - 2897 + "pt-ao" + // 01000 - 2899 + "pt-br" + // 00416 - 2904 + "pt-ch" + // 01000 - 2909 + "pt-cv" + // 01000 - 2914 + "pt-gq" + // 01000 - 2919 + "pt-gw" + // 01000 - 2924 + "pt-lu" + // 01000 - 2929 + "pt-mo" + // 01000 - 2934 + "pt-mz" + // 01000 - 2939 + "pt-pt" + // 00816 - 2944 + "pt-st" + // 01000 - 2949 + "pt-tl" + // 01000 - 2954 + "qps-latn-x-sh" + // 00901 - 2959 + "qps-ploc" + // 00501 - 2972 + "qps-ploca" + // 005fe - 2980 + "qps-plocm" + // 009ff - 2989 + "quc" + // 00086 - 2998 + "quc-latn" + // 07c86 - 3001 + "quc-latn-gt" + // 00486 - 3009 + "quz" + // 0006b - 3020 + "quz-bo" + // 0046b - 3023 + "quz-ec" + // 0086b - 3029 + "quz-pe" + // 00c6b - 3035 + "rm" + // 00017 - 3041 + "rm-ch" + // 00417 - 3043 + "rn" + // 01000 - 3048 + "rn-bi" + // 01000 - 3050 + "ro" + // 00018 - 3055 + "ro-md" + // 00818 - 3057 + "ro-ro" + // 00418 - 3062 + "rof" + // 01000 - 3067 + "rof-tz" + // 01000 - 3070 + "ru" + // 00019 - 3076 + "ru-by" + // 01000 - 3078 + "ru-kg" + // 01000 - 3083 + "ru-kz" + // 01000 - 3088 + "ru-md" + // 00819 - 3093 + "ru-ru" + // 00419 - 3098 + "ru-ua" + // 01000 - 3103 + "rw" + // 00087 - 3108 + "rw-rw" + // 00487 - 3110 + "rwk" + // 01000 - 3115 + "rwk-tz" + // 01000 - 3118 + "sa" + // 0004f - 3124 + "sa-in" + // 0044f - 3126 + "sah" + // 00085 - 3131 + "sah-ru" + // 00485 - 3134 + "saq" + // 01000 - 3140 + "saq-ke" + // 01000 - 3143 + "sbp" + // 01000 - 3149 + "sbp-tz" + // 01000 - 3152 + "sd" + // 00059 - 3158 + "sd-arab" + // 07c59 - 3160 + "sd-arab-pk" + // 00859 - 3167 + "sd-deva" + // 01000 - 3177 + "sd-deva-in" + // 00459 - 3184 + "se" + // 0003b - 3194 + "se-fi" + // 00c3b - 3196 + "se-no" + // 0043b - 3201 + "se-se" + // 0083b - 3206 + "seh" + // 01000 - 3211 + "seh-mz" + // 01000 - 3214 + "ses" + // 01000 - 3220 + "ses-ml" + // 01000 - 3223 + "sg" + // 01000 - 3229 + "sg-cf" + // 01000 - 3231 + "shi" + // 01000 - 3236 + "shi-latn" + // 01000 - 3239 + "shi-latn-ma" + // 01000 - 3247 + "shi-tfng" + // 01000 - 3258 + "shi-tfng-ma" + // 01000 - 3266 + "si" + // 0005b - 3277 + "si-lk" + // 0045b - 3279 + "sk" + // 0001b - 3284 + "sk-sk" + // 0041b - 3286 + "sl" + // 00024 - 3291 + "sl-si" + // 00424 - 3293 + "sma" + // 0783b - 3298 + "sma-no" + // 0183b - 3301 + "sma-se" + // 01c3b - 3307 + "smj" + // 07c3b - 3313 + "smj-no" + // 0103b - 3316 + "smj-se" + // 0143b - 3322 + "smn" + // 0703b - 3328 + "smn-fi" + // 0243b - 3331 + "sms" + // 0743b - 3337 + "sms-fi" + // 0203b - 3340 + "sn" + // 01000 - 3346 + "sn-latn" + // 01000 - 3348 + "sn-latn-zw" + // 01000 - 3355 + "so" + // 00077 - 3365 + "so-dj" + // 01000 - 3367 + "so-et" + // 01000 - 3372 + "so-ke" + // 01000 - 3377 + "so-so" + // 00477 - 3382 + "sq" + // 0001c - 3387 + "sq-al" + // 0041c - 3389 + "sq-mk" + // 01000 - 3394 + "sq-xk" + // 01000 - 3399 + "sr" + // 07c1a - 3404 + "sr-cyrl" + // 06c1a - 3406 + "sr-cyrl-ba" + // 01c1a - 3413 + "sr-cyrl-cs" + // 00c1a - 3423 + "sr-cyrl-me" + // 0301a - 3433 + "sr-cyrl-rs" + // 0281a - 3443 + "sr-cyrl-xk" + // 01000 - 3453 + "sr-latn" + // 0701a - 3463 + "sr-latn-ba" + // 0181a - 3470 + "sr-latn-cs" + // 0081a - 3480 + "sr-latn-me" + // 02c1a - 3490 + "sr-latn-rs" + // 0241a - 3500 + "sr-latn-xk" + // 01000 - 3510 + "ss" + // 01000 - 3520 + "ss-sz" + // 01000 - 3522 + "ss-za" + // 01000 - 3527 + "ssy" + // 01000 - 3532 + "ssy-er" + // 01000 - 3535 + "st" + // 00030 - 3541 + "st-ls" + // 01000 - 3543 + "st-za" + // 00430 - 3548 + "sv" + // 0001d - 3553 + "sv-ax" + // 01000 - 3555 + "sv-fi" + // 0081d - 3560 + "sv-se" + // 0041d - 3565 + "sw" + // 00041 - 3570 + "sw-cd" + // 01000 - 3572 + "sw-ke" + // 00441 - 3577 + "sw-tz" + // 01000 - 3582 + "sw-ug" + // 01000 - 3587 + "swc" + // 01000 - 3592 + "swc-cd" + // 01000 - 3595 + "syr" + // 0005a - 3601 + "syr-sy" + // 0045a - 3604 + "ta" + // 00049 - 3610 + "ta-in" + // 00449 - 3612 + "ta-lk" + // 00849 - 3617 + "ta-my" + // 01000 - 3622 + "ta-sg" + // 01000 - 3627 + "te" + // 0004a - 3632 + "te-in" + // 0044a - 3634 + "teo" + // 01000 - 3639 + "teo-ke" + // 01000 - 3642 + "teo-ug" + // 01000 - 3648 + "tg" + // 00028 - 3654 + "tg-cyrl" + // 07c28 - 3656 + "tg-cyrl-tj" + // 00428 - 3663 + "th" + // 0001e - 3673 + "th-th" + // 0041e - 3675 + "ti" + // 00073 - 3680 + "ti-er" + // 00873 - 3682 + "ti-et" + // 00473 - 3687 + "tig" + // 01000 - 3692 + "tig-er" + // 01000 - 3695 + "tk" + // 00042 - 3701 + "tk-tm" + // 00442 - 3703 + "tn" + // 00032 - 3708 + "tn-bw" + // 00832 - 3710 + "tn-za" + // 00432 - 3715 + "to" + // 01000 - 3720 + "to-to" + // 01000 - 3722 + "tr" + // 0001f - 3727 + "tr-cy" + // 01000 - 3729 + "tr-tr" + // 0041f - 3734 + "ts" + // 00031 - 3739 + "ts-za" + // 00431 - 3741 + "tt" + // 00044 - 3746 + "tt-ru" + // 00444 - 3748 + "twq" + // 01000 - 3753 + "twq-ne" + // 01000 - 3756 + "tzm" + // 0005f - 3762 + "tzm-arab" + // 01000 - 3765 + "tzm-arab-ma" + // 0045f - 3773 + "tzm-latn" + // 07c5f - 3784 + "tzm-latn-dz" + // 0085f - 3792 + "tzm-latn-ma" + // 01000 - 3803 + "tzm-tfng" + // 0785f - 3814 + "tzm-tfng-ma" + // 0105f - 3822 + "ug" + // 00080 - 3833 + "ug-cn" + // 00480 - 3835 + "uk" + // 00022 - 3840 + "uk-ua" + // 00422 - 3842 + "ur" + // 00020 - 3847 + "ur-in" + // 00820 - 3849 + "ur-pk" + // 00420 - 3854 + "uz" + // 00043 - 3859 + "uz-arab" + // 01000 - 3861 + "uz-arab-af" + // 01000 - 3868 + "uz-cyrl" + // 07843 - 3878 + "uz-cyrl-uz" + // 00843 - 3885 + "uz-latn" + // 07c43 - 3895 + "uz-latn-uz" + // 00443 - 3902 + "vai" + // 01000 - 3912 + "vai-latn" + // 01000 - 3915 + "vai-latn-lr" + // 01000 - 3923 + "vai-vaii" + // 01000 - 3934 + "vai-vaii-lr" + // 01000 - 3942 + "ve" + // 00033 - 3953 + "ve-za" + // 00433 - 3955 + "vi" + // 0002a - 3960 + "vi-vn" + // 0042a - 3962 + "vo" + // 01000 - 3967 + "vo-001" + // 01000 - 3969 + "vun" + // 01000 - 3975 + "vun-tz" + // 01000 - 3978 + "wae" + // 01000 - 3984 + "wae-ch" + // 01000 - 3987 + "wal" + // 01000 - 3993 + "wal-et" + // 01000 - 3996 + "wo" + // 00088 - 4002 + "wo-sn" + // 00488 - 4004 + "x-iv_mathan" + // 1007f - 4009 + "xh" + // 00034 - 4020 + "xh-za" + // 00434 - 4022 + "xog" + // 01000 - 4027 + "xog-ug" + // 01000 - 4030 + "yav" + // 01000 - 4036 + "yav-cm" + // 01000 - 4039 + "yi" + // 0003d - 4045 + "yi-001" + // 0043d - 4047 + "yo" + // 0006a - 4053 + "yo-bj" + // 01000 - 4055 + "yo-ng" + // 0046a - 4060 + "yue" + // 01000 - 4065 + "yue-hk" + // 01000 - 4068 + "zgh" + // 01000 - 4074 + "zgh-tfng" + // 01000 - 4077 + "zgh-tfng-ma" + // 01000 - 4085 + "zh" + // 07804 - 4096 + "zh-chs" + // 00004 - 4098 + "zh-cht" + // 07c04 - 4104 + "zh-cn" + // 00804 - 4110 + "zh-cn_phoneb" + // 50804 - 4115 + "zh-cn_stroke" + // 20804 - 4127 + "zh-hans" + // 00004 - 4139 + "zh-hans-hk" + // 01000 - 4146 + "zh-hans-mo" + // 01000 - 4156 + "zh-hant" + // 07c04 - 4166 + "zh-hk" + // 00c04 - 4173 + "zh-hk_radstr" + // 40c04 - 4178 + "zh-mo" + // 01404 - 4190 + "zh-mo_radstr" + // 41404 - 4195 + "zh-mo_stroke" + // 21404 - 4207 + "zh-sg" + // 01004 - 4219 + "zh-sg_phoneb" + // 51004 - 4224 + "zh-sg_stroke" + // 21004 - 4236 + "zh-tw" + // 00404 - 4248 + "zh-tw_pronun" + // 30404 - 4253 + "zh-tw_radstr" + // 40404 - 4265 + "zu" + // 00035 - 4277 + "zu-za"; // 00435 - 4279 + + // c_threeLetterWindowsLanguageName is string containing 3-letter Windows language names + // every 3-characters entry is matching locale name entry in c_localeNames + + private const string c_threeLetterWindowsLanguageName = + "ZZZ" + // aa + "ZZZ" + // aa-dj + "ZZZ" + // aa-er + "ZZZ" + // aa-et + "AFK" + // af + "ZZZ" + // af-na + "AFK" + // af-za + "ZZZ" + // agq + "ZZZ" + // agq-cm + "ZZZ" + // ak + "ZZZ" + // ak-gh + "AMH" + // am + "AMH" + // am-et + "ARA" + // ar + "ZZZ" + // ar-001 + "ARU" + // ar-ae + "ARH" + // ar-bh + "ZZZ" + // ar-dj + "ARG" + // ar-dz + "ARE" + // ar-eg + "ZZZ" + // ar-er + "ZZZ" + // ar-il + "ARI" + // ar-iq + "ARJ" + // ar-jo + "ZZZ" + // ar-km + "ARK" + // ar-kw + "ARB" + // ar-lb + "ARL" + // ar-ly + "ARM" + // ar-ma + "ZZZ" + // ar-mr + "ARO" + // ar-om + "ZZZ" + // ar-ps + "ARQ" + // ar-qa + "ARA" + // ar-sa + "ZZZ" + // ar-sd + "ZZZ" + // ar-so + "ZZZ" + // ar-ss + "ARS" + // ar-sy + "ZZZ" + // ar-td + "ART" + // ar-tn + "ARY" + // ar-ye + "MPD" + // arn + "MPD" + // arn-cl + "ASM" + // as + "ASM" + // as-in + "ZZZ" + // asa + "ZZZ" + // asa-tz + "ZZZ" + // ast + "ZZZ" + // ast-es + "AZE" + // az + "AZC" + // az-cyrl + "AZC" + // az-cyrl-az + "AZE" + // az-latn + "AZE" + // az-latn-az + "BAS" + // ba + "BAS" + // ba-ru + "ZZZ" + // bas + "ZZZ" + // bas-cm + "BEL" + // be + "BEL" + // be-by + "ZZZ" + // bem + "ZZZ" + // bem-zm + "ZZZ" + // bez + "ZZZ" + // bez-tz + "BGR" + // bg + "BGR" + // bg-bg + "ZZZ" + // bin + "ZZZ" + // bin-ng + "ZZZ" + // bm + "ZZZ" + // bm-latn + "ZZZ" + // bm-latn-ml + "BNB" + // bn + "BNB" + // bn-bd + "BNG" + // bn-in + "BOB" + // bo + "BOB" + // bo-cn + "ZZZ" + // bo-in + "BRE" + // br + "BRE" + // br-fr + "ZZZ" + // brx + "ZZZ" + // brx-in + "BSB" + // bs + "BSC" + // bs-cyrl + "BSC" + // bs-cyrl-ba + "BSB" + // bs-latn + "BSB" + // bs-latn-ba + "ZZZ" + // byn + "ZZZ" + // byn-er + "CAT" + // ca + "ZZZ" + // ca-ad + "CAT" + // ca-es + "VAL" + // ca-es-valencia + "ZZZ" + // ca-fr + "ZZZ" + // ca-it + "ZZZ" + // ce + "ZZZ" + // ce-ru + "ZZZ" + // cgg + "ZZZ" + // cgg-ug + "CRE" + // chr + "CRE" + // chr-cher + "CRE" + // chr-cher-us + "COS" + // co + "COS" + // co-fr + "CSY" + // cs + "CSY" + // cs-cz + "ZZZ" + // cu + "ZZZ" + // cu-ru + "CYM" + // cy + "CYM" + // cy-gb + "DAN" + // da + "DAN" + // da-dk + "ZZZ" + // da-gl + "ZZZ" + // dav + "ZZZ" + // dav-ke + "DEU" + // de + "DEA" + // de-at + "ZZZ" + // de-be + "DES" + // de-ch + "DEU" + // de-de + "DEU" + // de-de_phoneb + "ZZZ" + // de-it + "DEC" + // de-li + "DEL" + // de-lu + "ZZZ" + // dje + "ZZZ" + // dje-ne + "DSB" + // dsb + "DSB" + // dsb-de + "ZZZ" + // dua + "ZZZ" + // dua-cm + "DIV" + // dv + "DIV" + // dv-mv + "ZZZ" + // dyo + "ZZZ" + // dyo-sn + "ZZZ" + // dz + "ZZZ" + // dz-bt + "ZZZ" + // ebu + "ZZZ" + // ebu-ke + "ZZZ" + // ee + "ZZZ" + // ee-gh + "ZZZ" + // ee-tg + "ELL" + // el + "ZZZ" + // el-cy + "ELL" + // el-gr + "ENU" + // en + "ZZZ" + // en-001 + "ENB" + // en-029 + "ZZZ" + // en-150 + "ZZZ" + // en-ag + "ZZZ" + // en-ai + "ZZZ" + // en-as + "ZZZ" + // en-at + "ENA" + // en-au + "ZZZ" + // en-bb + "ZZZ" + // en-be + "ZZZ" + // en-bi + "ZZZ" + // en-bm + "ZZZ" + // en-bs + "ZZZ" + // en-bw + "ENL" + // en-bz + "ENC" + // en-ca + "ZZZ" + // en-cc + "ZZZ" + // en-ch + "ZZZ" + // en-ck + "ZZZ" + // en-cm + "ZZZ" + // en-cx + "ZZZ" + // en-cy + "ZZZ" + // en-de + "ZZZ" + // en-dk + "ZZZ" + // en-dm + "ZZZ" + // en-er + "ZZZ" + // en-fi + "ZZZ" + // en-fj + "ZZZ" + // en-fk + "ZZZ" + // en-fm + "ENG" + // en-gb + "ZZZ" + // en-gd + "ZZZ" + // en-gg + "ZZZ" + // en-gh + "ZZZ" + // en-gi + "ZZZ" + // en-gm + "ZZZ" + // en-gu + "ZZZ" + // en-gy + "ENH" + // en-hk + "ZZZ" + // en-id + "ENI" + // en-ie + "ZZZ" + // en-il + "ZZZ" + // en-im + "ENN" + // en-in + "ZZZ" + // en-io + "ZZZ" + // en-je + "ENJ" + // en-jm + "ZZZ" + // en-ke + "ZZZ" + // en-ki + "ZZZ" + // en-kn + "ZZZ" + // en-ky + "ZZZ" + // en-lc + "ZZZ" + // en-lr + "ZZZ" + // en-ls + "ZZZ" + // en-mg + "ZZZ" + // en-mh + "ZZZ" + // en-mo + "ZZZ" + // en-mp + "ZZZ" + // en-ms + "ZZZ" + // en-mt + "ZZZ" + // en-mu + "ZZZ" + // en-mw + "ENM" + // en-my + "ZZZ" + // en-na + "ZZZ" + // en-nf + "ZZZ" + // en-ng + "ZZZ" + // en-nl + "ZZZ" + // en-nr + "ZZZ" + // en-nu + "ENZ" + // en-nz + "ZZZ" + // en-pg + "ENP" + // en-ph + "ZZZ" + // en-pk + "ZZZ" + // en-pn + "ZZZ" + // en-pr + "ZZZ" + // en-pw + "ZZZ" + // en-rw + "ZZZ" + // en-sb + "ZZZ" + // en-sc + "ZZZ" + // en-sd + "ZZZ" + // en-se + "ENE" + // en-sg + "ZZZ" + // en-sh + "ZZZ" + // en-si + "ZZZ" + // en-sl + "ZZZ" + // en-ss + "ZZZ" + // en-sx + "ZZZ" + // en-sz + "ZZZ" + // en-tc + "ZZZ" + // en-tk + "ZZZ" + // en-to + "ENT" + // en-tt + "ZZZ" + // en-tv + "ZZZ" + // en-tz + "ZZZ" + // en-ug + "ZZZ" + // en-um + "ENU" + // en-us + "ZZZ" + // en-vc + "ZZZ" + // en-vg + "ZZZ" + // en-vi + "ZZZ" + // en-vu + "ZZZ" + // en-ws + "ENS" + // en-za + "ZZZ" + // en-zm + "ENW" + // en-zw + "ZZZ" + // eo + "ZZZ" + // eo-001 + "ESN" + // es + "ESJ" + // es-419 + "ESS" + // es-ar + "ESB" + // es-bo + "ZZZ" + // es-br + "ESL" + // es-cl + "ESO" + // es-co + "ESC" + // es-cr + "ESK" + // es-cu + "ESD" + // es-do + "ESF" + // es-ec + "ESN" + // es-es + "ESP" + // es-es_tradnl + "ZZZ" + // es-gq + "ESG" + // es-gt + "ESH" + // es-hn + "ESM" + // es-mx + "ESI" + // es-ni + "ESA" + // es-pa + "ESR" + // es-pe + "ZZZ" + // es-ph + "ESU" + // es-pr + "ESZ" + // es-py + "ESE" + // es-sv + "EST" + // es-us + "ESY" + // es-uy + "ESV" + // es-ve + "ETI" + // et + "ETI" + // et-ee + "EUQ" + // eu + "EUQ" + // eu-es + "ZZZ" + // ewo + "ZZZ" + // ewo-cm + "FAR" + // fa + "FAR" + // fa-ir + "FUL" + // ff + "ZZZ" + // ff-cm + "ZZZ" + // ff-gn + "FUL" + // ff-latn + "FUL" + // ff-latn-sn + "ZZZ" + // ff-mr + "ZZZ" + // ff-ng + "FIN" + // fi + "FIN" + // fi-fi + "FPO" + // fil + "FPO" + // fil-ph + "FOS" + // fo + "ZZZ" + // fo-dk + "FOS" + // fo-fo + "FRA" + // fr + "ZZZ" + // fr-029 + "FRB" + // fr-be + "ZZZ" + // fr-bf + "ZZZ" + // fr-bi + "ZZZ" + // fr-bj + "ZZZ" + // fr-bl + "FRC" + // fr-ca + "FRD" + // fr-cd + "ZZZ" + // fr-cf + "ZZZ" + // fr-cg + "FRS" + // fr-ch + "FRI" + // fr-ci + "FRE" + // fr-cm + "ZZZ" + // fr-dj + "ZZZ" + // fr-dz + "FRA" + // fr-fr + "ZZZ" + // fr-ga + "ZZZ" + // fr-gf + "ZZZ" + // fr-gn + "ZZZ" + // fr-gp + "ZZZ" + // fr-gq + "FRH" + // fr-ht + "ZZZ" + // fr-km + "FRL" + // fr-lu + "FRO" + // fr-ma + "FRM" + // fr-mc + "ZZZ" + // fr-mf + "ZZZ" + // fr-mg + "FRF" + // fr-ml + "ZZZ" + // fr-mq + "ZZZ" + // fr-mr + "ZZZ" + // fr-mu + "ZZZ" + // fr-nc + "ZZZ" + // fr-ne + "ZZZ" + // fr-pf + "ZZZ" + // fr-pm + "FRR" + // fr-re + "ZZZ" + // fr-rw + "ZZZ" + // fr-sc + "FRN" + // fr-sn + "ZZZ" + // fr-sy + "ZZZ" + // fr-td + "ZZZ" + // fr-tg + "ZZZ" + // fr-tn + "ZZZ" + // fr-vu + "ZZZ" + // fr-wf + "ZZZ" + // fr-yt + "ZZZ" + // fur + "ZZZ" + // fur-it + "FYN" + // fy + "FYN" + // fy-nl + "IRE" + // ga + "IRE" + // ga-ie + "GLA" + // gd + "GLA" + // gd-gb + "GLC" + // gl + "GLC" + // gl-es + "GRN" + // gn + "GRN" + // gn-py + "ZZZ" + // gsw + "ZZZ" + // gsw-ch + "GSW" + // gsw-fr + "ZZZ" + // gsw-li + "GUJ" + // gu + "GUJ" + // gu-in + "ZZZ" + // guz + "ZZZ" + // guz-ke + "ZZZ" + // gv + "ZZZ" + // gv-im + "HAU" + // ha + "HAU" + // ha-latn + "ZZZ" + // ha-latn-gh + "ZZZ" + // ha-latn-ne + "HAU" + // ha-latn-ng + "HAW" + // haw + "HAW" + // haw-us + "HEB" + // he + "HEB" + // he-il + "HIN" + // hi + "HIN" + // hi-in + "HRV" + // hr + "HRB" + // hr-ba + "HRV" + // hr-hr + "HSB" + // hsb + "HSB" + // hsb-de + "HUN" + // hu + "HUN" + // hu-hu + "HUN" + // hu-hu_technl + "HYE" + // hy + "HYE" + // hy-am + "ZZZ" + // ia + "ZZZ" + // ia-001 + "ZZZ" + // ia-fr + "ZZZ" + // ibb + "ZZZ" + // ibb-ng + "IND" + // id + "IND" + // id-id + "IBO" + // ig + "IBO" + // ig-ng + "III" + // ii + "III" + // ii-cn + "ISL" + // is + "ISL" + // is-is + "ITA" + // it + "ITS" + // it-ch + "ITA" + // it-it + "ZZZ" + // it-sm + "IUK" + // iu + "IUS" + // iu-cans + "IUS" + // iu-cans-ca + "IUK" + // iu-latn + "IUK" + // iu-latn-ca + "JPN" + // ja + "JPN" + // ja-jp + "JPN" + // ja-jp_radstr + "ZZZ" + // jgo + "ZZZ" + // jgo-cm + "ZZZ" + // jmc + "ZZZ" + // jmc-tz + "JAV" + // jv + "ZZZ" + // jv-java + "ZZZ" + // jv-java-id + "JAV" + // jv-latn + "JAV" + // jv-latn-id + "KAT" + // ka + "KAT" + // ka-ge + "KAT" + // ka-ge_modern + "ZZZ" + // kab + "ZZZ" + // kab-dz + "ZZZ" + // kam + "ZZZ" + // kam-ke + "ZZZ" + // kde + "ZZZ" + // kde-tz + "ZZZ" + // kea + "ZZZ" + // kea-cv + "ZZZ" + // khq + "ZZZ" + // khq-ml + "ZZZ" + // ki + "ZZZ" + // ki-ke + "KKZ" + // kk + "KKZ" + // kk-kz + "ZZZ" + // kkj + "ZZZ" + // kkj-cm + "KAL" + // kl + "KAL" + // kl-gl + "ZZZ" + // kln + "ZZZ" + // kln-ke + "KHM" + // km + "KHM" + // km-kh + "KDI" + // kn + "KDI" + // kn-in + "KOR" + // ko + "ZZZ" + // ko-kp + "KOR" + // ko-kr + "KNK" + // kok + "KNK" + // kok-in + "ZZZ" + // kr + "ZZZ" + // kr-ng + "ZZZ" + // ks + "ZZZ" + // ks-arab + "ZZZ" + // ks-arab-in + "ZZZ" + // ks-deva + "ZZZ" + // ks-deva-in + "ZZZ" + // ksb + "ZZZ" + // ksb-tz + "ZZZ" + // ksf + "ZZZ" + // ksf-cm + "ZZZ" + // ksh + "ZZZ" + // ksh-de + "KUR" + // ku + "KUR" + // ku-arab + "KUR" + // ku-arab-iq + "ZZZ" + // ku-arab-ir + "ZZZ" + // kw + "ZZZ" + // kw-gb + "KYR" + // ky + "KYR" + // ky-kg + "ZZZ" + // la + "ZZZ" + // la-001 + "ZZZ" + // lag + "ZZZ" + // lag-tz + "LBX" + // lb + "LBX" + // lb-lu + "ZZZ" + // lg + "ZZZ" + // lg-ug + "ZZZ" + // lkt + "ZZZ" + // lkt-us + "ZZZ" + // ln + "ZZZ" + // ln-ao + "ZZZ" + // ln-cd + "ZZZ" + // ln-cf + "ZZZ" + // ln-cg + "LAO" + // lo + "LAO" + // lo-la + "ZZZ" + // lrc + "ZZZ" + // lrc-iq + "ZZZ" + // lrc-ir + "LTH" + // lt + "LTH" + // lt-lt + "ZZZ" + // lu + "ZZZ" + // lu-cd + "ZZZ" + // luo + "ZZZ" + // luo-ke + "ZZZ" + // luy + "ZZZ" + // luy-ke + "LVI" + // lv + "LVI" + // lv-lv + "ZZZ" + // mas + "ZZZ" + // mas-ke + "ZZZ" + // mas-tz + "ZZZ" + // mer + "ZZZ" + // mer-ke + "ZZZ" + // mfe + "ZZZ" + // mfe-mu + "MLG" + // mg + "MLG" + // mg-mg + "ZZZ" + // mgh + "ZZZ" + // mgh-mz + "ZZZ" + // mgo + "ZZZ" + // mgo-cm + "MRI" + // mi + "MRI" + // mi-nz + "MKI" + // mk + "MKI" + // mk-mk + "MYM" + // ml + "MYM" + // ml-in + "MNN" + // mn + "MNN" + // mn-cyrl + "MNN" + // mn-mn + "MNG" + // mn-mong + "MNG" + // mn-mong-cn + "MNM" + // mn-mong-mn + "ZZZ" + // mni + "ZZZ" + // mni-in + "MWK" + // moh + "MWK" + // moh-ca + "MAR" + // mr + "MAR" + // mr-in + "MSL" + // ms + "MSB" + // ms-bn + "MSL" + // ms-my + "ZZZ" + // ms-sg + "MLT" + // mt + "MLT" + // mt-mt + "ZZZ" + // mua + "ZZZ" + // mua-cm + "MYA" + // my + "MYA" + // my-mm + "ZZZ" + // mzn + "ZZZ" + // mzn-ir + "ZZZ" + // naq + "ZZZ" + // naq-na + "NOR" + // nb + "NOR" + // nb-no + "ZZZ" + // nb-sj + "ZZZ" + // nd + "ZZZ" + // nd-zw + "ZZZ" + // nds + "ZZZ" + // nds-de + "ZZZ" + // nds-nl + "NEP" + // ne + "NEI" + // ne-in + "NEP" + // ne-np + "NLD" + // nl + "ZZZ" + // nl-aw + "NLB" + // nl-be + "ZZZ" + // nl-bq + "ZZZ" + // nl-cw + "NLD" + // nl-nl + "ZZZ" + // nl-sr + "ZZZ" + // nl-sx + "ZZZ" + // nmg + "ZZZ" + // nmg-cm + "NON" + // nn + "NON" + // nn-no + "ZZZ" + // nnh + "ZZZ" + // nnh-cm + "NOR" + // no + "NQO" + // nqo + "NQO" + // nqo-gn + "ZZZ" + // nr + "ZZZ" + // nr-za + "NSO" + // nso + "NSO" + // nso-za + "ZZZ" + // nus + "ZZZ" + // nus-ss + "ZZZ" + // nyn + "ZZZ" + // nyn-ug + "OCI" + // oc + "OCI" + // oc-fr + "ORM" + // om + "ORM" + // om-et + "ZZZ" + // om-ke + "ORI" + // or + "ORI" + // or-in + "ZZZ" + // os + "ZZZ" + // os-ge + "ZZZ" + // os-ru + "PAN" + // pa + "PAP" + // pa-arab + "PAP" + // pa-arab-pk + "PAN" + // pa-in + "ZZZ" + // pap + "ZZZ" + // pap-029 + "PLK" + // pl + "PLK" + // pl-pl + "ZZZ" + // prg + "ZZZ" + // prg-001 + "PRS" + // prs + "PRS" + // prs-af + "PAS" + // ps + "PAS" + // ps-af + "PTB" + // pt + "PTA" + // pt-ao + "PTB" + // pt-br + "ZZZ" + // pt-ch + "ZZZ" + // pt-cv + "ZZZ" + // pt-gq + "ZZZ" + // pt-gw + "ZZZ" + // pt-lu + "ZZZ" + // pt-mo + "ZZZ" + // pt-mz + "PTG" + // pt-pt + "ZZZ" + // pt-st + "ZZZ" + // pt-tl + "ENJ" + // qps-latn-x-sh + "ENU" + // qps-ploc + "JPN" + // qps-ploca + "ARA" + // qps-plocm + "QUT" + // quc + "QUT" + // quc-latn + "QUT" + // quc-latn-gt + "QUB" + // quz + "QUB" + // quz-bo + "QUE" + // quz-ec + "QUP" + // quz-pe + "RMC" + // rm + "RMC" + // rm-ch + "ZZZ" + // rn + "ZZZ" + // rn-bi + "ROM" + // ro + "ROD" + // ro-md + "ROM" + // ro-ro + "ZZZ" + // rof + "ZZZ" + // rof-tz + "RUS" + // ru + "ZZZ" + // ru-by + "ZZZ" + // ru-kg + "ZZZ" + // ru-kz + "RUM" + // ru-md + "RUS" + // ru-ru + "ZZZ" + // ru-ua + "KIN" + // rw + "KIN" + // rw-rw + "ZZZ" + // rwk + "ZZZ" + // rwk-tz + "SAN" + // sa + "SAN" + // sa-in + "SAH" + // sah + "SAH" + // sah-ru + "ZZZ" + // saq + "ZZZ" + // saq-ke + "ZZZ" + // sbp + "ZZZ" + // sbp-tz + "SIP" + // sd + "SIP" + // sd-arab + "SIP" + // sd-arab-pk + "ZZZ" + // sd-deva + "ZZZ" + // sd-deva-in + "SME" + // se + "SMG" + // se-fi + "SME" + // se-no + "SMF" + // se-se + "ZZZ" + // seh + "ZZZ" + // seh-mz + "ZZZ" + // ses + "ZZZ" + // ses-ml + "ZZZ" + // sg + "ZZZ" + // sg-cf + "ZZZ" + // shi + "ZZZ" + // shi-latn + "ZZZ" + // shi-latn-ma + "ZZZ" + // shi-tfng + "ZZZ" + // shi-tfng-ma + "SIN" + // si + "SIN" + // si-lk + "SKY" + // sk + "SKY" + // sk-sk + "SLV" + // sl + "SLV" + // sl-si + "SMB" + // sma + "SMA" + // sma-no + "SMB" + // sma-se + "SMK" + // smj + "SMJ" + // smj-no + "SMK" + // smj-se + "SMN" + // smn + "SMN" + // smn-fi + "SMS" + // sms + "SMS" + // sms-fi + "SNA" + // sn + "SNA" + // sn-latn + "SNA" + // sn-latn-zw + "SOM" + // so + "ZZZ" + // so-dj + "ZZZ" + // so-et + "ZZZ" + // so-ke + "SOM" + // so-so + "SQI" + // sq + "SQI" + // sq-al + "ZZZ" + // sq-mk + "ZZZ" + // sq-xk + "SRM" + // sr + "SRO" + // sr-cyrl + "SRN" + // sr-cyrl-ba + "SRB" + // sr-cyrl-cs + "SRQ" + // sr-cyrl-me + "SRO" + // sr-cyrl-rs + "ZZZ" + // sr-cyrl-xk + "SRM" + // sr-latn + "SRS" + // sr-latn-ba + "SRL" + // sr-latn-cs + "SRP" + // sr-latn-me + "SRM" + // sr-latn-rs + "ZZZ" + // sr-latn-xk + "ZZZ" + // ss + "ZZZ" + // ss-sz + "ZZZ" + // ss-za + "ZZZ" + // ssy + "ZZZ" + // ssy-er + "SOT" + // st + "ZZZ" + // st-ls + "SOT" + // st-za + "SVE" + // sv + "ZZZ" + // sv-ax + "SVF" + // sv-fi + "SVE" + // sv-se + "SWK" + // sw + "ZZZ" + // sw-cd + "SWK" + // sw-ke + "ZZZ" + // sw-tz + "ZZZ" + // sw-ug + "ZZZ" + // swc + "ZZZ" + // swc-cd + "SYR" + // syr + "SYR" + // syr-sy + "TAI" + // ta + "TAI" + // ta-in + "TAM" + // ta-lk + "ZZZ" + // ta-my + "ZZZ" + // ta-sg + "TEL" + // te + "TEL" + // te-in + "ZZZ" + // teo + "ZZZ" + // teo-ke + "ZZZ" + // teo-ug + "TAJ" + // tg + "TAJ" + // tg-cyrl + "TAJ" + // tg-cyrl-tj + "THA" + // th + "THA" + // th-th + "TIR" + // ti + "TIR" + // ti-er + "TIE" + // ti-et + "ZZZ" + // tig + "ZZZ" + // tig-er + "TUK" + // tk + "TUK" + // tk-tm + "TSN" + // tn + "TSB" + // tn-bw + "TSN" + // tn-za + "ZZZ" + // to + "ZZZ" + // to-to + "TRK" + // tr + "ZZZ" + // tr-cy + "TRK" + // tr-tr + "TSO" + // ts + "TSO" + // ts-za + "TTT" + // tt + "TTT" + // tt-ru + "ZZZ" + // twq + "ZZZ" + // twq-ne + "TZA" + // tzm + "ZZZ" + // tzm-arab + "ZZZ" + // tzm-arab-ma + "TZA" + // tzm-latn + "TZA" + // tzm-latn-dz + "ZZZ" + // tzm-latn-ma + "TZM" + // tzm-tfng + "TZM" + // tzm-tfng-ma + "UIG" + // ug + "UIG" + // ug-cn + "UKR" + // uk + "UKR" + // uk-ua + "URD" + // ur + "URI" + // ur-in + "URD" + // ur-pk + "UZB" + // uz + "ZZZ" + // uz-arab + "ZZZ" + // uz-arab-af + "UZC" + // uz-cyrl + "UZC" + // uz-cyrl-uz + "UZB" + // uz-latn + "UZB" + // uz-latn-uz + "ZZZ" + // vai + "ZZZ" + // vai-latn + "ZZZ" + // vai-latn-lr + "ZZZ" + // vai-vaii + "ZZZ" + // vai-vaii-lr + "ZZZ" + // ve + "ZZZ" + // ve-za + "VIT" + // vi + "VIT" + // vi-vn + "ZZZ" + // vo + "ZZZ" + // vo-001 + "ZZZ" + // vun + "ZZZ" + // vun-tz + "ZZZ" + // wae + "ZZZ" + // wae-ch + "ZZZ" + // wal + "ZZZ" + // wal-et + "WOL" + // wo + "WOL" + // wo-sn + "IVL" + // x-iv_mathan + "XHO" + // xh + "XHO" + // xh-za + "ZZZ" + // xog + "ZZZ" + // xog-ug + "ZZZ" + // yav + "ZZZ" + // yav-cm + "ZZZ" + // yi + "ZZZ" + // yi-001 + "YOR" + // yo + "ZZZ" + // yo-bj + "YOR" + // yo-ng + "ZZZ" + // yue + "ZZZ" + // yue-hk + "ZHG" + // zgh + "ZHG" + // zgh-tfng + "ZHG" + // zgh-tfng-ma + "CHS" + // zh + "CHS" + // zh-chs + "CHT" + // zh-cht + "CHS" + // zh-cn + "CHS" + // zh-cn_phoneb + "CHS" + // zh-cn_stroke + "CHS" + // zh-hans + "ZZZ" + // zh-hans-hk + "ZZZ" + // zh-hans-mo + "ZHH" + // zh-hant + "ZHH" + // zh-hk + "ZHH" + // zh-hk_radstr + "ZHM" + // zh-mo + "ZHM" + // zh-mo_radstr + "ZHM" + // zh-mo_stroke + "ZHI" + // zh-sg + "ZHI" + // zh-sg_phoneb + "ZHI" + // zh-sg_stroke + "CHT" + // zh-tw + "CHT" + // zh-tw_pronun + "CHT" + // zh-tw_radstr + "ZUL" + // zu + "ZUL"; // zu-za + + // s_localeNamesIndices contains the start index of every culture name in the string + // s_localeNames. We infer the length of each string by looking at the start index + // of the next string. + private static readonly int[] s_localeNamesIndices = new int[] + { + // c_localeNames index, // index to this array - culture name + 0 , // 0 - aa + 2 , // 1 - aa-dj + 7 , // 2 - aa-er + 12 , // 3 - aa-et + 17 , // 4 - af + 19 , // 5 - af-na + 24 , // 6 - af-za + 29 , // 7 - agq + 32 , // 8 - agq-cm + 38 , // 9 - ak + 40 , // 10 - ak-gh + 45 , // 11 - am + 47 , // 12 - am-et + 52 , // 13 - ar + 54 , // 14 - ar-001 + 60 , // 15 - ar-ae + 65 , // 16 - ar-bh + 70 , // 17 - ar-dj + 75 , // 18 - ar-dz + 80 , // 19 - ar-eg + 85 , // 20 - ar-er + 90 , // 21 - ar-il + 95 , // 22 - ar-iq + 100 , // 23 - ar-jo + 105 , // 24 - ar-km + 110 , // 25 - ar-kw + 115 , // 26 - ar-lb + 120 , // 27 - ar-ly + 125 , // 28 - ar-ma + 130 , // 29 - ar-mr + 135 , // 30 - ar-om + 140 , // 31 - ar-ps + 145 , // 32 - ar-qa + 150 , // 33 - ar-sa + 155 , // 34 - ar-sd + 160 , // 35 - ar-so + 165 , // 36 - ar-ss + 170 , // 37 - ar-sy + 175 , // 38 - ar-td + 180 , // 39 - ar-tn + 185 , // 40 - ar-ye + 190 , // 41 - arn + 193 , // 42 - arn-cl + 199 , // 43 - as + 201 , // 44 - as-in + 206 , // 45 - asa + 209 , // 46 - asa-tz + 215 , // 47 - ast + 218 , // 48 - ast-es + 224 , // 49 - az + 226 , // 50 - az-cyrl + 233 , // 51 - az-cyrl-az + 243 , // 52 - az-latn + 250 , // 53 - az-latn-az + 260 , // 54 - ba + 262 , // 55 - ba-ru + 267 , // 56 - bas + 270 , // 57 - bas-cm + 276 , // 58 - be + 278 , // 59 - be-by + 283 , // 60 - bem + 286 , // 61 - bem-zm + 292 , // 62 - bez + 295 , // 63 - bez-tz + 301 , // 64 - bg + 303 , // 65 - bg-bg + 308 , // 66 - bin + 311 , // 67 - bin-ng + 317 , // 68 - bm + 319 , // 69 - bm-latn + 326 , // 70 - bm-latn-ml + 336 , // 71 - bn + 338 , // 72 - bn-bd + 343 , // 73 - bn-in + 348 , // 74 - bo + 350 , // 75 - bo-cn + 355 , // 76 - bo-in + 360 , // 77 - br + 362 , // 78 - br-fr + 367 , // 79 - brx + 370 , // 80 - brx-in + 376 , // 81 - bs + 378 , // 82 - bs-cyrl + 385 , // 83 - bs-cyrl-ba + 395 , // 84 - bs-latn + 402 , // 85 - bs-latn-ba + 412 , // 86 - byn + 415 , // 87 - byn-er + 421 , // 88 - ca + 423 , // 89 - ca-ad + 428 , // 90 - ca-es + 433 , // 91 - ca-es-valencia + 447 , // 92 - ca-fr + 452 , // 93 - ca-it + 457 , // 94 - ce + 459 , // 95 - ce-ru + 464 , // 96 - cgg + 467 , // 97 - cgg-ug + 473 , // 98 - chr + 476 , // 99 - chr-cher + 484 , // 100 - chr-cher-us + 495 , // 101 - co + 497 , // 102 - co-fr + 502 , // 103 - cs + 504 , // 104 - cs-cz + 509 , // 105 - cu + 511 , // 106 - cu-ru + 516 , // 107 - cy + 518 , // 108 - cy-gb + 523 , // 109 - da + 525 , // 110 - da-dk + 530 , // 111 - da-gl + 535 , // 112 - dav + 538 , // 113 - dav-ke + 544 , // 114 - de + 546 , // 115 - de-at + 551 , // 116 - de-be + 556 , // 117 - de-ch + 561 , // 118 - de-de + 566 , // 119 - de-de_phoneb + 578 , // 120 - de-it + 583 , // 121 - de-li + 588 , // 122 - de-lu + 593 , // 123 - dje + 596 , // 124 - dje-ne + 602 , // 125 - dsb + 605 , // 126 - dsb-de + 611 , // 127 - dua + 614 , // 128 - dua-cm + 620 , // 129 - dv + 622 , // 130 - dv-mv + 627 , // 131 - dyo + 630 , // 132 - dyo-sn + 636 , // 133 - dz + 638 , // 134 - dz-bt + 643 , // 135 - ebu + 646 , // 136 - ebu-ke + 652 , // 137 - ee + 654 , // 138 - ee-gh + 659 , // 139 - ee-tg + 664 , // 140 - el + 666 , // 141 - el-cy + 671 , // 142 - el-gr + 676 , // 143 - en + 678 , // 144 - en-001 + 684 , // 145 - en-029 + 690 , // 146 - en-150 + 696 , // 147 - en-ag + 701 , // 148 - en-ai + 706 , // 149 - en-as + 711 , // 150 - en-at + 716 , // 151 - en-au + 721 , // 152 - en-bb + 726 , // 153 - en-be + 731 , // 154 - en-bi + 736 , // 155 - en-bm + 741 , // 156 - en-bs + 746 , // 157 - en-bw + 751 , // 158 - en-bz + 756 , // 159 - en-ca + 761 , // 160 - en-cc + 766 , // 161 - en-ch + 771 , // 162 - en-ck + 776 , // 163 - en-cm + 781 , // 164 - en-cx + 786 , // 165 - en-cy + 791 , // 166 - en-de + 796 , // 167 - en-dk + 801 , // 168 - en-dm + 806 , // 169 - en-er + 811 , // 170 - en-fi + 816 , // 171 - en-fj + 821 , // 172 - en-fk + 826 , // 173 - en-fm + 831 , // 174 - en-gb + 836 , // 175 - en-gd + 841 , // 176 - en-gg + 846 , // 177 - en-gh + 851 , // 178 - en-gi + 856 , // 179 - en-gm + 861 , // 180 - en-gu + 866 , // 181 - en-gy + 871 , // 182 - en-hk + 876 , // 183 - en-id + 881 , // 184 - en-ie + 886 , // 185 - en-il + 891 , // 186 - en-im + 896 , // 187 - en-in + 901 , // 188 - en-io + 906 , // 189 - en-je + 911 , // 190 - en-jm + 916 , // 191 - en-ke + 921 , // 192 - en-ki + 926 , // 193 - en-kn + 931 , // 194 - en-ky + 936 , // 195 - en-lc + 941 , // 196 - en-lr + 946 , // 197 - en-ls + 951 , // 198 - en-mg + 956 , // 199 - en-mh + 961 , // 200 - en-mo + 966 , // 201 - en-mp + 971 , // 202 - en-ms + 976 , // 203 - en-mt + 981 , // 204 - en-mu + 986 , // 205 - en-mw + 991 , // 206 - en-my + 996 , // 207 - en-na + 1001, // 208 - en-nf + 1006, // 209 - en-ng + 1011, // 210 - en-nl + 1016, // 211 - en-nr + 1021, // 212 - en-nu + 1026, // 213 - en-nz + 1031, // 214 - en-pg + 1036, // 215 - en-ph + 1041, // 216 - en-pk + 1046, // 217 - en-pn + 1051, // 218 - en-pr + 1056, // 219 - en-pw + 1061, // 220 - en-rw + 1066, // 221 - en-sb + 1071, // 222 - en-sc + 1076, // 223 - en-sd + 1081, // 224 - en-se + 1086, // 225 - en-sg + 1091, // 226 - en-sh + 1096, // 227 - en-si + 1101, // 228 - en-sl + 1106, // 229 - en-ss + 1111, // 230 - en-sx + 1116, // 231 - en-sz + 1121, // 232 - en-tc + 1126, // 233 - en-tk + 1131, // 234 - en-to + 1136, // 235 - en-tt + 1141, // 236 - en-tv + 1146, // 237 - en-tz + 1151, // 238 - en-ug + 1156, // 239 - en-um + 1161, // 240 - en-us + 1166, // 241 - en-vc + 1171, // 242 - en-vg + 1176, // 243 - en-vi + 1181, // 244 - en-vu + 1186, // 245 - en-ws + 1191, // 246 - en-za + 1196, // 247 - en-zm + 1201, // 248 - en-zw + 1206, // 249 - eo + 1208, // 250 - eo-001 + 1214, // 251 - es + 1216, // 252 - es-419 + 1222, // 253 - es-ar + 1227, // 254 - es-bo + 1232, // 255 - es-br + 1237, // 256 - es-cl + 1242, // 257 - es-co + 1247, // 258 - es-cr + 1252, // 259 - es-cu + 1257, // 260 - es-do + 1262, // 261 - es-ec + 1267, // 262 - es-es + 1272, // 263 - es-es_tradnl + 1284, // 264 - es-gq + 1289, // 265 - es-gt + 1294, // 266 - es-hn + 1299, // 267 - es-mx + 1304, // 268 - es-ni + 1309, // 269 - es-pa + 1314, // 270 - es-pe + 1319, // 271 - es-ph + 1324, // 272 - es-pr + 1329, // 273 - es-py + 1334, // 274 - es-sv + 1339, // 275 - es-us + 1344, // 276 - es-uy + 1349, // 277 - es-ve + 1354, // 278 - et + 1356, // 279 - et-ee + 1361, // 280 - eu + 1363, // 281 - eu-es + 1368, // 282 - ewo + 1371, // 283 - ewo-cm + 1377, // 284 - fa + 1379, // 285 - fa-ir + 1384, // 286 - ff + 1386, // 287 - ff-cm + 1391, // 288 - ff-gn + 1396, // 289 - ff-latn + 1403, // 290 - ff-latn-sn + 1413, // 291 - ff-mr + 1418, // 292 - ff-ng + 1423, // 293 - fi + 1425, // 294 - fi-fi + 1430, // 295 - fil + 1433, // 296 - fil-ph + 1439, // 297 - fo + 1441, // 298 - fo-dk + 1446, // 299 - fo-fo + 1451, // 300 - fr + 1453, // 301 - fr-029 + 1459, // 302 - fr-be + 1464, // 303 - fr-bf + 1469, // 304 - fr-bi + 1474, // 305 - fr-bj + 1479, // 306 - fr-bl + 1484, // 307 - fr-ca + 1489, // 308 - fr-cd + 1494, // 309 - fr-cf + 1499, // 310 - fr-cg + 1504, // 311 - fr-ch + 1509, // 312 - fr-ci + 1514, // 313 - fr-cm + 1519, // 314 - fr-dj + 1524, // 315 - fr-dz + 1529, // 316 - fr-fr + 1534, // 317 - fr-ga + 1539, // 318 - fr-gf + 1544, // 319 - fr-gn + 1549, // 320 - fr-gp + 1554, // 321 - fr-gq + 1559, // 322 - fr-ht + 1564, // 323 - fr-km + 1569, // 324 - fr-lu + 1574, // 325 - fr-ma + 1579, // 326 - fr-mc + 1584, // 327 - fr-mf + 1589, // 328 - fr-mg + 1594, // 329 - fr-ml + 1599, // 330 - fr-mq + 1604, // 331 - fr-mr + 1609, // 332 - fr-mu + 1614, // 333 - fr-nc + 1619, // 334 - fr-ne + 1624, // 335 - fr-pf + 1629, // 336 - fr-pm + 1634, // 337 - fr-re + 1639, // 338 - fr-rw + 1644, // 339 - fr-sc + 1649, // 340 - fr-sn + 1654, // 341 - fr-sy + 1659, // 342 - fr-td + 1664, // 343 - fr-tg + 1669, // 344 - fr-tn + 1674, // 345 - fr-vu + 1679, // 346 - fr-wf + 1684, // 347 - fr-yt + 1689, // 348 - fur + 1692, // 349 - fur-it + 1698, // 350 - fy + 1700, // 351 - fy-nl + 1705, // 352 - ga + 1707, // 353 - ga-ie + 1712, // 354 - gd + 1714, // 355 - gd-gb + 1719, // 356 - gl + 1721, // 357 - gl-es + 1726, // 358 - gn + 1728, // 359 - gn-py + 1733, // 360 - gsw + 1736, // 361 - gsw-ch + 1742, // 362 - gsw-fr + 1748, // 363 - gsw-li + 1754, // 364 - gu + 1756, // 365 - gu-in + 1761, // 366 - guz + 1764, // 367 - guz-ke + 1770, // 368 - gv + 1772, // 369 - gv-im + 1777, // 370 - ha + 1779, // 371 - ha-latn + 1786, // 372 - ha-latn-gh + 1796, // 373 - ha-latn-ne + 1806, // 374 - ha-latn-ng + 1816, // 375 - haw + 1819, // 376 - haw-us + 1825, // 377 - he + 1827, // 378 - he-il + 1832, // 379 - hi + 1834, // 380 - hi-in + 1839, // 381 - hr + 1841, // 382 - hr-ba + 1846, // 383 - hr-hr + 1851, // 384 - hsb + 1854, // 385 - hsb-de + 1860, // 386 - hu + 1862, // 387 - hu-hu + 1867, // 388 - hu-hu_technl + 1879, // 389 - hy + 1881, // 390 - hy-am + 1886, // 391 - ia + 1888, // 392 - ia-001 + 1894, // 393 - ia-fr + 1899, // 394 - ibb + 1902, // 395 - ibb-ng + 1908, // 396 - id + 1910, // 397 - id-id + 1915, // 398 - ig + 1917, // 399 - ig-ng + 1922, // 400 - ii + 1924, // 401 - ii-cn + 1929, // 402 - is + 1931, // 403 - is-is + 1936, // 404 - it + 1938, // 405 - it-ch + 1943, // 406 - it-it + 1948, // 407 - it-sm + 1953, // 408 - iu + 1955, // 409 - iu-cans + 1962, // 410 - iu-cans-ca + 1972, // 411 - iu-latn + 1979, // 412 - iu-latn-ca + 1989, // 413 - ja + 1991, // 414 - ja-jp + 1996, // 415 - ja-jp_radstr + 2008, // 416 - jgo + 2011, // 417 - jgo-cm + 2017, // 418 - jmc + 2020, // 419 - jmc-tz + 2026, // 420 - jv + 2028, // 421 - jv-java + 2035, // 422 - jv-java-id + 2045, // 423 - jv-latn + 2052, // 424 - jv-latn-id + 2062, // 425 - ka + 2064, // 426 - ka-ge + 2069, // 427 - ka-ge_modern + 2081, // 428 - kab + 2084, // 429 - kab-dz + 2090, // 430 - kam + 2093, // 431 - kam-ke + 2099, // 432 - kde + 2102, // 433 - kde-tz + 2108, // 434 - kea + 2111, // 435 - kea-cv + 2117, // 436 - khq + 2120, // 437 - khq-ml + 2126, // 438 - ki + 2128, // 439 - ki-ke + 2133, // 440 - kk + 2135, // 441 - kk-kz + 2140, // 442 - kkj + 2143, // 443 - kkj-cm + 2149, // 444 - kl + 2151, // 445 - kl-gl + 2156, // 446 - kln + 2159, // 447 - kln-ke + 2165, // 448 - km + 2167, // 449 - km-kh + 2172, // 450 - kn + 2174, // 451 - kn-in + 2179, // 452 - ko + 2181, // 453 - ko-kp + 2186, // 454 - ko-kr + 2191, // 455 - kok + 2194, // 456 - kok-in + 2200, // 457 - kr + 2202, // 458 - kr-ng + 2207, // 459 - ks + 2209, // 460 - ks-arab + 2216, // 461 - ks-arab-in + 2226, // 462 - ks-deva + 2233, // 463 - ks-deva-in + 2243, // 464 - ksb + 2246, // 465 - ksb-tz + 2252, // 466 - ksf + 2255, // 467 - ksf-cm + 2261, // 468 - ksh + 2264, // 469 - ksh-de + 2270, // 470 - ku + 2272, // 471 - ku-arab + 2279, // 472 - ku-arab-iq + 2289, // 473 - ku-arab-ir + 2299, // 474 - kw + 2301, // 475 - kw-gb + 2306, // 476 - ky + 2308, // 477 - ky-kg + 2313, // 478 - la + 2315, // 479 - la-001 + 2321, // 480 - lag + 2324, // 481 - lag-tz + 2330, // 482 - lb + 2332, // 483 - lb-lu + 2337, // 484 - lg + 2339, // 485 - lg-ug + 2344, // 486 - lkt + 2347, // 487 - lkt-us + 2353, // 488 - ln + 2355, // 489 - ln-ao + 2360, // 490 - ln-cd + 2365, // 491 - ln-cf + 2370, // 492 - ln-cg + 2375, // 493 - lo + 2377, // 494 - lo-la + 2382, // 495 - lrc + 2385, // 496 - lrc-iq + 2391, // 497 - lrc-ir + 2397, // 498 - lt + 2399, // 499 - lt-lt + 2404, // 500 - lu + 2406, // 501 - lu-cd + 2411, // 502 - luo + 2414, // 503 - luo-ke + 2420, // 504 - luy + 2423, // 505 - luy-ke + 2429, // 506 - lv + 2431, // 507 - lv-lv + 2436, // 508 - mas + 2439, // 509 - mas-ke + 2445, // 510 - mas-tz + 2451, // 511 - mer + 2454, // 512 - mer-ke + 2460, // 513 - mfe + 2463, // 514 - mfe-mu + 2469, // 515 - mg + 2471, // 516 - mg-mg + 2476, // 517 - mgh + 2479, // 518 - mgh-mz + 2485, // 519 - mgo + 2488, // 520 - mgo-cm + 2494, // 521 - mi + 2496, // 522 - mi-nz + 2501, // 523 - mk + 2503, // 524 - mk-mk + 2508, // 525 - ml + 2510, // 526 - ml-in + 2515, // 527 - mn + 2517, // 528 - mn-cyrl + 2524, // 529 - mn-mn + 2529, // 530 - mn-mong + 2536, // 531 - mn-mong-cn + 2546, // 532 - mn-mong-mn + 2556, // 533 - mni + 2559, // 534 - mni-in + 2565, // 535 - moh + 2568, // 536 - moh-ca + 2574, // 537 - mr + 2576, // 538 - mr-in + 2581, // 539 - ms + 2583, // 540 - ms-bn + 2588, // 541 - ms-my + 2593, // 542 - ms-sg + 2598, // 543 - mt + 2600, // 544 - mt-mt + 2605, // 545 - mua + 2608, // 546 - mua-cm + 2614, // 547 - my + 2616, // 548 - my-mm + 2621, // 549 - mzn + 2624, // 550 - mzn-ir + 2630, // 551 - naq + 2633, // 552 - naq-na + 2639, // 553 - nb + 2641, // 554 - nb-no + 2646, // 555 - nb-sj + 2651, // 556 - nd + 2653, // 557 - nd-zw + 2658, // 558 - nds + 2661, // 559 - nds-de + 2667, // 560 - nds-nl + 2673, // 561 - ne + 2675, // 562 - ne-in + 2680, // 563 - ne-np + 2685, // 564 - nl + 2687, // 565 - nl-aw + 2692, // 566 - nl-be + 2697, // 567 - nl-bq + 2702, // 568 - nl-cw + 2707, // 569 - nl-nl + 2712, // 570 - nl-sr + 2717, // 571 - nl-sx + 2722, // 572 - nmg + 2725, // 573 - nmg-cm + 2731, // 574 - nn + 2733, // 575 - nn-no + 2738, // 576 - nnh + 2741, // 577 - nnh-cm + 2747, // 578 - no + 2749, // 579 - nqo + 2752, // 580 - nqo-gn + 2758, // 581 - nr + 2760, // 582 - nr-za + 2765, // 583 - nso + 2768, // 584 - nso-za + 2774, // 585 - nus + 2777, // 586 - nus-ss + 2783, // 587 - nyn + 2786, // 588 - nyn-ug + 2792, // 589 - oc + 2794, // 590 - oc-fr + 2799, // 591 - om + 2801, // 592 - om-et + 2806, // 593 - om-ke + 2811, // 594 - or + 2813, // 595 - or-in + 2818, // 596 - os + 2820, // 597 - os-ge + 2825, // 598 - os-ru + 2830, // 599 - pa + 2832, // 600 - pa-arab + 2839, // 601 - pa-arab-pk + 2849, // 602 - pa-in + 2854, // 603 - pap + 2857, // 604 - pap-029 + 2864, // 605 - pl + 2866, // 606 - pl-pl + 2871, // 607 - prg + 2874, // 608 - prg-001 + 2881, // 609 - prs + 2884, // 610 - prs-af + 2890, // 611 - ps + 2892, // 612 - ps-af + 2897, // 613 - pt + 2899, // 614 - pt-ao + 2904, // 615 - pt-br + 2909, // 616 - pt-ch + 2914, // 617 - pt-cv + 2919, // 618 - pt-gq + 2924, // 619 - pt-gw + 2929, // 620 - pt-lu + 2934, // 621 - pt-mo + 2939, // 622 - pt-mz + 2944, // 623 - pt-pt + 2949, // 624 - pt-st + 2954, // 625 - pt-tl + 2959, // 626 - qps-latn-x-sh + 2972, // 627 - qps-ploc + 2980, // 628 - qps-ploca + 2989, // 629 - qps-plocm + 2998, // 630 - quc + 3001, // 631 - quc-latn + 3009, // 632 - quc-latn-gt + 3020, // 633 - quz + 3023, // 634 - quz-bo + 3029, // 635 - quz-ec + 3035, // 636 - quz-pe + 3041, // 637 - rm + 3043, // 638 - rm-ch + 3048, // 639 - rn + 3050, // 640 - rn-bi + 3055, // 641 - ro + 3057, // 642 - ro-md + 3062, // 643 - ro-ro + 3067, // 644 - rof + 3070, // 645 - rof-tz + 3076, // 646 - ru + 3078, // 647 - ru-by + 3083, // 648 - ru-kg + 3088, // 649 - ru-kz + 3093, // 650 - ru-md + 3098, // 651 - ru-ru + 3103, // 652 - ru-ua + 3108, // 653 - rw + 3110, // 654 - rw-rw + 3115, // 655 - rwk + 3118, // 656 - rwk-tz + 3124, // 657 - sa + 3126, // 658 - sa-in + 3131, // 659 - sah + 3134, // 660 - sah-ru + 3140, // 661 - saq + 3143, // 662 - saq-ke + 3149, // 663 - sbp + 3152, // 664 - sbp-tz + 3158, // 665 - sd + 3160, // 666 - sd-arab + 3167, // 667 - sd-arab-pk + 3177, // 668 - sd-deva + 3184, // 669 - sd-deva-in + 3194, // 670 - se + 3196, // 671 - se-fi + 3201, // 672 - se-no + 3206, // 673 - se-se + 3211, // 674 - seh + 3214, // 675 - seh-mz + 3220, // 676 - ses + 3223, // 677 - ses-ml + 3229, // 678 - sg + 3231, // 679 - sg-cf + 3236, // 680 - shi + 3239, // 681 - shi-latn + 3247, // 682 - shi-latn-ma + 3258, // 683 - shi-tfng + 3266, // 684 - shi-tfng-ma + 3277, // 685 - si + 3279, // 686 - si-lk + 3284, // 687 - sk + 3286, // 688 - sk-sk + 3291, // 689 - sl + 3293, // 690 - sl-si + 3298, // 691 - sma + 3301, // 692 - sma-no + 3307, // 693 - sma-se + 3313, // 694 - smj + 3316, // 695 - smj-no + 3322, // 696 - smj-se + 3328, // 697 - smn + 3331, // 698 - smn-fi + 3337, // 699 - sms + 3340, // 700 - sms-fi + 3346, // 701 - sn + 3348, // 702 - sn-latn + 3355, // 703 - sn-latn-zw + 3365, // 704 - so + 3367, // 705 - so-dj + 3372, // 706 - so-et + 3377, // 707 - so-ke + 3382, // 708 - so-so + 3387, // 709 - sq + 3389, // 710 - sq-al + 3394, // 711 - sq-mk + 3399, // 712 - sq-xk + 3404, // 713 - sr + 3406, // 714 - sr-cyrl + 3413, // 715 - sr-cyrl-ba + 3423, // 716 - sr-cyrl-cs + 3433, // 717 - sr-cyrl-me + 3443, // 718 - sr-cyrl-rs + 3453, // 719 - sr-cyrl-xk + 3463, // 720 - sr-latn + 3470, // 721 - sr-latn-ba + 3480, // 722 - sr-latn-cs + 3490, // 723 - sr-latn-me + 3500, // 724 - sr-latn-rs + 3510, // 725 - sr-latn-xk + 3520, // 726 - ss + 3522, // 727 - ss-sz + 3527, // 728 - ss-za + 3532, // 729 - ssy + 3535, // 730 - ssy-er + 3541, // 731 - st + 3543, // 732 - st-ls + 3548, // 733 - st-za + 3553, // 734 - sv + 3555, // 735 - sv-ax + 3560, // 736 - sv-fi + 3565, // 737 - sv-se + 3570, // 738 - sw + 3572, // 739 - sw-cd + 3577, // 740 - sw-ke + 3582, // 741 - sw-tz + 3587, // 742 - sw-ug + 3592, // 743 - swc + 3595, // 744 - swc-cd + 3601, // 745 - syr + 3604, // 746 - syr-sy + 3610, // 747 - ta + 3612, // 748 - ta-in + 3617, // 749 - ta-lk + 3622, // 750 - ta-my + 3627, // 751 - ta-sg + 3632, // 752 - te + 3634, // 753 - te-in + 3639, // 754 - teo + 3642, // 755 - teo-ke + 3648, // 756 - teo-ug + 3654, // 757 - tg + 3656, // 758 - tg-cyrl + 3663, // 759 - tg-cyrl-tj + 3673, // 760 - th + 3675, // 761 - th-th + 3680, // 762 - ti + 3682, // 763 - ti-er + 3687, // 764 - ti-et + 3692, // 765 - tig + 3695, // 766 - tig-er + 3701, // 767 - tk + 3703, // 768 - tk-tm + 3708, // 769 - tn + 3710, // 770 - tn-bw + 3715, // 771 - tn-za + 3720, // 772 - to + 3722, // 773 - to-to + 3727, // 774 - tr + 3729, // 775 - tr-cy + 3734, // 776 - tr-tr + 3739, // 777 - ts + 3741, // 778 - ts-za + 3746, // 779 - tt + 3748, // 780 - tt-ru + 3753, // 781 - twq + 3756, // 782 - twq-ne + 3762, // 783 - tzm + 3765, // 784 - tzm-arab + 3773, // 785 - tzm-arab-ma + 3784, // 786 - tzm-latn + 3792, // 787 - tzm-latn-dz + 3803, // 788 - tzm-latn-ma + 3814, // 789 - tzm-tfng + 3822, // 790 - tzm-tfng-ma + 3833, // 791 - ug + 3835, // 792 - ug-cn + 3840, // 793 - uk + 3842, // 794 - uk-ua + 3847, // 795 - ur + 3849, // 796 - ur-in + 3854, // 797 - ur-pk + 3859, // 798 - uz + 3861, // 799 - uz-arab + 3868, // 800 - uz-arab-af + 3878, // 801 - uz-cyrl + 3885, // 802 - uz-cyrl-uz + 3895, // 803 - uz-latn + 3902, // 804 - uz-latn-uz + 3912, // 805 - vai + 3915, // 806 - vai-latn + 3923, // 807 - vai-latn-lr + 3934, // 808 - vai-vaii + 3942, // 809 - vai-vaii-lr + 3953, // 810 - ve + 3955, // 811 - ve-za + 3960, // 812 - vi + 3962, // 813 - vi-vn + 3967, // 814 - vo + 3969, // 815 - vo-001 + 3975, // 816 - vun + 3978, // 817 - vun-tz + 3984, // 818 - wae + 3987, // 819 - wae-ch + 3993, // 820 - wal + 3996, // 821 - wal-et + 4002, // 822 - wo + 4004, // 823 - wo-sn + 4009, // 824 - x-iv_mathan + 4020, // 825 - xh + 4022, // 826 - xh-za + 4027, // 827 - xog + 4030, // 828 - xog-ug + 4036, // 829 - yav + 4039, // 830 - yav-cm + 4045, // 831 - yi + 4047, // 832 - yi-001 + 4053, // 833 - yo + 4055, // 834 - yo-bj + 4060, // 835 - yo-ng + 4065, // 836 - yue + 4068, // 837 - yue-hk + 4074, // 838 - zgh + 4077, // 839 - zgh-tfng + 4085, // 840 - zgh-tfng-ma + 4096, // 841 - zh + 4098, // 842 - zh-chs + 4104, // 843 - zh-cht + 4110, // 844 - zh-cn + 4115, // 845 - zh-cn_phoneb + 4127, // 846 - zh-cn_stroke + 4139, // 847 - zh-hans + 4146, // 848 - zh-hans-hk + 4156, // 849 - zh-hans-mo + 4166, // 850 - zh-hant + 4173, // 851 - zh-hk + 4178, // 852 - zh-hk_radstr + 4190, // 853 - zh-mo + 4195, // 854 - zh-mo_radstr + 4207, // 855 - zh-mo_stroke + 4219, // 856 - zh-sg + 4224, // 857 - zh-sg_phoneb + 4236, // 858 - zh-sg_stroke + 4248, // 859 - zh-tw + 4253, // 860 - zh-tw_pronun + 4265, // 861 - zh-tw_radstr + 4277, // 862 - zu + 4279, // 863 - zu-za + 4284 + }; + + private const int NUMERIC_LOCALE_DATA_COUNT_PER_ROW = 9; + // s_nameIndexToNumericData is mapping from index in s_localeNamesIndices to locale data. + // each row in the table will have the following data: + // Lcid, Ansi codepage, Oem codepage, MAC codepage, EBCDIC codepage, Geo Id, Digit Substitution, specific locale index, Console locale index + private static readonly int[] s_nameIndexToNumericData = new int[] + { + // Lcid, Ansi CP, Oem CP, MAC CP, EBCDIC CP, Geo Id, digit substitution, Specific culture index, keyboard Id, Console locale index // index - locale name + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x49 , 1 , 3 , 240 , // 0 - aa + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x3e , 1 , 1 , 240 , // 1 - aa-dj + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x47 , 1 , 2 , 240 , // 2 - aa-er + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x49 , 1 , 3 , 240 , // 3 - aa-et + 0x36 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xd1 , 1 , 6 , 6 , // 4 - af + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xfe , 1 , 5 , 240 , // 5 - af-na + 0x436 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xd1 , 1 , 6 , 6 , // 6 - af-za + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 8 , 240 , // 7 - agq + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 8 , 240 , // 8 - agq-cm + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x59 , 1 , 10 , 240 , // 9 - ak + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x59 , 1 , 10 , 240 , // 10 - ak-gh + 0x5e , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x49 , 1 , 12 , 143 , // 11 - am + 0x45e , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x49 , 1 , 12 , 143 , // 12 - am-et + 0x1 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0xcd , 0 , 33 , 143 , // 13 - ar + 0x1000 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x989e, 0 , 14 , 240 , // 14 - ar-001 + 0x3801 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0xe0 , 0 , 15 , 143 , // 15 - ar-ae + 0x3c01 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x11 , 0 , 16 , 143 , // 16 - ar-bh + 0x1000 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x3e , 0 , 17 , 240 , // 17 - ar-dj + 0x1401 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x4 , 1 , 18 , 300 , // 18 - ar-dz + 0xc01 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x43 , 0 , 19 , 143 , // 19 - ar-eg + 0x1000 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x47 , 0 , 20 , 240 , // 20 - ar-er + 0x1000 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x75 , 0 , 21 , 240 , // 21 - ar-il + 0x801 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x79 , 0 , 22 , 143 , // 22 - ar-iq + 0x2c01 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x7e , 0 , 23 , 143 , // 23 - ar-jo + 0x1000 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x32 , 0 , 24 , 240 , // 24 - ar-km + 0x3401 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x88 , 0 , 25 , 143 , // 25 - ar-kw + 0x3001 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x8b , 0 , 26 , 143 , // 26 - ar-lb + 0x1001 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x94 , 1 , 27 , 143 , // 27 - ar-ly + 0x1801 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x9f , 1 , 28 , 300 , // 28 - ar-ma + 0x1000 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0xa2 , 0 , 29 , 240 , // 29 - ar-mr + 0x2001 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0xa4 , 0 , 30 , 143 , // 30 - ar-om + 0x1000 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0xb8 , 0 , 31 , 240 , // 31 - ar-ps + 0x4001 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0xc5 , 0 , 32 , 143 , // 32 - ar-qa + 0x401 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0xcd , 0 , 33 , 143 , // 33 - ar-sa + 0x1000 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0xdb , 0 , 34 , 240 , // 34 - ar-sd + 0x1000 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0xd8 , 0 , 35 , 240 , // 35 - ar-so + 0x1000 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x114 , 0 , 36 , 240 , // 36 - ar-ss + 0x2801 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0xde , 0 , 37 , 143 , // 37 - ar-sy + 0x1000 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x29 , 0 , 38 , 240 , // 38 - ar-td + 0x1c01 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0xea , 1 , 39 , 300 , // 39 - ar-tn + 0x2401 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x105 , 0 , 40 , 143 , // 40 - ar-ye + 0x7a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0x2e , 1 , 42 , 42 , // 41 - arn + 0x47a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0x2e , 1 , 42 , 42 , // 42 - arn-cl + 0x4d , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 44 , 143 , // 43 - as + 0x44d , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 44 , 143 , // 44 - as-in + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 46 , 240 , // 45 - asa + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 46 , 240 , // 46 - asa-tz + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xd9 , 1 , 48 , 240 , // 47 - ast + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xd9 , 1 , 48 , 240 , // 48 - ast-es + 0x2c , 0x4e6 , 0x359 , 0x2761, 0x51a9, 0x5 , 1 , 53 , 53 , // 49 - az + 0x742c , 0x4e3 , 0x362 , 0x2717, 0x5190, 0x5 , 1 , 51 , 51 , // 50 - az-cyrl + 0x82c , 0x4e3 , 0x362 , 0x2717, 0x5190, 0x5 , 1 , 51 , 51 , // 51 - az-cyrl-az + 0x782c , 0x4e6 , 0x359 , 0x2761, 0x51a9, 0x5 , 1 , 53 , 53 , // 52 - az-latn + 0x42c , 0x4e6 , 0x359 , 0x2761, 0x51a9, 0x5 , 1 , 53 , 53 , // 53 - az-latn-az + 0x6d , 0x4e3 , 0x362 , 0x2717, 0x5190, 0xcb , 1 , 55 , 55 , // 54 - ba + 0x46d , 0x4e3 , 0x362 , 0x2717, 0x5190, 0xcb , 1 , 55 , 55 , // 55 - ba-ru + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 57 , 240 , // 56 - bas + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 57 , 240 , // 57 - bas-cm + 0x23 , 0x4e3 , 0x362 , 0x2717, 0x1f4 , 0x1d , 1 , 59 , 59 , // 58 - be + 0x423 , 0x4e3 , 0x362 , 0x2717, 0x1f4 , 0x1d , 1 , 59 , 59 , // 59 - be-by + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x107 , 1 , 61 , 240 , // 60 - bem + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x107 , 1 , 61 , 240 , // 61 - bem-zm + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 63 , 240 , // 62 - bez + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 63 , 240 , // 63 - bez-tz + 0x2 , 0x4e3 , 0x362 , 0x2717, 0x5221, 0x23 , 1 , 65 , 65 , // 64 - bg + 0x402 , 0x4e3 , 0x362 , 0x2717, 0x5221, 0x23 , 1 , 65 , 65 , // 65 - bg-bg + 0x66 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xaf , 1 , 67 , 240 , // 66 - bin + 0x466 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xaf , 1 , 67 , 240 , // 67 - bin-ng + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x9d , 1 , 70 , 240 , // 68 - bm + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x9d , 1 , 70 , 240 , // 69 - bm-latn + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x9d , 1 , 70 , 240 , // 70 - bm-latn-ml + 0x45 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x17 , 1 , 72 , 143 , // 71 - bn + 0x845 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x17 , 1 , 72 , 143 , // 72 - bn-bd + 0x445 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 73 , 143 , // 73 - bn-in + 0x51 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x2d , 1 , 75 , 143 , // 74 - bo + 0x451 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x2d , 1 , 75 , 143 , // 75 - bo-cn + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 76 , 240 , // 76 - bo-in + 0x7e , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x54 , 1 , 78 , 78 , // 77 - br + 0x47e , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x54 , 1 , 78 , 78 , // 78 - br-fr + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 80 , 240 , // 79 - brx + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 80 , 240 , // 80 - brx-in + 0x781a , 0x4e2 , 0x354 , 0x2762, 0x366 , 0x19 , 1 , 85 , 85 , // 81 - bs + 0x641a , 0x4e3 , 0x357 , 0x2762, 0x366 , 0x19 , 1 , 83 , 83 , // 82 - bs-cyrl + 0x201a , 0x4e3 , 0x357 , 0x2762, 0x366 , 0x19 , 1 , 83 , 83 , // 83 - bs-cyrl-ba + 0x681a , 0x4e2 , 0x354 , 0x2762, 0x366 , 0x19 , 1 , 85 , 85 , // 84 - bs-latn + 0x141a , 0x4e2 , 0x354 , 0x2762, 0x366 , 0x19 , 1 , 85 , 85 , // 85 - bs-latn-ba + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x47 , 1 , 87 , 240 , // 86 - byn + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x47 , 1 , 87 , 240 , // 87 - byn-er + 0x3 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xd9 , 1 , 90 , 90 , // 88 - ca + 0x1000 , 0x4e4 , 0x352 , 0x2 , 0x1f4 , 0x8 , 1 , 89 , 240 , // 89 - ca-ad + 0x403 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xd9 , 1 , 90 , 90 , // 90 - ca-es + 0x803 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xd9 , 1 , 91 , 90 , // 91 - ca-es-valencia + 0x1000 , 0x4e4 , 0x352 , 0x2 , 0x1f4 , 0x54 , 1 , 92 , 240 , // 92 - ca-fr + 0x1000 , 0x4e4 , 0x352 , 0x2 , 0x1f4 , 0x76 , 1 , 93 , 240 , // 93 - ca-it + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xcb , 1 , 95 , 240 , // 94 - ce + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xcb , 1 , 95 , 240 , // 95 - ce-ru + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xf0 , 1 , 97 , 240 , // 96 - cgg + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xf0 , 1 , 97 , 240 , // 97 - cgg-ug + 0x5c , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xf4 , 1 , 100 , 240 , // 98 - chr + 0x7c5c , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xf4 , 1 , 100 , 240 , // 99 - chr-cher + 0x45c , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xf4 , 1 , 100 , 240 , // 100 - chr-cher-us + 0x83 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x54 , 1 , 102 , 102 , // 101 - co + 0x483 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x54 , 1 , 102 , 102 , // 102 - co-fr + 0x5 , 0x4e2 , 0x354 , 0x272d, 0x1f4 , 0x4b , 1 , 104 , 104 , // 103 - cs + 0x405 , 0x4e2 , 0x354 , 0x272d, 0x1f4 , 0x4b , 1 , 104 , 104 , // 104 - cs-cz + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xcb , 1 , 106 , 240 , // 105 - cu + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xcb , 1 , 106 , 240 , // 106 - cu-ru + 0x52 , 0x4e4 , 0x352 , 0x2710, 0x4f3d, 0xf2 , 1 , 108 , 108 , // 107 - cy + 0x452 , 0x4e4 , 0x352 , 0x2710, 0x4f3d, 0xf2 , 1 , 108 , 108 , // 108 - cy-gb + 0x6 , 0x4e4 , 0x352 , 0x2710, 0x4f35, 0x3d , 1 , 110 , 110 , // 109 - da + 0x406 , 0x4e4 , 0x352 , 0x2710, 0x4f35, 0x3d , 1 , 110 , 110 , // 110 - da-dk + 0x1000 , 0x4e4 , 0x352 , 0x2 , 0x1f4 , 0x5d , 1 , 111 , 240 , // 111 - da-gl + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 113 , 240 , // 112 - dav + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 113 , 240 , // 113 - dav-ke + 0x7 , 0x4e4 , 0x352 , 0x2710, 0x4f31, 0x5e , 1 , 118 , 118 , // 114 - de + 0xc07 , 0x4e4 , 0x352 , 0x2710, 0x4f31, 0xe , 1 , 115 , 115 , // 115 - de-at + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f31, 0x15 , 1 , 116 , 240 , // 116 - de-be + 0x807 , 0x4e4 , 0x352 , 0x2710, 0x4f31, 0xdf , 1 , 117 , 117 , // 117 - de-ch + 0x407 , 0x4e4 , 0x352 , 0x2710, 0x4f31, 0x5e , 1 , 118 , 118 , // 118 - de-de + 0x10407, 0x4e4 , 0x352 , 0x2710, 0x4f31, 0x5e , 1 , 118 , 118 , // 119 - de-de_phoneb + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x76 , 1 , 120 , 240 , // 120 - de-it + 0x1407 , 0x4e4 , 0x352 , 0x2710, 0x4f31, 0x91 , 1 , 121 , 121 , // 121 - de-li + 0x1007 , 0x4e4 , 0x352 , 0x2710, 0x4f31, 0x93 , 1 , 122 , 122 , // 122 - de-lu + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xad , 1 , 124 , 240 , // 123 - dje + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xad , 1 , 124 , 240 , // 124 - dje-ne + 0x7c2e , 0x4e4 , 0x352 , 0x2710, 0x366 , 0x5e , 1 , 126 , 126 , // 125 - dsb + 0x82e , 0x4e4 , 0x352 , 0x2710, 0x366 , 0x5e , 1 , 126 , 126 , // 126 - dsb-de + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 128 , 240 , // 127 - dua + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 128 , 240 , // 128 - dua-cm + 0x65 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xa5 , 1 , 130 , 143 , // 129 - dv + 0x465 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xa5 , 1 , 130 , 143 , // 130 - dv-mv + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xd2 , 1 , 132 , 240 , // 131 - dyo + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xd2 , 1 , 132 , 240 , // 132 - dyo-sn + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x22 , 2 , 134 , 240 , // 133 - dz + 0xc51 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x22 , 2 , 134 , 240 , // 134 - dz-bt + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 136 , 240 , // 135 - ebu + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 136 , 240 , // 136 - ebu-ke + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x59 , 1 , 138 , 240 , // 137 - ee + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x59 , 1 , 138 , 240 , // 138 - ee-gh + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xe8 , 1 , 139 , 240 , // 139 - ee-tg + 0x8 , 0x4e5 , 0x2e1 , 0x2716, 0x4f31, 0x62 , 1 , 142 , 142 , // 140 - el + 0x1000 , 0x4e5 , 0x2e1 , 0x2716, 0x4f31, 0x3b , 1 , 141 , 240 , // 141 - el-cy + 0x408 , 0x4e5 , 0x2e1 , 0x2716, 0x4f31, 0x62 , 1 , 142 , 142 , // 142 - el-gr + 0x9 , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0xf4 , 1 , 240 , 240 , // 143 - en + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x989e, 1 , 144 , 240 , // 144 - en-001 + 0x2409 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x993248, 1 , 145 , 145 , // 145 - en-029 + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x292d, 1 , 146 , 240 , // 146 - en-150 + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x2 , 1 , 147 , 240 , // 147 - en-ag + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x12c , 1 , 148 , 240 , // 148 - en-ai + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xa , 1 , 149 , 240 , // 149 - en-as + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xe , 1 , 150 , 240 , // 150 - en-at + 0xc09 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xc , 1 , 151 , 151 , // 151 - en-au + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x12 , 1 , 152 , 240 , // 152 - en-bb + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x15 , 1 , 153 , 240 , // 153 - en-be + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x26 , 1 , 154 , 240 , // 154 - en-bi + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x14 , 1 , 155 , 240 , // 155 - en-bm + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x16 , 1 , 156 , 240 , // 156 - en-bs + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x13 , 1 , 157 , 240 , // 157 - en-bw + 0x2809 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x18 , 1 , 158 , 158 , // 158 - en-bz + 0x1009 , 0x4e4 , 0x352 , 0x2710, 0x25 , 0x27 , 1 , 159 , 159 , // 159 - en-ca + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x137 , 1 , 160 , 240 , // 160 - en-cc + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xdf , 1 , 161 , 240 , // 161 - en-ch + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x138 , 1 , 162 , 240 , // 162 - en-ck + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x31 , 1 , 163 , 240 , // 163 - en-cm + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x135 , 1 , 164 , 240 , // 164 - en-cx + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x3b , 1 , 165 , 240 , // 165 - en-cy + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x5e , 1 , 166 , 240 , // 166 - en-de + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x3d , 1 , 167 , 240 , // 167 - en-dk + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x3f , 1 , 168 , 240 , // 168 - en-dm + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x47 , 1 , 169 , 240 , // 169 - en-er + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x4d , 1 , 170 , 240 , // 170 - en-fi + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x4e , 1 , 171 , 240 , // 171 - en-fj + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x13b , 1 , 172 , 240 , // 172 - en-fk + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x50 , 1 , 173 , 240 , // 173 - en-fm + 0x809 , 0x4e4 , 0x352 , 0x2710, 0x4f3d, 0xf2 , 1 , 174 , 174 , // 174 - en-gb + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x5b , 1 , 175 , 240 , // 175 - en-gd + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x144 , 1 , 176 , 240 , // 176 - en-gg + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x59 , 1 , 177 , 240 , // 177 - en-gh + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x5a , 1 , 178 , 240 , // 178 - en-gi + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x56 , 1 , 179 , 240 , // 179 - en-gm + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x142 , 1 , 180 , 240 , // 180 - en-gu + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x65 , 1 , 181 , 240 , // 181 - en-gy + 0x3c09 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x68 , 1 , 182 , 240 , // 182 - en-hk + 0x3809 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x6f , 1 , 183 , 240 , // 183 - en-id + 0x1809 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x44 , 1 , 184 , 184 , // 184 - en-ie + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x75 , 1 , 185 , 240 , // 185 - en-il + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x3b16, 1 , 186 , 240 , // 186 - en-im + 0x4009 , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0x71 , 1 , 187 , 187 , // 187 - en-in + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x72 , 1 , 188 , 240 , // 188 - en-io + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x148 , 1 , 189 , 240 , // 189 - en-je + 0x2009 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x7c , 1 , 190 , 190 , // 190 - en-jm + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x81 , 1 , 191 , 240 , // 191 - en-ke + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x85 , 1 , 192 , 240 , // 192 - en-ki + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xcf , 1 , 193 , 240 , // 193 - en-kn + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x133 , 1 , 194 , 240 , // 194 - en-ky + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xda , 1 , 195 , 240 , // 195 - en-lc + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x8e , 1 , 196 , 240 , // 196 - en-lr + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x92 , 1 , 197 , 240 , // 197 - en-ls + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x95 , 1 , 198 , 240 , // 198 - en-mg + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xc7 , 1 , 199 , 240 , // 199 - en-mh + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x97 , 1 , 200 , 240 , // 200 - en-mo + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x151 , 1 , 201 , 240 , // 201 - en-mp + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x14c , 1 , 202 , 240 , // 202 - en-ms + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xa3 , 1 , 203 , 240 , // 203 - en-mt + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xa0 , 1 , 204 , 240 , // 204 - en-mu + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x9c , 1 , 205 , 240 , // 205 - en-mw + 0x4409 , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0xa7 , 1 , 206 , 206 , // 206 - en-my + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xfe , 1 , 207 , 240 , // 207 - en-na + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x150 , 1 , 208 , 240 , // 208 - en-nf + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xaf , 1 , 209 , 240 , // 209 - en-ng + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xb0 , 1 , 210 , 240 , // 210 - en-nl + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xb4 , 1 , 211 , 240 , // 211 - en-nr + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x14f , 1 , 212 , 240 , // 212 - en-nu + 0x1409 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xb7 , 1 , 213 , 213 , // 213 - en-nz + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xc2 , 1 , 214 , 240 , // 214 - en-pg + 0x3409 , 0x4e4 , 0x1b5 , 0x2710, 0x1f4 , 0xc9 , 1 , 215 , 215 , // 215 - en-ph + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xbe , 1 , 216 , 240 , // 216 - en-pk + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x153 , 1 , 217 , 240 , // 217 - en-pn + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xca , 1 , 218 , 240 , // 218 - en-pr + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xc3 , 1 , 219 , 240 , // 219 - en-pw + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xcc , 1 , 220 , 240 , // 220 - en-rw + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x1e , 1 , 221 , 240 , // 221 - en-sb + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xd0 , 1 , 222 , 240 , // 222 - en-sc + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xdb , 1 , 223 , 240 , // 223 - en-sd + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xdd , 1 , 224 , 240 , // 224 - en-se + 0x4809 , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0xd7 , 1 , 225 , 225 , // 225 - en-sg + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x157 , 1 , 226 , 240 , // 226 - en-sh + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xd4 , 1 , 227 , 240 , // 227 - en-si + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xd5 , 1 , 228 , 240 , // 228 - en-sl + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x114 , 1 , 229 , 240 , // 229 - en-ss + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x78f7, 1 , 230 , 240 , // 230 - en-sx + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x104 , 1 , 231 , 240 , // 231 - en-sz + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x15d , 1 , 232 , 240 , // 232 - en-tc + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x15b , 1 , 233 , 240 , // 233 - en-tk + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xe7 , 1 , 234 , 240 , // 234 - en-to + 0x2c09 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xe1 , 1 , 235 , 235 , // 235 - en-tt + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xec , 1 , 236 , 240 , // 236 - en-tv + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xef , 1 , 237 , 240 , // 237 - en-tz + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xf0 , 1 , 238 , 240 , // 238 - en-ug + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x9a55d40, 1 , 239 , 240 , // 239 - en-um + 0x409 , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0xf4 , 1 , 240 , 240 , // 240 - en-us + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xf8 , 1 , 241 , 240 , // 241 - en-vc + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x15f , 1 , 242 , 240 , // 242 - en-vg + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xfc , 1 , 243 , 240 , // 243 - en-vi + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xae , 1 , 244 , 240 , // 244 - en-vu + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x103 , 1 , 245 , 240 , // 245 - en-ws + 0x1c09 , 0x4e4 , 0x1b5 , 0x2710, 0x1f4 , 0xd1 , 1 , 246 , 246 , // 246 - en-za + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x107 , 1 , 247 , 240 , // 247 - en-zm + 0x3009 , 0x4e4 , 0x1b5 , 0x2710, 0x1f4 , 0x108 , 1 , 248 , 248 , // 248 - en-zw + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x989e, 1 , 250 , 240 , // 249 - eo + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x989e, 1 , 250 , 240 , // 250 - eo-001 + 0xa , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0xd9 , 1 , 262 , 262 , // 251 - es + 0x580a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0x9a55d41, 1 , 252 , 240 , // 252 - es-419 + 0x2c0a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0xb , 1 , 253 , 253 , // 253 - es-ar + 0x400a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0x1a , 1 , 254 , 254 , // 254 - es-bo + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x20 , 1 , 255 , 240 , // 255 - es-br + 0x340a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0x2e , 1 , 256 , 256 , // 256 - es-cl + 0x240a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0x33 , 1 , 257 , 257 , // 257 - es-co + 0x140a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0x36 , 1 , 258 , 258 , // 258 - es-cr + 0x5c0a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0x38 , 1 , 259 , 240 , // 259 - es-cu + 0x1c0a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0x41 , 1 , 260 , 260 , // 260 - es-do + 0x300a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0x42 , 1 , 261 , 261 , // 261 - es-ec + 0xc0a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0xd9 , 1 , 262 , 262 , // 262 - es-es + 0x40a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0xd9 , 1 , 263 , 263 , // 263 - es-es_tradnl + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0x45 , 1 , 264 , 240 , // 264 - es-gq + 0x100a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0x63 , 1 , 265 , 265 , // 265 - es-gt + 0x480a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0x6a , 1 , 266 , 266 , // 266 - es-hn + 0x80a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0xa6 , 1 , 267 , 267 , // 267 - es-mx + 0x4c0a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0xb6 , 1 , 268 , 268 , // 268 - es-ni + 0x180a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0xc0 , 1 , 269 , 269 , // 269 - es-pa + 0x280a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0xbb , 1 , 270 , 270 , // 270 - es-pe + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0xc9 , 1 , 271 , 240 , // 271 - es-ph + 0x500a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0xca , 1 , 272 , 272 , // 272 - es-pr + 0x3c0a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0xb9 , 1 , 273 , 273 , // 273 - es-py + 0x440a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0x48 , 1 , 274 , 274 , // 274 - es-sv + 0x540a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0xf4 , 1 , 275 , 275 , // 275 - es-us + 0x380a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0xf6 , 1 , 276 , 276 , // 276 - es-uy + 0x200a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0xf9 , 1 , 277 , 277 , // 277 - es-ve + 0x25 , 0x4e9 , 0x307 , 0x272d, 0x1f4 , 0x46 , 1 , 279 , 279 , // 278 - et + 0x425 , 0x4e9 , 0x307 , 0x272d, 0x1f4 , 0x46 , 1 , 279 , 279 , // 279 - et-ee + 0x2d , 0x4e4 , 0x352 , 0x2 , 0x1f4 , 0xd9 , 1 , 281 , 240 , // 280 - eu + 0x42d , 0x4e4 , 0x352 , 0x2 , 0x1f4 , 0xd9 , 1 , 281 , 240 , // 281 - eu-es + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 283 , 240 , // 282 - ewo + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 283 , 240 , // 283 - ewo-cm + 0x29 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x74 , 0 , 285 , 143 , // 284 - fa + 0x429 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x74 , 0 , 285 , 143 , // 285 - fa-ir + 0x67 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xd2 , 1 , 290 , 290 , // 286 - ff + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x31 , 1 , 287 , 240 , // 287 - ff-cm + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x64 , 1 , 288 , 240 , // 288 - ff-gn + 0x7c67 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xd2 , 1 , 290 , 290 , // 289 - ff-latn + 0x867 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xd2 , 1 , 290 , 290 , // 290 - ff-latn-sn + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xa2 , 1 , 291 , 240 , // 291 - ff-mr + 0x467 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xaf , 1 , 292 , 240 , // 292 - ff-ng + 0xb , 0x4e4 , 0x352 , 0x2710, 0x4f36, 0x4d , 1 , 294 , 294 , // 293 - fi + 0x40b , 0x4e4 , 0x352 , 0x2710, 0x4f36, 0x4d , 1 , 294 , 294 , // 294 - fi-fi + 0x64 , 0x4e4 , 0x1b5 , 0x2710, 0x1f4 , 0xc9 , 1 , 296 , 296 , // 295 - fil + 0x464 , 0x4e4 , 0x1b5 , 0x2710, 0x1f4 , 0xc9 , 1 , 296 , 296 , // 296 - fil-ph + 0x38 , 0x4e4 , 0x352 , 0x275f, 0x4f35, 0x51 , 1 , 299 , 299 , // 297 - fo + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x3d , 1 , 298 , 240 , // 298 - fo-dk + 0x438 , 0x4e4 , 0x352 , 0x275f, 0x4f35, 0x51 , 1 , 299 , 299 , // 299 - fo-fo + 0xc , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x54 , 1 , 316 , 316 , // 300 - fr + 0x1c0c , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x993248, 1 , 301 , 316 , // 301 - fr-029 + 0x80c , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x15 , 1 , 302 , 302 , // 302 - fr-be + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xf5 , 1 , 303 , 240 , // 303 - fr-bf + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x26 , 1 , 304 , 240 , // 304 - fr-bi + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x1c , 1 , 305 , 240 , // 305 - fr-bj + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x9a55c4f, 1 , 306 , 240 , // 306 - fr-bl + 0xc0c , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x27 , 1 , 307 , 307 , // 307 - fr-ca + 0x240c , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x2c , 1 , 308 , 240 , // 308 - fr-cd + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x37 , 1 , 309 , 240 , // 309 - fr-cf + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x2b , 1 , 310 , 240 , // 310 - fr-cg + 0x100c , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xdf , 1 , 311 , 311 , // 311 - fr-ch + 0x300c , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x77 , 1 , 312 , 240 , // 312 - fr-ci + 0x2c0c , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x31 , 1 , 313 , 240 , // 313 - fr-cm + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x3e , 1 , 314 , 240 , // 314 - fr-dj + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x4 , 1 , 315 , 240 , // 315 - fr-dz + 0x40c , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x54 , 1 , 316 , 316 , // 316 - fr-fr + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x57 , 1 , 317 , 240 , // 317 - fr-ga + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x13d , 1 , 318 , 240 , // 318 - fr-gf + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x64 , 1 , 319 , 240 , // 319 - fr-gn + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x141 , 1 , 320 , 240 , // 320 - fr-gp + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x45 , 1 , 321 , 240 , // 321 - fr-gq + 0x3c0c , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x67 , 1 , 322 , 240 , // 322 - fr-ht + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x32 , 1 , 323 , 240 , // 323 - fr-km + 0x140c , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x93 , 1 , 324 , 324 , // 324 - fr-lu + 0x380c , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x9f , 1 , 325 , 240 , // 325 - fr-ma + 0x180c , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x9e , 1 , 326 , 326 , // 326 - fr-mc + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x7bda, 1 , 327 , 240 , // 327 - fr-mf + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x95 , 1 , 328 , 240 , // 328 - fr-mg + 0x340c , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x9d , 1 , 329 , 240 , // 329 - fr-ml + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x14a , 1 , 330 , 240 , // 330 - fr-mq + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xa2 , 1 , 331 , 240 , // 331 - fr-mr + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xa0 , 1 , 332 , 240 , // 332 - fr-mu + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x14e , 1 , 333 , 240 , // 333 - fr-nc + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xad , 1 , 334 , 240 , // 334 - fr-ne + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x13e , 1 , 335 , 240 , // 335 - fr-pf + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xce , 1 , 336 , 240 , // 336 - fr-pm + 0x200c , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xc6 , 1 , 337 , 240 , // 337 - fr-re + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xcc , 1 , 338 , 240 , // 338 - fr-rw + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xd0 , 1 , 339 , 240 , // 339 - fr-sc + 0x280c , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xd2 , 1 , 340 , 240 , // 340 - fr-sn + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xde , 1 , 341 , 240 , // 341 - fr-sy + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x29 , 1 , 342 , 240 , // 342 - fr-td + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xe8 , 1 , 343 , 240 , // 343 - fr-tg + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xea , 1 , 344 , 240 , // 344 - fr-tn + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xae , 1 , 345 , 240 , // 345 - fr-vu + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x160 , 1 , 346 , 240 , // 346 - fr-wf + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x14b , 1 , 347 , 240 , // 347 - fr-yt + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x76 , 1 , 349 , 240 , // 348 - fur + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x76 , 1 , 349 , 240 , // 349 - fur-it + 0x62 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xb0 , 1 , 351 , 351 , // 350 - fy + 0x462 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xb0 , 1 , 351 , 351 , // 351 - fy-nl + 0x3c , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x44 , 1 , 353 , 353 , // 352 - ga + 0x83c , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x44 , 1 , 353 , 353 , // 353 - ga-ie + 0x91 , 0x4e4 , 0x352 , 0x2710, 0x4f3d, 0xf2 , 1 , 355 , 355 , // 354 - gd + 0x491 , 0x4e4 , 0x352 , 0x2710, 0x4f3d, 0xf2 , 1 , 355 , 355 , // 355 - gd-gb + 0x56 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xd9 , 1 , 357 , 357 , // 356 - gl + 0x456 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xd9 , 1 , 357 , 357 , // 357 - gl-es + 0x74 , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0xb9 , 1 , 359 , 359 , // 358 - gn + 0x474 , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0xb9 , 1 , 359 , 359 , // 359 - gn-py + 0x84 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xdf , 1 , 361 , 240 , // 360 - gsw + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xdf , 1 , 361 , 240 , // 361 - gsw-ch + 0x484 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x54 , 1 , 362 , 362 , // 362 - gsw-fr + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x91 , 1 , 363 , 240 , // 363 - gsw-li + 0x47 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 365 , 143 , // 364 - gu + 0x447 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 365 , 143 , // 365 - gu-in + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 367 , 240 , // 366 - guz + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 367 , 240 , // 367 - guz-ke + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x3b16, 1 , 369 , 240 , // 368 - gv + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x3b16, 1 , 369 , 240 , // 369 - gv-im + 0x68 , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0xaf , 1 , 374 , 374 , // 370 - ha + 0x7c68 , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0xaf , 1 , 374 , 374 , // 371 - ha-latn + 0x1000 , 0x4e4 , 0x1b5 , 0x2710, 0x1f4 , 0x59 , 1 , 372 , 240 , // 372 - ha-latn-gh + 0x1000 , 0x4e4 , 0x1b5 , 0x2710, 0x1f4 , 0xad , 1 , 373 , 240 , // 373 - ha-latn-ne + 0x468 , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0xaf , 1 , 374 , 374 , // 374 - ha-latn-ng + 0x75 , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0xf4 , 1 , 376 , 376 , // 375 - haw + 0x475 , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0xf4 , 1 , 376 , 376 , // 376 - haw-us + 0xd , 0x4e7 , 0x35e , 0x2715, 0x1f4 , 0x75 , 1 , 378 , 143 , // 377 - he + 0x40d , 0x4e7 , 0x35e , 0x2715, 0x1f4 , 0x75 , 1 , 378 , 143 , // 378 - he-il + 0x39 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 380 , 143 , // 379 - hi + 0x439 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 380 , 143 , // 380 - hi-in + 0x1a , 0x4e2 , 0x354 , 0x2762, 0x1f4 , 0x6c , 1 , 383 , 383 , // 381 - hr + 0x101a , 0x4e2 , 0x354 , 0x2762, 0x366 , 0x19 , 1 , 382 , 382 , // 382 - hr-ba + 0x41a , 0x4e2 , 0x354 , 0x2762, 0x1f4 , 0x6c , 1 , 383 , 383 , // 383 - hr-hr + 0x2e , 0x4e4 , 0x352 , 0x2710, 0x366 , 0x5e , 1 , 385 , 385 , // 384 - hsb + 0x42e , 0x4e4 , 0x352 , 0x2710, 0x366 , 0x5e , 1 , 385 , 385 , // 385 - hsb-de + 0xe , 0x4e2 , 0x354 , 0x272d, 0x1f4 , 0x6d , 1 , 387 , 387 , // 386 - hu + 0x40e , 0x4e2 , 0x354 , 0x272d, 0x1f4 , 0x6d , 1 , 387 , 387 , // 387 - hu-hu + 0x1040e, 0x4e2 , 0x354 , 0x272d, 0x1f4 , 0x6d , 1 , 387 , 387 , // 388 - hu-hu_technl + 0x2b , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x7 , 1 , 390 , 390 , // 389 - hy + 0x42b , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x7 , 1 , 390 , 390 , // 390 - hy-am + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x54 , 1 , 393 , 240 , // 391 - ia + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x989e, 1 , 392 , 240 , // 392 - ia-001 + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x54 , 1 , 393 , 240 , // 393 - ia-fr + 0x69 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xaf , 1 , 395 , 240 , // 394 - ibb + 0x469 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xaf , 1 , 395 , 240 , // 395 - ibb-ng + 0x21 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x6f , 1 , 397 , 397 , // 396 - id + 0x421 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x6f , 1 , 397 , 397 , // 397 - id-id + 0x70 , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0xaf , 1 , 399 , 399 , // 398 - ig + 0x470 , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0xaf , 1 , 399 , 399 , // 399 - ig-ng + 0x78 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x2d , 1 , 401 , 143 , // 400 - ii + 0x478 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x2d , 1 , 401 , 143 , // 401 - ii-cn + 0xf , 0x4e4 , 0x352 , 0x275f, 0x5187, 0x6e , 1 , 403 , 403 , // 402 - is + 0x40f , 0x4e4 , 0x352 , 0x275f, 0x5187, 0x6e , 1 , 403 , 403 , // 403 - is-is + 0x10 , 0x4e4 , 0x352 , 0x2710, 0x4f38, 0x76 , 1 , 406 , 406 , // 404 - it + 0x810 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xdf , 1 , 405 , 405 , // 405 - it-ch + 0x410 , 0x4e4 , 0x352 , 0x2710, 0x4f38, 0x76 , 1 , 406 , 406 , // 406 - it-it + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f38, 0xd6 , 1 , 407 , 240 , // 407 - it-sm + 0x5d , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0x27 , 1 , 412 , 412 , // 408 - iu + 0x785d , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x27 , 1 , 410 , 143 , // 409 - iu-cans + 0x45d , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x27 , 1 , 410 , 143 , // 410 - iu-cans-ca + 0x7c5d , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0x27 , 1 , 412 , 412 , // 411 - iu-latn + 0x85d , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0x27 , 1 , 412 , 412 , // 412 - iu-latn-ca + 0x11 , 0x3a4 , 0x3a4 , 0x2711, 0x4f42, 0x7a , 1 , 414 , 414 , // 413 - ja + 0x411 , 0x3a4 , 0x3a4 , 0x2711, 0x4f42, 0x7a , 1 , 414 , 414 , // 414 - ja-jp + 0x40411, 0x3a4 , 0x3a4 , 0x2711, 0x4f42, 0x7a , 1 , 414 , 414 , // 415 - ja-jp_radstr + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 417 , 240 , // 416 - jgo + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 417 , 240 , // 417 - jgo-cm + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 419 , 240 , // 418 - jmc + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 419 , 240 , // 419 - jmc-tz + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x6f , 1 , 424 , 424 , // 420 - jv + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x6f , 1 , 422 , 424 , // 421 - jv-java + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x6f , 1 , 422 , 424 , // 422 - jv-java-id + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x6f , 1 , 424 , 424 , // 423 - jv-latn + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x6f , 1 , 424 , 424 , // 424 - jv-latn-id + 0x37 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x58 , 1 , 426 , 426 , // 425 - ka + 0x437 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x58 , 1 , 426 , 426 , // 426 - ka-ge + 0x10437, 0x0 , 0x1 , 0x2 , 0x1f4 , 0x58 , 1 , 426 , 426 , // 427 - ka-ge_modern + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x4 , 1 , 429 , 240 , // 428 - kab + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x4 , 1 , 429 , 240 , // 429 - kab-dz + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 431 , 240 , // 430 - kam + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 431 , 240 , // 431 - kam-ke + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 433 , 240 , // 432 - kde + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 433 , 240 , // 433 - kde-tz + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x39 , 1 , 435 , 240 , // 434 - kea + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x39 , 1 , 435 , 240 , // 435 - kea-cv + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x9d , 1 , 437 , 240 , // 436 - khq + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x9d , 1 , 437 , 240 , // 437 - khq-ml + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 439 , 240 , // 438 - ki + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 439 , 240 , // 439 - ki-ke + 0x3f , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x89 , 1 , 441 , 441 , // 440 - kk + 0x43f , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x89 , 1 , 441 , 441 , // 441 - kk-kz + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 443 , 240 , // 442 - kkj + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 443 , 240 , // 443 - kkj-cm + 0x6f , 0x4e4 , 0x352 , 0x2710, 0x4f35, 0x5d , 1 , 445 , 445 , // 444 - kl + 0x46f , 0x4e4 , 0x352 , 0x2710, 0x4f35, 0x5d , 1 , 445 , 445 , // 445 - kl-gl + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 447 , 240 , // 446 - kln + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 447 , 240 , // 447 - kln-ke + 0x53 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x28 , 2 , 449 , 143 , // 448 - km + 0x453 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x28 , 2 , 449 , 143 , // 449 - km-kh + 0x4b , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 451 , 143 , // 450 - kn + 0x44b , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 451 , 143 , // 451 - kn-in + 0x12 , 0x3b5 , 0x3b5 , 0x2713, 0x5161, 0x86 , 1 , 454 , 454 , // 452 - ko + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x83 , 1 , 453 , 240 , // 453 - ko-kp + 0x412 , 0x3b5 , 0x3b5 , 0x2713, 0x5161, 0x86 , 1 , 454 , 454 , // 454 - ko-kr + 0x57 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 456 , 143 , // 455 - kok + 0x457 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 456 , 143 , // 456 - kok-in + 0x71 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xaf , 1 , 458 , 240 , // 457 - kr + 0x471 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xaf , 1 , 458 , 240 , // 458 - kr-ng + 0x60 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 2 , 461 , 240 , // 459 - ks + 0x460 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 2 , 461 , 240 , // 460 - ks-arab + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 2 , 461 , 240 , // 461 - ks-arab-in + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 463 , 187 , // 462 - ks-deva + 0x860 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 463 , 187 , // 463 - ks-deva-in + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 465 , 240 , // 464 - ksb + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 465 , 240 , // 465 - ksb-tz + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 467 , 240 , // 466 - ksf + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 467 , 240 , // 467 - ksf-cm + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x5e , 1 , 469 , 240 , // 468 - ksh + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x5e , 1 , 469 , 240 , // 469 - ksh-de + 0x92 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x79 , 0 , 472 , 143 , // 470 - ku + 0x7c92 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x79 , 0 , 472 , 143 , // 471 - ku-arab + 0x492 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x79 , 0 , 472 , 143 , // 472 - ku-arab-iq + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x74 , 0 , 473 , 240 , // 473 - ku-arab-ir + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xf2 , 1 , 475 , 240 , // 474 - kw + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xf2 , 1 , 475 , 240 , // 475 - kw-gb + 0x40 , 0x4e3 , 0x362 , 0x2717, 0x5190, 0x82 , 1 , 477 , 477 , // 476 - ky + 0x440 , 0x4e3 , 0x362 , 0x2717, 0x5190, 0x82 , 1 , 477 , 477 , // 477 - ky-kg + 0x76 , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0x989e, 1 , 479 , 143 , // 478 - la + 0x476 , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0x989e, 1 , 479 , 143 , // 479 - la-001 + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 481 , 240 , // 480 - lag + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 481 , 240 , // 481 - lag-tz + 0x6e , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x93 , 1 , 483 , 483 , // 482 - lb + 0x46e , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x93 , 1 , 483 , 483 , // 483 - lb-lu + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xf0 , 1 , 485 , 240 , // 484 - lg + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xf0 , 1 , 485 , 240 , // 485 - lg-ug + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xf4 , 1 , 487 , 240 , // 486 - lkt + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xf4 , 1 , 487 , 240 , // 487 - lkt-us + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x2c , 1 , 490 , 240 , // 488 - ln + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x9 , 1 , 489 , 240 , // 489 - ln-ao + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x2c , 1 , 490 , 240 , // 490 - ln-cd + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x37 , 1 , 491 , 240 , // 491 - ln-cf + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x2b , 1 , 492 , 240 , // 492 - ln-cg + 0x54 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x8a , 1 , 494 , 143 , // 493 - lo + 0x454 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x8a , 1 , 494 , 143 , // 494 - lo-la + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x74 , 2 , 497 , 240 , // 495 - lrc + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x79 , 2 , 496 , 240 , // 496 - lrc-iq + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x74 , 2 , 497 , 240 , // 497 - lrc-ir + 0x27 , 0x4e9 , 0x307 , 0x272d, 0x1f4 , 0x8d , 1 , 499 , 499 , // 498 - lt + 0x427 , 0x4e9 , 0x307 , 0x272d, 0x1f4 , 0x8d , 1 , 499 , 499 , // 499 - lt-lt + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x2c , 1 , 501 , 240 , // 500 - lu + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x2c , 1 , 501 , 240 , // 501 - lu-cd + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 503 , 240 , // 502 - luo + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 503 , 240 , // 503 - luo-ke + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 505 , 240 , // 504 - luy + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 505 , 240 , // 505 - luy-ke + 0x26 , 0x4e9 , 0x307 , 0x272d, 0x1f4 , 0x8c , 1 , 507 , 507 , // 506 - lv + 0x426 , 0x4e9 , 0x307 , 0x272d, 0x1f4 , 0x8c , 1 , 507 , 507 , // 507 - lv-lv + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 509 , 240 , // 508 - mas + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 509 , 240 , // 509 - mas-ke + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 510 , 240 , // 510 - mas-tz + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 512 , 240 , // 511 - mer + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 512 , 240 , // 512 - mer-ke + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xa0 , 1 , 514 , 240 , // 513 - mfe + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xa0 , 1 , 514 , 240 , // 514 - mfe-mu + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x95 , 1 , 516 , 240 , // 515 - mg + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x95 , 1 , 516 , 240 , // 516 - mg-mg + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xa8 , 1 , 518 , 240 , // 517 - mgh + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xa8 , 1 , 518 , 240 , // 518 - mgh-mz + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 520 , 240 , // 519 - mgo + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 520 , 240 , // 520 - mgo-cm + 0x81 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xb7 , 1 , 522 , 522 , // 521 - mi + 0x481 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xb7 , 1 , 522 , 522 , // 522 - mi-nz + 0x2f , 0x4e3 , 0x362 , 0x2717, 0x1f4 , 0x4ca2, 1 , 524 , 524 , // 523 - mk + 0x42f , 0x4e3 , 0x362 , 0x2717, 0x1f4 , 0x4ca2, 1 , 524 , 524 , // 524 - mk-mk + 0x4c , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 526 , 143 , // 525 - ml + 0x44c , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 526 , 143 , // 526 - ml-in + 0x50 , 0x4e3 , 0x362 , 0x2717, 0x5190, 0x9a , 1 , 529 , 529 , // 527 - mn + 0x7850 , 0x4e3 , 0x362 , 0x2717, 0x5190, 0x9a , 1 , 529 , 529 , // 528 - mn-cyrl + 0x450 , 0x4e3 , 0x362 , 0x2717, 0x5190, 0x9a , 1 , 529 , 529 , // 529 - mn-mn + 0x7c50 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x2d , 1 , 531 , 531 , // 530 - mn-mong + 0x850 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x2d , 1 , 531 , 531 , // 531 - mn-mong-cn + 0xc50 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x9a , 1 , 532 , 532 , // 532 - mn-mong-mn + 0x58 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 534 , 187 , // 533 - mni + 0x458 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 534 , 187 , // 534 - mni-in + 0x7c , 0x4e4 , 0x352 , 0x2710, 0x25 , 0x27 , 1 , 536 , 240 , // 535 - moh + 0x47c , 0x4e4 , 0x352 , 0x2710, 0x25 , 0x27 , 1 , 536 , 240 , // 536 - moh-ca + 0x4e , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 538 , 143 , // 537 - mr + 0x44e , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 538 , 143 , // 538 - mr-in + 0x3e , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xa7 , 1 , 541 , 541 , // 539 - ms + 0x83e , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x25 , 1 , 540 , 540 , // 540 - ms-bn + 0x43e , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xa7 , 1 , 541 , 541 , // 541 - ms-my + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xd7 , 1 , 542 , 240 , // 542 - ms-sg + 0x3a , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xa3 , 1 , 544 , 544 , // 543 - mt + 0x43a , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xa3 , 1 , 544 , 544 , // 544 - mt-mt + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 546 , 240 , // 545 - mua + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 546 , 240 , // 546 - mua-cm + 0x55 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x1b , 2 , 548 , 240 , // 547 - my + 0x455 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x1b , 2 , 548 , 240 , // 548 - my-mm + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x74 , 2 , 550 , 240 , // 549 - mzn + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x74 , 2 , 550 , 240 , // 550 - mzn-ir + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xfe , 1 , 552 , 240 , // 551 - naq + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xfe , 1 , 552 , 240 , // 552 - naq-na + 0x7c14 , 0x4e4 , 0x352 , 0x2710, 0x4f35, 0xb1 , 1 , 554 , 554 , // 553 - nb + 0x414 , 0x4e4 , 0x352 , 0x2710, 0x4f35, 0xb1 , 1 , 554 , 554 , // 554 - nb-no + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f35, 0xdc , 1 , 555 , 240 , // 555 - nb-sj + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x108 , 1 , 557 , 240 , // 556 - nd + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x108 , 1 , 557 , 240 , // 557 - nd-zw + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x5e , 1 , 559 , 240 , // 558 - nds + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x5e , 1 , 559 , 240 , // 559 - nds-de + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xb0 , 1 , 560 , 240 , // 560 - nds-nl + 0x61 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xb2 , 1 , 563 , 143 , // 561 - ne + 0x861 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 2 , 562 , 240 , // 562 - ne-in + 0x461 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xb2 , 1 , 563 , 143 , // 563 - ne-np + 0x13 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xb0 , 1 , 569 , 569 , // 564 - nl + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x12e , 1 , 565 , 240 , // 565 - nl-aw + 0x813 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x15 , 1 , 566 , 566 , // 566 - nl-be + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x9a55d42, 1 , 567 , 240 , // 567 - nl-bq + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x111 , 1 , 568 , 240 , // 568 - nl-cw + 0x413 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xb0 , 1 , 569 , 569 , // 569 - nl-nl + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xb5 , 1 , 570 , 240 , // 570 - nl-sr + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x78f7, 1 , 571 , 240 , // 571 - nl-sx + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 573 , 240 , // 572 - nmg + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 573 , 240 , // 573 - nmg-cm + 0x7814 , 0x4e4 , 0x352 , 0x2710, 0x4f35, 0xb1 , 1 , 575 , 575 , // 574 - nn + 0x814 , 0x4e4 , 0x352 , 0x2710, 0x4f35, 0xb1 , 1 , 575 , 575 , // 575 - nn-no + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 577 , 240 , // 576 - nnh + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 577 , 240 , // 577 - nnh-cm + 0x14 , 0x4e4 , 0x352 , 0x2710, 0x4f35, 0xb1 , 1 , 554 , 554 , // 578 - no + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x64 , 2 , 580 , 143 , // 579 - nqo + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x64 , 2 , 580 , 143 , // 580 - nqo-gn + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xd1 , 1 , 582 , 240 , // 581 - nr + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xd1 , 1 , 582 , 240 , // 582 - nr-za + 0x6c , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xd1 , 1 , 584 , 584 , // 583 - nso + 0x46c , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xd1 , 1 , 584 , 584 , // 584 - nso-za + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x114 , 1 , 586 , 240 , // 585 - nus + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x114 , 1 , 586 , 240 , // 586 - nus-ss + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xf0 , 1 , 588 , 240 , // 587 - nyn + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xf0 , 1 , 588 , 240 , // 588 - nyn-ug + 0x82 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x54 , 1 , 590 , 590 , // 589 - oc + 0x482 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x54 , 1 , 590 , 590 , // 590 - oc-fr + 0x72 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x49 , 1 , 592 , 240 , // 591 - om + 0x472 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x49 , 1 , 592 , 240 , // 592 - om-et + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 593 , 240 , // 593 - om-ke + 0x48 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 595 , 143 , // 594 - or + 0x448 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 595 , 143 , // 595 - or-in + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x58 , 1 , 597 , 240 , // 596 - os + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x58 , 1 , 597 , 240 , // 597 - os-ge + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xcb , 1 , 598 , 240 , // 598 - os-ru + 0x46 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 602 , 143 , // 599 - pa + 0x7c46 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0xbe , 2 , 601 , 143 , // 600 - pa-arab + 0x846 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0xbe , 2 , 601 , 143 , // 601 - pa-arab-pk + 0x446 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 602 , 143 , // 602 - pa-in + 0x79 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x993248, 1 , 604 , 145 , // 603 - pap + 0x479 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x993248, 1 , 604 , 145 , // 604 - pap-029 + 0x15 , 0x4e2 , 0x354 , 0x272d, 0x5190, 0xbf , 1 , 606 , 606 , // 605 - pl + 0x415 , 0x4e2 , 0x354 , 0x272d, 0x5190, 0xbf , 1 , 606 , 606 , // 606 - pl-pl + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x989e, 1 , 608 , 240 , // 607 - prg + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x989e, 1 , 608 , 240 , // 608 - prg-001 + 0x8c , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x3 , 2 , 610 , 143 , // 609 - prs + 0x48c , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x3 , 2 , 610 , 143 , // 610 - prs-af + 0x63 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x3 , 2 , 612 , 143 , // 611 - ps + 0x463 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x3 , 2 , 612 , 143 , // 612 - ps-af + 0x16 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x20 , 1 , 615 , 615 , // 613 - pt + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x9 , 1 , 614 , 240 , // 614 - pt-ao + 0x416 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x20 , 1 , 615 , 615 , // 615 - pt-br + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xdf , 1 , 616 , 240 , // 616 - pt-ch + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x39 , 1 , 617 , 240 , // 617 - pt-cv + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x45 , 1 , 618 , 240 , // 618 - pt-gq + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xc4 , 1 , 619 , 240 , // 619 - pt-gw + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x93 , 1 , 620 , 240 , // 620 - pt-lu + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x97 , 1 , 621 , 240 , // 621 - pt-mo + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xa8 , 1 , 622 , 240 , // 622 - pt-mz + 0x816 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xc1 , 1 , 623 , 623 , // 623 - pt-pt + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xe9 , 1 , 624 , 240 , // 624 - pt-st + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x6f60e7, 1 , 625 , 240 , // 625 - pt-tl + 0x901 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x7c , 1 , 626 , 190 , // 626 - qps-latn-x-sh + 0x501 , 0x4e2 , 0x354 , 0x272d, 0x5190, 0xf4 , 1 , 627 , 627 , // 627 - qps-ploc + 0x5fe , 0x3a4 , 0x3a4 , 0x2711, 0x4f42, 0x7a , 1 , 628 , 628 , // 628 - qps-ploca + 0x9ff , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0xcd , 0 , 629 , 143 , // 629 - qps-plocm + 0x86 , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0x63 , 1 , 632 , 632 , // 630 - quc + 0x7c86 , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0x63 , 1 , 632 , 632 , // 631 - quc-latn + 0x486 , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0x63 , 1 , 632 , 632 , // 632 - quc-latn-gt + 0x6b , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0x1a , 1 , 634 , 634 , // 633 - quz + 0x46b , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0x1a , 1 , 634 , 634 , // 634 - quz-bo + 0x86b , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0x42 , 1 , 635 , 635 , // 635 - quz-ec + 0xc6b , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0xbb , 1 , 636 , 636 , // 636 - quz-pe + 0x17 , 0x4e4 , 0x352 , 0x2710, 0x4f31, 0xdf , 1 , 638 , 638 , // 637 - rm + 0x417 , 0x4e4 , 0x352 , 0x2710, 0x4f31, 0xdf , 1 , 638 , 638 , // 638 - rm-ch + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x26 , 1 , 640 , 240 , // 639 - rn + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x26 , 1 , 640 , 240 , // 640 - rn-bi + 0x18 , 0x4e2 , 0x354 , 0x272d, 0x5190, 0xc8 , 1 , 643 , 643 , // 641 - ro + 0x818 , 0x4e2 , 0x354 , 0x2 , 0x1f4 , 0x98 , 1 , 642 , 240 , // 642 - ro-md + 0x418 , 0x4e2 , 0x354 , 0x272d, 0x5190, 0xc8 , 1 , 643 , 643 , // 643 - ro-ro + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 645 , 240 , // 644 - rof + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 645 , 240 , // 645 - rof-tz + 0x19 , 0x4e3 , 0x362 , 0x2717, 0x5190, 0xcb , 1 , 651 , 651 , // 646 - ru + 0x1000 , 0x4e3 , 0x362 , 0x2 , 0x1f4 , 0x1d , 1 , 647 , 240 , // 647 - ru-by + 0x1000 , 0x4e3 , 0x362 , 0x2 , 0x1f4 , 0x82 , 1 , 648 , 240 , // 648 - ru-kg + 0x1000 , 0x4e3 , 0x362 , 0x2 , 0x1f4 , 0x89 , 1 , 649 , 240 , // 649 - ru-kz + 0x819 , 0x4e3 , 0x362 , 0x2 , 0x1f4 , 0x98 , 1 , 650 , 240 , // 650 - ru-md + 0x419 , 0x4e3 , 0x362 , 0x2717, 0x5190, 0xcb , 1 , 651 , 651 , // 651 - ru-ru + 0x1000 , 0x4e3 , 0x362 , 0x2 , 0x1f4 , 0xf1 , 1 , 652 , 240 , // 652 - ru-ua + 0x87 , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0xcc , 1 , 654 , 654 , // 653 - rw + 0x487 , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0xcc , 1 , 654 , 654 , // 654 - rw-rw + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 656 , 240 , // 655 - rwk + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 656 , 240 , // 656 - rwk-tz + 0x4f , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 658 , 143 , // 657 - sa + 0x44f , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 658 , 143 , // 658 - sa-in + 0x85 , 0x4e3 , 0x362 , 0x2717, 0x5190, 0xcb , 1 , 660 , 660 , // 659 - sah + 0x485 , 0x4e3 , 0x362 , 0x2717, 0x5190, 0xcb , 1 , 660 , 660 , // 660 - sah-ru + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 662 , 240 , // 661 - saq + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 662 , 240 , // 662 - saq-ke + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 664 , 240 , // 663 - sbp + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 664 , 240 , // 664 - sbp-tz + 0x59 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0xbe , 2 , 667 , 143 , // 665 - sd + 0x7c59 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0xbe , 2 , 667 , 143 , // 666 - sd-arab + 0x859 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0xbe , 2 , 667 , 143 , // 667 - sd-arab-pk + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 669 , 187 , // 668 - sd-deva + 0x459 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 669 , 187 , // 669 - sd-deva-in + 0x3b , 0x4e4 , 0x352 , 0x2710, 0x4f35, 0xb1 , 1 , 672 , 672 , // 670 - se + 0xc3b , 0x4e4 , 0x352 , 0x2710, 0x4f36, 0x4d , 1 , 671 , 671 , // 671 - se-fi + 0x43b , 0x4e4 , 0x352 , 0x2710, 0x4f35, 0xb1 , 1 , 672 , 672 , // 672 - se-no + 0x83b , 0x4e4 , 0x352 , 0x2710, 0x4f36, 0xdd , 1 , 673 , 673 , // 673 - se-se + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xa8 , 1 , 675 , 240 , // 674 - seh + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xa8 , 1 , 675 , 240 , // 675 - seh-mz + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x9d , 1 , 677 , 240 , // 676 - ses + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x9d , 1 , 677 , 240 , // 677 - ses-ml + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x37 , 1 , 679 , 240 , // 678 - sg + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x37 , 1 , 679 , 240 , // 679 - sg-cf + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x9f , 1 , 684 , 240 , // 680 - shi + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x9f , 1 , 682 , 240 , // 681 - shi-latn + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x9f , 1 , 682 , 240 , // 682 - shi-latn-ma + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x9f , 1 , 684 , 240 , // 683 - shi-tfng + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x9f , 1 , 684 , 240 , // 684 - shi-tfng-ma + 0x5b , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x2a , 1 , 686 , 143 , // 685 - si + 0x45b , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x2a , 1 , 686 , 143 , // 686 - si-lk + 0x1b , 0x4e2 , 0x354 , 0x272d, 0x5190, 0x8f , 1 , 688 , 688 , // 687 - sk + 0x41b , 0x4e2 , 0x354 , 0x272d, 0x5190, 0x8f , 1 , 688 , 688 , // 688 - sk-sk + 0x24 , 0x4e2 , 0x354 , 0x272d, 0x5190, 0xd4 , 1 , 690 , 690 , // 689 - sl + 0x424 , 0x4e2 , 0x354 , 0x272d, 0x5190, 0xd4 , 1 , 690 , 690 , // 690 - sl-si + 0x783b , 0x4e4 , 0x352 , 0x2710, 0x4f36, 0xdd , 1 , 693 , 693 , // 691 - sma + 0x183b , 0x4e4 , 0x352 , 0x2710, 0x4f35, 0xb1 , 1 , 692 , 692 , // 692 - sma-no + 0x1c3b , 0x4e4 , 0x352 , 0x2710, 0x4f36, 0xdd , 1 , 693 , 693 , // 693 - sma-se + 0x7c3b , 0x4e4 , 0x352 , 0x2710, 0x4f36, 0xdd , 1 , 696 , 696 , // 694 - smj + 0x103b , 0x4e4 , 0x352 , 0x2710, 0x4f35, 0xb1 , 1 , 695 , 695 , // 695 - smj-no + 0x143b , 0x4e4 , 0x352 , 0x2710, 0x4f36, 0xdd , 1 , 696 , 696 , // 696 - smj-se + 0x703b , 0x4e4 , 0x352 , 0x2710, 0x4f36, 0x4d , 1 , 698 , 698 , // 697 - smn + 0x243b , 0x4e4 , 0x352 , 0x2710, 0x4f36, 0x4d , 1 , 698 , 698 , // 698 - smn-fi + 0x743b , 0x4e4 , 0x352 , 0x2710, 0x4f36, 0x4d , 1 , 700 , 700 , // 699 - sms + 0x203b , 0x4e4 , 0x352 , 0x2710, 0x4f36, 0x4d , 1 , 700 , 700 , // 700 - sms-fi + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x108 , 1 , 703 , 240 , // 701 - sn + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x108 , 1 , 703 , 240 , // 702 - sn-latn + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x108 , 1 , 703 , 240 , // 703 - sn-latn-zw + 0x77 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xd8 , 1 , 708 , 240 , // 704 - so + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x3e , 1 , 705 , 240 , // 705 - so-dj + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x49 , 1 , 706 , 240 , // 706 - so-et + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 707 , 240 , // 707 - so-ke + 0x477 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xd8 , 1 , 708 , 240 , // 708 - so-so + 0x1c , 0x4e2 , 0x354 , 0x272d, 0x5190, 0x6 , 1 , 710 , 710 , // 709 - sq + 0x41c , 0x4e2 , 0x354 , 0x272d, 0x5190, 0x6 , 1 , 710 , 710 , // 710 - sq-al + 0x1000 , 0x4e2 , 0x354 , 0x272d, 0x5190, 0x4ca2, 1 , 711 , 240 , // 711 - sq-mk + 0x1000 , 0x4e2 , 0x354 , 0x272d, 0x5190, 0x974941, 1 , 712 , 240 , // 712 - sq-xk + 0x7c1a , 0x4e2 , 0x354 , 0x272d, 0x1f4 , 0x10f , 1 , 724 , 724 , // 713 - sr + 0x6c1a , 0x4e3 , 0x357 , 0x2717, 0x5221, 0x10f , 1 , 718 , 718 , // 714 - sr-cyrl + 0x1c1a , 0x4e3 , 0x357 , 0x2717, 0x5221, 0x19 , 1 , 715 , 715 , // 715 - sr-cyrl-ba + 0xc1a , 0x4e3 , 0x357 , 0x2717, 0x5221, 0x10d , 1 , 716 , 716 , // 716 - sr-cyrl-cs + 0x301a , 0x4e3 , 0x357 , 0x2717, 0x5221, 0x10e , 1 , 717 , 717 , // 717 - sr-cyrl-me + 0x281a , 0x4e3 , 0x357 , 0x2717, 0x5221, 0x10f , 1 , 718 , 718 , // 718 - sr-cyrl-rs + 0x1000 , 0x4e3 , 0x357 , 0x2717, 0x5221, 0x974941, 1 , 719 , 240 , // 719 - sr-cyrl-xk + 0x701a , 0x4e2 , 0x354 , 0x272d, 0x1f4 , 0x10f , 1 , 724 , 724 , // 720 - sr-latn + 0x181a , 0x4e2 , 0x354 , 0x2762, 0x366 , 0x19 , 1 , 721 , 721 , // 721 - sr-latn-ba + 0x81a , 0x4e2 , 0x354 , 0x272d, 0x1f4 , 0x10d , 1 , 722 , 722 , // 722 - sr-latn-cs + 0x2c1a , 0x4e2 , 0x354 , 0x272d, 0x1f4 , 0x10e , 1 , 723 , 723 , // 723 - sr-latn-me + 0x241a , 0x4e2 , 0x354 , 0x272d, 0x1f4 , 0x10f , 1 , 724 , 724 , // 724 - sr-latn-rs + 0x1000 , 0x4e2 , 0x354 , 0x272d, 0x1f4 , 0x974941, 1 , 725 , 240 , // 725 - sr-latn-xk + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xd1 , 1 , 728 , 240 , // 726 - ss + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x104 , 1 , 727 , 240 , // 727 - ss-sz + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xd1 , 1 , 728 , 240 , // 728 - ss-za + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x47 , 1 , 730 , 240 , // 729 - ssy + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x47 , 1 , 730 , 240 , // 730 - ssy-er + 0x30 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xd1 , 1 , 733 , 240 , // 731 - st + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x92 , 1 , 732 , 240 , // 732 - st-ls + 0x430 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xd1 , 1 , 733 , 240 , // 733 - st-za + 0x1d , 0x4e4 , 0x352 , 0x2710, 0x4f36, 0xdd , 1 , 737 , 737 , // 734 - sv + 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f36, 0x9906f5, 1 , 735 , 240 , // 735 - sv-ax + 0x81d , 0x4e4 , 0x352 , 0x2710, 0x4f36, 0x4d , 1 , 736 , 736 , // 736 - sv-fi + 0x41d , 0x4e4 , 0x352 , 0x2710, 0x4f36, 0xdd , 1 , 737 , 737 , // 737 - sv-se + 0x41 , 0x4e4 , 0x1b5 , 0x2710, 0x1f4 , 0x81 , 1 , 740 , 740 , // 738 - sw + 0x1000 , 0x4e4 , 0x1b5 , 0x2710, 0x1f4 , 0x2c , 1 , 739 , 740 , // 739 - sw-cd + 0x441 , 0x4e4 , 0x1b5 , 0x2710, 0x1f4 , 0x81 , 1 , 740 , 740 , // 740 - sw-ke + 0x1000 , 0x4e4 , 0x1b5 , 0x2710, 0x1f4 , 0xef , 1 , 741 , 240 , // 741 - sw-tz + 0x1000 , 0x4e4 , 0x1b5 , 0x2710, 0x1f4 , 0xf0 , 1 , 742 , 240 , // 742 - sw-ug + 0x1000 , 0x0 , 0x1 , 0x0 , 0x1f4 , 0x2c , 1 , 744 , 240 , // 743 - swc + 0x1000 , 0x0 , 0x1 , 0x0 , 0x1f4 , 0x2c , 1 , 744 , 240 , // 744 - swc-cd + 0x5a , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xde , 1 , 746 , 143 , // 745 - syr + 0x45a , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xde , 1 , 746 , 143 , // 746 - syr-sy + 0x49 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 748 , 143 , // 747 - ta + 0x449 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 748 , 143 , // 748 - ta-in + 0x849 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x2a , 1 , 749 , 143 , // 749 - ta-lk + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xa7 , 1 , 750 , 240 , // 750 - ta-my + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xd7 , 1 , 751 , 240 , // 751 - ta-sg + 0x4a , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 753 , 143 , // 752 - te + 0x44a , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 753 , 143 , // 753 - te-in + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xf0 , 1 , 756 , 240 , // 754 - teo + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 755 , 240 , // 755 - teo-ke + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xf0 , 1 , 756 , 240 , // 756 - teo-ug + 0x28 , 0x4e3 , 0x362 , 0x2717, 0x5190, 0xe4 , 1 , 759 , 759 , // 757 - tg + 0x7c28 , 0x4e3 , 0x362 , 0x2717, 0x5190, 0xe4 , 1 , 759 , 759 , // 758 - tg-cyrl + 0x428 , 0x4e3 , 0x362 , 0x2717, 0x5190, 0xe4 , 1 , 759 , 759 , // 759 - tg-cyrl-tj + 0x1e , 0x36a , 0x36a , 0x2725, 0x5166, 0xe3 , 1 , 761 , 143 , // 760 - th + 0x41e , 0x36a , 0x36a , 0x2725, 0x5166, 0xe3 , 1 , 761 , 143 , // 761 - th-th + 0x73 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x47 , 1 , 763 , 143 , // 762 - ti + 0x873 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x47 , 1 , 763 , 143 , // 763 - ti-er + 0x473 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x49 , 1 , 764 , 143 , // 764 - ti-et + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x47 , 1 , 766 , 240 , // 765 - tig + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x47 , 1 , 766 , 240 , // 766 - tig-er + 0x42 , 0x4e2 , 0x354 , 0x272d, 0x5190, 0xee , 1 , 768 , 768 , // 767 - tk + 0x442 , 0x4e2 , 0x354 , 0x272d, 0x5190, 0xee , 1 , 768 , 768 , // 768 - tk-tm + 0x32 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xd1 , 1 , 771 , 771 , // 769 - tn + 0x832 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x13 , 1 , 770 , 770 , // 770 - tn-bw + 0x432 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xd1 , 1 , 771 , 771 , // 771 - tn-za + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xe7 , 1 , 773 , 240 , // 772 - to + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xe7 , 1 , 773 , 240 , // 773 - to-to + 0x1f , 0x4e6 , 0x359 , 0x2761, 0x51a9, 0xeb , 1 , 776 , 776 , // 774 - tr + 0x1000 , 0x4e6 , 0x359 , 0x2761, 0x51a9, 0x3b , 1 , 775 , 240 , // 775 - tr-cy + 0x41f , 0x4e6 , 0x359 , 0x2761, 0x51a9, 0xeb , 1 , 776 , 776 , // 776 - tr-tr + 0x31 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xd1 , 1 , 778 , 240 , // 777 - ts + 0x431 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xd1 , 1 , 778 , 240 , // 778 - ts-za + 0x44 , 0x4e3 , 0x362 , 0x2717, 0x5190, 0xcb , 1 , 780 , 780 , // 779 - tt + 0x444 , 0x4e3 , 0x362 , 0x2717, 0x5190, 0xcb , 1 , 780 , 780 , // 780 - tt-ru + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xad , 1 , 782 , 240 , // 781 - twq + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xad , 1 , 782 , 240 , // 782 - twq-ne + 0x5f , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x4 , 1 , 787 , 787 , // 783 - tzm + 0x1000 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x9f , 1 , 785 , 240 , // 784 - tzm-arab + 0x45f , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x9f , 1 , 785 , 240 , // 785 - tzm-arab-ma + 0x7c5f , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x4 , 1 , 787 , 787 , // 786 - tzm-latn + 0x85f , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x4 , 1 , 787 , 787 , // 787 - tzm-latn-dz + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x9f , 1 , 788 , 240 , // 788 - tzm-latn-ma + 0x785f , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x9f , 1 , 790 , 316 , // 789 - tzm-tfng + 0x105f , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x9f , 1 , 790 , 316 , // 790 - tzm-tfng-ma + 0x80 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x2d , 1 , 792 , 143 , // 791 - ug + 0x480 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x2d , 1 , 792 , 143 , // 792 - ug-cn + 0x22 , 0x4e3 , 0x362 , 0x2721, 0x1f4 , 0xf1 , 1 , 794 , 794 , // 793 - uk + 0x422 , 0x4e3 , 0x362 , 0x2721, 0x1f4 , 0xf1 , 1 , 794 , 794 , // 794 - uk-ua + 0x20 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0xbe , 1 , 797 , 143 , // 795 - ur + 0x820 , 0x4e8 , 0x2d0 , 0x2 , 0x1f4 , 0x71 , 2 , 796 , 240 , // 796 - ur-in + 0x420 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0xbe , 1 , 797 , 143 , // 797 - ur-pk + 0x43 , 0x4e6 , 0x359 , 0x272d, 0x1f4 , 0xf7 , 1 , 804 , 804 , // 798 - uz + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x3 , 2 , 800 , 240 , // 799 - uz-arab + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x3 , 2 , 800 , 240 , // 800 - uz-arab-af + 0x7843 , 0x4e3 , 0x362 , 0x2717, 0x5190, 0xf7 , 1 , 802 , 802 , // 801 - uz-cyrl + 0x843 , 0x4e3 , 0x362 , 0x2717, 0x5190, 0xf7 , 1 , 802 , 802 , // 802 - uz-cyrl-uz + 0x7c43 , 0x4e6 , 0x359 , 0x272d, 0x1f4 , 0xf7 , 1 , 804 , 804 , // 803 - uz-latn + 0x443 , 0x4e6 , 0x359 , 0x272d, 0x1f4 , 0xf7 , 1 , 804 , 804 , // 804 - uz-latn-uz + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x8e , 1 , 809 , 240 , // 805 - vai + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x8e , 1 , 807 , 240 , // 806 - vai-latn + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x8e , 1 , 807 , 240 , // 807 - vai-latn-lr + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x8e , 1 , 809 , 240 , // 808 - vai-vaii + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x8e , 1 , 809 , 240 , // 809 - vai-vaii-lr + 0x33 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xd1 , 1 , 811 , 240 , // 810 - ve + 0x433 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xd1 , 1 , 811 , 240 , // 811 - ve-za + 0x2a , 0x4ea , 0x4ea , 0x2710, 0x1f4 , 0xfb , 1 , 813 , 143 , // 812 - vi + 0x42a , 0x4ea , 0x4ea , 0x2710, 0x1f4 , 0xfb , 1 , 813 , 143 , // 813 - vi-vn + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x989e, 1 , 815 , 240 , // 814 - vo + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x989e, 1 , 815 , 240 , // 815 - vo-001 + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 817 , 240 , // 816 - vun + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 817 , 240 , // 817 - vun-tz + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xdf , 1 , 819 , 240 , // 818 - wae + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xdf , 1 , 819 , 240 , // 819 - wae-ch + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x49 , 1 , 821 , 240 , // 820 - wal + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x49 , 1 , 821 , 240 , // 821 - wal-et + 0x88 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xd2 , 1 , 823 , 823 , // 822 - wo + 0x488 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xd2 , 1 , 823 , 823 , // 823 - wo-sn + 0x1007f, 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0xf4 , 1 , -1 , -1 , // 824 - x-iv_mathan + 0x34 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xd1 , 1 , 826 , 826 , // 825 - xh + 0x434 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xd1 , 1 , 826 , 826 , // 826 - xh-za + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xf0 , 1 , 828 , 240 , // 827 - xog + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xf0 , 1 , 828 , 240 , // 828 - xog-ug + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 830 , 240 , // 829 - yav + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 830 , 240 , // 830 - yav-cm + 0x3d , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x989e, 1 , 832 , 240 , // 831 - yi + 0x43d , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x989e, 1 , 832 , 240 , // 832 - yi-001 + 0x6a , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0xaf , 1 , 835 , 835 , // 833 - yo + 0x1000 , 0x4e4 , 0x1b5 , 0x2710, 0x1f4 , 0x1c , 1 , 834 , 240 , // 834 - yo-bj + 0x46a , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0xaf , 1 , 835 , 835 , // 835 - yo-ng + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x68 , 1 , 837 , 240 , // 836 - yue + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x68 , 1 , 837 , 240 , // 837 - yue-hk + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x9f , 1 , 840 , 316 , // 838 - zgh + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x9f , 1 , 840 , 316 , // 839 - zgh-tfng + 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x9f , 1 , 840 , 316 , // 840 - zgh-tfng-ma + 0x7804 , 0x3a8 , 0x3a8 , 0x2718, 0x1f4 , 0x2d , 1 , 844 , 844 , // 841 - zh + 0x4 , 0x3a8 , 0x3a8 , 0x0 , 0x1f4 , 0x2d , 1 , 844 , 844 , // 842 - zh-chs + 0x7c04 , 0x3b6 , 0x3b6 , 0x0 , 0x1f4 , 0x68 , 1 , 851 , 851 , // 843 - zh-cht + 0x804 , 0x3a8 , 0x3a8 , 0x2718, 0x1f4 , 0x2d , 1 , 844 , 844 , // 844 - zh-cn + 0x50804, 0x3a8 , 0x3a8 , 0x2718, 0x1f4 , 0x2d , 1 , 844 , 844 , // 845 - zh-cn_phoneb + 0x20804, 0x3a8 , 0x3a8 , 0x2718, 0x1f4 , 0x2d , 1 , 844 , 844 , // 846 - zh-cn_stroke + 0x4 , 0x3a8 , 0x3a8 , 0x2718, 0x1f4 , 0x2d , 1 , 844 , 844 , // 847 - zh-hans + 0x1000 , 0x3a8 , 0x3a8 , 0x2718, 0x1f4 , 0x68 , 1 , 848 , 240 , // 848 - zh-hans-hk + 0x1000 , 0x3a8 , 0x3a8 , 0x2718, 0x1f4 , 0x97 , 1 , 849 , 240 , // 849 - zh-hans-mo + 0x7c04 , 0x3b6 , 0x3b6 , 0x2712, 0x1f4 , 0x68 , 1 , 851 , 851 , // 850 - zh-hant + 0xc04 , 0x3b6 , 0x3b6 , 0x2712, 0x1f4 , 0x68 , 1 , 851 , 851 , // 851 - zh-hk + 0x40c04, 0x3b6 , 0x3b6 , 0x2712, 0x1f4 , 0x68 , 1 , 851 , 851 , // 852 - zh-hk_radstr + 0x1404 , 0x3b6 , 0x3b6 , 0x2712, 0x1f4 , 0x97 , 1 , 853 , 853 , // 853 - zh-mo + 0x41404, 0x3b6 , 0x3b6 , 0x2712, 0x1f4 , 0x97 , 1 , 853 , 853 , // 854 - zh-mo_radstr + 0x21404, 0x3b6 , 0x3b6 , 0x2712, 0x1f4 , 0x97 , 1 , 853 , 853 , // 855 - zh-mo_stroke + 0x1004 , 0x3a8 , 0x3a8 , 0x2718, 0x1f4 , 0xd7 , 1 , 856 , 856 , // 856 - zh-sg + 0x51004, 0x3a8 , 0x3a8 , 0x2718, 0x1f4 , 0xd7 , 1 , 856 , 856 , // 857 - zh-sg_phoneb + 0x21004, 0x3a8 , 0x3a8 , 0x2718, 0x1f4 , 0xd7 , 1 , 856 , 856 , // 858 - zh-sg_stroke + 0x404 , 0x3b6 , 0x3b6 , 0x2712, 0x1f4 , 0xed , 1 , 859 , 859 , // 859 - zh-tw + 0x30404, 0x3b6 , 0x3b6 , 0x2712, 0x1f4 , 0xed , 1 , 859 , 859 , // 860 - zh-tw_pronun + 0x40404, 0x3b6 , 0x3b6 , 0x2712, 0x1f4 , 0xed , 1 , 859 , 859 , // 861 - zh-tw_radstr + 0x35 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xd1 , 1 , 863 , 863 , // 862 - zu + 0x435 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xd1 , 1 , 863 , 863 , // 863 - zu-za + }; + + // s_lcids list all supported lcids. used to binary search and we use the index of the matched lcid to + // get the index in s_localeNamesIndices using s_lcidToCultureNameIndices + private static readonly int[] s_lcids = new int[] + { + // Lcid , index - index in c_localeNames + 0x1 , // 0 - 52 + 0x2 , // 1 - 301 + 0x3 , // 2 - 421 + 0x4 , // 3 - 4139 + 0x5 , // 4 - 502 + 0x6 , // 5 - 523 + 0x7 , // 6 - 544 + 0x8 , // 7 - 664 + 0x9 , // 8 - 676 + 0xa , // 9 - 1214 + 0xb , // 10 - 1423 + 0xc , // 11 - 1451 + 0xd , // 12 - 1825 + 0xe , // 13 - 1860 + 0xf , // 14 - 1929 + 0x10 , // 15 - 1936 + 0x11 , // 16 - 1989 + 0x12 , // 17 - 2179 + 0x13 , // 18 - 2685 + 0x14 , // 19 - 2747 + 0x15 , // 20 - 2864 + 0x16 , // 21 - 2897 + 0x17 , // 22 - 3041 + 0x18 , // 23 - 3055 + 0x19 , // 24 - 3076 + 0x1a , // 25 - 1839 + 0x1b , // 26 - 3284 + 0x1c , // 27 - 3387 + 0x1d , // 28 - 3553 + 0x1e , // 29 - 3673 + 0x1f , // 30 - 3727 + 0x20 , // 31 - 3847 + 0x21 , // 32 - 1908 + 0x22 , // 33 - 3840 + 0x23 , // 34 - 276 + 0x24 , // 35 - 3291 + 0x25 , // 36 - 1354 + 0x26 , // 37 - 2429 + 0x27 , // 38 - 2397 + 0x28 , // 39 - 3654 + 0x29 , // 40 - 1377 + 0x2a , // 41 - 3960 + 0x2b , // 42 - 1879 + 0x2c , // 43 - 224 + 0x2d , // 44 - 1361 + 0x2e , // 45 - 1851 + 0x2f , // 46 - 2501 + 0x30 , // 47 - 3541 + 0x31 , // 48 - 3739 + 0x32 , // 49 - 3708 + 0x33 , // 50 - 3953 + 0x34 , // 51 - 4020 + 0x35 , // 52 - 4277 + 0x36 , // 53 - 17 + 0x37 , // 54 - 2062 + 0x38 , // 55 - 1439 + 0x39 , // 56 - 1832 + 0x3a , // 57 - 2598 + 0x3b , // 58 - 3194 + 0x3c , // 59 - 1705 + 0x3d , // 60 - 4045 + 0x3e , // 61 - 2581 + 0x3f , // 62 - 2133 + 0x40 , // 63 - 2306 + 0x41 , // 64 - 3570 + 0x42 , // 65 - 3701 + 0x43 , // 66 - 3859 + 0x44 , // 67 - 3746 + 0x45 , // 68 - 336 + 0x46 , // 69 - 2830 + 0x47 , // 70 - 1754 + 0x48 , // 71 - 2811 + 0x49 , // 72 - 3610 + 0x4a , // 73 - 3632 + 0x4b , // 74 - 2172 + 0x4c , // 75 - 2508 + 0x4d , // 76 - 199 + 0x4e , // 77 - 2574 + 0x4f , // 78 - 3124 + 0x50 , // 79 - 2515 + 0x51 , // 80 - 348 + 0x52 , // 81 - 516 + 0x53 , // 82 - 2165 + 0x54 , // 83 - 2375 + 0x55 , // 84 - 2614 + 0x56 , // 85 - 1719 + 0x57 , // 86 - 2191 + 0x58 , // 87 - 2556 + 0x59 , // 88 - 3158 + 0x5a , // 89 - 3601 + 0x5b , // 90 - 3277 + 0x5c , // 91 - 473 + 0x5d , // 92 - 1953 + 0x5e , // 93 - 45 + 0x5f , // 94 - 3762 + 0x60 , // 95 - 2207 + 0x61 , // 96 - 2673 + 0x62 , // 97 - 1698 + 0x63 , // 98 - 2890 + 0x64 , // 99 - 1430 + 0x65 , // 100 - 620 + 0x66 , // 101 - 308 + 0x67 , // 102 - 1384 + 0x68 , // 103 - 1777 + 0x69 , // 104 - 1899 + 0x6a , // 105 - 4053 + 0x6b , // 106 - 3020 + 0x6c , // 107 - 2765 + 0x6d , // 108 - 260 + 0x6e , // 109 - 2330 + 0x6f , // 110 - 2149 + 0x70 , // 111 - 1915 + 0x71 , // 112 - 2200 + 0x72 , // 113 - 2799 + 0x73 , // 114 - 3680 + 0x74 , // 115 - 1726 + 0x75 , // 116 - 1816 + 0x76 , // 117 - 2313 + 0x77 , // 118 - 3365 + 0x78 , // 119 - 1922 + 0x79 , // 120 - 2854 + 0x7a , // 121 - 190 + 0x7c , // 122 - 2565 + 0x7e , // 123 - 360 + 0x80 , // 124 - 3833 + 0x81 , // 125 - 2494 + 0x82 , // 126 - 2792 + 0x83 , // 127 - 495 + 0x84 , // 128 - 1733 + 0x85 , // 129 - 3131 + 0x86 , // 130 - 2998 + 0x87 , // 131 - 3108 + 0x88 , // 132 - 4002 + 0x8c , // 133 - 2881 + 0x91 , // 134 - 1712 + 0x92 , // 135 - 2270 + 0x401 , // 136 - 150 + 0x402 , // 137 - 303 + 0x403 , // 138 - 428 + 0x404 , // 139 - 4248 + 0x405 , // 140 - 504 + 0x406 , // 141 - 525 + 0x407 , // 142 - 561 + 0x408 , // 143 - 671 + 0x409 , // 144 - 1161 + 0x40a , // 145 - 1272 + 0x40b , // 146 - 1425 + 0x40c , // 147 - 1529 + 0x40d , // 148 - 1827 + 0x40e , // 149 - 1862 + 0x40f , // 150 - 1931 + 0x410 , // 151 - 1943 + 0x411 , // 152 - 1991 + 0x412 , // 153 - 2186 + 0x413 , // 154 - 2707 + 0x414 , // 155 - 2641 + 0x415 , // 156 - 2866 + 0x416 , // 157 - 2904 + 0x417 , // 158 - 3043 + 0x418 , // 159 - 3062 + 0x419 , // 160 - 3098 + 0x41a , // 161 - 1846 + 0x41b , // 162 - 3286 + 0x41c , // 163 - 3389 + 0x41d , // 164 - 3565 + 0x41e , // 165 - 3675 + 0x41f , // 166 - 3734 + 0x420 , // 167 - 3854 + 0x421 , // 168 - 1910 + 0x422 , // 169 - 3842 + 0x423 , // 170 - 278 + 0x424 , // 171 - 3293 + 0x425 , // 172 - 1356 + 0x426 , // 173 - 2431 + 0x427 , // 174 - 2399 + 0x428 , // 175 - 3663 + 0x429 , // 176 - 1379 + 0x42a , // 177 - 3962 + 0x42b , // 178 - 1881 + 0x42c , // 179 - 250 + 0x42d , // 180 - 1363 + 0x42e , // 181 - 1854 + 0x42f , // 182 - 2503 + 0x430 , // 183 - 3548 + 0x431 , // 184 - 3741 + 0x432 , // 185 - 3715 + 0x433 , // 186 - 3955 + 0x434 , // 187 - 4022 + 0x435 , // 188 - 4279 + 0x436 , // 189 - 24 + 0x437 , // 190 - 2064 + 0x438 , // 191 - 1446 + 0x439 , // 192 - 1834 + 0x43a , // 193 - 2600 + 0x43b , // 194 - 3201 + 0x43d , // 195 - 4047 + 0x43e , // 196 - 2588 + 0x43f , // 197 - 2135 + 0x440 , // 198 - 2308 + 0x441 , // 199 - 3577 + 0x442 , // 200 - 3703 + 0x443 , // 201 - 3902 + 0x444 , // 202 - 3748 + 0x445 , // 203 - 343 + 0x446 , // 204 - 2849 + 0x447 , // 205 - 1756 + 0x448 , // 206 - 2813 + 0x449 , // 207 - 3612 + 0x44a , // 208 - 3634 + 0x44b , // 209 - 2174 + 0x44c , // 210 - 2510 + 0x44d , // 211 - 201 + 0x44e , // 212 - 2576 + 0x44f , // 213 - 3126 + 0x450 , // 214 - 2524 + 0x451 , // 215 - 350 + 0x452 , // 216 - 518 + 0x453 , // 217 - 2167 + 0x454 , // 218 - 2377 + 0x455 , // 219 - 2616 + 0x456 , // 220 - 1721 + 0x457 , // 221 - 2194 + 0x458 , // 222 - 2559 + 0x459 , // 223 - 3184 + 0x45a , // 224 - 3604 + 0x45b , // 225 - 3279 + 0x45c , // 226 - 484 + 0x45d , // 227 - 1962 + 0x45e , // 228 - 47 + 0x45f , // 229 - 3773 + 0x460 , // 230 - 2209 + 0x461 , // 231 - 2680 + 0x462 , // 232 - 1700 + 0x463 , // 233 - 2892 + 0x464 , // 234 - 1433 + 0x465 , // 235 - 622 + 0x466 , // 236 - 311 + 0x467 , // 237 - 1418 + 0x468 , // 238 - 1806 + 0x469 , // 239 - 1902 + 0x46a , // 240 - 4060 + 0x46b , // 241 - 3023 + 0x46c , // 242 - 2768 + 0x46d , // 243 - 262 + 0x46e , // 244 - 2332 + 0x46f , // 245 - 2151 + 0x470 , // 246 - 1917 + 0x471 , // 247 - 2202 + 0x472 , // 248 - 2801 + 0x473 , // 249 - 3687 + 0x474 , // 250 - 1728 + 0x475 , // 251 - 1819 + 0x476 , // 252 - 2315 + 0x477 , // 253 - 3382 + 0x478 , // 254 - 1924 + 0x479 , // 255 - 2857 + 0x47a , // 256 - 193 + 0x47c , // 257 - 2568 + 0x47e , // 258 - 362 + 0x480 , // 259 - 3835 + 0x481 , // 260 - 2496 + 0x482 , // 261 - 2794 + 0x483 , // 262 - 497 + 0x484 , // 263 - 1742 + 0x485 , // 264 - 3134 + 0x486 , // 265 - 3009 + 0x487 , // 266 - 3110 + 0x488 , // 267 - 4004 + 0x48c , // 268 - 2884 + 0x491 , // 269 - 1714 + 0x492 , // 270 - 2279 + 0x501 , // 271 - 2972 + 0x5fe , // 272 - 2980 + 0x801 , // 273 - 95 + 0x803 , // 274 - 433 + 0x804 , // 275 - 4110 + 0x807 , // 276 - 556 + 0x809 , // 277 - 831 + 0x80a , // 278 - 1299 + 0x80c , // 279 - 1459 + 0x810 , // 280 - 1938 + 0x813 , // 281 - 2692 + 0x814 , // 282 - 2733 + 0x816 , // 283 - 2944 + 0x818 , // 284 - 3057 + 0x819 , // 285 - 3093 + 0x81a , // 286 - 3480 + 0x81d , // 287 - 3560 + 0x820 , // 288 - 3849 + 0x82c , // 289 - 233 + 0x82e , // 290 - 605 + 0x832 , // 291 - 3710 + 0x83b , // 292 - 3206 + 0x83c , // 293 - 1707 + 0x83e , // 294 - 2583 + 0x843 , // 295 - 3885 + 0x845 , // 296 - 338 + 0x846 , // 297 - 2839 + 0x849 , // 298 - 3617 + 0x850 , // 299 - 2536 + 0x859 , // 300 - 3167 + 0x85d , // 301 - 1979 + 0x85f , // 302 - 3792 + 0x860 , // 303 - 2233 + 0x861 , // 304 - 2675 + 0x867 , // 305 - 1403 + 0x86b , // 306 - 3029 + 0x873 , // 307 - 3682 + 0x901 , // 308 - 2959 + 0x9ff , // 309 - 2989 + 0xc01 , // 310 - 80 + 0xc04 , // 311 - 4173 + 0xc07 , // 312 - 546 + 0xc09 , // 313 - 716 + 0xc0a , // 314 - 1267 + 0xc0c , // 315 - 1484 + 0xc1a , // 316 - 3423 + 0xc3b , // 317 - 3196 + 0xc50 , // 318 - 2546 + 0xc51 , // 319 - 638 + 0xc6b , // 320 - 3035 + 0x1001 , // 321 - 120 + 0x1004 , // 322 - 4219 + 0x1007 , // 323 - 588 + 0x1009 , // 324 - 756 + 0x100a , // 325 - 1289 + 0x100c , // 326 - 1504 + 0x101a , // 327 - 1841 + 0x103b , // 328 - 3316 + 0x105f , // 329 - 3822 + 0x1401 , // 330 - 75 + 0x1404 , // 331 - 4190 + 0x1407 , // 332 - 583 + 0x1409 , // 333 - 1026 + 0x140a , // 334 - 1247 + 0x140c , // 335 - 1569 + 0x141a , // 336 - 402 + 0x143b , // 337 - 3322 + 0x1801 , // 338 - 125 + 0x1809 , // 339 - 881 + 0x180a , // 340 - 1309 + 0x180c , // 341 - 1579 + 0x181a , // 342 - 3470 + 0x183b , // 343 - 3301 + 0x1c01 , // 344 - 180 + 0x1c09 , // 345 - 1191 + 0x1c0a , // 346 - 1257 + 0x1c0c , // 347 - 1453 + 0x1c1a , // 348 - 3413 + 0x1c3b , // 349 - 3307 + 0x2001 , // 350 - 135 + 0x2009 , // 351 - 911 + 0x200a , // 352 - 1349 + 0x200c , // 353 - 1634 + 0x201a , // 354 - 385 + 0x203b , // 355 - 3340 + 0x2401 , // 356 - 185 + 0x2409 , // 357 - 684 + 0x240a , // 358 - 1242 + 0x240c , // 359 - 1489 + 0x241a , // 360 - 3500 + 0x243b , // 361 - 3331 + 0x2801 , // 362 - 170 + 0x2809 , // 363 - 751 + 0x280a , // 364 - 1314 + 0x280c , // 365 - 1649 + 0x281a , // 366 - 3443 + 0x2c01 , // 367 - 100 + 0x2c09 , // 368 - 1136 + 0x2c0a , // 369 - 1222 + 0x2c0c , // 370 - 1514 + 0x2c1a , // 371 - 3490 + 0x3001 , // 372 - 115 + 0x3009 , // 373 - 1201 + 0x300a , // 374 - 1262 + 0x300c , // 375 - 1509 + 0x301a , // 376 - 3433 + 0x3401 , // 377 - 110 + 0x3409 , // 378 - 1036 + 0x340a , // 379 - 1237 + 0x340c , // 380 - 1594 + 0x3801 , // 381 - 60 + 0x3809 , // 382 - 876 + 0x380a , // 383 - 1344 + 0x380c , // 384 - 1574 + 0x3c01 , // 385 - 65 + 0x3c09 , // 386 - 871 + 0x3c0a , // 387 - 1329 + 0x3c0c , // 388 - 1559 + 0x4001 , // 389 - 145 + 0x4009 , // 390 - 896 + 0x400a , // 391 - 1227 + 0x4409 , // 392 - 991 + 0x440a , // 393 - 1334 + 0x4809 , // 394 - 1086 + 0x480a , // 395 - 1294 + 0x4c0a , // 396 - 1304 + 0x500a , // 397 - 1324 + 0x540a , // 398 - 1339 + 0x580a , // 399 - 1216 + 0x5c0a , // 400 - 1252 + 0x641a , // 401 - 378 + 0x681a , // 402 - 395 + 0x6c1a , // 403 - 3406 + 0x701a , // 404 - 3463 + 0x703b , // 405 - 3328 + 0x742c , // 406 - 226 + 0x743b , // 407 - 3337 + 0x7804 , // 408 - 4096 + 0x7814 , // 409 - 2731 + 0x781a , // 410 - 376 + 0x782c , // 411 - 243 + 0x783b , // 412 - 3298 + 0x7843 , // 413 - 3878 + 0x7850 , // 414 - 2517 + 0x785d , // 415 - 1955 + 0x785f , // 416 - 3814 + 0x7c04 , // 417 - 4166 + 0x7c14 , // 418 - 2639 + 0x7c1a , // 419 - 3404 + 0x7c28 , // 420 - 3656 + 0x7c2e , // 421 - 602 + 0x7c3b , // 422 - 3313 + 0x7c43 , // 423 - 3895 + 0x7c46 , // 424 - 2832 + 0x7c50 , // 425 - 2529 + 0x7c59 , // 426 - 3160 + 0x7c5c , // 427 - 476 + 0x7c5d , // 428 - 1972 + 0x7c5f , // 429 - 3784 + 0x7c67 , // 430 - 1396 + 0x7c68 , // 431 - 1779 + 0x7c86 , // 432 - 3001 + 0x7c92 , // 433 - 2272 + 0x1007f, // 434 - 4009 + 0x10407, // 435 - 566 + 0x1040e, // 436 - 1867 + 0x10437, // 437 - 2069 + 0x20804, // 438 - 4127 + 0x21004, // 439 - 4236 + 0x21404, // 440 - 4207 + 0x30404, // 441 - 4253 + 0x40404, // 442 - 4265 + 0x40411, // 443 - 1996 + 0x40c04, // 444 - 4178 + 0x41404, // 445 - 4195 + 0x50804, // 446 - 4115 + 0x51004 // 447 - 4224 + }; + // each element in s_lcidToCultureNameIndices is index to s_localeNamesIndices + private static readonly int[] s_lcidToCultureNameIndices = new int[] + { + // Index to s_localeNamesIndices, index to this array - lcid - index to the c_localeNames + 13 , // 0 - 1 - 52 + 64 , // 1 - 2 - 301 + 88 , // 2 - 3 - 421 + 847 , // 3 - 4 - 4139 + 103 , // 4 - 5 - 502 + 109 , // 5 - 6 - 523 + 114 , // 6 - 7 - 544 + 140 , // 7 - 8 - 664 + 143 , // 8 - 9 - 676 + 251 , // 9 - a - 1214 + 293 , // 10 - b - 1423 + 300 , // 11 - c - 1451 + 377 , // 12 - d - 1825 + 386 , // 13 - e - 1860 + 402 , // 14 - f - 1929 + 404 , // 15 - 10 - 1936 + 413 , // 16 - 11 - 1989 + 452 , // 17 - 12 - 2179 + 564 , // 18 - 13 - 2685 + 578 , // 19 - 14 - 2747 + 605 , // 20 - 15 - 2864 + 613 , // 21 - 16 - 2897 + 637 , // 22 - 17 - 3041 + 641 , // 23 - 18 - 3055 + 646 , // 24 - 19 - 3076 + 381 , // 25 - 1a - 1839 + 687 , // 26 - 1b - 3284 + 709 , // 27 - 1c - 3387 + 734 , // 28 - 1d - 3553 + 760 , // 29 - 1e - 3673 + 774 , // 30 - 1f - 3727 + 795 , // 31 - 20 - 3847 + 396 , // 32 - 21 - 1908 + 793 , // 33 - 22 - 3840 + 58 , // 34 - 23 - 276 + 689 , // 35 - 24 - 3291 + 278 , // 36 - 25 - 1354 + 506 , // 37 - 26 - 2429 + 498 , // 38 - 27 - 2397 + 757 , // 39 - 28 - 3654 + 284 , // 40 - 29 - 1377 + 812 , // 41 - 2a - 3960 + 389 , // 42 - 2b - 1879 + 49 , // 43 - 2c - 224 + 280 , // 44 - 2d - 1361 + 384 , // 45 - 2e - 1851 + 523 , // 46 - 2f - 2501 + 731 , // 47 - 30 - 3541 + 777 , // 48 - 31 - 3739 + 769 , // 49 - 32 - 3708 + 810 , // 50 - 33 - 3953 + 825 , // 51 - 34 - 4020 + 862 , // 52 - 35 - 4277 + 4 , // 53 - 36 - 17 + 425 , // 54 - 37 - 2062 + 297 , // 55 - 38 - 1439 + 379 , // 56 - 39 - 1832 + 543 , // 57 - 3a - 2598 + 670 , // 58 - 3b - 3194 + 352 , // 59 - 3c - 1705 + 831 , // 60 - 3d - 4045 + 539 , // 61 - 3e - 2581 + 440 , // 62 - 3f - 2133 + 476 , // 63 - 40 - 2306 + 738 , // 64 - 41 - 3570 + 767 , // 65 - 42 - 3701 + 798 , // 66 - 43 - 3859 + 779 , // 67 - 44 - 3746 + 71 , // 68 - 45 - 336 + 599 , // 69 - 46 - 2830 + 364 , // 70 - 47 - 1754 + 594 , // 71 - 48 - 2811 + 747 , // 72 - 49 - 3610 + 752 , // 73 - 4a - 3632 + 450 , // 74 - 4b - 2172 + 525 , // 75 - 4c - 2508 + 43 , // 76 - 4d - 199 + 537 , // 77 - 4e - 2574 + 657 , // 78 - 4f - 3124 + 527 , // 79 - 50 - 2515 + 74 , // 80 - 51 - 348 + 107 , // 81 - 52 - 516 + 448 , // 82 - 53 - 2165 + 493 , // 83 - 54 - 2375 + 547 , // 84 - 55 - 2614 + 356 , // 85 - 56 - 1719 + 455 , // 86 - 57 - 2191 + 533 , // 87 - 58 - 2556 + 665 , // 88 - 59 - 3158 + 745 , // 89 - 5a - 3601 + 685 , // 90 - 5b - 3277 + 98 , // 91 - 5c - 473 + 408 , // 92 - 5d - 1953 + 11 , // 93 - 5e - 45 + 783 , // 94 - 5f - 3762 + 459 , // 95 - 60 - 2207 + 561 , // 96 - 61 - 2673 + 350 , // 97 - 62 - 1698 + 611 , // 98 - 63 - 2890 + 295 , // 99 - 64 - 1430 + 129 , // 100 - 65 - 620 + 66 , // 101 - 66 - 308 + 286 , // 102 - 67 - 1384 + 370 , // 103 - 68 - 1777 + 394 , // 104 - 69 - 1899 + 833 , // 105 - 6a - 4053 + 633 , // 106 - 6b - 3020 + 583 , // 107 - 6c - 2765 + 54 , // 108 - 6d - 260 + 482 , // 109 - 6e - 2330 + 444 , // 110 - 6f - 2149 + 398 , // 111 - 70 - 1915 + 457 , // 112 - 71 - 2200 + 591 , // 113 - 72 - 2799 + 762 , // 114 - 73 - 3680 + 358 , // 115 - 74 - 1726 + 375 , // 116 - 75 - 1816 + 478 , // 117 - 76 - 2313 + 704 , // 118 - 77 - 3365 + 400 , // 119 - 78 - 1922 + 603 , // 120 - 79 - 2854 + 41 , // 121 - 7a - 190 + 535 , // 122 - 7c - 2565 + 77 , // 123 - 7e - 360 + 791 , // 124 - 80 - 3833 + 521 , // 125 - 81 - 2494 + 589 , // 126 - 82 - 2792 + 101 , // 127 - 83 - 495 + 360 , // 128 - 84 - 1733 + 659 , // 129 - 85 - 3131 + 630 , // 130 - 86 - 2998 + 653 , // 131 - 87 - 3108 + 822 , // 132 - 88 - 4002 + 609 , // 133 - 8c - 2881 + 354 , // 134 - 91 - 1712 + 470 , // 135 - 92 - 2270 + 33 , // 136 - 401 - 150 + 65 , // 137 - 402 - 303 + 90 , // 138 - 403 - 428 + 859 , // 139 - 404 - 4248 + 104 , // 140 - 405 - 504 + 110 , // 141 - 406 - 525 + 118 , // 142 - 407 - 561 + 142 , // 143 - 408 - 671 + 240 , // 144 - 409 - 1161 + 263 , // 145 - 40a - 1272 + 294 , // 146 - 40b - 1425 + 316 , // 147 - 40c - 1529 + 378 , // 148 - 40d - 1827 + 387 , // 149 - 40e - 1862 + 403 , // 150 - 40f - 1931 + 406 , // 151 - 410 - 1943 + 414 , // 152 - 411 - 1991 + 454 , // 153 - 412 - 2186 + 569 , // 154 - 413 - 2707 + 554 , // 155 - 414 - 2641 + 606 , // 156 - 415 - 2866 + 615 , // 157 - 416 - 2904 + 638 , // 158 - 417 - 3043 + 643 , // 159 - 418 - 3062 + 651 , // 160 - 419 - 3098 + 383 , // 161 - 41a - 1846 + 688 , // 162 - 41b - 3286 + 710 , // 163 - 41c - 3389 + 737 , // 164 - 41d - 3565 + 761 , // 165 - 41e - 3675 + 776 , // 166 - 41f - 3734 + 797 , // 167 - 420 - 3854 + 397 , // 168 - 421 - 1910 + 794 , // 169 - 422 - 3842 + 59 , // 170 - 423 - 278 + 690 , // 171 - 424 - 3293 + 279 , // 172 - 425 - 1356 + 507 , // 173 - 426 - 2431 + 499 , // 174 - 427 - 2399 + 759 , // 175 - 428 - 3663 + 285 , // 176 - 429 - 1379 + 813 , // 177 - 42a - 3962 + 390 , // 178 - 42b - 1881 + 53 , // 179 - 42c - 250 + 281 , // 180 - 42d - 1363 + 385 , // 181 - 42e - 1854 + 524 , // 182 - 42f - 2503 + 733 , // 183 - 430 - 3548 + 778 , // 184 - 431 - 3741 + 771 , // 185 - 432 - 3715 + 811 , // 186 - 433 - 3955 + 826 , // 187 - 434 - 4022 + 863 , // 188 - 435 - 4279 + 6 , // 189 - 436 - 24 + 426 , // 190 - 437 - 2064 + 299 , // 191 - 438 - 1446 + 380 , // 192 - 439 - 1834 + 544 , // 193 - 43a - 2600 + 672 , // 194 - 43b - 3201 + 832 , // 195 - 43d - 4047 + 541 , // 196 - 43e - 2588 + 441 , // 197 - 43f - 2135 + 477 , // 198 - 440 - 2308 + 740 , // 199 - 441 - 3577 + 768 , // 200 - 442 - 3703 + 804 , // 201 - 443 - 3902 + 780 , // 202 - 444 - 3748 + 73 , // 203 - 445 - 343 + 602 , // 204 - 446 - 2849 + 365 , // 205 - 447 - 1756 + 595 , // 206 - 448 - 2813 + 748 , // 207 - 449 - 3612 + 753 , // 208 - 44a - 3634 + 451 , // 209 - 44b - 2174 + 526 , // 210 - 44c - 2510 + 44 , // 211 - 44d - 201 + 538 , // 212 - 44e - 2576 + 658 , // 213 - 44f - 3126 + 529 , // 214 - 450 - 2524 + 75 , // 215 - 451 - 350 + 108 , // 216 - 452 - 518 + 449 , // 217 - 453 - 2167 + 494 , // 218 - 454 - 2377 + 548 , // 219 - 455 - 2616 + 357 , // 220 - 456 - 1721 + 456 , // 221 - 457 - 2194 + 534 , // 222 - 458 - 2559 + 669 , // 223 - 459 - 3184 + 746 , // 224 - 45a - 3604 + 686 , // 225 - 45b - 3279 + 100 , // 226 - 45c - 484 + 410 , // 227 - 45d - 1962 + 12 , // 228 - 45e - 47 + 785 , // 229 - 45f - 3773 + 460 , // 230 - 460 - 2209 + 563 , // 231 - 461 - 2680 + 351 , // 232 - 462 - 1700 + 612 , // 233 - 463 - 2892 + 296 , // 234 - 464 - 1433 + 130 , // 235 - 465 - 622 + 67 , // 236 - 466 - 311 + 292 , // 237 - 467 - 1418 + 374 , // 238 - 468 - 1806 + 395 , // 239 - 469 - 1902 + 835 , // 240 - 46a - 4060 + 634 , // 241 - 46b - 3023 + 584 , // 242 - 46c - 2768 + 55 , // 243 - 46d - 262 + 483 , // 244 - 46e - 2332 + 445 , // 245 - 46f - 2151 + 399 , // 246 - 470 - 1917 + 458 , // 247 - 471 - 2202 + 592 , // 248 - 472 - 2801 + 764 , // 249 - 473 - 3687 + 359 , // 250 - 474 - 1728 + 376 , // 251 - 475 - 1819 + 479 , // 252 - 476 - 2315 + 708 , // 253 - 477 - 3382 + 401 , // 254 - 478 - 1924 + 604 , // 255 - 479 - 2857 + 42 , // 256 - 47a - 193 + 536 , // 257 - 47c - 2568 + 78 , // 258 - 47e - 362 + 792 , // 259 - 480 - 3835 + 522 , // 260 - 481 - 2496 + 590 , // 261 - 482 - 2794 + 102 , // 262 - 483 - 497 + 362 , // 263 - 484 - 1742 + 660 , // 264 - 485 - 3134 + 632 , // 265 - 486 - 3009 + 654 , // 266 - 487 - 3110 + 823 , // 267 - 488 - 4004 + 610 , // 268 - 48c - 2884 + 355 , // 269 - 491 - 1714 + 472 , // 270 - 492 - 2279 + 627 , // 271 - 501 - 2972 + 628 , // 272 - 5fe - 2980 + 22 , // 273 - 801 - 95 + 91 , // 274 - 803 - 433 + 844 , // 275 - 804 - 4110 + 117 , // 276 - 807 - 556 + 174 , // 277 - 809 - 831 + 267 , // 278 - 80a - 1299 + 302 , // 279 - 80c - 1459 + 405 , // 280 - 810 - 1938 + 566 , // 281 - 813 - 2692 + 575 , // 282 - 814 - 2733 + 623 , // 283 - 816 - 2944 + 642 , // 284 - 818 - 3057 + 650 , // 285 - 819 - 3093 + 722 , // 286 - 81a - 3480 + 736 , // 287 - 81d - 3560 + 796 , // 288 - 820 - 3849 + 51 , // 289 - 82c - 233 + 126 , // 290 - 82e - 605 + 770 , // 291 - 832 - 3710 + 673 , // 292 - 83b - 3206 + 353 , // 293 - 83c - 1707 + 540 , // 294 - 83e - 2583 + 802 , // 295 - 843 - 3885 + 72 , // 296 - 845 - 338 + 601 , // 297 - 846 - 2839 + 749 , // 298 - 849 - 3617 + 531 , // 299 - 850 - 2536 + 667 , // 300 - 859 - 3167 + 412 , // 301 - 85d - 1979 + 787 , // 302 - 85f - 3792 + 463 , // 303 - 860 - 2233 + 562 , // 304 - 861 - 2675 + 290 , // 305 - 867 - 1403 + 635 , // 306 - 86b - 3029 + 763 , // 307 - 873 - 3682 + 626 , // 308 - 901 - 2959 + 629 , // 309 - 9ff - 2989 + 19 , // 310 - c01 - 80 + 851 , // 311 - c04 - 4173 + 115 , // 312 - c07 - 546 + 151 , // 313 - c09 - 716 + 262 , // 314 - c0a - 1267 + 307 , // 315 - c0c - 1484 + 716 , // 316 - c1a - 3423 + 671 , // 317 - c3b - 3196 + 532 , // 318 - c50 - 2546 + 134 , // 319 - c51 - 638 + 636 , // 320 - c6b - 3035 + 27 , // 321 - 1001 - 120 + 856 , // 322 - 1004 - 4219 + 122 , // 323 - 1007 - 588 + 159 , // 324 - 1009 - 756 + 265 , // 325 - 100a - 1289 + 311 , // 326 - 100c - 1504 + 382 , // 327 - 101a - 1841 + 695 , // 328 - 103b - 3316 + 790 , // 329 - 105f - 3822 + 18 , // 330 - 1401 - 75 + 853 , // 331 - 1404 - 4190 + 121 , // 332 - 1407 - 583 + 213 , // 333 - 1409 - 1026 + 258 , // 334 - 140a - 1247 + 324 , // 335 - 140c - 1569 + 85 , // 336 - 141a - 402 + 696 , // 337 - 143b - 3322 + 28 , // 338 - 1801 - 125 + 184 , // 339 - 1809 - 881 + 269 , // 340 - 180a - 1309 + 326 , // 341 - 180c - 1579 + 721 , // 342 - 181a - 3470 + 692 , // 343 - 183b - 3301 + 39 , // 344 - 1c01 - 180 + 246 , // 345 - 1c09 - 1191 + 260 , // 346 - 1c0a - 1257 + 301 , // 347 - 1c0c - 1453 + 715 , // 348 - 1c1a - 3413 + 693 , // 349 - 1c3b - 3307 + 30 , // 350 - 2001 - 135 + 190 , // 351 - 2009 - 911 + 277 , // 352 - 200a - 1349 + 337 , // 353 - 200c - 1634 + 83 , // 354 - 201a - 385 + 700 , // 355 - 203b - 3340 + 40 , // 356 - 2401 - 185 + 145 , // 357 - 2409 - 684 + 257 , // 358 - 240a - 1242 + 308 , // 359 - 240c - 1489 + 724 , // 360 - 241a - 3500 + 698 , // 361 - 243b - 3331 + 37 , // 362 - 2801 - 170 + 158 , // 363 - 2809 - 751 + 270 , // 364 - 280a - 1314 + 340 , // 365 - 280c - 1649 + 718 , // 366 - 281a - 3443 + 23 , // 367 - 2c01 - 100 + 235 , // 368 - 2c09 - 1136 + 253 , // 369 - 2c0a - 1222 + 313 , // 370 - 2c0c - 1514 + 723 , // 371 - 2c1a - 3490 + 26 , // 372 - 3001 - 115 + 248 , // 373 - 3009 - 1201 + 261 , // 374 - 300a - 1262 + 312 , // 375 - 300c - 1509 + 717 , // 376 - 301a - 3433 + 25 , // 377 - 3401 - 110 + 215 , // 378 - 3409 - 1036 + 256 , // 379 - 340a - 1237 + 329 , // 380 - 340c - 1594 + 15 , // 381 - 3801 - 60 + 183 , // 382 - 3809 - 876 + 276 , // 383 - 380a - 1344 + 325 , // 384 - 380c - 1574 + 16 , // 385 - 3c01 - 65 + 182 , // 386 - 3c09 - 871 + 273 , // 387 - 3c0a - 1329 + 322 , // 388 - 3c0c - 1559 + 32 , // 389 - 4001 - 145 + 187 , // 390 - 4009 - 896 + 254 , // 391 - 400a - 1227 + 206 , // 392 - 4409 - 991 + 274 , // 393 - 440a - 1334 + 225 , // 394 - 4809 - 1086 + 266 , // 395 - 480a - 1294 + 268 , // 396 - 4c0a - 1304 + 272 , // 397 - 500a - 1324 + 275 , // 398 - 540a - 1339 + 252 , // 399 - 580a - 1216 + 259 , // 400 - 5c0a - 1252 + 82 , // 401 - 641a - 378 + 84 , // 402 - 681a - 395 + 714 , // 403 - 6c1a - 3406 + 720 , // 404 - 701a - 3463 + 697 , // 405 - 703b - 3328 + 50 , // 406 - 742c - 226 + 699 , // 407 - 743b - 3337 + 841 , // 408 - 7804 - 4096 + 574 , // 409 - 7814 - 2731 + 81 , // 410 - 781a - 376 + 52 , // 411 - 782c - 243 + 691 , // 412 - 783b - 3298 + 801 , // 413 - 7843 - 3878 + 528 , // 414 - 7850 - 2517 + 409 , // 415 - 785d - 1955 + 789 , // 416 - 785f - 3814 + 850 , // 417 - 7c04 - 4166 + 553 , // 418 - 7c14 - 2639 + 713 , // 419 - 7c1a - 3404 + 758 , // 420 - 7c28 - 3656 + 125 , // 421 - 7c2e - 602 + 694 , // 422 - 7c3b - 3313 + 803 , // 423 - 7c43 - 3895 + 600 , // 424 - 7c46 - 2832 + 530 , // 425 - 7c50 - 2529 + 666 , // 426 - 7c59 - 3160 + 99 , // 427 - 7c5c - 476 + 411 , // 428 - 7c5d - 1972 + 786 , // 429 - 7c5f - 3784 + 289 , // 430 - 7c67 - 1396 + 371 , // 431 - 7c68 - 1779 + 631 , // 432 - 7c86 - 3001 + 471 , // 433 - 7c92 - 2272 + 824 , // 434 - 1007f - 4009 + 119 , // 435 - 10407 - 566 + 388 , // 436 - 1040e - 1867 + 427 , // 437 - 10437 - 2069 + 846 , // 438 - 20804 - 4127 + 858 , // 439 - 21004 - 4236 + 855 , // 440 - 21404 - 4207 + 860 , // 441 - 30404 - 4253 + 861 , // 442 - 40404 - 4265 + 415 , // 443 - 40411 - 1996 + 852 , // 444 - 40c04 - 4178 + 854 , // 445 - 41404 - 4195 + 845 , // 446 - 50804 - 4115 + 857 // 447 - 51004 - 4224 + }; + + internal static string LCIDToLocaleName(int culture) + { + int left = 0; + int right = s_lcids.Length - 1; + int index; + + Debug.Assert(s_lcids.Length == s_lcidToCultureNameIndices.Length); + + while (left <= right) + { + index = (right + left) / 2; + + if (culture == s_lcids[index]) + { + int indexToLocaleNamesIndices = s_lcidToCultureNameIndices[index]; + Debug.Assert(indexToLocaleNamesIndices < s_localeNamesIndices.Length - 1); + + return c_localeNames.Substring(s_localeNamesIndices[indexToLocaleNamesIndices], + s_localeNamesIndices[indexToLocaleNamesIndices + 1] - + s_localeNamesIndices[indexToLocaleNamesIndices]); + } + else if (culture < s_lcids[index]) + { + right = index - 1; + } + else + { + left = index + 1; + } + } + + return null; + } + + internal static int GetLocaleDataNumericPart(string cultureName, LocaleDataParts part) + { + int index = SearchCultureName(cultureName); + if (index < 0) + { + return -1; + } + + Debug.Assert((s_localeNamesIndices.Length-1 == (s_nameIndexToNumericData.Length/NUMERIC_LOCALE_DATA_COUNT_PER_ROW)) && + index < s_localeNamesIndices.Length); + + return s_nameIndexToNumericData[index * NUMERIC_LOCALE_DATA_COUNT_PER_ROW + (int) part]; + } + + internal static string GetThreeLetterWindowsLangageName(string cultureName) + { + int index = SearchCultureName(cultureName); + if (index < 0) + { + return null; + } + + Debug.Assert(s_localeNamesIndices.Length-1 == (c_threeLetterWindowsLanguageName.Length / 3)); + return c_threeLetterWindowsLanguageName.Substring(index * 3, 3); + } + + internal static string GetLocaleDataMappedCulture(string cultureName, LocaleDataParts part) + { + int indexToIndicesTable = GetLocaleDataNumericPart(cultureName, part); + if (indexToIndicesTable < 0) + { + return ""; // fallback to invariant + } + + Debug.Assert(indexToIndicesTable < s_localeNamesIndices.Length-1); + + return c_localeNames.Substring(s_localeNamesIndices[indexToIndicesTable], + s_localeNamesIndices[indexToIndicesTable+1] - s_localeNamesIndices[indexToIndicesTable]); + } + + internal static string GetSpecificCultureName(string cultureName) + { + return GetLocaleDataMappedCulture(cultureName, LocaleDataParts.SpecificLocaleIndex); + } + + internal static string GetConsoleUICulture(string cultureName) + { + return GetLocaleDataMappedCulture(cultureName, LocaleDataParts.ConsoleLocaleIndex); + } + + // SearchCultureName will binary search c_localeNames using s_localeNamesIndices. + // return index in s_localeNamesIndices, or -1 if it fail finding any match + private static int SearchCultureName(string name) + { + int left = 0; + int right = s_localeNamesIndices.Length - 2; + int index; + int result; + + Debug.Assert(s_localeNamesIndices[s_localeNamesIndices.Length - 1] == c_localeNames.Length); + + name = CultureData.AnsiToLower(name); + + // Binary search the array until we have only a couple of elements left and then + // just walk those elements. + while ((right - left) > 3) + { + index = ((right - left) / 2) + left; + + Debug.Assert(index < s_localeNamesIndices.Length - 1); + result = CompareOrdinal(name, c_localeNames, s_localeNamesIndices[index], s_localeNamesIndices[index + 1] - s_localeNamesIndices[index]); + if (result == 0) + { + return index; + } + else if (result < 0) + { + right = index; + } + else + { + left = index; + } + } + + // Walk the remaining elements (it'll be 3 or fewer). + for (; left <= right; left++) + { + Debug.Assert(left < s_localeNamesIndices.Length - 1); + if (CompareOrdinal(name, c_localeNames, s_localeNamesIndices[left], s_localeNamesIndices[left + 1] - s_localeNamesIndices[left]) == 0) + { + return (left); + } + } + + // couldn't find culture name + return -1; + } + + // optimized to avoid parameters checking + private static int CompareOrdinal(string s1, string s2, int index, int length) + { + int count = s1.Length; + if (count > length) + count = length; + + int i = 0; + while (i < count && s1[i] == s2[index + i]) + i++; + + if (i < count) + return (int)(s1[i] - s2[index + i]); + + return s1.Length - length; + } + } +} diff --git a/src/mscorlib/shared/System/Globalization/NumberStyles.cs b/src/mscorlib/shared/System/Globalization/NumberStyles.cs new file mode 100644 index 0000000000..5909d65a2c --- /dev/null +++ b/src/mscorlib/shared/System/Globalization/NumberStyles.cs @@ -0,0 +1,65 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** +** +** Purpose: Contains valid formats for Numbers recognized by +** the Number class' parsing code. +** +** +===========================================================*/ + +namespace System.Globalization +{ + [Flags] + public enum NumberStyles + { + // Bit flag indicating that leading whitespace is allowed. Character values + // 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, and 0x0020 are considered to be + // whitespace. + + None = 0x00000000, + + AllowLeadingWhite = 0x00000001, + + AllowTrailingWhite = 0x00000002, //Bitflag indicating trailing whitespace is allowed. + + AllowLeadingSign = 0x00000004, //Can the number start with a sign char. + //Specified by NumberFormatInfo.PositiveSign and NumberFormatInfo.NegativeSign + + AllowTrailingSign = 0x00000008, //Allow the number to end with a sign char + + AllowParentheses = 0x00000010, //Allow the number to be enclosed in parens + + AllowDecimalPoint = 0x00000020, //Allow a decimal point + + AllowThousands = 0x00000040, //Allow thousands separators (more properly, allow group separators) + + AllowExponent = 0x00000080, //Allow an exponent + + AllowCurrencySymbol = 0x00000100, //Allow a currency symbol. + + AllowHexSpecifier = 0x00000200, //Allow specifiying hexadecimal. + //Common uses. These represent some of the most common combinations of these flags. + + + Integer = AllowLeadingWhite | AllowTrailingWhite | AllowLeadingSign, + + HexNumber = AllowLeadingWhite | AllowTrailingWhite | AllowHexSpecifier, + + Number = AllowLeadingWhite | AllowTrailingWhite | AllowLeadingSign | AllowTrailingSign | + AllowDecimalPoint | AllowThousands, + + Float = AllowLeadingWhite | AllowTrailingWhite | AllowLeadingSign | + AllowDecimalPoint | AllowExponent, + + Currency = AllowLeadingWhite | AllowTrailingWhite | AllowLeadingSign | AllowTrailingSign | + AllowParentheses | AllowDecimalPoint | AllowThousands | AllowCurrencySymbol, + + Any = AllowLeadingWhite | AllowTrailingWhite | AllowLeadingSign | AllowTrailingSign | + AllowParentheses | AllowDecimalPoint | AllowThousands | AllowCurrencySymbol | AllowExponent, + } +} diff --git a/src/mscorlib/shared/System/Globalization/PersianCalendar.cs b/src/mscorlib/shared/System/Globalization/PersianCalendar.cs new file mode 100644 index 0000000000..445bbd6d0c --- /dev/null +++ b/src/mscorlib/shared/System/Globalization/PersianCalendar.cs @@ -0,0 +1,606 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Diagnostics.Contracts; + +namespace System.Globalization +{ + // Modern Persian calendar is a solar observation based calendar. Each new year begins on the day when the vernal equinox occurs before noon. + // The epoch is the date of the vernal equinox prior to the epoch of the Islamic calendar (March 19, 622 Julian or March 22, 622 Gregorian) + + // There is no Persian year 0. Ordinary years have 365 days. Leap years have 366 days with the last month (Esfand) gaining the extra day. + /* + ** Calendar support range: + ** Calendar Minimum Maximum + ** ========== ========== ========== + ** Gregorian 0622/03/22 9999/12/31 + ** Persian 0001/01/01 9378/10/13 + */ + + [Serializable] + public class PersianCalendar : Calendar + { + public static readonly int PersianEra = 1; + + internal static long PersianEpoch = new DateTime(622, 3, 22).Ticks / GregorianCalendar.TicksPerDay; + private const int ApproximateHalfYear = 180; + + internal const int DatePartYear = 0; + internal const int DatePartDayOfYear = 1; + internal const int DatePartMonth = 2; + internal const int DatePartDay = 3; + internal const int MonthsPerYear = 12; + + internal static int[] DaysToMonth = { 0, 31, 62, 93, 124, 155, 186, 216, 246, 276, 306, 336, 366 }; + + internal const int MaxCalendarYear = 9378; + internal const int MaxCalendarMonth = 10; + internal const int MaxCalendarDay = 13; + + // Persian calendar (year: 1, month: 1, day:1 ) = Gregorian (year: 622, month: 3, day: 22) + // This is the minimal Gregorian date that we support in the PersianCalendar. + internal static DateTime minDate = new DateTime(622, 3, 22); + internal static DateTime maxDate = DateTime.MaxValue; + + public override DateTime MinSupportedDateTime + { + get + { + return (minDate); + } + } + + public override DateTime MaxSupportedDateTime + { + get + { + return (maxDate); + } + } + + public override CalendarAlgorithmType AlgorithmType + { + get + { + return CalendarAlgorithmType.SolarCalendar; + } + } + + // Construct an instance of Persian calendar. + + public PersianCalendar() + { + } + + + internal override CalendarId BaseCalendarID + { + get + { + return CalendarId.GREGORIAN; + } + } + + internal override CalendarId ID + { + get + { + return CalendarId.PERSIAN; + } + } + + + /*=================================GetAbsoluteDatePersian========================== + **Action: Gets the Absolute date for the given Persian date. The absolute date means + ** the number of days from January 1st, 1 A.D. + **Returns: + **Arguments: + **Exceptions: + ============================================================================*/ + + private long GetAbsoluteDatePersian(int year, int month, int day) + { + if (year >= 1 && year <= MaxCalendarYear && month >= 1 && month <= 12) + { + int ordinalDay = DaysInPreviousMonths(month) + day - 1; // day is one based, make 0 based since this will be the number of days we add to beginning of year below + int approximateDaysFromEpochForYearStart = (int)(CalendricalCalculationsHelper.MeanTropicalYearInDays * (year - 1)); + long yearStart = CalendricalCalculationsHelper.PersianNewYearOnOrBefore(PersianEpoch + approximateDaysFromEpochForYearStart + ApproximateHalfYear); + yearStart += ordinalDay; + return yearStart; + } + throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadYearMonthDay); + } + + internal static void CheckTicksRange(long ticks) + { + if (ticks < minDate.Ticks || ticks > maxDate.Ticks) + { + throw new ArgumentOutOfRangeException( + "time", + String.Format( + CultureInfo.InvariantCulture, + SR.ArgumentOutOfRange_CalendarRange, + minDate, + maxDate)); + } + } + + internal static void CheckEraRange(int era) + { + if (era != CurrentEra && era != PersianEra) + { + throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue); + } + } + + internal static void CheckYearRange(int year, int era) + { + CheckEraRange(era); + if (year < 1 || year > MaxCalendarYear) + { + throw new ArgumentOutOfRangeException( + nameof(year), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 1, + MaxCalendarYear)); + } + } + + internal static void CheckYearMonthRange(int year, int month, int era) + { + CheckYearRange(year, era); + if (year == MaxCalendarYear) + { + if (month > MaxCalendarMonth) + { + throw new ArgumentOutOfRangeException( + nameof(month), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 1, + MaxCalendarMonth)); + } + } + + if (month < 1 || month > 12) + { + throw new ArgumentOutOfRangeException(nameof(month), SR.ArgumentOutOfRange_Month); + } + } + + private static int MonthFromOrdinalDay(int ordinalDay) + { + Debug.Assert(ordinalDay <= 366); + int index = 0; + while (ordinalDay > DaysToMonth[index]) + index++; + + return index; + } + + private static int DaysInPreviousMonths(int month) + { + Debug.Assert(1 <= month && month <= 12); + --month; // months are one based but for calculations use 0 based + return DaysToMonth[month]; + } + + /*=================================GetDatePart========================== + **Action: Returns a given date part of this <i>DateTime</i>. This method is used + ** to compute the year, day-of-year, month, or day part. + **Returns: + **Arguments: + **Exceptions: ArgumentException if part is incorrect. + ============================================================================*/ + + internal int GetDatePart(long ticks, int part) + { + long NumDays; // The calculation buffer in number of days. + + CheckTicksRange(ticks); + + // + // Get the absolute date. The absolute date is the number of days from January 1st, 1 A.D. + // 1/1/0001 is absolute date 1. + // + NumDays = ticks / GregorianCalendar.TicksPerDay + 1; + + // + // Calculate the appromixate Persian Year. + // + + long yearStart = CalendricalCalculationsHelper.PersianNewYearOnOrBefore(NumDays); + int y = (int)(Math.Floor(((yearStart - PersianEpoch) / CalendricalCalculationsHelper.MeanTropicalYearInDays) + 0.5)) + 1; + Debug.Assert(y >= 1); + + if (part == DatePartYear) + { + return y; + } + + // + // Calculate the Persian Month. + // + + int ordinalDay = (int)(NumDays - CalendricalCalculationsHelper.GetNumberOfDays(this.ToDateTime(y, 1, 1, 0, 0, 0, 0, 1))); + + if (part == DatePartDayOfYear) + { + return ordinalDay; + } + + int m = MonthFromOrdinalDay(ordinalDay); + Debug.Assert(ordinalDay >= 1); + Debug.Assert(m >= 1 && m <= 12); + if (part == DatePartMonth) + { + return m; + } + + int d = ordinalDay - DaysInPreviousMonths(m); + Debug.Assert(1 <= d); + Debug.Assert(d <= 31); + + // + // Calculate the Persian Day. + // + + if (part == DatePartDay) + { + return (d); + } + + // Incorrect part value. + throw new InvalidOperationException(SR.InvalidOperation_DateTimeParsing); + } + + // Returns the DateTime resulting from adding the given number of + // months to the specified DateTime. The result is computed by incrementing + // (or decrementing) the year and month parts of the specified DateTime by + // value months, and, if required, adjusting the day part of the + // resulting date downwards to the last day of the resulting month in the + // resulting year. The time-of-day part of the result is the same as the + // time-of-day part of the specified DateTime. + // + // In more precise terms, considering the specified DateTime to be of the + // form y / m / d + t, where y is the + // year, m is the month, d is the day, and t is the + // time-of-day, the result is y1 / m1 / d1 + t, + // where y1 and m1 are computed by adding value months + // to y and m, and d1 is the largest value less than + // or equal to d that denotes a valid day in month m1 of year + // y1. + // + + + public override DateTime AddMonths(DateTime time, int months) + { + if (months < -120000 || months > 120000) + { + throw new ArgumentOutOfRangeException( + nameof(months), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + -120000, + 120000)); + } + Contract.EndContractBlock(); + // Get the date in Persian calendar. + int y = GetDatePart(time.Ticks, DatePartYear); + int m = GetDatePart(time.Ticks, DatePartMonth); + int d = GetDatePart(time.Ticks, DatePartDay); + int i = m - 1 + months; + if (i >= 0) + { + m = i % 12 + 1; + y = y + i / 12; + } + else + { + m = 12 + (i + 1) % 12; + y = y + (i - 11) / 12; + } + int days = GetDaysInMonth(y, m); + if (d > days) + { + d = days; + } + long ticks = GetAbsoluteDatePersian(y, m, d) * TicksPerDay + time.Ticks % TicksPerDay; + Calendar.CheckAddResult(ticks, MinSupportedDateTime, MaxSupportedDateTime); + return (new DateTime(ticks)); + } + + // Returns the DateTime resulting from adding the given number of + // years to the specified DateTime. The result is computed by incrementing + // (or decrementing) the year part of the specified DateTime by value + // years. If the month and day of the specified DateTime is 2/29, and if the + // resulting year is not a leap year, the month and day of the resulting + // DateTime becomes 2/28. Otherwise, the month, day, and time-of-day + // parts of the result are the same as those of the specified DateTime. + // + + + public override DateTime AddYears(DateTime time, int years) + { + return (AddMonths(time, years * 12)); + } + + // Returns the day-of-month part of the specified DateTime. The returned + // value is an integer between 1 and 31. + // + + + public override int GetDayOfMonth(DateTime time) + { + return (GetDatePart(time.Ticks, DatePartDay)); + } + + // Returns the day-of-week part of the specified DateTime. The returned value + // is an integer between 0 and 6, where 0 indicates Sunday, 1 indicates + // Monday, 2 indicates Tuesday, 3 indicates Wednesday, 4 indicates + // Thursday, 5 indicates Friday, and 6 indicates Saturday. + // + + + public override DayOfWeek GetDayOfWeek(DateTime time) + { + return ((DayOfWeek)((int)(time.Ticks / TicksPerDay + 1) % 7)); + } + + // Returns the day-of-year part of the specified DateTime. The returned value + // is an integer between 1 and 366. + // + + + public override int GetDayOfYear(DateTime time) + { + return (GetDatePart(time.Ticks, DatePartDayOfYear)); + } + + // Returns the number of days in the month given by the year and + // month arguments. + // + + + public override int GetDaysInMonth(int year, int month, int era) + { + CheckYearMonthRange(year, month, era); + + if ((month == MaxCalendarMonth) && (year == MaxCalendarYear)) + { + return MaxCalendarDay; + } + + int daysInMonth = DaysToMonth[month] - DaysToMonth[month - 1]; + if ((month == MonthsPerYear) && !IsLeapYear(year)) + { + Debug.Assert(daysInMonth == 30); + --daysInMonth; + } + return daysInMonth; + } + + // Returns the number of days in the year given by the year argument for the current era. + // + + public override int GetDaysInYear(int year, int era) + { + CheckYearRange(year, era); + if (year == MaxCalendarYear) + { + return DaysToMonth[MaxCalendarMonth - 1] + MaxCalendarDay; + } + // Common years have 365 days. Leap years have 366 days. + return (IsLeapYear(year, CurrentEra) ? 366 : 365); + } + + // Returns the era for the specified DateTime value. + + + public override int GetEra(DateTime time) + { + CheckTicksRange(time.Ticks); + return (PersianEra); + } + + + + public override int[] Eras + { + get + { + return (new int[] { PersianEra }); + } + } + + // Returns the month part of the specified DateTime. The returned value is an + // integer between 1 and 12. + // + + + public override int GetMonth(DateTime time) + { + return (GetDatePart(time.Ticks, DatePartMonth)); + } + + // Returns the number of months in the specified year and era. + + + public override int GetMonthsInYear(int year, int era) + { + CheckYearRange(year, era); + if (year == MaxCalendarYear) + { + return MaxCalendarMonth; + } + return (12); + } + + // Returns the year part of the specified DateTime. The returned value is an + // integer between 1 and MaxCalendarYear. + // + + + public override int GetYear(DateTime time) + { + return (GetDatePart(time.Ticks, DatePartYear)); + } + + // Checks whether a given day in the specified era is a leap day. This method returns true if + // the date is a leap day, or false if not. + // + + + public override bool IsLeapDay(int year, int month, int day, int era) + { + // The year/month/era value checking is done in GetDaysInMonth(). + int daysInMonth = GetDaysInMonth(year, month, era); + if (day < 1 || day > daysInMonth) + { + throw new ArgumentOutOfRangeException( + nameof(day), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Day, + daysInMonth, + month)); + } + return (IsLeapYear(year, era) && month == 12 && day == 30); + } + + // Returns the leap month in a calendar year of the specified era. This method returns 0 + // if this calendar does not have leap month, or this year is not a leap year. + // + + + public override int GetLeapMonth(int year, int era) + { + CheckYearRange(year, era); + return (0); + } + + // Checks whether a given month in the specified era is a leap month. This method returns true if + // month is a leap month, or false if not. + // + + + public override bool IsLeapMonth(int year, int month, int era) + { + CheckYearMonthRange(year, month, era); + return (false); + } + + // Checks whether a given year in the specified era is a leap year. This method returns true if + // year is a leap year, or false if not. + // + + public override bool IsLeapYear(int year, int era) + { + CheckYearRange(year, era); + + if (year == MaxCalendarYear) + { + return false; + } + + return (GetAbsoluteDatePersian(year + 1, 1, 1) - GetAbsoluteDatePersian(year, 1, 1)) == 366; + } + + // Returns the date and time converted to a DateTime value. Throws an exception if the n-tuple is invalid. + // + + + public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era) + { + // The year/month/era checking is done in GetDaysInMonth(). + int daysInMonth = GetDaysInMonth(year, month, era); + if (day < 1 || day > daysInMonth) + { + // BCLDebug.Log("year = " + year + ", month = " + month + ", day = " + day); + throw new ArgumentOutOfRangeException( + nameof(day), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Day, + daysInMonth, + month)); + } + + long lDate = GetAbsoluteDatePersian(year, month, day); + + if (lDate >= 0) + { + return (new DateTime(lDate * GregorianCalendar.TicksPerDay + TimeToTicks(hour, minute, second, millisecond))); + } + else + { + throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadYearMonthDay); + } + } + + private const int DEFAULT_TWO_DIGIT_YEAR_MAX = 1410; + + public override int TwoDigitYearMax + { + get + { + if (twoDigitYearMax == -1) + { + twoDigitYearMax = GetSystemTwoDigitYearSetting(ID, DEFAULT_TWO_DIGIT_YEAR_MAX); + } + return (twoDigitYearMax); + } + + set + { + VerifyWritable(); + if (value < 99 || value > MaxCalendarYear) + { + throw new ArgumentOutOfRangeException( + nameof(value), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 99, + MaxCalendarYear)); + } + twoDigitYearMax = value; + } + } + + + + public override int ToFourDigitYear(int year) + { + if (year < 0) + { + throw new ArgumentOutOfRangeException(nameof(year), + SR.ArgumentOutOfRange_NeedNonNegNum); + } + Contract.EndContractBlock(); + + if (year < 100) + { + return (base.ToFourDigitYear(year)); + } + + if (year > MaxCalendarYear) + { + throw new ArgumentOutOfRangeException( + nameof(year), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 1, + MaxCalendarYear)); + } + return (year); + } + } +} diff --git a/src/mscorlib/shared/System/Globalization/SortVersion.cs b/src/mscorlib/shared/System/Globalization/SortVersion.cs new file mode 100644 index 0000000000..a7aef6d84b --- /dev/null +++ b/src/mscorlib/shared/System/Globalization/SortVersion.cs @@ -0,0 +1,98 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Globalization +{ + [Serializable] + public sealed class SortVersion : IEquatable<SortVersion> + { + private int _nlsVersion; + private Guid _sortId; + + public int FullVersion + { + get + { + return _nlsVersion; + } + } + + public Guid SortId + { + get + { + return _sortId; + } + } + + public SortVersion(int fullVersion, Guid sortId) + { + _sortId = sortId; + _nlsVersion = fullVersion; + } + + internal SortVersion(int nlsVersion, int effectiveId, Guid customVersion) + { + _nlsVersion = nlsVersion; + + if (customVersion == Guid.Empty) + { + byte b1 = (byte)(effectiveId >> 24); + byte b2 = (byte)((effectiveId & 0x00FF0000) >> 16); + byte b3 = (byte)((effectiveId & 0x0000FF00) >> 8); + byte b4 = (byte)(effectiveId & 0xFF); + customVersion = new Guid(0, 0, 0, 0, 0, 0, 0, b1, b2, b3, b4); + } + + _sortId = customVersion; + } + + public override bool Equals(object obj) + { + SortVersion n = obj as SortVersion; + if (n != null) + { + return this.Equals(n); + } + + return false; + } + + public bool Equals(SortVersion other) + { + if (other == null) + { + return false; + } + + return _nlsVersion == other._nlsVersion && _sortId == other._sortId; + } + + public override int GetHashCode() + { + return _nlsVersion * 7 | _sortId.GetHashCode(); + } + + public static bool operator ==(SortVersion left, SortVersion right) + { + if (((object)left) != null) + { + return left.Equals(right); + } + + if (((object)right) != null) + { + return right.Equals(left); + } + + // Both null. + return true; + } + + public static bool operator !=(SortVersion left, SortVersion right) + { + return !(left == right); + } + } +} diff --git a/src/mscorlib/shared/System/Globalization/TaiwanCalendar.cs b/src/mscorlib/shared/System/Globalization/TaiwanCalendar.cs new file mode 100644 index 0000000000..2e735e0cb9 --- /dev/null +++ b/src/mscorlib/shared/System/Globalization/TaiwanCalendar.cs @@ -0,0 +1,285 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.Contracts; + +namespace System.Globalization +{ + /*=================================TaiwanCalendar========================== + ** + ** Taiwan calendar is based on the Gregorian calendar. And the year is an offset to Gregorian calendar. + ** That is, + ** Taiwan year = Gregorian year - 1911. So 1912/01/01 A.D. is Taiwan 1/01/01 + ** + ** Calendar support range: + ** Calendar Minimum Maximum + ** ========== ========== ========== + ** Gregorian 1912/01/01 9999/12/31 + ** Taiwan 01/01/01 8088/12/31 + ============================================================================*/ + + [Serializable] + public class TaiwanCalendar : Calendar + { + // + // The era value for the current era. + // + + // Since + // Gregorian Year = Era Year + yearOffset + // When Gregorian Year 1912 is year 1, so that + // 1912 = 1 + yearOffset + // So yearOffset = 1911 + //m_EraInfo[0] = new EraInfo(1, new DateTime(1912, 1, 1).Ticks, 1911, 1, GregorianCalendar.MaxYear - 1911); + + // Initialize our era info. + internal static EraInfo[] taiwanEraInfo = new EraInfo[] { + new EraInfo( 1, 1912, 1, 1, 1911, 1, GregorianCalendar.MaxYear - 1911) // era #, start year/month/day, yearOffset, minEraYear + }; + + internal static volatile Calendar s_defaultInstance; + + internal GregorianCalendarHelper helper; + + /*=================================GetDefaultInstance========================== + **Action: Internal method to provide a default intance of TaiwanCalendar. Used by NLS+ implementation + ** and other calendars. + **Returns: + **Arguments: + **Exceptions: + ============================================================================*/ + + internal static Calendar GetDefaultInstance() + { + if (s_defaultInstance == null) + { + s_defaultInstance = new TaiwanCalendar(); + } + return (s_defaultInstance); + } + + internal static readonly DateTime calendarMinValue = new DateTime(1912, 1, 1); + + + public override DateTime MinSupportedDateTime + { + get + { + return (calendarMinValue); + } + } + + public override DateTime MaxSupportedDateTime + { + get + { + return (DateTime.MaxValue); + } + } + + public override CalendarAlgorithmType AlgorithmType + { + get + { + return CalendarAlgorithmType.SolarCalendar; + } + } + + // Return the type of the Taiwan calendar. + // + + public TaiwanCalendar() + { + try + { + new CultureInfo("zh-TW"); + } + catch (ArgumentException e) + { + throw new TypeInitializationException(this.GetType().ToString(), e); + } + helper = new GregorianCalendarHelper(this, taiwanEraInfo); + } + + internal override CalendarId ID + { + get + { + return CalendarId.TAIWAN; + } + } + + + public override DateTime AddMonths(DateTime time, int months) + { + return (helper.AddMonths(time, months)); + } + + + public override DateTime AddYears(DateTime time, int years) + { + return (helper.AddYears(time, years)); + } + + + public override int GetDaysInMonth(int year, int month, int era) + { + return (helper.GetDaysInMonth(year, month, era)); + } + + + public override int GetDaysInYear(int year, int era) + { + return (helper.GetDaysInYear(year, era)); + } + + + public override int GetDayOfMonth(DateTime time) + { + return (helper.GetDayOfMonth(time)); + } + + + public override DayOfWeek GetDayOfWeek(DateTime time) + { + return (helper.GetDayOfWeek(time)); + } + + + public override int GetDayOfYear(DateTime time) + { + return (helper.GetDayOfYear(time)); + } + + + public override int GetMonthsInYear(int year, int era) + { + return (helper.GetMonthsInYear(year, era)); + } + + + [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems. + public override int GetWeekOfYear(DateTime time, CalendarWeekRule rule, DayOfWeek firstDayOfWeek) + { + return (helper.GetWeekOfYear(time, rule, firstDayOfWeek)); + } + + + public override int GetEra(DateTime time) + { + return (helper.GetEra(time)); + } + + public override int GetMonth(DateTime time) + { + return (helper.GetMonth(time)); + } + + + public override int GetYear(DateTime time) + { + return (helper.GetYear(time)); + } + + + public override bool IsLeapDay(int year, int month, int day, int era) + { + return (helper.IsLeapDay(year, month, day, era)); + } + + + public override bool IsLeapYear(int year, int era) + { + return (helper.IsLeapYear(year, era)); + } + + // Returns the leap month in a calendar year of the specified era. This method returns 0 + // if this calendar does not have leap month, or this year is not a leap year. + // + + public override int GetLeapMonth(int year, int era) + { + return (helper.GetLeapMonth(year, era)); + } + + + public override bool IsLeapMonth(int year, int month, int era) + { + return (helper.IsLeapMonth(year, month, era)); + } + + + public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era) + { + return (helper.ToDateTime(year, month, day, hour, minute, second, millisecond, era)); + } + + + public override int[] Eras + { + get + { + return (helper.Eras); + } + } + + private const int DEFAULT_TWO_DIGIT_YEAR_MAX = 99; + + public override int TwoDigitYearMax + { + get + { + if (twoDigitYearMax == -1) + { + twoDigitYearMax = GetSystemTwoDigitYearSetting(ID, DEFAULT_TWO_DIGIT_YEAR_MAX); + } + return (twoDigitYearMax); + } + + set + { + VerifyWritable(); + if (value < 99 || value > helper.MaxYear) + { + throw new ArgumentOutOfRangeException( + "year", + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 99, + helper.MaxYear)); + } + twoDigitYearMax = value; + } + } + + // For Taiwan calendar, four digit year is not used. + // Therefore, for any two digit number, we just return the original number. + + public override int ToFourDigitYear(int year) + { + if (year <= 0) + { + throw new ArgumentOutOfRangeException(nameof(year), + SR.ArgumentOutOfRange_NeedPosNum); + } + Contract.EndContractBlock(); + + if (year > helper.MaxYear) + { + throw new ArgumentOutOfRangeException( + nameof(year), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 1, + helper.MaxYear)); + } + return (year); + } + } +} + diff --git a/src/mscorlib/shared/System/Globalization/TaiwanLunisolarCalendar.cs b/src/mscorlib/shared/System/Globalization/TaiwanLunisolarCalendar.cs new file mode 100644 index 0000000000..8ba1f278e7 --- /dev/null +++ b/src/mscorlib/shared/System/Globalization/TaiwanLunisolarCalendar.cs @@ -0,0 +1,325 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics.Contracts; + +namespace System.Globalization +{ + /* + ** Calendar support range: + ** Calendar Minimum Maximum + ** ========== ========== ========== + ** Gregorian 1912/02/18 2051/02/10 + ** TaiwanLunisolar 1912/01/01 2050/13/29 + */ + + [Serializable] + public class TaiwanLunisolarCalendar : EastAsianLunisolarCalendar + { + // Since + // Gregorian Year = Era Year + yearOffset + // When Gregorian Year 1912 is year 1, so that + // 1912 = 1 + yearOffset + // So yearOffset = 1911 + //m_EraInfo[0] = new EraInfo(1, new DateTime(1912, 1, 1).Ticks, 1911, 1, GregorianCalendar.MaxYear - 1911); + + // Initialize our era info. + internal static EraInfo[] taiwanLunisolarEraInfo = new EraInfo[] { + new EraInfo( 1, 1912, 1, 1, 1911, 1, GregorianCalendar.MaxYear - 1911) // era #, start year/month/day, yearOffset, minEraYear + }; + + internal GregorianCalendarHelper helper; + + internal const int MIN_LUNISOLAR_YEAR = 1912; + internal const int MAX_LUNISOLAR_YEAR = 2050; + + internal const int MIN_GREGORIAN_YEAR = 1912; + internal const int MIN_GREGORIAN_MONTH = 2; + internal const int MIN_GREGORIAN_DAY = 18; + + internal const int MAX_GREGORIAN_YEAR = 2051; + internal const int MAX_GREGORIAN_MONTH = 2; + internal const int MAX_GREGORIAN_DAY = 10; + + internal static DateTime minDate = new DateTime(MIN_GREGORIAN_YEAR, MIN_GREGORIAN_MONTH, MIN_GREGORIAN_DAY); + internal static DateTime maxDate = new DateTime((new DateTime(MAX_GREGORIAN_YEAR, MAX_GREGORIAN_MONTH, MAX_GREGORIAN_DAY, 23, 59, 59, 999)).Ticks + 9999); + + public override DateTime MinSupportedDateTime + { + get + { + return (minDate); + } + } + + + + public override DateTime MaxSupportedDateTime + { + get + { + return (maxDate); + } + } + + protected override int DaysInYearBeforeMinSupportedYear + { + get + { + // 1911 from ChineseLunisolarCalendar + return 384; + } + } + + private static readonly int[,] s_yinfo = + { + /*Y LM Lmon Lday DaysPerMonth D1 D2 D3 D4 D5 D6 D7 D8 D9 D10 D11 D12 D13 #Days + 1912 */ + { 0 , 2 , 18 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354 +1913 */{ 0 , 2 , 6 , 53840 },/* 30 30 29 30 29 29 30 29 29 30 29 30 0 354 +1914 */{ 5 , 1 , 26 , 54568 },/* 30 30 29 30 29 30 29 30 29 29 30 29 30 384 +1915 */{ 0 , 2 , 14 , 46400 },/* 30 29 30 30 29 30 29 30 29 30 29 29 0 354 +1916 */{ 0 , 2 , 3 , 54944 },/* 30 30 29 30 29 30 30 29 30 29 30 29 0 355 +1917 */{ 2 , 1 , 23 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 29 384 +1918 */{ 0 , 2 , 11 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355 +1919 */{ 7 , 2 , 1 , 18872 },/* 29 30 29 29 30 29 29 30 30 29 30 30 30 384 +1920 */{ 0 , 2 , 20 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354 +1921 */{ 0 , 2 , 8 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354 +1922 */{ 5 , 1 , 28 , 45656 },/* 30 29 30 30 29 29 30 29 29 30 29 30 30 384 +1923 */{ 0 , 2 , 16 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 0 354 +1924 */{ 0 , 2 , 5 , 27968 },/* 29 30 30 29 30 30 29 30 29 30 29 29 0 354 +1925 */{ 4 , 1 , 24 , 44456 },/* 30 29 30 29 30 30 29 30 30 29 30 29 30 385 +1926 */{ 0 , 2 , 13 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354 +1927 */{ 0 , 2 , 2 , 38256 },/* 30 29 29 30 29 30 29 30 29 30 30 30 0 355 +1928 */{ 2 , 1 , 23 , 18808 },/* 29 30 29 29 30 29 29 30 29 30 30 30 30 384 +1929 */{ 0 , 2 , 10 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354 +1930 */{ 6 , 1 , 30 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 29 383 +1931 */{ 0 , 2 , 17 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354 +1932 */{ 0 , 2 , 6 , 59984 },/* 30 30 30 29 30 29 30 29 29 30 29 30 0 355 +1933 */{ 5 , 1 , 26 , 27976 },/* 29 30 30 29 30 30 29 30 29 30 29 29 30 384 +1934 */{ 0 , 2 , 14 , 23248 },/* 29 30 29 30 30 29 30 29 30 30 29 30 0 355 +1935 */{ 0 , 2 , 4 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354 +1936 */{ 3 , 1 , 24 , 37744 },/* 30 29 29 30 29 29 30 30 29 30 30 30 29 384 +1937 */{ 0 , 2 , 11 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354 +1938 */{ 7 , 1 , 31 , 51560 },/* 30 30 29 29 30 29 29 30 29 30 30 29 30 384 +1939 */{ 0 , 2 , 19 , 51536 },/* 30 30 29 29 30 29 29 30 29 30 29 30 0 354 +1940 */{ 0 , 2 , 8 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354 +1941 */{ 6 , 1 , 27 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 29 384 +1942 */{ 0 , 2 , 15 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355 +1943 */{ 0 , 2 , 5 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354 +1944 */{ 4 , 1 , 25 , 43736 },/* 30 29 30 29 30 29 30 29 30 30 29 30 30 385 +1945 */{ 0 , 2 , 13 , 9680 },/* 29 29 30 29 29 30 29 30 30 30 29 30 0 354 +1946 */{ 0 , 2 , 2 , 37584 },/* 30 29 29 30 29 29 30 29 30 30 29 30 0 354 +1947 */{ 2 , 1 , 22 , 51544 },/* 30 30 29 29 30 29 29 30 29 30 29 30 30 384 +1948 */{ 0 , 2 , 10 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354 +1949 */{ 7 , 1 , 29 , 46248 },/* 30 29 30 30 29 30 29 29 30 29 30 29 30 384 +1950 */{ 0 , 2 , 17 , 27808 },/* 29 30 30 29 30 30 29 29 30 29 30 29 0 354 +1951 */{ 0 , 2 , 6 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355 +1952 */{ 5 , 1 , 27 , 21928 },/* 29 30 29 30 29 30 29 30 30 29 30 29 30 384 +1953 */{ 0 , 2 , 14 , 19872 },/* 29 30 29 29 30 30 29 30 30 29 30 29 0 354 +1954 */{ 0 , 2 , 3 , 42416 },/* 30 29 30 29 29 30 29 30 30 29 30 30 0 355 +1955 */{ 3 , 1 , 24 , 21176 },/* 29 30 29 30 29 29 30 29 30 29 30 30 30 384 +1956 */{ 0 , 2 , 12 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354 +1957 */{ 8 , 1 , 31 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 29 383 +1958 */{ 0 , 2 , 18 , 59728 },/* 30 30 30 29 30 29 29 30 29 30 29 30 0 355 +1959 */{ 0 , 2 , 8 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354 +1960 */{ 6 , 1 , 28 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 29 384 +1961 */{ 0 , 2 , 15 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 0 355 +1962 */{ 0 , 2 , 5 , 19296 },/* 29 30 29 29 30 29 30 30 29 30 30 29 0 354 +1963 */{ 4 , 1 , 25 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 29 384 +1964 */{ 0 , 2 , 13 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355 +1965 */{ 0 , 2 , 2 , 21088 },/* 29 30 29 30 29 29 30 29 29 30 30 29 0 353 +1966 */{ 3 , 1 , 21 , 59696 },/* 30 30 30 29 30 29 29 30 29 29 30 30 29 384 +1967 */{ 0 , 2 , 9 , 55632 },/* 30 30 29 30 30 29 29 30 29 30 29 30 0 355 +1968 */{ 7 , 1 , 30 , 23208 },/* 29 30 29 30 30 29 30 29 30 29 30 29 30 384 +1969 */{ 0 , 2 , 17 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354 +1970 */{ 0 , 2 , 6 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 0 355 +1971 */{ 5 , 1 , 27 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384 +1972 */{ 0 , 2 , 15 , 19152 },/* 29 30 29 29 30 29 30 29 30 30 29 30 0 354 +1973 */{ 0 , 2 , 3 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354 +1974 */{ 4 , 1 , 23 , 53864 },/* 30 30 29 30 29 29 30 29 29 30 30 29 30 384 +1975 */{ 0 , 2 , 11 , 53840 },/* 30 30 29 30 29 29 30 29 29 30 29 30 0 354 +1976 */{ 8 , 1 , 31 , 54568 },/* 30 30 29 30 29 30 29 30 29 29 30 29 30 384 +1977 */{ 0 , 2 , 18 , 46400 },/* 30 29 30 30 29 30 29 30 29 30 29 29 0 354 +1978 */{ 0 , 2 , 7 , 46752 },/* 30 29 30 30 29 30 30 29 30 29 30 29 0 355 +1979 */{ 6 , 1 , 28 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 29 384 +1980 */{ 0 , 2 , 16 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355 +1981 */{ 0 , 2 , 5 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354 +1982 */{ 4 , 1 , 25 , 42168 },/* 30 29 30 29 29 30 29 29 30 29 30 30 30 384 +1983 */{ 0 , 2 , 13 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354 +1984 */{ 10 , 2 , 2 , 45656 },/* 30 29 30 30 29 29 30 29 29 30 29 30 30 384 +1985 */{ 0 , 2 , 20 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 0 354 +1986 */{ 0 , 2 , 9 , 27968 },/* 29 30 30 29 30 30 29 30 29 30 29 29 0 354 +1987 */{ 6 , 1 , 29 , 44448 },/* 30 29 30 29 30 30 29 30 30 29 30 29 29 384 +1988 */{ 0 , 2 , 17 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 0 355 +1989 */{ 0 , 2 , 6 , 38256 },/* 30 29 29 30 29 30 29 30 29 30 30 30 0 355 +1990 */{ 5 , 1 , 27 , 18808 },/* 29 30 29 29 30 29 29 30 29 30 30 30 30 384 +1991 */{ 0 , 2 , 15 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354 +1992 */{ 0 , 2 , 4 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 0 354 +1993 */{ 3 , 1 , 23 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 29 383 +1994 */{ 0 , 2 , 10 , 59984 },/* 30 30 30 29 30 29 30 29 29 30 29 30 0 355 +1995 */{ 8 , 1 , 31 , 27432 },/* 29 30 30 29 30 29 30 30 29 29 30 29 30 384 +1996 */{ 0 , 2 , 19 , 23232 },/* 29 30 29 30 30 29 30 29 30 30 29 29 0 354 +1997 */{ 0 , 2 , 7 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 0 355 +1998 */{ 5 , 1 , 28 , 37736 },/* 30 29 29 30 29 29 30 30 29 30 30 29 30 384 +1999 */{ 0 , 2 , 16 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354 +2000 */{ 0 , 2 , 5 , 51552 },/* 30 30 29 29 30 29 29 30 29 30 30 29 0 354 +2001 */{ 4 , 1 , 24 , 54440 },/* 30 30 29 30 29 30 29 29 30 29 30 29 30 384 +2002 */{ 0 , 2 , 12 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354 +2003 */{ 0 , 2 , 1 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 0 355 +2004 */{ 2 , 1 , 22 , 23208 },/* 29 30 29 30 30 29 30 29 30 29 30 29 30 384 +2005 */{ 0 , 2 , 9 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354 +2006 */{ 7 , 1 , 29 , 43736 },/* 30 29 30 29 30 29 30 29 30 30 29 30 30 385 +2007 */{ 0 , 2 , 18 , 9680 },/* 29 29 30 29 29 30 29 30 30 30 29 30 0 354 +2008 */{ 0 , 2 , 7 , 37584 },/* 30 29 29 30 29 29 30 29 30 30 29 30 0 354 +2009 */{ 5 , 1 , 26 , 51544 },/* 30 30 29 29 30 29 29 30 29 30 29 30 30 384 +2010 */{ 0 , 2 , 14 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354 +2011 */{ 0 , 2 , 3 , 46240 },/* 30 29 30 30 29 30 29 29 30 29 30 29 0 354 +2012 */{ 4 , 1 , 23 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 29 384 +2013 */{ 0 , 2 , 10 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355 +2014 */{ 9 , 1 , 31 , 21928 },/* 29 30 29 30 29 30 29 30 30 29 30 29 30 384 +2015 */{ 0 , 2 , 19 , 19360 },/* 29 30 29 29 30 29 30 30 30 29 30 29 0 354 +2016 */{ 0 , 2 , 8 , 42416 },/* 30 29 30 29 29 30 29 30 30 29 30 30 0 355 +2017 */{ 6 , 1 , 28 , 21176 },/* 29 30 29 30 29 29 30 29 30 29 30 30 30 384 +2018 */{ 0 , 2 , 16 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354 +2019 */{ 0 , 2 , 5 , 43312 },/* 30 29 30 29 30 29 29 30 29 29 30 30 0 354 +2020 */{ 4 , 1 , 25 , 29864 },/* 29 30 30 30 29 30 29 29 30 29 30 29 30 384 +2021 */{ 0 , 2 , 12 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354 +2022 */{ 0 , 2 , 1 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355 +2023 */{ 2 , 1 , 22 , 19880 },/* 29 30 29 29 30 30 29 30 30 29 30 29 30 384 +2024 */{ 0 , 2 , 10 , 19296 },/* 29 30 29 29 30 29 30 30 29 30 30 29 0 354 +2025 */{ 6 , 1 , 29 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 29 384 +2026 */{ 0 , 2 , 17 , 42208 },/* 30 29 30 29 29 30 29 29 30 30 30 29 0 354 +2027 */{ 0 , 2 , 6 , 53856 },/* 30 30 29 30 29 29 30 29 29 30 30 29 0 354 +2028 */{ 5 , 1 , 26 , 59696 },/* 30 30 30 29 30 29 29 30 29 29 30 30 29 384 +2029 */{ 0 , 2 , 13 , 54576 },/* 30 30 29 30 29 30 29 30 29 29 30 30 0 355 +2030 */{ 0 , 2 , 3 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354 +2031 */{ 3 , 1 , 23 , 27472 },/* 29 30 30 29 30 29 30 30 29 30 29 30 29 384 +2032 */{ 0 , 2 , 11 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 0 355 +2033 */{ 11 , 1 , 31 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384 +2034 */{ 0 , 2 , 19 , 19152 },/* 29 30 29 29 30 29 30 29 30 30 29 30 0 354 +2035 */{ 0 , 2 , 8 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354 +2036 */{ 6 , 1 , 28 , 53848 },/* 30 30 29 30 29 29 30 29 29 30 29 30 30 384 +2037 */{ 0 , 2 , 15 , 53840 },/* 30 30 29 30 29 29 30 29 29 30 29 30 0 354 +2038 */{ 0 , 2 , 4 , 54560 },/* 30 30 29 30 29 30 29 30 29 29 30 29 0 354 +2039 */{ 5 , 1 , 24 , 55968 },/* 30 30 29 30 30 29 30 29 30 29 30 29 29 384 +2040 */{ 0 , 2 , 12 , 46496 },/* 30 29 30 30 29 30 29 30 30 29 30 29 0 355 +2041 */{ 0 , 2 , 1 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 0 355 +2042 */{ 2 , 1 , 22 , 19160 },/* 29 30 29 29 30 29 30 29 30 30 29 30 30 384 +2043 */{ 0 , 2 , 10 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354 +2044 */{ 7 , 1 , 30 , 42168 },/* 30 29 30 29 29 30 29 29 30 29 30 30 30 384 +2045 */{ 0 , 2 , 17 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354 +2046 */{ 0 , 2 , 6 , 43600 },/* 30 29 30 29 30 29 30 29 29 30 29 30 0 354 +2047 */{ 5 , 1 , 26 , 46376 },/* 30 29 30 30 29 30 29 30 29 29 30 29 30 384 +2048 */{ 0 , 2 , 14 , 27936 },/* 29 30 30 29 30 30 29 30 29 29 30 29 0 354 +2049 */{ 0 , 2 , 2 , 44448 },/* 30 29 30 29 30 30 29 30 30 29 30 29 0 355 +2050 */{ 3 , 1 , 23 , 21936 },/* 29 30 29 30 29 30 29 30 30 29 30 30 29 384 + */}; + + + internal override int MinCalendarYear + { + get + { + return (MIN_LUNISOLAR_YEAR); + } + } + + internal override int MaxCalendarYear + { + get + { + return (MAX_LUNISOLAR_YEAR); + } + } + + internal override DateTime MinDate + { + get + { + return (minDate); + } + } + + internal override DateTime MaxDate + { + get + { + return (maxDate); + } + } + + internal override EraInfo[] CalEraInfo + { + get + { + return (taiwanLunisolarEraInfo); + } + } + + internal override int GetYearInfo(int lunarYear, int index) + { + if ((lunarYear < MIN_LUNISOLAR_YEAR) || (lunarYear > MAX_LUNISOLAR_YEAR)) + { + throw new ArgumentOutOfRangeException( + "year", + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + MIN_LUNISOLAR_YEAR, + MAX_LUNISOLAR_YEAR)); + } + Contract.EndContractBlock(); + + return s_yinfo[lunarYear - MIN_LUNISOLAR_YEAR, index]; + } + + internal override int GetYear(int year, DateTime time) + { + return helper.GetYear(year, time); + } + + internal override int GetGregorianYear(int year, int era) + { + return helper.GetGregorianYear(year, era); + } + + public TaiwanLunisolarCalendar() + { + helper = new GregorianCalendarHelper(this, taiwanLunisolarEraInfo); + } + + public override int GetEra(DateTime time) + { + return (helper.GetEra(time)); + } + + internal override CalendarId BaseCalendarID + { + get + { + return (CalendarId.TAIWAN); + } + } + + internal override CalendarId ID + { + get + { + return (CalendarId.TAIWANLUNISOLAR); + } + } + + + + public override int[] Eras + { + get + { + return (helper.Eras); + } + } + } +} diff --git a/src/mscorlib/shared/System/Globalization/ThaiBuddhistCalendar.cs b/src/mscorlib/shared/System/Globalization/ThaiBuddhistCalendar.cs new file mode 100644 index 0000000000..9e6e30406c --- /dev/null +++ b/src/mscorlib/shared/System/Globalization/ThaiBuddhistCalendar.cs @@ -0,0 +1,236 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.Contracts; + +namespace System.Globalization +{ + /*=================================ThaiBuddhistCalendar========================== + ** + ** ThaiBuddhistCalendar is based on Gregorian calendar. Its year value has + ** an offset to the Gregorain calendar. + ** + ** Calendar support range: + ** Calendar Minimum Maximum + ** ========== ========== ========== + ** Gregorian 0001/01/01 9999/12/31 + ** Thai 0544/01/01 10542/12/31 + ============================================================================*/ + + [Serializable] + public class ThaiBuddhistCalendar : Calendar + { + // Initialize our era info. + internal static EraInfo[] thaiBuddhistEraInfo = new EraInfo[] { + new EraInfo( 1, 1, 1, 1, -543, 544, GregorianCalendar.MaxYear + 543) // era #, start year/month/day, yearOffset, minEraYear + }; + + // + // The era value for the current era. + // + + public const int ThaiBuddhistEra = 1; + + internal GregorianCalendarHelper helper; + + + public override DateTime MinSupportedDateTime + { + get + { + return (DateTime.MinValue); + } + } + + public override DateTime MaxSupportedDateTime + { + get + { + return (DateTime.MaxValue); + } + } + + public override CalendarAlgorithmType AlgorithmType + { + get + { + return CalendarAlgorithmType.SolarCalendar; + } + } + + public ThaiBuddhistCalendar() + { + helper = new GregorianCalendarHelper(this, thaiBuddhistEraInfo); + } + + internal override CalendarId ID + { + get + { + return (CalendarId.THAI); + } + } + + + public override DateTime AddMonths(DateTime time, int months) + { + return (helper.AddMonths(time, months)); + } + + + public override DateTime AddYears(DateTime time, int years) + { + return (helper.AddYears(time, years)); + } + + + public override int GetDaysInMonth(int year, int month, int era) + { + return (helper.GetDaysInMonth(year, month, era)); + } + + + public override int GetDaysInYear(int year, int era) + { + return (helper.GetDaysInYear(year, era)); + } + + + public override int GetDayOfMonth(DateTime time) + { + return (helper.GetDayOfMonth(time)); + } + + + public override DayOfWeek GetDayOfWeek(DateTime time) + { + return (helper.GetDayOfWeek(time)); + } + + + public override int GetDayOfYear(DateTime time) + { + return (helper.GetDayOfYear(time)); + } + + + public override int GetMonthsInYear(int year, int era) + { + return (helper.GetMonthsInYear(year, era)); + } + + + [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems. + public override int GetWeekOfYear(DateTime time, CalendarWeekRule rule, DayOfWeek firstDayOfWeek) + { + return (helper.GetWeekOfYear(time, rule, firstDayOfWeek)); + } + + + public override int GetEra(DateTime time) + { + return (helper.GetEra(time)); + } + + public override int GetMonth(DateTime time) + { + return (helper.GetMonth(time)); + } + + + public override int GetYear(DateTime time) + { + return (helper.GetYear(time)); + } + + + public override bool IsLeapDay(int year, int month, int day, int era) + { + return (helper.IsLeapDay(year, month, day, era)); + } + + + public override bool IsLeapYear(int year, int era) + { + return (helper.IsLeapYear(year, era)); + } + + // Returns the leap month in a calendar year of the specified era. This method returns 0 + // if this calendar does not have leap month, or this year is not a leap year. + // + + public override int GetLeapMonth(int year, int era) + { + return (helper.GetLeapMonth(year, era)); + } + + + public override bool IsLeapMonth(int year, int month, int era) + { + return (helper.IsLeapMonth(year, month, era)); + } + + + public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era) + { + return (helper.ToDateTime(year, month, day, hour, minute, second, millisecond, era)); + } + + + public override int[] Eras + { + get + { + return (helper.Eras); + } + } + + private const int DEFAULT_TWO_DIGIT_YEAR_MAX = 2572; + + + public override int TwoDigitYearMax + { + get + { + if (twoDigitYearMax == -1) + { + twoDigitYearMax = GetSystemTwoDigitYearSetting(ID, DEFAULT_TWO_DIGIT_YEAR_MAX); + } + return (twoDigitYearMax); + } + + set + { + VerifyWritable(); + if (value < 99 || value > helper.MaxYear) + { + throw new ArgumentOutOfRangeException( + "year", + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + 99, + helper.MaxYear)); + } + twoDigitYearMax = value; + } + } + + + public override int ToFourDigitYear(int year) + { + if (year < 0) + { + throw new ArgumentOutOfRangeException(nameof(year), + SR.ArgumentOutOfRange_NeedNonNegNum); + } + Contract.EndContractBlock(); + + return (helper.ToFourDigitYear(year, this.TwoDigitYearMax)); + } + } +} + diff --git a/src/mscorlib/shared/System/Globalization/TimeSpanStyles.cs b/src/mscorlib/shared/System/Globalization/TimeSpanStyles.cs new file mode 100644 index 0000000000..68a47bcbe6 --- /dev/null +++ b/src/mscorlib/shared/System/Globalization/TimeSpanStyles.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Globalization +{ + [Flags] + public enum TimeSpanStyles + { + None = 0x00000000, + AssumeNegative = 0x00000001, + } +} diff --git a/src/mscorlib/shared/System/Globalization/UmAlQuraCalendar.cs b/src/mscorlib/shared/System/Globalization/UmAlQuraCalendar.cs new file mode 100644 index 0000000000..b7ba6d0112 --- /dev/null +++ b/src/mscorlib/shared/System/Globalization/UmAlQuraCalendar.cs @@ -0,0 +1,865 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Diagnostics.Contracts; + +namespace System.Globalization +{ + /* + ** Calendar support range: + ** Calendar Minimum Maximum + ** ========== ========== ========== + ** Gregorian 1900/04/30 2077/05/13 + ** UmAlQura 1318/01/01 1500/12/30 + */ + + [Serializable] + public partial class UmAlQuraCalendar : Calendar + { + internal const int MinCalendarYear = 1318; + internal const int MaxCalendarYear = 1500; + + internal struct DateMapping + { + internal DateMapping(int MonthsLengthFlags, int GYear, int GMonth, int GDay) + { + HijriMonthsLengthFlags = MonthsLengthFlags; + GregorianDate = new DateTime(GYear, GMonth, GDay); + } + internal int HijriMonthsLengthFlags; + internal DateTime GregorianDate; + } + + private static readonly DateMapping[] s_hijriYearInfo = InitDateMapping(); + + private static DateMapping[] InitDateMapping() + { + short[] rawData = new short[] + { + //These data is taken from Tables/Excel/UmAlQura.xls please make sure that the two places are in sync + /* DaysPerM GY GM GD D1 D2 D3 D4 D5 D6 D7 D8 D9 D10 D11 D12 + 1318*/0x02EA, 1900, 4, 30,/* 0 1 0 1 0 1 1 1 0 1 0 0 4/30/1900 + 1319*/0x06E9, 1901, 4, 19,/* 1 0 0 1 0 1 1 1 0 1 1 0 4/19/1901 + 1320*/0x0ED2, 1902, 4, 9,/* 0 1 0 0 1 0 1 1 0 1 1 1 4/9/1902 + 1321*/0x0EA4, 1903, 3, 30,/* 0 0 1 0 0 1 0 1 0 1 1 1 3/30/1903 + 1322*/0x0D4A, 1904, 3, 18,/* 0 1 0 1 0 0 1 0 1 0 1 1 3/18/1904 + 1323*/0x0A96, 1905, 3, 7,/* 0 1 1 0 1 0 0 1 0 1 0 1 3/7/1905 + 1324*/0x0536, 1906, 2, 24,/* 0 1 1 0 1 1 0 0 1 0 1 0 2/24/1906 + 1325*/0x0AB5, 1907, 2, 13,/* 1 0 1 0 1 1 0 1 0 1 0 1 2/13/1907 + 1326*/0x0DAA, 1908, 2, 3,/* 0 1 0 1 0 1 0 1 1 0 1 1 2/3/1908 + 1327*/0x0BA4, 1909, 1, 23,/* 0 0 1 0 0 1 0 1 1 1 0 1 1/23/1909 + 1328*/0x0B49, 1910, 1, 12,/* 1 0 0 1 0 0 1 0 1 1 0 1 1/12/1910 + 1329*/0x0A93, 1911, 1, 1,/* 1 1 0 0 1 0 0 1 0 1 0 1 1/1/1911 + 1330*/0x052B, 1911, 12, 21,/* 1 1 0 1 0 1 0 0 1 0 1 0 12/21/1911 + 1331*/0x0A57, 1912, 12, 9,/* 1 1 1 0 1 0 1 0 0 1 0 1 12/9/1912 + 1332*/0x04B6, 1913, 11, 29,/* 0 1 1 0 1 1 0 1 0 0 1 0 11/29/1913 + 1333*/0x0AB5, 1914, 11, 18,/* 1 0 1 0 1 1 0 1 0 1 0 1 11/18/1914 + 1334*/0x05AA, 1915, 11, 8,/* 0 1 0 1 0 1 0 1 1 0 1 0 11/8/1915 + 1335*/0x0D55, 1916, 10, 27,/* 1 0 1 0 1 0 1 0 1 0 1 1 10/27/1916 + 1336*/0x0D2A, 1917, 10, 17,/* 0 1 0 1 0 1 0 0 1 0 1 1 10/17/1917 + 1337*/0x0A56, 1918, 10, 6,/* 0 1 1 0 1 0 1 0 0 1 0 1 10/6/1918 + 1338*/0x04AE, 1919, 9, 25,/* 0 1 1 1 0 1 0 1 0 0 1 0 9/25/1919 + 1339*/0x095D, 1920, 9, 13,/* 1 0 1 1 1 0 1 0 1 0 0 1 9/13/1920 + 1340*/0x02EC, 1921, 9, 3,/* 0 0 1 1 0 1 1 1 0 1 0 0 9/3/1921 + 1341*/0x06D5, 1922, 8, 23,/* 1 0 1 0 1 0 1 1 0 1 1 0 8/23/1922 + 1342*/0x06AA, 1923, 8, 13,/* 0 1 0 1 0 1 0 1 0 1 1 0 8/13/1923 + 1343*/0x0555, 1924, 8, 1,/* 1 0 1 0 1 0 1 0 1 0 1 0 8/1/1924 + 1344*/0x04AB, 1925, 7, 21,/* 1 1 0 1 0 1 0 1 0 0 1 0 7/21/1925 + 1345*/0x095B, 1926, 7, 10,/* 1 1 0 1 1 0 1 0 1 0 0 1 7/10/1926 + 1346*/0x02BA, 1927, 6, 30,/* 0 1 0 1 1 1 0 1 0 1 0 0 6/30/1927 + 1347*/0x0575, 1928, 6, 18,/* 1 0 1 0 1 1 1 0 1 0 1 0 6/18/1928 + 1348*/0x0BB2, 1929, 6, 8,/* 0 1 0 0 1 1 0 1 1 1 0 1 6/8/1929 + 1349*/0x0764, 1930, 5, 29,/* 0 0 1 0 0 1 1 0 1 1 1 0 5/29/1930 + 1350*/0x0749, 1931, 5, 18,/* 1 0 0 1 0 0 1 0 1 1 1 0 5/18/1931 + 1351*/0x0655, 1932, 5, 6,/* 1 0 1 0 1 0 1 0 0 1 1 0 5/6/1932 + 1352*/0x02AB, 1933, 4, 25,/* 1 1 0 1 0 1 0 1 0 1 0 0 4/25/1933 + 1353*/0x055B, 1934, 4, 14,/* 1 1 0 1 1 0 1 0 1 0 1 0 4/14/1934 + 1354*/0x0ADA, 1935, 4, 4,/* 0 1 0 1 1 0 1 1 0 1 0 1 4/4/1935 + 1355*/0x06D4, 1936, 3, 24,/* 0 0 1 0 1 0 1 1 0 1 1 0 3/24/1936 + 1356*/0x0EC9, 1937, 3, 13,/* 1 0 0 1 0 0 1 1 0 1 1 1 3/13/1937 + 1357*/0x0D92, 1938, 3, 3,/* 0 1 0 0 1 0 0 1 1 0 1 1 3/3/1938 + 1358*/0x0D25, 1939, 2, 20,/* 1 0 1 0 0 1 0 0 1 0 1 1 2/20/1939 + 1359*/0x0A4D, 1940, 2, 9,/* 1 0 1 1 0 0 1 0 0 1 0 1 2/9/1940 + 1360*/0x02AD, 1941, 1, 28,/* 1 0 1 1 0 1 0 1 0 1 0 0 1/28/1941 + 1361*/0x056D, 1942, 1, 17,/* 1 0 1 1 0 1 1 0 1 0 1 0 1/17/1942 + 1362*/0x0B6A, 1943, 1, 7,/* 0 1 0 1 0 1 1 0 1 1 0 1 1/7/1943 + 1363*/0x0B52, 1943, 12, 28,/* 0 1 0 0 1 0 1 0 1 1 0 1 12/28/1943 + 1364*/0x0AA5, 1944, 12, 16,/* 1 0 1 0 0 1 0 1 0 1 0 1 12/16/1944 + 1365*/0x0A4B, 1945, 12, 5,/* 1 1 0 1 0 0 1 0 0 1 0 1 12/5/1945 + 1366*/0x0497, 1946, 11, 24,/* 1 1 1 0 1 0 0 1 0 0 1 0 11/24/1946 + 1367*/0x0937, 1947, 11, 13,/* 1 1 1 0 1 1 0 0 1 0 0 1 11/13/1947 + 1368*/0x02B6, 1948, 11, 2,/* 0 1 1 0 1 1 0 1 0 1 0 0 11/2/1948 + 1369*/0x0575, 1949, 10, 22,/* 1 0 1 0 1 1 1 0 1 0 1 0 10/22/1949 + 1370*/0x0D6A, 1950, 10, 12,/* 0 1 0 1 0 1 1 0 1 0 1 1 10/12/1950 + 1371*/0x0D52, 1951, 10, 2,/* 0 1 0 0 1 0 1 0 1 0 1 1 10/2/1951 + 1372*/0x0A96, 1952, 9, 20,/* 0 1 1 0 1 0 0 1 0 1 0 1 9/20/1952 + 1373*/0x092D, 1953, 9, 9,/* 1 0 1 1 0 1 0 0 1 0 0 1 9/9/1953 + 1374*/0x025D, 1954, 8, 29,/* 1 0 1 1 1 0 1 0 0 1 0 0 8/29/1954 + 1375*/0x04DD, 1955, 8, 18,/* 1 0 1 1 1 0 1 1 0 0 1 0 8/18/1955 + 1376*/0x0ADA, 1956, 8, 7,/* 0 1 0 1 1 0 1 1 0 1 0 1 8/7/1956 + 1377*/0x05D4, 1957, 7, 28,/* 0 0 1 0 1 0 1 1 1 0 1 0 7/28/1957 + 1378*/0x0DA9, 1958, 7, 17,/* 1 0 0 1 0 1 0 1 1 0 1 1 7/17/1958 + 1379*/0x0D52, 1959, 7, 7,/* 0 1 0 0 1 0 1 0 1 0 1 1 7/7/1959 + 1380*/0x0AAA, 1960, 6, 25,/* 0 1 0 1 0 1 0 1 0 1 0 1 6/25/1960 + 1381*/0x04D6, 1961, 6, 14,/* 0 1 1 0 1 0 1 1 0 0 1 0 6/14/1961 + 1382*/0x09B6, 1962, 6, 3,/* 0 1 1 0 1 1 0 1 1 0 0 1 6/3/1962 + 1383*/0x0374, 1963, 5, 24,/* 0 0 1 0 1 1 1 0 1 1 0 0 5/24/1963 + 1384*/0x0769, 1964, 5, 12,/* 1 0 0 1 0 1 1 0 1 1 1 0 5/12/1964 + 1385*/0x0752, 1965, 5, 2,/* 0 1 0 0 1 0 1 0 1 1 1 0 5/2/1965 + 1386*/0x06A5, 1966, 4, 21,/* 1 0 1 0 0 1 0 1 0 1 1 0 4/21/1966 + 1387*/0x054B, 1967, 4, 10,/* 1 1 0 1 0 0 1 0 1 0 1 0 4/10/1967 + 1388*/0x0AAB, 1968, 3, 29,/* 1 1 0 1 0 1 0 1 0 1 0 1 3/29/1968 + 1389*/0x055A, 1969, 3, 19,/* 0 1 0 1 1 0 1 0 1 0 1 0 3/19/1969 + 1390*/0x0AD5, 1970, 3, 8,/* 1 0 1 0 1 0 1 1 0 1 0 1 3/8/1970 + 1391*/0x0DD2, 1971, 2, 26,/* 0 1 0 0 1 0 1 1 1 0 1 1 2/26/1971 + 1392*/0x0DA4, 1972, 2, 16,/* 0 0 1 0 0 1 0 1 1 0 1 1 2/16/1972 + 1393*/0x0D49, 1973, 2, 4,/* 1 0 0 1 0 0 1 0 1 0 1 1 2/4/1973 + 1394*/0x0A95, 1974, 1, 24,/* 1 0 1 0 1 0 0 1 0 1 0 1 1/24/1974 + 1395*/0x052D, 1975, 1, 13,/* 1 0 1 1 0 1 0 0 1 0 1 0 1/13/1975 + 1396*/0x0A5D, 1976, 1, 2,/* 1 0 1 1 1 0 1 0 0 1 0 1 1/2/1976 + 1397*/0x055A, 1976, 12, 22,/* 0 1 0 1 1 0 1 0 1 0 1 0 12/22/1976 + 1398*/0x0AD5, 1977, 12, 11,/* 1 0 1 0 1 0 1 1 0 1 0 1 12/11/1977 + 1399*/0x06AA, 1978, 12, 1,/* 0 1 0 1 0 1 0 1 0 1 1 0 12/1/1978 + 1400*/0x0695, 1979, 11, 20,/* 1 0 1 0 1 0 0 1 0 1 1 0 11/20/1979 + 1401*/0x052B, 1980, 11, 8,/* 1 1 0 1 0 1 0 0 1 0 1 0 11/8/1980 + 1402*/0x0A57, 1981, 10, 28,/* 1 1 1 0 1 0 1 0 0 1 0 1 10/28/1981 + 1403*/0x04AE, 1982, 10, 18,/* 0 1 1 1 0 1 0 1 0 0 1 0 10/18/1982 + 1404*/0x0976, 1983, 10, 7,/* 0 1 1 0 1 1 1 0 1 0 0 1 10/7/1983 + 1405*/0x056C, 1984, 9, 26,/* 0 0 1 1 0 1 1 0 1 0 1 0 9/26/1984 + 1406*/0x0B55, 1985, 9, 15,/* 1 0 1 0 1 0 1 0 1 1 0 1 9/15/1985 + 1407*/0x0AAA, 1986, 9, 5,/* 0 1 0 1 0 1 0 1 0 1 0 1 9/5/1986 + 1408*/0x0A55, 1987, 8, 25,/* 1 0 1 0 1 0 1 0 0 1 0 1 8/25/1987 + 1409*/0x04AD, 1988, 8, 13,/* 1 0 1 1 0 1 0 1 0 0 1 0 8/13/1988 + 1410*/0x095D, 1989, 8, 2,/* 1 0 1 1 1 0 1 0 1 0 0 1 8/2/1989 + 1411*/0x02DA, 1990, 7, 23,/* 0 1 0 1 1 0 1 1 0 1 0 0 7/23/1990 + 1412*/0x05D9, 1991, 7, 12,/* 1 0 0 1 1 0 1 1 1 0 1 0 7/12/1991 + 1413*/0x0DB2, 1992, 7, 1,/* 0 1 0 0 1 1 0 1 1 0 1 1 7/1/1992 + 1414*/0x0BA4, 1993, 6, 21,/* 0 0 1 0 0 1 0 1 1 1 0 1 6/21/1993 + 1415*/0x0B4A, 1994, 6, 10,/* 0 1 0 1 0 0 1 0 1 1 0 1 6/10/1994 + 1416*/0x0A55, 1995, 5, 30,/* 1 0 1 0 1 0 1 0 0 1 0 1 5/30/1995 + 1417*/0x02B5, 1996, 5, 18,/* 1 0 1 0 1 1 0 1 0 1 0 0 5/18/1996 + 1418*/0x0575, 1997, 5, 7,/* 1 0 1 0 1 1 1 0 1 0 1 0 5/7/1997 + 1419*/0x0B6A, 1998, 4, 27,/* 0 1 0 1 0 1 1 0 1 1 0 1 4/27/1998 + 1420*/0x0BD2, 1999, 4, 17,/* 0 1 0 0 1 0 1 1 1 1 0 1 4/17/1999 + 1421*/0x0BC4, 2000, 4, 6,/* 0 0 1 0 0 0 1 1 1 1 0 1 4/6/2000 + 1422*/0x0B89, 2001, 3, 26,/* 1 0 0 1 0 0 0 1 1 1 0 1 3/26/2001 + 1423*/0x0A95, 2002, 3, 15,/* 1 0 1 0 1 0 0 1 0 1 0 1 3/15/2002 + 1424*/0x052D, 2003, 3, 4,/* 1 0 1 1 0 1 0 0 1 0 1 0 3/4/2003 + 1425*/0x05AD, 2004, 2, 21,/* 1 0 1 1 0 1 0 1 1 0 1 0 2/21/2004 + 1426*/0x0B6A, 2005, 2, 10,/* 0 1 0 1 0 1 1 0 1 1 0 1 2/10/2005 + 1427*/0x06D4, 2006, 1, 31,/* 0 0 1 0 1 0 1 1 0 1 1 0 1/31/2006 + 1428*/0x0DC9, 2007, 1, 20,/* 1 0 0 1 0 0 1 1 1 0 1 1 1/20/2007 + 1429*/0x0D92, 2008, 1, 10,/* 0 1 0 0 1 0 0 1 1 0 1 1 1/10/2008 + 1430*/0x0AA6, 2008, 12, 29,/* 0 1 1 0 0 1 0 1 0 1 0 1 12/29/2008 + 1431*/0x0956, 2009, 12, 18,/* 0 1 1 0 1 0 1 0 1 0 0 1 12/18/2009 + 1432*/0x02AE, 2010, 12, 7,/* 0 1 1 1 0 1 0 1 0 1 0 0 12/7/2010 + 1433*/0x056D, 2011, 11, 26,/* 1 0 1 1 0 1 1 0 1 0 1 0 11/26/2011 + 1434*/0x036A, 2012, 11, 15,/* 0 1 0 1 0 1 1 0 1 1 0 0 11/15/2012 + 1435*/0x0B55, 2013, 11, 4,/* 1 0 1 0 1 0 1 0 1 1 0 1 11/4/2013 + 1436*/0x0AAA, 2014, 10, 25,/* 0 1 0 1 0 1 0 1 0 1 0 1 10/25/2014 + 1437*/0x094D, 2015, 10, 14,/* 1 0 1 1 0 0 1 0 1 0 0 1 10/14/2015 + 1438*/0x049D, 2016, 10, 2,/* 1 0 1 1 1 0 0 1 0 0 1 0 10/2/2016 + 1439*/0x095D, 2017, 9, 21,/* 1 0 1 1 1 0 1 0 1 0 0 1 9/21/2017 + 1440*/0x02BA, 2018, 9, 11,/* 0 1 0 1 1 1 0 1 0 1 0 0 9/11/2018 + 1441*/0x05B5, 2019, 8, 31,/* 1 0 1 0 1 1 0 1 1 0 1 0 8/31/2019 + 1442*/0x05AA, 2020, 8, 20,/* 0 1 0 1 0 1 0 1 1 0 1 0 8/20/2020 + 1443*/0x0D55, 2021, 8, 9,/* 1 0 1 0 1 0 1 0 1 0 1 1 8/9/2021 + 1444*/0x0A9A, 2022, 7, 30,/* 0 1 0 1 1 0 0 1 0 1 0 1 7/30/2022 + 1445*/0x092E, 2023, 7, 19,/* 0 1 1 1 0 1 0 0 1 0 0 1 7/19/2023 + 1446*/0x026E, 2024, 7, 7,/* 0 1 1 1 0 1 1 0 0 1 0 0 7/7/2024 + 1447*/0x055D, 2025, 6, 26,/* 1 0 1 1 1 0 1 0 1 0 1 0 6/26/2025 + 1448*/0x0ADA, 2026, 6, 16,/* 0 1 0 1 1 0 1 1 0 1 0 1 6/16/2026 + 1449*/0x06D4, 2027, 6, 6,/* 0 0 1 0 1 0 1 1 0 1 1 0 6/6/2027 + 1450*/0x06A5, 2028, 5, 25,/* 1 0 1 0 0 1 0 1 0 1 1 0 5/25/2028 + 1451*/0x054B, 2029, 5, 14,/* 1 1 0 1 0 0 1 0 1 0 1 0 5/14/2029 + 1452*/0x0A97, 2030, 5, 3,/* 1 1 1 0 1 0 0 1 0 1 0 1 5/3/2030 + 1453*/0x054E, 2031, 4, 23,/* 0 1 1 1 0 0 1 0 1 0 1 0 4/23/2031 + 1454*/0x0AAE, 2032, 4, 11,/* 0 1 1 1 0 1 0 1 0 1 0 1 4/11/2032 + 1455*/0x05AC, 2033, 4, 1,/* 0 0 1 1 0 1 0 1 1 0 1 0 4/1/2033 + 1456*/0x0BA9, 2034, 3, 21,/* 1 0 0 1 0 1 0 1 1 1 0 1 3/21/2034 + 1457*/0x0D92, 2035, 3, 11,/* 0 1 0 0 1 0 0 1 1 0 1 1 3/11/2035 + 1458*/0x0B25, 2036, 2, 28,/* 1 0 1 0 0 1 0 0 1 1 0 1 2/28/2036 + 1459*/0x064B, 2037, 2, 16,/* 1 1 0 1 0 0 1 0 0 1 1 0 2/16/2037 + 1460*/0x0CAB, 2038, 2, 5,/* 1 1 0 1 0 1 0 1 0 0 1 1 2/5/2038 + 1461*/0x055A, 2039, 1, 26,/* 0 1 0 1 1 0 1 0 1 0 1 0 1/26/2039 + 1462*/0x0B55, 2040, 1, 15,/* 1 0 1 0 1 0 1 0 1 1 0 1 1/15/2040 + 1463*/0x06D2, 2041, 1, 4,/* 0 1 0 0 1 0 1 1 0 1 1 0 1/4/2041 + 1464*/0x0EA5, 2041, 12, 24,/* 1 0 1 0 0 1 0 1 0 1 1 1 12/24/2041 + 1465*/0x0E4A, 2042, 12, 14,/* 0 1 0 1 0 0 1 0 0 1 1 1 12/14/2042 + 1466*/0x0A95, 2043, 12, 3,/* 1 0 1 0 1 0 0 1 0 1 0 1 12/3/2043 + 1467*/0x052D, 2044, 11, 21,/* 1 0 1 1 0 1 0 0 1 0 1 0 11/21/2044 + 1468*/0x0AAD, 2045, 11, 10,/* 1 0 1 1 0 1 0 1 0 1 0 1 11/10/2045 + 1469*/0x036C, 2046, 10, 31,/* 0 0 1 1 0 1 1 0 1 1 0 0 10/31/2046 + 1470*/0x0759, 2047, 10, 20,/* 1 0 0 1 1 0 1 0 1 1 1 0 10/20/2047 + 1471*/0x06D2, 2048, 10, 9,/* 0 1 0 0 1 0 1 1 0 1 1 0 10/9/2048 + 1472*/0x0695, 2049, 9, 28,/* 1 0 1 0 1 0 0 1 0 1 1 0 9/28/2049 + 1473*/0x052D, 2050, 9, 17,/* 1 0 1 1 0 1 0 0 1 0 1 0 9/17/2050 + 1474*/0x0A5B, 2051, 9, 6,/* 1 1 0 1 1 0 1 0 0 1 0 1 9/6/2051 + 1475*/0x04BA, 2052, 8, 26,/* 0 1 0 1 1 1 0 1 0 0 1 0 8/26/2052 + 1476*/0x09BA, 2053, 8, 15,/* 0 1 0 1 1 1 0 1 1 0 0 1 8/15/2053 + 1477*/0x03B4, 2054, 8, 5,/* 0 0 1 0 1 1 0 1 1 1 0 0 8/5/2054 + 1478*/0x0B69, 2055, 7, 25,/* 1 0 0 1 0 1 1 0 1 1 0 1 7/25/2055 + 1479*/0x0B52, 2056, 7, 14,/* 0 1 0 0 1 0 1 0 1 1 0 1 7/14/2056 + 1480*/0x0AA6, 2057, 7, 3,/* 0 1 1 0 0 1 0 1 0 1 0 1 7/3/2057 + 1481*/0x04B6, 2058, 6, 22,/* 0 1 1 0 1 1 0 1 0 0 1 0 6/22/2058 + 1482*/0x096D, 2059, 6, 11,/* 1 0 1 1 0 1 1 0 1 0 0 1 6/11/2059 + 1483*/0x02EC, 2060, 5, 31,/* 0 0 1 1 0 1 1 1 0 1 0 0 5/31/2060 + 1484*/0x06D9, 2061, 5, 20,/* 1 0 0 1 1 0 1 1 0 1 1 0 5/20/2061 + 1485*/0x0EB2, 2062, 5, 10,/* 0 1 0 0 1 1 0 1 0 1 1 1 5/10/2062 + 1486*/0x0D54, 2063, 4, 30,/* 0 0 1 0 1 0 1 0 1 0 1 1 4/30/2063 + 1487*/0x0D2A, 2064, 4, 18,/* 0 1 0 1 0 1 0 0 1 0 1 1 4/18/2064 + 1488*/0x0A56, 2065, 4, 7,/* 0 1 1 0 1 0 1 0 0 1 0 1 4/7/2065 + 1489*/0x04AE, 2066, 3, 27,/* 0 1 1 1 0 1 0 1 0 0 1 0 3/27/2066 + 1490*/0x096D, 2067, 3, 16,/* 1 0 1 1 0 1 1 0 1 0 0 1 3/16/2067 + 1491*/0x0D6A, 2068, 3, 5,/* 0 1 0 1 0 1 1 0 1 0 1 1 3/5/2068 + 1492*/0x0B54, 2069, 2, 23,/* 0 0 1 0 1 0 1 0 1 1 0 1 2/23/2069 + 1493*/0x0B29, 2070, 2, 12,/* 1 0 0 1 0 1 0 0 1 1 0 1 2/12/2070 + 1494*/0x0A93, 2071, 2, 1,/* 1 1 0 0 1 0 0 1 0 1 0 1 2/1/2071 + 1495*/0x052B, 2072, 1, 21,/* 1 1 0 1 0 1 0 0 1 0 1 0 1/21/2072 + 1496*/0x0A57, 2073, 1, 9,/* 1 1 1 0 1 0 1 0 0 1 0 1 1/9/2073 + 1497*/0x0536, 2073, 12, 30,/* 0 1 1 0 1 1 0 0 1 0 1 0 12/30/2073 + 1498*/0x0AB5, 2074, 12, 19,/* 1 0 1 0 1 1 0 1 0 1 0 1 12/19/2074 + 1499*/0x06AA, 2075, 12, 9,/* 0 1 0 1 0 1 0 1 0 1 1 0 12/9/2075 + 1500*/0x0E93, 2076, 11, 27,/* 1 1 0 0 1 0 0 1 0 1 1 1 11/27/2076 + 1501*/ 0, 2077, 11, 17,/* 0 0 0 0 0 0 0 0 0 0 0 0 11/17/2077 + */ }; + // Direct inline initialization of DateMapping array would produce a lot of code bloat. + + // We take advantage of C# compiler compiles inline initialization of primitive type array into very compact code. + // So we start with raw data stored in primitive type array, and initialize the DateMapping out of it + + DateMapping[] mapping = new DateMapping[rawData.Length / 4]; + for (int i = 0; i < mapping.Length; i++) + mapping[i] = new DateMapping(rawData[i * 4], rawData[i * 4 + 1], rawData[i * 4 + 2], rawData[i * 4 + 3]); + return mapping; + } + + public const int UmAlQuraEra = 1; + + internal const int DateCycle = 30; + internal const int DatePartYear = 0; + internal const int DatePartDayOfYear = 1; + internal const int DatePartMonth = 2; + internal const int DatePartDay = 3; + + + // This is the minimal Gregorian date that we support in the UmAlQuraCalendar. + internal static DateTime minDate = new DateTime(1900, 4, 30); + internal static DateTime maxDate = new DateTime((new DateTime(2077, 11, 16, 23, 59, 59, 999)).Ticks + 9999); + + public override DateTime MinSupportedDateTime + { + get + { + return (minDate); + } + } + + public override DateTime MaxSupportedDateTime + { + get + { + return (maxDate); + } + } + + public override CalendarAlgorithmType AlgorithmType + { + get + { + return CalendarAlgorithmType.LunarCalendar; + } + } + + public UmAlQuraCalendar() + { + } + + internal override CalendarId BaseCalendarID + { + get + { + return (CalendarId.HIJRI); + } + } + + internal override CalendarId ID + { + get + { + return (CalendarId.UMALQURA); + } + } + + protected override int DaysInYearBeforeMinSupportedYear + { + get + { + // HijriCalendar has same number of days as UmAlQuraCalendar for any given year + // HijriCalendar says year 1317 has 355 days. + return 355; + } + } + + /*==========================ConvertHijriToGregorian========================== + ** Purpose: convert Hdate(year,month,day) to Gdate(year,month,day) + ** Arguments: + ** Input/Ouput: Hijrah date: year:yh, month:mh, day:dh + ** Output: Gregorian date: year:yg, month:mg, day:dg , day of week:dayweek + ** and returns flag found:1 not found:0 + =========================ConvertHijriToGregorian============================*/ + private static void ConvertHijriToGregorian(int HijriYear, int HijriMonth, int HijriDay, ref int yg, ref int mg, ref int dg) + { + Debug.Assert((HijriYear >= MinCalendarYear) && (HijriYear <= MaxCalendarYear), "Hijri year is out of range."); + Debug.Assert(HijriMonth >= 1, "Hijri month is out of range."); + Debug.Assert(HijriDay >= 1, "Hijri day is out of range."); + int index, b, nDays = HijriDay - 1; + DateTime dt; + + + index = HijriYear - MinCalendarYear; + dt = s_hijriYearInfo[index].GregorianDate; + + + b = s_hijriYearInfo[index].HijriMonthsLengthFlags; + + for (int m = 1; m < HijriMonth; m++) + { + nDays = nDays + 29 + (b & 1); /* Add the months lengths before mh */ + b = b >> 1; + } + + dt = dt.AddDays(nDays); + yg = dt.Year; + mg = dt.Month; + dg = dt.Day; + } + + /*=================================GetAbsoluteDateUmAlQura========================== + **Action: Gets the Absolute date for the given UmAlQura date. The absolute date means + ** the number of days from January 1st, 1 A.D. + **Returns: + **Arguments: + **Exceptions: + ============================================================================*/ + private static long GetAbsoluteDateUmAlQura(int year, int month, int day) + { + //Caller should check the validaty of year, month and day. + + int yg = 0, mg = 0, dg = 0; + ConvertHijriToGregorian(year, month, day, ref yg, ref mg, ref dg); + return GregorianCalendar.GetAbsoluteDate(yg, mg, dg); + } + + internal static void CheckTicksRange(long ticks) + { + if (ticks < minDate.Ticks || ticks > maxDate.Ticks) + { + throw new ArgumentOutOfRangeException( + "time", + String.Format( + CultureInfo.InvariantCulture, + SR.ArgumentOutOfRange_CalendarRange, + minDate, + maxDate)); + } + } + + internal static void CheckEraRange(int era) + { + if (era != CurrentEra && era != UmAlQuraEra) + { + throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue); + } + } + + internal static void CheckYearRange(int year, int era) + { + CheckEraRange(era); + if (year < MinCalendarYear || year > MaxCalendarYear) + { + throw new ArgumentOutOfRangeException( + nameof(year), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + MinCalendarYear, + MaxCalendarYear)); + } + } + + internal static void CheckYearMonthRange(int year, int month, int era) + { + CheckYearRange(year, era); + if (month < 1 || month > 12) + { + throw new ArgumentOutOfRangeException(nameof(month), SR.ArgumentOutOfRange_Month); + } + } + + /*========================ConvertGregorianToHijri============================ + ** Purpose: convert DateTime to Hdate(year,month,day) + ** Arguments: + ** Input: DateTime + ** Output: Hijrah date: year:yh, month:mh, day:dh + ============================================================================*/ + private static void ConvertGregorianToHijri(DateTime time, ref int HijriYear, ref int HijriMonth, ref int HijriDay) + { + int index, b, DaysPerThisMonth; + double nDays; + TimeSpan ts; + int yh1 = 0, mh1 = 0, dh1 = 0; + + Debug.Assert((time.Ticks >= minDate.Ticks) && (time.Ticks <= maxDate.Ticks), "Gregorian date is out of range."); + + // Find the index where we should start our search by quessing the Hijri year that we will be in HijriYearInfo. + // A Hijri year is 354 or 355 days. Use 355 days so that we will search from a lower index. + + index = (int)((time.Ticks - minDate.Ticks) / Calendar.TicksPerDay) / 355; + do + { + } while (time.CompareTo(s_hijriYearInfo[++index].GregorianDate) > 0); //while greater + + if (time.CompareTo(s_hijriYearInfo[index].GregorianDate) != 0) + { + index--; + } + + ts = time.Subtract(s_hijriYearInfo[index].GregorianDate); + yh1 = index + MinCalendarYear; + + mh1 = 1; + dh1 = 1; + nDays = ts.TotalDays; + b = s_hijriYearInfo[index].HijriMonthsLengthFlags; + DaysPerThisMonth = 29 + (b & 1); + + while (nDays >= DaysPerThisMonth) + { + nDays -= DaysPerThisMonth; + b = b >> 1; + DaysPerThisMonth = 29 + (b & 1); + mh1++; + } + dh1 += (int)nDays; + + HijriDay = dh1; + HijriMonth = mh1; + HijriYear = yh1; + } + + /*=================================GetDatePart========================== + **Action: Returns a given date part of this <i>DateTime</i>. This method is used + ** to compute the year, day-of-year, month, or day part. + **Returns: + **Arguments: + **Exceptions: ArgumentException if part is incorrect. + **Notes: + ** First, we get the absolute date (the number of days from January 1st, 1 A.C) for the given ticks. + ** Use the formula (((AbsoluteDate - 226894) * 33) / (33 * 365 + 8)) + 1, we can a rough value for the UmAlQura year. + ** In order to get the exact UmAlQura year, we compare the exact absolute date for UmAlQuraYear and (UmAlQuraYear + 1). + ** From here, we can get the correct UmAlQura year. + ============================================================================*/ + + internal virtual int GetDatePart(DateTime time, int part) + { + int UmAlQuraYear = 0; // UmAlQura year + int UmAlQuraMonth = 0; // UmAlQura month + int UmAlQuraDay = 0; // UmAlQura day + long ticks = time.Ticks; + CheckTicksRange(ticks); + + ConvertGregorianToHijri(time, ref UmAlQuraYear, ref UmAlQuraMonth, ref UmAlQuraDay); + + if (part == DatePartYear) + return (UmAlQuraYear); + + if (part == DatePartMonth) + return (UmAlQuraMonth); + + if (part == DatePartDay) + return (UmAlQuraDay); + + if (part == DatePartDayOfYear) + return (int)(GetAbsoluteDateUmAlQura(UmAlQuraYear, UmAlQuraMonth, UmAlQuraDay) - GetAbsoluteDateUmAlQura(UmAlQuraYear, 1, 1) + 1); + + // Incorrect part value. + throw new InvalidOperationException(SR.InvalidOperation_DateTimeParsing); + } + + // Returns the DateTime resulting from adding the given number of + // months to the specified DateTime. The result is computed by incrementing + // (or decrementing) the year and month parts of the specified DateTime by + // value months, and, if required, adjusting the day part of the + // resulting date downwards to the last day of the resulting month in the + // resulting year. The time-of-day part of the result is the same as the + // time-of-day part of the specified DateTime. + // + // In more precise terms, considering the specified DateTime to be of the + // form y / m / d + t, where y is the + // year, m is the month, d is the day, and t is the + // time-of-day, the result is y1 / m1 / d1 + t, + // where y1 and m1 are computed by adding value months + // to y and m, and d1 is the largest value less than + // or equal to d that denotes a valid day in month m1 of year + // y1. + // + + + public override DateTime AddMonths(DateTime time, int months) + { + if (months < -120000 || months > 120000) + { + throw new ArgumentOutOfRangeException( + nameof(months), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + -120000, + 120000)); + } + Contract.EndContractBlock(); + // Get the date in UmAlQura calendar. + int y = GetDatePart(time, DatePartYear); + int m = GetDatePart(time, DatePartMonth); + int d = GetDatePart(time, DatePartDay); + int i = m - 1 + months; + + if (i >= 0) + { + m = i % 12 + 1; + y = y + i / 12; + } + else + { + m = 12 + (i + 1) % 12; + y = y + (i - 11) / 12; + } + + if (d > 29) + { + int days = GetDaysInMonth(y, m); + if (d > days) + { + d = days; + } + } + CheckYearRange(y, UmAlQuraEra); + DateTime dt = new DateTime(GetAbsoluteDateUmAlQura(y, m, d) * TicksPerDay + time.Ticks % TicksPerDay); + Calendar.CheckAddResult(dt.Ticks, MinSupportedDateTime, MaxSupportedDateTime); + return (dt); + } + + // Returns the DateTime resulting from adding the given number of + // years to the specified DateTime. The result is computed by incrementing + // (or decrementing) the year part of the specified DateTime by value + // years. If the month and day of the specified DateTime is 2/29, and if the + // resulting year is not a leap year, the month and day of the resulting + // DateTime becomes 2/28. Otherwise, the month, day, and time-of-day + // parts of the result are the same as those of the specified DateTime. + // + + + public override DateTime AddYears(DateTime time, int years) + { + return (AddMonths(time, years * 12)); + } + + // Returns the day-of-month part of the specified DateTime. The returned + // value is an integer between 1 and 31. + // + + + public override int GetDayOfMonth(DateTime time) + { + return (GetDatePart(time, DatePartDay)); + } + + // Returns the day-of-week part of the specified DateTime. The returned value + // is an integer between 0 and 6, where 0 indicates Sunday, 1 indicates + // Monday, 2 indicates Tuesday, 3 indicates Wednesday, 4 indicates + // Thursday, 5 indicates Friday, and 6 indicates Saturday. + // + + + public override DayOfWeek GetDayOfWeek(DateTime time) + { + return ((DayOfWeek)((int)(time.Ticks / TicksPerDay + 1) % 7)); + } + + // Returns the day-of-year part of the specified DateTime. The returned value + // is an integer between 1 and 354 or 355. + // + + + public override int GetDayOfYear(DateTime time) + { + return (GetDatePart(time, DatePartDayOfYear)); + } + + /* + internal bool CouldBeLeapYear(int year) + { + return ((((year * 11) + 14) % 30) < 11); + } + */ + + // Returns the number of days in the month given by the year and + // month arguments. + // + + + public override int GetDaysInMonth(int year, int month, int era) + { + CheckYearMonthRange(year, month, era); + + if ((s_hijriYearInfo[year - MinCalendarYear].HijriMonthsLengthFlags & (1 << month - 1)) == 0) + return 29; + else + return 30; + } + + internal static int RealGetDaysInYear(int year) + { + int days = 0, b; + + Debug.Assert((year >= MinCalendarYear) && (year <= MaxCalendarYear), "Hijri year is out of range."); + + b = s_hijriYearInfo[year - MinCalendarYear].HijriMonthsLengthFlags; + + for (int m = 1; m <= 12; m++) + { + days = days + 29 + (b & 1); /* Add the months lengths before mh */ + b = b >> 1; + } + Debug.Assert((days == 354) || (days == 355), "Hijri year has to be 354 or 355 days."); + return days; + } + + // Returns the number of days in the year given by the year argument for the current era. + // + + + public override int GetDaysInYear(int year, int era) + { + CheckYearRange(year, era); + return (RealGetDaysInYear(year)); + } + + // Returns the era for the specified DateTime value. + + + public override int GetEra(DateTime time) + { + CheckTicksRange(time.Ticks); + return (UmAlQuraEra); + } + + + + public override int[] Eras + { + get + { + return (new int[] { UmAlQuraEra }); + } + } + + // Returns the month part of the specified DateTime. The returned value is an + // integer between 1 and 12. + // + + + public override int GetMonth(DateTime time) + { + return (GetDatePart(time, DatePartMonth)); + } + + // Returns the number of months in the specified year and era. + + + public override int GetMonthsInYear(int year, int era) + { + CheckYearRange(year, era); + return (12); + } + + // Returns the year part of the specified DateTime. The returned value is an + // integer between MinCalendarYear and MaxCalendarYear. + // + + + public override int GetYear(DateTime time) + { + return (GetDatePart(time, DatePartYear)); + } + + // Checks whether a given day in the specified era is a leap day. This method returns true if + // the date is a leap day, or false if not. + // + + + public override bool IsLeapDay(int year, int month, int day, int era) + { + if (day >= 1 && day <= 29) + { + CheckYearMonthRange(year, month, era); + return (false); + } + + // The year/month/era value checking is done in GetDaysInMonth(). + int daysInMonth = GetDaysInMonth(year, month, era); + if (day < 1 || day > daysInMonth) + { + throw new ArgumentOutOfRangeException( + nameof(day), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Day, + daysInMonth, + month)); + } + return (false); + } + + // Returns the leap month in a calendar year of the specified era. This method returns 0 + // if this calendar does not have leap month, or this year is not a leap year. + // + + + public override int GetLeapMonth(int year, int era) + { + CheckYearRange(year, era); + return (0); + } + + // Checks whether a given month in the specified era is a leap month. This method returns true if + // month is a leap month, or false if not. + // + + + public override bool IsLeapMonth(int year, int month, int era) + { + CheckYearMonthRange(year, month, era); + return (false); + } + + // Checks whether a given year in the specified era is a leap year. This method returns true if + // year is a leap year, or false if not. + // + + + public override bool IsLeapYear(int year, int era) + { + CheckYearRange(year, era); + if (RealGetDaysInYear(year) == 355) + return true; + else + return false; + } + + // Returns the date and time converted to a DateTime value. Throws an exception if the n-tuple is invalid. + // + + + public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era) + { + if (day >= 1 && day <= 29) + { + CheckYearMonthRange(year, month, era); + goto DayInRang; + } + + // The year/month/era value checking is done in GetDaysInMonth(). + int daysInMonth = GetDaysInMonth(year, month, era); + + if (day < 1 || day > daysInMonth) + { + throw new ArgumentOutOfRangeException( + nameof(day), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Day, + daysInMonth, + month)); + } + DayInRang: + long lDate = GetAbsoluteDateUmAlQura(year, month, day); + + if (lDate >= 0) + { + return (new DateTime(lDate * GregorianCalendar.TicksPerDay + TimeToTicks(hour, minute, second, millisecond))); + } + else + { + throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadYearMonthDay); + } + } + + private const int DEFAULT_TWO_DIGIT_YEAR_MAX = 1451; + + + + public override int TwoDigitYearMax + { + get + { + if (twoDigitYearMax == -1) + { + twoDigitYearMax = GetSystemTwoDigitYearSetting(ID, DEFAULT_TWO_DIGIT_YEAR_MAX); + } + return (twoDigitYearMax); + } + + set + { + if (value != 99 && (value < MinCalendarYear || value > MaxCalendarYear)) + { + throw new ArgumentOutOfRangeException( + nameof(value), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + MinCalendarYear, + MaxCalendarYear)); + } + Contract.EndContractBlock(); + VerifyWritable(); + // We allow year 99 to be set so that one can make ToFourDigitYearMax a no-op by setting TwoDigitYearMax to 99. + twoDigitYearMax = value; + } + } + + + + public override int ToFourDigitYear(int year) + { + if (year < 0) + { + throw new ArgumentOutOfRangeException(nameof(year), + SR.ArgumentOutOfRange_NeedNonNegNum); + } + Contract.EndContractBlock(); + + if (year < 100) + { + return (base.ToFourDigitYear(year)); + } + + if ((year < MinCalendarYear) || (year > MaxCalendarYear)) + { + throw new ArgumentOutOfRangeException( + nameof(year), + String.Format( + CultureInfo.CurrentCulture, + SR.ArgumentOutOfRange_Range, + MinCalendarYear, + MaxCalendarYear)); + } + return (year); + } + } +} + diff --git a/src/mscorlib/shared/System/Globalization/UnicodeCategory.cs b/src/mscorlib/shared/System/Globalization/UnicodeCategory.cs new file mode 100644 index 0000000000..f0ae1fdfd9 --- /dev/null +++ b/src/mscorlib/shared/System/Globalization/UnicodeCategory.cs @@ -0,0 +1,40 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Globalization +{ + public enum UnicodeCategory + { + UppercaseLetter = 0, + LowercaseLetter = 1, + TitlecaseLetter = 2, + ModifierLetter = 3, + OtherLetter = 4, + NonSpacingMark = 5, + SpacingCombiningMark = 6, + EnclosingMark = 7, + DecimalDigitNumber = 8, + LetterNumber = 9, + OtherNumber = 10, + SpaceSeparator = 11, + LineSeparator = 12, + ParagraphSeparator = 13, + Control = 14, + Format = 15, + Surrogate = 16, + PrivateUse = 17, + ConnectorPunctuation = 18, + DashPunctuation = 19, + OpenPunctuation = 20, + ClosePunctuation = 21, + InitialQuotePunctuation = 22, + FinalQuotePunctuation = 23, + OtherPunctuation = 24, + MathSymbol = 25, + CurrencySymbol = 26, + ModifierSymbol = 27, + OtherSymbol = 28, + OtherNotAssigned = 29, + } +} |