Bumping manifests a=b2g-bump
[gecko.git] / js / src / jsdate.cpp
blobb5e0a37ca1e99e6ccf0af21a4ac8f51ceda3c91c
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /*
8 * JS date methods.
10 * "For example, OS/360 devotes 26 bytes of the permanently
11 * resident date-turnover routine to the proper handling of
12 * December 31 on leap years (when it is Day 366). That
13 * might have been left to the operator."
15 * Frederick Brooks, 'The Second-System Effect'.
18 #include "jsdate.h"
20 #include "mozilla/ArrayUtils.h"
21 #include "mozilla/FloatingPoint.h"
23 #include <ctype.h>
24 #include <math.h>
25 #include <string.h>
27 #include "jsapi.h"
28 #include "jscntxt.h"
29 #include "jsnum.h"
30 #include "jsobj.h"
31 #include "jsprf.h"
32 #include "jsstr.h"
33 #include "jstypes.h"
34 #include "jsutil.h"
35 #include "jswrapper.h"
36 #include "prmjtime.h"
38 #include "js/Conversions.h"
39 #include "js/Date.h"
40 #include "vm/DateTime.h"
41 #include "vm/GlobalObject.h"
42 #include "vm/Interpreter.h"
43 #include "vm/String.h"
44 #include "vm/StringBuffer.h"
46 #include "jsobjinlines.h"
48 using namespace js;
49 using namespace js::types;
51 using mozilla::ArrayLength;
52 using mozilla::IsFinite;
53 using mozilla::IsNaN;
55 using JS::AutoCheckCannotGC;
56 using JS::GenericNaN;
57 using JS::ToInteger;
60 * The JS 'Date' object is patterned after the Java 'Date' object.
61 * Here is a script:
63 * today = new Date();
65 * print(today.toLocaleString());
67 * weekDay = today.getDay();
70 * These Java (and ECMA-262) methods are supported:
72 * UTC
73 * getDate (getUTCDate)
74 * getDay (getUTCDay)
75 * getHours (getUTCHours)
76 * getMinutes (getUTCMinutes)
77 * getMonth (getUTCMonth)
78 * getSeconds (getUTCSeconds)
79 * getMilliseconds (getUTCMilliseconds)
80 * getTime
81 * getTimezoneOffset
82 * getYear
83 * getFullYear (getUTCFullYear)
84 * parse
85 * setDate (setUTCDate)
86 * setHours (setUTCHours)
87 * setMinutes (setUTCMinutes)
88 * setMonth (setUTCMonth)
89 * setSeconds (setUTCSeconds)
90 * setMilliseconds (setUTCMilliseconds)
91 * setTime
92 * setYear (setFullYear, setUTCFullYear)
93 * toGMTString (toUTCString)
94 * toLocaleString
95 * toString
98 * These Java methods are not supported
100 * setDay
101 * before
102 * after
103 * equals
104 * hashCode
107 static inline double
108 Day(double t)
110 return floor(t / msPerDay);
113 static double
114 TimeWithinDay(double t)
116 double result = fmod(t, msPerDay);
117 if (result < 0)
118 result += msPerDay;
119 return result;
122 /* ES5 15.9.1.3. */
123 static inline bool
124 IsLeapYear(double year)
126 MOZ_ASSERT(ToInteger(year) == year);
127 return fmod(year, 4) == 0 && (fmod(year, 100) != 0 || fmod(year, 400) == 0);
130 static inline double
131 DaysInYear(double year)
133 if (!IsFinite(year))
134 return GenericNaN();
135 return IsLeapYear(year) ? 366 : 365;
138 static inline double
139 DayFromYear(double y)
141 return 365 * (y - 1970) +
142 floor((y - 1969) / 4.0) -
143 floor((y - 1901) / 100.0) +
144 floor((y - 1601) / 400.0);
147 static inline double
148 TimeFromYear(double y)
150 return DayFromYear(y) * msPerDay;
153 static double
154 YearFromTime(double t)
156 if (!IsFinite(t))
157 return GenericNaN();
159 MOZ_ASSERT(ToInteger(t) == t);
161 double y = floor(t / (msPerDay * 365.2425)) + 1970;
162 double t2 = TimeFromYear(y);
165 * Adjust the year if the approximation was wrong. Since the year was
166 * computed using the average number of ms per year, it will usually
167 * be wrong for dates within several hours of a year transition.
169 if (t2 > t) {
170 y--;
171 } else {
172 if (t2 + msPerDay * DaysInYear(y) <= t)
173 y++;
175 return y;
178 static inline int
179 DaysInFebruary(double year)
181 return IsLeapYear(year) ? 29 : 28;
184 /* ES5 15.9.1.4. */
185 static inline double
186 DayWithinYear(double t, double year)
188 MOZ_ASSERT_IF(IsFinite(t), YearFromTime(t) == year);
189 return Day(t) - DayFromYear(year);
192 static double
193 MonthFromTime(double t)
195 if (!IsFinite(t))
196 return GenericNaN();
198 double year = YearFromTime(t);
199 double d = DayWithinYear(t, year);
201 int step;
202 if (d < (step = 31))
203 return 0;
204 if (d < (step += DaysInFebruary(year)))
205 return 1;
206 if (d < (step += 31))
207 return 2;
208 if (d < (step += 30))
209 return 3;
210 if (d < (step += 31))
211 return 4;
212 if (d < (step += 30))
213 return 5;
214 if (d < (step += 31))
215 return 6;
216 if (d < (step += 31))
217 return 7;
218 if (d < (step += 30))
219 return 8;
220 if (d < (step += 31))
221 return 9;
222 if (d < (step += 30))
223 return 10;
224 return 11;
227 /* ES5 15.9.1.5. */
228 static double
229 DateFromTime(double t)
231 if (!IsFinite(t))
232 return GenericNaN();
234 double year = YearFromTime(t);
235 double d = DayWithinYear(t, year);
237 int next;
238 if (d <= (next = 30))
239 return d + 1;
240 int step = next;
241 if (d <= (next += DaysInFebruary(year)))
242 return d - step;
243 step = next;
244 if (d <= (next += 31))
245 return d - step;
246 step = next;
247 if (d <= (next += 30))
248 return d - step;
249 step = next;
250 if (d <= (next += 31))
251 return d - step;
252 step = next;
253 if (d <= (next += 30))
254 return d - step;
255 step = next;
256 if (d <= (next += 31))
257 return d - step;
258 step = next;
259 if (d <= (next += 31))
260 return d - step;
261 step = next;
262 if (d <= (next += 30))
263 return d - step;
264 step = next;
265 if (d <= (next += 31))
266 return d - step;
267 step = next;
268 if (d <= (next += 30))
269 return d - step;
270 step = next;
271 return d - step;
274 /* ES5 15.9.1.6. */
275 static int
276 WeekDay(double t)
279 * We can't assert TimeClip(t) == t because we call this function with
280 * local times, which can be offset outside TimeClip's permitted range.
282 MOZ_ASSERT(ToInteger(t) == t);
283 int result = (int(Day(t)) + 4) % 7;
284 if (result < 0)
285 result += 7;
286 return result;
289 static inline int
290 DayFromMonth(int month, bool isLeapYear)
293 * The following array contains the day of year for the first day of
294 * each month, where index 0 is January, and day 0 is January 1.
296 static const int firstDayOfMonth[2][13] = {
297 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
298 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}
301 MOZ_ASSERT(0 <= month && month <= 12);
302 return firstDayOfMonth[isLeapYear][month];
305 template<typename T>
306 static inline int
307 DayFromMonth(T month, bool isLeapYear) = delete;
309 /* ES5 15.9.1.12 (out of order to accommodate DaylightSavingTA). */
310 static double
311 MakeDay(double year, double month, double date)
313 /* Step 1. */
314 if (!IsFinite(year) || !IsFinite(month) || !IsFinite(date))
315 return GenericNaN();
317 /* Steps 2-4. */
318 double y = ToInteger(year);
319 double m = ToInteger(month);
320 double dt = ToInteger(date);
322 /* Step 5. */
323 double ym = y + floor(m / 12);
325 /* Step 6. */
326 int mn = int(fmod(m, 12.0));
327 if (mn < 0)
328 mn += 12;
330 /* Steps 7-8. */
331 bool leap = IsLeapYear(ym);
333 double yearday = floor(TimeFromYear(ym) / msPerDay);
334 double monthday = DayFromMonth(mn, leap);
336 return yearday + monthday + dt - 1;
339 /* ES5 15.9.1.13 (out of order to accommodate DaylightSavingTA). */
340 static inline double
341 MakeDate(double day, double time)
343 /* Step 1. */
344 if (!IsFinite(day) || !IsFinite(time))
345 return GenericNaN();
347 /* Step 2. */
348 return day * msPerDay + time;
351 JS_PUBLIC_API(double)
352 JS::MakeDate(double year, unsigned month, unsigned day)
354 return TimeClip(::MakeDate(MakeDay(year, month, day), 0));
357 JS_PUBLIC_API(double)
358 JS::YearFromTime(double time)
360 return ::YearFromTime(time);
363 JS_PUBLIC_API(double)
364 JS::MonthFromTime(double time)
366 return ::MonthFromTime(time);
369 JS_PUBLIC_API(double)
370 JS::DayFromTime(double time)
372 return DateFromTime(time);
376 * Find a year for which any given date will fall on the same weekday.
378 * This function should be used with caution when used other than
379 * for determining DST; it hasn't been proven not to produce an
380 * incorrect year for times near year boundaries.
382 static int
383 EquivalentYearForDST(int year)
386 * Years and leap years on which Jan 1 is a Sunday, Monday, etc.
388 * yearStartingWith[0][i] is an example non-leap year where
389 * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
391 * yearStartingWith[1][i] is an example leap year where
392 * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
394 static const int yearStartingWith[2][7] = {
395 {1978, 1973, 1974, 1975, 1981, 1971, 1977},
396 {1984, 1996, 1980, 1992, 1976, 1988, 1972}
399 int day = int(DayFromYear(year) + 4) % 7;
400 if (day < 0)
401 day += 7;
403 return yearStartingWith[IsLeapYear(year)][day];
406 /* ES5 15.9.1.8. */
407 static double
408 DaylightSavingTA(double t, DateTimeInfo* dtInfo)
410 if (!IsFinite(t))
411 return GenericNaN();
414 * If earlier than 1970 or after 2038, potentially beyond the ken of
415 * many OSes, map it to an equivalent year before asking.
417 if (t < 0.0 || t > 2145916800000.0) {
418 int year = EquivalentYearForDST(int(YearFromTime(t)));
419 double day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
420 t = MakeDate(day, TimeWithinDay(t));
423 int64_t utcMilliseconds = static_cast<int64_t>(t);
424 int64_t offsetMilliseconds = dtInfo->getDSTOffsetMilliseconds(utcMilliseconds);
425 return static_cast<double>(offsetMilliseconds);
428 static double
429 AdjustTime(double date, DateTimeInfo* dtInfo)
431 double t = DaylightSavingTA(date, dtInfo) + dtInfo->localTZA();
432 t = (dtInfo->localTZA() >= 0) ? fmod(t, msPerDay) : -fmod(msPerDay - t, msPerDay);
433 return t;
436 /* ES5 15.9.1.9. */
437 static double
438 LocalTime(double t, DateTimeInfo* dtInfo)
440 return t + AdjustTime(t, dtInfo);
443 static double
444 UTC(double t, DateTimeInfo* dtInfo)
446 return t - AdjustTime(t - dtInfo->localTZA(), dtInfo);
449 /* ES5 15.9.1.10. */
450 static double
451 HourFromTime(double t)
453 double result = fmod(floor(t/msPerHour), HoursPerDay);
454 if (result < 0)
455 result += HoursPerDay;
456 return result;
459 static double
460 MinFromTime(double t)
462 double result = fmod(floor(t / msPerMinute), MinutesPerHour);
463 if (result < 0)
464 result += MinutesPerHour;
465 return result;
468 static double
469 SecFromTime(double t)
471 double result = fmod(floor(t / msPerSecond), SecondsPerMinute);
472 if (result < 0)
473 result += SecondsPerMinute;
474 return result;
477 static double
478 msFromTime(double t)
480 double result = fmod(t, msPerSecond);
481 if (result < 0)
482 result += msPerSecond;
483 return result;
486 /* ES5 15.9.1.11. */
487 static double
488 MakeTime(double hour, double min, double sec, double ms)
490 /* Step 1. */
491 if (!IsFinite(hour) ||
492 !IsFinite(min) ||
493 !IsFinite(sec) ||
494 !IsFinite(ms))
496 return GenericNaN();
499 /* Step 2. */
500 double h = ToInteger(hour);
502 /* Step 3. */
503 double m = ToInteger(min);
505 /* Step 4. */
506 double s = ToInteger(sec);
508 /* Step 5. */
509 double milli = ToInteger(ms);
511 /* Steps 6-7. */
512 return h * msPerHour + m * msPerMinute + s * msPerSecond + milli;
516 * end of ECMA 'support' functions
519 static bool
520 date_convert(JSContext* cx, HandleObject obj, JSType hint, MutableHandleValue vp)
522 MOZ_ASSERT(hint == JSTYPE_NUMBER || hint == JSTYPE_STRING || hint == JSTYPE_VOID);
523 MOZ_ASSERT(obj->is<DateObject>());
525 return DefaultValue(cx, obj, (hint == JSTYPE_VOID) ? JSTYPE_STRING : hint, vp);
528 /* for use by date_parse */
530 static const char* const wtb[] = {
531 "am", "pm",
532 "monday", "tuesday", "wednesday", "thursday", "friday",
533 "saturday", "sunday",
534 "january", "february", "march", "april", "may", "june",
535 "july", "august", "september", "october", "november", "december",
536 "gmt", "ut", "utc",
537 "est", "edt",
538 "cst", "cdt",
539 "mst", "mdt",
540 "pst", "pdt"
541 /* time zone table needs to be expanded */
544 static const int ttb[] = {
545 -1, -2, 0, 0, 0, 0, 0, 0, 0, /* AM/PM */
546 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
547 10000 + 0, 10000 + 0, 10000 + 0, /* GMT/UT/UTC */
548 10000 + 5 * 60, 10000 + 4 * 60, /* EST/EDT */
549 10000 + 6 * 60, 10000 + 5 * 60, /* CST/CDT */
550 10000 + 7 * 60, 10000 + 6 * 60, /* MST/MDT */
551 10000 + 8 * 60, 10000 + 7 * 60 /* PST/PDT */
554 template <typename CharT>
555 static bool
556 RegionMatches(const char* s1, int s1off, const CharT* s2, int s2off, int count)
558 while (count > 0 && s1[s1off] && s2[s2off]) {
559 if (unicode::ToLowerCase(s1[s1off]) != unicode::ToLowerCase(s2[s2off]))
560 break;
562 s1off++;
563 s2off++;
564 count--;
567 return count == 0;
570 /* find UTC time from given date... no 1900 correction! */
571 static double
572 date_msecFromDate(double year, double mon, double mday, double hour,
573 double min, double sec, double msec)
575 return MakeDate(MakeDay(year, mon, mday), MakeTime(hour, min, sec, msec));
578 /* compute the time in msec (unclipped) from the given args */
579 #define MAXARGS 7
581 static bool
582 date_msecFromArgs(JSContext* cx, CallArgs args, double* rval)
584 unsigned loop;
585 double array[MAXARGS];
586 double msec_time;
588 for (loop = 0; loop < MAXARGS; loop++) {
589 if (loop < args.length()) {
590 double d;
591 if (!ToNumber(cx, args[loop], &d))
592 return false;
593 /* return NaN if any arg is not finite */
594 if (!IsFinite(d)) {
595 *rval = GenericNaN();
596 return true;
598 array[loop] = ToInteger(d);
599 } else {
600 if (loop == 2) {
601 array[loop] = 1; /* Default the date argument to 1. */
602 } else {
603 array[loop] = 0;
608 /* adjust 2-digit years into the 20th century */
609 if (array[0] >= 0 && array[0] <= 99)
610 array[0] += 1900;
612 msec_time = date_msecFromDate(array[0], array[1], array[2],
613 array[3], array[4], array[5], array[6]);
614 *rval = msec_time;
615 return true;
619 * See ECMA 15.9.4.[3-10];
621 static bool
622 date_UTC(JSContext* cx, unsigned argc, Value* vp)
624 CallArgs args = CallArgsFromVp(argc, vp);
626 double msec_time;
627 if (!date_msecFromArgs(cx, args, &msec_time))
628 return false;
630 msec_time = TimeClip(msec_time);
632 args.rval().setNumber(msec_time);
633 return true;
637 * Read and convert decimal digits from s[*i] into *result
638 * while *i < limit.
640 * Succeed if any digits are converted. Advance *i only
641 * as digits are consumed.
643 template <typename CharT>
644 static bool
645 ParseDigits(size_t* result, const CharT* s, size_t* i, size_t limit)
647 size_t init = *i;
648 *result = 0;
649 while (*i < limit && ('0' <= s[*i] && s[*i] <= '9')) {
650 *result *= 10;
651 *result += (s[*i] - '0');
652 ++(*i);
654 return *i != init;
658 * Read and convert decimal digits to the right of a decimal point,
659 * representing a fractional integer, from s[*i] into *result
660 * while *i < limit.
662 * Succeed if any digits are converted. Advance *i only
663 * as digits are consumed.
665 template <typename CharT>
666 static bool
667 ParseFractional(double* result, const CharT* s, size_t* i, size_t limit)
669 double factor = 0.1;
670 size_t init = *i;
671 *result = 0.0;
672 while (*i < limit && ('0' <= s[*i] && s[*i] <= '9')) {
673 *result += (s[*i] - '0') * factor;
674 factor *= 0.1;
675 ++(*i);
677 return *i != init;
681 * Read and convert exactly n decimal digits from s[*i]
682 * to s[min(*i+n,limit)] into *result.
684 * Succeed if exactly n digits are converted. Advance *i only
685 * on success.
687 template <typename CharT>
688 static bool
689 ParseDigitsN(size_t n, size_t* result, const CharT* s, size_t* i, size_t limit)
691 size_t init = *i;
693 if (ParseDigits(result, s, i, Min(limit, init + n)))
694 return (*i - init) == n;
696 *i = init;
697 return false;
700 static int
701 DaysInMonth(int year, int month)
703 bool leap = IsLeapYear(year);
704 int result = int(DayFromMonth(month, leap) - DayFromMonth(month - 1, leap));
705 return result;
709 * Parse a string in one of the date-time formats given by the W3C
710 * "NOTE-datetime" specification. These formats make up a restricted
711 * profile of the ISO 8601 format. Quoted here:
713 * The formats are as follows. Exactly the components shown here
714 * must be present, with exactly this punctuation. Note that the "T"
715 * appears literally in the string, to indicate the beginning of the
716 * time element, as specified in ISO 8601.
718 * Any combination of the date formats with the time formats is
719 * allowed, and also either the date or the time can be missing.
721 * The specification is silent on the meaning when fields are
722 * ommitted so the interpretations are a guess, but hopefully a
723 * reasonable one. We default the month to January, the day to the
724 * 1st, and hours minutes and seconds all to 0. If the date is
725 * missing entirely then we assume 1970-01-01 so that the time can
726 * be aded to a date later. If the time is missing then we assume
727 * 00:00 UTC. If the time is present but the time zone field is
728 * missing then we use local time.
730 * Date part:
732 * Year:
733 * YYYY (eg 1997)
735 * Year and month:
736 * YYYY-MM (eg 1997-07)
738 * Complete date:
739 * YYYY-MM-DD (eg 1997-07-16)
741 * Time part:
743 * Hours and minutes:
744 * Thh:mmTZD (eg T19:20+01:00)
746 * Hours, minutes and seconds:
747 * Thh:mm:ssTZD (eg T19:20:30+01:00)
749 * Hours, minutes, seconds and a decimal fraction of a second:
750 * Thh:mm:ss.sTZD (eg T19:20:30.45+01:00)
752 * where:
754 * YYYY = four-digit year or six digit year as +YYYYYY or -YYYYYY
755 * MM = two-digit month (01=January, etc.)
756 * DD = two-digit day of month (01 through 31)
757 * hh = two digits of hour (00 through 23) (am/pm NOT allowed)
758 * mm = two digits of minute (00 through 59)
759 * ss = two digits of second (00 through 59)
760 * s = one or more digits representing a decimal fraction of a second
761 * TZD = time zone designator (Z or +hh:mm or -hh:mm or missing for local)
763 template <typename CharT>
764 static bool
765 ParseISODate(const CharT* s, size_t length, double* result, DateTimeInfo* dtInfo)
767 size_t i = 0;
768 int tzMul = 1;
769 int dateMul = 1;
770 size_t year = 1970;
771 size_t month = 1;
772 size_t day = 1;
773 size_t hour = 0;
774 size_t min = 0;
775 size_t sec = 0;
776 double frac = 0;
777 bool isLocalTime = false;
778 size_t tzHour = 0;
779 size_t tzMin = 0;
781 #define PEEK(ch) (i < length && s[i] == ch)
783 #define NEED(ch) \
784 if (i >= length || s[i] != ch) { return false; } else { ++i; }
786 #define DONE_DATE_UNLESS(ch) \
787 if (i >= length || s[i] != ch) { goto done_date; } else { ++i; }
789 #define DONE_UNLESS(ch) \
790 if (i >= length || s[i] != ch) { goto done; } else { ++i; }
792 #define NEED_NDIGITS(n, field) \
793 if (!ParseDigitsN(n, &field, s, &i, length)) { return false; }
795 if (PEEK('+') || PEEK('-')) {
796 if (PEEK('-'))
797 dateMul = -1;
798 ++i;
799 NEED_NDIGITS(6, year);
800 } else if (!PEEK('T')) {
801 NEED_NDIGITS(4, year);
803 DONE_DATE_UNLESS('-');
804 NEED_NDIGITS(2, month);
805 DONE_DATE_UNLESS('-');
806 NEED_NDIGITS(2, day);
808 done_date:
809 DONE_UNLESS('T');
810 NEED_NDIGITS(2, hour);
811 NEED(':');
812 NEED_NDIGITS(2, min);
814 if (PEEK(':')) {
815 ++i;
816 NEED_NDIGITS(2, sec);
817 if (PEEK('.')) {
818 ++i;
819 if (!ParseFractional(&frac, s, &i, length))
820 return false;
824 if (PEEK('Z')) {
825 ++i;
826 } else if (PEEK('+') || PEEK('-')) {
827 if (PEEK('-'))
828 tzMul = -1;
829 ++i;
830 NEED_NDIGITS(2, tzHour);
832 * Non-standard extension to the ISO date format (permitted by ES5):
833 * allow "-0700" as a time zone offset, not just "-07:00".
835 if (PEEK(':'))
836 ++i;
837 NEED_NDIGITS(2, tzMin);
838 } else {
839 isLocalTime = true;
842 done:
843 if (year > 275943 // ceil(1e8/365) + 1970
844 || (month == 0 || month > 12)
845 || (day == 0 || day > size_t(DaysInMonth(year,month)))
846 || hour > 24
847 || ((hour == 24) && (min > 0 || sec > 0))
848 || min > 59
849 || sec > 59
850 || tzHour > 23
851 || tzMin > 59)
853 return false;
856 if (i != length)
857 return false;
859 month -= 1; /* convert month to 0-based */
861 double msec = date_msecFromDate(dateMul * double(year), month, day,
862 hour, min, sec, frac * 1000.0);
864 if (isLocalTime)
865 msec = UTC(msec, dtInfo);
866 else
867 msec -= tzMul * (tzHour * msPerHour + tzMin * msPerMinute);
869 if (msec < -8.64e15 || msec > 8.64e15)
870 return false;
872 *result = msec;
873 return true;
875 #undef PEEK
876 #undef NEED
877 #undef DONE_UNLESS
878 #undef NEED_NDIGITS
881 template <typename CharT>
882 static bool
883 ParseDate(const CharT* s, size_t length, double* result, DateTimeInfo* dtInfo)
885 if (ParseISODate(s, length, result, dtInfo))
886 return true;
888 if (length == 0)
889 return false;
891 int year = -1;
892 int mon = -1;
893 int mday = -1;
894 int hour = -1;
895 int min = -1;
896 int sec = -1;
897 int tzOffset = -1;
899 int prevc = 0;
901 bool seenPlusMinus = false;
902 bool seenMonthName = false;
904 size_t i = 0;
905 while (i < length) {
906 int c = s[i];
907 i++;
908 if (c <= ' ' || c == ',' || c == '-') {
909 if (c == '-' && '0' <= s[i] && s[i] <= '9')
910 prevc = c;
911 continue;
913 if (c == '(') { /* comments) */
914 int depth = 1;
915 while (i < length) {
916 c = s[i];
917 i++;
918 if (c == '(') {
919 depth++;
920 } else if (c == ')') {
921 if (--depth <= 0)
922 break;
925 continue;
927 if ('0' <= c && c <= '9') {
928 int n = c - '0';
929 while (i < length && '0' <= (c = s[i]) && c <= '9') {
930 n = n * 10 + c - '0';
931 i++;
935 * Allow TZA before the year, so 'Wed Nov 05 21:49:11 GMT-0800 1997'
936 * works.
938 * Uses of seenPlusMinus allow ':' in TZA, so Java no-timezone style
939 * of GMT+4:30 works.
942 if ((prevc == '+' || prevc == '-')/* && year>=0 */) {
943 /* Make ':' case below change tzOffset. */
944 seenPlusMinus = true;
946 /* offset */
947 if (n < 24)
948 n = n * 60; /* EG. "GMT-3" */
949 else
950 n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */
952 if (prevc == '+') /* plus means east of GMT */
953 n = -n;
955 if (tzOffset != 0 && tzOffset != -1)
956 return false;
958 tzOffset = n;
959 } else if (prevc == '/' && mon >= 0 && mday >= 0 && year < 0) {
960 if (c <= ' ' || c == ',' || c == '/' || i >= length)
961 year = n;
962 else
963 return false;
964 } else if (c == ':') {
965 if (hour < 0)
966 hour = /*byte*/ n;
967 else if (min < 0)
968 min = /*byte*/ n;
969 else
970 return false;
971 } else if (c == '/') {
973 * Until it is determined that mon is the actual month, keep
974 * it as 1-based rather than 0-based.
976 if (mon < 0)
977 mon = /*byte*/ n;
978 else if (mday < 0)
979 mday = /*byte*/ n;
980 else
981 return false;
982 } else if (i < length && c != ',' && c > ' ' && c != '-' && c != '(') {
983 return false;
984 } else if (seenPlusMinus && n < 60) { /* handle GMT-3:30 */
985 if (tzOffset < 0)
986 tzOffset -= n;
987 else
988 tzOffset += n;
989 } else if (hour >= 0 && min < 0) {
990 min = /*byte*/ n;
991 } else if (prevc == ':' && min >= 0 && sec < 0) {
992 sec = /*byte*/ n;
993 } else if (mon < 0) {
994 mon = /*byte*/n;
995 } else if (mon >= 0 && mday < 0) {
996 mday = /*byte*/ n;
997 } else if (mon >= 0 && mday >= 0 && year < 0) {
998 year = n;
999 } else {
1000 return false;
1002 prevc = 0;
1003 } else if (c == '/' || c == ':' || c == '+' || c == '-') {
1004 prevc = c;
1005 } else {
1006 size_t st = i - 1;
1007 int k;
1008 while (i < length) {
1009 c = s[i];
1010 if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')))
1011 break;
1012 i++;
1015 if (i <= st + 1)
1016 return false;
1018 for (k = ArrayLength(wtb); --k >= 0;) {
1019 if (RegionMatches(wtb[k], 0, s, st, i - st)) {
1020 int action = ttb[k];
1021 if (action != 0) {
1022 if (action < 0) {
1024 * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as
1025 * 12:30, instead of blindly adding 12 if PM.
1027 MOZ_ASSERT(action == -1 || action == -2);
1028 if (hour > 12 || hour < 0)
1029 return false;
1031 if (action == -1 && hour == 12) /* am */
1032 hour = 0;
1033 else if (action == -2 && hour != 12) /* pm */
1034 hour += 12;
1035 } else if (action <= 13) { /* month! */
1037 * Adjust mon to be 1-based until the final values
1038 * for mon, mday and year are adjusted below.
1040 if (seenMonthName)
1041 return false;
1043 seenMonthName = true;
1044 int temp = /*byte*/ (action - 2) + 1;
1046 if (mon < 0) {
1047 mon = temp;
1048 } else if (mday < 0) {
1049 mday = mon;
1050 mon = temp;
1051 } else if (year < 0) {
1052 year = mon;
1053 mon = temp;
1054 } else {
1055 return false;
1057 } else {
1058 tzOffset = action - 10000;
1061 break;
1065 if (k < 0)
1066 return false;
1068 prevc = 0;
1072 if (year < 0 || mon < 0 || mday < 0)
1073 return false;
1076 * Case 1. The input string contains an English month name.
1077 * The form of the string can be month f l, or f month l, or
1078 * f l month which each evaluate to the same date.
1079 * If f and l are both greater than or equal to 70, or
1080 * both less than 70, the date is invalid.
1081 * The year is taken to be the greater of the values f, l.
1082 * If the year is greater than or equal to 70 and less than 100,
1083 * it is considered to be the number of years after 1900.
1084 * Case 2. The input string is of the form "f/m/l" where f, m and l are
1085 * integers, e.g. 7/16/45.
1086 * Adjust the mon, mday and year values to achieve 100% MSIE
1087 * compatibility.
1088 * a. If 0 <= f < 70, f/m/l is interpreted as month/day/year.
1089 * i. If year < 100, it is the number of years after 1900
1090 * ii. If year >= 100, it is the number of years after 0.
1091 * b. If 70 <= f < 100
1092 * i. If m < 70, f/m/l is interpreted as
1093 * year/month/day where year is the number of years after
1094 * 1900.
1095 * ii. If m >= 70, the date is invalid.
1096 * c. If f >= 100
1097 * i. If m < 70, f/m/l is interpreted as
1098 * year/month/day where year is the number of years after 0.
1099 * ii. If m >= 70, the date is invalid.
1101 if (seenMonthName) {
1102 if ((mday >= 70 && year >= 70) || (mday < 70 && year < 70))
1103 return false;
1105 if (mday > year) {
1106 int temp = year;
1107 year = mday;
1108 mday = temp;
1110 if (year >= 70 && year < 100) {
1111 year += 1900;
1113 } else if (mon < 70) { /* (a) month/day/year */
1114 if (year < 100) {
1115 year += 1900;
1117 } else if (mon < 100) { /* (b) year/month/day */
1118 if (mday < 70) {
1119 int temp = year;
1120 year = mon + 1900;
1121 mon = mday;
1122 mday = temp;
1123 } else {
1124 return false;
1126 } else { /* (c) year/month/day */
1127 if (mday < 70) {
1128 int temp = year;
1129 year = mon;
1130 mon = mday;
1131 mday = temp;
1132 } else {
1133 return false;
1137 mon -= 1; /* convert month to 0-based */
1138 if (sec < 0)
1139 sec = 0;
1140 if (min < 0)
1141 min = 0;
1142 if (hour < 0)
1143 hour = 0;
1145 double msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
1147 if (tzOffset == -1) /* no time zone specified, have to use local */
1148 msec = UTC(msec, dtInfo);
1149 else
1150 msec += tzOffset * msPerMinute;
1152 *result = msec;
1153 return true;
1156 static bool
1157 ParseDate(JSLinearString* s, double* result, DateTimeInfo* dtInfo)
1159 AutoCheckCannotGC nogc;
1160 return s->hasLatin1Chars()
1161 ? ParseDate(s->latin1Chars(nogc), s->length(), result, dtInfo)
1162 : ParseDate(s->twoByteChars(nogc), s->length(), result, dtInfo);
1165 static bool
1166 date_parse(JSContext* cx, unsigned argc, Value* vp)
1168 CallArgs args = CallArgsFromVp(argc, vp);
1169 if (args.length() == 0) {
1170 args.rval().setNaN();
1171 return true;
1174 JSString* str = ToString<CanGC>(cx, args[0]);
1175 if (!str)
1176 return false;
1178 JSLinearString* linearStr = str->ensureLinear(cx);
1179 if (!linearStr)
1180 return false;
1182 double result;
1183 if (!ParseDate(linearStr, &result, &cx->runtime()->dateTimeInfo)) {
1184 args.rval().setNaN();
1185 return true;
1188 result = TimeClip(result);
1189 args.rval().setNumber(result);
1190 return true;
1193 static inline double
1194 NowAsMillis()
1196 return (double) (PRMJ_Now() / PRMJ_USEC_PER_MSEC);
1199 bool
1200 js::date_now(JSContext* cx, unsigned argc, Value* vp)
1202 CallArgs args = CallArgsFromVp(argc, vp);
1203 args.rval().setDouble(NowAsMillis());
1204 return true;
1207 void
1208 DateObject::setUTCTime(double t)
1210 for (size_t ind = COMPONENTS_START_SLOT; ind < RESERVED_SLOTS; ind++)
1211 setReservedSlot(ind, UndefinedValue());
1213 setFixedSlot(UTC_TIME_SLOT, DoubleValue(t));
1216 void
1217 DateObject::setUTCTime(double t, MutableHandleValue vp)
1219 setUTCTime(t);
1220 vp.setDouble(t);
1223 void
1224 DateObject::fillLocalTimeSlots(DateTimeInfo* dtInfo)
1226 /* Check if the cache is already populated. */
1227 if (!getReservedSlot(LOCAL_TIME_SLOT).isUndefined() &&
1228 getReservedSlot(TZA_SLOT).toDouble() == dtInfo->localTZA())
1230 return;
1233 /* Remember timezone used to generate the local cache. */
1234 setReservedSlot(TZA_SLOT, DoubleValue(dtInfo->localTZA()));
1236 double utcTime = UTCTime().toNumber();
1238 if (!IsFinite(utcTime)) {
1239 for (size_t ind = COMPONENTS_START_SLOT; ind < RESERVED_SLOTS; ind++)
1240 setReservedSlot(ind, DoubleValue(utcTime));
1241 return;
1244 double localTime = LocalTime(utcTime, dtInfo);
1246 setReservedSlot(LOCAL_TIME_SLOT, DoubleValue(localTime));
1248 int year = (int) floor(localTime /(msPerDay * 365.2425)) + 1970;
1249 double yearStartTime = TimeFromYear(year);
1251 /* Adjust the year in case the approximation was wrong, as in YearFromTime. */
1252 int yearDays;
1253 if (yearStartTime > localTime) {
1254 year--;
1255 yearStartTime -= (msPerDay * DaysInYear(year));
1256 yearDays = DaysInYear(year);
1257 } else {
1258 yearDays = DaysInYear(year);
1259 double nextStart = yearStartTime + (msPerDay * yearDays);
1260 if (nextStart <= localTime) {
1261 year++;
1262 yearStartTime = nextStart;
1263 yearDays = DaysInYear(year);
1267 setReservedSlot(LOCAL_YEAR_SLOT, Int32Value(year));
1269 uint64_t yearTime = uint64_t(localTime - yearStartTime);
1270 int yearSeconds = uint32_t(yearTime / 1000);
1272 int day = yearSeconds / int(SecondsPerDay);
1274 int step = -1, next = 30;
1275 int month;
1277 do {
1278 if (day <= next) {
1279 month = 0;
1280 break;
1282 step = next;
1283 next += ((yearDays == 366) ? 29 : 28);
1284 if (day <= next) {
1285 month = 1;
1286 break;
1288 step = next;
1289 if (day <= (next += 31)) {
1290 month = 2;
1291 break;
1293 step = next;
1294 if (day <= (next += 30)) {
1295 month = 3;
1296 break;
1298 step = next;
1299 if (day <= (next += 31)) {
1300 month = 4;
1301 break;
1303 step = next;
1304 if (day <= (next += 30)) {
1305 month = 5;
1306 break;
1308 step = next;
1309 if (day <= (next += 31)) {
1310 month = 6;
1311 break;
1313 step = next;
1314 if (day <= (next += 31)) {
1315 month = 7;
1316 break;
1318 step = next;
1319 if (day <= (next += 30)) {
1320 month = 8;
1321 break;
1323 step = next;
1324 if (day <= (next += 31)) {
1325 month = 9;
1326 break;
1328 step = next;
1329 if (day <= (next += 30)) {
1330 month = 10;
1331 break;
1333 step = next;
1334 month = 11;
1335 } while (0);
1337 setReservedSlot(LOCAL_MONTH_SLOT, Int32Value(month));
1338 setReservedSlot(LOCAL_DATE_SLOT, Int32Value(day - step));
1340 int weekday = WeekDay(localTime);
1341 setReservedSlot(LOCAL_DAY_SLOT, Int32Value(weekday));
1343 int seconds = yearSeconds % 60;
1344 setReservedSlot(LOCAL_SECONDS_SLOT, Int32Value(seconds));
1346 int minutes = (yearSeconds / 60) % 60;
1347 setReservedSlot(LOCAL_MINUTES_SLOT, Int32Value(minutes));
1349 int hours = (yearSeconds / (60 * 60)) % 24;
1350 setReservedSlot(LOCAL_HOURS_SLOT, Int32Value(hours));
1353 inline double
1354 DateObject::cachedLocalTime(DateTimeInfo* dtInfo)
1356 fillLocalTimeSlots(dtInfo);
1357 return getReservedSlot(LOCAL_TIME_SLOT).toDouble();
1360 MOZ_ALWAYS_INLINE bool
1361 IsDate(HandleValue v)
1363 return v.isObject() && v.toObject().is<DateObject>();
1367 * See ECMA 15.9.5.4 thru 15.9.5.23
1369 /* static */ MOZ_ALWAYS_INLINE bool
1370 DateObject::getTime_impl(JSContext* cx, CallArgs args)
1372 args.rval().set(args.thisv().toObject().as<DateObject>().UTCTime());
1373 return true;
1376 static bool
1377 date_getTime(JSContext* cx, unsigned argc, Value* vp)
1379 CallArgs args = CallArgsFromVp(argc, vp);
1380 return CallNonGenericMethod<IsDate, DateObject::getTime_impl>(cx, args);
1383 /* static */ MOZ_ALWAYS_INLINE bool
1384 DateObject::getYear_impl(JSContext* cx, CallArgs args)
1386 DateObject* dateObj = &args.thisv().toObject().as<DateObject>();
1387 dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo);
1389 Value yearVal = dateObj->getReservedSlot(LOCAL_YEAR_SLOT);
1390 if (yearVal.isInt32()) {
1391 /* Follow ECMA-262 to the letter, contrary to IE JScript. */
1392 int year = yearVal.toInt32() - 1900;
1393 args.rval().setInt32(year);
1394 } else {
1395 args.rval().set(yearVal);
1398 return true;
1401 static bool
1402 date_getYear(JSContext* cx, unsigned argc, Value* vp)
1404 CallArgs args = CallArgsFromVp(argc, vp);
1405 return CallNonGenericMethod<IsDate, DateObject::getYear_impl>(cx, args);
1408 /* static */ MOZ_ALWAYS_INLINE bool
1409 DateObject::getFullYear_impl(JSContext* cx, CallArgs args)
1411 DateObject* dateObj = &args.thisv().toObject().as<DateObject>();
1412 dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo);
1414 args.rval().set(dateObj->getReservedSlot(LOCAL_YEAR_SLOT));
1415 return true;
1418 static bool
1419 date_getFullYear(JSContext* cx, unsigned argc, Value* vp)
1421 CallArgs args = CallArgsFromVp(argc, vp);
1422 return CallNonGenericMethod<IsDate, DateObject::getFullYear_impl>(cx, args);
1425 /* static */ MOZ_ALWAYS_INLINE bool
1426 DateObject::getUTCFullYear_impl(JSContext* cx, CallArgs args)
1428 double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
1429 if (IsFinite(result))
1430 result = YearFromTime(result);
1432 args.rval().setNumber(result);
1433 return true;
1436 static bool
1437 date_getUTCFullYear(JSContext* cx, unsigned argc, Value* vp)
1439 CallArgs args = CallArgsFromVp(argc, vp);
1440 return CallNonGenericMethod<IsDate, DateObject::getUTCFullYear_impl>(cx, args);
1443 /* static */ MOZ_ALWAYS_INLINE bool
1444 DateObject::getMonth_impl(JSContext* cx, CallArgs args)
1446 DateObject* dateObj = &args.thisv().toObject().as<DateObject>();
1447 dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo);
1449 args.rval().set(dateObj->getReservedSlot(LOCAL_MONTH_SLOT));
1450 return true;
1453 static bool
1454 date_getMonth(JSContext* cx, unsigned argc, Value* vp)
1456 CallArgs args = CallArgsFromVp(argc, vp);
1457 return CallNonGenericMethod<IsDate, DateObject::getMonth_impl>(cx, args);
1460 /* static */ MOZ_ALWAYS_INLINE bool
1461 DateObject::getUTCMonth_impl(JSContext* cx, CallArgs args)
1463 double d = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
1464 args.rval().setNumber(MonthFromTime(d));
1465 return true;
1468 static bool
1469 date_getUTCMonth(JSContext* cx, unsigned argc, Value* vp)
1471 CallArgs args = CallArgsFromVp(argc, vp);
1472 return CallNonGenericMethod<IsDate, DateObject::getUTCMonth_impl>(cx, args);
1475 /* static */ MOZ_ALWAYS_INLINE bool
1476 DateObject::getDate_impl(JSContext* cx, CallArgs args)
1478 DateObject* dateObj = &args.thisv().toObject().as<DateObject>();
1479 dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo);
1481 args.rval().set(dateObj->getReservedSlot(LOCAL_DATE_SLOT));
1482 return true;
1485 static bool
1486 date_getDate(JSContext* cx, unsigned argc, Value* vp)
1488 CallArgs args = CallArgsFromVp(argc, vp);
1489 return CallNonGenericMethod<IsDate, DateObject::getDate_impl>(cx, args);
1492 /* static */ MOZ_ALWAYS_INLINE bool
1493 DateObject::getUTCDate_impl(JSContext* cx, CallArgs args)
1495 double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
1496 if (IsFinite(result))
1497 result = DateFromTime(result);
1499 args.rval().setNumber(result);
1500 return true;
1503 static bool
1504 date_getUTCDate(JSContext* cx, unsigned argc, Value* vp)
1506 CallArgs args = CallArgsFromVp(argc, vp);
1507 return CallNonGenericMethod<IsDate, DateObject::getUTCDate_impl>(cx, args);
1510 /* static */ MOZ_ALWAYS_INLINE bool
1511 DateObject::getDay_impl(JSContext* cx, CallArgs args)
1513 DateObject* dateObj = &args.thisv().toObject().as<DateObject>();
1514 dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo);
1516 args.rval().set(dateObj->getReservedSlot(LOCAL_DAY_SLOT));
1517 return true;
1520 static bool
1521 date_getDay(JSContext* cx, unsigned argc, Value* vp)
1523 CallArgs args = CallArgsFromVp(argc, vp);
1524 return CallNonGenericMethod<IsDate, DateObject::getDay_impl>(cx, args);
1527 /* static */ MOZ_ALWAYS_INLINE bool
1528 DateObject::getUTCDay_impl(JSContext* cx, CallArgs args)
1530 double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
1531 if (IsFinite(result))
1532 result = WeekDay(result);
1534 args.rval().setNumber(result);
1535 return true;
1538 static bool
1539 date_getUTCDay(JSContext* cx, unsigned argc, Value* vp)
1541 CallArgs args = CallArgsFromVp(argc, vp);
1542 return CallNonGenericMethod<IsDate, DateObject::getUTCDay_impl>(cx, args);
1545 /* static */ MOZ_ALWAYS_INLINE bool
1546 DateObject::getHours_impl(JSContext* cx, CallArgs args)
1548 DateObject* dateObj = &args.thisv().toObject().as<DateObject>();
1549 dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo);
1551 args.rval().set(dateObj->getReservedSlot(LOCAL_HOURS_SLOT));
1552 return true;
1555 static bool
1556 date_getHours(JSContext* cx, unsigned argc, Value* vp)
1558 CallArgs args = CallArgsFromVp(argc, vp);
1559 return CallNonGenericMethod<IsDate, DateObject::getHours_impl>(cx, args);
1562 /* static */ MOZ_ALWAYS_INLINE bool
1563 DateObject::getUTCHours_impl(JSContext* cx, CallArgs args)
1565 double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
1566 if (IsFinite(result))
1567 result = HourFromTime(result);
1569 args.rval().setNumber(result);
1570 return true;
1573 static bool
1574 date_getUTCHours(JSContext* cx, unsigned argc, Value* vp)
1576 CallArgs args = CallArgsFromVp(argc, vp);
1577 return CallNonGenericMethod<IsDate, DateObject::getUTCHours_impl>(cx, args);
1580 /* static */ MOZ_ALWAYS_INLINE bool
1581 DateObject::getMinutes_impl(JSContext* cx, CallArgs args)
1583 DateObject* dateObj = &args.thisv().toObject().as<DateObject>();
1584 dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo);
1586 args.rval().set(dateObj->getReservedSlot(LOCAL_MINUTES_SLOT));
1587 return true;
1590 static bool
1591 date_getMinutes(JSContext* cx, unsigned argc, Value* vp)
1593 CallArgs args = CallArgsFromVp(argc, vp);
1594 return CallNonGenericMethod<IsDate, DateObject::getMinutes_impl>(cx, args);
1597 /* static */ MOZ_ALWAYS_INLINE bool
1598 DateObject::getUTCMinutes_impl(JSContext* cx, CallArgs args)
1600 double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
1601 if (IsFinite(result))
1602 result = MinFromTime(result);
1604 args.rval().setNumber(result);
1605 return true;
1608 static bool
1609 date_getUTCMinutes(JSContext* cx, unsigned argc, Value* vp)
1611 CallArgs args = CallArgsFromVp(argc, vp);
1612 return CallNonGenericMethod<IsDate, DateObject::getUTCMinutes_impl>(cx, args);
1615 /* Date.getSeconds is mapped to getUTCSeconds */
1617 /* static */ MOZ_ALWAYS_INLINE bool
1618 DateObject::getUTCSeconds_impl(JSContext* cx, CallArgs args)
1620 DateObject* dateObj = &args.thisv().toObject().as<DateObject>();
1621 dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo);
1623 args.rval().set(dateObj->getReservedSlot(LOCAL_SECONDS_SLOT));
1624 return true;
1627 static bool
1628 date_getUTCSeconds(JSContext* cx, unsigned argc, Value* vp)
1630 CallArgs args = CallArgsFromVp(argc, vp);
1631 return CallNonGenericMethod<IsDate, DateObject::getUTCSeconds_impl>(cx, args);
1634 /* Date.getMilliseconds is mapped to getUTCMilliseconds */
1636 /* static */ MOZ_ALWAYS_INLINE bool
1637 DateObject::getUTCMilliseconds_impl(JSContext* cx, CallArgs args)
1639 double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
1640 if (IsFinite(result))
1641 result = msFromTime(result);
1643 args.rval().setNumber(result);
1644 return true;
1647 static bool
1648 date_getUTCMilliseconds(JSContext* cx, unsigned argc, Value* vp)
1650 CallArgs args = CallArgsFromVp(argc, vp);
1651 return CallNonGenericMethod<IsDate, DateObject::getUTCMilliseconds_impl>(cx, args);
1654 /* static */ MOZ_ALWAYS_INLINE bool
1655 DateObject::getTimezoneOffset_impl(JSContext* cx, CallArgs args)
1657 DateObject* dateObj = &args.thisv().toObject().as<DateObject>();
1658 double utctime = dateObj->UTCTime().toNumber();
1659 double localtime = dateObj->cachedLocalTime(&cx->runtime()->dateTimeInfo);
1662 * Return the time zone offset in minutes for the current locale that is
1663 * appropriate for this time. This value would be a constant except for
1664 * daylight savings time.
1666 double result = (utctime - localtime) / msPerMinute;
1667 args.rval().setNumber(result);
1668 return true;
1671 static bool
1672 date_getTimezoneOffset(JSContext* cx, unsigned argc, Value* vp)
1674 CallArgs args = CallArgsFromVp(argc, vp);
1675 return CallNonGenericMethod<IsDate, DateObject::getTimezoneOffset_impl>(cx, args);
1678 MOZ_ALWAYS_INLINE bool
1679 date_setTime_impl(JSContext* cx, CallArgs args)
1681 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1682 if (args.length() == 0) {
1683 dateObj->setUTCTime(GenericNaN(), args.rval());
1684 return true;
1687 double result;
1688 if (!ToNumber(cx, args[0], &result))
1689 return false;
1691 dateObj->setUTCTime(TimeClip(result), args.rval());
1692 return true;
1695 static bool
1696 date_setTime(JSContext* cx, unsigned argc, Value* vp)
1698 CallArgs args = CallArgsFromVp(argc, vp);
1699 return CallNonGenericMethod<IsDate, date_setTime_impl>(cx, args);
1702 static bool
1703 GetMsecsOrDefault(JSContext* cx, const CallArgs& args, unsigned i, double t, double* millis)
1705 if (args.length() <= i) {
1706 *millis = msFromTime(t);
1707 return true;
1709 return ToNumber(cx, args[i], millis);
1712 static bool
1713 GetSecsOrDefault(JSContext* cx, const CallArgs& args, unsigned i, double t, double* sec)
1715 if (args.length() <= i) {
1716 *sec = SecFromTime(t);
1717 return true;
1719 return ToNumber(cx, args[i], sec);
1722 static bool
1723 GetMinsOrDefault(JSContext* cx, const CallArgs& args, unsigned i, double t, double* mins)
1725 if (args.length() <= i) {
1726 *mins = MinFromTime(t);
1727 return true;
1729 return ToNumber(cx, args[i], mins);
1732 /* ES5 15.9.5.28. */
1733 MOZ_ALWAYS_INLINE bool
1734 date_setMilliseconds_impl(JSContext* cx, CallArgs args)
1736 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1738 /* Step 1. */
1739 double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo);
1741 /* Step 2. */
1742 double milli;
1743 if (!ToNumber(cx, args.get(0), &milli))
1744 return false;
1745 double time = MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), milli);
1747 /* Step 3. */
1748 double u = TimeClip(UTC(MakeDate(Day(t), time), &cx->runtime()->dateTimeInfo));
1750 /* Steps 4-5. */
1751 dateObj->setUTCTime(u, args.rval());
1752 return true;
1755 static bool
1756 date_setMilliseconds(JSContext* cx, unsigned argc, Value* vp)
1758 CallArgs args = CallArgsFromVp(argc, vp);
1759 return CallNonGenericMethod<IsDate, date_setMilliseconds_impl>(cx, args);
1762 /* ES5 15.9.5.29. */
1763 MOZ_ALWAYS_INLINE bool
1764 date_setUTCMilliseconds_impl(JSContext* cx, CallArgs args)
1766 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1768 /* Step 1. */
1769 double t = dateObj->UTCTime().toNumber();
1771 /* Step 2. */
1772 double milli;
1773 if (!ToNumber(cx, args.get(0), &milli))
1774 return false;
1775 double time = MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), milli);
1777 /* Step 3. */
1778 double v = TimeClip(MakeDate(Day(t), time));
1780 /* Steps 4-5. */
1781 dateObj->setUTCTime(v, args.rval());
1782 return true;
1785 static bool
1786 date_setUTCMilliseconds(JSContext* cx, unsigned argc, Value* vp)
1788 CallArgs args = CallArgsFromVp(argc, vp);
1789 return CallNonGenericMethod<IsDate, date_setUTCMilliseconds_impl>(cx, args);
1792 /* ES5 15.9.5.30. */
1793 MOZ_ALWAYS_INLINE bool
1794 date_setSeconds_impl(JSContext* cx, CallArgs args)
1796 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1798 /* Step 1. */
1799 double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo);
1801 /* Step 2. */
1802 double s;
1803 if (!ToNumber(cx, args.get(0), &s))
1804 return false;
1806 /* Step 3. */
1807 double milli;
1808 if (!GetMsecsOrDefault(cx, args, 1, t, &milli))
1809 return false;
1811 /* Step 4. */
1812 double date = MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli));
1814 /* Step 5. */
1815 double u = TimeClip(UTC(date, &cx->runtime()->dateTimeInfo));
1817 /* Steps 6-7. */
1818 dateObj->setUTCTime(u, args.rval());
1819 return true;
1822 /* ES5 15.9.5.31. */
1823 static bool
1824 date_setSeconds(JSContext* cx, unsigned argc, Value* vp)
1826 CallArgs args = CallArgsFromVp(argc, vp);
1827 return CallNonGenericMethod<IsDate, date_setSeconds_impl>(cx, args);
1830 MOZ_ALWAYS_INLINE bool
1831 date_setUTCSeconds_impl(JSContext* cx, CallArgs args)
1833 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1835 /* Step 1. */
1836 double t = dateObj->UTCTime().toNumber();
1838 /* Step 2. */
1839 double s;
1840 if (!ToNumber(cx, args.get(0), &s))
1841 return false;
1843 /* Step 3. */
1844 double milli;
1845 if (!GetMsecsOrDefault(cx, args, 1, t, &milli))
1846 return false;
1848 /* Step 4. */
1849 double date = MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli));
1851 /* Step 5. */
1852 double v = TimeClip(date);
1854 /* Steps 6-7. */
1855 dateObj->setUTCTime(v, args.rval());
1856 return true;
1859 /* ES5 15.9.5.32. */
1860 static bool
1861 date_setUTCSeconds(JSContext* cx, unsigned argc, Value* vp)
1863 CallArgs args = CallArgsFromVp(argc, vp);
1864 return CallNonGenericMethod<IsDate, date_setUTCSeconds_impl>(cx, args);
1867 MOZ_ALWAYS_INLINE bool
1868 date_setMinutes_impl(JSContext* cx, CallArgs args)
1870 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1872 /* Step 1. */
1873 double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo);
1875 /* Step 2. */
1876 double m;
1877 if (!ToNumber(cx, args.get(0), &m))
1878 return false;
1880 /* Step 3. */
1881 double s;
1882 if (!GetSecsOrDefault(cx, args, 1, t, &s))
1883 return false;
1885 /* Step 4. */
1886 double milli;
1887 if (!GetMsecsOrDefault(cx, args, 2, t, &milli))
1888 return false;
1890 /* Step 5. */
1891 double date = MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli));
1893 /* Step 6. */
1894 double u = TimeClip(UTC(date, &cx->runtime()->dateTimeInfo));
1896 /* Steps 7-8. */
1897 dateObj->setUTCTime(u, args.rval());
1898 return true;
1901 /* ES5 15.9.5.33. */
1902 static bool
1903 date_setMinutes(JSContext* cx, unsigned argc, Value* vp)
1905 CallArgs args = CallArgsFromVp(argc, vp);
1906 return CallNonGenericMethod<IsDate, date_setMinutes_impl>(cx, args);
1909 MOZ_ALWAYS_INLINE bool
1910 date_setUTCMinutes_impl(JSContext* cx, CallArgs args)
1912 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1914 /* Step 1. */
1915 double t = dateObj->UTCTime().toNumber();
1917 /* Step 2. */
1918 double m;
1919 if (!ToNumber(cx, args.get(0), &m))
1920 return false;
1922 /* Step 3. */
1923 double s;
1924 if (!GetSecsOrDefault(cx, args, 1, t, &s))
1925 return false;
1927 /* Step 4. */
1928 double milli;
1929 if (!GetMsecsOrDefault(cx, args, 2, t, &milli))
1930 return false;
1932 /* Step 5. */
1933 double date = MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli));
1935 /* Step 6. */
1936 double v = TimeClip(date);
1938 /* Steps 7-8. */
1939 dateObj->setUTCTime(v, args.rval());
1940 return true;
1943 /* ES5 15.9.5.34. */
1944 static bool
1945 date_setUTCMinutes(JSContext* cx, unsigned argc, Value* vp)
1947 CallArgs args = CallArgsFromVp(argc, vp);
1948 return CallNonGenericMethod<IsDate, date_setUTCMinutes_impl>(cx, args);
1951 MOZ_ALWAYS_INLINE bool
1952 date_setHours_impl(JSContext* cx, CallArgs args)
1954 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1956 /* Step 1. */
1957 double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo);
1959 /* Step 2. */
1960 double h;
1961 if (!ToNumber(cx, args.get(0), &h))
1962 return false;
1964 /* Step 3. */
1965 double m;
1966 if (!GetMinsOrDefault(cx, args, 1, t, &m))
1967 return false;
1969 /* Step 4. */
1970 double s;
1971 if (!GetSecsOrDefault(cx, args, 2, t, &s))
1972 return false;
1974 /* Step 5. */
1975 double milli;
1976 if (!GetMsecsOrDefault(cx, args, 3, t, &milli))
1977 return false;
1979 /* Step 6. */
1980 double date = MakeDate(Day(t), MakeTime(h, m, s, milli));
1982 /* Step 6. */
1983 double u = TimeClip(UTC(date, &cx->runtime()->dateTimeInfo));
1985 /* Steps 7-8. */
1986 dateObj->setUTCTime(u, args.rval());
1987 return true;
1990 /* ES5 15.9.5.35. */
1991 static bool
1992 date_setHours(JSContext* cx, unsigned argc, Value* vp)
1994 CallArgs args = CallArgsFromVp(argc, vp);
1995 return CallNonGenericMethod<IsDate, date_setHours_impl>(cx, args);
1998 MOZ_ALWAYS_INLINE bool
1999 date_setUTCHours_impl(JSContext* cx, CallArgs args)
2001 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2003 /* Step 1. */
2004 double t = dateObj->UTCTime().toNumber();
2006 /* Step 2. */
2007 double h;
2008 if (!ToNumber(cx, args.get(0), &h))
2009 return false;
2011 /* Step 3. */
2012 double m;
2013 if (!GetMinsOrDefault(cx, args, 1, t, &m))
2014 return false;
2016 /* Step 4. */
2017 double s;
2018 if (!GetSecsOrDefault(cx, args, 2, t, &s))
2019 return false;
2021 /* Step 5. */
2022 double milli;
2023 if (!GetMsecsOrDefault(cx, args, 3, t, &milli))
2024 return false;
2026 /* Step 6. */
2027 double newDate = MakeDate(Day(t), MakeTime(h, m, s, milli));
2029 /* Step 7. */
2030 double v = TimeClip(newDate);
2032 /* Steps 8-9. */
2033 dateObj->setUTCTime(v, args.rval());
2034 return true;
2037 /* ES5 15.9.5.36. */
2038 static bool
2039 date_setUTCHours(JSContext* cx, unsigned argc, Value* vp)
2041 CallArgs args = CallArgsFromVp(argc, vp);
2042 return CallNonGenericMethod<IsDate, date_setUTCHours_impl>(cx, args);
2045 MOZ_ALWAYS_INLINE bool
2046 date_setDate_impl(JSContext* cx, CallArgs args)
2048 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2050 /* Step 1. */
2051 double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo);
2053 /* Step 2. */
2054 double date;
2055 if (!ToNumber(cx, args.get(0), &date))
2056 return false;
2058 /* Step 3. */
2059 double newDate = MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t));
2061 /* Step 4. */
2062 double u = TimeClip(UTC(newDate, &cx->runtime()->dateTimeInfo));
2064 /* Steps 5-6. */
2065 dateObj->setUTCTime(u, args.rval());
2066 return true;
2069 /* ES5 15.9.5.37. */
2070 static bool
2071 date_setDate(JSContext* cx, unsigned argc, Value* vp)
2073 CallArgs args = CallArgsFromVp(argc, vp);
2074 return CallNonGenericMethod<IsDate, date_setDate_impl>(cx, args);
2077 MOZ_ALWAYS_INLINE bool
2078 date_setUTCDate_impl(JSContext* cx, CallArgs args)
2080 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2082 /* Step 1. */
2083 double t = dateObj->UTCTime().toNumber();
2085 /* Step 2. */
2086 double date;
2087 if (!ToNumber(cx, args.get(0), &date))
2088 return false;
2090 /* Step 3. */
2091 double newDate = MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t));
2093 /* Step 4. */
2094 double v = TimeClip(newDate);
2096 /* Steps 5-6. */
2097 dateObj->setUTCTime(v, args.rval());
2098 return true;
2101 static bool
2102 date_setUTCDate(JSContext* cx, unsigned argc, Value* vp)
2104 CallArgs args = CallArgsFromVp(argc, vp);
2105 return CallNonGenericMethod<IsDate, date_setUTCDate_impl>(cx, args);
2108 static bool
2109 GetDateOrDefault(JSContext* cx, const CallArgs& args, unsigned i, double t, double* date)
2111 if (args.length() <= i) {
2112 *date = DateFromTime(t);
2113 return true;
2115 return ToNumber(cx, args[i], date);
2118 static bool
2119 GetMonthOrDefault(JSContext* cx, const CallArgs& args, unsigned i, double t, double* month)
2121 if (args.length() <= i) {
2122 *month = MonthFromTime(t);
2123 return true;
2125 return ToNumber(cx, args[i], month);
2128 /* ES5 15.9.5.38. */
2129 MOZ_ALWAYS_INLINE bool
2130 date_setMonth_impl(JSContext* cx, CallArgs args)
2132 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2134 /* Step 1. */
2135 double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo);
2137 /* Step 2. */
2138 double m;
2139 if (!ToNumber(cx, args.get(0), &m))
2140 return false;
2142 /* Step 3. */
2143 double date;
2144 if (!GetDateOrDefault(cx, args, 1, t, &date))
2145 return false;
2147 /* Step 4. */
2148 double newDate = MakeDate(MakeDay(YearFromTime(t), m, date), TimeWithinDay(t));
2150 /* Step 5. */
2151 double u = TimeClip(UTC(newDate, &cx->runtime()->dateTimeInfo));
2153 /* Steps 6-7. */
2154 dateObj->setUTCTime(u, args.rval());
2155 return true;
2158 static bool
2159 date_setMonth(JSContext* cx, unsigned argc, Value* vp)
2161 CallArgs args = CallArgsFromVp(argc, vp);
2162 return CallNonGenericMethod<IsDate, date_setMonth_impl>(cx, args);
2165 /* ES5 15.9.5.39. */
2166 MOZ_ALWAYS_INLINE bool
2167 date_setUTCMonth_impl(JSContext* cx, CallArgs args)
2169 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2171 /* Step 1. */
2172 double t = dateObj->UTCTime().toNumber();
2174 /* Step 2. */
2175 double m;
2176 if (!ToNumber(cx, args.get(0), &m))
2177 return false;
2179 /* Step 3. */
2180 double date;
2181 if (!GetDateOrDefault(cx, args, 1, t, &date))
2182 return false;
2184 /* Step 4. */
2185 double newDate = MakeDate(MakeDay(YearFromTime(t), m, date), TimeWithinDay(t));
2187 /* Step 5. */
2188 double v = TimeClip(newDate);
2190 /* Steps 6-7. */
2191 dateObj->setUTCTime(v, args.rval());
2192 return true;
2195 static bool
2196 date_setUTCMonth(JSContext* cx, unsigned argc, Value* vp)
2198 CallArgs args = CallArgsFromVp(argc, vp);
2199 return CallNonGenericMethod<IsDate, date_setUTCMonth_impl>(cx, args);
2202 static double
2203 ThisLocalTimeOrZero(Handle<DateObject*> dateObj, DateTimeInfo* dtInfo)
2205 double t = dateObj->UTCTime().toNumber();
2206 if (IsNaN(t))
2207 return +0;
2208 return LocalTime(t, dtInfo);
2211 static double
2212 ThisUTCTimeOrZero(Handle<DateObject*> dateObj)
2214 double t = dateObj->as<DateObject>().UTCTime().toNumber();
2215 return IsNaN(t) ? +0 : t;
2218 /* ES5 15.9.5.40. */
2219 MOZ_ALWAYS_INLINE bool
2220 date_setFullYear_impl(JSContext* cx, CallArgs args)
2222 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2224 /* Step 1. */
2225 double t = ThisLocalTimeOrZero(dateObj, &cx->runtime()->dateTimeInfo);
2227 /* Step 2. */
2228 double y;
2229 if (!ToNumber(cx, args.get(0), &y))
2230 return false;
2232 /* Step 3. */
2233 double m;
2234 if (!GetMonthOrDefault(cx, args, 1, t, &m))
2235 return false;
2237 /* Step 4. */
2238 double date;
2239 if (!GetDateOrDefault(cx, args, 2, t, &date))
2240 return false;
2242 /* Step 5. */
2243 double newDate = MakeDate(MakeDay(y, m, date), TimeWithinDay(t));
2245 /* Step 6. */
2246 double u = TimeClip(UTC(newDate, &cx->runtime()->dateTimeInfo));
2248 /* Steps 7-8. */
2249 dateObj->setUTCTime(u, args.rval());
2250 return true;
2253 static bool
2254 date_setFullYear(JSContext* cx, unsigned argc, Value* vp)
2256 CallArgs args = CallArgsFromVp(argc, vp);
2257 return CallNonGenericMethod<IsDate, date_setFullYear_impl>(cx, args);
2260 /* ES5 15.9.5.41. */
2261 MOZ_ALWAYS_INLINE bool
2262 date_setUTCFullYear_impl(JSContext* cx, CallArgs args)
2264 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2266 /* Step 1. */
2267 double t = ThisUTCTimeOrZero(dateObj);
2269 /* Step 2. */
2270 double y;
2271 if (!ToNumber(cx, args.get(0), &y))
2272 return false;
2274 /* Step 3. */
2275 double m;
2276 if (!GetMonthOrDefault(cx, args, 1, t, &m))
2277 return false;
2279 /* Step 4. */
2280 double date;
2281 if (!GetDateOrDefault(cx, args, 2, t, &date))
2282 return false;
2284 /* Step 5. */
2285 double newDate = MakeDate(MakeDay(y, m, date), TimeWithinDay(t));
2287 /* Step 6. */
2288 double v = TimeClip(newDate);
2290 /* Steps 7-8. */
2291 dateObj->setUTCTime(v, args.rval());
2292 return true;
2295 static bool
2296 date_setUTCFullYear(JSContext* cx, unsigned argc, Value* vp)
2298 CallArgs args = CallArgsFromVp(argc, vp);
2299 return CallNonGenericMethod<IsDate, date_setUTCFullYear_impl>(cx, args);
2302 /* ES5 Annex B.2.5. */
2303 MOZ_ALWAYS_INLINE bool
2304 date_setYear_impl(JSContext* cx, CallArgs args)
2306 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2308 /* Step 1. */
2309 double t = ThisLocalTimeOrZero(dateObj, &cx->runtime()->dateTimeInfo);
2311 /* Step 2. */
2312 double y;
2313 if (!ToNumber(cx, args.get(0), &y))
2314 return false;
2316 /* Step 3. */
2317 if (IsNaN(y)) {
2318 dateObj->setUTCTime(GenericNaN(), args.rval());
2319 return true;
2322 /* Step 4. */
2323 double yint = ToInteger(y);
2324 if (0 <= yint && yint <= 99)
2325 yint += 1900;
2327 /* Step 5. */
2328 double day = MakeDay(yint, MonthFromTime(t), DateFromTime(t));
2330 /* Step 6. */
2331 double u = UTC(MakeDate(day, TimeWithinDay(t)), &cx->runtime()->dateTimeInfo);
2333 /* Steps 7-8. */
2334 dateObj->setUTCTime(TimeClip(u), args.rval());
2335 return true;
2338 static bool
2339 date_setYear(JSContext* cx, unsigned argc, Value* vp)
2341 CallArgs args = CallArgsFromVp(argc, vp);
2342 return CallNonGenericMethod<IsDate, date_setYear_impl>(cx, args);
2345 /* constants for toString, toUTCString */
2346 static const char js_NaN_date_str[] = "Invalid Date";
2347 static const char * const days[] =
2349 "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
2351 static const char * const months[] =
2353 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
2357 // Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
2358 // requires a PRMJTime... which only has 16-bit years. Sub-ECMA.
2359 static void
2360 print_gmt_string(char* buf, size_t size, double utctime)
2362 MOZ_ASSERT(TimeClip(utctime) == utctime);
2363 JS_snprintf(buf, size, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
2364 days[int(WeekDay(utctime))],
2365 int(DateFromTime(utctime)),
2366 months[int(MonthFromTime(utctime))],
2367 int(YearFromTime(utctime)),
2368 int(HourFromTime(utctime)),
2369 int(MinFromTime(utctime)),
2370 int(SecFromTime(utctime)));
2373 static void
2374 print_iso_string(char* buf, size_t size, double utctime)
2376 MOZ_ASSERT(TimeClip(utctime) == utctime);
2377 JS_snprintf(buf, size, "%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.3dZ",
2378 int(YearFromTime(utctime)),
2379 int(MonthFromTime(utctime)) + 1,
2380 int(DateFromTime(utctime)),
2381 int(HourFromTime(utctime)),
2382 int(MinFromTime(utctime)),
2383 int(SecFromTime(utctime)),
2384 int(msFromTime(utctime)));
2387 /* ES5 B.2.6. */
2388 MOZ_ALWAYS_INLINE bool
2389 date_toGMTString_impl(JSContext* cx, CallArgs args)
2391 double utctime = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
2393 char buf[100];
2394 if (!IsFinite(utctime))
2395 JS_snprintf(buf, sizeof buf, js_NaN_date_str);
2396 else
2397 print_gmt_string(buf, sizeof buf, utctime);
2399 JSString* str = JS_NewStringCopyZ(cx, buf);
2400 if (!str)
2401 return false;
2402 args.rval().setString(str);
2403 return true;
2406 /* ES5 15.9.5.43. */
2407 static bool
2408 date_toGMTString(JSContext* cx, unsigned argc, Value* vp)
2410 CallArgs args = CallArgsFromVp(argc, vp);
2411 return CallNonGenericMethod<IsDate, date_toGMTString_impl>(cx, args);
2414 MOZ_ALWAYS_INLINE bool
2415 date_toISOString_impl(JSContext* cx, CallArgs args)
2417 double utctime = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
2418 if (!IsFinite(utctime)) {
2419 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INVALID_DATE);
2420 return false;
2423 char buf[100];
2424 print_iso_string(buf, sizeof buf, utctime);
2426 JSString* str = JS_NewStringCopyZ(cx, buf);
2427 if (!str)
2428 return false;
2429 args.rval().setString(str);
2430 return true;
2434 static bool
2435 date_toISOString(JSContext* cx, unsigned argc, Value* vp)
2437 CallArgs args = CallArgsFromVp(argc, vp);
2438 return CallNonGenericMethod<IsDate, date_toISOString_impl>(cx, args);
2441 /* ES5 15.9.5.44. */
2442 static bool
2443 date_toJSON(JSContext* cx, unsigned argc, Value* vp)
2445 CallArgs args = CallArgsFromVp(argc, vp);
2447 /* Step 1. */
2448 RootedObject obj(cx, ToObject(cx, args.thisv()));
2449 if (!obj)
2450 return false;
2452 /* Step 2. */
2453 RootedValue tv(cx, ObjectValue(*obj));
2454 if (!ToPrimitive(cx, JSTYPE_NUMBER, &tv))
2455 return false;
2457 /* Step 3. */
2458 if (tv.isDouble() && !IsFinite(tv.toDouble())) {
2459 args.rval().setNull();
2460 return true;
2463 /* Step 4. */
2464 RootedValue toISO(cx);
2465 if (!JSObject::getProperty(cx, obj, obj, cx->names().toISOString, &toISO))
2466 return false;
2468 /* Step 5. */
2469 if (!IsCallable(toISO)) {
2470 JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, nullptr,
2471 JSMSG_BAD_TOISOSTRING_PROP);
2472 return false;
2475 /* Step 6. */
2476 InvokeArgs args2(cx);
2477 if (!args2.init(0))
2478 return false;
2480 args2.setCallee(toISO);
2481 args2.setThis(ObjectValue(*obj));
2483 if (!Invoke(cx, args2))
2484 return false;
2485 args.rval().set(args2.rval());
2486 return true;
2489 /* for Date.toLocaleFormat; interface to PRMJTime date struct.
2491 static void
2492 new_explode(double timeval, PRMJTime* split, DateTimeInfo* dtInfo)
2494 double year = YearFromTime(timeval);
2496 split->tm_usec = int32_t(msFromTime(timeval)) * 1000;
2497 split->tm_sec = int8_t(SecFromTime(timeval));
2498 split->tm_min = int8_t(MinFromTime(timeval));
2499 split->tm_hour = int8_t(HourFromTime(timeval));
2500 split->tm_mday = int8_t(DateFromTime(timeval));
2501 split->tm_mon = int8_t(MonthFromTime(timeval));
2502 split->tm_wday = int8_t(WeekDay(timeval));
2503 split->tm_year = year;
2504 split->tm_yday = int16_t(DayWithinYear(timeval, year));
2506 /* not sure how this affects things, but it doesn't seem
2507 to matter. */
2508 split->tm_isdst = (DaylightSavingTA(timeval, dtInfo) != 0);
2511 typedef enum formatspec {
2512 FORMATSPEC_FULL, FORMATSPEC_DATE, FORMATSPEC_TIME
2513 } formatspec;
2515 /* helper function */
2516 static bool
2517 date_format(JSContext* cx, double date, formatspec format, MutableHandleValue rval)
2519 char buf[100];
2520 char tzbuf[100];
2521 bool usetz;
2522 size_t i, tzlen;
2523 PRMJTime split;
2525 if (!IsFinite(date)) {
2526 JS_snprintf(buf, sizeof buf, js_NaN_date_str);
2527 } else {
2528 MOZ_ASSERT(TimeClip(date) == date);
2530 double local = LocalTime(date, &cx->runtime()->dateTimeInfo);
2532 /* offset from GMT in minutes. The offset includes daylight savings,
2533 if it applies. */
2534 int minutes = (int) floor(AdjustTime(date, &cx->runtime()->dateTimeInfo) / msPerMinute);
2536 /* map 510 minutes to 0830 hours */
2537 int offset = (minutes / 60) * 100 + minutes % 60;
2539 /* print as "Wed Nov 05 19:38:03 GMT-0800 (PST) 1997" The TZA is
2540 * printed as 'GMT-0800' rather than as 'PST' to avoid
2541 * operating-system dependence on strftime (which
2542 * PRMJ_FormatTimeUSEnglish calls, for %Z only.) win32 prints
2543 * PST as 'Pacific Standard Time.' This way we always know
2544 * what we're getting, and can parse it if we produce it.
2545 * The OS TZA string is included as a comment.
2548 /* get a timezone string from the OS to include as a
2549 comment. */
2550 new_explode(date, &split, &cx->runtime()->dateTimeInfo);
2551 if (PRMJ_FormatTime(tzbuf, sizeof tzbuf, "(%Z)", &split) != 0) {
2553 /* Decide whether to use the resulting timezone string.
2555 * Reject it if it contains any non-ASCII, non-alphanumeric
2556 * characters. It's then likely in some other character
2557 * encoding, and we probably won't display it correctly.
2559 usetz = true;
2560 tzlen = strlen(tzbuf);
2561 if (tzlen > 100) {
2562 usetz = false;
2563 } else {
2564 for (i = 0; i < tzlen; i++) {
2565 char16_t c = tzbuf[i];
2566 if (c > 127 ||
2567 !(isalpha(c) || isdigit(c) ||
2568 c == ' ' || c == '(' || c == ')')) {
2569 usetz = false;
2574 /* Also reject it if it's not parenthesized or if it's '()'. */
2575 if (tzbuf[0] != '(' || tzbuf[1] == ')')
2576 usetz = false;
2577 } else
2578 usetz = false;
2580 switch (format) {
2581 case FORMATSPEC_FULL:
2583 * Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
2584 * requires a PRMJTime... which only has 16-bit years. Sub-ECMA.
2586 /* Tue Oct 31 2000 09:41:40 GMT-0800 (PST) */
2587 JS_snprintf(buf, sizeof buf,
2588 "%s %s %.2d %.4d %.2d:%.2d:%.2d GMT%+.4d%s%s",
2589 days[int(WeekDay(local))],
2590 months[int(MonthFromTime(local))],
2591 int(DateFromTime(local)),
2592 int(YearFromTime(local)),
2593 int(HourFromTime(local)),
2594 int(MinFromTime(local)),
2595 int(SecFromTime(local)),
2596 offset,
2597 usetz ? " " : "",
2598 usetz ? tzbuf : "");
2599 break;
2600 case FORMATSPEC_DATE:
2601 /* Tue Oct 31 2000 */
2602 JS_snprintf(buf, sizeof buf,
2603 "%s %s %.2d %.4d",
2604 days[int(WeekDay(local))],
2605 months[int(MonthFromTime(local))],
2606 int(DateFromTime(local)),
2607 int(YearFromTime(local)));
2608 break;
2609 case FORMATSPEC_TIME:
2610 /* 09:41:40 GMT-0800 (PST) */
2611 JS_snprintf(buf, sizeof buf,
2612 "%.2d:%.2d:%.2d GMT%+.4d%s%s",
2613 int(HourFromTime(local)),
2614 int(MinFromTime(local)),
2615 int(SecFromTime(local)),
2616 offset,
2617 usetz ? " " : "",
2618 usetz ? tzbuf : "");
2619 break;
2623 JSString* str = JS_NewStringCopyZ(cx, buf);
2624 if (!str)
2625 return false;
2626 rval.setString(str);
2627 return true;
2630 static bool
2631 ToLocaleFormatHelper(JSContext* cx, HandleObject obj, const char* format, MutableHandleValue rval)
2633 double utctime = obj->as<DateObject>().UTCTime().toNumber();
2635 char buf[100];
2636 if (!IsFinite(utctime)) {
2637 JS_snprintf(buf, sizeof buf, js_NaN_date_str);
2638 } else {
2639 int result_len;
2640 double local = LocalTime(utctime, &cx->runtime()->dateTimeInfo);
2641 PRMJTime split;
2642 new_explode(local, &split, &cx->runtime()->dateTimeInfo);
2644 /* Let PRMJTime format it. */
2645 result_len = PRMJ_FormatTime(buf, sizeof buf, format, &split);
2647 /* If it failed, default to toString. */
2648 if (result_len == 0)
2649 return date_format(cx, utctime, FORMATSPEC_FULL, rval);
2651 /* Hacked check against undesired 2-digit year 00/00/00 form. */
2652 if (strcmp(format, "%x") == 0 && result_len >= 6 &&
2653 /* Format %x means use OS settings, which may have 2-digit yr, so
2654 hack end of 3/11/22 or 11.03.22 or 11Mar22 to use 4-digit yr...*/
2655 !isdigit(buf[result_len - 3]) &&
2656 isdigit(buf[result_len - 2]) && isdigit(buf[result_len - 1]) &&
2657 /* ...but not if starts with 4-digit year, like 2022/3/11. */
2658 !(isdigit(buf[0]) && isdigit(buf[1]) &&
2659 isdigit(buf[2]) && isdigit(buf[3]))) {
2660 double localtime = obj->as<DateObject>().cachedLocalTime(&cx->runtime()->dateTimeInfo);
2661 int year = IsNaN(localtime) ? 0 : (int) YearFromTime(localtime);
2662 JS_snprintf(buf + (result_len - 2), (sizeof buf) - (result_len - 2),
2663 "%d", year);
2668 if (cx->runtime()->localeCallbacks && cx->runtime()->localeCallbacks->localeToUnicode)
2669 return cx->runtime()->localeCallbacks->localeToUnicode(cx, buf, rval);
2671 JSString* str = JS_NewStringCopyZ(cx, buf);
2672 if (!str)
2673 return false;
2674 rval.setString(str);
2675 return true;
2678 #if !EXPOSE_INTL_API
2679 static bool
2680 ToLocaleStringHelper(JSContext* cx, Handle<DateObject*> dateObj, MutableHandleValue rval)
2683 * Use '%#c' for windows, because '%c' is backward-compatible and non-y2k
2684 * with msvc; '%#c' requests that a full year be used in the result string.
2686 return ToLocaleFormatHelper(cx, dateObj,
2687 #if defined(_WIN32) && !defined(__MWERKS__)
2688 "%#c"
2689 #else
2690 "%c"
2691 #endif
2692 , rval);
2695 /* ES5 15.9.5.5. */
2696 MOZ_ALWAYS_INLINE bool
2697 date_toLocaleString_impl(JSContext* cx, CallArgs args)
2699 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2700 return ToLocaleStringHelper(cx, dateObj, args.rval());
2703 static bool
2704 date_toLocaleString(JSContext* cx, unsigned argc, Value* vp)
2706 CallArgs args = CallArgsFromVp(argc, vp);
2707 return CallNonGenericMethod<IsDate, date_toLocaleString_impl>(cx, args);
2710 /* ES5 15.9.5.6. */
2711 MOZ_ALWAYS_INLINE bool
2712 date_toLocaleDateString_impl(JSContext* cx, CallArgs args)
2715 * Use '%#x' for windows, because '%x' is backward-compatible and non-y2k
2716 * with msvc; '%#x' requests that a full year be used in the result string.
2718 static const char format[] =
2719 #if defined(_WIN32) && !defined(__MWERKS__)
2720 "%#x"
2721 #else
2722 "%x"
2723 #endif
2726 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2727 return ToLocaleFormatHelper(cx, dateObj, format, args.rval());
2730 static bool
2731 date_toLocaleDateString(JSContext* cx, unsigned argc, Value* vp)
2733 CallArgs args = CallArgsFromVp(argc, vp);
2734 return CallNonGenericMethod<IsDate, date_toLocaleDateString_impl>(cx, args);
2737 /* ES5 15.9.5.7. */
2738 MOZ_ALWAYS_INLINE bool
2739 date_toLocaleTimeString_impl(JSContext* cx, CallArgs args)
2741 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2742 return ToLocaleFormatHelper(cx, dateObj, "%X", args.rval());
2745 static bool
2746 date_toLocaleTimeString(JSContext* cx, unsigned argc, Value* vp)
2748 CallArgs args = CallArgsFromVp(argc, vp);
2749 return CallNonGenericMethod<IsDate, date_toLocaleTimeString_impl>(cx, args);
2751 #endif /* !EXPOSE_INTL_API */
2753 MOZ_ALWAYS_INLINE bool
2754 date_toLocaleFormat_impl(JSContext* cx, CallArgs args)
2756 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2758 if (args.length() == 0) {
2760 * Use '%#c' for windows, because '%c' is backward-compatible and non-y2k
2761 * with msvc; '%#c' requests that a full year be used in the result string.
2763 return ToLocaleFormatHelper(cx, dateObj,
2764 #if defined(_WIN32) && !defined(__MWERKS__)
2765 "%#c"
2766 #else
2767 "%c"
2768 #endif
2769 , args.rval());
2772 RootedString fmt(cx, ToString<CanGC>(cx, args[0]));
2773 if (!fmt)
2774 return false;
2776 JSAutoByteString fmtbytes(cx, fmt);
2777 if (!fmtbytes)
2778 return false;
2780 return ToLocaleFormatHelper(cx, dateObj, fmtbytes.ptr(), args.rval());
2783 static bool
2784 date_toLocaleFormat(JSContext* cx, unsigned argc, Value* vp)
2786 CallArgs args = CallArgsFromVp(argc, vp);
2787 return CallNonGenericMethod<IsDate, date_toLocaleFormat_impl>(cx, args);
2790 /* ES5 15.9.5.4. */
2791 MOZ_ALWAYS_INLINE bool
2792 date_toTimeString_impl(JSContext* cx, CallArgs args)
2794 return date_format(cx, args.thisv().toObject().as<DateObject>().UTCTime().toNumber(),
2795 FORMATSPEC_TIME, args.rval());
2798 static bool
2799 date_toTimeString(JSContext* cx, unsigned argc, Value* vp)
2801 CallArgs args = CallArgsFromVp(argc, vp);
2802 return CallNonGenericMethod<IsDate, date_toTimeString_impl>(cx, args);
2805 /* ES5 15.9.5.3. */
2806 MOZ_ALWAYS_INLINE bool
2807 date_toDateString_impl(JSContext* cx, CallArgs args)
2809 return date_format(cx, args.thisv().toObject().as<DateObject>().UTCTime().toNumber(),
2810 FORMATSPEC_DATE, args.rval());
2813 static bool
2814 date_toDateString(JSContext* cx, unsigned argc, Value* vp)
2816 CallArgs args = CallArgsFromVp(argc, vp);
2817 return CallNonGenericMethod<IsDate, date_toDateString_impl>(cx, args);
2820 #if JS_HAS_TOSOURCE
2821 MOZ_ALWAYS_INLINE bool
2822 date_toSource_impl(JSContext* cx, CallArgs args)
2824 StringBuffer sb(cx);
2825 if (!sb.append("(new Date(") ||
2826 !NumberValueToStringBuffer(cx, args.thisv().toObject().as<DateObject>().UTCTime(), sb) ||
2827 !sb.append("))"))
2829 return false;
2832 JSString* str = sb.finishString();
2833 if (!str)
2834 return false;
2835 args.rval().setString(str);
2836 return true;
2839 static bool
2840 date_toSource(JSContext* cx, unsigned argc, Value* vp)
2842 CallArgs args = CallArgsFromVp(argc, vp);
2843 return CallNonGenericMethod<IsDate, date_toSource_impl>(cx, args);
2845 #endif
2847 MOZ_ALWAYS_INLINE bool
2848 date_toString_impl(JSContext* cx, CallArgs args)
2850 return date_format(cx, args.thisv().toObject().as<DateObject>().UTCTime().toNumber(),
2851 FORMATSPEC_FULL, args.rval());
2854 static bool
2855 date_toString(JSContext* cx, unsigned argc, Value* vp)
2857 CallArgs args = CallArgsFromVp(argc, vp);
2858 return CallNonGenericMethod<IsDate, date_toString_impl>(cx, args);
2861 MOZ_ALWAYS_INLINE bool
2862 date_valueOf_impl(JSContext* cx, CallArgs args)
2864 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2865 args.rval().set(dateObj->UTCTime());
2866 return true;
2869 bool
2870 js::date_valueOf(JSContext* cx, unsigned argc, Value* vp)
2872 CallArgs args = CallArgsFromVp(argc, vp);
2873 return CallNonGenericMethod<IsDate, date_valueOf_impl>(cx, args);
2876 static const JSFunctionSpec date_static_methods[] = {
2877 JS_FN("UTC", date_UTC, MAXARGS,0),
2878 JS_FN("parse", date_parse, 1,0),
2879 JS_FN("now", date_now, 0,0),
2880 JS_FS_END
2883 static const JSFunctionSpec date_methods[] = {
2884 JS_FN("getTime", date_getTime, 0,0),
2885 JS_FN("getTimezoneOffset", date_getTimezoneOffset, 0,0),
2886 JS_FN("getYear", date_getYear, 0,0),
2887 JS_FN("getFullYear", date_getFullYear, 0,0),
2888 JS_FN("getUTCFullYear", date_getUTCFullYear, 0,0),
2889 JS_FN("getMonth", date_getMonth, 0,0),
2890 JS_FN("getUTCMonth", date_getUTCMonth, 0,0),
2891 JS_FN("getDate", date_getDate, 0,0),
2892 JS_FN("getUTCDate", date_getUTCDate, 0,0),
2893 JS_FN("getDay", date_getDay, 0,0),
2894 JS_FN("getUTCDay", date_getUTCDay, 0,0),
2895 JS_FN("getHours", date_getHours, 0,0),
2896 JS_FN("getUTCHours", date_getUTCHours, 0,0),
2897 JS_FN("getMinutes", date_getMinutes, 0,0),
2898 JS_FN("getUTCMinutes", date_getUTCMinutes, 0,0),
2899 JS_FN("getSeconds", date_getUTCSeconds, 0,0),
2900 JS_FN("getUTCSeconds", date_getUTCSeconds, 0,0),
2901 JS_FN("getMilliseconds", date_getUTCMilliseconds, 0,0),
2902 JS_FN("getUTCMilliseconds", date_getUTCMilliseconds, 0,0),
2903 JS_FN("setTime", date_setTime, 1,0),
2904 JS_FN("setYear", date_setYear, 1,0),
2905 JS_FN("setFullYear", date_setFullYear, 3,0),
2906 JS_FN("setUTCFullYear", date_setUTCFullYear, 3,0),
2907 JS_FN("setMonth", date_setMonth, 2,0),
2908 JS_FN("setUTCMonth", date_setUTCMonth, 2,0),
2909 JS_FN("setDate", date_setDate, 1,0),
2910 JS_FN("setUTCDate", date_setUTCDate, 1,0),
2911 JS_FN("setHours", date_setHours, 4,0),
2912 JS_FN("setUTCHours", date_setUTCHours, 4,0),
2913 JS_FN("setMinutes", date_setMinutes, 3,0),
2914 JS_FN("setUTCMinutes", date_setUTCMinutes, 3,0),
2915 JS_FN("setSeconds", date_setSeconds, 2,0),
2916 JS_FN("setUTCSeconds", date_setUTCSeconds, 2,0),
2917 JS_FN("setMilliseconds", date_setMilliseconds, 1,0),
2918 JS_FN("setUTCMilliseconds", date_setUTCMilliseconds, 1,0),
2919 JS_FN("toUTCString", date_toGMTString, 0,0),
2920 JS_FN("toLocaleFormat", date_toLocaleFormat, 0,0),
2921 #if EXPOSE_INTL_API
2922 JS_SELF_HOSTED_FN(js_toLocaleString_str, "Date_toLocaleString", 0,0),
2923 JS_SELF_HOSTED_FN("toLocaleDateString", "Date_toLocaleDateString", 0,0),
2924 JS_SELF_HOSTED_FN("toLocaleTimeString", "Date_toLocaleTimeString", 0,0),
2925 #else
2926 JS_FN(js_toLocaleString_str, date_toLocaleString, 0,0),
2927 JS_FN("toLocaleDateString", date_toLocaleDateString, 0,0),
2928 JS_FN("toLocaleTimeString", date_toLocaleTimeString, 0,0),
2929 #endif
2930 JS_FN("toDateString", date_toDateString, 0,0),
2931 JS_FN("toTimeString", date_toTimeString, 0,0),
2932 JS_FN("toISOString", date_toISOString, 0,0),
2933 JS_FN(js_toJSON_str, date_toJSON, 1,0),
2934 #if JS_HAS_TOSOURCE
2935 JS_FN(js_toSource_str, date_toSource, 0,0),
2936 #endif
2937 JS_FN(js_toString_str, date_toString, 0,0),
2938 JS_FN(js_valueOf_str, date_valueOf, 0,0),
2939 JS_FS_END
2942 bool
2943 js_Date(JSContext* cx, unsigned argc, Value* vp)
2945 CallArgs args = CallArgsFromVp(argc, vp);
2947 /* Date called as function. */
2948 if (!args.isConstructing())
2949 return date_format(cx, NowAsMillis(), FORMATSPEC_FULL, args.rval());
2951 /* Date called as constructor. */
2952 double d;
2953 if (args.length() == 0) {
2954 /* ES5 15.9.3.3. */
2955 d = NowAsMillis();
2956 } else if (args.length() == 1) {
2957 /* ES5 15.9.3.2. */
2959 /* Step 1. */
2960 if (!ToPrimitive(cx, args[0]))
2961 return false;
2963 if (args[0].isString()) {
2964 /* Step 2. */
2965 JSString* str = args[0].toString();
2966 if (!str)
2967 return false;
2969 JSLinearString* linearStr = str->ensureLinear(cx);
2970 if (!linearStr)
2971 return false;
2973 if (!ParseDate(linearStr, &d, &cx->runtime()->dateTimeInfo))
2974 d = GenericNaN();
2975 else
2976 d = TimeClip(d);
2977 } else {
2978 /* Step 3. */
2979 if (!ToNumber(cx, args[0], &d))
2980 return false;
2981 d = TimeClip(d);
2983 } else {
2984 double msec_time;
2985 if (!date_msecFromArgs(cx, args, &msec_time))
2986 return false;
2988 if (IsFinite(msec_time)) {
2989 msec_time = UTC(msec_time, &cx->runtime()->dateTimeInfo);
2990 msec_time = TimeClip(msec_time);
2992 d = msec_time;
2995 JSObject* obj = js_NewDateObjectMsec(cx, d);
2996 if (!obj)
2997 return false;
2999 args.rval().setObject(*obj);
3000 return true;
3003 static bool
3004 FinishDateClassInit(JSContext* cx, HandleObject ctor, HandleObject proto)
3006 proto->as<DateObject>().setUTCTime(GenericNaN());
3009 * Date.prototype.toGMTString has the same initial value as
3010 * Date.prototype.toUTCString.
3012 RootedValue toUTCStringFun(cx);
3013 RootedId toUTCStringId(cx, NameToId(cx->names().toUTCString));
3014 RootedId toGMTStringId(cx, NameToId(cx->names().toGMTString));
3015 return baseops::GetProperty(cx, proto.as<DateObject>(), toUTCStringId, &toUTCStringFun) &&
3016 baseops::DefineGeneric(cx, proto.as<DateObject>(), toGMTStringId, toUTCStringFun,
3017 nullptr, nullptr, 0);
3020 const Class DateObject::class_ = {
3021 js_Date_str,
3022 JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) |
3023 JSCLASS_HAS_CACHED_PROTO(JSProto_Date),
3024 nullptr, /* addProperty */
3025 nullptr, /* delProperty */
3026 nullptr, /* getProperty */
3027 nullptr, /* setProperty */
3028 nullptr, /* enumerate */
3029 nullptr, /* resolve */
3030 date_convert,
3031 nullptr, /* finalize */
3032 nullptr, /* call */
3033 nullptr, /* hasInstance */
3034 nullptr, /* construct */
3035 nullptr, /* trace */
3037 GenericCreateConstructor<js_Date, MAXARGS, JSFunction::FinalizeKind>,
3038 GenericCreatePrototype,
3039 date_static_methods,
3040 date_methods,
3041 nullptr,
3042 FinishDateClassInit
3046 JS_FRIEND_API(JSObject*)
3047 js_NewDateObjectMsec(JSContext* cx, double msec_time)
3049 JSObject* obj = NewBuiltinClassInstance(cx, &DateObject::class_);
3050 if (!obj)
3051 return nullptr;
3052 obj->as<DateObject>().setUTCTime(msec_time);
3053 return obj;
3056 JS_FRIEND_API(JSObject*)
3057 js_NewDateObject(JSContext* cx, int year, int mon, int mday,
3058 int hour, int min, int sec)
3060 MOZ_ASSERT(mon < 12);
3061 double msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
3062 return js_NewDateObjectMsec(cx, UTC(msec_time, &cx->runtime()->dateTimeInfo));
3065 JS_FRIEND_API(bool)
3066 js::DateIsValid(JSContext* cx, JSObject* objArg)
3068 RootedObject obj(cx, objArg);
3069 if (!ObjectClassIs(obj, ESClass_Date, cx))
3070 return false;
3072 RootedValue unboxed(cx);
3073 if (!Unbox(cx, obj, &unboxed)) {
3074 // This can't actually happen, so we don't force consumers to deal with
3075 // a clunky out-param API. Do something sane-ish if it does happen.
3076 cx->clearPendingException();
3077 return false;
3080 return !IsNaN(unboxed.toNumber());
3083 JS_FRIEND_API(double)
3084 js::DateGetMsecSinceEpoch(JSContext* cx, JSObject* objArg)
3086 RootedObject obj(cx, objArg);
3087 if (!ObjectClassIs(obj, ESClass_Date, cx))
3088 return 0;
3090 RootedValue unboxed(cx);
3091 if (!Unbox(cx, obj, &unboxed)) {
3092 // This can't actually happen, so we don't force consumers to deal with
3093 // a clunky out-param API. Do something sane-ish if it does happen.
3094 cx->clearPendingException();
3095 return 0;
3098 return unboxed.toNumber();