DateTimeUtils
using System;
using System.Globalization;
using System.IO;
using System.Runtime.CompilerServices;
namespace Newtonsoft.Json.Utilities
{
    [System.Runtime.CompilerServices.NullableContext(1)]
    [System.Runtime.CompilerServices.Nullable(0)]
    internal static class DateTimeUtils
    {
        internal static readonly long InitialJavaScriptDateTicks;
        private const string IsoDateFormat = "yyyy-MM-ddTHH:mm:ss.FFFFFFFK";
        private const int DaysPer100Years = 36524;
        private const int DaysPer400Years = 146097;
        private const int DaysPer4Years = 1461;
        private const int DaysPerYear = 365;
        private const long TicksPerDay = 864000000000;
        private static readonly int[] DaysToMonth365;
        private static readonly int[] DaysToMonth366;
        static DateTimeUtils()
        {
            InitialJavaScriptDateTicks = 621355968000000000;
            DaysToMonth365 = new int[13] {
                0,
                31,
                59,
                90,
                120,
                151,
                181,
                212,
                243,
                273,
                304,
                334,
                365
            };
            DaysToMonth366 = new int[13] {
                0,
                31,
                60,
                91,
                121,
                152,
                182,
                213,
                244,
                274,
                305,
                335,
                366
            };
        }
        public static TimeSpan GetUtcOffset(this DateTime d)
        {
            return TimeZoneInfo.Local.GetUtcOffset(d);
        }
        public static string ToDateTimeFormat(DateTimeKind kind)
        {
            switch (kind) {
            case DateTimeKind.Local:
                return "yyyy-MM-ddTHH:mm:ss.FFFFFFFK";
            case DateTimeKind.Unspecified:
                return "yyyy-MM-ddTHH:mm:ss.FFFFFFF";
            case DateTimeKind.Utc:
                return "yyyy-MM-ddTHH:mm:ss.FFFFFFFZ";
            default:
                throw MiscellaneousUtils.CreateArgumentOutOfRangeException("kind", kind, "Unexpected DateTimeKind value.");
            }
        }
        internal static DateTime EnsureDateTime(DateTime value, DateTimeZoneHandling timeZone)
        {
            switch (timeZone) {
            case DateTimeZoneHandling.Local:
                value = SwitchToLocalTime(value);
                break;
            case DateTimeZoneHandling.Utc:
                value = SwitchToUtcTime(value);
                break;
            case DateTimeZoneHandling.Unspecified:
                value = new DateTime(value.Ticks, DateTimeKind.Unspecified);
                break;
            default:
                throw new ArgumentException("Invalid date time handling value.");
            case DateTimeZoneHandling.RoundtripKind:
                break;
            }
            return value;
        }
        private static DateTime SwitchToLocalTime(DateTime value)
        {
            switch (value.Kind) {
            case DateTimeKind.Unspecified:
                return new DateTime(value.Ticks, DateTimeKind.Local);
            case DateTimeKind.Utc:
                return value.ToLocalTime();
            case DateTimeKind.Local:
                return value;
            default:
                return value;
            }
        }
        private static DateTime SwitchToUtcTime(DateTime value)
        {
            switch (value.Kind) {
            case DateTimeKind.Unspecified:
                return new DateTime(value.Ticks, DateTimeKind.Utc);
            case DateTimeKind.Utc:
                return value;
            case DateTimeKind.Local:
                return value.ToUniversalTime();
            default:
                return value;
            }
        }
        private static long ToUniversalTicks(DateTime dateTime)
        {
            if (dateTime.Kind == DateTimeKind.Utc)
                return dateTime.Ticks;
            return ToUniversalTicks(dateTime, dateTime.GetUtcOffset());
        }
        private static long ToUniversalTicks(DateTime dateTime, TimeSpan offset)
        {
            if (dateTime.Kind == DateTimeKind.Utc || dateTime == DateTime.MaxValue || dateTime == DateTime.MinValue)
                return dateTime.Ticks;
            long num = dateTime.Ticks - offset.Ticks;
            if (num > 3155378975999999999)
                return 3155378975999999999;
            if (num < 0)
                return 0;
            return num;
        }
        internal static long ConvertDateTimeToJavaScriptTicks(DateTime dateTime, TimeSpan offset)
        {
            return UniversalTicksToJavaScriptTicks(ToUniversalTicks(dateTime, offset));
        }
        internal static long ConvertDateTimeToJavaScriptTicks(DateTime dateTime)
        {
            return ConvertDateTimeToJavaScriptTicks(dateTime, true);
        }
        internal static long ConvertDateTimeToJavaScriptTicks(DateTime dateTime, bool convertToUtc)
        {
            return UniversalTicksToJavaScriptTicks(convertToUtc ? ToUniversalTicks(dateTime) : dateTime.Ticks);
        }
        private static long UniversalTicksToJavaScriptTicks(long universalTicks)
        {
            return (universalTicks - InitialJavaScriptDateTicks) / 10000;
        }
        internal static DateTime ConvertJavaScriptTicksToDateTime(long javaScriptTicks)
        {
            return new DateTime(javaScriptTicks * 10000 + InitialJavaScriptDateTicks, DateTimeKind.Utc);
        }
        internal static bool TryParseDateTimeIso(StringReference text, DateTimeZoneHandling dateTimeZoneHandling, out DateTime dt)
        {
            DateTimeParser dateTimeParser = default(DateTimeParser);
            if (!dateTimeParser.Parse(text.Chars, text.StartIndex, text.Length)) {
                dt = default(DateTime);
                return false;
            }
            DateTime dateTime = CreateDateTime(dateTimeParser);
            DateTime dateTime2;
            TimeSpan utcOffset;
            switch (dateTimeParser.Zone) {
            case ParserTimeZone.Utc:
                dateTime = new DateTime(dateTime.Ticks, DateTimeKind.Utc);
                break;
            case ParserTimeZone.LocalWestOfUtc: {
                TimeSpan timeSpan2 = new TimeSpan(dateTimeParser.ZoneHour, dateTimeParser.ZoneMinute, 0);
                long num = dateTime.Ticks + timeSpan2.Ticks;
                long num5 = num;
                dateTime2 = DateTime.MaxValue;
                if (num5 <= dateTime2.Ticks) {
                    dateTime2 = new DateTime(num, DateTimeKind.Utc);
                    dateTime = dateTime2.ToLocalTime();
                } else {
                    long num6 = num;
                    utcOffset = dateTime.GetUtcOffset();
                    num = num6 + utcOffset.Ticks;
                    long num7 = num;
                    dateTime2 = DateTime.MaxValue;
                    if (num7 > dateTime2.Ticks) {
                        dateTime2 = DateTime.MaxValue;
                        num = dateTime2.Ticks;
                    }
                    dateTime = new DateTime(num, DateTimeKind.Local);
                }
                break;
            }
            case ParserTimeZone.LocalEastOfUtc: {
                TimeSpan timeSpan = new TimeSpan(dateTimeParser.ZoneHour, dateTimeParser.ZoneMinute, 0);
                long num = dateTime.Ticks - timeSpan.Ticks;
                long num2 = num;
                dateTime2 = DateTime.MinValue;
                if (num2 >= dateTime2.Ticks) {
                    dateTime2 = new DateTime(num, DateTimeKind.Utc);
                    dateTime = dateTime2.ToLocalTime();
                } else {
                    long num3 = num;
                    utcOffset = dateTime.GetUtcOffset();
                    num = num3 + utcOffset.Ticks;
                    long num4 = num;
                    dateTime2 = DateTime.MinValue;
                    if (num4 < dateTime2.Ticks) {
                        dateTime2 = DateTime.MinValue;
                        num = dateTime2.Ticks;
                    }
                    dateTime = new DateTime(num, DateTimeKind.Local);
                }
                break;
            }
            }
            dt = EnsureDateTime(dateTime, dateTimeZoneHandling);
            return true;
        }
        internal static bool TryParseDateTimeOffsetIso(StringReference text, out DateTimeOffset dt)
        {
            DateTimeParser dateTimeParser = default(DateTimeParser);
            if (!dateTimeParser.Parse(text.Chars, text.StartIndex, text.Length)) {
                dt = default(DateTimeOffset);
                return false;
            }
            DateTime dateTime = CreateDateTime(dateTimeParser);
            TimeSpan offset;
            switch (dateTimeParser.Zone) {
            case ParserTimeZone.Utc:
                offset = new TimeSpan(0);
                break;
            case ParserTimeZone.LocalWestOfUtc:
                offset = new TimeSpan(-dateTimeParser.ZoneHour, -dateTimeParser.ZoneMinute, 0);
                break;
            case ParserTimeZone.LocalEastOfUtc:
                offset = new TimeSpan(dateTimeParser.ZoneHour, dateTimeParser.ZoneMinute, 0);
                break;
            default:
                offset = TimeZoneInfo.Local.GetUtcOffset(dateTime);
                break;
            }
            long num = dateTime.Ticks - offset.Ticks;
            if (num < 0 || num > 3155378975999999999) {
                dt = default(DateTimeOffset);
                return false;
            }
            dt = new DateTimeOffset(dateTime, offset);
            return true;
        }
        private static DateTime CreateDateTime(DateTimeParser dateTimeParser)
        {
            bool flag;
            if (dateTimeParser.Hour == 24) {
                flag = true;
                dateTimeParser.Hour = 0;
            } else
                flag = false;
            DateTime result = new DateTime(dateTimeParser.Year, dateTimeParser.Month, dateTimeParser.Day, dateTimeParser.Hour, dateTimeParser.Minute, dateTimeParser.Second);
            result = result.AddTicks(dateTimeParser.Fraction);
            if (flag)
                result = result.AddDays(1);
            return result;
        }
        internal static bool TryParseDateTime(StringReference s, DateTimeZoneHandling dateTimeZoneHandling, [System.Runtime.CompilerServices.Nullable(2)] string dateFormatString, CultureInfo culture, out DateTime dt)
        {
            if (s.Length > 0) {
                int startIndex = s.StartIndex;
                if (s[startIndex] == '/') {
                    if (s.Length >= 9 && s.StartsWith("/Date(") && s.EndsWith(")/") && TryParseDateTimeMicrosoft(s, dateTimeZoneHandling, out dt))
                        return true;
                } else if (s.Length >= 19 && s.Length <= 40 && char.IsDigit(s[startIndex]) && s[startIndex + 10] == 'T' && TryParseDateTimeIso(s, dateTimeZoneHandling, out dt)) {
                    return true;
                }
                if (!StringUtils.IsNullOrEmpty(dateFormatString) && TryParseDateTimeExact(s.ToString(), dateTimeZoneHandling, dateFormatString, culture, out dt))
                    return true;
            }
            dt = default(DateTime);
            return false;
        }
        internal static bool TryParseDateTime(string s, DateTimeZoneHandling dateTimeZoneHandling, [System.Runtime.CompilerServices.Nullable(2)] string dateFormatString, CultureInfo culture, out DateTime dt)
        {
            if (s.Length > 0) {
                if (s[0] == '/') {
                    if (s.Length >= 9 && s.StartsWith("/Date(", StringComparison.Ordinal) && s.EndsWith(")/", StringComparison.Ordinal) && TryParseDateTimeMicrosoft(new StringReference(s.ToCharArray(), 0, s.Length), dateTimeZoneHandling, out dt))
                        return true;
                } else if (s.Length >= 19 && s.Length <= 40 && char.IsDigit(s[0]) && s[10] == 'T' && DateTime.TryParseExact(s, "yyyy-MM-ddTHH:mm:ss.FFFFFFFK", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out dt)) {
                    dt = EnsureDateTime(dt, dateTimeZoneHandling);
                    return true;
                }
                if (!StringUtils.IsNullOrEmpty(dateFormatString) && TryParseDateTimeExact(s, dateTimeZoneHandling, dateFormatString, culture, out dt))
                    return true;
            }
            dt = default(DateTime);
            return false;
        }
        internal static bool TryParseDateTimeOffset(StringReference s, [System.Runtime.CompilerServices.Nullable(2)] string dateFormatString, CultureInfo culture, out DateTimeOffset dt)
        {
            if (s.Length > 0) {
                int startIndex = s.StartIndex;
                if (s[startIndex] == '/') {
                    if (s.Length >= 9 && s.StartsWith("/Date(") && s.EndsWith(")/") && TryParseDateTimeOffsetMicrosoft(s, out dt))
                        return true;
                } else if (s.Length >= 19 && s.Length <= 40 && char.IsDigit(s[startIndex]) && s[startIndex + 10] == 'T' && TryParseDateTimeOffsetIso(s, out dt)) {
                    return true;
                }
                if (!StringUtils.IsNullOrEmpty(dateFormatString) && TryParseDateTimeOffsetExact(s.ToString(), dateFormatString, culture, out dt))
                    return true;
            }
            dt = default(DateTimeOffset);
            return false;
        }
        internal static bool TryParseDateTimeOffset(string s, [System.Runtime.CompilerServices.Nullable(2)] string dateFormatString, CultureInfo culture, out DateTimeOffset dt)
        {
            if (s.Length > 0) {
                if (s[0] == '/') {
                    if (s.Length >= 9 && s.StartsWith("/Date(", StringComparison.Ordinal) && s.EndsWith(")/", StringComparison.Ordinal) && TryParseDateTimeOffsetMicrosoft(new StringReference(s.ToCharArray(), 0, s.Length), out dt))
                        return true;
                } else if (s.Length >= 19 && s.Length <= 40 && char.IsDigit(s[0]) && s[10] == 'T' && DateTimeOffset.TryParseExact(s, "yyyy-MM-ddTHH:mm:ss.FFFFFFFK", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out dt) && TryParseDateTimeOffsetIso(new StringReference(s.ToCharArray(), 0, s.Length), out dt)) {
                    return true;
                }
                if (!StringUtils.IsNullOrEmpty(dateFormatString) && TryParseDateTimeOffsetExact(s, dateFormatString, culture, out dt))
                    return true;
            }
            dt = default(DateTimeOffset);
            return false;
        }
        private static bool TryParseMicrosoftDate(StringReference text, out long ticks, out TimeSpan offset, out DateTimeKind kind)
        {
            kind = DateTimeKind.Utc;
            int num = text.IndexOf('+', 7, text.Length - 8);
            if (num == -1)
                num = text.IndexOf('-', 7, text.Length - 8);
            if (num != -1) {
                kind = DateTimeKind.Local;
                if (!TryReadOffset(text, num + text.StartIndex, out offset)) {
                    ticks = 0;
                    return false;
                }
            } else {
                offset = TimeSpan.Zero;
                num = text.Length - 2;
            }
            return ConvertUtils.Int64TryParse(text.Chars, 6 + text.StartIndex, num - 6, out ticks) == ParseResult.Success;
        }
        private static bool TryParseDateTimeMicrosoft(StringReference text, DateTimeZoneHandling dateTimeZoneHandling, out DateTime dt)
        {
            if (!TryParseMicrosoftDate(text, out long ticks, out TimeSpan _, out DateTimeKind kind)) {
                dt = default(DateTime);
                return false;
            }
            DateTime dateTime = ConvertJavaScriptTicksToDateTime(ticks);
            switch (kind) {
            case DateTimeKind.Unspecified:
                dt = DateTime.SpecifyKind(dateTime.ToLocalTime(), DateTimeKind.Unspecified);
                break;
            case DateTimeKind.Local:
                dt = dateTime.ToLocalTime();
                break;
            default:
                dt = dateTime;
                break;
            }
            dt = EnsureDateTime(dt, dateTimeZoneHandling);
            return true;
        }
        private static bool TryParseDateTimeExact(string text, DateTimeZoneHandling dateTimeZoneHandling, string dateFormatString, CultureInfo culture, out DateTime dt)
        {
            if (DateTime.TryParseExact(text, dateFormatString, culture, DateTimeStyles.RoundtripKind, out DateTime result)) {
                result = (dt = EnsureDateTime(result, dateTimeZoneHandling));
                return true;
            }
            dt = default(DateTime);
            return false;
        }
        private static bool TryParseDateTimeOffsetMicrosoft(StringReference text, out DateTimeOffset dt)
        {
            DateTime dateTime;
            if (!TryParseMicrosoftDate(text, out long ticks, out TimeSpan offset, out DateTimeKind _)) {
                dateTime = default(DateTime);
                dt = dateTime;
                return false;
            }
            dateTime = ConvertJavaScriptTicksToDateTime(ticks).Add(offset);
            dt = new DateTimeOffset(dateTime.Ticks, offset);
            return true;
        }
        private static bool TryParseDateTimeOffsetExact(string text, string dateFormatString, CultureInfo culture, out DateTimeOffset dt)
        {
            if (DateTimeOffset.TryParseExact(text, dateFormatString, culture, DateTimeStyles.RoundtripKind, out DateTimeOffset result)) {
                dt = result;
                return true;
            }
            dt = default(DateTimeOffset);
            return false;
        }
        private static bool TryReadOffset(StringReference offsetText, int startIndex, out TimeSpan offset)
        {
            bool flag = offsetText[startIndex] == '-';
            if (ConvertUtils.Int32TryParse(offsetText.Chars, startIndex + 1, 2, out int value) != ParseResult.Success) {
                offset = default(TimeSpan);
                return false;
            }
            int value2 = 0;
            if (offsetText.Length - startIndex > 5 && ConvertUtils.Int32TryParse(offsetText.Chars, startIndex + 3, 2, out value2) != ParseResult.Success) {
                offset = default(TimeSpan);
                return false;
            }
            offset = TimeSpan.FromHours((double)value) + TimeSpan.FromMinutes((double)value2);
            if (flag)
                offset = offset.Negate();
            return true;
        }
        internal static void WriteDateTimeString(TextWriter writer, DateTime value, DateFormatHandling format, [System.Runtime.CompilerServices.Nullable(2)] string formatString, CultureInfo culture)
        {
            if (StringUtils.IsNullOrEmpty(formatString)) {
                char[] array = new char[64];
                int count = WriteDateTimeString(array, 0, value, null, value.Kind, format);
                writer.Write(array, 0, count);
            } else
                writer.Write(value.ToString(formatString, culture));
        }
        internal static int WriteDateTimeString(char[] chars, int start, DateTime value, TimeSpan? offset, DateTimeKind kind, DateFormatHandling format)
        {
            int num2;
            if (format == DateFormatHandling.MicrosoftDateFormat) {
                TimeSpan offset2 = offset ?? value.GetUtcOffset();
                long num = ConvertDateTimeToJavaScriptTicks(value, offset2);
                "\\/Date(".CopyTo(0, chars, start, 7);
                num2 = start + 7;
                string text = num.ToString(CultureInfo.InvariantCulture);
                text.CopyTo(0, chars, num2, text.Length);
                num2 += text.Length;
                switch (kind) {
                case DateTimeKind.Unspecified:
                    if (value != DateTime.MaxValue && value != DateTime.MinValue)
                        num2 = WriteDateTimeOffset(chars, num2, offset2, format);
                    break;
                case DateTimeKind.Local:
                    num2 = WriteDateTimeOffset(chars, num2, offset2, format);
                    break;
                }
                ")\\/".CopyTo(0, chars, num2, 3);
                num2 += 3;
            } else {
                num2 = WriteDefaultIsoDate(chars, start, value);
                switch (kind) {
                case DateTimeKind.Local:
                    num2 = WriteDateTimeOffset(chars, num2, offset ?? value.GetUtcOffset(), format);
                    break;
                case DateTimeKind.Utc:
                    chars[num2++] = 'Z';
                    break;
                }
            }
            return num2;
        }
        internal static int WriteDefaultIsoDate(char[] chars, int start, DateTime dt)
        {
            int num = 19;
            GetDateValues(dt, out int year, out int month, out int day);
            CopyIntToCharArray(chars, start, year, 4);
            chars[start + 4] = '-';
            CopyIntToCharArray(chars, start + 5, month, 2);
            chars[start + 7] = '-';
            CopyIntToCharArray(chars, start + 8, day, 2);
            chars[start + 10] = 'T';
            CopyIntToCharArray(chars, start + 11, dt.Hour, 2);
            chars[start + 13] = ':';
            CopyIntToCharArray(chars, start + 14, dt.Minute, 2);
            chars[start + 16] = ':';
            CopyIntToCharArray(chars, start + 17, dt.Second, 2);
            int num2 = (int)(dt.Ticks % 10000000);
            if (num2 != 0) {
                int num3 = 7;
                while (num2 % 10 == 0) {
                    num3--;
                    num2 /= 10;
                }
                chars[start + 19] = '.';
                CopyIntToCharArray(chars, start + 20, num2, num3);
                num += num3 + 1;
            }
            return start + num;
        }
        private static void CopyIntToCharArray(char[] chars, int start, int value, int digits)
        {
            while (digits-- != 0) {
                chars[start + digits] = (char)(value % 10 + 48);
                value /= 10;
            }
        }
        internal static int WriteDateTimeOffset(char[] chars, int start, TimeSpan offset, DateFormatHandling format)
        {
            chars[start++] = ((offset.Ticks >= 0) ? '+' : '-');
            int value = Math.Abs(offset.Hours);
            CopyIntToCharArray(chars, start, value, 2);
            start += 2;
            if (format == DateFormatHandling.IsoDateFormat)
                chars[start++] = ':';
            int value2 = Math.Abs(offset.Minutes);
            CopyIntToCharArray(chars, start, value2, 2);
            start += 2;
            return start;
        }
        internal static void WriteDateTimeOffsetString(TextWriter writer, DateTimeOffset value, DateFormatHandling format, [System.Runtime.CompilerServices.Nullable(2)] string formatString, CultureInfo culture)
        {
            if (StringUtils.IsNullOrEmpty(formatString)) {
                char[] array = new char[64];
                int count = WriteDateTimeString(array, 0, (format == DateFormatHandling.IsoDateFormat) ? value.DateTime : value.UtcDateTime, value.Offset, DateTimeKind.Local, format);
                writer.Write(array, 0, count);
            } else
                writer.Write(value.ToString(formatString, culture));
        }
        private static void GetDateValues(DateTime td, out int year, out int month, out int day)
        {
            int num = (int)(td.Ticks / 864000000000);
            int num2 = num / 146097;
            num -= num2 * 146097;
            int num3 = num / 36524;
            if (num3 == 4)
                num3 = 3;
            num -= num3 * 36524;
            int num4 = num / 1461;
            num -= num4 * 1461;
            int num5 = num / 365;
            if (num5 == 4)
                num5 = 3;
            year = num2 * 400 + num3 * 100 + num4 * 4 + num5 + 1;
            num -= num5 * 365;
            int[] array = (num5 == 3 && (num4 != 24 || num3 == 3)) ? DaysToMonth366 : DaysToMonth365;
            int i;
            for (i = num >> 6; num >= array[i]; i++) {
            }
            month = i;
            day = num - array[i - 1] + 1;
        }
    }
}