<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />

DateTimeUtils

static class DateTimeUtils
using System; using System.Globalization; using System.IO; using System.Xml; namespace Newtonsoft.Json.Utilities { 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 XmlDateTimeSerializationMode ToSerializationMode(DateTimeKind kind) { switch (kind) { case DateTimeKind.Local: return XmlDateTimeSerializationMode.Local; case DateTimeKind.Unspecified: return XmlDateTimeSerializationMode.Unspecified; case DateTimeKind.Utc: return XmlDateTimeSerializationMode.Utc; 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 UniversialTicksToJavaScriptTicks(ToUniversalTicks(dateTime, offset)); } internal static long ConvertDateTimeToJavaScriptTicks(DateTime dateTime) { return ConvertDateTimeToJavaScriptTicks(dateTime, true); } internal static long ConvertDateTimeToJavaScriptTicks(DateTime dateTime, bool convertToUtc) { return UniversialTicksToJavaScriptTicks(convertToUtc ? ToUniversalTicks(dateTime) : dateTime.Ticks); } private static long UniversialTicksToJavaScriptTicks(long universialTicks) { return (universialTicks - 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, 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 (!string.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, 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 (!string.IsNullOrEmpty(dateFormatString) && TryParseDateTimeExact(s, dateTimeZoneHandling, dateFormatString, culture, out dt)) return true; } dt = default(DateTime); return false; } internal static bool TryParseDateTimeOffset(StringReference s, 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 (!string.IsNullOrEmpty(dateFormatString) && TryParseDateTimeOffsetExact(s.ToString(), dateFormatString, culture, out dt)) return true; } dt = default(DateTimeOffset); return false; } internal static bool TryParseDateTimeOffset(string s, 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 (!string.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, string formatString, CultureInfo culture) { if (string.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, string formatString, CultureInfo culture) { if (string.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; } } }