DatePrototype.cs 46 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281
  1. using System;
  2. using System.Globalization;
  3. using System.Runtime.CompilerServices;
  4. using Jint.Collections;
  5. using Jint.Native.Object;
  6. using Jint.Native.Symbol;
  7. using Jint.Runtime;
  8. using Jint.Runtime.Descriptors;
  9. using Jint.Runtime.Interop;
  10. namespace Jint.Native.Date
  11. {
  12. /// <summary>
  13. /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.9.5
  14. /// </summary>
  15. public sealed class DatePrototype : ObjectInstance
  16. {
  17. // ES6 section 20.3.1.1 Time Values and Time Range
  18. private const double MinYear = -1000000.0;
  19. private const double MaxYear = -MinYear;
  20. private const double MinMonth = -10000000.0;
  21. private const double MaxMonth = -MinMonth;
  22. private readonly Realm _realm;
  23. private readonly DateConstructor _constructor;
  24. internal DatePrototype(
  25. Engine engine,
  26. Realm realm,
  27. DateConstructor constructor,
  28. ObjectPrototype objectPrototype)
  29. : base(engine)
  30. {
  31. _prototype = objectPrototype;
  32. _realm = realm;
  33. _constructor = constructor;
  34. }
  35. protected override void Initialize()
  36. {
  37. const PropertyFlag lengthFlags = PropertyFlag.Configurable;
  38. const PropertyFlag propertyFlags = PropertyFlag.Configurable | PropertyFlag.Writable;
  39. var properties = new PropertyDictionary(50, checkExistingKeys: false)
  40. {
  41. ["constructor"] = new PropertyDescriptor(_constructor, PropertyFlag.NonEnumerable),
  42. ["toString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toString", ToString, 0, lengthFlags), propertyFlags),
  43. ["toDateString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toDateString", ToDateString, 0, lengthFlags), propertyFlags),
  44. ["toTimeString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toTimeString", ToTimeString, 0, lengthFlags), propertyFlags),
  45. ["toLocaleString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toLocaleString", ToLocaleString, 0, lengthFlags), propertyFlags),
  46. ["toLocaleDateString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toLocaleDateString", ToLocaleDateString, 0, lengthFlags), propertyFlags),
  47. ["toLocaleTimeString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toLocaleTimeString", ToLocaleTimeString, 0, lengthFlags), propertyFlags),
  48. ["valueOf"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "valueOf", ValueOf, 0, lengthFlags), propertyFlags),
  49. ["getTime"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getTime", GetTime, 0, lengthFlags), propertyFlags),
  50. ["getFullYear"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getFullYear", GetFullYear, 0, lengthFlags), propertyFlags),
  51. ["getYear"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getYear", GetYear, 0, lengthFlags), propertyFlags),
  52. ["getUTCFullYear"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getUTCFullYear", GetUTCFullYear, 0, lengthFlags), propertyFlags),
  53. ["getMonth"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getMonth", GetMonth, 0, lengthFlags), propertyFlags),
  54. ["getUTCMonth"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getUTCMonth", GetUTCMonth, 0, lengthFlags), propertyFlags),
  55. ["getDate"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getDate", GetDate, 0, lengthFlags), propertyFlags),
  56. ["getUTCDate"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getUTCDate", GetUTCDate, 0, lengthFlags), propertyFlags),
  57. ["getDay"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getDay", GetDay, 0, lengthFlags), propertyFlags),
  58. ["getUTCDay"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getUTCDay", GetUTCDay, 0, lengthFlags), propertyFlags),
  59. ["getHours"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getHours", GetHours, 0, lengthFlags), propertyFlags),
  60. ["getUTCHours"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getUTCHours", GetUTCHours, 0, lengthFlags), propertyFlags),
  61. ["getMinutes"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getMinutes", GetMinutes, 0, lengthFlags), propertyFlags),
  62. ["getUTCMinutes"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getUTCMinutes", GetUTCMinutes, 0, lengthFlags), propertyFlags),
  63. ["getSeconds"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getSeconds", GetSeconds, 0, lengthFlags), propertyFlags),
  64. ["getUTCSeconds"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getUTCSeconds", GetUTCSeconds, 0, lengthFlags), propertyFlags),
  65. ["getMilliseconds"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getMilliseconds", GetMilliseconds, 0, lengthFlags), propertyFlags),
  66. ["getUTCMilliseconds"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getUTCMilliseconds", GetUTCMilliseconds, 0, lengthFlags), propertyFlags),
  67. ["getTimezoneOffset"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getTimezoneOffset", GetTimezoneOffset, 0, lengthFlags), propertyFlags),
  68. ["setTime"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setTime", SetTime, 1, lengthFlags), propertyFlags),
  69. ["setMilliseconds"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setMilliseconds", SetMilliseconds, 1, lengthFlags), propertyFlags),
  70. ["setUTCMilliseconds"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setUTCMilliseconds", SetUTCMilliseconds, 1, lengthFlags), propertyFlags),
  71. ["setSeconds"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setSeconds", SetSeconds, 2, lengthFlags), propertyFlags),
  72. ["setUTCSeconds"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setUTCSeconds", SetUTCSeconds, 2, lengthFlags), propertyFlags),
  73. ["setMinutes"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setMinutes", SetMinutes, 3, lengthFlags), propertyFlags),
  74. ["setUTCMinutes"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setUTCMinutes", SetUTCMinutes, 3, lengthFlags), propertyFlags),
  75. ["setHours"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setHours", SetHours, 4, lengthFlags), propertyFlags),
  76. ["setUTCHours"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setUTCHours", SetUTCHours, 4, lengthFlags), propertyFlags),
  77. ["setDate"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setDate", SetDate, 1, lengthFlags), propertyFlags),
  78. ["setUTCDate"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setUTCDate", SetUTCDate, 1, lengthFlags), propertyFlags),
  79. ["setMonth"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setMonth", SetMonth, 2, lengthFlags), propertyFlags),
  80. ["setUTCMonth"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setUTCMonth", SetUTCMonth, 2, lengthFlags), propertyFlags),
  81. ["setFullYear"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setFullYear", SetFullYear, 3, lengthFlags), propertyFlags),
  82. ["setYear"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setYear", SetYear, 1, lengthFlags), propertyFlags),
  83. ["setUTCFullYear"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setUTCFullYear", SetUTCFullYear, 3, lengthFlags), propertyFlags),
  84. ["toUTCString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toUTCString", ToUtcString, 0, lengthFlags), propertyFlags),
  85. ["toISOString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toISOString", ToISOString, 0, lengthFlags), propertyFlags),
  86. ["toJSON"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toJSON", ToJSON, 1, lengthFlags), propertyFlags)
  87. };
  88. SetProperties(properties);
  89. var symbols = new SymbolDictionary(1)
  90. {
  91. [GlobalSymbolRegistry.ToPrimitive] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "[Symbol.toPrimitive]", ToPrimitive, 1, PropertyFlag.Configurable), PropertyFlag.Configurable),
  92. };
  93. SetSymbols(symbols);
  94. }
  95. private JsValue ToPrimitive(JsValue thisObject, JsValue[] arguments)
  96. {
  97. var oi = thisObject as ObjectInstance;
  98. if (oi is null)
  99. {
  100. ExceptionHelper.ThrowTypeError(_realm);
  101. }
  102. var hint = arguments.At(0);
  103. if (!hint.IsString())
  104. {
  105. ExceptionHelper.ThrowTypeError(_realm);
  106. }
  107. var hintString = hint.ToString();
  108. var tryFirst = Types.None;
  109. if (hintString == "default" || hintString == "string")
  110. {
  111. tryFirst = Types.String;
  112. }
  113. else if (hintString == "number")
  114. {
  115. tryFirst = Types.Number;
  116. }
  117. else
  118. {
  119. ExceptionHelper.ThrowTypeError(_realm);
  120. }
  121. return TypeConverter.OrdinaryToPrimitive(oi, tryFirst);
  122. }
  123. private JsValue ValueOf(JsValue thisObj, JsValue[] arguments)
  124. {
  125. return EnsureDateInstance(thisObj).PrimitiveValue;
  126. }
  127. /// <summary>
  128. /// Converts a value to a <see cref="DateInstance"/> or throws a TypeError exception.
  129. /// c.f., http://www.ecma-international.org/ecma-262/5.1/#sec-15.9.5
  130. /// </summary>
  131. private DateInstance EnsureDateInstance(JsValue thisObj)
  132. {
  133. if (thisObj is DateInstance dateInstance)
  134. {
  135. return dateInstance;
  136. }
  137. ExceptionHelper.ThrowTypeError(_realm, "this is not a Date object");
  138. return null;
  139. }
  140. public JsValue ToString(JsValue thisObj, JsValue[] arg2)
  141. {
  142. var dateInstance = EnsureDateInstance(thisObj);
  143. if (double.IsNaN(dateInstance.PrimitiveValue))
  144. return "Invalid Date";
  145. var t = ToLocalTime(dateInstance.ToDateTime(), Engine.Options._LocalTimeZone);
  146. return t.ToString("ddd MMM dd yyyy HH:mm:ss ", CultureInfo.InvariantCulture) + TimeZoneString(t);
  147. }
  148. internal JsValue ToDateString(JsValue thisObj, JsValue[] arguments)
  149. {
  150. var dateInstance = EnsureDateInstance(thisObj);
  151. if (double.IsNaN(dateInstance.PrimitiveValue))
  152. return "Invalid Date";
  153. return ToLocalTime(dateInstance.ToDateTime(), Engine.Options._LocalTimeZone).ToString("ddd MMM dd yyyy", CultureInfo.InvariantCulture);
  154. }
  155. private JsValue ToTimeString(JsValue thisObj, JsValue[] arguments)
  156. {
  157. var dateInstance = EnsureDateInstance(thisObj);
  158. if (double.IsNaN(dateInstance.PrimitiveValue))
  159. return "Invalid Date";
  160. var t = ToLocalTime(dateInstance.ToDateTime(), Engine.Options._LocalTimeZone);
  161. var timeString = t.ToString("HH:mm:ss ", CultureInfo.InvariantCulture);
  162. var timeZoneString = TimeZoneString(t);
  163. return timeString + timeZoneString;
  164. }
  165. private static string TimeZoneString(DateTimeOffset t)
  166. {
  167. return t.ToString("'GMT'K", CultureInfo.InvariantCulture).Replace(":", "");
  168. }
  169. private JsValue ToLocaleString(JsValue thisObj, JsValue[] arguments)
  170. {
  171. var dateInstance = EnsureDateInstance(thisObj);
  172. if (double.IsNaN(dateInstance.PrimitiveValue))
  173. return "Invalid Date";
  174. return ToLocalTime(dateInstance.ToDateTime(), Engine.Options._LocalTimeZone).ToString("F", Engine.Options._Culture);
  175. }
  176. private JsValue ToLocaleDateString(JsValue thisObj, JsValue[] arguments)
  177. {
  178. var dateInstance = EnsureDateInstance(thisObj);
  179. if (double.IsNaN(dateInstance.PrimitiveValue))
  180. return "Invalid Date";
  181. return ToLocalTime(dateInstance.ToDateTime(), Engine.Options._LocalTimeZone).ToString("D", Engine.Options._Culture);
  182. }
  183. private JsValue ToLocaleTimeString(JsValue thisObj, JsValue[] arguments)
  184. {
  185. var dateInstance = EnsureDateInstance(thisObj);
  186. if (double.IsNaN(dateInstance.PrimitiveValue))
  187. return "Invalid Date";
  188. return ToLocalTime(dateInstance.ToDateTime(), Engine.Options._LocalTimeZone).ToString("T", Engine.Options._Culture);
  189. }
  190. private JsValue GetTime(JsValue thisObj, JsValue[] arguments)
  191. {
  192. var t = EnsureDateInstance(thisObj).PrimitiveValue;
  193. if (!IsFinite(t))
  194. {
  195. return JsNumber.DoubleNaN;
  196. }
  197. return t;
  198. }
  199. private JsValue GetFullYear(JsValue thisObj, JsValue[] arguments)
  200. {
  201. var t = EnsureDateInstance(thisObj).PrimitiveValue;
  202. if (!IsFinite(t))
  203. {
  204. return JsNumber.DoubleNaN;
  205. }
  206. return YearFromTime(LocalTime(t));
  207. }
  208. private JsValue GetYear(JsValue thisObj, JsValue[] arguments)
  209. {
  210. var t = EnsureDateInstance(thisObj).PrimitiveValue;
  211. if (!IsFinite(t))
  212. {
  213. return JsNumber.DoubleNaN;
  214. }
  215. return YearFromTime(LocalTime(t)) - 1900;
  216. }
  217. private JsValue GetUTCFullYear(JsValue thisObj, JsValue[] arguments)
  218. {
  219. var t = EnsureDateInstance(thisObj).PrimitiveValue;
  220. if (!IsFinite(t))
  221. {
  222. return JsNumber.DoubleNaN;
  223. }
  224. return YearFromTime(t);
  225. }
  226. private JsValue GetMonth(JsValue thisObj, JsValue[] arguments)
  227. {
  228. var t = EnsureDateInstance(thisObj).PrimitiveValue;
  229. if (!IsFinite(t))
  230. {
  231. return JsNumber.DoubleNaN;
  232. }
  233. return MonthFromTime(LocalTime(t));
  234. }
  235. private JsValue GetUTCMonth(JsValue thisObj, JsValue[] arguments)
  236. {
  237. var t = EnsureDateInstance(thisObj).PrimitiveValue;
  238. if (!IsFinite(t))
  239. {
  240. return JsNumber.DoubleNaN;
  241. }
  242. return MonthFromTime(t);
  243. }
  244. private JsValue GetDate(JsValue thisObj, JsValue[] arguments)
  245. {
  246. var t = EnsureDateInstance(thisObj).PrimitiveValue;
  247. if (!IsFinite(t))
  248. {
  249. return JsNumber.DoubleNaN;
  250. }
  251. return DateFromTime(LocalTime(t));
  252. }
  253. private JsValue GetUTCDate(JsValue thisObj, JsValue[] arguments)
  254. {
  255. var t = EnsureDateInstance(thisObj).PrimitiveValue;
  256. if (!IsFinite(t))
  257. {
  258. return JsNumber.DoubleNaN;
  259. }
  260. return DateFromTime(t);
  261. }
  262. private JsValue GetDay(JsValue thisObj, JsValue[] arguments)
  263. {
  264. var t = EnsureDateInstance(thisObj).PrimitiveValue;
  265. if (!IsFinite(t))
  266. {
  267. return JsNumber.DoubleNaN;
  268. }
  269. return WeekDay(LocalTime(t));
  270. }
  271. private JsValue GetUTCDay(JsValue thisObj, JsValue[] arguments)
  272. {
  273. var t = EnsureDateInstance(thisObj).PrimitiveValue;
  274. if (!IsFinite(t))
  275. {
  276. return JsNumber.DoubleNaN;
  277. }
  278. return WeekDay(t);
  279. }
  280. private JsValue GetHours(JsValue thisObj, JsValue[] arguments)
  281. {
  282. var t = EnsureDateInstance(thisObj).PrimitiveValue;
  283. if (!IsFinite(t))
  284. {
  285. return JsNumber.DoubleNaN;
  286. }
  287. return HourFromTime(LocalTime(t));
  288. }
  289. private JsValue GetUTCHours(JsValue thisObj, JsValue[] arguments)
  290. {
  291. var t = EnsureDateInstance(thisObj).PrimitiveValue;
  292. if (!IsFinite(t))
  293. {
  294. return JsNumber.DoubleNaN;
  295. }
  296. return HourFromTime(t);
  297. }
  298. private JsValue GetMinutes(JsValue thisObj, JsValue[] arguments)
  299. {
  300. var t = EnsureDateInstance(thisObj).PrimitiveValue;
  301. if (!IsFinite(t))
  302. {
  303. return JsNumber.DoubleNaN;
  304. }
  305. return MinFromTime(LocalTime(t));
  306. }
  307. private JsValue GetUTCMinutes(JsValue thisObj, JsValue[] arguments)
  308. {
  309. var t = EnsureDateInstance(thisObj).PrimitiveValue;
  310. if (!IsFinite(t))
  311. {
  312. return JsNumber.DoubleNaN;
  313. }
  314. return MinFromTime(t);
  315. }
  316. private JsValue GetSeconds(JsValue thisObj, JsValue[] arguments)
  317. {
  318. var t = EnsureDateInstance(thisObj).PrimitiveValue;
  319. if (!IsFinite(t))
  320. {
  321. return JsNumber.DoubleNaN;
  322. }
  323. return SecFromTime(LocalTime(t));
  324. }
  325. private JsValue GetUTCSeconds(JsValue thisObj, JsValue[] arguments)
  326. {
  327. var t = EnsureDateInstance(thisObj).PrimitiveValue;
  328. if (!IsFinite(t))
  329. {
  330. return JsNumber.DoubleNaN;
  331. }
  332. return SecFromTime(t);
  333. }
  334. private JsValue GetMilliseconds(JsValue thisObj, JsValue[] arguments)
  335. {
  336. var t = EnsureDateInstance(thisObj).PrimitiveValue;
  337. if (!IsFinite(t))
  338. {
  339. return JsNumber.DoubleNaN;
  340. }
  341. return MsFromTime(LocalTime(t));
  342. }
  343. private JsValue GetUTCMilliseconds(JsValue thisObj, JsValue[] arguments)
  344. {
  345. var t = EnsureDateInstance(thisObj).PrimitiveValue;
  346. if (!IsFinite(t))
  347. {
  348. return JsNumber.DoubleNaN;
  349. }
  350. return MsFromTime(t);
  351. }
  352. private JsValue GetTimezoneOffset(JsValue thisObj, JsValue[] arguments)
  353. {
  354. var t = EnsureDateInstance(thisObj).PrimitiveValue;
  355. if (!IsFinite(t))
  356. {
  357. return JsNumber.DoubleNaN;
  358. }
  359. return (int) (t - LocalTime(t))/MsPerMinute;
  360. }
  361. private JsValue SetTime(JsValue thisObj, JsValue[] arguments)
  362. {
  363. return EnsureDateInstance(thisObj).PrimitiveValue = TimeClip(TypeConverter.ToNumber(arguments.At(0)));
  364. }
  365. private JsValue SetMilliseconds(JsValue thisObj, JsValue[] arguments)
  366. {
  367. var t = LocalTime(EnsureDateInstance(thisObj).PrimitiveValue);
  368. if (!IsFinite(t))
  369. {
  370. return JsNumber.DoubleNaN;
  371. }
  372. var time = MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), TypeConverter.ToNumber(arguments.At(0)));
  373. var u = TimeClip(Utc(MakeDate(Day(t), time)));
  374. thisObj.As<DateInstance>().PrimitiveValue = u;
  375. return u;
  376. }
  377. private JsValue SetUTCMilliseconds(JsValue thisObj, JsValue[] arguments)
  378. {
  379. var t = EnsureDateInstance(thisObj).PrimitiveValue;
  380. if (!IsFinite(t))
  381. {
  382. return double.NaN;
  383. }
  384. var time = MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), TypeConverter.ToNumber(arguments.At(0)));
  385. var u = TimeClip(MakeDate(Day(t), time));
  386. thisObj.As<DateInstance>().PrimitiveValue = u;
  387. return u;
  388. }
  389. private JsValue SetSeconds(JsValue thisObj, JsValue[] arguments)
  390. {
  391. var t = LocalTime(EnsureDateInstance(thisObj).PrimitiveValue);
  392. if (!IsFinite(t))
  393. {
  394. return JsNumber.DoubleNaN;
  395. }
  396. var s = TypeConverter.ToNumber(arguments.At(0));
  397. var milli = arguments.Length <= 1 ? MsFromTime(t) : TypeConverter.ToNumber(arguments.At(1));
  398. var date = MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli));
  399. var u = TimeClip(Utc(date));
  400. thisObj.As<DateInstance>().PrimitiveValue = u;
  401. return u;
  402. }
  403. private JsValue SetUTCSeconds(JsValue thisObj, JsValue[] arguments)
  404. {
  405. var t = EnsureDateInstance(thisObj).PrimitiveValue;
  406. if (!IsFinite(t))
  407. {
  408. return JsNumber.DoubleNaN;
  409. }
  410. var s = TypeConverter.ToNumber(arguments.At(0));
  411. var milli = arguments.Length <= 1 ? MsFromTime(t) : TypeConverter.ToNumber(arguments.At(1));
  412. var date = MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli));
  413. var u = TimeClip(date);
  414. thisObj.As<DateInstance>().PrimitiveValue = u;
  415. return u;
  416. }
  417. private JsValue SetMinutes(JsValue thisObj, JsValue[] arguments)
  418. {
  419. var t = LocalTime(EnsureDateInstance(thisObj).PrimitiveValue);
  420. if (!IsFinite(t))
  421. {
  422. return JsNumber.DoubleNaN;
  423. }
  424. var m = TypeConverter.ToNumber(arguments.At(0));
  425. var s = arguments.Length <= 1 ? SecFromTime(t) : TypeConverter.ToNumber(arguments.At(1));
  426. var milli = arguments.Length <= 2 ? MsFromTime(t) : TypeConverter.ToNumber(arguments.At(2));
  427. var date = MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli));
  428. var u = TimeClip(Utc(date));
  429. thisObj.As<DateInstance>().PrimitiveValue = u;
  430. return u;
  431. }
  432. private JsValue SetUTCMinutes(JsValue thisObj, JsValue[] arguments)
  433. {
  434. var t = EnsureDateInstance(thisObj).PrimitiveValue;
  435. if (!IsFinite(t))
  436. {
  437. return JsNumber.DoubleNaN;
  438. }
  439. var m = TypeConverter.ToNumber(arguments.At(0));
  440. var s = arguments.Length <= 1 ? SecFromTime(t) : TypeConverter.ToNumber(arguments.At(1));
  441. var milli = arguments.Length <= 2 ? MsFromTime(t) : TypeConverter.ToNumber(arguments.At(2));
  442. var date = MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli));
  443. var u = TimeClip(date);
  444. thisObj.As<DateInstance>().PrimitiveValue = u;
  445. return u;
  446. }
  447. private JsValue SetHours(JsValue thisObj, JsValue[] arguments)
  448. {
  449. var t = LocalTime(EnsureDateInstance(thisObj).PrimitiveValue);
  450. if (!IsFinite(t))
  451. {
  452. return JsNumber.DoubleNaN;
  453. }
  454. var h = TypeConverter.ToNumber(arguments.At(0));
  455. var m = arguments.Length <= 1 ? MinFromTime(t) : TypeConverter.ToNumber(arguments.At(1));
  456. var s = arguments.Length <= 2 ? SecFromTime(t) : TypeConverter.ToNumber(arguments.At(2));
  457. var milli = arguments.Length <= 3 ? MsFromTime(t) : TypeConverter.ToNumber(arguments.At(3));
  458. var date = MakeDate(Day(t), MakeTime(h, m, s, milli));
  459. var u = TimeClip(Utc(date));
  460. thisObj.As<DateInstance>().PrimitiveValue = u;
  461. return u;
  462. }
  463. private JsValue SetUTCHours(JsValue thisObj, JsValue[] arguments)
  464. {
  465. var t = EnsureDateInstance(thisObj).PrimitiveValue;
  466. if (!IsFinite(t))
  467. {
  468. return JsNumber.DoubleNaN;
  469. }
  470. var h = TypeConverter.ToNumber(arguments.At(0));
  471. var m = arguments.Length <= 1 ? MinFromTime(t) : TypeConverter.ToNumber(arguments.At(1));
  472. var s = arguments.Length <= 2 ? SecFromTime(t) : TypeConverter.ToNumber(arguments.At(2));
  473. var milli = arguments.Length <= 3 ? MsFromTime(t) : TypeConverter.ToNumber(arguments.At(3));
  474. var date = MakeDate(Day(t), MakeTime(h, m, s, milli));
  475. var u = TimeClip(date);
  476. thisObj.As<DateInstance>().PrimitiveValue = u;
  477. return u;
  478. }
  479. private JsValue SetDate(JsValue thisObj, JsValue[] arguments)
  480. {
  481. var t = LocalTime(EnsureDateInstance(thisObj).PrimitiveValue);
  482. if (!IsFinite(t))
  483. {
  484. return JsNumber.DoubleNaN;
  485. }
  486. var dt = TypeConverter.ToNumber(arguments.At(0));
  487. var (year, month, __) = YearMonthDayFromTime(t);
  488. var newDate = MakeDate(MakeDay(year, month, dt), TimeWithinDay(t));
  489. var u = TimeClip(Utc(newDate));
  490. thisObj.As<DateInstance>().PrimitiveValue = u;
  491. return u;
  492. }
  493. private JsValue SetUTCDate(JsValue thisObj, JsValue[] arguments)
  494. {
  495. var t = EnsureDateInstance(thisObj).PrimitiveValue;
  496. if (!IsFinite(t))
  497. {
  498. return JsNumber.DoubleNaN;
  499. }
  500. var dt = TypeConverter.ToNumber(arguments.At(0));
  501. var newDate = MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), dt), TimeWithinDay(t));
  502. var u = TimeClip(newDate);
  503. thisObj.As<DateInstance>().PrimitiveValue = u;
  504. return u;
  505. }
  506. private JsValue SetMonth(JsValue thisObj, JsValue[] arguments)
  507. {
  508. var t = LocalTime(EnsureDateInstance(thisObj).PrimitiveValue);
  509. if (!IsFinite(t))
  510. {
  511. return JsNumber.DoubleNaN;
  512. }
  513. var m = TypeConverter.ToNumber(arguments.At(0));
  514. var dt = arguments.Length <= 1 ? DateFromTime(t) : TypeConverter.ToNumber(arguments.At(1));
  515. var newDate = MakeDate(MakeDay(YearFromTime(t), m, dt), TimeWithinDay(t));
  516. var u = TimeClip(Utc(newDate));
  517. thisObj.As<DateInstance>().PrimitiveValue = u;
  518. return u;
  519. }
  520. private JsValue SetUTCMonth(JsValue thisObj, JsValue[] arguments)
  521. {
  522. var t = EnsureDateInstance(thisObj).PrimitiveValue;
  523. if (!IsFinite(t))
  524. {
  525. return JsNumber.DoubleNaN;
  526. }
  527. var m = TypeConverter.ToNumber(arguments.At(0));
  528. var dt = arguments.Length <= 1 ? DateFromTime(t) : TypeConverter.ToNumber(arguments.At(1));
  529. var newDate = MakeDate(MakeDay(YearFromTime(t), m, dt), TimeWithinDay(t));
  530. var u = TimeClip(newDate);
  531. thisObj.As<DateInstance>().PrimitiveValue = u;
  532. return u;
  533. }
  534. private JsValue SetFullYear(JsValue thisObj, JsValue[] arguments)
  535. {
  536. var thisTime = EnsureDateInstance(thisObj).PrimitiveValue;
  537. var t = double.IsNaN(thisTime) ? 0 : LocalTime(thisTime);
  538. var y = TypeConverter.ToNumber(arguments.At(0));
  539. var m = arguments.Length <= 1 ? MonthFromTime(t) : TypeConverter.ToNumber(arguments.At(1));
  540. var dt = arguments.Length <= 2 ? DateFromTime(t) : TypeConverter.ToNumber(arguments.At(2));
  541. var newDate = MakeDate(MakeDay(y, m, dt), TimeWithinDay(t));
  542. var u = TimeClip(Utc(newDate));
  543. thisObj.As<DateInstance>().PrimitiveValue = u;
  544. return u;
  545. }
  546. private JsValue SetYear(JsValue thisObj, JsValue[] arguments)
  547. {
  548. var thisTime = EnsureDateInstance(thisObj).PrimitiveValue;
  549. var t = double.IsNaN(thisTime) ? 0 : LocalTime(thisTime);
  550. var y = TypeConverter.ToNumber(arguments.At(0));
  551. if (double.IsNaN(y))
  552. {
  553. EnsureDateInstance(thisObj).PrimitiveValue = double.NaN;
  554. return JsNumber.DoubleNaN;
  555. }
  556. var fy = TypeConverter.ToInteger(y);
  557. if (y >= 0 && y <= 99)
  558. {
  559. fy += 1900;
  560. }
  561. var newDate = MakeDay(fy, MonthFromTime(t), DateFromTime(t));
  562. var u = Utc(MakeDate(newDate, TimeWithinDay(t)));
  563. EnsureDateInstance(thisObj).PrimitiveValue = TimeClip(u);
  564. return u;
  565. }
  566. private JsValue SetUTCFullYear(JsValue thisObj, JsValue[] arguments)
  567. {
  568. var thisTime = EnsureDateInstance(thisObj).PrimitiveValue;
  569. var t = (long) (double.IsNaN(thisTime) ? 0 : thisTime);
  570. var y = TypeConverter.ToNumber(arguments.At(0));
  571. var m = arguments.Length <= 1 ? MonthFromTime(t) : TypeConverter.ToNumber(arguments.At(1));
  572. var dt = arguments.Length <= 2 ? DateFromTime(t) : TypeConverter.ToNumber(arguments.At(2));
  573. var newDate = MakeDate(MakeDay(y, m, dt), TimeWithinDay(t));
  574. var u = TimeClip(newDate);
  575. thisObj.As<DateInstance>().PrimitiveValue = u;
  576. return u;
  577. }
  578. private JsValue ToUtcString(JsValue thisObj, JsValue[] arguments)
  579. {
  580. var thisTime = EnsureDateInstance(thisObj);
  581. if (!IsFinite(thisTime.PrimitiveValue))
  582. {
  583. return "Invalid Date";
  584. }
  585. return thisTime.ToDateTime().ToUniversalTime().ToString("ddd, dd MMM yyyy HH:mm:ss 'GMT'", CultureInfo.InvariantCulture);
  586. }
  587. private JsValue ToISOString(JsValue thisObj, JsValue[] arguments)
  588. {
  589. var thisTime = EnsureDateInstance(thisObj);
  590. var t = thisTime.PrimitiveValue;
  591. if (!IsFinite(t))
  592. {
  593. ExceptionHelper.ThrowRangeError(_realm);
  594. }
  595. if (thisTime.DateTimeRangeValid)
  596. {
  597. // shortcut
  598. var dt = thisTime.ToDateTime();
  599. return $"{dt.Year:0000}-{dt.Month:00}-{dt.Day:00}T{dt.Hour:00}:{dt.Minute:00}:{dt.Second:00}.{dt.Millisecond:000}Z";
  600. }
  601. var h = HourFromTime(t);
  602. var m = MinFromTime(t);
  603. var s = SecFromTime(t);
  604. var ms = MsFromTime(t);
  605. if (h < 0) { h += HoursPerDay; }
  606. if (m < 0) { m += MinutesPerHour; }
  607. if (s < 0) { s += SecondsPerMinute; }
  608. if (ms < 0) { ms += MsPerSecond; }
  609. var (year, month, day) = YearMonthDayFromTime(t);
  610. return $"{year:0000}-{month:00}-{day:00}T{h:00}:{m:00}:{s:00}.{ms:000}Z";
  611. }
  612. private JsValue ToJSON(JsValue thisObj, JsValue[] arguments)
  613. {
  614. var o = TypeConverter.ToObject(_realm, thisObj);
  615. var tv = TypeConverter.ToPrimitive(o, Types.Number);
  616. if (tv.IsNumber() && !IsFinite(((JsNumber) tv)._value))
  617. {
  618. return Null;
  619. }
  620. var toIso = o.Get("toISOString", o);
  621. var callable = toIso as ICallable;
  622. if (callable is null)
  623. {
  624. ExceptionHelper.ThrowTypeError(_realm);
  625. }
  626. return callable.Call(o, Arguments.Empty);
  627. }
  628. public const int HoursPerDay = 24;
  629. public const int MinutesPerHour = 60;
  630. public const int SecondsPerMinute = 60;
  631. public const int MsPerSecond = 1000;
  632. public const int MsPerMinute = 60000;
  633. public const int MsPerHour = 3600000;
  634. public const long MsPerDay = 86400000;
  635. /// <summary>
  636. /// 15.9.1.2
  637. /// </summary>
  638. public static int Day(double t)
  639. {
  640. return (int) System.Math.Floor(t / MsPerDay);
  641. }
  642. /// <summary>
  643. /// 15.9.1.2
  644. /// </summary>
  645. public static double TimeWithinDay(double t)
  646. {
  647. var result = t % MsPerDay;
  648. if (result < 0)
  649. {
  650. result += MsPerDay;
  651. }
  652. return result;
  653. }
  654. /// <summary>
  655. /// The number of days in a year
  656. /// </summary>
  657. public static int DaysInYear(double y)
  658. {
  659. if (y%4 != 0)
  660. {
  661. return 365;
  662. }
  663. if (y%4 == 0 && y%100 != 0)
  664. {
  665. return 366;
  666. }
  667. if (y%100 == 0 && y%400 != 0)
  668. {
  669. return 365;
  670. }
  671. if (y%400 == 0)
  672. {
  673. return 366;
  674. }
  675. return 365;
  676. }
  677. /// <summary>
  678. /// The day number of the first day of the year.
  679. /// </summary>
  680. public static int DayFromYear(double y)
  681. {
  682. return (int) (365*(y - 1970)
  683. + System.Math.Floor((y - 1969)/4)
  684. - System.Math.Floor((y - 1901)/100)
  685. + System.Math.Floor((y - 1601)/400));
  686. }
  687. /// <summary>
  688. /// The time value of the start of the year
  689. /// </summary>
  690. public static long TimeFromYear(double y)
  691. {
  692. return MsPerDay*DayFromYear(y);
  693. }
  694. /// <summary>
  695. /// The year of a time value.
  696. /// </summary>
  697. public static int YearFromTime(double t)
  698. {
  699. var (year, _, _) = YearMonthDayFromTime(t);
  700. return year;
  701. }
  702. /// <summary>
  703. /// <value>true</value> if the time is within a leap year, <value>false</value> otherwise
  704. /// </summary>
  705. public static int InLeapYear(double t)
  706. {
  707. var daysInYear = DaysInYear(YearFromTime(t));
  708. if (daysInYear == 365)
  709. {
  710. return 0;
  711. }
  712. if (daysInYear == 366)
  713. {
  714. return 1;
  715. }
  716. ExceptionHelper.ThrowArgumentException();
  717. return 0;
  718. }
  719. /// <summary>
  720. /// The month number of a time value.
  721. /// </summary>
  722. public static int MonthFromTime(double t)
  723. {
  724. var dayWithinYear = DayWithinYear(t);
  725. var inLeapYear = InLeapYear(t);
  726. if (dayWithinYear < 31)
  727. {
  728. return 0;
  729. }
  730. if (dayWithinYear < 59 + inLeapYear)
  731. {
  732. return 1;
  733. }
  734. if (dayWithinYear < 90 + inLeapYear)
  735. {
  736. return 2;
  737. }
  738. if (dayWithinYear < 120 + inLeapYear)
  739. {
  740. return 3;
  741. }
  742. if (dayWithinYear < 151 + inLeapYear)
  743. {
  744. return 4;
  745. }
  746. if (dayWithinYear < 181 + inLeapYear)
  747. {
  748. return 5;
  749. }
  750. if (dayWithinYear < 212 + inLeapYear)
  751. {
  752. return 6;
  753. }
  754. if (dayWithinYear < 243 + inLeapYear)
  755. {
  756. return 7;
  757. }
  758. if (dayWithinYear < 273 + inLeapYear)
  759. {
  760. return 8;
  761. }
  762. if (dayWithinYear < 304 + inLeapYear)
  763. {
  764. return 9;
  765. }
  766. if (dayWithinYear < 334 + inLeapYear)
  767. {
  768. return 10;
  769. }
  770. if (dayWithinYear < 365 + inLeapYear)
  771. {
  772. return 11;
  773. }
  774. ExceptionHelper.ThrowInvalidOperationException();
  775. return 0;
  776. }
  777. public static int DayWithinYear(double t)
  778. {
  779. return Day(t) - DayFromYear(YearFromTime(t));
  780. }
  781. public static int DateFromTime(double t)
  782. {
  783. var monthFromTime = MonthFromTime(t);
  784. var dayWithinYear = DayWithinYear(t);
  785. if (monthFromTime == 0)
  786. {
  787. return dayWithinYear + 1;
  788. }
  789. if (monthFromTime== 1)
  790. {
  791. return dayWithinYear - 30;
  792. }
  793. if (monthFromTime == 2)
  794. {
  795. return dayWithinYear - 58 - InLeapYear(t);
  796. }
  797. if (monthFromTime == 3)
  798. {
  799. return dayWithinYear - 89 - InLeapYear(t);
  800. }
  801. if (monthFromTime == 4)
  802. {
  803. return dayWithinYear - 119 - InLeapYear(t);
  804. }
  805. if (monthFromTime == 5)
  806. {
  807. return dayWithinYear - 150 - InLeapYear(t);
  808. }
  809. if (monthFromTime == 6)
  810. {
  811. return dayWithinYear - 180 - InLeapYear(t);
  812. }
  813. if (monthFromTime == 7)
  814. {
  815. return dayWithinYear - 211 - InLeapYear(t);
  816. }
  817. if (monthFromTime == 8)
  818. {
  819. return dayWithinYear - 242 - InLeapYear(t);
  820. }
  821. if (monthFromTime == 9)
  822. {
  823. return dayWithinYear - 272 - InLeapYear(t);
  824. }
  825. if (monthFromTime == 10)
  826. {
  827. return dayWithinYear - 303 - InLeapYear(t);
  828. }
  829. if (monthFromTime == 11)
  830. {
  831. return dayWithinYear - 333 - InLeapYear(t);
  832. }
  833. ExceptionHelper.ThrowInvalidOperationException();
  834. return 0;
  835. }
  836. /// <summary>
  837. /// The weekday for a particular time value.
  838. /// </summary>
  839. public static int WeekDay(double t)
  840. {
  841. return (Day(t) + 4)%7;
  842. }
  843. public long LocalTza => (long) Engine.Options._LocalTimeZone.BaseUtcOffset.TotalMilliseconds;
  844. public double DaylightSavingTa(double t)
  845. {
  846. if (double.IsNaN(t))
  847. {
  848. return t;
  849. }
  850. var year = YearFromTime(t);
  851. var timeInYear = t - TimeFromYear(year);
  852. if (double.IsInfinity(timeInYear) || double.IsNaN(timeInYear))
  853. {
  854. return 0;
  855. }
  856. if (year < 9999 && year > -9999 && year != 0)
  857. {
  858. // in DateTimeOffset range so we can use it
  859. }
  860. else
  861. {
  862. // use similar leap-ed year
  863. var isLeapYear = InLeapYear((long) t) == 1;
  864. year = isLeapYear ? 2000 : 1999;
  865. }
  866. var dateTime = new DateTime(year, 1, 1).AddMilliseconds(timeInYear);
  867. return Engine.Options._LocalTimeZone.IsDaylightSavingTime(dateTime) ? MsPerHour : 0;
  868. }
  869. private static DateTimeOffset ToLocalTime(DateTime t, TimeZoneInfo timeZone)
  870. {
  871. return t.Kind switch
  872. {
  873. DateTimeKind.Local => new DateTimeOffset(TimeZoneInfo.ConvertTime(t.ToUniversalTime(), timeZone), timeZone.GetUtcOffset(t)),
  874. DateTimeKind.Utc => new DateTimeOffset(TimeZoneInfo.ConvertTime(t, timeZone), timeZone.GetUtcOffset(t)),
  875. _ => t
  876. };
  877. }
  878. public double LocalTime(double t)
  879. {
  880. if (!IsFinite(t))
  881. {
  882. return double.NaN;
  883. }
  884. return (long) (t + LocalTza + DaylightSavingTa((long) t));
  885. }
  886. public double Utc(double t)
  887. {
  888. return t - LocalTza - DaylightSavingTa(t - LocalTza);
  889. }
  890. public static int HourFromTime(double t)
  891. {
  892. var hours = System.Math.Floor(t / MsPerHour) % HoursPerDay;
  893. if (hours < 0)
  894. {
  895. hours += HoursPerDay;
  896. }
  897. return (int) hours;
  898. }
  899. public static int MinFromTime(double t)
  900. {
  901. var minutes = System.Math.Floor(t / MsPerMinute) % MinutesPerHour;
  902. if (minutes < 0)
  903. {
  904. minutes += MinutesPerHour;
  905. }
  906. return (int) minutes;
  907. }
  908. public static int SecFromTime(double t)
  909. {
  910. var seconds = System.Math.Floor(t / MsPerSecond) % SecondsPerMinute;
  911. if (seconds < 0)
  912. {
  913. seconds += SecondsPerMinute;
  914. }
  915. return (int) seconds;
  916. }
  917. public static int MsFromTime(double t)
  918. {
  919. var milli = t % MsPerSecond;
  920. if (milli < 0)
  921. {
  922. milli += MsPerSecond;
  923. }
  924. return (int) milli;
  925. }
  926. public static double MakeTime(double hour, double min, double sec, double ms)
  927. {
  928. if (!AreFinite(hour, min, sec, ms))
  929. {
  930. return double.NaN;
  931. }
  932. var h = TypeConverter.ToInteger(hour);
  933. var m = TypeConverter.ToInteger(min);
  934. var s = TypeConverter.ToInteger(sec);
  935. var milli = TypeConverter.ToInteger(ms);
  936. var t = h*MsPerHour + m*MsPerMinute + s*MsPerSecond + milli;
  937. return t;
  938. }
  939. public static double MakeDay(double year, double month, double date)
  940. {
  941. if ((year < MinYear || year > MaxYear) || month < MinMonth || month > MaxMonth || !AreFinite(year, month, date))
  942. {
  943. return double.NaN;
  944. }
  945. var y = (long) TypeConverter.ToInteger(year);
  946. var m = (long) TypeConverter.ToInteger(month);
  947. y += m / 12;
  948. m %= 12;
  949. if (m < 0) {
  950. m += 12;
  951. y -= 1;
  952. }
  953. // kYearDelta is an arbitrary number such that:
  954. // a) kYearDelta = -1 (mod 400)
  955. // b) year + kYearDelta > 0 for years in the range defined by
  956. // ECMA 262 - 15.9.1.1, i.e. upto 100,000,000 days on either side of
  957. // Jan 1 1970. This is required so that we don't run into integer
  958. // division of negative numbers.
  959. // c) there shouldn't be an overflow for 32-bit integers in the following
  960. // operations.
  961. const int kYearDelta = 399999;
  962. const int kBaseDay =
  963. 365 * (1970 + kYearDelta) + (1970 + kYearDelta) / 4 -
  964. (1970 + kYearDelta) / 100 + (1970 + kYearDelta) / 400;
  965. long dayFromYear = 365 * (y + kYearDelta) + (y + kYearDelta) / 4 -
  966. (y + kYearDelta) / 100 + (y + kYearDelta) / 400 - kBaseDay;
  967. if ((y % 4 != 0) || (y % 100 == 0 && y % 400 != 0)) {
  968. var dayFromMonth = new [] {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
  969. dayFromYear += dayFromMonth[m];
  970. } else {
  971. var dayFromMonthLeapYear = new [] {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335};
  972. dayFromYear += dayFromMonthLeapYear[m];
  973. }
  974. return dayFromYear - 1 + TypeConverter.ToInteger(date);
  975. }
  976. public static double MakeDate(double day, double time)
  977. {
  978. if (!AreFinite(day, time))
  979. {
  980. return double.NaN;
  981. }
  982. return day * MsPerDay + time;
  983. }
  984. public static double TimeClip(double time)
  985. {
  986. if (!IsFinite(time))
  987. {
  988. return double.NaN;
  989. }
  990. if (System.Math.Abs(time) > 8640000000000000)
  991. {
  992. return double.NaN;
  993. }
  994. return (long) time + 0;
  995. }
  996. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  997. private static bool IsFinite(double value)
  998. {
  999. return !double.IsNaN(value) && !double.IsInfinity(value);
  1000. }
  1001. private static bool AreFinite(double value1, double value2)
  1002. {
  1003. return IsFinite(value1) && IsFinite(value2);
  1004. }
  1005. private static bool AreFinite(double value1, double value2, double value3)
  1006. {
  1007. return IsFinite(value1) && IsFinite(value2) && IsFinite(value3);
  1008. }
  1009. private static bool AreFinite(double value1, double value2, double value3, double value4)
  1010. {
  1011. return IsFinite(value1) && IsFinite(value2) && IsFinite(value3) && IsFinite(value4);
  1012. }
  1013. private readonly struct Date
  1014. {
  1015. public Date(int year, int month, int day)
  1016. {
  1017. Year = year;
  1018. Month = month;
  1019. Day = day;
  1020. }
  1021. public readonly int Year;
  1022. public readonly int Month;
  1023. public readonly int Day;
  1024. public void Deconstruct(out int year, out int month, out int day)
  1025. {
  1026. year = Year;
  1027. month = Month;
  1028. day = Day;
  1029. }
  1030. }
  1031. private static readonly int[] kDaysInMonths = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  1032. private static Date YearMonthDayFromTime(double t) => YearMonthDayFromDays((long) (t / 1000 / 60 / 60 / 24));
  1033. private static Date YearMonthDayFromDays(long days)
  1034. {
  1035. const int kDaysIn4Years = 4 * 365 + 1;
  1036. const int kDaysIn100Years = 25 * kDaysIn4Years - 1;
  1037. const int kDaysIn400Years = 4 * kDaysIn100Years + 1;
  1038. const int kDays1970to2000 = 30 * 365 + 7;
  1039. const int kDaysOffset =
  1040. 1000 * kDaysIn400Years + 5 * kDaysIn400Years - kDays1970to2000;
  1041. const int kYearsOffset = 400000;
  1042. days += kDaysOffset;
  1043. var year = 400 * (days / kDaysIn400Years) - kYearsOffset;
  1044. days %= kDaysIn400Years;
  1045. days--;
  1046. var yd1 = days / kDaysIn100Years;
  1047. days %= kDaysIn100Years;
  1048. year += 100 * yd1;
  1049. days++;
  1050. var yd2 = days / kDaysIn4Years;
  1051. days %= kDaysIn4Years;
  1052. year += 4 * yd2;
  1053. days--;
  1054. var yd3 = days / 365;
  1055. days %= 365;
  1056. year += yd3;
  1057. var is_leap = (yd1 == 0 || yd2 != 0) && yd3 == 0;
  1058. days += is_leap ? 1 : 0;
  1059. var month = 0;
  1060. var day = 0;
  1061. // Check if the date is after February.
  1062. if (days >= 31 + 28 + (is_leap ? 1 : 0))
  1063. {
  1064. days -= 31 + 28 + (is_leap ? 1 : 0);
  1065. // Find the date starting from March.
  1066. for (int i = 2; i < 12; i++)
  1067. {
  1068. if (days < kDaysInMonths[i])
  1069. {
  1070. month = i;
  1071. day = (int) (days + 1);
  1072. break;
  1073. }
  1074. days -= kDaysInMonths[i];
  1075. }
  1076. }
  1077. else
  1078. {
  1079. // Check January and February.
  1080. if (days < 31)
  1081. {
  1082. month = 0;
  1083. day = (int) (days + 1);
  1084. }
  1085. else
  1086. {
  1087. month = 1;
  1088. day = (int) (days - 31 + 1);
  1089. }
  1090. }
  1091. return new Date((int) year, month, day);
  1092. }
  1093. public override string ToString()
  1094. {
  1095. return "Date.prototype";
  1096. }
  1097. }
  1098. }