DateImplementation.cpp 59 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #include "RuntimeLibraryPch.h"
  6. #include "DateImplementationData.h"
  7. #include "CharClassifier.h"
  8. namespace Js {
  9. static double ConvertToInteger(double dbl)
  10. {
  11. Assert(Js::NumberUtilities::IsFinite(dbl));
  12. if (Js::NumberUtilities::LuHiDbl(dbl) & 0x80000000)
  13. {
  14. Js::NumberUtilities::LuHiDbl(dbl) &= 0x7FFFFFFF;
  15. dbl = floor(dbl);
  16. Js::NumberUtilities::LuHiDbl(dbl) |= 0x80000000;
  17. }
  18. else
  19. {
  20. dbl = floor(dbl);
  21. // We have to do this because some implementations map 0.5 to -0.
  22. Js::NumberUtilities::LuHiDbl(dbl) &= 0x7FFFFFFF;
  23. }
  24. return dbl;
  25. }
  26. static const double kdblHalfSecond = 0.5 / 86400.0;
  27. struct SZS
  28. {
  29. const char16 *psz; // string
  30. short cch; // length of string
  31. short szst; // type of entry
  32. int32 lwVal; // value
  33. };
  34. BEGIN_ENUM_BYTE(ParseStringTokenType)
  35. AmPm,
  36. Month,
  37. Day,
  38. Zone,
  39. BcAd,
  40. END_ENUM_BYTE()
  41. const static SZS g_rgszs[] =
  42. {
  43. #define Szs(sz, val) { _u(sz), _countof(_u(sz)) - 1, ParseStringTokenType::##szst, val }
  44. // bc and ad
  45. #undef szst
  46. #define szst BcAd
  47. Szs("bc", -1),
  48. Szs("b.c", -1),
  49. Szs("ad", +1),
  50. Szs("a.d", +1),
  51. // am and pm
  52. #undef szst
  53. #define szst AmPm
  54. Szs("am", -1),
  55. Szs("a.m", -1),
  56. Szs("pm", +1),
  57. Szs("p.m", +1),
  58. // time zones
  59. #undef szst
  60. #define szst Zone
  61. Szs("est", -5 * 60),
  62. Szs("edt", -4 * 60),
  63. Szs("cst", -6 * 60),
  64. Szs("cdt", -5 * 60),
  65. Szs("mst", -7 * 60),
  66. Szs("mdt", -6 * 60),
  67. Szs("pst", -8 * 60),
  68. Szs("pdt", -7 * 60),
  69. Szs("gmt", 0),
  70. Szs("utc", 0),
  71. // week days
  72. #undef szst
  73. #define szst Day
  74. Szs("sunday", 0),
  75. Szs("monday", 1),
  76. Szs("tuesday", 2),
  77. Szs("wednesday", 3),
  78. Szs("thursday", 4),
  79. Szs("friday", 5),
  80. Szs("saturday", 6),
  81. // months
  82. #undef szst
  83. #define szst Month
  84. Szs("january", 0),
  85. Szs("february", 1),
  86. Szs("march", 2),
  87. Szs("april", 3),
  88. Szs("may", 4),
  89. Szs("june", 5),
  90. Szs("july", 6),
  91. Szs("august", 7),
  92. Szs("september", 8),
  93. Szs("october", 9),
  94. Szs("november", 10),
  95. Szs("december", 11),
  96. #undef szst
  97. #undef Szs
  98. };
  99. const int32 kcszs = sizeof(g_rgszs) / sizeof(SZS);
  100. ///----------------------------------------------------------------------------
  101. ///----------------------------------------------------------------------------
  102. ///
  103. /// class DateImplementation
  104. ///
  105. ///----------------------------------------------------------------------------
  106. ///----------------------------------------------------------------------------
  107. DateImplementation::DateImplementation(double value)
  108. {
  109. // Assume DateImplementation is allocated in the recycler and is zero initialized
  110. // Do not stack allocate of this struct, as it doesn't initialize all fields.
  111. // If the stack allocated struct is copied in to recycler allocated ones, it
  112. // many introduce false reference from the stack
  113. Assert(!ThreadContext::IsOnStack(this));
  114. AssertValue<byte>(this, 0, sizeof(DateImplementation));
  115. m_modified = false;
  116. SetTvUtc(value);
  117. };
  118. double
  119. DateImplementation::GetMilliSeconds()
  120. {
  121. return m_tvUtc;
  122. }
  123. double
  124. DateImplementation::NowFromHiResTimer(ScriptContext* scriptContext)
  125. {
  126. // Use current time.
  127. return scriptContext->GetThreadContext()->GetHiResTimer()->Now();
  128. }
  129. double
  130. DateImplementation::NowInMilliSeconds(ScriptContext * scriptContext)
  131. {
  132. return DoubleToTvUtc(DateImplementation::NowFromHiResTimer(scriptContext));
  133. }
  134. JavascriptString*
  135. DateImplementation::GetString(DateStringFormat dsf,
  136. ScriptContext* requestContext, DateTimeFlag noDateTime)
  137. {
  138. if (JavascriptNumber::IsNan(m_tvUtc))
  139. {
  140. return requestContext->GetLibrary()->GetInvalidDateString();
  141. }
  142. switch (dsf)
  143. {
  144. default:
  145. EnsureYmdLcl(requestContext);
  146. return GetDateDefaultString(&m_ymdLcl, &m_tzd, noDateTime, requestContext);
  147. #ifdef ENABLE_GLOBALIZATION
  148. case DateStringFormat::Locale:
  149. EnsureYmdLcl(requestContext);
  150. if( m_ymdLcl.year > 1600 && m_ymdLcl.year < 10000 )
  151. {
  152. // The year falls in the range which can be handled by both the Win32
  153. // function GetDateFormat and the COM+ date type
  154. // - the latter is for forward compatibility with JS 7.
  155. JavascriptString *bs = GetDateLocaleString(&m_ymdLcl, &m_tzd, noDateTime, requestContext);
  156. if (bs != nullptr)
  157. {
  158. return bs;
  159. }
  160. else
  161. {
  162. return GetDateDefaultString(&m_ymdLcl, &m_tzd, noDateTime, requestContext);
  163. }
  164. }
  165. else
  166. {
  167. return GetDateDefaultString(&m_ymdLcl, &m_tzd, noDateTime, requestContext);
  168. }
  169. #endif
  170. case DateStringFormat::GMT:
  171. EnsureYmdUtc();
  172. return GetDateGmtString(&m_ymdUtc, requestContext);
  173. }
  174. }
  175. JavascriptString*
  176. DateImplementation::GetISOString(ScriptContext* requestContext)
  177. {
  178. // ES5 15.9.5.43: throw RangeError if time value is not a finite number
  179. if (!Js::NumberUtilities::IsFinite(m_tvUtc))
  180. {
  181. JavascriptError::ThrowRangeError(requestContext, JSERR_NeedNumber);
  182. }
  183. CompoundString *const bs = CompoundString::NewWithCharCapacity(30, requestContext->GetLibrary());
  184. GetDateComponent(bs, DateData::FullYear, 0, requestContext);
  185. bs->AppendChars(_u('-'));
  186. // month
  187. GetDateComponent(bs, DateData::Month, 1/*adjustment*/, requestContext);
  188. bs->AppendChars(_u('-'));
  189. // date
  190. GetDateComponent(bs, DateData::Date, 0, requestContext);
  191. bs->AppendChars(_u('T'));
  192. // hours
  193. GetDateComponent(bs, DateData::Hours, 0, requestContext);
  194. bs->AppendChars(_u(':'));
  195. // minutes
  196. GetDateComponent(bs, DateData::Minutes, 0, requestContext);
  197. bs->AppendChars(_u(':'));
  198. // seconds
  199. GetDateComponent(bs, DateData::Seconds, 0, requestContext);
  200. // ES5 fill in milliseconds but v5.8 does not
  201. bs->AppendChars(_u('.'));
  202. // milliseconds
  203. GetDateComponent(bs, DateData::Milliseconds, 0, requestContext);
  204. bs->AppendChars(_u('Z'));
  205. return bs;
  206. }
  207. void
  208. DateImplementation::GetDateComponent(CompoundString *bs, DateData componentType, int adjust,
  209. ScriptContext* requestContext)
  210. {
  211. double value = this->GetDateData(componentType, true /* fUTC */, requestContext);
  212. if(Js::NumberUtilities::IsFinite(value))
  213. {
  214. const int ival = (int)value + adjust;
  215. const int ivalAbs = ival < 0 ? -ival : ival;
  216. switch(componentType)
  217. {
  218. case DateData::FullYear:
  219. if(ival < 0 || ival > 9999)
  220. {
  221. // ES5 spec section 15.9.1.15.1 states that for years outside the range 0-9999, the expanded year
  222. // representation should:
  223. // - always include the sign
  224. // - have 2 extra digits (6 digits total)
  225. bs->AppendChars(ival < 0 ? _u('-') : _u('+'));
  226. if(ivalAbs < 100000)
  227. {
  228. bs->AppendChars(_u('0'));
  229. if(ivalAbs < 10000)
  230. {
  231. bs->AppendChars(_u('0'));
  232. }
  233. }
  234. }
  235. // Years are zero-padded to at least 4 digits in ES5
  236. if(ivalAbs < 1000)
  237. {
  238. bs->AppendChars(_u('0'));
  239. // will fall through to next case for additional padding
  240. }
  241. else
  242. {
  243. break;
  244. }
  245. // fall through
  246. case DateData::Milliseconds:
  247. if (ivalAbs < 100)
  248. {
  249. bs->AppendChars(_u('0'));
  250. // will fall through to next case for additional padding
  251. }
  252. else
  253. {
  254. break;
  255. }
  256. // fall through
  257. default:
  258. if (ivalAbs < 10)
  259. {
  260. bs->AppendChars(_u('0'));
  261. }
  262. }
  263. // _itow_s makes use of max 12 bytes for a base-10 32-bit int (_u("-2147483648\0")), although we don't need the sign
  264. // and our numbers shouldn't be that big anyway
  265. bs->AppendChars(
  266. ivalAbs,
  267. 10,
  268. [](const int value, char16 *const buffer, const CharCount charCapacity)
  269. {
  270. errno_t err = _itow_s(value, buffer, charCapacity, 10);
  271. Assert(err == 0);
  272. });
  273. }
  274. }
  275. ///----------------------------------------------------------------------------
  276. ///
  277. /// Use tv as the UTC time.
  278. ///
  279. ///----------------------------------------------------------------------------
  280. void
  281. DateImplementation::SetTvUtc(double tv)
  282. {
  283. m_grfval = 0;
  284. m_tvUtc = DoubleToTvUtc(tv);
  285. }
  286. double
  287. DateImplementation::DoubleToTvUtc(double tv)
  288. {
  289. if (JavascriptNumber::IsNan(tv) || tv < ktvMin || tv > ktvMax)
  290. {
  291. return JavascriptNumber::NaN;
  292. }
  293. return CONFIG_FLAG(HighPrecisionDate)? tv : ConvertToInteger(tv);
  294. }
  295. ///----------------------------------------------------------------------------
  296. ///
  297. /// Use tv as the local time and set m_tvUtc appropriately.
  298. ///
  299. ///----------------------------------------------------------------------------
  300. void
  301. DateImplementation::SetTvLcl(double tv, ScriptContext* requestContext)
  302. {
  303. m_grfval = 0;
  304. m_tvUtc = GetTvUtc(tv, requestContext);
  305. }
  306. JavascriptString*
  307. DateImplementation::ConvertVariantDateToString(double dbl, ScriptContext* scriptContext)
  308. {
  309. Js::DateImplementation::TZD tzd;
  310. DateTime::YMD ymd;
  311. double tv = Js::DateImplementation::GetTvUtc(Js::DateImplementation::JsLocalTimeFromVarDate(dbl), scriptContext);
  312. tv = Js::DateImplementation::GetTvLcl(tv, scriptContext, &tzd);
  313. if (Js::JavascriptNumber::IsNan(tv))
  314. {
  315. return JavascriptNumber::ToStringNan(scriptContext);
  316. }
  317. Js::DateImplementation::GetYmdFromTv(tv, &ymd);
  318. return DateImplementation::GetDateDefaultString(&ymd, &tzd, 0, scriptContext);
  319. }
  320. JavascriptString*
  321. DateImplementation::GetDateDefaultString(DateTime::YMD *pymd, TZD *ptzd,DateTimeFlag noDateTime,ScriptContext* scriptContext)
  322. {
  323. return GetDateDefaultString<CompoundString>(pymd, ptzd, noDateTime, scriptContext,
  324. [=](CharCount capacity) -> CompoundString*
  325. {
  326. return CompoundString::NewWithCharCapacity(capacity, scriptContext->GetLibrary());
  327. });
  328. }
  329. JavascriptString*
  330. DateImplementation::GetDateGmtString(DateTime::YMD *pymd,ScriptContext* scriptContext)
  331. {
  332. // toUTCString() or toGMTString() will return for example:
  333. // "Thu, 02 Feb 2012 09:02:03 GMT" for versions IE11 or above
  334. CompoundString *const bs = CompoundString::NewWithCharCapacity(30, scriptContext->GetLibrary());
  335. const auto ConvertUInt16ToString_ZeroPad_2 = [](const uint16 value, char16 *const buffer, const CharCount charCapacity)
  336. {
  337. const charcount_t cchWritten = NumberUtilities::UInt16ToString(value, buffer, charCapacity, 2);
  338. Assert(cchWritten != 0);
  339. };
  340. const auto ConvertLongToString = [](const int32 value, char16 *const buffer, const CharCount charCapacity)
  341. {
  342. const errno_t err = _ltow_s(value, buffer, charCapacity, 10);
  343. Assert(err == 0);
  344. };
  345. bs->AppendChars(g_rgpszDay[pymd->wday]);
  346. bs->AppendChars(_u(", "));
  347. // sz - as %02d - output is "01" to "31"
  348. bs->AppendChars(static_cast<WORD>(pymd->mday + 1), 2, ConvertUInt16ToString_ZeroPad_2);
  349. bs->AppendChars(_u(' '));
  350. bs->AppendChars(g_rgpszMonth[pymd->mon]);
  351. bs->AppendChars(_u(' '));
  352. // Add the year.
  353. if ((pymd->year) > 0)
  354. {
  355. bs->AppendChars(pymd->year, 10, ConvertUInt32ToString_ZeroPad_4);
  356. }
  357. else
  358. {
  359. int positiveYear = -(pymd->year); // pymd->year is negative
  360. bs->AppendChars(_u('-'));
  361. bs->AppendChars(positiveYear, 10, ConvertUInt32ToString_ZeroPad_4);
  362. }
  363. // Add the time.
  364. bs->AppendChars(_u(' '));
  365. // sz - as %02d - HOUR
  366. bs->AppendChars(static_cast<WORD>(pymd->time / 3600000), 2, ConvertUInt16ToString_ZeroPad_2);
  367. bs->AppendChars(_u(':'));
  368. // sz - as %02d - MINUTE
  369. bs->AppendChars(static_cast<WORD>((pymd->time / 60000) % 60), 2, ConvertUInt16ToString_ZeroPad_2);
  370. bs->AppendChars(_u(':'));
  371. // sz - as %02d - SECOND
  372. bs->AppendChars(static_cast<WORD>((pymd->time / 1000) % 60), 2, ConvertUInt16ToString_ZeroPad_2);
  373. bs->AppendChars(_u(' '));
  374. bs->AppendChars(_u("GMT"));
  375. return bs;
  376. }
  377. #ifdef ENABLE_GLOBALIZATION
  378. JavascriptString*
  379. DateImplementation::GetDateLocaleString(DateTime::YMD *pymd, TZD *ptzd, DateTimeFlag noDateTime,ScriptContext* scriptContext)
  380. {
  381. SYSTEMTIME st;
  382. int cch;
  383. int count = 0;
  384. const int kcchMax = 256;
  385. WCHAR wszBuf[kcchMax];
  386. WCHAR *pwszBuf, *pToBeFreed = NULL, *p;
  387. JavascriptString *bs = nullptr;
  388. // the caller of this function should ensure that the range of pymd->year is such that the following conversion works.
  389. st.wYear = (WORD)pymd->year;
  390. st.wMonth = (WORD)pymd->mon + 1;
  391. st.wDayOfWeek = (WORD)pymd->wday;
  392. st.wDay = (WORD)pymd->mday + 1;
  393. st.wHour = (WORD)(pymd->time / 3600000);
  394. st.wMinute = (WORD)((pymd->time / 60000) % 60);
  395. st.wSecond = (WORD)((pymd->time / 1000) % 60);
  396. st.wMilliseconds = (WORD)(pymd->time % 60);
  397. cch = 0;
  398. LCID lcid = GetUserDefaultLCID();
  399. if( !(noDateTime & DateTimeFlag::NoDate))
  400. {
  401. DWORD dwFormat = DATE_LONGDATE;
  402. if ((PRIMARYLANGID(LANGIDFROMLCID(lcid)) == LANG_ARABIC) ||
  403. (PRIMARYLANGID(LANGIDFROMLCID(lcid)) == LANG_HEBREW))
  404. {
  405. dwFormat |= DATE_RTLREADING;
  406. }
  407. int c = GetDateFormatW( lcid, dwFormat, &st, NULL, NULL, 0 );
  408. if( c <= 0 )
  409. {
  410. if (PRIMARYLANGID(LANGIDFROMLCID(lcid)) == LANG_HEBREW)
  411. {
  412. // Can't support some Hebrew dates - current limit is 1 Jan AD 2240
  413. Js::JavascriptError::ThrowRangeError(scriptContext, VBSERR_CantDisplayDate);
  414. }
  415. AssertMsg(false, "GetDateFormat failed");
  416. goto Error;
  417. }
  418. cch += c;
  419. }
  420. if( !(noDateTime & DateTimeFlag::NoTime))
  421. {
  422. int c = GetTimeFormatW( lcid, 0, &st, NULL, NULL, 0 );
  423. if( c <= 0 )
  424. {
  425. AssertMsg(false, "GetTimeFormat failed");
  426. goto Error;
  427. }
  428. cch += c;
  429. }
  430. cch++; // For the space between the date and the time.
  431. if( cch > kcchMax )
  432. {
  433. pwszBuf = pToBeFreed = (WCHAR *)malloc( cch * sizeof(WCHAR) );
  434. if(!pwszBuf)
  435. {
  436. Js::JavascriptError::ThrowOutOfMemoryError(scriptContext);
  437. }
  438. }
  439. else
  440. {
  441. wszBuf[0] = '\0';
  442. pwszBuf = wszBuf;
  443. }
  444. count = cch;
  445. p = pwszBuf;
  446. if( !(noDateTime & DateTimeFlag::NoDate))
  447. {
  448. DWORD dwFormat = DATE_LONGDATE;
  449. if ((PRIMARYLANGID(LANGIDFROMLCID(lcid)) == LANG_ARABIC) ||
  450. (PRIMARYLANGID(LANGIDFROMLCID(lcid)) == LANG_HEBREW))
  451. {
  452. dwFormat |= DATE_RTLREADING;
  453. }
  454. int c = GetDateFormatW( lcid, dwFormat, &st, NULL, p, cch );
  455. if( c <= 0 || c > cch)
  456. {
  457. if (PRIMARYLANGID(LANGIDFROMLCID(lcid)) == LANG_HEBREW)
  458. {
  459. // Can't support some Hebrew dates - current limit is 1 Jan AD 2240
  460. Js::JavascriptError::ThrowRangeError(scriptContext, VBSERR_CantDisplayDate);
  461. }
  462. AssertMsg(false, "GetDateFormat failed");
  463. goto Error;
  464. }
  465. p += (c-1);
  466. cch -= (c-1);
  467. if( !(noDateTime & DateTimeFlag::NoTime))
  468. {
  469. *p++ = _u(' ');
  470. cch--;
  471. }
  472. }
  473. if( !(noDateTime & DateTimeFlag::NoTime))
  474. {
  475. int c = GetTimeFormatW( lcid, 0, &st, NULL, p, cch );
  476. Assert( c > 0 );
  477. if( c <= 0 )
  478. {
  479. AssertMsg(false, "GetTimeFormat failed");
  480. goto Error;
  481. }
  482. cch -= (c-1);
  483. }
  484. bs = JavascriptString::NewCopyBuffer(pwszBuf, count-cch, scriptContext);
  485. Error:
  486. if( pToBeFreed )
  487. free(pToBeFreed);
  488. return bs;
  489. }
  490. #endif // ENABLE_GLOBALIZATION
  491. double
  492. DateImplementation::GetDateData(DateData dd, bool fUtc, ScriptContext* scriptContext)
  493. {
  494. DateTime::YMD *pymd;
  495. double value = 0;
  496. if (JavascriptNumber::IsNan(m_tvUtc))
  497. {
  498. return m_tvUtc;
  499. }
  500. if (fUtc)
  501. {
  502. EnsureYmdUtc();
  503. pymd = &m_ymdUtc;
  504. }
  505. else
  506. {
  507. EnsureYmdLcl(scriptContext);
  508. pymd = &m_ymdLcl;
  509. }
  510. switch (dd)
  511. {
  512. case DateData::Year:
  513. Assert(scriptContext);
  514. // WOOB bug 1099381: ES5 spec B.2.4: getYear() must return YearFromTime() - 1900.
  515. // Note that negative value is OK for the spec.
  516. value = pymd->year - 1900;
  517. break;
  518. case DateData::FullYear:
  519. value = pymd->year;
  520. break;
  521. case DateData::Month:
  522. value = pymd->mon;
  523. break;
  524. case DateData::Date:
  525. value = pymd->mday + 1;
  526. break;
  527. case DateData::Day:
  528. value = pymd->wday;
  529. break;
  530. case DateData::Hours:
  531. value = (pymd->time / 3600000);
  532. break;
  533. case DateData::Minutes:
  534. value = (pymd->time / 60000) % 60;
  535. break;
  536. case DateData::Seconds:
  537. value = (pymd->time / 1000) % 60;
  538. break;
  539. case DateData::Milliseconds:
  540. value = pymd->time % 1000;
  541. break;
  542. case DateData::TimezoneOffset:
  543. //Assert(!fUtc);
  544. value = (m_tvUtc - m_tvLcl) / 60000;
  545. break;
  546. default:
  547. // Shouldn't come here
  548. AssertMsg(false, "DateData type invalid");
  549. }
  550. return value;
  551. }
  552. inline bool
  553. DateImplementation::FBig(char16 ch)
  554. {
  555. return (unsigned int)ch >= 128;
  556. }
  557. inline bool
  558. DateImplementation::FDateDelimiter(char16 ch)
  559. {
  560. return (ch == '/' || ch == '-');
  561. }
  562. ///------------------------------------------------------------------------------
  563. // Parse a string as a date and return the number of milliseconds since
  564. // January 1, 1970 GMT.
  565. //
  566. // TODO: This function is ported from IE8 jscript engine. This lengthy
  567. // and needs a cleanup - break into smaller functions.
  568. ///------------------------------------------------------------------------------
  569. double DateImplementation::UtcTimeFromStr(ScriptContext *scriptContext, JavascriptString *pParseString)
  570. {
  571. Assert(pParseString != nullptr);
  572. double dbl;
  573. if (scriptContext->GetLastUtcTimeFromStr(pParseString, dbl))
  574. {
  575. return dbl;
  576. }
  577. unsigned int ulength = pParseString->GetLength();
  578. const char16 *psz = pParseString->GetSz();
  579. if(UtcTimeFromStrCore(psz, ulength, dbl, scriptContext))
  580. {
  581. scriptContext->SetLastUtcTimeFromStr(pParseString, dbl);
  582. return dbl;
  583. }
  584. Js::JavascriptError::ThrowOutOfMemoryError(scriptContext);
  585. }
  586. bool DateImplementation::TryParseDecimalDigits(
  587. const char16 *const str,
  588. const size_t length,
  589. const size_t startIndex,
  590. const size_t numDigits,
  591. int &value)
  592. {
  593. Assert(str);
  594. Assert(length);
  595. Assert(startIndex <= length);
  596. Assert(numDigits != 0 && numDigits <= 9); // will fit in an 'int'
  597. if(numDigits > length - startIndex)
  598. return false;
  599. size_t i = 0;
  600. // Skip leading zeroes
  601. while(str[startIndex + i] == _u('0') && ++i < numDigits);
  602. // Parse remaining digits
  603. int v = 0;
  604. for(; i < numDigits; ++i)
  605. {
  606. const unsigned short d = str[startIndex + i] - _u('0');
  607. if(d > 9)
  608. break;
  609. v = v * 10 + d;
  610. }
  611. if(i < numDigits)
  612. return false;
  613. Assert(i == numDigits);
  614. value = v;
  615. // The next character must not be a digit
  616. return !(i < length - startIndex && static_cast<unsigned short>(str[startIndex + i] - _u('0')) <= 9);
  617. }
  618. // Either 1 digit or 2 digits or 3 digits
  619. // Ignore any digits after the third
  620. bool DateImplementation::TryParseMilliseconds(
  621. const char16 *const str,
  622. const size_t length,
  623. const size_t startIndex,
  624. int &value,
  625. size_t &foundDigits)
  626. {
  627. const size_t minNumDigits = 1;
  628. Assert(str);
  629. Assert(length);
  630. Assert(startIndex <= length);
  631. size_t allDigits = length - startIndex;
  632. if(allDigits < minNumDigits)
  633. return false;
  634. size_t i = 0;
  635. // Skip leading zeroes
  636. while(str[startIndex + i] == _u('0') && ++i < allDigits);
  637. // Parse remaining digits
  638. int v = 0;
  639. for(; i < allDigits ; ++i)
  640. {
  641. const unsigned short d = str[startIndex + i] - _u('0');
  642. if(d > 9)
  643. break;
  644. if (i < 3) // not past the 3rd digit in the milliseconds, don't ignore
  645. v = v * 10 + d;
  646. }
  647. if(i < minNumDigits)
  648. return false;
  649. foundDigits = i;
  650. if (foundDigits == 1)
  651. v = v * 100;
  652. else if (foundDigits == 2)
  653. v = v * 10;
  654. value = v;
  655. // The next character must not be a digit
  656. return !(i < length - startIndex && static_cast<unsigned short>(str[startIndex + i] - _u('0')) <= 9);
  657. }
  658. bool DateImplementation::TryParseTwoDecimalDigits(
  659. const char16 *const str,
  660. const size_t length,
  661. const size_t startIndex,
  662. int &value,
  663. bool canHaveTrailingDigit /* = false */)
  664. {
  665. Assert(str);
  666. Assert(length);
  667. Assert(startIndex <= length);
  668. if(length - startIndex < 2)
  669. return false;
  670. unsigned short d = str[startIndex] - _u('0');
  671. if(d > 9)
  672. return false;
  673. short v = d * 10;
  674. d = str[startIndex + 1] - _u('0');
  675. if(d > 9)
  676. return false;
  677. value = v + d;
  678. // The next character must not be a digit if canHaveTrailingDigit is false
  679. bool hasNoTrailingDigit = !(length - startIndex > 2 && static_cast<unsigned short>(str[startIndex + 2] - _u('0')) <= 9);
  680. return canHaveTrailingDigit || hasNoTrailingDigit;
  681. }
  682. bool DateImplementation::TryParseIsoString(const char16 *const str, const size_t length, double &timeValue, ScriptContext *scriptContext)
  683. {
  684. Assert(str);
  685. size_t i = 0;
  686. const Js::CharClassifier *classifier = scriptContext->GetCharClassifier();
  687. // Skip leading whitespace (for cross-browser compatibility)
  688. // Also skip bidirectional characters, for Round tripping locale formatted date
  689. while ((classifier->IsWhiteSpace(str[i]) || classifier->IsBiDirectionalChar(str[i])) && ++i < length);
  690. // Minimum length must be 4 (YYYY)
  691. if(length - i < 4)
  692. return false;
  693. // YYYY|(+|-)YYYYYY
  694. int year;
  695. switch(str[i])
  696. {
  697. case _u('+'):
  698. ++i;
  699. if(!TryParseDecimalDigits(str, length, i, 6, year))
  700. return false;
  701. i += 6;
  702. break;
  703. case _u('-'):
  704. ++i;
  705. if(!TryParseDecimalDigits(str, length, i, 6, year) || year == 0)
  706. return false;
  707. year = -year;
  708. i += 6;
  709. break;
  710. case _u('0'):
  711. case _u('1'):
  712. case _u('2'):
  713. case _u('3'):
  714. case _u('4'):
  715. case _u('5'):
  716. case _u('6'):
  717. case _u('7'):
  718. case _u('8'):
  719. case _u('9'):
  720. if(!TryParseDecimalDigits(str, length, i, 4, year))
  721. return false;
  722. i += 4;
  723. break;
  724. default:
  725. return false;
  726. }
  727. // Skip bidirectional characters, for Round tripping locale formatted date
  728. i += classifier->SkipBiDirectionalChars(str, i, length);
  729. int month = 0,
  730. day = 0,
  731. timePortionMilliseconds = 0,
  732. utcOffsetMilliseconds = 0;
  733. bool isLocalTime = false;
  734. do // while(false);
  735. {
  736. do // while(false);
  737. {
  738. // -MM
  739. if(i >= length || str[i] != _u('-'))
  740. break;
  741. ++i;
  742. if(!TryParseTwoDecimalDigits(str, length, i, month))
  743. return false;
  744. --month;
  745. if(month < 0 || month > 11)
  746. return false;
  747. i += 2;
  748. // Skip bidirectional characters, for Round tripping locale formatted date
  749. i += classifier->SkipBiDirectionalChars(str, i, length);
  750. // -DD
  751. if(i >= length || str[i] != _u('-'))
  752. break;
  753. ++i;
  754. if(!TryParseTwoDecimalDigits(str, length, i, day))
  755. return false;
  756. --day;
  757. if(day < 0 || day > 30)
  758. return false;
  759. i += 2;
  760. // Skip bidirectional characters, for Round tripping locale formatted date
  761. i += classifier->SkipBiDirectionalChars(str, i, length);
  762. } while(false);
  763. // THH:mm
  764. if(i >= length || str[i] != _u('T'))
  765. break;
  766. ++i;
  767. int t;
  768. if(!TryParseTwoDecimalDigits(str, length, i, t) || t > 24)
  769. return false;
  770. timePortionMilliseconds += t * (60 * 60 * 1000);
  771. i += 2;
  772. // Skip bidirectional characters, for Round tripping locale formatted date
  773. i += classifier->SkipBiDirectionalChars(str, i, length);
  774. if(i >= length || str[i] != _u(':'))
  775. return false;
  776. ++i;
  777. if(!TryParseTwoDecimalDigits(str, length, i, t) || t > 59)
  778. return false;
  779. timePortionMilliseconds += t * (60 * 1000);
  780. i += 2;
  781. // Skip bidirectional characters, for Round tripping locale formatted date
  782. i += classifier->SkipBiDirectionalChars(str, i, length);
  783. do // while(false);
  784. {
  785. // :ss
  786. if(i >= length || str[i] != _u(':'))
  787. break;
  788. ++i;
  789. if(!TryParseTwoDecimalDigits(str, length, i, t) || t > 59)
  790. return false;
  791. timePortionMilliseconds += t * 1000;
  792. i += 2;
  793. // Skip bidirectional characters, for Round tripping locale formatted date
  794. i += classifier->SkipBiDirectionalChars(str, i, length);
  795. // .sss
  796. if(i >= length || str[i] != _u('.'))
  797. break;
  798. ++i;
  799. // Require one or more decimal digits. Ignore digits beyond the third
  800. size_t foundDigits = 0;
  801. if(!TryParseMilliseconds(str, length, i, t, foundDigits))
  802. return false;
  803. timePortionMilliseconds += t;
  804. i += foundDigits;
  805. // Skip bidirectional characters, for Round tripping locale formatted date
  806. i += classifier->SkipBiDirectionalChars(str, i, length);
  807. } while(false);
  808. // Z|(+|-)HH:mm
  809. if(i >= length)
  810. {
  811. isLocalTime = true;
  812. break;
  813. }
  814. const char16 utcOffsetSign = str[i];
  815. if(utcOffsetSign == _u('Z'))
  816. {
  817. ++i;
  818. break;
  819. }
  820. if(utcOffsetSign != _u('+') && utcOffsetSign != _u('-'))
  821. {
  822. isLocalTime = true;
  823. break;
  824. }
  825. ++i;
  826. // In -version:6 we allow optional colons in the timezone offset
  827. if (!TryParseTwoDecimalDigits(str, length, i, t, scriptContext->GetConfig()->IsES6DateParseFixEnabled() /* Timezone may be 4 sequential digits */) || t > 24)
  828. return false;
  829. utcOffsetMilliseconds += t * (60 * 60 * 1000);
  830. i += 2;
  831. // Skip bidirectional characters, for Round tripping locale formatted date
  832. i += classifier->SkipBiDirectionalChars(str, i, length);
  833. if(i >= length)
  834. return false;
  835. // The ':' is optional in ISO 8601
  836. if (str[i] == _u(':'))
  837. {
  838. ++i;
  839. }
  840. if(!TryParseTwoDecimalDigits(str, length, i, t) || t > 59)
  841. return false;
  842. utcOffsetMilliseconds += t * (60 * 1000);
  843. i += 2;
  844. // Skip bidirectional characters, for Round tripping locale formatted date
  845. i += classifier->SkipBiDirectionalChars(str, i, length);
  846. if(utcOffsetSign == _u('-'))
  847. utcOffsetMilliseconds = -utcOffsetMilliseconds;
  848. } while(false);
  849. // Skip trailing whitespace (for cross-browser compatibility)
  850. // Skip bidirectional characters, for Round tripping locale formatted date
  851. while (i < length && (classifier->IsWhiteSpace(str[i]) || classifier->IsBiDirectionalChar(str[i])))
  852. ++i;
  853. // There should only have been whitespace remaining, if any
  854. if(i < length)
  855. return false;
  856. Assert(i == length);
  857. // Compute the time value
  858. timeValue = TvFromDate(year, month, day, timePortionMilliseconds - utcOffsetMilliseconds);
  859. if (isLocalTime)
  860. {
  861. // Compatibility note:
  862. // In ES5, it was unspecified how to handle date strings without the trailing time zone offset "Z|(+|-)HH:mm".
  863. // In ES5.1, an absent time zone offset defaulted to "Z", which contradicted ISO8601:2004(E).
  864. // This was corrected in an ES5.1 errata note. Moreover, the ES6 draft now follows ISO8601.
  865. timeValue = GetTvUtc(timeValue, scriptContext);
  866. }
  867. return true;
  868. }
  869. bool DateImplementation::UtcTimeFromStrCore(
  870. __in_ecount_z(ulength) const char16 *psz,
  871. unsigned int ulength,
  872. double &retVal,
  873. ScriptContext *const scriptContext)
  874. {
  875. Assert(scriptContext);
  876. if (ulength >= 0x7fffffff)
  877. {
  878. //Prevent unreasonable requests from causing overflows.
  879. return false;
  880. }
  881. if (nullptr == psz)
  882. {
  883. retVal = JavascriptNumber::NaN;
  884. return true;
  885. }
  886. // Try to parse the string as the ISO format first
  887. if(TryParseIsoString(psz, ulength, retVal, scriptContext))
  888. {
  889. return true;
  890. }
  891. enum
  892. {
  893. ssNil,
  894. ssMinutes,
  895. ssSeconds,
  896. ssMillisecond,
  897. ssAddOffset,
  898. ssSubOffset,
  899. ssDate,
  900. ssMonth,
  901. ssYear
  902. };
  903. char16 *pchBase;
  904. char16 *pch;
  905. char16 ch;
  906. char16 *pszSrc = nullptr;
  907. const int32 lwNil = 0x80000000;
  908. int32 cch;
  909. int32 depth;
  910. int32 lwT;
  911. int32 lwYear = lwNil;
  912. int32 lwMonth = lwNil;
  913. int32 lwDate = lwNil;
  914. int32 lwTime = lwNil;
  915. int32 lwMillisecond = lwNil;
  916. int32 lwZone = lwNil;
  917. int32 lwOffset = lwNil;
  918. int32 ss = ssNil;
  919. const SZS *pszs;
  920. bool fUtc;
  921. int tAmPm = 0;
  922. int tBcAd = 0;
  923. size_t numOfDigits = 0;
  924. double tv = JavascriptNumber::NaN; // Initialized for error handling.
  925. //Create a copy to analyze
  926. BEGIN_TEMP_ALLOCATOR(tempAllocator, scriptContext, _u("UtcTimeFromStr"));
  927. pszSrc = AnewArray(tempAllocator, char16, ulength + 1);
  928. size_t size = sizeof(char16) * (ulength + 1);
  929. js_memcpy_s(pszSrc, size, psz, size);
  930. _wcslwr_s(pszSrc,ulength+1);
  931. bool isDateNegativeVersion5 = false;
  932. bool isNextFieldDateNegativeVersion5 = false;
  933. bool isZeroPaddedYear = false;
  934. const Js::CharClassifier *classifier = scriptContext->GetCharClassifier();
  935. #pragma prefast(suppress: __WARNING_INCORRECT_VALIDATION, "pch is guaranteed to be null terminated by __in_z on psz and js_memcpy_s copying the null byte")
  936. for (pch = pszSrc; 0 != (ch = classifier->SkipBiDirectionalChars(pch));)
  937. {
  938. pch++;
  939. if (ch <= ' ')
  940. {
  941. continue;
  942. }
  943. switch (ch)
  944. {
  945. case '(':
  946. {
  947. // skip stuff in parens
  948. for (depth = 1; 0 != (ch = *pch); )
  949. {
  950. pch++;
  951. if (ch == '(')
  952. {
  953. depth++;
  954. }
  955. else if (ch == ')' && --depth <= 0)
  956. {
  957. break;
  958. }
  959. }
  960. continue;
  961. }
  962. case ',':
  963. case ':':
  964. case '/':
  965. {
  966. // ignore these
  967. continue;
  968. }
  969. case '+':
  970. {
  971. if (lwNil != lwTime)
  972. {
  973. ss = ssAddOffset;
  974. }
  975. continue;
  976. }
  977. case '-':
  978. {
  979. if (lwNil != lwTime)
  980. {
  981. ss = ssSubOffset;
  982. }
  983. continue;
  984. }
  985. }
  986. pchBase = pch - 1;
  987. if (!FBig(ch) && isalpha(ch))
  988. {
  989. for ( ; !FBig(*pch) && (isalpha(*pch) || '.' == *pch); pch++)
  990. ;
  991. cch = (int32)(pch - pchBase);
  992. if ('.' == pchBase[cch - 1])
  993. {
  994. cch--;
  995. }
  996. //Assert(cch > 0);
  997. // skip to the next real character
  998. while (0 != (*pch) && (*pch <= ' ' || classifier->IsBiDirectionalChar(*pch)))
  999. {
  1000. pch++;
  1001. }
  1002. // have an alphabetic token - look it up
  1003. if (cch == 1)
  1004. {
  1005. AssertMsg(isNextFieldDateNegativeVersion5 == false, "isNextFieldDateNegativeVersion5 == false");
  1006. // military version of time zone
  1007. // z = GMT
  1008. // j isn't used
  1009. // a to m are 1 to 12
  1010. // n to y are -1 to -12
  1011. if (lwNil != lwZone)
  1012. {
  1013. goto LError;
  1014. }
  1015. if (ch <= 'm')
  1016. {
  1017. if (ch == 'j' || ch < 'a')
  1018. {
  1019. goto LError;
  1020. }
  1021. lwZone = (int32)(ch - 'a' + (ch < 'j')) * 60;
  1022. }
  1023. else if (ch <= 'y')
  1024. {
  1025. lwZone = -(int32)(ch - 'm') * 60;
  1026. }
  1027. else if (ch == 'z')
  1028. {
  1029. lwZone = 0;
  1030. }
  1031. else
  1032. {
  1033. goto LError;
  1034. }
  1035. // look for a time zone offset
  1036. ss = ('+' == *pch) ? (pch++, ssAddOffset) :
  1037. ('-' == *pch) ? (pch++, ssSubOffset) : ssNil;
  1038. continue;
  1039. }
  1040. // look for a token
  1041. for (pszs = g_rgszs + kcszs; ; )
  1042. {
  1043. if (pszs-- <= g_rgszs)
  1044. goto LError;
  1045. if (cch <= pszs->cch &&
  1046. 0 == memcmp(pchBase, pszs->psz, cch * sizeof(char16)))
  1047. {
  1048. break;
  1049. }
  1050. }
  1051. switch (pszs->szst)
  1052. {
  1053. case ParseStringTokenType::BcAd:
  1054. {
  1055. if (tBcAd != 0)
  1056. {
  1057. goto LError;
  1058. }
  1059. tBcAd = (int)pszs->lwVal;
  1060. break;
  1061. }
  1062. case ParseStringTokenType::AmPm:
  1063. {
  1064. if (tAmPm != 0)
  1065. {
  1066. goto LError;
  1067. }
  1068. tAmPm = (int)pszs->lwVal;
  1069. break;
  1070. }
  1071. case ParseStringTokenType::Month:
  1072. {
  1073. if (lwNil != lwMonth)
  1074. {
  1075. goto LError;
  1076. }
  1077. lwMonth = pszs->lwVal;
  1078. if ('-' == *pch)
  1079. {
  1080. // handle the case date is negative for "Thu, 23 Sep -0007 00:00:00 GMT"
  1081. isDateNegativeVersion5 = true;
  1082. pch++;
  1083. }
  1084. break;
  1085. }
  1086. case ParseStringTokenType::Zone:
  1087. {
  1088. if (lwNil != lwZone)
  1089. {
  1090. goto LError;
  1091. }
  1092. lwZone = pszs->lwVal;
  1093. // look for a time zone offset
  1094. ss = ('+' == *pch) ? (pch++, ssAddOffset) :
  1095. ('-' == *pch) ? (pch++, ssSubOffset) : ssNil;
  1096. break;
  1097. }
  1098. }
  1099. continue;
  1100. }
  1101. if (FBig(ch) || !isdigit(ch))
  1102. {
  1103. goto LError;
  1104. }
  1105. for (lwT = ch - '0'; ; pch++)
  1106. {
  1107. // for time zone offset HH:mm, we already got HH so skip ':' and grab mm
  1108. if (((ss == ssAddOffset) || (ss == ssSubOffset)) && (*pch == ':'))
  1109. {
  1110. continue;
  1111. }
  1112. if (FBig(*pch) || !isdigit(*pch))
  1113. {
  1114. break;
  1115. }
  1116. // to avoid overflow
  1117. if (pch - pchBase > 6)
  1118. {
  1119. goto LError;
  1120. }
  1121. // convert string to number, e.g. 07:30 -> 730
  1122. lwT = lwT * 10 + *pch - '0';
  1123. }
  1124. numOfDigits = pch - pchBase;
  1125. // skip to the next real character
  1126. while (0 != (ch = *pch) && (ch <= ' ' || classifier->IsBiDirectionalChar(ch)))
  1127. {
  1128. pch++;
  1129. }
  1130. switch (ss)
  1131. {
  1132. case ssAddOffset:
  1133. case ssSubOffset:
  1134. {
  1135. AssertMsg(isNextFieldDateNegativeVersion5 == false, "isNextFieldDateNegativeVersion5 == false");
  1136. if (lwNil != lwOffset)
  1137. goto LError;
  1138. // convert into minutes, e.g. 730 -> 7*60+30
  1139. lwOffset = lwT < 24 ? lwT * 60 :
  1140. (lwT % 100) + (lwT / 100) * 60;
  1141. if (ssSubOffset == ss)
  1142. lwOffset = -lwOffset;
  1143. lwZone = 0; // An offset is always with respect to UTC
  1144. ss = ssNil;
  1145. break;
  1146. }
  1147. case ssMinutes:
  1148. {
  1149. AssertMsg(isNextFieldDateNegativeVersion5 == false, "isNextFieldDateNegativeVersion5 == false");
  1150. if (lwT >= 60)
  1151. goto LError;
  1152. lwTime += lwT * 60;
  1153. ss = (ch == ':') ? (pch++, ssSeconds) : ssNil;
  1154. break;
  1155. }
  1156. case ssSeconds:
  1157. {
  1158. AssertMsg(isNextFieldDateNegativeVersion5 == false, "isNextFieldDateNegativeVersion5 == false");
  1159. if (lwT >= 60)
  1160. goto LError;
  1161. lwTime += lwT;
  1162. ss = (ch == '.') ? (pch++, ssMillisecond) : ssNil;
  1163. break;
  1164. }
  1165. case ssMillisecond:
  1166. {
  1167. AssertMsg(isNextFieldDateNegativeVersion5 == false, "isNextFieldDateNegativeVersion5 == false");
  1168. if (numOfDigits <= 1)
  1169. {
  1170. // 1 digit only, treat it as hundreds
  1171. lwMillisecond = lwT * 100;
  1172. }
  1173. else if (numOfDigits <= 2)
  1174. {
  1175. // 2 digit only, treat it as tens
  1176. lwMillisecond = lwT * 10;
  1177. }
  1178. else if (numOfDigits <= 3)
  1179. {
  1180. // canonical 3 digit, per EcmaScript spec
  1181. lwMillisecond = lwT;
  1182. }
  1183. else
  1184. {
  1185. // ignore any digits beyond the third
  1186. lwMillisecond = (pchBase[0] - '0') * 100 + (pchBase[1] - '0') * 10 + (pchBase[2] - '0');
  1187. }
  1188. ss = ssNil;
  1189. break;
  1190. }
  1191. case ssDate:
  1192. {
  1193. AssertMsg(isNextFieldDateNegativeVersion5 == false, "isNextFieldDateNegativeVersion5 == false");
  1194. if (lwNil != lwDate)
  1195. goto LError;
  1196. lwDate = lwT;
  1197. if ((lwNil == lwYear) && FDateDelimiter(ch))
  1198. {
  1199. // We have already parsed the year if the date is specified as YYYY/MM/DD,
  1200. // but not when it is specified as MM/DD/YYYY.
  1201. ss = ssYear;
  1202. pch++;
  1203. }
  1204. else
  1205. {
  1206. ss = ssNil;
  1207. }
  1208. break;
  1209. }
  1210. case ssMonth:
  1211. {
  1212. AssertMsg(isNextFieldDateNegativeVersion5 == false, "isNextFieldDateNegativeVersion5 == false");
  1213. if (lwNil != lwMonth)
  1214. {
  1215. goto LError;
  1216. }
  1217. lwMonth = lwT - 1;
  1218. if (FDateDelimiter(ch))
  1219. {
  1220. // Mark the next token as the date so that it won't be confused with another token.
  1221. // For example, if the next character is '-' as in "2015-1-1", then it'd be used as
  1222. // the time offset without this.
  1223. ss = ssDate;
  1224. pch++;
  1225. }
  1226. break;
  1227. }
  1228. case ssYear:
  1229. {
  1230. AssertMsg(isNextFieldDateNegativeVersion5 == false, "isNextFieldDateNegativeVersion5 == false");
  1231. if (lwNil != lwYear)
  1232. goto LError;
  1233. AssertMsg(isDateNegativeVersion5 == false, "lwYear should be positive as pre-version:5 parsing");
  1234. lwYear = lwT;
  1235. ss = ssNil;
  1236. if (lwT < 1000 && numOfDigits >= 4)
  1237. {
  1238. isZeroPaddedYear = true;
  1239. }
  1240. break;
  1241. }
  1242. default:
  1243. {
  1244. // assumptions for getting a YEAR:
  1245. // - an absolute value greater or equal than 70 (thus not hour!)
  1246. // - wasn't preceded by negative sign for -version:5 year format
  1247. // - lwT has at least 4 digits (e.g. 0017 is year 17 AD)
  1248. if (lwT >= 70 || isNextFieldDateNegativeVersion5 || numOfDigits >= 4)
  1249. {
  1250. // assume it's a year - this is used particularly as version:5 year parsing
  1251. if (lwNil != lwYear)
  1252. goto LError;
  1253. // handle the case date is negative for "Tue Feb 02 -2012 01:02:03 GMT-0800"
  1254. lwYear = isDateNegativeVersion5 ? -lwT : lwT;
  1255. isNextFieldDateNegativeVersion5 = false;
  1256. if (lwT < 1000 && numOfDigits >= 4)
  1257. {
  1258. isZeroPaddedYear = true;
  1259. }
  1260. if (FDateDelimiter(ch))
  1261. {
  1262. // Mark the next token as the month so that it won't be confused with another token.
  1263. // For example, if the next character is '-' as in "2015-1-1", then it'd be used as
  1264. // the time offset without this.
  1265. ss = ssMonth;
  1266. pch++;
  1267. }
  1268. break;
  1269. }
  1270. switch (ch)
  1271. {
  1272. case ':':
  1273. {
  1274. // hour
  1275. if (lwNil != lwTime)
  1276. goto LError;
  1277. if (lwT >= 24)
  1278. goto LError;
  1279. lwTime = lwT * 3600;
  1280. ss = ssMinutes;
  1281. pch++;
  1282. break;
  1283. }
  1284. case '/':
  1285. case '-':
  1286. {
  1287. // month
  1288. if (lwNil != lwMonth)
  1289. {
  1290. // can be year
  1291. if (lwNil != lwYear)
  1292. {
  1293. // both were already parsed!
  1294. goto LError;
  1295. }
  1296. else
  1297. {
  1298. // this is a day - with the negative sign for the date (version 5+)
  1299. lwDate = lwT;
  1300. isDateNegativeVersion5 = true;
  1301. // mark the next field to be year (version 5+)
  1302. isNextFieldDateNegativeVersion5 = true;
  1303. }
  1304. }
  1305. else
  1306. {
  1307. // this is a month
  1308. lwMonth = lwT - 1;
  1309. ss = ssDate;
  1310. }
  1311. pch++;
  1312. break;
  1313. }
  1314. default:
  1315. {
  1316. // date
  1317. if (lwNil != lwDate)
  1318. goto LError;
  1319. lwDate = lwT;
  1320. break;
  1321. }
  1322. }
  1323. break;
  1324. }
  1325. }
  1326. continue;
  1327. }
  1328. if (lwNil == lwYear ||
  1329. lwNil == lwMonth || lwMonth > 11 ||
  1330. lwNil == lwDate || lwDate > 31)
  1331. {
  1332. goto LError;
  1333. }
  1334. if (tBcAd != 0)
  1335. {
  1336. if (tBcAd < 0)
  1337. {
  1338. // BC. Note that 1 BC is year 0 and 2 BC is year -1.
  1339. lwYear = -lwYear + 1;
  1340. }
  1341. }
  1342. else if (lwYear < 50 && isDateNegativeVersion5 == false && isZeroPaddedYear == false)
  1343. {
  1344. lwYear += 2000;
  1345. }
  1346. else if (lwYear < 100 && isDateNegativeVersion5 == false && isZeroPaddedYear == false)
  1347. {
  1348. lwYear += 1900;
  1349. }
  1350. if (tAmPm != 0)
  1351. {
  1352. if (lwNil == lwTime)
  1353. {
  1354. goto LError;
  1355. }
  1356. if (lwTime >= 12 * 3600L && lwTime < 13 * 3600L)
  1357. {
  1358. // In the 12:00 hour. AM means subtract 12 hours and PM means
  1359. // do nothing.
  1360. if (tAmPm < 0)
  1361. {
  1362. lwTime -= 12 * 3600L;
  1363. }
  1364. }
  1365. else
  1366. {
  1367. // Not in the 12:00 hour. AM means do nothing and PM means
  1368. // add 12 hours.
  1369. if (tAmPm > 0)
  1370. {
  1371. if (lwTime >= 12 * 3600L)
  1372. {
  1373. goto LError;
  1374. }
  1375. lwTime += 12 * 3600L;
  1376. }
  1377. }
  1378. }
  1379. else if (lwNil == lwTime)
  1380. {
  1381. lwTime = 0;
  1382. }
  1383. if (lwNil == lwMillisecond)
  1384. {
  1385. lwMillisecond = 0;
  1386. }
  1387. if (lwNil != lwZone)
  1388. {
  1389. lwTime -= lwZone * 60;
  1390. fUtc = TRUE;
  1391. }
  1392. else
  1393. {
  1394. fUtc = FALSE;
  1395. }
  1396. if (lwNil != lwOffset)
  1397. {
  1398. lwTime -= lwOffset * 60;
  1399. }
  1400. // Rebuild time.
  1401. tv = TvFromDate(lwYear, lwMonth, lwDate - 1, (double)lwTime * 1000 + lwMillisecond);
  1402. if (!fUtc)
  1403. {
  1404. tv = GetTvUtc(tv, scriptContext);
  1405. }
  1406. LError:
  1407. END_TEMP_ALLOCATOR(tempAllocator, scriptContext);
  1408. retVal = tv;
  1409. return true;
  1410. }
  1411. //------------------------------------
  1412. //Convert a utc time to a variant date.
  1413. //------------------------------------
  1414. double DateImplementation::VarDateFromJsUtcTime(double dbl, ScriptContext * scriptContext)
  1415. {
  1416. Assert(scriptContext);
  1417. // Convert to local time.
  1418. dbl = Js::DateImplementation::GetTvLcl(dbl, scriptContext);
  1419. if (!Js::NumberUtilities::IsFinite(dbl))
  1420. return Js::JavascriptNumber::NaN;
  1421. // Convert to an automation date.
  1422. dbl = dbl / 86400000 + g_kdblJanuary1st1970;
  1423. // dbl is the actual number of days since 0000h 12/30/1899.
  1424. // Convert this to a true Automation-style date.
  1425. if (dbl < 0.0)
  1426. {
  1427. // This works around a bug in OLE Automation.
  1428. // If a date is negative _and_ less than 500
  1429. // milliseconds before midnight then Automation will
  1430. // "round" it to two days earlier. To work around this
  1431. // bug, round dates just before midnight to midnight.
  1432. double dblT;
  1433. dbl = 2.0 * floor(dbl) - dbl;
  1434. dblT = dbl - floor(dbl);
  1435. if (dblT <= kdblHalfSecond && 0.0 < dblT)
  1436. dbl = ceil(dbl) + 1.0;
  1437. }
  1438. return dbl;
  1439. }
  1440. double DateImplementation::JsUtcTimeFromVarDate(double dbl, ScriptContext * scriptContext)
  1441. {
  1442. Assert(scriptContext);
  1443. return GetTvUtc(JsLocalTimeFromVarDate(dbl), scriptContext);
  1444. }
  1445. double DateImplementation::DateFncUTC(ScriptContext* scriptContext, Arguments args)
  1446. {
  1447. const int kcvarMax = 7;
  1448. double rgdbl[kcvarMax];
  1449. double tv;
  1450. double dblT;
  1451. uint ivar;
  1452. // See: https://github.com/Microsoft/ChakraCore/issues/1665
  1453. // Date.UTC should return NaN with 0 arguments.
  1454. // args.Info.Count includes an implicit first parameter, so we check for Count <= 1.
  1455. if (args.Info.Count <= 1)
  1456. {
  1457. return JavascriptNumber::NaN;
  1458. }
  1459. for (ivar = 0; (ivar < (args.Info.Count-1)) && ivar < kcvarMax; ++ivar)
  1460. {
  1461. rgdbl[ivar] = JavascriptConversion::ToNumber(args[ivar+1],scriptContext);
  1462. }
  1463. for (ivar = 0; ivar < kcvarMax; ivar++)
  1464. {
  1465. // Unspecified parameters are treated like zero, except date, which
  1466. // is treated as 1.
  1467. if (ivar >= (args.Info.Count - 1))
  1468. {
  1469. rgdbl[ivar] = (ivar == 2);
  1470. continue;
  1471. }
  1472. #pragma prefast(suppress:6001, "rgdbl index ivar < args.Info.Count - 1 are initialized")
  1473. dblT = rgdbl[ivar];
  1474. if (!Js::NumberUtilities::IsFinite(dblT))
  1475. {
  1476. return JavascriptNumber::NaN;
  1477. }
  1478. rgdbl[ivar] = ConvertToInteger(dblT);
  1479. }
  1480. // adjust the year
  1481. if (rgdbl[0] < 100 && rgdbl[0] >= 0)
  1482. {
  1483. rgdbl[0] += 1900;
  1484. }
  1485. // Get the UTC time value.
  1486. tv = TvFromDate(rgdbl[0], rgdbl[1], rgdbl[2] - 1,
  1487. rgdbl[3] * 3600000 + rgdbl[4] * 60000 + rgdbl[5] * 1000 + rgdbl[6]);
  1488. if (tv < ktvMin || tv > ktvMax)
  1489. {
  1490. return JavascriptNumber::NaN;
  1491. }
  1492. else
  1493. {
  1494. return tv;
  1495. }
  1496. }
  1497. // Maximum number of arguments used by this set operation.
  1498. static const int mpddcvar[] =
  1499. {
  1500. 1, // Year
  1501. 3, // FullYear
  1502. 2, // Month
  1503. 1, // Date
  1504. 4, // Hours
  1505. 3, // Minutes
  1506. 2, // Seconds
  1507. 1, // Milliseconds
  1508. 0, // Day (shouldn't happen)
  1509. 0, // TimezoneOffset (shouldn't happen)
  1510. };
  1511. double DateImplementation::SetDateData(Arguments args, DateData dd, bool fUtc, ScriptContext* scriptContext)
  1512. {
  1513. // This must accommodate the largest cvar in mpddcvar.
  1514. double rgdbl[5];
  1515. double tv = 0;
  1516. DateTime::YMD *pymd = NULL;
  1517. DateTime::YMD emptyYMD = {0};
  1518. uint count = 0;
  1519. uint cvarMax;
  1520. uint ivar;
  1521. // PREFAST: check limits
  1522. if (dd < 0 || dd >= DateData::Lim)
  1523. {
  1524. AssertMsg(false, "DateData type invalid");
  1525. Js::JavascriptError::ThrowError(scriptContext, VBSERR_InternalError);
  1526. }
  1527. // Get the parameters.
  1528. cvarMax = mpddcvar[dd];
  1529. __analysis_assume(cvarMax <= 4);
  1530. //
  1531. // arg[0] would be the date object itself
  1532. //
  1533. for (ivar = 0; (ivar < (args.Info.Count-1)) && ivar < cvarMax; ++ivar)
  1534. {
  1535. rgdbl[ivar] = JavascriptConversion::ToNumber(args[ivar+1],scriptContext);
  1536. if (!Js::NumberUtilities::IsFinite(rgdbl[ivar]))
  1537. goto LSetNan;
  1538. rgdbl[ivar] = ConvertToInteger(rgdbl[ivar]);
  1539. }
  1540. if ((count = ivar) < 1)
  1541. {
  1542. goto LSetNan;
  1543. }
  1544. if (JavascriptNumber::IsNan(m_tvUtc))
  1545. {
  1546. // If the current time is not finite, the only way we can end up
  1547. // with non-NaN is for setFullYear/setYear.
  1548. // See ES5 15.9.5.40, ES5 B.2.5.
  1549. if (!(DateData::FullYear == dd || DateData::Year == dd))
  1550. {
  1551. goto LSetNan;
  1552. }
  1553. pymd = &emptyYMD; // We need mon, mday, time to be 0.
  1554. // Fall through to DateData::Year and DataData::FullYear cases below.
  1555. }
  1556. else
  1557. {
  1558. if (fUtc)
  1559. {
  1560. EnsureYmdUtc();
  1561. pymd = &m_ymdUtc;
  1562. tv = m_tvUtc;
  1563. }
  1564. else
  1565. {
  1566. EnsureYmdLcl(scriptContext);
  1567. pymd = &m_ymdLcl;
  1568. tv = m_tvLcl;
  1569. }
  1570. }
  1571. ivar = 0;
  1572. switch (dd)
  1573. {
  1574. case DateData::Year:
  1575. if (rgdbl[0] < 100 && rgdbl[0] >= 0)
  1576. rgdbl[0] += 1900;
  1577. // fall-through
  1578. case DateData::FullYear:
  1579. LFullYear:
  1580. if (count < 3)
  1581. {
  1582. // Only {year} or {year, month} is specified. Day is not specified.
  1583. rgdbl[2] = pymd->mday + 1;
  1584. if (count < 2)
  1585. {
  1586. // Month is not specified.
  1587. rgdbl[1] = pymd->mon;
  1588. }
  1589. }
  1590. tv = TvFromDate(rgdbl[0], rgdbl[1], rgdbl[2] - 1, pymd->time);
  1591. break;
  1592. case DateData::Month:
  1593. memmove(rgdbl + 1, rgdbl, count * sizeof(double));
  1594. count++;
  1595. rgdbl[0] = pymd->year;
  1596. goto LFullYear;
  1597. case DateData::Date:
  1598. tv += (rgdbl[ivar] - pymd->mday - 1) * 86400000;
  1599. if (++ivar >= count)
  1600. {
  1601. break;
  1602. }
  1603. // fall-through
  1604. case DateData::Hours:
  1605. tv += (rgdbl[ivar] - (pymd->time / 3600000)) * 3600000;
  1606. if (++ivar >= count)
  1607. {
  1608. break;
  1609. }
  1610. // fall-through
  1611. case DateData::Minutes:
  1612. tv += (rgdbl[ivar] - (pymd->time / 60000) % 60) * 60000;
  1613. if (++ivar >= count)
  1614. {
  1615. break;
  1616. }
  1617. // fall-through
  1618. case DateData::Seconds:
  1619. tv += (rgdbl[ivar] - (pymd->time / 1000) % 60) * 1000;
  1620. if (++ivar >= count)
  1621. {
  1622. break;
  1623. }
  1624. // fall-through
  1625. case DateData::Milliseconds:
  1626. tv += rgdbl[ivar] - pymd->time % 1000;
  1627. break;
  1628. default:
  1629. AssertMsg(false, "DataData type invalid");
  1630. }
  1631. if (fUtc)
  1632. {
  1633. SetTvUtc(tv);
  1634. }
  1635. else
  1636. {
  1637. SetTvLcl(tv, scriptContext);
  1638. }
  1639. m_modified = true;
  1640. return m_tvUtc;
  1641. LSetNan:
  1642. m_grfval = 0;
  1643. m_tvUtc = JavascriptNumber::NaN;
  1644. m_modified = true;
  1645. return m_tvUtc;
  1646. }
  1647. } // namespace Js