123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175 |
- using System.Globalization;
- using Jint.Native;
- using Jint.Native.Date;
- namespace Jint.Runtime;
- public class DefaultTimeSystem : ITimeSystem
- {
- private static readonly string[] _defaultFormats = ["yyyy-MM-dd", "yyyy-MM", "yyyy"];
- private static readonly string[] _secondaryFormats =
- [
- "yyyy-MM-ddTHH:mm:ss.FFF",
- "yyyy-MM-ddTHH:mm:ss",
- "yyyy-MM-ddTHH:mm",
- // Formats used in DatePrototype toString methods
- "ddd MMM dd yyyy HH:mm:ss 'GMT'K",
- "ddd MMM dd yyyy",
- "HH:mm:ss 'GMT'K",
- // standard formats
- "yyyy-M-dTH:m:s.FFFK",
- "yyyy/M/dTH:m:s.FFFK",
- "yyyy-M-dTH:m:sK",
- "yyyy/M/dTH:m:sK",
- "yyyy-M-dTH:mK",
- "yyyy/M/dTH:mK",
- "yyyy-M-d H:m:s.FFFK",
- "yyyy/M/d H:m:s.FFFK",
- "yyyy-M-d H:m:sK",
- "yyyy/M/d H:m:sK",
- "yyyy-M-d H:mK",
- "yyyy/M/d H:mK",
- "yyyy-M-dK",
- "yyyy/M/dK",
- "yyyy-MK",
- "yyyy/MK",
- "yyyyK",
- "THH:mm:ss.FFFK",
- "THH:mm:ssK",
- "THH:mmK",
- "THHK"
- ];
- private readonly CultureInfo _parsingCulture;
- public DefaultTimeSystem(TimeZoneInfo timeZoneInfo, CultureInfo parsingCulture)
- {
- _parsingCulture = parsingCulture;
- DefaultTimeZone = timeZoneInfo;
- }
- public virtual DateTimeOffset GetUtcNow()
- {
- return DateTimeOffset.UtcNow;
- }
- public TimeZoneInfo DefaultTimeZone { get; }
- public virtual bool TryParse(string date, out long epochMilliseconds)
- {
- epochMilliseconds = long.MinValue;
- if (string.IsNullOrEmpty(date))
- {
- return false;
- }
- // special check for large years that always require + or - in front and have 6 digit year
- if ((date[0] == '+' || date[0] == '-') && date.IndexOf('-', 1) == 7)
- {
- return TryParseLargeYear(date, out epochMilliseconds);
- }
- var startParen = date.IndexOf('(');
- if (startParen != -1)
- {
- // informative text
- date = date.Substring(0, startParen);
- }
- date = date.Trim();
- if (!DateTime.TryParseExact(date, _defaultFormats, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal, out var result))
- {
- if (!DateTime.TryParseExact(date, _secondaryFormats, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out result))
- {
- if (!DateTime.TryParse(date, _parsingCulture, DateTimeStyles.AdjustToUniversal, out result))
- {
- if (!DateTime.TryParse(date, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out result))
- {
- // fall back to trying with MimeKit
- if (DateUtils.TryParse(date, out var mimeKitResult))
- {
- var dateAsUtc = mimeKitResult.ToUniversalTime();
- #pragma warning disable MA0132
- epochMilliseconds = (long) Math.Floor((dateAsUtc - DateConstructor.Epoch).TotalMilliseconds);
- #pragma warning restore MA0132
- return true;
- }
- return false;
- }
- }
- }
- }
- var convertToUtcAfter = result.Kind == DateTimeKind.Unspecified;
- var dateAsUtc1 = result.Kind == DateTimeKind.Local
- ? result.ToUniversalTime()
- : DateTime.SpecifyKind(result, DateTimeKind.Utc);
- DatePresentation datePresentation = (long) (dateAsUtc1 - DateConstructor.Epoch).TotalMilliseconds;
- if (convertToUtcAfter)
- {
- var offset = GetUtcOffset(datePresentation.Value).TotalMilliseconds;
- datePresentation -= offset;
- }
- epochMilliseconds = datePresentation.Value;
- return true;
- }
- /// <summary>
- /// Supports parsing of large (> 10 000) and negative years, should not be needed that often...
- /// </summary>
- private static bool TryParseLargeYear(string date, out long epochMilliseconds)
- {
- epochMilliseconds = long.MinValue;
- var yearString = date.Substring(0, 7);
- if (!int.TryParse(yearString, NumberStyles.Integer, CultureInfo.InvariantCulture, out var year))
- {
- return false;
- }
- if (year == 0 && date[0] == '-')
- {
- // cannot be negative zero ever
- return false;
- }
- // create replacement string
- #pragma warning disable CA1845
- var dateToParse = "2000" + date.Substring(7);
- #pragma warning restore CA1845
- if (!DateTime.TryParse(dateToParse, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out var parsed))
- {
- return false;
- }
- var dateTime = parsed.ToUniversalTime();
- var datePresentation = DatePrototype.MakeDate(
- DatePrototype.MakeDay(year, dateTime.Month - 1, dateTime.Day),
- DatePrototype.MakeTime(dateTime.Hour, dateTime.Minute, dateTime.Second, dateTime.Millisecond)
- );
- epochMilliseconds = datePresentation.Value;
- return true;
- }
- public virtual TimeSpan GetUtcOffset(long epochMilliseconds)
- {
- // we have limited capabilities without addon like NodaTime
- if (epochMilliseconds is < -62135596800000L or > 253402300799999L)
- {
- return this.DefaultTimeZone.BaseUtcOffset;
- }
- return this.DefaultTimeZone.GetUtcOffset(DateTimeOffset.FromUnixTimeMilliseconds(epochMilliseconds));
- }
- }
|