123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245 |
- using System;
- using System.Globalization;
- using Jint.Native.Function;
- using Jint.Native.Object;
- using Jint.Runtime;
- using Jint.Runtime.Descriptors;
- using Jint.Runtime.Interop;
- namespace Jint.Native.Date
- {
- public sealed class DateConstructor : FunctionInstance, IConstructor
- {
- internal static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
- private static readonly string[] DefaultFormats = {
- "yyyy-MM-ddTHH:mm:ss.FFF",
- "yyyy-MM-ddTHH:mm:ss",
- "yyyy-MM-ddTHH:mm",
- "yyyy-MM-dd",
- "yyyy-MM",
- "yyyy"
- };
- private static readonly string[] SecondaryFormats = {
- // 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"
- };
- public DateConstructor(Engine engine) : base(engine, "Date", null, null, false)
- {
- }
- public static DateConstructor CreateDateConstructor(Engine engine)
- {
- var obj = new DateConstructor(engine);
- obj.Extensible = true;
- // The value of the [[Prototype]] internal property of the Date constructor is the Function prototype object
- obj.Prototype = engine.Function.PrototypeObject;
- obj.PrototypeObject = DatePrototype.CreatePrototypeObject(engine, obj);
- obj.SetOwnProperty("length", new PropertyDescriptor(7, PropertyFlag.AllForbidden));
- // The initial value of Date.prototype is the Date prototype object
- obj.SetOwnProperty("prototype", new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden));
- return obj;
- }
- public void Configure()
- {
- FastAddProperty("parse", new ClrFunctionInstance(Engine, "parse", Parse, 1), true, false, true);
- FastAddProperty("UTC", new ClrFunctionInstance(Engine, "utc", Utc, 7), true, false, true);
- FastAddProperty("now", new ClrFunctionInstance(Engine, "now", Now, 0), true, false, true);
- }
- private JsValue Parse(JsValue thisObj, JsValue[] arguments)
- {
- var date = TypeConverter.ToString(arguments.At(0));
- 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, Engine.Options._Culture, DateTimeStyles.AdjustToUniversal, out result))
- {
- if (!DateTime.TryParse(date, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out result))
- {
- // unrecognized dates should return NaN (15.9.4.2)
- return JsNumber.DoubleNaN;
- }
- }
- }
- }
- return FromDateTime(result);
- }
- private JsValue Utc(JsValue thisObj, JsValue[] arguments)
- {
- return TimeClip(ConstructTimeValue(arguments, useUtc: true));
- }
- private static JsValue Now(JsValue thisObj, JsValue[] arguments)
- {
- return System.Math.Floor((DateTime.UtcNow - Epoch).TotalMilliseconds);
- }
- public override JsValue Call(JsValue thisObject, JsValue[] arguments)
- {
- return PrototypeObject.ToString(Construct(Arguments.Empty), Arguments.Empty);
- }
- /// <summary>
- /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.9.3
- /// </summary>
- /// <param name="arguments"></param>
- /// <returns></returns>
- public ObjectInstance Construct(JsValue[] arguments)
- {
- if (arguments.Length == 0)
- {
- return Construct(DateTime.UtcNow);
- }
- else if (arguments.Length == 1)
- {
- var v = TypeConverter.ToPrimitive(arguments[0]);
- if (v.IsString())
- {
- return Construct(((JsNumber) Parse(Undefined, Arguments.From(v)))._value);
- }
- return Construct(TypeConverter.ToNumber(v));
- }
- else
- {
- return Construct(ConstructTimeValue(arguments, useUtc: false));
- }
- }
- private double ConstructTimeValue(JsValue[] arguments, bool useUtc)
- {
- if (arguments.Length < 2)
- {
- ExceptionHelper.ThrowArgumentOutOfRangeException(nameof(arguments), "There must be at least two arguments.");
- }
- var y = TypeConverter.ToNumber(arguments[0]);
- var m = (int)TypeConverter.ToInteger(arguments[1]);
- var dt = arguments.Length > 2 ? (int)TypeConverter.ToInteger(arguments[2]) : 1;
- var h = arguments.Length > 3 ? (int)TypeConverter.ToInteger(arguments[3]) : 0;
- var min = arguments.Length > 4 ? (int)TypeConverter.ToInteger(arguments[4]) : 0;
- var s = arguments.Length > 5 ? (int)TypeConverter.ToInteger(arguments[5]) : 0;
- var milli = arguments.Length > 6 ? (int)TypeConverter.ToInteger(arguments[6]) : 0;
- for (int i = 2; i < arguments.Length; i++)
- {
- if (double.IsNaN(TypeConverter.ToNumber(arguments[i])))
- {
- return double.NaN;
- }
- }
- if (!double.IsNaN(y) && 0 <= TypeConverter.ToInteger(y) && TypeConverter.ToInteger(y) <= 99)
- {
- y += 1900;
- }
- var finalDate = DatePrototype.MakeDate(
- DatePrototype.MakeDay(y, m, dt),
- DatePrototype.MakeTime(h, min, s, milli));
- return TimeClip(useUtc ? finalDate : PrototypeObject.Utc(finalDate));
- }
- public DatePrototype PrototypeObject { get; private set; }
- public DateInstance Construct(DateTimeOffset value)
- {
- return Construct(value.UtcDateTime);
- }
- public DateInstance Construct(DateTime value)
- {
- var instance = new DateInstance(Engine)
- {
- Prototype = PrototypeObject,
- PrimitiveValue = FromDateTime(value),
- Extensible = true
- };
- return instance;
- }
- public DateInstance Construct(double time)
- {
- var instance = new DateInstance(Engine)
- {
- Prototype = PrototypeObject,
- PrimitiveValue = TimeClip(time),
- Extensible = true
- };
- return instance;
- }
- public static double TimeClip(double time)
- {
- if (double.IsInfinity(time) || double.IsNaN(time))
- {
- return double.NaN;
- }
- if (System.Math.Abs(time) > 8640000000000000)
- {
- return double.NaN;
- }
- return TypeConverter.ToInteger(time);
- }
- public double FromDateTime(DateTime dt)
- {
- var convertToUtcAfter = (dt.Kind == DateTimeKind.Unspecified);
- var dateAsUtc = dt.Kind == DateTimeKind.Local
- ? dt.ToUniversalTime()
- : DateTime.SpecifyKind(dt, DateTimeKind.Utc);
- var result = (dateAsUtc - Epoch).TotalMilliseconds;
- if (convertToUtcAfter)
- {
- result = PrototypeObject.Utc(result);
- }
- return System.Math.Floor(result);
- }
- }
- }
|