DateImplementation.cpp 56 KB

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