PageRenderTime 213ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 1ms

/js/src/jsdate.cpp

http://github.com/zpao/v8monkey
C++ | 2820 lines | 2011 code | 393 blank | 416 comment | 507 complexity | d888822aeeaa26b4c5976840971fb528 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, LGPL-3.0, AGPL-1.0, LGPL-2.1, BSD-3-Clause, GPL-2.0, JSON, Apache-2.0, 0BSD
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2. * vim: set ts=8 sw=4 et tw=78:
  3. *
  4. * ***** BEGIN LICENSE BLOCK *****
  5. * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  6. *
  7. * The contents of this file are subject to the Mozilla Public License Version
  8. * 1.1 (the "License"); you may not use this file except in compliance with
  9. * the License. You may obtain a copy of the License at
  10. * http://www.mozilla.org/MPL/
  11. *
  12. * Software distributed under the License is distributed on an "AS IS" basis,
  13. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  14. * for the specific language governing rights and limitations under the
  15. * License.
  16. *
  17. * The Original Code is Mozilla Communicator client code, released
  18. * March 31, 1998.
  19. *
  20. * The Initial Developer of the Original Code is
  21. * Netscape Communications Corporation.
  22. * Portions created by the Initial Developer are Copyright (C) 1998
  23. * the Initial Developer. All Rights Reserved.
  24. *
  25. * Contributor(s):
  26. *
  27. * Alternatively, the contents of this file may be used under the terms of
  28. * either of the GNU General Public License Version 2 or later (the "GPL"),
  29. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  30. * in which case the provisions of the GPL or the LGPL are applicable instead
  31. * of those above. If you wish to allow use of your version of this file only
  32. * under the terms of either the GPL or the LGPL, and not to allow others to
  33. * use your version of this file under the terms of the MPL, indicate your
  34. * decision by deleting the provisions above and replace them with the notice
  35. * and other provisions required by the GPL or the LGPL. If you do not delete
  36. * the provisions above, a recipient may use your version of this file under
  37. * the terms of any one of the MPL, the GPL or the LGPL.
  38. *
  39. * ***** END LICENSE BLOCK ***** */
  40. /*
  41. * JS date methods.
  42. */
  43. /*
  44. * "For example, OS/360 devotes 26 bytes of the permanently
  45. * resident date-turnover routine to the proper handling of
  46. * December 31 on leap years (when it is Day 366). That
  47. * might have been left to the operator."
  48. *
  49. * Frederick Brooks, 'The Second-System Effect'.
  50. */
  51. #include <ctype.h>
  52. #include <locale.h>
  53. #include <math.h>
  54. #include <stdlib.h>
  55. #include <string.h>
  56. #include "mozilla/Util.h"
  57. #include "jstypes.h"
  58. #include "jsprf.h"
  59. #include "prmjtime.h"
  60. #include "jsutil.h"
  61. #include "jsapi.h"
  62. #include "jsversion.h"
  63. #include "jscntxt.h"
  64. #include "jsdate.h"
  65. #include "jsinterp.h"
  66. #include "jsnum.h"
  67. #include "jsobj.h"
  68. #include "jsstr.h"
  69. #include "jslibmath.h"
  70. #include "vm/GlobalObject.h"
  71. #include "jsinferinlines.h"
  72. #include "jsobjinlines.h"
  73. #include "jsstrinlines.h"
  74. #include "vm/Stack-inl.h"
  75. using namespace mozilla;
  76. using namespace js;
  77. using namespace js::types;
  78. /*
  79. * The JS 'Date' object is patterned after the Java 'Date' object.
  80. * Here is an script:
  81. *
  82. * today = new Date();
  83. *
  84. * print(today.toLocaleString());
  85. *
  86. * weekDay = today.getDay();
  87. *
  88. *
  89. * These Java (and ECMA-262) methods are supported:
  90. *
  91. * UTC
  92. * getDate (getUTCDate)
  93. * getDay (getUTCDay)
  94. * getHours (getUTCHours)
  95. * getMinutes (getUTCMinutes)
  96. * getMonth (getUTCMonth)
  97. * getSeconds (getUTCSeconds)
  98. * getMilliseconds (getUTCMilliseconds)
  99. * getTime
  100. * getTimezoneOffset
  101. * getYear
  102. * getFullYear (getUTCFullYear)
  103. * parse
  104. * setDate (setUTCDate)
  105. * setHours (setUTCHours)
  106. * setMinutes (setUTCMinutes)
  107. * setMonth (setUTCMonth)
  108. * setSeconds (setUTCSeconds)
  109. * setMilliseconds (setUTCMilliseconds)
  110. * setTime
  111. * setYear (setFullYear, setUTCFullYear)
  112. * toGMTString (toUTCString)
  113. * toLocaleString
  114. * toString
  115. *
  116. *
  117. * These Java methods are not supported
  118. *
  119. * setDay
  120. * before
  121. * after
  122. * equals
  123. * hashCode
  124. */
  125. /*
  126. * 11/97 - jsdate.c has been rewritten to conform to the ECMA-262 language
  127. * definition and reduce dependence on NSPR. NSPR is used to get the current
  128. * time in milliseconds, the time zone offset, and the daylight savings time
  129. * offset for a given time. NSPR is also used for Date.toLocaleString(), for
  130. * locale-specific formatting, and to get a string representing the timezone.
  131. * (Which turns out to be platform-dependent.)
  132. *
  133. * To do:
  134. * (I did some performance tests by timing how long it took to run what
  135. * I had of the js ECMA conformance tests.)
  136. *
  137. * - look at saving results across multiple calls to supporting
  138. * functions; the toString functions compute some of the same values
  139. * multiple times. Although - I took a quick stab at this, and I lost
  140. * rather than gained. (Fractionally.) Hard to tell what compilers/processors
  141. * are doing these days.
  142. *
  143. * - look at tweaking function return types to return double instead
  144. * of int; this seems to make things run slightly faster sometimes.
  145. * (though it could be architecture-dependent.) It'd be good to see
  146. * how this does on win32. (Tried it on irix.) Types could use a
  147. * general going-over.
  148. */
  149. /*
  150. * Supporting functions - ECMA 15.9.1.*
  151. */
  152. #define HoursPerDay 24.0
  153. #define MinutesPerDay (HoursPerDay * MinutesPerHour)
  154. #define MinutesPerHour 60.0
  155. #define SecondsPerDay (MinutesPerDay * SecondsPerMinute)
  156. #define SecondsPerHour (MinutesPerHour * SecondsPerMinute)
  157. #define SecondsPerMinute 60.0
  158. #if defined(XP_WIN) || defined(XP_OS2)
  159. /* Work around msvc double optimization bug by making these runtime values; if
  160. * they're available at compile time, msvc optimizes division by them by
  161. * computing the reciprocal and multiplying instead of dividing - this loses
  162. * when the reciprocal isn't representable in a double.
  163. */
  164. static jsdouble msPerSecond = 1000.0;
  165. static jsdouble msPerDay = SecondsPerDay * 1000.0;
  166. static jsdouble msPerHour = SecondsPerHour * 1000.0;
  167. static jsdouble msPerMinute = SecondsPerMinute * 1000.0;
  168. #else
  169. #define msPerDay (SecondsPerDay * msPerSecond)
  170. #define msPerHour (SecondsPerHour * msPerSecond)
  171. #define msPerMinute (SecondsPerMinute * msPerSecond)
  172. #define msPerSecond 1000.0
  173. #endif
  174. #define Day(t) floor((t) / msPerDay)
  175. static jsdouble
  176. TimeWithinDay(jsdouble t)
  177. {
  178. jsdouble result;
  179. result = fmod(t, msPerDay);
  180. if (result < 0)
  181. result += msPerDay;
  182. return result;
  183. }
  184. static inline bool
  185. IsLeapYear(jsint year)
  186. {
  187. return year % 4 == 0 && (year % 100 || (year % 400 == 0));
  188. }
  189. static inline jsint
  190. DaysInYear(jsint year)
  191. {
  192. return IsLeapYear(year) ? 366 : 365;
  193. }
  194. static inline jsint
  195. DaysInFebruary(jsint year)
  196. {
  197. return IsLeapYear(year) ? 29 : 28;
  198. }
  199. /* math here has to be f.p, because we need
  200. * floor((1968 - 1969) / 4) == -1
  201. */
  202. #define DayFromYear(y) (365 * ((y)-1970) + floor(((y)-1969)/4.0) \
  203. - floor(((y)-1901)/100.0) + floor(((y)-1601)/400.0))
  204. #define TimeFromYear(y) (DayFromYear(y) * msPerDay)
  205. static jsint
  206. YearFromTime(jsdouble t)
  207. {
  208. jsint y = (jsint) floor(t /(msPerDay*365.2425)) + 1970;
  209. jsdouble t2 = (jsdouble) TimeFromYear(y);
  210. /*
  211. * Adjust the year if the approximation was wrong. Since the year was
  212. * computed using the average number of ms per year, it will usually
  213. * be wrong for dates within several hours of a year transition.
  214. */
  215. if (t2 > t) {
  216. y--;
  217. } else {
  218. if (t2 + msPerDay * DaysInYear(y) <= t)
  219. y++;
  220. }
  221. return y;
  222. }
  223. #define DayWithinYear(t, year) ((intN) (Day(t) - DayFromYear(year)))
  224. /*
  225. * The following array contains the day of year for the first day of
  226. * each month, where index 0 is January, and day 0 is January 1.
  227. */
  228. static jsdouble firstDayOfMonth[2][13] = {
  229. {0.0, 31.0, 59.0, 90.0, 120.0, 151.0, 181.0, 212.0, 243.0, 273.0, 304.0, 334.0, 365.0},
  230. {0.0, 31.0, 60.0, 91.0, 121.0, 152.0, 182.0, 213.0, 244.0, 274.0, 305.0, 335.0, 366.0}
  231. };
  232. #define DayFromMonth(m, leap) firstDayOfMonth[leap][(intN)m]
  233. static intN
  234. DaysInMonth(jsint year, jsint month)
  235. {
  236. JSBool leap = IsLeapYear(year);
  237. intN result = intN(DayFromMonth(month, leap) - DayFromMonth(month-1, leap));
  238. return result;
  239. }
  240. static intN
  241. MonthFromTime(jsdouble t)
  242. {
  243. intN d, step;
  244. jsint year = YearFromTime(t);
  245. d = DayWithinYear(t, year);
  246. if (d < (step = 31))
  247. return 0;
  248. if (d < (step += DaysInFebruary(year)))
  249. return 1;
  250. if (d < (step += 31))
  251. return 2;
  252. if (d < (step += 30))
  253. return 3;
  254. if (d < (step += 31))
  255. return 4;
  256. if (d < (step += 30))
  257. return 5;
  258. if (d < (step += 31))
  259. return 6;
  260. if (d < (step += 31))
  261. return 7;
  262. if (d < (step += 30))
  263. return 8;
  264. if (d < (step += 31))
  265. return 9;
  266. if (d < (step += 30))
  267. return 10;
  268. return 11;
  269. }
  270. static intN
  271. DateFromTime(jsdouble t)
  272. {
  273. intN d, step, next;
  274. jsint year = YearFromTime(t);
  275. d = DayWithinYear(t, year);
  276. if (d <= (next = 30))
  277. return d + 1;
  278. step = next;
  279. if (d <= (next += DaysInFebruary(year)))
  280. return d - step;
  281. step = next;
  282. if (d <= (next += 31))
  283. return d - step;
  284. step = next;
  285. if (d <= (next += 30))
  286. return d - step;
  287. step = next;
  288. if (d <= (next += 31))
  289. return d - step;
  290. step = next;
  291. if (d <= (next += 30))
  292. return d - step;
  293. step = next;
  294. if (d <= (next += 31))
  295. return d - step;
  296. step = next;
  297. if (d <= (next += 31))
  298. return d - step;
  299. step = next;
  300. if (d <= (next += 30))
  301. return d - step;
  302. step = next;
  303. if (d <= (next += 31))
  304. return d - step;
  305. step = next;
  306. if (d <= (next += 30))
  307. return d - step;
  308. step = next;
  309. return d - step;
  310. }
  311. static intN
  312. WeekDay(jsdouble t)
  313. {
  314. jsint result;
  315. result = (jsint) Day(t) + 4;
  316. result = result % 7;
  317. if (result < 0)
  318. result += 7;
  319. return (intN) result;
  320. }
  321. #define MakeTime(hour, min, sec, ms) \
  322. ((((hour) * MinutesPerHour + (min)) * SecondsPerMinute + (sec)) * msPerSecond + (ms))
  323. static jsdouble
  324. MakeDay(jsdouble year, jsdouble month, jsdouble date)
  325. {
  326. JSBool leap;
  327. jsdouble yearday;
  328. jsdouble monthday;
  329. year += floor(month / 12);
  330. month = fmod(month, 12.0);
  331. if (month < 0)
  332. month += 12;
  333. leap = IsLeapYear((jsint) year);
  334. yearday = floor(TimeFromYear(year) / msPerDay);
  335. monthday = DayFromMonth(month, leap);
  336. return yearday + monthday + date - 1;
  337. }
  338. #define MakeDate(day, time) ((day) * msPerDay + (time))
  339. /*
  340. * Years and leap years on which Jan 1 is a Sunday, Monday, etc.
  341. *
  342. * yearStartingWith[0][i] is an example non-leap year where
  343. * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
  344. *
  345. * yearStartingWith[1][i] is an example leap year where
  346. * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
  347. */
  348. static jsint yearStartingWith[2][7] = {
  349. {1978, 1973, 1974, 1975, 1981, 1971, 1977},
  350. {1984, 1996, 1980, 1992, 1976, 1988, 1972}
  351. };
  352. /*
  353. * Find a year for which any given date will fall on the same weekday.
  354. *
  355. * This function should be used with caution when used other than
  356. * for determining DST; it hasn't been proven not to produce an
  357. * incorrect year for times near year boundaries.
  358. */
  359. static jsint
  360. EquivalentYearForDST(jsint year)
  361. {
  362. jsint day;
  363. day = (jsint) DayFromYear(year) + 4;
  364. day = day % 7;
  365. if (day < 0)
  366. day += 7;
  367. return yearStartingWith[IsLeapYear(year)][day];
  368. }
  369. /* LocalTZA gets set by js_InitDateClass() */
  370. static jsdouble LocalTZA;
  371. static jsdouble
  372. DaylightSavingTA(jsdouble t, JSContext *cx)
  373. {
  374. /* abort if NaN */
  375. if (JSDOUBLE_IS_NaN(t))
  376. return t;
  377. /*
  378. * If earlier than 1970 or after 2038, potentially beyond the ken of
  379. * many OSes, map it to an equivalent year before asking.
  380. */
  381. if (t < 0.0 || t > 2145916800000.0) {
  382. jsint year = EquivalentYearForDST(YearFromTime(t));
  383. jsdouble day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
  384. t = MakeDate(day, TimeWithinDay(t));
  385. }
  386. int64_t timeMilliseconds = static_cast<int64_t>(t);
  387. int64_t offsetMilliseconds = cx->dstOffsetCache.getDSTOffsetMilliseconds(timeMilliseconds, cx);
  388. return static_cast<jsdouble>(offsetMilliseconds);
  389. }
  390. static jsdouble
  391. AdjustTime(jsdouble date, JSContext *cx)
  392. {
  393. jsdouble t = DaylightSavingTA(date, cx) + LocalTZA;
  394. t = (LocalTZA >= 0) ? fmod(t, msPerDay) : -fmod(msPerDay - t, msPerDay);
  395. return t;
  396. }
  397. static jsdouble
  398. LocalTime(jsdouble t, JSContext *cx)
  399. {
  400. return t + AdjustTime(t, cx);
  401. }
  402. static jsdouble
  403. UTC(jsdouble t, JSContext *cx)
  404. {
  405. return t - AdjustTime(t - LocalTZA, cx);
  406. }
  407. static intN
  408. HourFromTime(jsdouble t)
  409. {
  410. intN result = (intN) fmod(floor(t/msPerHour), HoursPerDay);
  411. if (result < 0)
  412. result += (intN)HoursPerDay;
  413. return result;
  414. }
  415. static intN
  416. MinFromTime(jsdouble t)
  417. {
  418. intN result = (intN) fmod(floor(t / msPerMinute), MinutesPerHour);
  419. if (result < 0)
  420. result += (intN)MinutesPerHour;
  421. return result;
  422. }
  423. static intN
  424. SecFromTime(jsdouble t)
  425. {
  426. intN result = (intN) fmod(floor(t / msPerSecond), SecondsPerMinute);
  427. if (result < 0)
  428. result += (intN)SecondsPerMinute;
  429. return result;
  430. }
  431. static intN
  432. msFromTime(jsdouble t)
  433. {
  434. intN result = (intN) fmod(t, msPerSecond);
  435. if (result < 0)
  436. result += (intN)msPerSecond;
  437. return result;
  438. }
  439. /**
  440. * end of ECMA 'support' functions
  441. */
  442. static JSBool
  443. date_convert(JSContext *cx, JSObject *obj, JSType hint, Value *vp)
  444. {
  445. JS_ASSERT(hint == JSTYPE_NUMBER || hint == JSTYPE_STRING || hint == JSTYPE_VOID);
  446. JS_ASSERT(obj->isDate());
  447. return DefaultValue(cx, obj, (hint == JSTYPE_VOID) ? JSTYPE_STRING : hint, vp);
  448. }
  449. /*
  450. * Other Support routines and definitions
  451. */
  452. Class js::DateClass = {
  453. js_Date_str,
  454. JSCLASS_HAS_RESERVED_SLOTS(JSObject::DATE_CLASS_RESERVED_SLOTS) |
  455. JSCLASS_HAS_CACHED_PROTO(JSProto_Date),
  456. JS_PropertyStub, /* addProperty */
  457. JS_PropertyStub, /* delProperty */
  458. JS_PropertyStub, /* getProperty */
  459. JS_StrictPropertyStub, /* setProperty */
  460. JS_EnumerateStub,
  461. JS_ResolveStub,
  462. date_convert
  463. };
  464. /* for use by date_parse */
  465. static const char* wtb[] = {
  466. "am", "pm",
  467. "monday", "tuesday", "wednesday", "thursday", "friday",
  468. "saturday", "sunday",
  469. "january", "february", "march", "april", "may", "june",
  470. "july", "august", "september", "october", "november", "december",
  471. "gmt", "ut", "utc",
  472. "est", "edt",
  473. "cst", "cdt",
  474. "mst", "mdt",
  475. "pst", "pdt"
  476. /* time zone table needs to be expanded */
  477. };
  478. static int ttb[] = {
  479. -1, -2, 0, 0, 0, 0, 0, 0, 0, /* AM/PM */
  480. 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
  481. 10000 + 0, 10000 + 0, 10000 + 0, /* GMT/UT/UTC */
  482. 10000 + 5 * 60, 10000 + 4 * 60, /* EST/EDT */
  483. 10000 + 6 * 60, 10000 + 5 * 60, /* CST/CDT */
  484. 10000 + 7 * 60, 10000 + 6 * 60, /* MST/MDT */
  485. 10000 + 8 * 60, 10000 + 7 * 60 /* PST/PDT */
  486. };
  487. /* helper for date_parse */
  488. static JSBool
  489. date_regionMatches(const char* s1, int s1off, const jschar* s2, int s2off,
  490. int count, int ignoreCase)
  491. {
  492. JSBool result = JS_FALSE;
  493. /* return true if matches, otherwise, false */
  494. while (count > 0 && s1[s1off] && s2[s2off]) {
  495. if (ignoreCase) {
  496. if (unicode::ToLowerCase(s1[s1off]) != unicode::ToLowerCase(s2[s2off]))
  497. break;
  498. } else {
  499. if ((jschar)s1[s1off] != s2[s2off]) {
  500. break;
  501. }
  502. }
  503. s1off++;
  504. s2off++;
  505. count--;
  506. }
  507. if (count == 0) {
  508. result = JS_TRUE;
  509. }
  510. return result;
  511. }
  512. /* find UTC time from given date... no 1900 correction! */
  513. static jsdouble
  514. date_msecFromDate(jsdouble year, jsdouble mon, jsdouble mday, jsdouble hour,
  515. jsdouble min, jsdouble sec, jsdouble msec)
  516. {
  517. jsdouble day;
  518. jsdouble msec_time;
  519. jsdouble result;
  520. day = MakeDay(year, mon, mday);
  521. msec_time = MakeTime(hour, min, sec, msec);
  522. result = MakeDate(day, msec_time);
  523. return result;
  524. }
  525. /* compute the time in msec (unclipped) from the given args */
  526. #define MAXARGS 7
  527. static JSBool
  528. date_msecFromArgs(JSContext *cx, CallArgs args, double *rval)
  529. {
  530. uintN loop;
  531. jsdouble array[MAXARGS];
  532. jsdouble msec_time;
  533. for (loop = 0; loop < MAXARGS; loop++) {
  534. if (loop < args.length()) {
  535. jsdouble d;
  536. if (!ToNumber(cx, args[loop], &d))
  537. return JS_FALSE;
  538. /* return NaN if any arg is not finite */
  539. if (!JSDOUBLE_IS_FINITE(d)) {
  540. *rval = js_NaN;
  541. return JS_TRUE;
  542. }
  543. array[loop] = js_DoubleToInteger(d);
  544. } else {
  545. if (loop == 2) {
  546. array[loop] = 1; /* Default the date argument to 1. */
  547. } else {
  548. array[loop] = 0;
  549. }
  550. }
  551. }
  552. /* adjust 2-digit years into the 20th century */
  553. if (array[0] >= 0 && array[0] <= 99)
  554. array[0] += 1900;
  555. msec_time = date_msecFromDate(array[0], array[1], array[2],
  556. array[3], array[4], array[5], array[6]);
  557. *rval = msec_time;
  558. return JS_TRUE;
  559. }
  560. /*
  561. * See ECMA 15.9.4.[3-10];
  562. */
  563. static JSBool
  564. date_UTC(JSContext *cx, uintN argc, Value *vp)
  565. {
  566. CallArgs args = CallArgsFromVp(argc, vp);
  567. jsdouble msec_time;
  568. if (!date_msecFromArgs(cx, args, &msec_time))
  569. return JS_FALSE;
  570. msec_time = TIMECLIP(msec_time);
  571. args.rval().setNumber(msec_time);
  572. return JS_TRUE;
  573. }
  574. /*
  575. * Read and convert decimal digits from s[*i] into *result
  576. * while *i < limit.
  577. *
  578. * Succeed if any digits are converted. Advance *i only
  579. * as digits are consumed.
  580. */
  581. static JSBool
  582. digits(size_t *result, const jschar *s, size_t *i, size_t limit)
  583. {
  584. size_t init = *i;
  585. *result = 0;
  586. while (*i < limit &&
  587. ('0' <= s[*i] && s[*i] <= '9')) {
  588. *result *= 10;
  589. *result += (s[*i] - '0');
  590. ++(*i);
  591. }
  592. return (*i != init);
  593. }
  594. /*
  595. * Read and convert decimal digits to the right of a decimal point,
  596. * representing a fractional integer, from s[*i] into *result
  597. * while *i < limit.
  598. *
  599. * Succeed if any digits are converted. Advance *i only
  600. * as digits are consumed.
  601. */
  602. static JSBool
  603. fractional(jsdouble *result, const jschar *s, size_t *i, size_t limit)
  604. {
  605. jsdouble factor = 0.1;
  606. size_t init = *i;
  607. *result = 0.0;
  608. while (*i < limit &&
  609. ('0' <= s[*i] && s[*i] <= '9')) {
  610. *result += (s[*i] - '0') * factor;
  611. factor *= 0.1;
  612. ++(*i);
  613. }
  614. return (*i != init);
  615. }
  616. /*
  617. * Read and convert exactly n decimal digits from s[*i]
  618. * to s[min(*i+n,limit)] into *result.
  619. *
  620. * Succeed if exactly n digits are converted. Advance *i only
  621. * on success.
  622. */
  623. static JSBool
  624. ndigits(size_t n, size_t *result, const jschar *s, size_t* i, size_t limit)
  625. {
  626. size_t init = *i;
  627. if (digits(result, s, i, JS_MIN(limit, init+n)))
  628. return ((*i - init) == n);
  629. *i = init;
  630. return JS_FALSE;
  631. }
  632. /*
  633. * Parse a string in one of the date-time formats given by the W3C
  634. * "NOTE-datetime" specification. These formats make up a restricted
  635. * profile of the ISO 8601 format. Quoted here:
  636. *
  637. * The formats are as follows. Exactly the components shown here
  638. * must be present, with exactly this punctuation. Note that the "T"
  639. * appears literally in the string, to indicate the beginning of the
  640. * time element, as specified in ISO 8601.
  641. *
  642. * Any combination of the date formats with the time formats is
  643. * allowed, and also either the date or the time can be missing.
  644. *
  645. * The specification is silent on the meaning when fields are
  646. * ommitted so the interpretations are a guess, but hopefully a
  647. * reasonable one. We default the month to January, the day to the
  648. * 1st, and hours minutes and seconds all to 0. If the date is
  649. * missing entirely then we assume 1970-01-01 so that the time can
  650. * be aded to a date later. If the time is missing then we assume
  651. * 00:00 UTC. If the time is present but the time zone field is
  652. * missing then we use local time.
  653. *
  654. * Date part:
  655. *
  656. * Year:
  657. * YYYY (eg 1997)
  658. *
  659. * Year and month:
  660. * YYYY-MM (eg 1997-07)
  661. *
  662. * Complete date:
  663. * YYYY-MM-DD (eg 1997-07-16)
  664. *
  665. * Time part:
  666. *
  667. * Hours and minutes:
  668. * Thh:mmTZD (eg T19:20+01:00)
  669. *
  670. * Hours, minutes and seconds:
  671. * Thh:mm:ssTZD (eg T19:20:30+01:00)
  672. *
  673. * Hours, minutes, seconds and a decimal fraction of a second:
  674. * Thh:mm:ss.sTZD (eg T19:20:30.45+01:00)
  675. *
  676. * where:
  677. *
  678. * YYYY = four-digit year or six digit year as +YYYYYY or -YYYYYY
  679. * MM = two-digit month (01=January, etc.)
  680. * DD = two-digit day of month (01 through 31)
  681. * hh = two digits of hour (00 through 23) (am/pm NOT allowed)
  682. * mm = two digits of minute (00 through 59)
  683. * ss = two digits of second (00 through 59)
  684. * s = one or more digits representing a decimal fraction of a second
  685. * TZD = time zone designator (Z or +hh:mm or -hh:mm or missing for local)
  686. */
  687. static JSBool
  688. date_parseISOString(JSLinearString *str, jsdouble *result, JSContext *cx)
  689. {
  690. jsdouble msec;
  691. const jschar *s;
  692. size_t limit;
  693. size_t i = 0;
  694. int tzMul = 1;
  695. int dateMul = 1;
  696. size_t year = 1970;
  697. size_t month = 1;
  698. size_t day = 1;
  699. size_t hour = 0;
  700. size_t min = 0;
  701. size_t sec = 0;
  702. jsdouble frac = 0;
  703. bool isLocalTime = JS_FALSE;
  704. size_t tzHour = 0;
  705. size_t tzMin = 0;
  706. #define PEEK(ch) (i < limit && s[i] == ch)
  707. #define NEED(ch) \
  708. JS_BEGIN_MACRO \
  709. if (i >= limit || s[i] != ch) { goto syntax; } else { ++i; } \
  710. JS_END_MACRO
  711. #define DONE_DATE_UNLESS(ch) \
  712. JS_BEGIN_MACRO \
  713. if (i >= limit || s[i] != ch) { goto done_date; } else { ++i; } \
  714. JS_END_MACRO
  715. #define DONE_UNLESS(ch) \
  716. JS_BEGIN_MACRO \
  717. if (i >= limit || s[i] != ch) { goto done; } else { ++i; } \
  718. JS_END_MACRO
  719. #define NEED_NDIGITS(n, field) \
  720. JS_BEGIN_MACRO \
  721. if (!ndigits(n, &field, s, &i, limit)) { goto syntax; } \
  722. JS_END_MACRO
  723. s = str->chars();
  724. limit = str->length();
  725. if (PEEK('+') || PEEK('-')) {
  726. if (PEEK('-'))
  727. dateMul = -1;
  728. ++i;
  729. NEED_NDIGITS(6, year);
  730. } else if (!PEEK('T')) {
  731. NEED_NDIGITS(4, year);
  732. }
  733. DONE_DATE_UNLESS('-');
  734. NEED_NDIGITS(2, month);
  735. DONE_DATE_UNLESS('-');
  736. NEED_NDIGITS(2, day);
  737. done_date:
  738. DONE_UNLESS('T');
  739. NEED_NDIGITS(2, hour);
  740. NEED(':');
  741. NEED_NDIGITS(2, min);
  742. if (PEEK(':')) {
  743. ++i;
  744. NEED_NDIGITS(2, sec);
  745. if (PEEK('.')) {
  746. ++i;
  747. if (!fractional(&frac, s, &i, limit))
  748. goto syntax;
  749. }
  750. }
  751. if (PEEK('Z')) {
  752. ++i;
  753. } else if (PEEK('+') || PEEK('-')) {
  754. if (PEEK('-'))
  755. tzMul = -1;
  756. ++i;
  757. NEED_NDIGITS(2, tzHour);
  758. /*
  759. * Non-standard extension to the ISO date format (permitted by ES5):
  760. * allow "-0700" as a time zone offset, not just "-07:00".
  761. */
  762. if (PEEK(':'))
  763. ++i;
  764. NEED_NDIGITS(2, tzMin);
  765. } else {
  766. isLocalTime = JS_TRUE;
  767. }
  768. done:
  769. if (year > 275943 // ceil(1e8/365) + 1970
  770. || (month == 0 || month > 12)
  771. || (day == 0 || day > size_t(DaysInMonth(year,month)))
  772. || hour > 24
  773. || ((hour == 24) && (min > 0 || sec > 0))
  774. || min > 59
  775. || sec > 59
  776. || tzHour > 23
  777. || tzMin > 59)
  778. goto syntax;
  779. if (i != limit)
  780. goto syntax;
  781. month -= 1; /* convert month to 0-based */
  782. msec = date_msecFromDate(dateMul * (jsdouble)year, month, day,
  783. hour, min, sec,
  784. frac * 1000.0);;
  785. if (isLocalTime) {
  786. msec = UTC(msec, cx);
  787. } else {
  788. msec -= ((tzMul) * ((tzHour * msPerHour)
  789. + (tzMin * msPerMinute)));
  790. }
  791. if (msec < -8.64e15 || msec > 8.64e15)
  792. goto syntax;
  793. *result = msec;
  794. return JS_TRUE;
  795. syntax:
  796. /* syntax error */
  797. *result = 0;
  798. return JS_FALSE;
  799. #undef PEEK
  800. #undef NEED
  801. #undef DONE_UNLESS
  802. #undef NEED_NDIGITS
  803. }
  804. static JSBool
  805. date_parseString(JSLinearString *str, jsdouble *result, JSContext *cx)
  806. {
  807. jsdouble msec;
  808. const jschar *s;
  809. size_t limit;
  810. size_t i = 0;
  811. int year = -1;
  812. int mon = -1;
  813. int mday = -1;
  814. int hour = -1;
  815. int min = -1;
  816. int sec = -1;
  817. int c = -1;
  818. int n = -1;
  819. int tzoffset = -1;
  820. int prevc = 0;
  821. JSBool seenplusminus = JS_FALSE;
  822. int temp;
  823. JSBool seenmonthname = JS_FALSE;
  824. if (date_parseISOString(str, result, cx))
  825. return JS_TRUE;
  826. s = str->chars();
  827. limit = str->length();
  828. if (limit == 0)
  829. goto syntax;
  830. while (i < limit) {
  831. c = s[i];
  832. i++;
  833. if (c <= ' ' || c == ',' || c == '-') {
  834. if (c == '-' && '0' <= s[i] && s[i] <= '9') {
  835. prevc = c;
  836. }
  837. continue;
  838. }
  839. if (c == '(') { /* comments) */
  840. int depth = 1;
  841. while (i < limit) {
  842. c = s[i];
  843. i++;
  844. if (c == '(') depth++;
  845. else if (c == ')')
  846. if (--depth <= 0)
  847. break;
  848. }
  849. continue;
  850. }
  851. if ('0' <= c && c <= '9') {
  852. n = c - '0';
  853. while (i < limit && '0' <= (c = s[i]) && c <= '9') {
  854. n = n * 10 + c - '0';
  855. i++;
  856. }
  857. /* allow TZA before the year, so
  858. * 'Wed Nov 05 21:49:11 GMT-0800 1997'
  859. * works */
  860. /* uses of seenplusminus allow : in TZA, so Java
  861. * no-timezone style of GMT+4:30 works
  862. */
  863. if ((prevc == '+' || prevc == '-')/* && year>=0 */) {
  864. /* make ':' case below change tzoffset */
  865. seenplusminus = JS_TRUE;
  866. /* offset */
  867. if (n < 24)
  868. n = n * 60; /* EG. "GMT-3" */
  869. else
  870. n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */
  871. if (prevc == '+') /* plus means east of GMT */
  872. n = -n;
  873. if (tzoffset != 0 && tzoffset != -1)
  874. goto syntax;
  875. tzoffset = n;
  876. } else if (prevc == '/' && mon >= 0 && mday >= 0 && year < 0) {
  877. if (c <= ' ' || c == ',' || c == '/' || i >= limit)
  878. year = n;
  879. else
  880. goto syntax;
  881. } else if (c == ':') {
  882. if (hour < 0)
  883. hour = /*byte*/ n;
  884. else if (min < 0)
  885. min = /*byte*/ n;
  886. else
  887. goto syntax;
  888. } else if (c == '/') {
  889. /* until it is determined that mon is the actual
  890. month, keep it as 1-based rather than 0-based */
  891. if (mon < 0)
  892. mon = /*byte*/ n;
  893. else if (mday < 0)
  894. mday = /*byte*/ n;
  895. else
  896. goto syntax;
  897. } else if (i < limit && c != ',' && c > ' ' && c != '-' && c != '(') {
  898. goto syntax;
  899. } else if (seenplusminus && n < 60) { /* handle GMT-3:30 */
  900. if (tzoffset < 0)
  901. tzoffset -= n;
  902. else
  903. tzoffset += n;
  904. } else if (hour >= 0 && min < 0) {
  905. min = /*byte*/ n;
  906. } else if (prevc == ':' && min >= 0 && sec < 0) {
  907. sec = /*byte*/ n;
  908. } else if (mon < 0) {
  909. mon = /*byte*/n;
  910. } else if (mon >= 0 && mday < 0) {
  911. mday = /*byte*/ n;
  912. } else if (mon >= 0 && mday >= 0 && year < 0) {
  913. year = n;
  914. } else {
  915. goto syntax;
  916. }
  917. prevc = 0;
  918. } else if (c == '/' || c == ':' || c == '+' || c == '-') {
  919. prevc = c;
  920. } else {
  921. size_t st = i - 1;
  922. int k;
  923. while (i < limit) {
  924. c = s[i];
  925. if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')))
  926. break;
  927. i++;
  928. }
  929. if (i <= st + 1)
  930. goto syntax;
  931. for (k = ArrayLength(wtb); --k >= 0;)
  932. if (date_regionMatches(wtb[k], 0, s, st, i-st, 1)) {
  933. int action = ttb[k];
  934. if (action != 0) {
  935. if (action < 0) {
  936. /*
  937. * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as
  938. * 12:30, instead of blindly adding 12 if PM.
  939. */
  940. JS_ASSERT(action == -1 || action == -2);
  941. if (hour > 12 || hour < 0) {
  942. goto syntax;
  943. } else {
  944. if (action == -1 && hour == 12) { /* am */
  945. hour = 0;
  946. } else if (action == -2 && hour != 12) { /* pm */
  947. hour += 12;
  948. }
  949. }
  950. } else if (action <= 13) { /* month! */
  951. /* Adjust mon to be 1-based until the final values
  952. for mon, mday and year are adjusted below */
  953. if (seenmonthname) {
  954. goto syntax;
  955. }
  956. seenmonthname = JS_TRUE;
  957. temp = /*byte*/ (action - 2) + 1;
  958. if (mon < 0) {
  959. mon = temp;
  960. } else if (mday < 0) {
  961. mday = mon;
  962. mon = temp;
  963. } else if (year < 0) {
  964. year = mon;
  965. mon = temp;
  966. } else {
  967. goto syntax;
  968. }
  969. } else {
  970. tzoffset = action - 10000;
  971. }
  972. }
  973. break;
  974. }
  975. if (k < 0)
  976. goto syntax;
  977. prevc = 0;
  978. }
  979. }
  980. if (year < 0 || mon < 0 || mday < 0)
  981. goto syntax;
  982. /*
  983. Case 1. The input string contains an English month name.
  984. The form of the string can be month f l, or f month l, or
  985. f l month which each evaluate to the same date.
  986. If f and l are both greater than or equal to 70, or
  987. both less than 70, the date is invalid.
  988. The year is taken to be the greater of the values f, l.
  989. If the year is greater than or equal to 70 and less than 100,
  990. it is considered to be the number of years after 1900.
  991. Case 2. The input string is of the form "f/m/l" where f, m and l are
  992. integers, e.g. 7/16/45.
  993. Adjust the mon, mday and year values to achieve 100% MSIE
  994. compatibility.
  995. a. If 0 <= f < 70, f/m/l is interpreted as month/day/year.
  996. i. If year < 100, it is the number of years after 1900
  997. ii. If year >= 100, it is the number of years after 0.
  998. b. If 70 <= f < 100
  999. i. If m < 70, f/m/l is interpreted as
  1000. year/month/day where year is the number of years after
  1001. 1900.
  1002. ii. If m >= 70, the date is invalid.
  1003. c. If f >= 100
  1004. i. If m < 70, f/m/l is interpreted as
  1005. year/month/day where year is the number of years after 0.
  1006. ii. If m >= 70, the date is invalid.
  1007. */
  1008. if (seenmonthname) {
  1009. if ((mday >= 70 && year >= 70) || (mday < 70 && year < 70)) {
  1010. goto syntax;
  1011. }
  1012. if (mday > year) {
  1013. temp = year;
  1014. year = mday;
  1015. mday = temp;
  1016. }
  1017. if (year >= 70 && year < 100) {
  1018. year += 1900;
  1019. }
  1020. } else if (mon < 70) { /* (a) month/day/year */
  1021. if (year < 100) {
  1022. year += 1900;
  1023. }
  1024. } else if (mon < 100) { /* (b) year/month/day */
  1025. if (mday < 70) {
  1026. temp = year;
  1027. year = mon + 1900;
  1028. mon = mday;
  1029. mday = temp;
  1030. } else {
  1031. goto syntax;
  1032. }
  1033. } else { /* (c) year/month/day */
  1034. if (mday < 70) {
  1035. temp = year;
  1036. year = mon;
  1037. mon = mday;
  1038. mday = temp;
  1039. } else {
  1040. goto syntax;
  1041. }
  1042. }
  1043. mon -= 1; /* convert month to 0-based */
  1044. if (sec < 0)
  1045. sec = 0;
  1046. if (min < 0)
  1047. min = 0;
  1048. if (hour < 0)
  1049. hour = 0;
  1050. msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
  1051. if (tzoffset == -1) { /* no time zone specified, have to use local */
  1052. msec = UTC(msec, cx);
  1053. } else {
  1054. msec += tzoffset * msPerMinute;
  1055. }
  1056. *result = msec;
  1057. return JS_TRUE;
  1058. syntax:
  1059. /* syntax error */
  1060. *result = 0;
  1061. return JS_FALSE;
  1062. }
  1063. static JSBool
  1064. date_parse(JSContext *cx, uintN argc, Value *vp)
  1065. {
  1066. JSString *str;
  1067. jsdouble result;
  1068. if (argc == 0) {
  1069. vp->setDouble(js_NaN);
  1070. return true;
  1071. }
  1072. str = ToString(cx, vp[2]);
  1073. if (!str)
  1074. return JS_FALSE;
  1075. vp[2].setString(str);
  1076. JSLinearString *linearStr = str->ensureLinear(cx);
  1077. if (!linearStr)
  1078. return false;
  1079. if (!date_parseString(linearStr, &result, cx)) {
  1080. vp->setDouble(js_NaN);
  1081. return true;
  1082. }
  1083. result = TIMECLIP(result);
  1084. vp->setNumber(result);
  1085. return true;
  1086. }
  1087. static inline jsdouble
  1088. NowAsMillis()
  1089. {
  1090. return (jsdouble) (PRMJ_Now() / PRMJ_USEC_PER_MSEC);
  1091. }
  1092. static JSBool
  1093. date_now(JSContext *cx, uintN argc, Value *vp)
  1094. {
  1095. vp->setDouble(NowAsMillis());
  1096. return JS_TRUE;
  1097. }
  1098. /*
  1099. * Set UTC time to a given time and invalidate cached local time.
  1100. */
  1101. static JSBool
  1102. SetUTCTime(JSContext *cx, JSObject *obj, jsdouble t, Value *vp = NULL)
  1103. {
  1104. JS_ASSERT(obj->isDate());
  1105. for (size_t ind = JSObject::JSSLOT_DATE_COMPONENTS_START;
  1106. ind < JSObject::DATE_CLASS_RESERVED_SLOTS;
  1107. ind++) {
  1108. obj->setSlot(ind, UndefinedValue());
  1109. }
  1110. obj->setDateUTCTime(DoubleValue(t));
  1111. if (vp)
  1112. vp->setDouble(t);
  1113. return true;
  1114. }
  1115. static void
  1116. SetDateToNaN(JSContext *cx, JSObject *obj, Value *vp = NULL)
  1117. {
  1118. jsdouble NaN = cx->runtime->NaNValue.getDoubleRef();
  1119. SetUTCTime(cx, obj, NaN, vp);
  1120. }
  1121. /*
  1122. * Cache the local time, year, month, and so forth of the object.
  1123. * If UTC time is not finite (e.g., NaN), the local time
  1124. * slots will be set to the UTC time without conversion.
  1125. */
  1126. static bool
  1127. FillLocalTimes(JSContext *cx, JSObject *obj)
  1128. {
  1129. JS_ASSERT(obj->isDate());
  1130. jsdouble utcTime = obj->getDateUTCTime().toNumber();
  1131. if (!JSDOUBLE_IS_FINITE(utcTime)) {
  1132. for (size_t ind = JSObject::JSSLOT_DATE_COMPONENTS_START;
  1133. ind < JSObject::DATE_CLASS_RESERVED_SLOTS;
  1134. ind++) {
  1135. obj->setSlot(ind, DoubleValue(utcTime));
  1136. }
  1137. return true;
  1138. }
  1139. jsdouble localTime = LocalTime(utcTime, cx);
  1140. obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_TIME, DoubleValue(localTime));
  1141. jsint year = (jsint) floor(localTime /(msPerDay*365.2425)) + 1970;
  1142. jsdouble yearStartTime = (jsdouble) TimeFromYear(year);
  1143. /* Adjust the year in case the approximation was wrong, as in YearFromTime. */
  1144. jsint yearDays;
  1145. if (yearStartTime > localTime) {
  1146. year--;
  1147. yearStartTime -= (msPerDay * DaysInYear(year));
  1148. yearDays = DaysInYear(year);
  1149. } else {
  1150. yearDays = DaysInYear(year);
  1151. jsdouble nextStart = yearStartTime + (msPerDay * yearDays);
  1152. if (nextStart <= localTime) {
  1153. year++;
  1154. yearStartTime = nextStart;
  1155. yearDays = DaysInYear(year);
  1156. }
  1157. }
  1158. obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_YEAR, Int32Value(year));
  1159. uint64_t yearTime = uint64_t(localTime - yearStartTime);
  1160. jsint yearSeconds = uint32_t(yearTime / 1000);
  1161. jsint day = yearSeconds / jsint(SecondsPerDay);
  1162. jsint step = -1, next = 30;
  1163. jsint month;
  1164. do {
  1165. if (day <= next) {
  1166. month = 0;
  1167. break;
  1168. }
  1169. step = next;
  1170. next += ((yearDays == 366) ? 29 : 28);
  1171. if (day <= next) {
  1172. month = 1;
  1173. break;
  1174. }
  1175. step = next;
  1176. if (day <= (next += 31)) {
  1177. month = 2;
  1178. break;
  1179. }
  1180. step = next;
  1181. if (day <= (next += 30)) {
  1182. month = 3;
  1183. break;
  1184. }
  1185. step = next;
  1186. if (day <= (next += 31)) {
  1187. month = 4;
  1188. break;
  1189. }
  1190. step = next;
  1191. if (day <= (next += 30)) {
  1192. month = 5;
  1193. break;
  1194. }
  1195. step = next;
  1196. if (day <= (next += 31)) {
  1197. month = 6;
  1198. break;
  1199. }
  1200. step = next;
  1201. if (day <= (next += 31)) {
  1202. month = 7;
  1203. break;
  1204. }
  1205. step = next;
  1206. if (day <= (next += 30)) {
  1207. month = 8;
  1208. break;
  1209. }
  1210. step = next;
  1211. if (day <= (next += 31)) {
  1212. month = 9;
  1213. break;
  1214. }
  1215. step = next;
  1216. if (day <= (next += 30)) {
  1217. month = 10;
  1218. break;
  1219. }
  1220. step = next;
  1221. month = 11;
  1222. } while (0);
  1223. obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_MONTH, Int32Value(month));
  1224. obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_DATE, Int32Value(day - step));
  1225. jsint weekday = WeekDay(localTime);
  1226. obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_DAY, Int32Value(weekday));
  1227. jsint seconds = yearSeconds % 60;
  1228. obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_SECONDS, Int32Value(seconds));
  1229. jsint minutes = (yearSeconds / 60) % 60;
  1230. obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_MINUTES, Int32Value(minutes));
  1231. jsint hours = (yearSeconds / (60 * 60)) % 24;
  1232. obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_HOURS, Int32Value(hours));
  1233. return true;
  1234. }
  1235. /* Cache the local times in obj, if necessary. */
  1236. static inline bool
  1237. GetAndCacheLocalTime(JSContext *cx, JSObject *obj)
  1238. {
  1239. JS_ASSERT(obj->isDate());
  1240. /* If the local time is undefined, we need to fill in the cached values. */
  1241. if (obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_TIME).isUndefined()) {
  1242. if (!FillLocalTimes(cx, obj))
  1243. return false;
  1244. }
  1245. return true;
  1246. }
  1247. static inline bool
  1248. GetAndCacheLocalTime(JSContext *cx, JSObject *obj, double *time)
  1249. {
  1250. if (!obj || !GetAndCacheLocalTime(cx, obj))
  1251. return false;
  1252. *time = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_TIME).toDouble();
  1253. return true;
  1254. }
  1255. /*
  1256. * See ECMA 15.9.5.4 thru 15.9.5.23
  1257. */
  1258. static JSBool
  1259. date_getTime(JSContext *cx, uintN argc, Value *vp)
  1260. {
  1261. CallArgs args = CallArgsFromVp(argc, vp);
  1262. bool ok;
  1263. JSObject *obj = NonGenericMethodGuard(cx, args, date_getTime, &DateClass, &ok);
  1264. if (!obj)
  1265. return ok;
  1266. args.rval() = obj->getDateUTCTime();
  1267. return true;
  1268. }
  1269. static JSBool
  1270. date_getYear(JSContext *cx, uintN argc, Value *vp)
  1271. {
  1272. CallArgs args = CallArgsFromVp(argc, vp);
  1273. bool ok;
  1274. JSObject *obj = NonGenericMethodGuard(cx, args, date_getYear, &DateClass, &ok);
  1275. if (!obj)
  1276. return ok;
  1277. if (!GetAndCacheLocalTime(cx, obj))
  1278. return false;
  1279. Value yearVal = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_YEAR);
  1280. if (yearVal.isInt32()) {
  1281. /* Follow ECMA-262 to the letter, contrary to IE JScript. */
  1282. jsint year = yearVal.toInt32() - 1900;
  1283. args.rval().setInt32(year);
  1284. } else {
  1285. args.rval() = yearVal;
  1286. }
  1287. return true;
  1288. }
  1289. static JSBool
  1290. date_getFullYear(JSContext *cx, uintN argc, Value *vp)
  1291. {
  1292. CallArgs args = CallArgsFromVp(argc, vp);
  1293. bool ok;
  1294. JSObject *obj = NonGenericMethodGuard(cx, args, date_getFullYear, &DateClass, &ok);
  1295. if (!obj)
  1296. return ok;
  1297. if (!GetAndCacheLocalTime(cx, obj))
  1298. return false;
  1299. args.rval() = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_YEAR);
  1300. return true;
  1301. }
  1302. static JSBool
  1303. date_getUTCFullYear(JSContext *cx, uintN argc, Value *vp)
  1304. {
  1305. CallArgs args = CallArgsFromVp(argc, vp);
  1306. bool ok;
  1307. JSObject *obj = NonGenericMethodGuard(cx, args, date_getUTCFullYear, &DateClass, &ok);
  1308. if (!obj)
  1309. return ok;
  1310. double result = obj->getDateUTCTime().toNumber();
  1311. if (JSDOUBLE_IS_FINITE(result))
  1312. result = YearFromTime(result);
  1313. args.rval().setNumber(result);
  1314. return true;
  1315. }
  1316. static JSBool
  1317. date_getMonth(JSContext *cx, uintN argc, Value *vp)
  1318. {
  1319. CallArgs args = CallArgsFromVp(argc, vp);
  1320. bool ok;
  1321. JSObject *obj = NonGenericMethodGuard(cx, args, date_getMonth, &DateClass, &ok);
  1322. if (!obj)
  1323. return ok;
  1324. if (!GetAndCacheLocalTime(cx, obj))
  1325. return false;
  1326. args.rval() = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_MONTH);
  1327. return true;
  1328. }
  1329. static JSBool
  1330. date_getUTCMonth(JSContext *cx, uintN argc, Value *vp)
  1331. {
  1332. CallArgs args = CallArgsFromVp(argc, vp);
  1333. bool ok;
  1334. JSObject *obj = NonGenericMethodGuard(cx, args, date_getUTCMonth, &DateClass, &ok);
  1335. if (!obj)
  1336. return ok;
  1337. double result = obj->getDateUTCTime().toNumber();
  1338. if (JSDOUBLE_IS_FINITE(result))
  1339. result = MonthFromTime(result);
  1340. args.rval().setNumber(result);
  1341. return true;
  1342. }
  1343. static JSBool
  1344. date_getDate(JSContext *cx, uintN argc, Value *vp)
  1345. {
  1346. CallArgs args = CallArgsFromVp(argc, vp);
  1347. bool ok;
  1348. JSObject *obj = NonGenericMethodGuard(cx, args, date_getDate, &DateClass, &ok);
  1349. if (!obj)
  1350. return ok;
  1351. if (!GetAndCacheLocalTime(cx, obj))
  1352. return false;
  1353. args.rval() = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_DATE);
  1354. return true;
  1355. }
  1356. static JSBool
  1357. date_getUTCDate(JSContext *cx, uintN argc, Value *vp)
  1358. {
  1359. CallArgs args = CallArgsFromVp(argc, vp);
  1360. bool ok;
  1361. JSObject *obj = NonGenericMethodGuard(cx, args, date_getUTCDate, &DateClass, &ok);
  1362. if (!obj)
  1363. return ok;
  1364. double result = obj->getDateUTCTime().toNumber();
  1365. if (JSDOUBLE_IS_FINITE(result))
  1366. result = DateFromTime(result);
  1367. args.rval().setNumber(result);
  1368. return true;
  1369. }
  1370. static JSBool
  1371. date_getDay(JSContext *cx, uintN argc, Value *vp)
  1372. {
  1373. CallArgs args = CallArgsFromVp(argc, vp);
  1374. bool ok;
  1375. JSObject *obj = NonGenericMethodGuard(cx, args, date_getDay, &DateClass, &ok);
  1376. if (!obj)
  1377. return ok;
  1378. if (!GetAndCacheLocalTime(cx, obj))
  1379. return false;
  1380. args.rval() = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_DAY);
  1381. return true;
  1382. }
  1383. static JSBool
  1384. date_getUTCDay(JSContext *cx, uintN argc, Value *vp)
  1385. {
  1386. CallArgs args = CallArgsFromVp(argc, vp);
  1387. bool ok;
  1388. JSObject *obj = NonGenericMethodGuard(cx, args, date_getUTCDay, &DateClass, &ok);
  1389. if (!obj)
  1390. return ok;
  1391. jsdouble result = obj->getDateUTCTime().toNumber();
  1392. if (JSDOUBLE_IS_FINITE(result))
  1393. result = WeekDay(result);
  1394. args.rval().setNumber(result);
  1395. return true;
  1396. }
  1397. static JSBool
  1398. date_getHours(JSContext *cx, uintN argc, Value *vp)
  1399. {
  1400. CallArgs args = CallArgsFromVp(argc, vp);
  1401. bool ok;
  1402. JSObject *obj = NonGenericMethodGuard(cx, args, date_getHours, &DateClass, &ok);
  1403. if (!obj)
  1404. return ok;
  1405. if (!GetAndCacheLocalTime(cx, obj))
  1406. return false;
  1407. args.rval() = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_HOURS);
  1408. return true;
  1409. }
  1410. static JSBool
  1411. date_getUTCHours(JSContext *cx, uintN argc, Value *vp)
  1412. {
  1413. CallArgs args = CallArgsFromVp(argc, vp);
  1414. bool ok;
  1415. JSObject *obj = NonGenericMethodGuard(cx, args, date_getUTCHours, &DateClass, &ok);
  1416. if (!obj)
  1417. return ok;
  1418. double result = obj->getDateUTCTime().toNumber();
  1419. if (JSDOUBLE_IS_FINITE(result))
  1420. result = HourFromTime(result);
  1421. args.rval().setNumber(result);
  1422. return JS_TRUE;
  1423. }
  1424. static JSBool
  1425. date_getMinutes(JSContext *cx, uintN argc, Value *vp)
  1426. {
  1427. CallArgs args = CallArgsFromVp(argc, vp);
  1428. bool ok;
  1429. JSObject *obj = NonGenericMethodGuard(cx, args, date_getMinutes, &DateClass, &ok);
  1430. if (!obj)
  1431. return ok;
  1432. if (!GetAndCacheLocalTime(cx, obj))
  1433. return false;
  1434. args.rval() = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_MINUTES);
  1435. return true;
  1436. }
  1437. static JSBool
  1438. date_getUTCMinutes(JSContext *cx, uintN argc, Value *vp)
  1439. {
  1440. CallArgs args = CallArgsFromVp(argc, vp);
  1441. bool ok;
  1442. JSObject *obj = NonGenericMethodGuard(cx, args, date_getUTCMinutes, &DateClass, &ok);
  1443. if (!obj)
  1444. return ok;
  1445. double result = obj->getDateUTCTime().toNumber();
  1446. if (JSDOUBLE_IS_FINITE(result))
  1447. result = MinFromTime(result);
  1448. args.rval().setNumber(result);
  1449. return true;
  1450. }
  1451. /* Date.getSeconds is mapped to getUTCSeconds */
  1452. static JSBool
  1453. date_getUTCSeconds(JSContext *cx, uintN argc, Value *vp)
  1454. {
  1455. CallArgs args = CallArgsFromVp(argc, vp);
  1456. bool ok;
  1457. JSObject *obj = NonGenericMethodGuard(cx, args, date_getUTCSeconds, &DateClass, &ok);
  1458. if (!obj)
  1459. return ok;
  1460. if (!GetAndCacheLocalTime(cx, obj))
  1461. return false;
  1462. args.rval() = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_SECONDS);
  1463. return true;
  1464. }
  1465. /* Date.getMilliseconds is mapped to getUTCMilliseconds */
  1466. static JSBool
  1467. date_getUTCMilliseconds(JSContext *cx, uintN argc, Value *vp)
  1468. {
  1469. CallArgs args = CallArgsFromVp(argc, vp);
  1470. bool ok;
  1471. JSObject *obj = NonGenericMethodGuard(cx, args, date_getUTCMilliseconds, &DateClass, &ok);
  1472. if (!obj)
  1473. return ok;
  1474. double result = obj->getDateUTCTime().toNumber();
  1475. if (JSDOUBLE_IS_FINITE(result))
  1476. result = msFromTime(result);
  1477. args.rval().setNumber(result);
  1478. return true;
  1479. }
  1480. static JSBool
  1481. date_getTimezoneOffset(JSContext *cx, uintN argc, Value *vp)
  1482. {
  1483. CallArgs args = CallArgsFromVp(argc, vp);
  1484. bool ok;
  1485. JSObject *obj = NonGenericMethodGuard(cx, args, date_getTimezoneOffset, &DateClass, &ok);
  1486. if (!obj)
  1487. return ok;
  1488. double utctime = obj->getDateUTCTime().toNumber();
  1489. double localtime;
  1490. if (!GetAndCacheLocalTime(cx, obj, &localtime))
  1491. return false;
  1492. /*
  1493. * Return the time zone offset in minutes for the current locale that is
  1494. * appropriate for this time. This value would be a constant except for
  1495. * daylight savings time.
  1496. */
  1497. double result = (utctime - localtime) / msPerMinute;
  1498. args.rval().setNumber(result);
  1499. return true;
  1500. }
  1501. static JSBool
  1502. date_setTime(JSContext *cx, uintN argc, Value *vp)
  1503. {
  1504. CallArgs args = CallArgsFromVp(argc, vp);
  1505. bool ok;
  1506. JSObject *obj = NonGenericMethodGuard(cx, args, date_setTime, &DateClass, &ok);
  1507. if (!obj)
  1508. return ok;
  1509. if (args.length() == 0) {
  1510. SetDateToNaN(cx, obj, &args.rval());
  1511. return true;
  1512. }
  1513. jsdouble result;
  1514. if (!ToNumber(cx, args[0], &result))
  1515. return false;
  1516. return SetUTCTime(cx, obj, TIMECLIP(result), &args.rval());
  1517. }
  1518. static JSBool
  1519. date_makeTime(JSContext *cx, Native native, uintN maxargs, JSBool local, uintN argc, Value *vp)
  1520. {
  1521. CallArgs args = CallArgsFromVp(argc, vp);
  1522. bool ok;
  1523. JSObject *obj = NonGenericMethodGuard(cx, args, native, &DateClass, &ok);
  1524. if (!obj)
  1525. return ok;
  1526. double result = obj->getDateUTCTime().toNumber();
  1527. /* just return NaN if the date is already NaN */
  1528. if (!JSDOUBLE_IS_FINITE(result)) {
  1529. args.rval().setNumber(result);
  1530. return true;
  1531. }
  1532. /*
  1533. * Satisfy the ECMA rule that if a function is called with
  1534. * fewer arguments than the specified formal arguments, the
  1535. * remaining arguments are set to undefined. Seems like all
  1536. * the Date.setWhatever functions in ECMA are only varargs
  1537. * beyond the first argument; this should be set to undefined
  1538. * if it's not given. This means that "d = new Date();
  1539. * d.setMilliseconds()" returns NaN. Blech.
  1540. */
  1541. if (args.length() == 0) {
  1542. SetDateToNaN(cx, obj, &args.rval());
  1543. return true;
  1544. }
  1545. uintN numNums = Min(args.length(), maxargs);
  1546. JS_ASSERT(numNums <= 4);
  1547. double nums[4];
  1548. for (uintN i = 0; i < numNums; i++) {
  1549. if (!ToNumber(cx, args[i], &nums[i]))
  1550. return false;
  1551. if (!JSDOUBLE_IS_FINITE(nums[i])) {
  1552. SetDateToNaN(cx, obj, &args.rval());
  1553. return true;
  1554. }
  1555. nums[i] = js_DoubleToInteger(nums[i]);
  1556. }
  1557. double lorutime; /* Local or UTC version of *date */
  1558. if (local)
  1559. lorutime = LocalTime(result, cx);
  1560. else
  1561. lorutime = result;
  1562. double *argp = nums;
  1563. double *stop = argp + numNums;
  1564. double hour;
  1565. if (maxargs >= 4 && argp < stop)
  1566. hour = *argp++;
  1567. else
  1568. hour = HourFromTime(lorutime);
  1569. double min;
  1570. if (maxargs >= 3 && argp < stop)
  1571. min = *argp++;
  1572. else
  1573. min = MinFromTime(lorutime);
  1574. double sec;
  1575. if (maxargs >= 2 && argp < stop)
  1576. sec = *argp++;
  1577. else
  1578. sec = SecFromTime(lorutime);
  1579. double msec;
  1580. if (maxargs >= 1 && argp < stop)
  1581. msec = *argp;
  1582. else
  1583. msec = msFromTime(lorutime);
  1584. double msec_time = MakeTime(hour, min, sec, msec);
  1585. result = MakeDate(Day(lorutime), msec_time);
  1586. if (local)
  1587. result = UTC(result, cx);
  1588. return SetUTCTime(cx, obj, TIMECLIP(result), &args.rval());
  1589. }
  1590. static JSBool
  1591. date_setMilliseconds(JSContext *cx, uintN argc, Value *vp)
  1592. {
  1593. return date_makeTime(cx, date_setMilliseconds, 1, JS_TRUE, argc, vp);
  1594. }
  1595. static JSBool
  1596. date_setUTCMilliseconds(JSContext *cx, uintN argc, Value *vp)
  1597. {
  1598. return date_makeTime(cx, date_setUTCMilliseconds, 1, JS_FALSE, argc, vp);
  1599. }
  1600. static JSBool
  1601. date_setSeconds(JSContext *cx, uintN argc, Value *vp)
  1602. {
  1603. return date_makeTime(cx, date_setSeconds, 2, JS_TRUE, argc, vp);
  1604. }
  1605. static JSBool
  1606. date_setUTCSeconds(JSContext *cx, uintN argc, Value *vp)
  1607. {
  1608. return date_makeTime(cx, date_setUTCSeconds, 2, JS_FALSE, argc, vp);
  1609. }
  1610. static JSBool
  1611. date_setMinutes(JSContext *cx, uintN argc, Value *vp)
  1612. {
  1613. return date_makeTime(cx, date_setMinutes, 3, JS_TRUE, argc, vp);
  1614. }
  1615. static JSBool
  1616. date_setUTCMinutes(JSContext *cx, uintN argc, Value *vp)
  1617. {
  1618. return date_makeTime(cx, date_setUTCMinutes, 3, JS_FALSE, argc, vp);
  1619. }
  1620. static JSBool
  1621. date_setHours(JSContext *cx, uintN argc, Value *vp)
  1622. {
  1623. return date_makeTime(cx, date_setHours, 4, JS_TRUE, argc, vp);
  1624. }
  1625. static JSBool
  1626. date_setUTCHours(JSContext *cx, uintN argc, Value *vp)
  1627. {
  1628. return date_makeTime(cx, date_setUTCHours, 4, JS_FALSE, argc, vp);
  1629. }
  1630. static JSBool
  1631. date_makeDate(JSContext *cx, Native native, uintN maxargs, JSBool local, uintN argc, Value *vp)
  1632. {
  1633. CallArgs args = CallArgsFromVp(argc, vp);
  1634. bool ok;
  1635. JSObject *obj = NonGenericMethodGuard(cx, args, native, &DateClass, &ok);
  1636. if (!obj)
  1637. return ok;
  1638. double result = obj->getDateUTCTime().toNumber();
  1639. /* see complaint about ECMA in date_MakeTime */
  1640. if (args.length() == 0) {
  1641. SetDateToNaN(cx, obj, &args.rval());
  1642. return true;
  1643. }
  1644. uintN numNums = Min(args.length(), maxargs);
  1645. JS_ASSERT(1 <= numNums && numNums <= 3);
  1646. double nums[3];
  1647. for (uintN i = 0; i < numNums; i++) {
  1648. if (!ToNumber(cx, args[i], &nums[i]))
  1649. return JS_FALSE;
  1650. if (!JSDOUBLE_IS_FINITE(nums[i])) {
  1651. SetDateToNaN(cx, obj, &args.rval());
  1652. return true;
  1653. }
  1654. nums[i] = js_DoubleToInteger(nums[i]);
  1655. }
  1656. /*
  1657. * return NaN if date is NaN and we're not setting the year, If we are, use
  1658. * 0 as the time.
  1659. */
  1660. double lorutime; /* local or UTC version of *date */
  1661. if (!(JSDOUBLE_IS_FINITE(result))) {
  1662. if (maxargs < 3) {
  1663. args.rval().setDouble(result);
  1664. return true;
  1665. }
  1666. lorutime = +0.;
  1667. } else {
  1668. lorutime = local ? LocalTime(result, cx) : result;
  1669. }
  1670. double *argp = nums;
  1671. double *stop = argp + numNums;
  1672. double year;
  1673. if (maxargs >= 3 && argp < stop)
  1674. year = *argp++;
  1675. else
  1676. year = YearFromTime(lorutime);
  1677. double month;
  1678. if (maxargs >= 2 && argp < stop)
  1679. month = *argp++;
  1680. else
  1681. month = MonthFromTime(lorutime);
  1682. double day;
  1683. if (maxargs >= 1 && argp < stop)
  1684. day = *argp++;
  1685. else
  1686. day = DateFromTime(lorutime);
  1687. day = MakeDay(year, month, day); /* day within year */
  1688. result = MakeDate(day, TimeWithinDay(lorutime));
  1689. if (local)
  1690. result = UTC(result, cx);
  1691. return SetUTCTime(cx, obj, TIMECLIP(result), &args.rval());
  1692. }
  1693. static JSBool
  1694. date_setDate(JSContext *cx, uintN argc, Value *vp)
  1695. {
  1696. return date_makeDate(cx, date_setDate, 1, JS_TRUE, argc, vp);
  1697. }
  1698. static JSBool
  1699. date_setUTCDate(JSContext *cx, uintN argc, Value *vp)
  1700. {
  1701. return date_makeDate(cx, date_setUTCDate, 1, JS_FALSE, argc, vp);
  1702. }
  1703. static JSBool
  1704. date_setMonth(JSContext *cx, uintN argc, Value *vp)
  1705. {
  1706. return date_makeDate(cx, date_setMonth, 2, JS_TRUE, argc, vp);
  1707. }
  1708. static JSBool
  1709. date_setUTCMonth(JSContext *cx, uintN argc, Value *vp)
  1710. {
  1711. return date_makeDate(cx, date_setUTCMonth, 2, JS_FALSE, argc, vp);
  1712. }
  1713. static JSBool
  1714. date_setFullYear(JSContext *cx, uintN argc, Value *vp)
  1715. {
  1716. return date_makeDate(cx, date_setFullYear, 3, JS_TRUE, argc, vp);
  1717. }
  1718. static JSBool
  1719. date_setUTCFullYear(JSContext *cx, uintN argc, Value *vp)
  1720. {
  1721. return date_makeDate(cx, date_setUTCFullYear, 3, JS_FALSE, argc, vp);
  1722. }
  1723. static JSBool
  1724. date_setYear(JSContext *cx, uintN argc, Value *vp)
  1725. {
  1726. CallArgs args = CallArgsFromVp(argc, vp);
  1727. bool ok;
  1728. JSObject *obj = NonGenericMethodGuard(cx, args, date_setYear, &DateClass, &ok);
  1729. if (!obj)
  1730. return ok;
  1731. if (args.length() == 0) {
  1732. /* Call this only after verifying that obj.[[Class]] = "Date". */
  1733. SetDateToNaN(cx, obj, &args.rval());
  1734. return true;
  1735. }
  1736. double result = obj->getDateUTCTime().toNumber();
  1737. double year;
  1738. if (!ToNumber(cx, args[0], &year))
  1739. return false;
  1740. if (!JSDOUBLE_IS_FINITE(year)) {
  1741. SetDateToNaN(cx, obj, &args.rval());
  1742. return true;
  1743. }
  1744. year = js_DoubleToInteger(year);
  1745. if (year >= 0 && year <= 99)
  1746. year += 1900;
  1747. double t = JSDOUBLE_IS_FINITE(result) ? LocalTime(result, cx) : +0.0;
  1748. double day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
  1749. result = MakeDate(day, TimeWithinDay(t));
  1750. result = UTC(result, cx);
  1751. return SetUTCTime(cx, obj, TIMECLIP(result), &args.rval());
  1752. }
  1753. /* constants for toString, toUTCString */
  1754. static char js_NaN_date_str[] = "Invalid Date";
  1755. static const char* days[] =
  1756. {
  1757. "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
  1758. };
  1759. static const char* months[] =
  1760. {
  1761. "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
  1762. };
  1763. // Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
  1764. // requires a PRMJTime... which only has 16-bit years. Sub-ECMA.
  1765. static void
  1766. print_gmt_string(char* buf, size_t size, jsdouble utctime)
  1767. {
  1768. JS_snprintf(buf, size, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
  1769. days[WeekDay(utctime)],
  1770. DateFromTime(utctime),
  1771. months[MonthFromTime(utctime)],
  1772. YearFromTime(utctime),
  1773. HourFromTime(utctime),
  1774. MinFromTime(utctime),
  1775. SecFromTime(utctime));
  1776. }
  1777. static void
  1778. print_iso_string(char* buf, size_t size, jsdouble utctime)
  1779. {
  1780. JS_snprintf(buf, size, "%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.3dZ",
  1781. YearFromTime(utctime),
  1782. MonthFromTime(utctime) + 1,
  1783. DateFromTime(utctime),
  1784. HourFromTime(utctime),
  1785. MinFromTime(utctime),
  1786. SecFromTime(utctime),
  1787. msFromTime(utctime));
  1788. }
  1789. static JSBool
  1790. date_utc_format(JSContext *cx, Native native, CallArgs args,
  1791. void (*printFunc)(char*, size_t, jsdouble))
  1792. {
  1793. bool ok;
  1794. JSObject *obj = NonGenericMethodGuard(cx, args, native, &DateClass, &ok);
  1795. if (!obj)
  1796. return ok;
  1797. double utctime = obj->getDateUTCTime().toNumber();
  1798. char buf[100];
  1799. if (!JSDOUBLE_IS_FINITE(utctime)) {
  1800. if (printFunc == print_iso_string) {
  1801. JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INVALID_DATE);
  1802. return false;
  1803. }
  1804. JS_snprintf(buf, sizeof buf, js_NaN_date_str);
  1805. } else {
  1806. (*printFunc)(buf, sizeof buf, utctime);
  1807. }
  1808. JSString *str = JS_NewStringCopyZ(cx, buf);
  1809. if (!str)
  1810. return false;
  1811. args.rval().setString(str);
  1812. return true;
  1813. }
  1814. static JSBool
  1815. date_toGMTString(JSContext *cx, uintN argc, Value *vp)
  1816. {
  1817. return date_utc_format(cx, date_toGMTString, CallArgsFromVp(argc, vp), print_gmt_string);
  1818. }
  1819. static JSBool
  1820. date_toISOString(JSContext *cx, uintN argc, Value *vp)
  1821. {
  1822. return date_utc_format(cx, date_toISOString, CallArgsFromVp(argc, vp), print_iso_string);
  1823. }
  1824. /* ES5 15.9.5.44. */
  1825. static JSBool
  1826. date_toJSON(JSContext *cx, uintN argc, Value *vp)
  1827. {
  1828. /* Step 1. */
  1829. JSObject *obj = ToObject(cx, &vp[1]);
  1830. if (!obj)
  1831. return false;
  1832. /* Step 2. */
  1833. Value tv = ObjectValue(*obj);
  1834. if (!ToPrimitive(cx, JSTYPE_NUMBER, &tv))
  1835. return false;
  1836. /* Step 3. */
  1837. if (tv.isDouble() && !JSDOUBLE_IS_FINITE(tv.toDouble())) {
  1838. vp->setNull();
  1839. return true;
  1840. }
  1841. /* Step 4. */
  1842. Value &toISO = vp[0];
  1843. if (!obj->getProperty(cx, cx->runtime->atomState.toISOStringAtom, &toISO))
  1844. return false;
  1845. /* Step 5. */
  1846. if (!js_IsCallable(toISO)) {
  1847. JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL,
  1848. JSMSG_BAD_TOISOSTRING_PROP);
  1849. return false;
  1850. }
  1851. /* Step 6. */
  1852. InvokeArgsGuard args;
  1853. if (!cx->stack.pushInvokeArgs(cx, 0, &args))
  1854. return false;
  1855. args.calleev() = toISO;
  1856. args.thisv().setObject(*obj);
  1857. if (!Invoke(cx, args))
  1858. return false;
  1859. *vp = args.rval();
  1860. return true;
  1861. }
  1862. /* for Date.toLocaleString; interface to PRMJTime date struct.
  1863. */
  1864. static void
  1865. new_explode(jsdouble timeval, PRMJTime *split, JSContext *cx)
  1866. {
  1867. jsint year = YearFromTime(timeval);
  1868. split->tm_usec = int32_t(msFromTime(timeval)) * 1000;
  1869. split->tm_sec = int8_t(SecFromTime(timeval));
  1870. split->tm_min = int8_t(MinFromTime(timeval));
  1871. split->tm_hour = int8_t(HourFromTime(timeval));
  1872. split->tm_mday = int8_t(DateFromTime(timeval));
  1873. split->tm_mon = int8_t(MonthFromTime(timeval));
  1874. split->tm_wday = int8_t(WeekDay(timeval));
  1875. split->tm_year = year;
  1876. split->tm_yday = int16_t(DayWithinYear(timeval, year));
  1877. /* not sure how this affects things, but it doesn't seem
  1878. to matter. */
  1879. split->tm_isdst = (DaylightSavingTA(timeval, cx) != 0);
  1880. }
  1881. typedef enum formatspec {
  1882. FORMATSPEC_FULL, FORMATSPEC_DATE, FORMATSPEC_TIME
  1883. } formatspec;
  1884. /* helper function */
  1885. static JSBool
  1886. date_format(JSContext *cx, jsdouble date, formatspec format, CallReceiver call)
  1887. {
  1888. char buf[100];
  1889. JSString *str;
  1890. char tzbuf[100];
  1891. JSBool usetz;
  1892. size_t i, tzlen;
  1893. PRMJTime split;
  1894. if (!JSDOUBLE_IS_FINITE(date)) {
  1895. JS_snprintf(buf, sizeof buf, js_NaN_date_str);
  1896. } else {
  1897. jsdouble local = LocalTime(date, cx);
  1898. /* offset from GMT in minutes. The offset includes daylight savings,
  1899. if it applies. */
  1900. jsint minutes = (jsint) floor(AdjustTime(date, cx) / msPerMinute);
  1901. /* map 510 minutes to 0830 hours */
  1902. intN offset = (minutes / 60) * 100 + minutes % 60;
  1903. /* print as "Wed Nov 05 19:38:03 GMT-0800 (PST) 1997" The TZA is
  1904. * printed as 'GMT-0800' rather than as 'PST' to avoid
  1905. * operating-system dependence on strftime (which
  1906. * PRMJ_FormatTimeUSEnglish calls, for %Z only.) win32 prints
  1907. * PST as 'Pacific Standard Time.' This way we always know
  1908. * what we're getting, and can parse it if we produce it.
  1909. * The OS TZA string is included as a comment.
  1910. */
  1911. /* get a timezone string from the OS to include as a
  1912. comment. */
  1913. new_explode(date, &split, cx);
  1914. if (PRMJ_FormatTime(tzbuf, sizeof tzbuf, "(%Z)", &split) != 0) {
  1915. /* Decide whether to use the resulting timezone string.
  1916. *
  1917. * Reject it if it contains any non-ASCII, non-alphanumeric
  1918. * characters. It's then likely in some other character
  1919. * encoding, and we probably won't display it correctly.
  1920. */
  1921. usetz = JS_TRUE;
  1922. tzlen = strlen(tzbuf);
  1923. if (tzlen > 100) {
  1924. usetz = JS_FALSE;
  1925. } else {
  1926. for (i = 0; i < tzlen; i++) {
  1927. jschar c = tzbuf[i];
  1928. if (c > 127 ||
  1929. !(isalpha(c) || isdigit(c) ||
  1930. c == ' ' || c == '(' || c == ')')) {
  1931. usetz = JS_FALSE;
  1932. }
  1933. }
  1934. }
  1935. /* Also reject it if it's not parenthesized or if it's '()'. */
  1936. if (tzbuf[0] != '(' || tzbuf[1] == ')')
  1937. usetz = JS_FALSE;
  1938. } else
  1939. usetz = JS_FALSE;
  1940. switch (format) {
  1941. case FORMATSPEC_FULL:
  1942. /*
  1943. * Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
  1944. * requires a PRMJTime... which only has 16-bit years. Sub-ECMA.
  1945. */
  1946. /* Tue Oct 31 2000 09:41:40 GMT-0800 (PST) */
  1947. JS_snprintf(buf, sizeof buf,
  1948. "%s %s %.2d %.4d %.2d:%.2d:%.2d GMT%+.4d%s%s",
  1949. days[WeekDay(local)],
  1950. months[MonthFromTime(local)],
  1951. DateFromTime(local),
  1952. YearFromTime(local),
  1953. HourFromTime(local),
  1954. MinFromTime(local),
  1955. SecFromTime(local),
  1956. offset,
  1957. usetz ? " " : "",
  1958. usetz ? tzbuf : "");
  1959. break;
  1960. case FORMATSPEC_DATE:
  1961. /* Tue Oct 31 2000 */
  1962. JS_snprintf(buf, sizeof buf,
  1963. "%s %s %.2d %.4d",
  1964. days[WeekDay(local)],
  1965. months[MonthFromTime(local)],
  1966. DateFromTime(local),
  1967. YearFromTime(local));
  1968. break;
  1969. case FORMATSPEC_TIME:
  1970. /* 09:41:40 GMT-0800 (PST) */
  1971. JS_snprintf(buf, sizeof buf,
  1972. "%.2d:%.2d:%.2d GMT%+.4d%s%s",
  1973. HourFromTime(local),
  1974. MinFromTime(local),
  1975. SecFromTime(local),
  1976. offset,
  1977. usetz ? " " : "",
  1978. usetz ? tzbuf : "");
  1979. break;
  1980. }
  1981. }
  1982. str = JS_NewStringCopyZ(cx, buf);
  1983. if (!str)
  1984. return JS_FALSE;
  1985. call.rval().setString(str);
  1986. return JS_TRUE;
  1987. }
  1988. static bool
  1989. ToLocaleHelper(JSContext *cx, CallReceiver call, JSObject *obj, const char *format)
  1990. {
  1991. double utctime = obj->getDateUTCTime().toNumber();
  1992. char buf[100];
  1993. if (!JSDOUBLE_IS_FINITE(utctime)) {
  1994. JS_snprintf(buf, sizeof buf, js_NaN_date_str);
  1995. } else {
  1996. intN result_len;
  1997. jsdouble local = LocalTime(utctime, cx);
  1998. PRMJTime split;
  1999. new_explode(local, &split, cx);
  2000. /* let PRMJTime format it. */
  2001. result_len = PRMJ_FormatTime(buf, sizeof buf, format, &split);
  2002. /* If it failed, default to toString. */
  2003. if (result_len == 0)
  2004. return date_format(cx, utctime, FORMATSPEC_FULL, call);
  2005. /* Hacked check against undesired 2-digit year 00/00/00 form. */
  2006. if (strcmp(format, "%x") == 0 && result_len >= 6 &&
  2007. /* Format %x means use OS settings, which may have 2-digit yr, so
  2008. hack end of 3/11/22 or 11.03.22 or 11Mar22 to use 4-digit yr...*/
  2009. !isdigit(buf[result_len - 3]) &&
  2010. isdigit(buf[result_len - 2]) && isdigit(buf[result_len - 1]) &&
  2011. /* ...but not if starts with 4-digit year, like 2022/3/11. */
  2012. !(isdigit(buf[0]) && isdigit(buf[1]) &&
  2013. isdigit(buf[2]) && isdigit(buf[3]))) {
  2014. JS_snprintf(buf + (result_len - 2), (sizeof buf) - (result_len - 2),
  2015. "%d", js_DateGetYear(cx, obj));
  2016. }
  2017. }
  2018. if (cx->localeCallbacks && cx->localeCallbacks->localeToUnicode)
  2019. return cx->localeCallbacks->localeToUnicode(cx, buf, &call.rval());
  2020. JSString *str = JS_NewStringCopyZ(cx, buf);
  2021. if (!str)
  2022. return false;
  2023. call.rval().setString(str);
  2024. return true;
  2025. }
  2026. /*
  2027. * NB: Because of NonGenericMethodGuard, the calling native return immediately
  2028. * after calling date_toLocaleHelper, even if it returns 'true'.
  2029. */
  2030. static JSBool
  2031. date_toLocaleHelper(JSContext *cx, uintN argc, Value *vp, Native native, const char *format)
  2032. {
  2033. CallArgs args = CallArgsFromVp(argc, vp);
  2034. bool ok;
  2035. JSObject *obj = NonGenericMethodGuard(cx, args, native, &DateClass, &ok);
  2036. if (!obj)
  2037. return ok;
  2038. return ToLocaleHelper(cx, args, obj, format);
  2039. }
  2040. static JSBool
  2041. date_toLocaleStringHelper(JSContext *cx, Native native, uintN argc, Value *vp)
  2042. {
  2043. /*
  2044. * Use '%#c' for windows, because '%c' is backward-compatible and non-y2k
  2045. * with msvc; '%#c' requests that a full year be used in the result string.
  2046. */
  2047. return date_toLocaleHelper(cx, argc, vp, native,
  2048. #if defined(_WIN32) && !defined(__MWERKS__)
  2049. "%#c"
  2050. #else
  2051. "%c"
  2052. #endif
  2053. );
  2054. }
  2055. static JSBool
  2056. date_toLocaleString(JSContext *cx, uintN argc, Value *vp)
  2057. {
  2058. return date_toLocaleStringHelper(cx, date_toLocaleString, argc, vp);
  2059. }
  2060. static JSBool
  2061. date_toLocaleDateString(JSContext *cx, uintN argc, Value *vp)
  2062. {
  2063. /*
  2064. * Use '%#x' for windows, because '%x' is backward-compatible and non-y2k
  2065. * with msvc; '%#x' requests that a full year be used in the result string.
  2066. */
  2067. return date_toLocaleHelper(cx, argc, vp, date_toLocaleDateString,
  2068. #if defined(_WIN32) && !defined(__MWERKS__)
  2069. "%#x"
  2070. #else
  2071. "%x"
  2072. #endif
  2073. );
  2074. }
  2075. static JSBool
  2076. date_toLocaleTimeString(JSContext *cx, uintN argc, Value *vp)
  2077. {
  2078. return date_toLocaleHelper(cx, argc, vp, date_toLocaleTimeString, "%X");
  2079. }
  2080. static JSBool
  2081. date_toLocaleFormat(JSContext *cx, uintN argc, Value *vp)
  2082. {
  2083. if (argc == 0)
  2084. return date_toLocaleStringHelper(cx, date_toLocaleFormat, argc, vp);
  2085. CallArgs args = CallArgsFromVp(argc, vp);
  2086. bool ok;
  2087. JSObject *obj = NonGenericMethodGuard(cx, args, date_toLocaleFormat, &DateClass, &ok);
  2088. if (!obj)
  2089. return ok;
  2090. JSString *fmt = ToString(cx, args[0]);
  2091. if (!fmt)
  2092. return false;
  2093. args[0].setString(fmt);
  2094. JSAutoByteString fmtbytes(cx, fmt);
  2095. if (!fmtbytes)
  2096. return false;
  2097. return ToLocaleHelper(cx, args, obj, fmtbytes.ptr());
  2098. }
  2099. static JSBool
  2100. date_toTimeString(JSContext *cx, uintN argc, Value *vp)
  2101. {
  2102. CallArgs args = CallArgsFromVp(argc, vp);
  2103. bool ok;
  2104. JSObject *obj = NonGenericMethodGuard(cx, args, date_toTimeString, &DateClass, &ok);
  2105. if (!obj)
  2106. return ok;
  2107. return date_format(cx, obj->getDateUTCTime().toNumber(), FORMATSPEC_TIME, args);
  2108. }
  2109. static JSBool
  2110. date_toDateString(JSContext *cx, uintN argc, Value *vp)
  2111. {
  2112. CallArgs args = CallArgsFromVp(argc, vp);
  2113. bool ok;
  2114. JSObject *obj = NonGenericMethodGuard(cx, args, date_toDateString, &DateClass, &ok);
  2115. if (!obj)
  2116. return ok;
  2117. return date_format(cx, obj->getDateUTCTime().toNumber(), FORMATSPEC_DATE, args);
  2118. }
  2119. #if JS_HAS_TOSOURCE
  2120. static JSBool
  2121. date_toSource(JSContext *cx, uintN argc, Value *vp)
  2122. {
  2123. CallArgs args = CallArgsFromVp(argc, vp);
  2124. bool ok;
  2125. JSObject *obj = NonGenericMethodGuard(cx, args, date_toSource, &DateClass, &ok);
  2126. if (!obj)
  2127. return ok;
  2128. StringBuffer sb(cx);
  2129. if (!sb.append("(new Date(") || !NumberValueToStringBuffer(cx, obj->getDateUTCTime(), sb) ||
  2130. !sb.append("))"))
  2131. {
  2132. return false;
  2133. }
  2134. JSString *str = sb.finishString();
  2135. if (!str)
  2136. return false;
  2137. args.rval().setString(str);
  2138. return true;
  2139. }
  2140. #endif
  2141. static JSBool
  2142. date_toString(JSContext *cx, uintN argc, Value *vp)
  2143. {
  2144. CallArgs args = CallArgsFromVp(argc, vp);
  2145. bool ok;
  2146. JSObject *obj = NonGenericMethodGuard(cx, args, date_toString, &DateClass, &ok);
  2147. if (!obj)
  2148. return ok;
  2149. return date_format(cx, obj->getDateUTCTime().toNumber(), FORMATSPEC_FULL, args);
  2150. }
  2151. static JSBool
  2152. date_valueOf(JSContext *cx, uintN argc, Value *vp)
  2153. {
  2154. CallArgs args = CallArgsFromVp(argc, vp);
  2155. bool ok;
  2156. JSObject *obj = NonGenericMethodGuard(cx, args, date_valueOf, &DateClass, &ok);
  2157. if (!obj)
  2158. return ok;
  2159. /* If called directly with no arguments, convert to a time number. */
  2160. if (argc == 0) {
  2161. args.rval() = obj->getDateUTCTime();
  2162. return true;
  2163. }
  2164. /* Convert to number only if the hint was given, otherwise favor string. */
  2165. JSString *str = ToString(cx, args[0]);
  2166. if (!str)
  2167. return false;
  2168. JSLinearString *linear_str = str->ensureLinear(cx);
  2169. if (!linear_str)
  2170. return false;
  2171. JSAtom *number_str = cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER];
  2172. if (EqualStrings(linear_str, number_str)) {
  2173. args.rval() = obj->getDateUTCTime();
  2174. return true;
  2175. }
  2176. return date_format(cx, obj->getDateUTCTime().toNumber(), FORMATSPEC_FULL, args);
  2177. }
  2178. static JSFunctionSpec date_static_methods[] = {
  2179. JS_FN("UTC", date_UTC, MAXARGS,0),
  2180. JS_FN("parse", date_parse, 1,0),
  2181. JS_FN("now", date_now, 0,0),
  2182. JS_FS_END
  2183. };
  2184. static JSFunctionSpec date_methods[] = {
  2185. JS_FN("getTime", date_getTime, 0,0),
  2186. JS_FN("getTimezoneOffset", date_getTimezoneOffset, 0,0),
  2187. JS_FN("getYear", date_getYear, 0,0),
  2188. JS_FN("getFullYear", date_getFullYear, 0,0),
  2189. JS_FN("getUTCFullYear", date_getUTCFullYear, 0,0),
  2190. JS_FN("getMonth", date_getMonth, 0,0),
  2191. JS_FN("getUTCMonth", date_getUTCMonth, 0,0),
  2192. JS_FN("getDate", date_getDate, 0,0),
  2193. JS_FN("getUTCDate", date_getUTCDate, 0,0),
  2194. JS_FN("getDay", date_getDay, 0,0),
  2195. JS_FN("getUTCDay", date_getUTCDay, 0,0),
  2196. JS_FN("getHours", date_getHours, 0,0),
  2197. JS_FN("getUTCHours", date_getUTCHours, 0,0),
  2198. JS_FN("getMinutes", date_getMinutes, 0,0),
  2199. JS_FN("getUTCMinutes", date_getUTCMinutes, 0,0),
  2200. JS_FN("getSeconds", date_getUTCSeconds, 0,0),
  2201. JS_FN("getUTCSeconds", date_getUTCSeconds, 0,0),
  2202. JS_FN("getMilliseconds", date_getUTCMilliseconds, 0,0),
  2203. JS_FN("getUTCMilliseconds", date_getUTCMilliseconds, 0,0),
  2204. JS_FN("setTime", date_setTime, 1,0),
  2205. JS_FN("setYear", date_setYear, 1,0),
  2206. JS_FN("setFullYear", date_setFullYear, 3,0),
  2207. JS_FN("setUTCFullYear", date_setUTCFullYear, 3,0),
  2208. JS_FN("setMonth", date_setMonth, 2,0),
  2209. JS_FN("setUTCMonth", date_setUTCMonth, 2,0),
  2210. JS_FN("setDate", date_setDate, 1,0),
  2211. JS_FN("setUTCDate", date_setUTCDate, 1,0),
  2212. JS_FN("setHours", date_setHours, 4,0),
  2213. JS_FN("setUTCHours", date_setUTCHours, 4,0),
  2214. JS_FN("setMinutes", date_setMinutes, 3,0),
  2215. JS_FN("setUTCMinutes", date_setUTCMinutes, 3,0),
  2216. JS_FN("setSeconds", date_setSeconds, 2,0),
  2217. JS_FN("setUTCSeconds", date_setUTCSeconds, 2,0),
  2218. JS_FN("setMilliseconds", date_setMilliseconds, 1,0),
  2219. JS_FN("setUTCMilliseconds", date_setUTCMilliseconds, 1,0),
  2220. JS_FN("toUTCString", date_toGMTString, 0,0),
  2221. JS_FN(js_toLocaleString_str, date_toLocaleString, 0,0),
  2222. JS_FN("toLocaleDateString", date_toLocaleDateString, 0,0),
  2223. JS_FN("toLocaleTimeString", date_toLocaleTimeString, 0,0),
  2224. JS_FN("toLocaleFormat", date_toLocaleFormat, 0,0),
  2225. JS_FN("toDateString", date_toDateString, 0,0),
  2226. JS_FN("toTimeString", date_toTimeString, 0,0),
  2227. JS_FN("toISOString", date_toISOString, 0,0),
  2228. JS_FN(js_toJSON_str, date_toJSON, 1,0),
  2229. #if JS_HAS_TOSOURCE
  2230. JS_FN(js_toSource_str, date_toSource, 0,0),
  2231. #endif
  2232. JS_FN(js_toString_str, date_toString, 0,0),
  2233. JS_FN(js_valueOf_str, date_valueOf, 0,0),
  2234. JS_FS_END
  2235. };
  2236. JSBool
  2237. js_Date(JSContext *cx, uintN argc, Value *vp)
  2238. {
  2239. CallArgs args = CallArgsFromVp(argc, vp);
  2240. /* Date called as function. */
  2241. if (!IsConstructing(args))
  2242. return date_format(cx, NowAsMillis(), FORMATSPEC_FULL, args);
  2243. /* Date called as constructor. */
  2244. jsdouble d;
  2245. if (args.length() == 0) {
  2246. d = NowAsMillis();
  2247. } else if (args.length() == 1) {
  2248. if (!args[0].isString()) {
  2249. /* the argument is a millisecond number */
  2250. if (!ToNumber(cx, args[0], &d))
  2251. return false;
  2252. d = TIMECLIP(d);
  2253. } else {
  2254. /* the argument is a string; parse it. */
  2255. JSString *str = ToString(cx, args[0]);
  2256. if (!str)
  2257. return false;
  2258. args[0].setString(str);
  2259. JSLinearString *linearStr = str->ensureLinear(cx);
  2260. if (!linearStr)
  2261. return false;
  2262. if (!date_parseString(linearStr, &d, cx))
  2263. d = js_NaN;
  2264. else
  2265. d = TIMECLIP(d);
  2266. }
  2267. } else {
  2268. jsdouble msec_time;
  2269. if (!date_msecFromArgs(cx, args, &msec_time))
  2270. return false;
  2271. if (JSDOUBLE_IS_FINITE(msec_time)) {
  2272. msec_time = UTC(msec_time, cx);
  2273. msec_time = TIMECLIP(msec_time);
  2274. }
  2275. d = msec_time;
  2276. }
  2277. JSObject *obj = js_NewDateObjectMsec(cx, d);
  2278. if (!obj)
  2279. return false;
  2280. args.rval().setObject(*obj);
  2281. return true;
  2282. }
  2283. JSObject *
  2284. js_InitDateClass(JSContext *cx, JSObject *obj)
  2285. {
  2286. JS_ASSERT(obj->isNative());
  2287. /* Set the static LocalTZA. */
  2288. LocalTZA = -(PRMJ_LocalGMTDifference() * msPerSecond);
  2289. GlobalObject *global = &obj->asGlobal();
  2290. JSObject *dateProto = global->createBlankPrototype(cx, &DateClass);
  2291. if (!dateProto)
  2292. return NULL;
  2293. SetDateToNaN(cx, dateProto);
  2294. JSFunction *ctor = global->createConstructor(cx, js_Date, &DateClass,
  2295. CLASS_ATOM(cx, Date), MAXARGS);
  2296. if (!ctor)
  2297. return NULL;
  2298. if (!LinkConstructorAndPrototype(cx, ctor, dateProto))
  2299. return NULL;
  2300. if (!DefinePropertiesAndBrand(cx, ctor, NULL, date_static_methods))
  2301. return NULL;
  2302. /*
  2303. * Define all Date.prototype.* functions, then brand for trace-jitted code.
  2304. * Date.prototype.toGMTString has the same initial value as
  2305. * Date.prototype.toUTCString.
  2306. */
  2307. if (!JS_DefineFunctions(cx, dateProto, date_methods))
  2308. return NULL;
  2309. Value toUTCStringFun;
  2310. jsid toUTCStringId = ATOM_TO_JSID(cx->runtime->atomState.toUTCStringAtom);
  2311. jsid toGMTStringId = ATOM_TO_JSID(cx->runtime->atomState.toGMTStringAtom);
  2312. if (!js_GetProperty(cx, dateProto, toUTCStringId, &toUTCStringFun) ||
  2313. !js_DefineProperty(cx, dateProto, toGMTStringId, &toUTCStringFun,
  2314. JS_PropertyStub, JS_StrictPropertyStub, 0))
  2315. {
  2316. return NULL;
  2317. }
  2318. if (!DefineConstructorAndPrototype(cx, global, JSProto_Date, ctor, dateProto))
  2319. return NULL;
  2320. return dateProto;
  2321. }
  2322. JS_FRIEND_API(JSObject *)
  2323. js_NewDateObjectMsec(JSContext *cx, jsdouble msec_time)
  2324. {
  2325. JSObject *obj = NewBuiltinClassInstance(cx, &DateClass);
  2326. if (!obj)
  2327. return NULL;
  2328. if (!SetUTCTime(cx, obj, msec_time))
  2329. return NULL;
  2330. return obj;
  2331. }
  2332. JS_FRIEND_API(JSObject *)
  2333. js_NewDateObject(JSContext* cx, int year, int mon, int mday,
  2334. int hour, int min, int sec)
  2335. {
  2336. JSObject *obj;
  2337. jsdouble msec_time;
  2338. JS_ASSERT(mon < 12);
  2339. msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
  2340. obj = js_NewDateObjectMsec(cx, UTC(msec_time, cx));
  2341. return obj;
  2342. }
  2343. JS_FRIEND_API(JSBool)
  2344. js_DateIsValid(JSContext *cx, JSObject* obj)
  2345. {
  2346. return obj->isDate() && !JSDOUBLE_IS_NaN(obj->getDateUTCTime().toNumber());
  2347. }
  2348. JS_FRIEND_API(int)
  2349. js_DateGetYear(JSContext *cx, JSObject* obj)
  2350. {
  2351. jsdouble localtime;
  2352. /* Preserve legacy API behavior of returning 0 for invalid dates. */
  2353. if (!GetAndCacheLocalTime(cx, obj, &localtime) ||
  2354. JSDOUBLE_IS_NaN(localtime)) {
  2355. return 0;
  2356. }
  2357. return (int) YearFromTime(localtime);
  2358. }
  2359. JS_FRIEND_API(int)
  2360. js_DateGetMonth(JSContext *cx, JSObject* obj)
  2361. {
  2362. jsdouble localtime;
  2363. if (!GetAndCacheLocalTime(cx, obj, &localtime) ||
  2364. JSDOUBLE_IS_NaN(localtime)) {
  2365. return 0;
  2366. }
  2367. return (int) MonthFromTime(localtime);
  2368. }
  2369. JS_FRIEND_API(int)
  2370. js_DateGetDate(JSContext *cx, JSObject* obj)
  2371. {
  2372. jsdouble localtime;
  2373. if (!GetAndCacheLocalTime(cx, obj, &localtime) ||
  2374. JSDOUBLE_IS_NaN(localtime)) {
  2375. return 0;
  2376. }
  2377. return (int) DateFromTime(localtime);
  2378. }
  2379. JS_FRIEND_API(int)
  2380. js_DateGetHours(JSContext *cx, JSObject* obj)
  2381. {
  2382. jsdouble localtime;
  2383. if (!GetAndCacheLocalTime(cx, obj, &localtime) ||
  2384. JSDOUBLE_IS_NaN(localtime)) {
  2385. return 0;
  2386. }
  2387. return (int) HourFromTime(localtime);
  2388. }
  2389. JS_FRIEND_API(int)
  2390. js_DateGetMinutes(JSContext *cx, JSObject* obj)
  2391. {
  2392. jsdouble localtime;
  2393. if (!GetAndCacheLocalTime(cx, obj, &localtime) ||
  2394. JSDOUBLE_IS_NaN(localtime)) {
  2395. return 0;
  2396. }
  2397. return (int) MinFromTime(localtime);
  2398. }
  2399. JS_FRIEND_API(int)
  2400. js_DateGetSeconds(JSContext *cx, JSObject* obj)
  2401. {
  2402. if (!obj->isDate())
  2403. return 0;
  2404. double utctime = obj->getDateUTCTime().toNumber();
  2405. if (JSDOUBLE_IS_NaN(utctime))
  2406. return 0;
  2407. return (int) SecFromTime(utctime);
  2408. }
  2409. JS_FRIEND_API(jsdouble)
  2410. js_DateGetMsecSinceEpoch(JSContext *cx, JSObject *obj)
  2411. {
  2412. return obj->isDate() ? obj->getDateUTCTime().toNumber() : 0;
  2413. }
  2414. #ifdef JS_THREADSAFE
  2415. #include "prinrval.h"
  2416. JS_FRIEND_API(uint32_t)
  2417. js_IntervalNow()
  2418. {
  2419. return uint32_t(PR_IntervalToMilliseconds(PR_IntervalNow()));
  2420. }
  2421. #else /* !JS_THREADSAFE */
  2422. JS_FRIEND_API(uint32_t)
  2423. js_IntervalNow()
  2424. {
  2425. return uint32_t(PRMJ_Now() / PRMJ_USEC_PER_MSEC);
  2426. }
  2427. #endif