DateImplementation.cpp 57 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809
  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::GetDateDefaultString(DateTime::YMD *pymd, TZD *ptzd,DateTimeFlag noDateTime,ScriptContext* scriptContext)
  308. {
  309. return GetDateDefaultString<CompoundString>(pymd, ptzd, noDateTime, scriptContext,
  310. [=](CharCount capacity) -> CompoundString*
  311. {
  312. return CompoundString::NewWithCharCapacity(capacity, scriptContext->GetLibrary());
  313. });
  314. }
  315. JavascriptString*
  316. DateImplementation::GetDateGmtString(DateTime::YMD *pymd,ScriptContext* scriptContext)
  317. {
  318. // toUTCString() or toGMTString() will return for example:
  319. // "Thu, 02 Feb 2012 09:02:03 GMT" for versions IE11 or above
  320. CompoundString *const bs = CompoundString::NewWithCharCapacity(30, scriptContext->GetLibrary());
  321. const auto ConvertUInt16ToString_ZeroPad_2 = [](const uint16 value, char16 *const buffer, const CharCount charCapacity)
  322. {
  323. const charcount_t cchWritten = NumberUtilities::UInt16ToString(value, buffer, charCapacity, 2);
  324. Assert(cchWritten != 0);
  325. };
  326. const auto ConvertLongToString = [](const int32 value, char16 *const buffer, const CharCount charCapacity)
  327. {
  328. const errno_t err = _ltow_s(value, buffer, charCapacity, 10);
  329. Assert(err == 0);
  330. };
  331. bs->AppendChars(g_rgpszDay[pymd->wday]);
  332. bs->AppendChars(_u(", "));
  333. // sz - as %02d - output is "01" to "31"
  334. bs->AppendChars(static_cast<WORD>(pymd->mday + 1), 2, ConvertUInt16ToString_ZeroPad_2);
  335. bs->AppendChars(_u(' '));
  336. bs->AppendChars(g_rgpszMonth[pymd->mon]);
  337. bs->AppendChars(_u(' '));
  338. // Add the year.
  339. if ((pymd->year) > 0)
  340. {
  341. bs->AppendChars(pymd->year, 10, ConvertUInt32ToString_ZeroPad_4);
  342. }
  343. else
  344. {
  345. int positiveYear = -(pymd->year); // pymd->year is negative
  346. bs->AppendChars(_u('-'));
  347. bs->AppendChars(positiveYear, 10, ConvertUInt32ToString_ZeroPad_4);
  348. }
  349. // Add the time.
  350. bs->AppendChars(_u(' '));
  351. // sz - as %02d - HOUR
  352. bs->AppendChars(static_cast<WORD>(pymd->time / 3600000), 2, ConvertUInt16ToString_ZeroPad_2);
  353. bs->AppendChars(_u(':'));
  354. // sz - as %02d - MINUTE
  355. bs->AppendChars(static_cast<WORD>((pymd->time / 60000) % 60), 2, ConvertUInt16ToString_ZeroPad_2);
  356. bs->AppendChars(_u(':'));
  357. // sz - as %02d - SECOND
  358. bs->AppendChars(static_cast<WORD>((pymd->time / 1000) % 60), 2, ConvertUInt16ToString_ZeroPad_2);
  359. bs->AppendChars(_u(' '));
  360. bs->AppendChars(_u("GMT"));
  361. return bs;
  362. }
  363. #ifdef ENABLE_GLOBALIZATION
  364. JavascriptString*
  365. DateImplementation::GetDateLocaleString(DateTime::YMD *pymd, TZD *ptzd, DateTimeFlag noDateTime,ScriptContext* scriptContext)
  366. {
  367. SYSTEMTIME st;
  368. int cch;
  369. int count = 0;
  370. const int kcchMax = 256;
  371. WCHAR wszBuf[kcchMax];
  372. WCHAR *pwszBuf, *pToBeFreed = NULL, *p;
  373. JavascriptString *bs = nullptr;
  374. // the caller of this function should ensure that the range of pymd->year is such that the following conversion works.
  375. st.wYear = (WORD)pymd->year;
  376. st.wMonth = (WORD)pymd->mon + 1;
  377. st.wDayOfWeek = (WORD)pymd->wday;
  378. st.wDay = (WORD)pymd->mday + 1;
  379. st.wHour = (WORD)(pymd->time / 3600000);
  380. st.wMinute = (WORD)((pymd->time / 60000) % 60);
  381. st.wSecond = (WORD)((pymd->time / 1000) % 60);
  382. st.wMilliseconds = (WORD)(pymd->time % 60);
  383. cch = 0;
  384. LCID lcid = GetUserDefaultLCID();
  385. if( !(noDateTime & DateTimeFlag::NoDate))
  386. {
  387. DWORD dwFormat = DATE_LONGDATE;
  388. if ((PRIMARYLANGID(LANGIDFROMLCID(lcid)) == LANG_ARABIC) ||
  389. (PRIMARYLANGID(LANGIDFROMLCID(lcid)) == LANG_HEBREW))
  390. {
  391. dwFormat |= DATE_RTLREADING;
  392. }
  393. int c = GetDateFormatW( lcid, dwFormat, &st, NULL, NULL, 0 );
  394. if( c <= 0 )
  395. {
  396. if (PRIMARYLANGID(LANGIDFROMLCID(lcid)) == LANG_HEBREW)
  397. {
  398. // Can't support some Hebrew dates - current limit is 1 Jan AD 2240
  399. Js::JavascriptError::ThrowRangeError(scriptContext, VBSERR_CantDisplayDate);
  400. }
  401. AssertMsg(false, "GetDateFormat failed");
  402. goto Error;
  403. }
  404. cch += c;
  405. }
  406. if( !(noDateTime & DateTimeFlag::NoTime))
  407. {
  408. int c = GetTimeFormatW( lcid, 0, &st, NULL, NULL, 0 );
  409. if( c <= 0 )
  410. {
  411. AssertMsg(false, "GetTimeFormat failed");
  412. goto Error;
  413. }
  414. cch += c;
  415. }
  416. cch++; // For the space between the date and the time.
  417. if( cch > kcchMax )
  418. {
  419. pwszBuf = pToBeFreed = (WCHAR *)malloc( cch * sizeof(WCHAR) );
  420. if(!pwszBuf)
  421. {
  422. Js::JavascriptError::ThrowOutOfMemoryError(scriptContext);
  423. }
  424. }
  425. else
  426. {
  427. wszBuf[0] = '\0';
  428. pwszBuf = wszBuf;
  429. }
  430. count = cch;
  431. p = pwszBuf;
  432. if( !(noDateTime & DateTimeFlag::NoDate))
  433. {
  434. DWORD dwFormat = DATE_LONGDATE;
  435. if ((PRIMARYLANGID(LANGIDFROMLCID(lcid)) == LANG_ARABIC) ||
  436. (PRIMARYLANGID(LANGIDFROMLCID(lcid)) == LANG_HEBREW))
  437. {
  438. dwFormat |= DATE_RTLREADING;
  439. }
  440. int c = GetDateFormatW( lcid, dwFormat, &st, NULL, p, cch );
  441. if( c <= 0 || c > cch)
  442. {
  443. if (PRIMARYLANGID(LANGIDFROMLCID(lcid)) == LANG_HEBREW)
  444. {
  445. // Can't support some Hebrew dates - current limit is 1 Jan AD 2240
  446. Js::JavascriptError::ThrowRangeError(scriptContext, VBSERR_CantDisplayDate);
  447. }
  448. AssertMsg(false, "GetDateFormat failed");
  449. goto Error;
  450. }
  451. p += (c-1);
  452. cch -= (c-1);
  453. if( !(noDateTime & DateTimeFlag::NoTime))
  454. {
  455. *p++ = _u(' ');
  456. cch--;
  457. }
  458. }
  459. if( !(noDateTime & DateTimeFlag::NoTime))
  460. {
  461. int c = GetTimeFormatW( lcid, 0, &st, NULL, p, cch );
  462. Assert( c > 0 );
  463. if( c <= 0 )
  464. {
  465. AssertMsg(false, "GetTimeFormat failed");
  466. goto Error;
  467. }
  468. cch -= (c-1);
  469. }
  470. bs = JavascriptString::NewCopyBuffer(pwszBuf, count-cch, scriptContext);
  471. Error:
  472. if( pToBeFreed )
  473. free(pToBeFreed);
  474. return bs;
  475. }
  476. #endif // ENABLE_GLOBALIZATION
  477. double
  478. DateImplementation::GetDateData(DateData dd, bool fUtc, ScriptContext* scriptContext)
  479. {
  480. DateTime::YMD *pymd;
  481. double value = 0;
  482. if (JavascriptNumber::IsNan(m_tvUtc))
  483. {
  484. return m_tvUtc;
  485. }
  486. if (fUtc)
  487. {
  488. EnsureYmdUtc();
  489. pymd = &m_ymdUtc;
  490. }
  491. else
  492. {
  493. EnsureYmdLcl(scriptContext);
  494. pymd = &m_ymdLcl;
  495. }
  496. switch (dd)
  497. {
  498. case DateData::Year:
  499. Assert(scriptContext);
  500. // WOOB bug 1099381: ES5 spec B.2.4: getYear() must return YearFromTime() - 1900.
  501. // Note that negative value is OK for the spec.
  502. value = pymd->year - 1900;
  503. break;
  504. case DateData::FullYear:
  505. value = pymd->year;
  506. break;
  507. case DateData::Month:
  508. value = pymd->mon;
  509. break;
  510. case DateData::Date:
  511. value = pymd->mday + 1;
  512. break;
  513. case DateData::Day:
  514. value = pymd->wday;
  515. break;
  516. case DateData::Hours:
  517. value = (pymd->time / 3600000);
  518. break;
  519. case DateData::Minutes:
  520. value = (pymd->time / 60000) % 60;
  521. break;
  522. case DateData::Seconds:
  523. value = (pymd->time / 1000) % 60;
  524. break;
  525. case DateData::Milliseconds:
  526. value = pymd->time % 1000;
  527. break;
  528. case DateData::TimezoneOffset:
  529. //Assert(!fUtc);
  530. value = (m_tvUtc - m_tvLcl) / 60000;
  531. break;
  532. default:
  533. // Shouldn't come here
  534. AssertMsg(false, "DateData type invalid");
  535. }
  536. return value;
  537. }
  538. inline bool
  539. DateImplementation::FBig(char16 ch)
  540. {
  541. return (unsigned int)ch >= 128;
  542. }
  543. inline bool
  544. DateImplementation::FDateDelimiter(char16 ch)
  545. {
  546. return (ch == '/' || ch == '-');
  547. }
  548. ///------------------------------------------------------------------------------
  549. // Parse a string as a date and return the number of milliseconds since
  550. // January 1, 1970 GMT.
  551. //
  552. // TODO: This function is ported from IE8 jscript engine. This lengthy
  553. // and needs a cleanup - break into smaller functions.
  554. ///------------------------------------------------------------------------------
  555. double DateImplementation::UtcTimeFromStr(ScriptContext *scriptContext, JavascriptString *pParseString)
  556. {
  557. Assert(pParseString != nullptr);
  558. double dbl;
  559. if (scriptContext->GetLastUtcTimeFromStr(pParseString, dbl))
  560. {
  561. return dbl;
  562. }
  563. unsigned int ulength = pParseString->GetLength();
  564. const char16 *psz = pParseString->GetSz();
  565. if(UtcTimeFromStrCore(psz, ulength, dbl, scriptContext))
  566. {
  567. scriptContext->SetLastUtcTimeFromStr(pParseString, dbl);
  568. return dbl;
  569. }
  570. Js::JavascriptError::ThrowOutOfMemoryError(scriptContext);
  571. }
  572. bool DateImplementation::TryParseDecimalDigits(
  573. const char16 *const str,
  574. const size_t length,
  575. const size_t startIndex,
  576. const size_t numDigits,
  577. int &value)
  578. {
  579. Assert(str);
  580. Assert(length);
  581. Assert(startIndex <= length);
  582. Assert(numDigits != 0 && numDigits <= 9); // will fit in an 'int'
  583. if(numDigits > length - startIndex)
  584. return false;
  585. size_t i = 0;
  586. // Skip leading zeroes
  587. while(str[startIndex + i] == _u('0') && ++i < numDigits);
  588. // Parse remaining digits
  589. int v = 0;
  590. for(; i < numDigits; ++i)
  591. {
  592. const unsigned short d = str[startIndex + i] - _u('0');
  593. if(d > 9)
  594. break;
  595. v = v * 10 + d;
  596. }
  597. if(i < numDigits)
  598. return false;
  599. Assert(i == numDigits);
  600. value = v;
  601. // The next character must not be a digit
  602. return !(i < length - startIndex && static_cast<unsigned short>(str[startIndex + i] - _u('0')) <= 9);
  603. }
  604. // Either 1 digit or 2 digits or 3 digits
  605. // Ignore any digits after the third
  606. bool DateImplementation::TryParseMilliseconds(
  607. const char16 *const str,
  608. const size_t length,
  609. const size_t startIndex,
  610. int &value,
  611. size_t &foundDigits)
  612. {
  613. const size_t minNumDigits = 1;
  614. Assert(str);
  615. Assert(length);
  616. Assert(startIndex <= length);
  617. size_t allDigits = length - startIndex;
  618. if(allDigits < minNumDigits)
  619. return false;
  620. size_t i = 0;
  621. // Skip leading zeroes
  622. while(str[startIndex + i] == _u('0') && ++i < allDigits);
  623. // Parse remaining digits
  624. int v = 0;
  625. for(; i < allDigits ; ++i)
  626. {
  627. const unsigned short d = str[startIndex + i] - _u('0');
  628. if(d > 9)
  629. break;
  630. if (i < 3) // not past the 3rd digit in the milliseconds, don't ignore
  631. v = v * 10 + d;
  632. }
  633. if(i < minNumDigits)
  634. return false;
  635. foundDigits = i;
  636. if (foundDigits == 1)
  637. v = v * 100;
  638. else if (foundDigits == 2)
  639. v = v * 10;
  640. value = v;
  641. // The next character must not be a digit
  642. return !(i < length - startIndex && static_cast<unsigned short>(str[startIndex + i] - _u('0')) <= 9);
  643. }
  644. bool DateImplementation::TryParseTwoDecimalDigits(
  645. const char16 *const str,
  646. const size_t length,
  647. const size_t startIndex,
  648. int &value,
  649. bool canHaveTrailingDigit /* = false */)
  650. {
  651. Assert(str);
  652. Assert(length);
  653. Assert(startIndex <= length);
  654. if(length - startIndex < 2)
  655. return false;
  656. unsigned short d = str[startIndex] - _u('0');
  657. if(d > 9)
  658. return false;
  659. short v = d * 10;
  660. d = str[startIndex + 1] - _u('0');
  661. if(d > 9)
  662. return false;
  663. value = v + d;
  664. // The next character must not be a digit if canHaveTrailingDigit is false
  665. bool hasNoTrailingDigit = !(length - startIndex > 2 && static_cast<unsigned short>(str[startIndex + 2] - _u('0')) <= 9);
  666. return canHaveTrailingDigit || hasNoTrailingDigit;
  667. }
  668. bool DateImplementation::TryParseIsoString(const char16 *const str, const size_t length, double &timeValue, ScriptContext *scriptContext)
  669. {
  670. Assert(str);
  671. size_t i = 0;
  672. const Js::CharClassifier *classifier = scriptContext->GetCharClassifier();
  673. // Skip leading whitespace (for cross-browser compatibility)
  674. // Also skip bidirectional characters, for Round tripping locale formatted date
  675. while ((classifier->IsWhiteSpace(str[i]) || classifier->IsBiDirectionalChar(str[i])) && ++i < length);
  676. // Minimum length must be 4 (YYYY)
  677. if(length - i < 4)
  678. return false;
  679. // YYYY|(+|-)YYYYYY
  680. int year;
  681. switch(str[i])
  682. {
  683. case _u('+'):
  684. ++i;
  685. if(!TryParseDecimalDigits(str, length, i, 6, year))
  686. return false;
  687. i += 6;
  688. break;
  689. case _u('-'):
  690. ++i;
  691. if(!TryParseDecimalDigits(str, length, i, 6, year) || year == 0)
  692. return false;
  693. year = -year;
  694. i += 6;
  695. break;
  696. case _u('0'):
  697. case _u('1'):
  698. case _u('2'):
  699. case _u('3'):
  700. case _u('4'):
  701. case _u('5'):
  702. case _u('6'):
  703. case _u('7'):
  704. case _u('8'):
  705. case _u('9'):
  706. if(!TryParseDecimalDigits(str, length, i, 4, year))
  707. return false;
  708. i += 4;
  709. break;
  710. default:
  711. return false;
  712. }
  713. // Skip bidirectional characters, for Round tripping locale formatted date
  714. i += classifier->SkipBiDirectionalChars(str, i, length);
  715. int month = 0,
  716. day = 0,
  717. timePortionMilliseconds = 0,
  718. utcOffsetMilliseconds = 0;
  719. bool isLocalTime = false;
  720. do // while(false);
  721. {
  722. do // while(false);
  723. {
  724. // -MM
  725. if(i >= length || str[i] != _u('-'))
  726. break;
  727. ++i;
  728. if(!TryParseTwoDecimalDigits(str, length, i, month))
  729. return false;
  730. --month;
  731. if(month < 0 || month > 11)
  732. return false;
  733. i += 2;
  734. // Skip bidirectional characters, for Round tripping locale formatted date
  735. i += classifier->SkipBiDirectionalChars(str, i, length);
  736. // -DD
  737. if(i >= length || str[i] != _u('-'))
  738. break;
  739. ++i;
  740. if(!TryParseTwoDecimalDigits(str, length, i, day))
  741. return false;
  742. --day;
  743. if(day < 0 || day > 30)
  744. return false;
  745. i += 2;
  746. // Skip bidirectional characters, for Round tripping locale formatted date
  747. i += classifier->SkipBiDirectionalChars(str, i, length);
  748. } while(false);
  749. // THH:mm
  750. if(i >= length || str[i] != _u('T'))
  751. break;
  752. ++i;
  753. int t;
  754. if(!TryParseTwoDecimalDigits(str, length, i, t) || t > 24)
  755. return false;
  756. timePortionMilliseconds += t * (60 * 60 * 1000);
  757. i += 2;
  758. // Skip bidirectional characters, for Round tripping locale formatted date
  759. i += classifier->SkipBiDirectionalChars(str, i, length);
  760. if(i >= length || str[i] != _u(':'))
  761. return false;
  762. ++i;
  763. if(!TryParseTwoDecimalDigits(str, length, i, t) || t > 59)
  764. return false;
  765. timePortionMilliseconds += t * (60 * 1000);
  766. i += 2;
  767. // Skip bidirectional characters, for Round tripping locale formatted date
  768. i += classifier->SkipBiDirectionalChars(str, i, length);
  769. do // while(false);
  770. {
  771. // :ss
  772. if(i >= length || str[i] != _u(':'))
  773. break;
  774. ++i;
  775. if(!TryParseTwoDecimalDigits(str, length, i, t) || t > 59)
  776. return false;
  777. timePortionMilliseconds += t * 1000;
  778. i += 2;
  779. // Skip bidirectional characters, for Round tripping locale formatted date
  780. i += classifier->SkipBiDirectionalChars(str, i, length);
  781. // .sss
  782. if(i >= length || str[i] != _u('.'))
  783. break;
  784. ++i;
  785. // Require one or more decimal digits. Ignore digits beyond the third
  786. size_t foundDigits = 0;
  787. if(!TryParseMilliseconds(str, length, i, t, foundDigits))
  788. return false;
  789. timePortionMilliseconds += t;
  790. i += foundDigits;
  791. // Skip bidirectional characters, for Round tripping locale formatted date
  792. i += classifier->SkipBiDirectionalChars(str, i, length);
  793. } while(false);
  794. // Z|(+|-)HH:mm
  795. if(i >= length)
  796. {
  797. isLocalTime = true;
  798. break;
  799. }
  800. const char16 utcOffsetSign = str[i];
  801. if(utcOffsetSign == _u('Z'))
  802. {
  803. ++i;
  804. break;
  805. }
  806. if(utcOffsetSign != _u('+') && utcOffsetSign != _u('-'))
  807. {
  808. isLocalTime = true;
  809. break;
  810. }
  811. ++i;
  812. // In -version:6 we allow optional colons in the timezone offset
  813. if (!TryParseTwoDecimalDigits(str, length, i, t, scriptContext->GetConfig()->IsES6DateParseFixEnabled() /* Timezone may be 4 sequential digits */) || t > 24)
  814. return false;
  815. utcOffsetMilliseconds += t * (60 * 60 * 1000);
  816. i += 2;
  817. // Skip bidirectional characters, for Round tripping locale formatted date
  818. i += classifier->SkipBiDirectionalChars(str, i, length);
  819. if(i >= length)
  820. return false;
  821. // The ':' is optional in ISO 8601
  822. if (str[i] == _u(':'))
  823. {
  824. ++i;
  825. }
  826. if(!TryParseTwoDecimalDigits(str, length, i, t) || t > 59)
  827. return false;
  828. utcOffsetMilliseconds += t * (60 * 1000);
  829. i += 2;
  830. // Skip bidirectional characters, for Round tripping locale formatted date
  831. i += classifier->SkipBiDirectionalChars(str, i, length);
  832. if(utcOffsetSign == _u('-'))
  833. utcOffsetMilliseconds = -utcOffsetMilliseconds;
  834. } while(false);
  835. // Skip trailing whitespace (for cross-browser compatibility)
  836. // Skip bidirectional characters, for Round tripping locale formatted date
  837. while (i < length && (classifier->IsWhiteSpace(str[i]) || classifier->IsBiDirectionalChar(str[i])))
  838. ++i;
  839. // There should only have been whitespace remaining, if any
  840. if(i < length)
  841. return false;
  842. Assert(i == length);
  843. // Compute the time value
  844. timeValue = TvFromDate(year, month, day, timePortionMilliseconds - utcOffsetMilliseconds);
  845. if (isLocalTime)
  846. {
  847. // Compatibility note:
  848. // In ES5, it was unspecified how to handle date strings without the trailing time zone offset "Z|(+|-)HH:mm".
  849. // In ES5.1, an absent time zone offset defaulted to "Z", which contradicted ISO8601:2004(E).
  850. // This was corrected in an ES5.1 errata note. Moreover, the ES6 draft now follows ISO8601.
  851. timeValue = GetTvUtc(timeValue, scriptContext);
  852. }
  853. return true;
  854. }
  855. bool DateImplementation::UtcTimeFromStrCore(
  856. __in_ecount_z(ulength) const char16 *psz,
  857. unsigned int ulength,
  858. double &retVal,
  859. ScriptContext *const scriptContext)
  860. {
  861. Assert(scriptContext);
  862. if (ulength >= 0x7fffffff)
  863. {
  864. //Prevent unreasonable requests from causing overflows.
  865. return false;
  866. }
  867. if (nullptr == psz)
  868. {
  869. retVal = JavascriptNumber::NaN;
  870. return true;
  871. }
  872. // Try to parse the string as the ISO format first
  873. if(TryParseIsoString(psz, ulength, retVal, scriptContext))
  874. {
  875. return true;
  876. }
  877. enum
  878. {
  879. ssNil,
  880. ssMinutes,
  881. ssSeconds,
  882. ssMillisecond,
  883. ssAddOffset,
  884. ssSubOffset,
  885. ssDate,
  886. ssMonth,
  887. ssYear
  888. };
  889. char16 *pchBase;
  890. char16 *pch;
  891. char16 ch;
  892. char16 *pszSrc = nullptr;
  893. const int32 lwNil = 0x80000000;
  894. int32 cch;
  895. int32 depth;
  896. int32 lwT;
  897. int32 lwYear = lwNil;
  898. int32 lwMonth = lwNil;
  899. int32 lwDate = lwNil;
  900. int32 lwTime = lwNil;
  901. int32 lwMillisecond = lwNil;
  902. int32 lwZone = lwNil;
  903. int32 lwOffset = lwNil;
  904. int32 ss = ssNil;
  905. const SZS *pszs;
  906. bool fUtc;
  907. int tAmPm = 0;
  908. int tBcAd = 0;
  909. size_t numOfDigits = 0;
  910. double tv = JavascriptNumber::NaN; // Initialized for error handling.
  911. //Create a copy to analyze
  912. BEGIN_TEMP_ALLOCATOR(tempAllocator, scriptContext, _u("UtcTimeFromStr"));
  913. pszSrc = AnewArray(tempAllocator, char16, ulength + 1);
  914. size_t size = sizeof(char16) * (ulength + 1);
  915. js_memcpy_s(pszSrc, size, psz, size);
  916. _wcslwr_s(pszSrc,ulength+1);
  917. bool isDateNegativeVersion5 = false;
  918. bool isNextFieldDateNegativeVersion5 = false;
  919. bool isZeroPaddedYear = false;
  920. const Js::CharClassifier *classifier = scriptContext->GetCharClassifier();
  921. #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")
  922. for (pch = pszSrc; 0 != (ch = classifier->SkipBiDirectionalChars(pch));)
  923. {
  924. pch++;
  925. if (ch <= ' ')
  926. {
  927. continue;
  928. }
  929. switch (ch)
  930. {
  931. case '(':
  932. {
  933. // skip stuff in parens
  934. for (depth = 1; 0 != (ch = *pch); )
  935. {
  936. pch++;
  937. if (ch == '(')
  938. {
  939. depth++;
  940. }
  941. else if (ch == ')' && --depth <= 0)
  942. {
  943. break;
  944. }
  945. }
  946. continue;
  947. }
  948. case ',':
  949. case ':':
  950. case '/':
  951. {
  952. // ignore these
  953. continue;
  954. }
  955. case '+':
  956. case '-':
  957. {
  958. if (lwNil == lwTime)
  959. {
  960. goto LError;
  961. }
  962. ss = (ch == '+') ? ssAddOffset : ssSubOffset;
  963. continue;
  964. }
  965. }
  966. pchBase = pch - 1;
  967. if (!FBig(ch) && isalpha(ch))
  968. {
  969. for ( ; !FBig(*pch) && (isalpha(*pch) || '.' == *pch); pch++)
  970. ;
  971. cch = (int32)(pch - pchBase);
  972. if ('.' == pchBase[cch - 1])
  973. {
  974. cch--;
  975. }
  976. //Assert(cch > 0);
  977. // skip to the next real character
  978. while (0 != (*pch) && (*pch <= ' ' || classifier->IsBiDirectionalChar(*pch)))
  979. {
  980. pch++;
  981. }
  982. // have an alphabetic token - look it up
  983. if (cch == 1)
  984. {
  985. AssertMsg(isNextFieldDateNegativeVersion5 == false, "isNextFieldDateNegativeVersion5 == false");
  986. // military version of time zone
  987. // z = GMT
  988. // j isn't used
  989. // a to m are 1 to 12
  990. // n to y are -1 to -12
  991. if (lwNil != lwZone)
  992. {
  993. goto LError;
  994. }
  995. if (ch <= 'm')
  996. {
  997. if (ch == 'j' || ch < 'a')
  998. {
  999. goto LError;
  1000. }
  1001. lwZone = (int32)(ch - 'a' + (ch < 'j')) * 60;
  1002. }
  1003. else if (ch <= 'y')
  1004. {
  1005. lwZone = -(int32)(ch - 'm') * 60;
  1006. }
  1007. else if (ch == 'z')
  1008. {
  1009. lwZone = 0;
  1010. }
  1011. else
  1012. {
  1013. goto LError;
  1014. }
  1015. // look for a time zone offset
  1016. ss = ('+' == *pch) ? (pch++, ssAddOffset) :
  1017. ('-' == *pch) ? (pch++, ssSubOffset) : ssNil;
  1018. continue;
  1019. }
  1020. // look for a token
  1021. for (pszs = g_rgszs + kcszs; ; )
  1022. {
  1023. if (pszs-- <= g_rgszs)
  1024. goto LError;
  1025. if (cch <= pszs->cch &&
  1026. 0 == memcmp(pchBase, pszs->psz, cch * sizeof(char16)))
  1027. {
  1028. break;
  1029. }
  1030. }
  1031. switch (pszs->szst)
  1032. {
  1033. case ParseStringTokenType::BcAd:
  1034. {
  1035. if (tBcAd != 0)
  1036. {
  1037. goto LError;
  1038. }
  1039. tBcAd = (int)pszs->lwVal;
  1040. break;
  1041. }
  1042. case ParseStringTokenType::AmPm:
  1043. {
  1044. if (tAmPm != 0)
  1045. {
  1046. goto LError;
  1047. }
  1048. tAmPm = (int)pszs->lwVal;
  1049. break;
  1050. }
  1051. case ParseStringTokenType::Month:
  1052. {
  1053. if (lwNil != lwMonth)
  1054. {
  1055. goto LError;
  1056. }
  1057. lwMonth = pszs->lwVal;
  1058. if ('-' == *pch)
  1059. {
  1060. // handle the case date is negative for "Thu, 23 Sep -0007 00:00:00 GMT"
  1061. isDateNegativeVersion5 = true;
  1062. pch++;
  1063. }
  1064. break;
  1065. }
  1066. case ParseStringTokenType::Zone:
  1067. {
  1068. if (lwNil != lwZone)
  1069. {
  1070. goto LError;
  1071. }
  1072. lwZone = pszs->lwVal;
  1073. // look for a time zone offset
  1074. ss = ('+' == *pch) ? (pch++, ssAddOffset) :
  1075. ('-' == *pch) ? (pch++, ssSubOffset) : ssNil;
  1076. break;
  1077. }
  1078. }
  1079. continue;
  1080. }
  1081. if (FBig(ch) || !isdigit(ch))
  1082. {
  1083. goto LError;
  1084. }
  1085. for (lwT = ch - '0'; ; pch++)
  1086. {
  1087. // for time zone offset HH:mm, we already got HH so skip ':' and grab mm
  1088. if (((ss == ssAddOffset) || (ss == ssSubOffset)) && (*pch == ':'))
  1089. {
  1090. continue;
  1091. }
  1092. if (FBig(*pch) || !isdigit(*pch))
  1093. {
  1094. break;
  1095. }
  1096. // to avoid overflow
  1097. if (pch - pchBase > 6)
  1098. {
  1099. goto LError;
  1100. }
  1101. // convert string to number, e.g. 07:30 -> 730
  1102. lwT = lwT * 10 + *pch - '0';
  1103. }
  1104. numOfDigits = pch - pchBase;
  1105. // skip to the next real character
  1106. while (0 != (ch = *pch) && (ch <= ' ' || classifier->IsBiDirectionalChar(ch)))
  1107. {
  1108. pch++;
  1109. }
  1110. switch (ss)
  1111. {
  1112. case ssAddOffset:
  1113. case ssSubOffset:
  1114. {
  1115. AssertMsg(isNextFieldDateNegativeVersion5 == false, "isNextFieldDateNegativeVersion5 == false");
  1116. if (lwNil != lwOffset || lwNil == lwTime)
  1117. goto LError;
  1118. // convert into minutes, e.g. 730 -> 7*60+30
  1119. lwOffset = lwT < 24 ? lwT * 60 :
  1120. (lwT % 100) + (lwT / 100) * 60;
  1121. if (ssSubOffset == ss)
  1122. lwOffset = -lwOffset;
  1123. lwZone = 0; // An offset is always with respect to UTC
  1124. ss = ssNil;
  1125. break;
  1126. }
  1127. case ssMinutes:
  1128. {
  1129. AssertMsg(isNextFieldDateNegativeVersion5 == false, "isNextFieldDateNegativeVersion5 == false");
  1130. if (lwT >= 60)
  1131. goto LError;
  1132. lwTime += lwT * 60;
  1133. ss = (ch == ':') ? (pch++, ssSeconds) : ssNil;
  1134. break;
  1135. }
  1136. case ssSeconds:
  1137. {
  1138. AssertMsg(isNextFieldDateNegativeVersion5 == false, "isNextFieldDateNegativeVersion5 == false");
  1139. if (lwT >= 60)
  1140. goto LError;
  1141. lwTime += lwT;
  1142. ss = (ch == '.') ? (pch++, ssMillisecond) : ssNil;
  1143. break;
  1144. }
  1145. case ssMillisecond:
  1146. {
  1147. AssertMsg(isNextFieldDateNegativeVersion5 == false, "isNextFieldDateNegativeVersion5 == false");
  1148. if (numOfDigits <= 1)
  1149. {
  1150. // 1 digit only, treat it as hundreds
  1151. lwMillisecond = lwT * 100;
  1152. }
  1153. else if (numOfDigits <= 2)
  1154. {
  1155. // 2 digit only, treat it as tens
  1156. lwMillisecond = lwT * 10;
  1157. }
  1158. else if (numOfDigits <= 3)
  1159. {
  1160. // canonical 3 digit, per EcmaScript spec
  1161. lwMillisecond = lwT;
  1162. }
  1163. else
  1164. {
  1165. // ignore any digits beyond the third
  1166. lwMillisecond = (pchBase[0] - '0') * 100 + (pchBase[1] - '0') * 10 + (pchBase[2] - '0');
  1167. }
  1168. ss = ssNil;
  1169. break;
  1170. }
  1171. case ssDate:
  1172. {
  1173. AssertMsg(isNextFieldDateNegativeVersion5 == false, "isNextFieldDateNegativeVersion5 == false");
  1174. if (lwNil != lwDate)
  1175. goto LError;
  1176. lwDate = lwT;
  1177. if ((lwNil == lwYear) && FDateDelimiter(ch))
  1178. {
  1179. // We have already parsed the year if the date is specified as YYYY/MM/DD,
  1180. // but not when it is specified as MM/DD/YYYY.
  1181. ss = ssYear;
  1182. pch++;
  1183. }
  1184. else
  1185. {
  1186. ss = ssNil;
  1187. }
  1188. break;
  1189. }
  1190. case ssMonth:
  1191. {
  1192. AssertMsg(isNextFieldDateNegativeVersion5 == false, "isNextFieldDateNegativeVersion5 == false");
  1193. if (lwNil != lwMonth)
  1194. {
  1195. goto LError;
  1196. }
  1197. lwMonth = lwT - 1;
  1198. if (FDateDelimiter(ch))
  1199. {
  1200. // Mark the next token as the date so that it won't be confused with another token.
  1201. // For example, if the next character is '-' as in "2015-1-1", then it'd be used as
  1202. // the time offset without this.
  1203. ss = ssDate;
  1204. pch++;
  1205. }
  1206. break;
  1207. }
  1208. case ssYear:
  1209. {
  1210. AssertMsg(isNextFieldDateNegativeVersion5 == false, "isNextFieldDateNegativeVersion5 == false");
  1211. if (lwNil != lwYear)
  1212. goto LError;
  1213. AssertMsg(isDateNegativeVersion5 == false, "lwYear should be positive as pre-version:5 parsing");
  1214. lwYear = lwT;
  1215. ss = ssNil;
  1216. if (lwT < 1000 && numOfDigits >= 4)
  1217. {
  1218. isZeroPaddedYear = true;
  1219. }
  1220. break;
  1221. }
  1222. default:
  1223. {
  1224. // assumptions for getting a YEAR:
  1225. // - an absolute value greater or equal than 70 (thus not hour!)
  1226. // - wasn't preceded by negative sign for -version:5 year format
  1227. // - lwT has at least 4 digits (e.g. 0017 is year 17 AD)
  1228. if (lwT >= 70 || isNextFieldDateNegativeVersion5 || numOfDigits >= 4)
  1229. {
  1230. // assume it's a year - this is used particularly as version:5 year parsing
  1231. if (lwNil != lwYear)
  1232. goto LError;
  1233. // handle the case date is negative for "Tue Feb 02 -2012 01:02:03 GMT-0800"
  1234. lwYear = isDateNegativeVersion5 ? -lwT : lwT;
  1235. isNextFieldDateNegativeVersion5 = false;
  1236. if (lwT < 1000 && numOfDigits >= 4)
  1237. {
  1238. isZeroPaddedYear = true;
  1239. }
  1240. if (FDateDelimiter(ch))
  1241. {
  1242. // Mark the next token as the month so that it won't be confused with another token.
  1243. // For example, if the next character is '-' as in "2015-1-1", then it'd be used as
  1244. // the time offset without this.
  1245. ss = ssMonth;
  1246. pch++;
  1247. }
  1248. break;
  1249. }
  1250. switch (ch)
  1251. {
  1252. case ':':
  1253. {
  1254. // hour
  1255. if (lwNil != lwTime)
  1256. goto LError;
  1257. if (lwT >= 24)
  1258. goto LError;
  1259. lwTime = lwT * 3600;
  1260. ss = ssMinutes;
  1261. pch++;
  1262. break;
  1263. }
  1264. case '/':
  1265. case '-':
  1266. {
  1267. // month
  1268. if (lwNil != lwMonth)
  1269. {
  1270. // can be year
  1271. if (lwNil != lwYear)
  1272. {
  1273. // both were already parsed!
  1274. goto LError;
  1275. }
  1276. else
  1277. {
  1278. // this is a day - with the negative sign for the date (version 5+)
  1279. lwDate = lwT;
  1280. isDateNegativeVersion5 = true;
  1281. // mark the next field to be year (version 5+)
  1282. isNextFieldDateNegativeVersion5 = true;
  1283. }
  1284. }
  1285. else
  1286. {
  1287. // this is a month
  1288. lwMonth = lwT - 1;
  1289. ss = ssDate;
  1290. }
  1291. pch++;
  1292. break;
  1293. }
  1294. default:
  1295. {
  1296. // date
  1297. if (lwNil != lwDate)
  1298. goto LError;
  1299. lwDate = lwT;
  1300. break;
  1301. }
  1302. }
  1303. break;
  1304. }
  1305. }
  1306. continue;
  1307. }
  1308. if (lwNil == lwYear ||
  1309. lwNil == lwMonth || lwMonth > 11 ||
  1310. lwNil == lwDate || lwDate > 31)
  1311. {
  1312. goto LError;
  1313. }
  1314. if (tBcAd != 0)
  1315. {
  1316. if (tBcAd < 0)
  1317. {
  1318. // BC. Note that 1 BC is year 0 and 2 BC is year -1.
  1319. lwYear = -lwYear + 1;
  1320. }
  1321. }
  1322. else if (lwYear < 50 && isDateNegativeVersion5 == false && isZeroPaddedYear == false)
  1323. {
  1324. lwYear += 2000;
  1325. }
  1326. else if (lwYear < 100 && isDateNegativeVersion5 == false && isZeroPaddedYear == false)
  1327. {
  1328. lwYear += 1900;
  1329. }
  1330. if (tAmPm != 0)
  1331. {
  1332. if (lwNil == lwTime)
  1333. {
  1334. goto LError;
  1335. }
  1336. if (lwTime >= 12 * 3600L && lwTime < 13 * 3600L)
  1337. {
  1338. // In the 12:00 hour. AM means subtract 12 hours and PM means
  1339. // do nothing.
  1340. if (tAmPm < 0)
  1341. {
  1342. lwTime -= 12 * 3600L;
  1343. }
  1344. }
  1345. else
  1346. {
  1347. // Not in the 12:00 hour. AM means do nothing and PM means
  1348. // add 12 hours.
  1349. if (tAmPm > 0)
  1350. {
  1351. if (lwTime >= 12 * 3600L)
  1352. {
  1353. goto LError;
  1354. }
  1355. lwTime += 12 * 3600L;
  1356. }
  1357. }
  1358. }
  1359. else if (lwNil == lwTime)
  1360. {
  1361. lwTime = 0;
  1362. }
  1363. if (lwNil == lwMillisecond)
  1364. {
  1365. lwMillisecond = 0;
  1366. }
  1367. if (lwNil != lwZone)
  1368. {
  1369. lwTime -= lwZone * 60;
  1370. fUtc = TRUE;
  1371. }
  1372. else
  1373. {
  1374. fUtc = FALSE;
  1375. }
  1376. if (lwNil != lwOffset)
  1377. {
  1378. lwTime -= lwOffset * 60;
  1379. }
  1380. // Rebuild time.
  1381. tv = TvFromDate(lwYear, lwMonth, lwDate - 1, (double)lwTime * 1000 + lwMillisecond);
  1382. if (!fUtc)
  1383. {
  1384. tv = GetTvUtc(tv, scriptContext);
  1385. }
  1386. LError:
  1387. END_TEMP_ALLOCATOR(tempAllocator, scriptContext);
  1388. retVal = tv;
  1389. return true;
  1390. }
  1391. double DateImplementation::DateFncUTC(ScriptContext* scriptContext, Arguments args)
  1392. {
  1393. const int kcvarMax = 7;
  1394. double rgdbl[kcvarMax];
  1395. double tv;
  1396. double dblT;
  1397. uint ivar;
  1398. // See: https://github.com/Microsoft/ChakraCore/issues/1665
  1399. // Date.UTC should return NaN with 0 arguments.
  1400. // args.Info.Count includes an implicit first parameter, so we check for Count <= 1.
  1401. if (args.Info.Count <= 1)
  1402. {
  1403. return JavascriptNumber::NaN;
  1404. }
  1405. for (ivar = 0; (ivar < (args.Info.Count-1)) && ivar < kcvarMax; ++ivar)
  1406. {
  1407. rgdbl[ivar] = JavascriptConversion::ToNumber(args[ivar+1],scriptContext);
  1408. }
  1409. for (ivar = 0; ivar < kcvarMax; ivar++)
  1410. {
  1411. // Unspecified parameters are treated like zero, except date, which
  1412. // is treated as 1.
  1413. if (ivar >= (args.Info.Count - 1))
  1414. {
  1415. rgdbl[ivar] = (ivar == 2);
  1416. continue;
  1417. }
  1418. #pragma prefast(suppress:6001, "rgdbl index ivar < args.Info.Count - 1 are initialized")
  1419. dblT = rgdbl[ivar];
  1420. if (!Js::NumberUtilities::IsFinite(dblT))
  1421. {
  1422. return JavascriptNumber::NaN;
  1423. }
  1424. rgdbl[ivar] = ConvertToInteger(dblT);
  1425. }
  1426. // adjust the year
  1427. if (rgdbl[0] < 100 && rgdbl[0] >= 0)
  1428. {
  1429. rgdbl[0] += 1900;
  1430. }
  1431. // Get the UTC time value.
  1432. tv = TvFromDate(rgdbl[0], rgdbl[1], rgdbl[2] - 1,
  1433. rgdbl[3] * 3600000 + rgdbl[4] * 60000 + rgdbl[5] * 1000 + rgdbl[6]);
  1434. if (tv < ktvMin || tv > ktvMax)
  1435. {
  1436. return JavascriptNumber::NaN;
  1437. }
  1438. else
  1439. {
  1440. return tv;
  1441. }
  1442. }
  1443. // Maximum number of arguments used by this set operation.
  1444. static const int mpddcvar[] =
  1445. {
  1446. 1, // Year
  1447. 3, // FullYear
  1448. 2, // Month
  1449. 1, // Date
  1450. 4, // Hours
  1451. 3, // Minutes
  1452. 2, // Seconds
  1453. 1, // Milliseconds
  1454. 0, // Day (shouldn't happen)
  1455. 0, // TimezoneOffset (shouldn't happen)
  1456. };
  1457. double DateImplementation::SetDateData(Arguments args, DateData dd, bool fUtc, ScriptContext* scriptContext)
  1458. {
  1459. // This must accommodate the largest cvar in mpddcvar.
  1460. double rgdbl[5];
  1461. double tv = 0;
  1462. DateTime::YMD *pymd = NULL;
  1463. DateTime::YMD emptyYMD = {0};
  1464. uint count = 0;
  1465. uint cvarMax;
  1466. uint ivar;
  1467. // PREFAST: check limits
  1468. if (dd < 0 || dd >= DateData::Lim)
  1469. {
  1470. AssertMsg(false, "DateData type invalid");
  1471. Js::JavascriptError::ThrowError(scriptContext, VBSERR_InternalError);
  1472. }
  1473. // Get the parameters.
  1474. cvarMax = mpddcvar[dd];
  1475. __analysis_assume(cvarMax <= 4);
  1476. //
  1477. // arg[0] would be the date object itself
  1478. //
  1479. for (ivar = 0; (ivar < (args.Info.Count-1)) && ivar < cvarMax; ++ivar)
  1480. {
  1481. rgdbl[ivar] = JavascriptConversion::ToNumber(args[ivar+1],scriptContext);
  1482. if (!Js::NumberUtilities::IsFinite(rgdbl[ivar]))
  1483. goto LSetNan;
  1484. rgdbl[ivar] = ConvertToInteger(rgdbl[ivar]);
  1485. }
  1486. if ((count = ivar) < 1)
  1487. {
  1488. goto LSetNan;
  1489. }
  1490. if (JavascriptNumber::IsNan(m_tvUtc))
  1491. {
  1492. // If the current time is not finite, the only way we can end up
  1493. // with non-NaN is for setFullYear/setYear.
  1494. // See ES5 15.9.5.40, ES5 B.2.5.
  1495. if (!(DateData::FullYear == dd || DateData::Year == dd))
  1496. {
  1497. goto LSetNan;
  1498. }
  1499. pymd = &emptyYMD; // We need mon, mday, time to be 0.
  1500. // Fall through to DateData::Year and DataData::FullYear cases below.
  1501. }
  1502. else
  1503. {
  1504. if (fUtc)
  1505. {
  1506. EnsureYmdUtc();
  1507. pymd = &m_ymdUtc;
  1508. tv = m_tvUtc;
  1509. }
  1510. else
  1511. {
  1512. EnsureYmdLcl(scriptContext);
  1513. pymd = &m_ymdLcl;
  1514. tv = m_tvLcl;
  1515. }
  1516. }
  1517. ivar = 0;
  1518. switch (dd)
  1519. {
  1520. case DateData::Year:
  1521. if (rgdbl[0] < 100 && rgdbl[0] >= 0)
  1522. rgdbl[0] += 1900;
  1523. // fall-through
  1524. case DateData::FullYear:
  1525. LFullYear:
  1526. if (count < 3)
  1527. {
  1528. // Only {year} or {year, month} is specified. Day is not specified.
  1529. rgdbl[2] = pymd->mday + 1;
  1530. if (count < 2)
  1531. {
  1532. // Month is not specified.
  1533. rgdbl[1] = pymd->mon;
  1534. }
  1535. }
  1536. tv = TvFromDate(rgdbl[0], rgdbl[1], rgdbl[2] - 1, pymd->time);
  1537. break;
  1538. case DateData::Month:
  1539. memmove(rgdbl + 1, rgdbl, count * sizeof(double));
  1540. count++;
  1541. rgdbl[0] = pymd->year;
  1542. goto LFullYear;
  1543. case DateData::Date:
  1544. tv += (rgdbl[ivar] - pymd->mday - 1) * 86400000;
  1545. if (++ivar >= count)
  1546. {
  1547. break;
  1548. }
  1549. // fall-through
  1550. case DateData::Hours:
  1551. tv += (rgdbl[ivar] - (pymd->time / 3600000)) * 3600000;
  1552. if (++ivar >= count)
  1553. {
  1554. break;
  1555. }
  1556. // fall-through
  1557. case DateData::Minutes:
  1558. tv += (rgdbl[ivar] - (pymd->time / 60000) % 60) * 60000;
  1559. if (++ivar >= count)
  1560. {
  1561. break;
  1562. }
  1563. // fall-through
  1564. case DateData::Seconds:
  1565. tv += (rgdbl[ivar] - (pymd->time / 1000) % 60) * 1000;
  1566. if (++ivar >= count)
  1567. {
  1568. break;
  1569. }
  1570. // fall-through
  1571. case DateData::Milliseconds:
  1572. tv += rgdbl[ivar] - pymd->time % 1000;
  1573. break;
  1574. default:
  1575. AssertMsg(false, "DataData type invalid");
  1576. }
  1577. if (fUtc)
  1578. {
  1579. SetTvUtc(tv);
  1580. }
  1581. else
  1582. {
  1583. SetTvLcl(tv, scriptContext);
  1584. }
  1585. m_modified = true;
  1586. return m_tvUtc;
  1587. LSetNan:
  1588. m_grfval = 0;
  1589. m_tvUtc = JavascriptNumber::NaN;
  1590. m_modified = true;
  1591. return m_tvUtc;
  1592. }
  1593. } // namespace Js