Bumping manifests a=b2g-bump
[gecko.git] / js / src / jsdate.cpp
blob3d55a2f7102b3f68131df40eeae86bab477466ce
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/Date.h"
39 #include "vm/DateTime.h"
40 #include "vm/GlobalObject.h"
41 #include "vm/Interpreter.h"
42 #include "vm/NumericConversions.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;
59 * The JS 'Date' object is patterned after the Java 'Date' object.
60 * Here is a script:
62 * today = new Date();
64 * print(today.toLocaleString());
66 * weekDay = today.getDay();
69 * These Java (and ECMA-262) methods are supported:
71 * UTC
72 * getDate (getUTCDate)
73 * getDay (getUTCDay)
74 * getHours (getUTCHours)
75 * getMinutes (getUTCMinutes)
76 * getMonth (getUTCMonth)
77 * getSeconds (getUTCSeconds)
78 * getMilliseconds (getUTCMilliseconds)
79 * getTime
80 * getTimezoneOffset
81 * getYear
82 * getFullYear (getUTCFullYear)
83 * parse
84 * setDate (setUTCDate)
85 * setHours (setUTCHours)
86 * setMinutes (setUTCMinutes)
87 * setMonth (setUTCMonth)
88 * setSeconds (setUTCSeconds)
89 * setMilliseconds (setUTCMilliseconds)
90 * setTime
91 * setYear (setFullYear, setUTCFullYear)
92 * toGMTString (toUTCString)
93 * toLocaleString
94 * toString
97 * These Java methods are not supported
99 * setDay
100 * before
101 * after
102 * equals
103 * hashCode
106 static inline double
107 Day(double t)
109 return floor(t / msPerDay);
112 static double
113 TimeWithinDay(double t)
115 double result = fmod(t, msPerDay);
116 if (result < 0)
117 result += msPerDay;
118 return result;
121 /* ES5 15.9.1.3. */
122 static inline bool
123 IsLeapYear(double year)
125 JS_ASSERT(ToInteger(year) == year);
126 return fmod(year, 4) == 0 && (fmod(year, 100) != 0 || fmod(year, 400) == 0);
129 static inline double
130 DaysInYear(double year)
132 if (!IsFinite(year))
133 return GenericNaN();
134 return IsLeapYear(year) ? 366 : 365;
137 static inline double
138 DayFromYear(double y)
140 return 365 * (y - 1970) +
141 floor((y - 1969) / 4.0) -
142 floor((y - 1901) / 100.0) +
143 floor((y - 1601) / 400.0);
146 static inline double
147 TimeFromYear(double y)
149 return DayFromYear(y) * msPerDay;
152 static double
153 YearFromTime(double t)
155 if (!IsFinite(t))
156 return GenericNaN();
158 JS_ASSERT(ToInteger(t) == t);
160 double y = floor(t / (msPerDay * 365.2425)) + 1970;
161 double t2 = TimeFromYear(y);
164 * Adjust the year if the approximation was wrong. Since the year was
165 * computed using the average number of ms per year, it will usually
166 * be wrong for dates within several hours of a year transition.
168 if (t2 > t) {
169 y--;
170 } else {
171 if (t2 + msPerDay * DaysInYear(y) <= t)
172 y++;
174 return y;
177 static inline int
178 DaysInFebruary(double year)
180 return IsLeapYear(year) ? 29 : 28;
183 /* ES5 15.9.1.4. */
184 static inline double
185 DayWithinYear(double t, double year)
187 JS_ASSERT_IF(IsFinite(t), YearFromTime(t) == year);
188 return Day(t) - DayFromYear(year);
191 static double
192 MonthFromTime(double t)
194 if (!IsFinite(t))
195 return GenericNaN();
197 double year = YearFromTime(t);
198 double d = DayWithinYear(t, year);
200 int step;
201 if (d < (step = 31))
202 return 0;
203 if (d < (step += DaysInFebruary(year)))
204 return 1;
205 if (d < (step += 31))
206 return 2;
207 if (d < (step += 30))
208 return 3;
209 if (d < (step += 31))
210 return 4;
211 if (d < (step += 30))
212 return 5;
213 if (d < (step += 31))
214 return 6;
215 if (d < (step += 31))
216 return 7;
217 if (d < (step += 30))
218 return 8;
219 if (d < (step += 31))
220 return 9;
221 if (d < (step += 30))
222 return 10;
223 return 11;
226 /* ES5 15.9.1.5. */
227 static double
228 DateFromTime(double t)
230 if (!IsFinite(t))
231 return GenericNaN();
233 double year = YearFromTime(t);
234 double d = DayWithinYear(t, year);
236 int next;
237 if (d <= (next = 30))
238 return d + 1;
239 int step = next;
240 if (d <= (next += DaysInFebruary(year)))
241 return d - step;
242 step = next;
243 if (d <= (next += 31))
244 return d - step;
245 step = next;
246 if (d <= (next += 30))
247 return d - step;
248 step = next;
249 if (d <= (next += 31))
250 return d - step;
251 step = next;
252 if (d <= (next += 30))
253 return d - step;
254 step = next;
255 if (d <= (next += 31))
256 return d - step;
257 step = next;
258 if (d <= (next += 31))
259 return d - step;
260 step = next;
261 if (d <= (next += 30))
262 return d - step;
263 step = next;
264 if (d <= (next += 31))
265 return d - step;
266 step = next;
267 if (d <= (next += 30))
268 return d - step;
269 step = next;
270 return d - step;
273 /* ES5 15.9.1.6. */
274 static int
275 WeekDay(double t)
278 * We can't assert TimeClip(t) == t because we call this function with
279 * local times, which can be offset outside TimeClip's permitted range.
281 JS_ASSERT(ToInteger(t) == t);
282 int result = (int(Day(t)) + 4) % 7;
283 if (result < 0)
284 result += 7;
285 return result;
288 static inline int
289 DayFromMonth(int month, bool isLeapYear)
292 * The following array contains the day of year for the first day of
293 * each month, where index 0 is January, and day 0 is January 1.
295 static const int firstDayOfMonth[2][13] = {
296 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
297 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}
300 JS_ASSERT(0 <= month && month <= 12);
301 return firstDayOfMonth[isLeapYear][month];
304 template<typename T>
305 static inline int
306 DayFromMonth(T month, bool isLeapYear) MOZ_DELETE;
308 /* ES5 15.9.1.12 (out of order to accommodate DaylightSavingTA). */
309 static double
310 MakeDay(double year, double month, double date)
312 /* Step 1. */
313 if (!IsFinite(year) || !IsFinite(month) || !IsFinite(date))
314 return GenericNaN();
316 /* Steps 2-4. */
317 double y = ToInteger(year);
318 double m = ToInteger(month);
319 double dt = ToInteger(date);
321 /* Step 5. */
322 double ym = y + floor(m / 12);
324 /* Step 6. */
325 int mn = int(fmod(m, 12.0));
326 if (mn < 0)
327 mn += 12;
329 /* Steps 7-8. */
330 bool leap = IsLeapYear(ym);
332 double yearday = floor(TimeFromYear(ym) / msPerDay);
333 double monthday = DayFromMonth(mn, leap);
335 return yearday + monthday + dt - 1;
338 /* ES5 15.9.1.13 (out of order to accommodate DaylightSavingTA). */
339 static inline double
340 MakeDate(double day, double time)
342 /* Step 1. */
343 if (!IsFinite(day) || !IsFinite(time))
344 return GenericNaN();
346 /* Step 2. */
347 return day * msPerDay + time;
350 JS_PUBLIC_API(double)
351 JS::MakeDate(double year, unsigned month, unsigned day)
353 return TimeClip(::MakeDate(MakeDay(year, month, day), 0));
356 JS_PUBLIC_API(double)
357 JS::YearFromTime(double time)
359 return ::YearFromTime(time);
362 JS_PUBLIC_API(double)
363 JS::MonthFromTime(double time)
365 return ::MonthFromTime(time);
368 JS_PUBLIC_API(double)
369 JS::DayFromTime(double time)
371 return DateFromTime(time);
375 * Find a year for which any given date will fall on the same weekday.
377 * This function should be used with caution when used other than
378 * for determining DST; it hasn't been proven not to produce an
379 * incorrect year for times near year boundaries.
381 static int
382 EquivalentYearForDST(int year)
385 * Years and leap years on which Jan 1 is a Sunday, Monday, etc.
387 * yearStartingWith[0][i] is an example non-leap year where
388 * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
390 * yearStartingWith[1][i] is an example leap year where
391 * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
393 static const int yearStartingWith[2][7] = {
394 {1978, 1973, 1974, 1975, 1981, 1971, 1977},
395 {1984, 1996, 1980, 1992, 1976, 1988, 1972}
398 int day = int(DayFromYear(year) + 4) % 7;
399 if (day < 0)
400 day += 7;
402 return yearStartingWith[IsLeapYear(year)][day];
405 /* ES5 15.9.1.8. */
406 static double
407 DaylightSavingTA(double t, DateTimeInfo *dtInfo)
409 if (!IsFinite(t))
410 return GenericNaN();
413 * If earlier than 1970 or after 2038, potentially beyond the ken of
414 * many OSes, map it to an equivalent year before asking.
416 if (t < 0.0 || t > 2145916800000.0) {
417 int year = EquivalentYearForDST(int(YearFromTime(t)));
418 double day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
419 t = MakeDate(day, TimeWithinDay(t));
422 int64_t utcMilliseconds = static_cast<int64_t>(t);
423 int64_t offsetMilliseconds = dtInfo->getDSTOffsetMilliseconds(utcMilliseconds);
424 return static_cast<double>(offsetMilliseconds);
427 static double
428 AdjustTime(double date, DateTimeInfo *dtInfo)
430 double t = DaylightSavingTA(date, dtInfo) + dtInfo->localTZA();
431 t = (dtInfo->localTZA() >= 0) ? fmod(t, msPerDay) : -fmod(msPerDay - t, msPerDay);
432 return t;
435 /* ES5 15.9.1.9. */
436 static double
437 LocalTime(double t, DateTimeInfo *dtInfo)
439 return t + AdjustTime(t, dtInfo);
442 static double
443 UTC(double t, DateTimeInfo *dtInfo)
445 return t - AdjustTime(t - dtInfo->localTZA(), dtInfo);
448 /* ES5 15.9.1.10. */
449 static double
450 HourFromTime(double t)
452 double result = fmod(floor(t/msPerHour), HoursPerDay);
453 if (result < 0)
454 result += HoursPerDay;
455 return result;
458 static double
459 MinFromTime(double t)
461 double result = fmod(floor(t / msPerMinute), MinutesPerHour);
462 if (result < 0)
463 result += MinutesPerHour;
464 return result;
467 static double
468 SecFromTime(double t)
470 double result = fmod(floor(t / msPerSecond), SecondsPerMinute);
471 if (result < 0)
472 result += SecondsPerMinute;
473 return result;
476 static double
477 msFromTime(double t)
479 double result = fmod(t, msPerSecond);
480 if (result < 0)
481 result += msPerSecond;
482 return result;
485 /* ES5 15.9.1.11. */
486 static double
487 MakeTime(double hour, double min, double sec, double ms)
489 /* Step 1. */
490 if (!IsFinite(hour) ||
491 !IsFinite(min) ||
492 !IsFinite(sec) ||
493 !IsFinite(ms))
495 return GenericNaN();
498 /* Step 2. */
499 double h = ToInteger(hour);
501 /* Step 3. */
502 double m = ToInteger(min);
504 /* Step 4. */
505 double s = ToInteger(sec);
507 /* Step 5. */
508 double milli = ToInteger(ms);
510 /* Steps 6-7. */
511 return h * msPerHour + m * msPerMinute + s * msPerSecond + milli;
515 * end of ECMA 'support' functions
518 static bool
519 date_convert(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp)
521 JS_ASSERT(hint == JSTYPE_NUMBER || hint == JSTYPE_STRING || hint == JSTYPE_VOID);
522 JS_ASSERT(obj->is<DateObject>());
524 return DefaultValue(cx, obj, (hint == JSTYPE_VOID) ? JSTYPE_STRING : hint, vp);
527 /* for use by date_parse */
529 static const char* const wtb[] = {
530 "am", "pm",
531 "monday", "tuesday", "wednesday", "thursday", "friday",
532 "saturday", "sunday",
533 "january", "february", "march", "april", "may", "june",
534 "july", "august", "september", "october", "november", "december",
535 "gmt", "ut", "utc",
536 "est", "edt",
537 "cst", "cdt",
538 "mst", "mdt",
539 "pst", "pdt"
540 /* time zone table needs to be expanded */
543 static const int ttb[] = {
544 -1, -2, 0, 0, 0, 0, 0, 0, 0, /* AM/PM */
545 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
546 10000 + 0, 10000 + 0, 10000 + 0, /* GMT/UT/UTC */
547 10000 + 5 * 60, 10000 + 4 * 60, /* EST/EDT */
548 10000 + 6 * 60, 10000 + 5 * 60, /* CST/CDT */
549 10000 + 7 * 60, 10000 + 6 * 60, /* MST/MDT */
550 10000 + 8 * 60, 10000 + 7 * 60 /* PST/PDT */
553 template <typename CharT>
554 static bool
555 RegionMatches(const char *s1, int s1off, const CharT *s2, int s2off, int count)
557 while (count > 0 && s1[s1off] && s2[s2off]) {
558 if (unicode::ToLowerCase(s1[s1off]) != unicode::ToLowerCase(s2[s2off]))
559 break;
561 s1off++;
562 s2off++;
563 count--;
566 return count == 0;
569 /* find UTC time from given date... no 1900 correction! */
570 static double
571 date_msecFromDate(double year, double mon, double mday, double hour,
572 double min, double sec, double msec)
574 return MakeDate(MakeDay(year, mon, mday), MakeTime(hour, min, sec, msec));
577 /* compute the time in msec (unclipped) from the given args */
578 #define MAXARGS 7
580 static bool
581 date_msecFromArgs(JSContext *cx, CallArgs args, double *rval)
583 unsigned loop;
584 double array[MAXARGS];
585 double msec_time;
587 for (loop = 0; loop < MAXARGS; loop++) {
588 if (loop < args.length()) {
589 double d;
590 if (!ToNumber(cx, args[loop], &d))
591 return false;
592 /* return NaN if any arg is not finite */
593 if (!IsFinite(d)) {
594 *rval = GenericNaN();
595 return true;
597 array[loop] = ToInteger(d);
598 } else {
599 if (loop == 2) {
600 array[loop] = 1; /* Default the date argument to 1. */
601 } else {
602 array[loop] = 0;
607 /* adjust 2-digit years into the 20th century */
608 if (array[0] >= 0 && array[0] <= 99)
609 array[0] += 1900;
611 msec_time = date_msecFromDate(array[0], array[1], array[2],
612 array[3], array[4], array[5], array[6]);
613 *rval = msec_time;
614 return true;
618 * See ECMA 15.9.4.[3-10];
620 static bool
621 date_UTC(JSContext *cx, unsigned argc, Value *vp)
623 CallArgs args = CallArgsFromVp(argc, vp);
625 double msec_time;
626 if (!date_msecFromArgs(cx, args, &msec_time))
627 return false;
629 msec_time = TimeClip(msec_time);
631 args.rval().setNumber(msec_time);
632 return true;
636 * Read and convert decimal digits from s[*i] into *result
637 * while *i < limit.
639 * Succeed if any digits are converted. Advance *i only
640 * as digits are consumed.
642 template <typename CharT>
643 static bool
644 ParseDigits(size_t *result, const CharT *s, size_t *i, size_t limit)
646 size_t init = *i;
647 *result = 0;
648 while (*i < limit && ('0' <= s[*i] && s[*i] <= '9')) {
649 *result *= 10;
650 *result += (s[*i] - '0');
651 ++(*i);
653 return *i != init;
657 * Read and convert decimal digits to the right of a decimal point,
658 * representing a fractional integer, from s[*i] into *result
659 * while *i < limit.
661 * Succeed if any digits are converted. Advance *i only
662 * as digits are consumed.
664 template <typename CharT>
665 static bool
666 ParseFractional(double *result, const CharT *s, size_t *i, size_t limit)
668 double factor = 0.1;
669 size_t init = *i;
670 *result = 0.0;
671 while (*i < limit && ('0' <= s[*i] && s[*i] <= '9')) {
672 *result += (s[*i] - '0') * factor;
673 factor *= 0.1;
674 ++(*i);
676 return *i != init;
680 * Read and convert exactly n decimal digits from s[*i]
681 * to s[min(*i+n,limit)] into *result.
683 * Succeed if exactly n digits are converted. Advance *i only
684 * on success.
686 template <typename CharT>
687 static bool
688 ParseDigitsN(size_t n, size_t *result, const CharT *s, size_t *i, size_t limit)
690 size_t init = *i;
692 if (ParseDigits(result, s, i, Min(limit, init + n)))
693 return (*i - init) == n;
695 *i = init;
696 return false;
699 static int
700 DaysInMonth(int year, int month)
702 bool leap = IsLeapYear(year);
703 int result = int(DayFromMonth(month, leap) - DayFromMonth(month - 1, leap));
704 return result;
708 * Parse a string in one of the date-time formats given by the W3C
709 * "NOTE-datetime" specification. These formats make up a restricted
710 * profile of the ISO 8601 format. Quoted here:
712 * The formats are as follows. Exactly the components shown here
713 * must be present, with exactly this punctuation. Note that the "T"
714 * appears literally in the string, to indicate the beginning of the
715 * time element, as specified in ISO 8601.
717 * Any combination of the date formats with the time formats is
718 * allowed, and also either the date or the time can be missing.
720 * The specification is silent on the meaning when fields are
721 * ommitted so the interpretations are a guess, but hopefully a
722 * reasonable one. We default the month to January, the day to the
723 * 1st, and hours minutes and seconds all to 0. If the date is
724 * missing entirely then we assume 1970-01-01 so that the time can
725 * be aded to a date later. If the time is missing then we assume
726 * 00:00 UTC. If the time is present but the time zone field is
727 * missing then we use local time.
729 * Date part:
731 * Year:
732 * YYYY (eg 1997)
734 * Year and month:
735 * YYYY-MM (eg 1997-07)
737 * Complete date:
738 * YYYY-MM-DD (eg 1997-07-16)
740 * Time part:
742 * Hours and minutes:
743 * Thh:mmTZD (eg T19:20+01:00)
745 * Hours, minutes and seconds:
746 * Thh:mm:ssTZD (eg T19:20:30+01:00)
748 * Hours, minutes, seconds and a decimal fraction of a second:
749 * Thh:mm:ss.sTZD (eg T19:20:30.45+01:00)
751 * where:
753 * YYYY = four-digit year or six digit year as +YYYYYY or -YYYYYY
754 * MM = two-digit month (01=January, etc.)
755 * DD = two-digit day of month (01 through 31)
756 * hh = two digits of hour (00 through 23) (am/pm NOT allowed)
757 * mm = two digits of minute (00 through 59)
758 * ss = two digits of second (00 through 59)
759 * s = one or more digits representing a decimal fraction of a second
760 * TZD = time zone designator (Z or +hh:mm or -hh:mm or missing for local)
762 template <typename CharT>
763 static bool
764 ParseISODate(const CharT *s, size_t length, double *result, DateTimeInfo *dtInfo)
766 size_t i = 0;
767 int tzMul = 1;
768 int dateMul = 1;
769 size_t year = 1970;
770 size_t month = 1;
771 size_t day = 1;
772 size_t hour = 0;
773 size_t min = 0;
774 size_t sec = 0;
775 double frac = 0;
776 bool isLocalTime = false;
777 size_t tzHour = 0;
778 size_t tzMin = 0;
780 #define PEEK(ch) (i < length && s[i] == ch)
782 #define NEED(ch) \
783 if (i >= length || s[i] != ch) { return false; } else { ++i; }
785 #define DONE_DATE_UNLESS(ch) \
786 if (i >= length || s[i] != ch) { goto done_date; } else { ++i; }
788 #define DONE_UNLESS(ch) \
789 if (i >= length || s[i] != ch) { goto done; } else { ++i; }
791 #define NEED_NDIGITS(n, field) \
792 if (!ParseDigitsN(n, &field, s, &i, length)) { return false; }
794 if (PEEK('+') || PEEK('-')) {
795 if (PEEK('-'))
796 dateMul = -1;
797 ++i;
798 NEED_NDIGITS(6, year);
799 } else if (!PEEK('T')) {
800 NEED_NDIGITS(4, year);
802 DONE_DATE_UNLESS('-');
803 NEED_NDIGITS(2, month);
804 DONE_DATE_UNLESS('-');
805 NEED_NDIGITS(2, day);
807 done_date:
808 DONE_UNLESS('T');
809 NEED_NDIGITS(2, hour);
810 NEED(':');
811 NEED_NDIGITS(2, min);
813 if (PEEK(':')) {
814 ++i;
815 NEED_NDIGITS(2, sec);
816 if (PEEK('.')) {
817 ++i;
818 if (!ParseFractional(&frac, s, &i, length))
819 return false;
823 if (PEEK('Z')) {
824 ++i;
825 } else if (PEEK('+') || PEEK('-')) {
826 if (PEEK('-'))
827 tzMul = -1;
828 ++i;
829 NEED_NDIGITS(2, tzHour);
831 * Non-standard extension to the ISO date format (permitted by ES5):
832 * allow "-0700" as a time zone offset, not just "-07:00".
834 if (PEEK(':'))
835 ++i;
836 NEED_NDIGITS(2, tzMin);
837 } else {
838 isLocalTime = true;
841 done:
842 if (year > 275943 // ceil(1e8/365) + 1970
843 || (month == 0 || month > 12)
844 || (day == 0 || day > size_t(DaysInMonth(year,month)))
845 || hour > 24
846 || ((hour == 24) && (min > 0 || sec > 0))
847 || min > 59
848 || sec > 59
849 || tzHour > 23
850 || tzMin > 59)
852 return false;
855 if (i != length)
856 return false;
858 month -= 1; /* convert month to 0-based */
860 double msec = date_msecFromDate(dateMul * double(year), month, day,
861 hour, min, sec, frac * 1000.0);
863 if (isLocalTime)
864 msec = UTC(msec, dtInfo);
865 else
866 msec -= tzMul * (tzHour * msPerHour + tzMin * msPerMinute);
868 if (msec < -8.64e15 || msec > 8.64e15)
869 return false;
871 *result = msec;
872 return true;
874 #undef PEEK
875 #undef NEED
876 #undef DONE_UNLESS
877 #undef NEED_NDIGITS
880 template <typename CharT>
881 static bool
882 ParseDate(const CharT *s, size_t length, double *result, DateTimeInfo *dtInfo)
884 if (ParseISODate(s, length, result, dtInfo))
885 return true;
887 if (length == 0)
888 return false;
890 int year = -1;
891 int mon = -1;
892 int mday = -1;
893 int hour = -1;
894 int min = -1;
895 int sec = -1;
896 int tzOffset = -1;
898 int prevc = 0;
900 bool seenPlusMinus = false;
901 bool seenMonthName = false;
903 size_t i = 0;
904 while (i < length) {
905 int c = s[i];
906 i++;
907 if (c <= ' ' || c == ',' || c == '-') {
908 if (c == '-' && '0' <= s[i] && s[i] <= '9')
909 prevc = c;
910 continue;
912 if (c == '(') { /* comments) */
913 int depth = 1;
914 while (i < length) {
915 c = s[i];
916 i++;
917 if (c == '(') {
918 depth++;
919 } else if (c == ')') {
920 if (--depth <= 0)
921 break;
924 continue;
926 if ('0' <= c && c <= '9') {
927 int n = c - '0';
928 while (i < length && '0' <= (c = s[i]) && c <= '9') {
929 n = n * 10 + c - '0';
930 i++;
934 * Allow TZA before the year, so 'Wed Nov 05 21:49:11 GMT-0800 1997'
935 * works.
937 * Uses of seenPlusMinus allow ':' in TZA, so Java no-timezone style
938 * of GMT+4:30 works.
941 if ((prevc == '+' || prevc == '-')/* && year>=0 */) {
942 /* Make ':' case below change tzOffset. */
943 seenPlusMinus = true;
945 /* offset */
946 if (n < 24)
947 n = n * 60; /* EG. "GMT-3" */
948 else
949 n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */
951 if (prevc == '+') /* plus means east of GMT */
952 n = -n;
954 if (tzOffset != 0 && tzOffset != -1)
955 return false;
957 tzOffset = n;
958 } else if (prevc == '/' && mon >= 0 && mday >= 0 && year < 0) {
959 if (c <= ' ' || c == ',' || c == '/' || i >= length)
960 year = n;
961 else
962 return false;
963 } else if (c == ':') {
964 if (hour < 0)
965 hour = /*byte*/ n;
966 else if (min < 0)
967 min = /*byte*/ n;
968 else
969 return false;
970 } else if (c == '/') {
972 * Until it is determined that mon is the actual month, keep
973 * it as 1-based rather than 0-based.
975 if (mon < 0)
976 mon = /*byte*/ n;
977 else if (mday < 0)
978 mday = /*byte*/ n;
979 else
980 return false;
981 } else if (i < length && c != ',' && c > ' ' && c != '-' && c != '(') {
982 return false;
983 } else if (seenPlusMinus && n < 60) { /* handle GMT-3:30 */
984 if (tzOffset < 0)
985 tzOffset -= n;
986 else
987 tzOffset += n;
988 } else if (hour >= 0 && min < 0) {
989 min = /*byte*/ n;
990 } else if (prevc == ':' && min >= 0 && sec < 0) {
991 sec = /*byte*/ n;
992 } else if (mon < 0) {
993 mon = /*byte*/n;
994 } else if (mon >= 0 && mday < 0) {
995 mday = /*byte*/ n;
996 } else if (mon >= 0 && mday >= 0 && year < 0) {
997 year = n;
998 } else {
999 return false;
1001 prevc = 0;
1002 } else if (c == '/' || c == ':' || c == '+' || c == '-') {
1003 prevc = c;
1004 } else {
1005 size_t st = i - 1;
1006 int k;
1007 while (i < length) {
1008 c = s[i];
1009 if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')))
1010 break;
1011 i++;
1014 if (i <= st + 1)
1015 return false;
1017 for (k = ArrayLength(wtb); --k >= 0;) {
1018 if (RegionMatches(wtb[k], 0, s, st, i - st)) {
1019 int action = ttb[k];
1020 if (action != 0) {
1021 if (action < 0) {
1023 * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as
1024 * 12:30, instead of blindly adding 12 if PM.
1026 JS_ASSERT(action == -1 || action == -2);
1027 if (hour > 12 || hour < 0)
1028 return false;
1030 if (action == -1 && hour == 12) /* am */
1031 hour = 0;
1032 else if (action == -2 && hour != 12) /* pm */
1033 hour += 12;
1034 } else if (action <= 13) { /* month! */
1036 * Adjust mon to be 1-based until the final values
1037 * for mon, mday and year are adjusted below.
1039 if (seenMonthName)
1040 return false;
1042 seenMonthName = true;
1043 int temp = /*byte*/ (action - 2) + 1;
1045 if (mon < 0) {
1046 mon = temp;
1047 } else if (mday < 0) {
1048 mday = mon;
1049 mon = temp;
1050 } else if (year < 0) {
1051 year = mon;
1052 mon = temp;
1053 } else {
1054 return false;
1056 } else {
1057 tzOffset = action - 10000;
1060 break;
1064 if (k < 0)
1065 return false;
1067 prevc = 0;
1071 if (year < 0 || mon < 0 || mday < 0)
1072 return false;
1075 * Case 1. The input string contains an English month name.
1076 * The form of the string can be month f l, or f month l, or
1077 * f l month which each evaluate to the same date.
1078 * If f and l are both greater than or equal to 70, or
1079 * both less than 70, the date is invalid.
1080 * The year is taken to be the greater of the values f, l.
1081 * If the year is greater than or equal to 70 and less than 100,
1082 * it is considered to be the number of years after 1900.
1083 * Case 2. The input string is of the form "f/m/l" where f, m and l are
1084 * integers, e.g. 7/16/45.
1085 * Adjust the mon, mday and year values to achieve 100% MSIE
1086 * compatibility.
1087 * a. If 0 <= f < 70, f/m/l is interpreted as month/day/year.
1088 * i. If year < 100, it is the number of years after 1900
1089 * ii. If year >= 100, it is the number of years after 0.
1090 * b. If 70 <= f < 100
1091 * i. If m < 70, f/m/l is interpreted as
1092 * year/month/day where year is the number of years after
1093 * 1900.
1094 * ii. If m >= 70, the date is invalid.
1095 * c. If f >= 100
1096 * i. If m < 70, f/m/l is interpreted as
1097 * year/month/day where year is the number of years after 0.
1098 * ii. If m >= 70, the date is invalid.
1100 if (seenMonthName) {
1101 if ((mday >= 70 && year >= 70) || (mday < 70 && year < 70))
1102 return false;
1104 if (mday > year) {
1105 int temp = year;
1106 year = mday;
1107 mday = temp;
1109 if (year >= 70 && year < 100) {
1110 year += 1900;
1112 } else if (mon < 70) { /* (a) month/day/year */
1113 if (year < 100) {
1114 year += 1900;
1116 } else if (mon < 100) { /* (b) year/month/day */
1117 if (mday < 70) {
1118 int temp = year;
1119 year = mon + 1900;
1120 mon = mday;
1121 mday = temp;
1122 } else {
1123 return false;
1125 } else { /* (c) year/month/day */
1126 if (mday < 70) {
1127 int temp = year;
1128 year = mon;
1129 mon = mday;
1130 mday = temp;
1131 } else {
1132 return false;
1136 mon -= 1; /* convert month to 0-based */
1137 if (sec < 0)
1138 sec = 0;
1139 if (min < 0)
1140 min = 0;
1141 if (hour < 0)
1142 hour = 0;
1144 double msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
1146 if (tzOffset == -1) /* no time zone specified, have to use local */
1147 msec = UTC(msec, dtInfo);
1148 else
1149 msec += tzOffset * msPerMinute;
1151 *result = msec;
1152 return true;
1155 static bool
1156 ParseDate(JSLinearString *s, double *result, DateTimeInfo *dtInfo)
1158 AutoCheckCannotGC nogc;
1159 return s->hasLatin1Chars()
1160 ? ParseDate(s->latin1Chars(nogc), s->length(), result, dtInfo)
1161 : ParseDate(s->twoByteChars(nogc), s->length(), result, dtInfo);
1164 static bool
1165 date_parse(JSContext *cx, unsigned argc, Value *vp)
1167 CallArgs args = CallArgsFromVp(argc, vp);
1168 if (args.length() == 0) {
1169 args.rval().setNaN();
1170 return true;
1173 JSString *str = ToString<CanGC>(cx, args[0]);
1174 if (!str)
1175 return false;
1177 JSLinearString *linearStr = str->ensureLinear(cx);
1178 if (!linearStr)
1179 return false;
1181 double result;
1182 if (!ParseDate(linearStr, &result, &cx->runtime()->dateTimeInfo)) {
1183 args.rval().setNaN();
1184 return true;
1187 result = TimeClip(result);
1188 args.rval().setNumber(result);
1189 return true;
1192 static inline double
1193 NowAsMillis()
1195 return (double) (PRMJ_Now() / PRMJ_USEC_PER_MSEC);
1198 static bool
1199 date_now(JSContext *cx, unsigned argc, Value *vp)
1201 CallArgs args = CallArgsFromVp(argc, vp);
1202 args.rval().setDouble(NowAsMillis());
1203 return true;
1206 void
1207 DateObject::setUTCTime(double t, Value *vp)
1209 for (size_t ind = COMPONENTS_START_SLOT; ind < RESERVED_SLOTS; ind++)
1210 setReservedSlot(ind, UndefinedValue());
1212 setFixedSlot(UTC_TIME_SLOT, DoubleValue(t));
1213 if (vp)
1214 vp->setDouble(t);
1217 void
1218 DateObject::fillLocalTimeSlots(DateTimeInfo *dtInfo)
1220 /* Check if the cache is already populated. */
1221 if (!getReservedSlot(LOCAL_TIME_SLOT).isUndefined() &&
1222 getReservedSlot(TZA_SLOT).toDouble() == dtInfo->localTZA())
1224 return;
1227 /* Remember timezone used to generate the local cache. */
1228 setReservedSlot(TZA_SLOT, DoubleValue(dtInfo->localTZA()));
1230 double utcTime = UTCTime().toNumber();
1232 if (!IsFinite(utcTime)) {
1233 for (size_t ind = COMPONENTS_START_SLOT; ind < RESERVED_SLOTS; ind++)
1234 setReservedSlot(ind, DoubleValue(utcTime));
1235 return;
1238 double localTime = LocalTime(utcTime, dtInfo);
1240 setReservedSlot(LOCAL_TIME_SLOT, DoubleValue(localTime));
1242 int year = (int) floor(localTime /(msPerDay * 365.2425)) + 1970;
1243 double yearStartTime = TimeFromYear(year);
1245 /* Adjust the year in case the approximation was wrong, as in YearFromTime. */
1246 int yearDays;
1247 if (yearStartTime > localTime) {
1248 year--;
1249 yearStartTime -= (msPerDay * DaysInYear(year));
1250 yearDays = DaysInYear(year);
1251 } else {
1252 yearDays = DaysInYear(year);
1253 double nextStart = yearStartTime + (msPerDay * yearDays);
1254 if (nextStart <= localTime) {
1255 year++;
1256 yearStartTime = nextStart;
1257 yearDays = DaysInYear(year);
1261 setReservedSlot(LOCAL_YEAR_SLOT, Int32Value(year));
1263 uint64_t yearTime = uint64_t(localTime - yearStartTime);
1264 int yearSeconds = uint32_t(yearTime / 1000);
1266 int day = yearSeconds / int(SecondsPerDay);
1268 int step = -1, next = 30;
1269 int month;
1271 do {
1272 if (day <= next) {
1273 month = 0;
1274 break;
1276 step = next;
1277 next += ((yearDays == 366) ? 29 : 28);
1278 if (day <= next) {
1279 month = 1;
1280 break;
1282 step = next;
1283 if (day <= (next += 31)) {
1284 month = 2;
1285 break;
1287 step = next;
1288 if (day <= (next += 30)) {
1289 month = 3;
1290 break;
1292 step = next;
1293 if (day <= (next += 31)) {
1294 month = 4;
1295 break;
1297 step = next;
1298 if (day <= (next += 30)) {
1299 month = 5;
1300 break;
1302 step = next;
1303 if (day <= (next += 31)) {
1304 month = 6;
1305 break;
1307 step = next;
1308 if (day <= (next += 31)) {
1309 month = 7;
1310 break;
1312 step = next;
1313 if (day <= (next += 30)) {
1314 month = 8;
1315 break;
1317 step = next;
1318 if (day <= (next += 31)) {
1319 month = 9;
1320 break;
1322 step = next;
1323 if (day <= (next += 30)) {
1324 month = 10;
1325 break;
1327 step = next;
1328 month = 11;
1329 } while (0);
1331 setReservedSlot(LOCAL_MONTH_SLOT, Int32Value(month));
1332 setReservedSlot(LOCAL_DATE_SLOT, Int32Value(day - step));
1334 int weekday = WeekDay(localTime);
1335 setReservedSlot(LOCAL_DAY_SLOT, Int32Value(weekday));
1337 int seconds = yearSeconds % 60;
1338 setReservedSlot(LOCAL_SECONDS_SLOT, Int32Value(seconds));
1340 int minutes = (yearSeconds / 60) % 60;
1341 setReservedSlot(LOCAL_MINUTES_SLOT, Int32Value(minutes));
1343 int hours = (yearSeconds / (60 * 60)) % 24;
1344 setReservedSlot(LOCAL_HOURS_SLOT, Int32Value(hours));
1347 inline double
1348 DateObject::cachedLocalTime(DateTimeInfo *dtInfo)
1350 fillLocalTimeSlots(dtInfo);
1351 return getReservedSlot(LOCAL_TIME_SLOT).toDouble();
1354 MOZ_ALWAYS_INLINE bool
1355 IsDate(HandleValue v)
1357 return v.isObject() && v.toObject().is<DateObject>();
1361 * See ECMA 15.9.5.4 thru 15.9.5.23
1363 /* static */ MOZ_ALWAYS_INLINE bool
1364 DateObject::getTime_impl(JSContext *cx, CallArgs args)
1366 args.rval().set(args.thisv().toObject().as<DateObject>().UTCTime());
1367 return true;
1370 static bool
1371 date_getTime(JSContext *cx, unsigned argc, Value *vp)
1373 CallArgs args = CallArgsFromVp(argc, vp);
1374 return CallNonGenericMethod<IsDate, DateObject::getTime_impl>(cx, args);
1377 /* static */ MOZ_ALWAYS_INLINE bool
1378 DateObject::getYear_impl(JSContext *cx, CallArgs args)
1380 DateObject *dateObj = &args.thisv().toObject().as<DateObject>();
1381 dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo);
1383 Value yearVal = dateObj->getReservedSlot(LOCAL_YEAR_SLOT);
1384 if (yearVal.isInt32()) {
1385 /* Follow ECMA-262 to the letter, contrary to IE JScript. */
1386 int year = yearVal.toInt32() - 1900;
1387 args.rval().setInt32(year);
1388 } else {
1389 args.rval().set(yearVal);
1392 return true;
1395 static bool
1396 date_getYear(JSContext *cx, unsigned argc, Value *vp)
1398 CallArgs args = CallArgsFromVp(argc, vp);
1399 return CallNonGenericMethod<IsDate, DateObject::getYear_impl>(cx, args);
1402 /* static */ MOZ_ALWAYS_INLINE bool
1403 DateObject::getFullYear_impl(JSContext *cx, CallArgs args)
1405 DateObject *dateObj = &args.thisv().toObject().as<DateObject>();
1406 dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo);
1408 args.rval().set(dateObj->getReservedSlot(LOCAL_YEAR_SLOT));
1409 return true;
1412 static bool
1413 date_getFullYear(JSContext *cx, unsigned argc, Value *vp)
1415 CallArgs args = CallArgsFromVp(argc, vp);
1416 return CallNonGenericMethod<IsDate, DateObject::getFullYear_impl>(cx, args);
1419 /* static */ MOZ_ALWAYS_INLINE bool
1420 DateObject::getUTCFullYear_impl(JSContext *cx, CallArgs args)
1422 double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
1423 if (IsFinite(result))
1424 result = YearFromTime(result);
1426 args.rval().setNumber(result);
1427 return true;
1430 static bool
1431 date_getUTCFullYear(JSContext *cx, unsigned argc, Value *vp)
1433 CallArgs args = CallArgsFromVp(argc, vp);
1434 return CallNonGenericMethod<IsDate, DateObject::getUTCFullYear_impl>(cx, args);
1437 /* static */ MOZ_ALWAYS_INLINE bool
1438 DateObject::getMonth_impl(JSContext *cx, CallArgs args)
1440 DateObject *dateObj = &args.thisv().toObject().as<DateObject>();
1441 dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo);
1443 args.rval().set(dateObj->getReservedSlot(LOCAL_MONTH_SLOT));
1444 return true;
1447 static bool
1448 date_getMonth(JSContext *cx, unsigned argc, Value *vp)
1450 CallArgs args = CallArgsFromVp(argc, vp);
1451 return CallNonGenericMethod<IsDate, DateObject::getMonth_impl>(cx, args);
1454 /* static */ MOZ_ALWAYS_INLINE bool
1455 DateObject::getUTCMonth_impl(JSContext *cx, CallArgs args)
1457 double d = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
1458 args.rval().setNumber(MonthFromTime(d));
1459 return true;
1462 static bool
1463 date_getUTCMonth(JSContext *cx, unsigned argc, Value *vp)
1465 CallArgs args = CallArgsFromVp(argc, vp);
1466 return CallNonGenericMethod<IsDate, DateObject::getUTCMonth_impl>(cx, args);
1469 /* static */ MOZ_ALWAYS_INLINE bool
1470 DateObject::getDate_impl(JSContext *cx, CallArgs args)
1472 DateObject *dateObj = &args.thisv().toObject().as<DateObject>();
1473 dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo);
1475 args.rval().set(dateObj->getReservedSlot(LOCAL_DATE_SLOT));
1476 return true;
1479 static bool
1480 date_getDate(JSContext *cx, unsigned argc, Value *vp)
1482 CallArgs args = CallArgsFromVp(argc, vp);
1483 return CallNonGenericMethod<IsDate, DateObject::getDate_impl>(cx, args);
1486 /* static */ MOZ_ALWAYS_INLINE bool
1487 DateObject::getUTCDate_impl(JSContext *cx, CallArgs args)
1489 double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
1490 if (IsFinite(result))
1491 result = DateFromTime(result);
1493 args.rval().setNumber(result);
1494 return true;
1497 static bool
1498 date_getUTCDate(JSContext *cx, unsigned argc, Value *vp)
1500 CallArgs args = CallArgsFromVp(argc, vp);
1501 return CallNonGenericMethod<IsDate, DateObject::getUTCDate_impl>(cx, args);
1504 /* static */ MOZ_ALWAYS_INLINE bool
1505 DateObject::getDay_impl(JSContext *cx, CallArgs args)
1507 DateObject *dateObj = &args.thisv().toObject().as<DateObject>();
1508 dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo);
1510 args.rval().set(dateObj->getReservedSlot(LOCAL_DAY_SLOT));
1511 return true;
1514 static bool
1515 date_getDay(JSContext *cx, unsigned argc, Value *vp)
1517 CallArgs args = CallArgsFromVp(argc, vp);
1518 return CallNonGenericMethod<IsDate, DateObject::getDay_impl>(cx, args);
1521 /* static */ MOZ_ALWAYS_INLINE bool
1522 DateObject::getUTCDay_impl(JSContext *cx, CallArgs args)
1524 double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
1525 if (IsFinite(result))
1526 result = WeekDay(result);
1528 args.rval().setNumber(result);
1529 return true;
1532 static bool
1533 date_getUTCDay(JSContext *cx, unsigned argc, Value *vp)
1535 CallArgs args = CallArgsFromVp(argc, vp);
1536 return CallNonGenericMethod<IsDate, DateObject::getUTCDay_impl>(cx, args);
1539 /* static */ MOZ_ALWAYS_INLINE bool
1540 DateObject::getHours_impl(JSContext *cx, CallArgs args)
1542 DateObject *dateObj = &args.thisv().toObject().as<DateObject>();
1543 dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo);
1545 args.rval().set(dateObj->getReservedSlot(LOCAL_HOURS_SLOT));
1546 return true;
1549 static bool
1550 date_getHours(JSContext *cx, unsigned argc, Value *vp)
1552 CallArgs args = CallArgsFromVp(argc, vp);
1553 return CallNonGenericMethod<IsDate, DateObject::getHours_impl>(cx, args);
1556 /* static */ MOZ_ALWAYS_INLINE bool
1557 DateObject::getUTCHours_impl(JSContext *cx, CallArgs args)
1559 double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
1560 if (IsFinite(result))
1561 result = HourFromTime(result);
1563 args.rval().setNumber(result);
1564 return true;
1567 static bool
1568 date_getUTCHours(JSContext *cx, unsigned argc, Value *vp)
1570 CallArgs args = CallArgsFromVp(argc, vp);
1571 return CallNonGenericMethod<IsDate, DateObject::getUTCHours_impl>(cx, args);
1574 /* static */ MOZ_ALWAYS_INLINE bool
1575 DateObject::getMinutes_impl(JSContext *cx, CallArgs args)
1577 DateObject *dateObj = &args.thisv().toObject().as<DateObject>();
1578 dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo);
1580 args.rval().set(dateObj->getReservedSlot(LOCAL_MINUTES_SLOT));
1581 return true;
1584 static bool
1585 date_getMinutes(JSContext *cx, unsigned argc, Value *vp)
1587 CallArgs args = CallArgsFromVp(argc, vp);
1588 return CallNonGenericMethod<IsDate, DateObject::getMinutes_impl>(cx, args);
1591 /* static */ MOZ_ALWAYS_INLINE bool
1592 DateObject::getUTCMinutes_impl(JSContext *cx, CallArgs args)
1594 double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
1595 if (IsFinite(result))
1596 result = MinFromTime(result);
1598 args.rval().setNumber(result);
1599 return true;
1602 static bool
1603 date_getUTCMinutes(JSContext *cx, unsigned argc, Value *vp)
1605 CallArgs args = CallArgsFromVp(argc, vp);
1606 return CallNonGenericMethod<IsDate, DateObject::getUTCMinutes_impl>(cx, args);
1609 /* Date.getSeconds is mapped to getUTCSeconds */
1611 /* static */ MOZ_ALWAYS_INLINE bool
1612 DateObject::getUTCSeconds_impl(JSContext *cx, CallArgs args)
1614 DateObject *dateObj = &args.thisv().toObject().as<DateObject>();
1615 dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo);
1617 args.rval().set(dateObj->getReservedSlot(LOCAL_SECONDS_SLOT));
1618 return true;
1621 static bool
1622 date_getUTCSeconds(JSContext *cx, unsigned argc, Value *vp)
1624 CallArgs args = CallArgsFromVp(argc, vp);
1625 return CallNonGenericMethod<IsDate, DateObject::getUTCSeconds_impl>(cx, args);
1628 /* Date.getMilliseconds is mapped to getUTCMilliseconds */
1630 /* static */ MOZ_ALWAYS_INLINE bool
1631 DateObject::getUTCMilliseconds_impl(JSContext *cx, CallArgs args)
1633 double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
1634 if (IsFinite(result))
1635 result = msFromTime(result);
1637 args.rval().setNumber(result);
1638 return true;
1641 static bool
1642 date_getUTCMilliseconds(JSContext *cx, unsigned argc, Value *vp)
1644 CallArgs args = CallArgsFromVp(argc, vp);
1645 return CallNonGenericMethod<IsDate, DateObject::getUTCMilliseconds_impl>(cx, args);
1648 /* static */ MOZ_ALWAYS_INLINE bool
1649 DateObject::getTimezoneOffset_impl(JSContext *cx, CallArgs args)
1651 DateObject *dateObj = &args.thisv().toObject().as<DateObject>();
1652 double utctime = dateObj->UTCTime().toNumber();
1653 double localtime = dateObj->cachedLocalTime(&cx->runtime()->dateTimeInfo);
1656 * Return the time zone offset in minutes for the current locale that is
1657 * appropriate for this time. This value would be a constant except for
1658 * daylight savings time.
1660 double result = (utctime - localtime) / msPerMinute;
1661 args.rval().setNumber(result);
1662 return true;
1665 static bool
1666 date_getTimezoneOffset(JSContext *cx, unsigned argc, Value *vp)
1668 CallArgs args = CallArgsFromVp(argc, vp);
1669 return CallNonGenericMethod<IsDate, DateObject::getTimezoneOffset_impl>(cx, args);
1672 MOZ_ALWAYS_INLINE bool
1673 date_setTime_impl(JSContext *cx, CallArgs args)
1675 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1676 if (args.length() == 0) {
1677 dateObj->setUTCTime(GenericNaN(), args.rval().address());
1678 return true;
1681 double result;
1682 if (!ToNumber(cx, args[0], &result))
1683 return false;
1685 dateObj->setUTCTime(TimeClip(result), args.rval().address());
1686 return true;
1689 static bool
1690 date_setTime(JSContext *cx, unsigned argc, Value *vp)
1692 CallArgs args = CallArgsFromVp(argc, vp);
1693 return CallNonGenericMethod<IsDate, date_setTime_impl>(cx, args);
1696 static bool
1697 GetMsecsOrDefault(JSContext *cx, const CallArgs &args, unsigned i, double t, double *millis)
1699 if (args.length() <= i) {
1700 *millis = msFromTime(t);
1701 return true;
1703 return ToNumber(cx, args[i], millis);
1706 static bool
1707 GetSecsOrDefault(JSContext *cx, const CallArgs &args, unsigned i, double t, double *sec)
1709 if (args.length() <= i) {
1710 *sec = SecFromTime(t);
1711 return true;
1713 return ToNumber(cx, args[i], sec);
1716 static bool
1717 GetMinsOrDefault(JSContext *cx, const CallArgs &args, unsigned i, double t, double *mins)
1719 if (args.length() <= i) {
1720 *mins = MinFromTime(t);
1721 return true;
1723 return ToNumber(cx, args[i], mins);
1726 /* ES5 15.9.5.28. */
1727 MOZ_ALWAYS_INLINE bool
1728 date_setMilliseconds_impl(JSContext *cx, CallArgs args)
1730 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1732 /* Step 1. */
1733 double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo);
1735 /* Step 2. */
1736 double milli;
1737 if (!ToNumber(cx, args.get(0), &milli))
1738 return false;
1739 double time = MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), milli);
1741 /* Step 3. */
1742 double u = TimeClip(UTC(MakeDate(Day(t), time), &cx->runtime()->dateTimeInfo));
1744 /* Steps 4-5. */
1745 dateObj->setUTCTime(u, args.rval().address());
1746 return true;
1749 static bool
1750 date_setMilliseconds(JSContext *cx, unsigned argc, Value *vp)
1752 CallArgs args = CallArgsFromVp(argc, vp);
1753 return CallNonGenericMethod<IsDate, date_setMilliseconds_impl>(cx, args);
1756 /* ES5 15.9.5.29. */
1757 MOZ_ALWAYS_INLINE bool
1758 date_setUTCMilliseconds_impl(JSContext *cx, CallArgs args)
1760 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1762 /* Step 1. */
1763 double t = dateObj->UTCTime().toNumber();
1765 /* Step 2. */
1766 double milli;
1767 if (!ToNumber(cx, args.get(0), &milli))
1768 return false;
1769 double time = MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), milli);
1771 /* Step 3. */
1772 double v = TimeClip(MakeDate(Day(t), time));
1774 /* Steps 4-5. */
1775 dateObj->setUTCTime(v, args.rval().address());
1776 return true;
1779 static bool
1780 date_setUTCMilliseconds(JSContext *cx, unsigned argc, Value *vp)
1782 CallArgs args = CallArgsFromVp(argc, vp);
1783 return CallNonGenericMethod<IsDate, date_setUTCMilliseconds_impl>(cx, args);
1786 /* ES5 15.9.5.30. */
1787 MOZ_ALWAYS_INLINE bool
1788 date_setSeconds_impl(JSContext *cx, CallArgs args)
1790 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1792 /* Step 1. */
1793 double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo);
1795 /* Step 2. */
1796 double s;
1797 if (!ToNumber(cx, args.get(0), &s))
1798 return false;
1800 /* Step 3. */
1801 double milli;
1802 if (!GetMsecsOrDefault(cx, args, 1, t, &milli))
1803 return false;
1805 /* Step 4. */
1806 double date = MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli));
1808 /* Step 5. */
1809 double u = TimeClip(UTC(date, &cx->runtime()->dateTimeInfo));
1811 /* Steps 6-7. */
1812 dateObj->setUTCTime(u, args.rval().address());
1813 return true;
1816 /* ES5 15.9.5.31. */
1817 static bool
1818 date_setSeconds(JSContext *cx, unsigned argc, Value *vp)
1820 CallArgs args = CallArgsFromVp(argc, vp);
1821 return CallNonGenericMethod<IsDate, date_setSeconds_impl>(cx, args);
1824 MOZ_ALWAYS_INLINE bool
1825 date_setUTCSeconds_impl(JSContext *cx, CallArgs args)
1827 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1829 /* Step 1. */
1830 double t = dateObj->UTCTime().toNumber();
1832 /* Step 2. */
1833 double s;
1834 if (!ToNumber(cx, args.get(0), &s))
1835 return false;
1837 /* Step 3. */
1838 double milli;
1839 if (!GetMsecsOrDefault(cx, args, 1, t, &milli))
1840 return false;
1842 /* Step 4. */
1843 double date = MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli));
1845 /* Step 5. */
1846 double v = TimeClip(date);
1848 /* Steps 6-7. */
1849 dateObj->setUTCTime(v, args.rval().address());
1850 return true;
1853 /* ES5 15.9.5.32. */
1854 static bool
1855 date_setUTCSeconds(JSContext *cx, unsigned argc, Value *vp)
1857 CallArgs args = CallArgsFromVp(argc, vp);
1858 return CallNonGenericMethod<IsDate, date_setUTCSeconds_impl>(cx, args);
1861 MOZ_ALWAYS_INLINE bool
1862 date_setMinutes_impl(JSContext *cx, CallArgs args)
1864 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1866 /* Step 1. */
1867 double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo);
1869 /* Step 2. */
1870 double m;
1871 if (!ToNumber(cx, args.get(0), &m))
1872 return false;
1874 /* Step 3. */
1875 double s;
1876 if (!GetSecsOrDefault(cx, args, 1, t, &s))
1877 return false;
1879 /* Step 4. */
1880 double milli;
1881 if (!GetMsecsOrDefault(cx, args, 2, t, &milli))
1882 return false;
1884 /* Step 5. */
1885 double date = MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli));
1887 /* Step 6. */
1888 double u = TimeClip(UTC(date, &cx->runtime()->dateTimeInfo));
1890 /* Steps 7-8. */
1891 dateObj->setUTCTime(u, args.rval().address());
1892 return true;
1895 /* ES5 15.9.5.33. */
1896 static bool
1897 date_setMinutes(JSContext *cx, unsigned argc, Value *vp)
1899 CallArgs args = CallArgsFromVp(argc, vp);
1900 return CallNonGenericMethod<IsDate, date_setMinutes_impl>(cx, args);
1903 MOZ_ALWAYS_INLINE bool
1904 date_setUTCMinutes_impl(JSContext *cx, CallArgs args)
1906 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1908 /* Step 1. */
1909 double t = dateObj->UTCTime().toNumber();
1911 /* Step 2. */
1912 double m;
1913 if (!ToNumber(cx, args.get(0), &m))
1914 return false;
1916 /* Step 3. */
1917 double s;
1918 if (!GetSecsOrDefault(cx, args, 1, t, &s))
1919 return false;
1921 /* Step 4. */
1922 double milli;
1923 if (!GetMsecsOrDefault(cx, args, 2, t, &milli))
1924 return false;
1926 /* Step 5. */
1927 double date = MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli));
1929 /* Step 6. */
1930 double v = TimeClip(date);
1932 /* Steps 7-8. */
1933 dateObj->setUTCTime(v, args.rval().address());
1934 return true;
1937 /* ES5 15.9.5.34. */
1938 static bool
1939 date_setUTCMinutes(JSContext *cx, unsigned argc, Value *vp)
1941 CallArgs args = CallArgsFromVp(argc, vp);
1942 return CallNonGenericMethod<IsDate, date_setUTCMinutes_impl>(cx, args);
1945 MOZ_ALWAYS_INLINE bool
1946 date_setHours_impl(JSContext *cx, CallArgs args)
1948 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1950 /* Step 1. */
1951 double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo);
1953 /* Step 2. */
1954 double h;
1955 if (!ToNumber(cx, args.get(0), &h))
1956 return false;
1958 /* Step 3. */
1959 double m;
1960 if (!GetMinsOrDefault(cx, args, 1, t, &m))
1961 return false;
1963 /* Step 4. */
1964 double s;
1965 if (!GetSecsOrDefault(cx, args, 2, t, &s))
1966 return false;
1968 /* Step 5. */
1969 double milli;
1970 if (!GetMsecsOrDefault(cx, args, 3, t, &milli))
1971 return false;
1973 /* Step 6. */
1974 double date = MakeDate(Day(t), MakeTime(h, m, s, milli));
1976 /* Step 6. */
1977 double u = TimeClip(UTC(date, &cx->runtime()->dateTimeInfo));
1979 /* Steps 7-8. */
1980 dateObj->setUTCTime(u, args.rval().address());
1981 return true;
1984 /* ES5 15.9.5.35. */
1985 static bool
1986 date_setHours(JSContext *cx, unsigned argc, Value *vp)
1988 CallArgs args = CallArgsFromVp(argc, vp);
1989 return CallNonGenericMethod<IsDate, date_setHours_impl>(cx, args);
1992 MOZ_ALWAYS_INLINE bool
1993 date_setUTCHours_impl(JSContext *cx, CallArgs args)
1995 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1997 /* Step 1. */
1998 double t = dateObj->UTCTime().toNumber();
2000 /* Step 2. */
2001 double h;
2002 if (!ToNumber(cx, args.get(0), &h))
2003 return false;
2005 /* Step 3. */
2006 double m;
2007 if (!GetMinsOrDefault(cx, args, 1, t, &m))
2008 return false;
2010 /* Step 4. */
2011 double s;
2012 if (!GetSecsOrDefault(cx, args, 2, t, &s))
2013 return false;
2015 /* Step 5. */
2016 double milli;
2017 if (!GetMsecsOrDefault(cx, args, 3, t, &milli))
2018 return false;
2020 /* Step 6. */
2021 double newDate = MakeDate(Day(t), MakeTime(h, m, s, milli));
2023 /* Step 7. */
2024 double v = TimeClip(newDate);
2026 /* Steps 8-9. */
2027 dateObj->setUTCTime(v, args.rval().address());
2028 return true;
2031 /* ES5 15.9.5.36. */
2032 static bool
2033 date_setUTCHours(JSContext *cx, unsigned argc, Value *vp)
2035 CallArgs args = CallArgsFromVp(argc, vp);
2036 return CallNonGenericMethod<IsDate, date_setUTCHours_impl>(cx, args);
2039 MOZ_ALWAYS_INLINE bool
2040 date_setDate_impl(JSContext *cx, CallArgs args)
2042 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2044 /* Step 1. */
2045 double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo);
2047 /* Step 2. */
2048 double date;
2049 if (!ToNumber(cx, args.get(0), &date))
2050 return false;
2052 /* Step 3. */
2053 double newDate = MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t));
2055 /* Step 4. */
2056 double u = TimeClip(UTC(newDate, &cx->runtime()->dateTimeInfo));
2058 /* Steps 5-6. */
2059 dateObj->setUTCTime(u, args.rval().address());
2060 return true;
2063 /* ES5 15.9.5.37. */
2064 static bool
2065 date_setDate(JSContext *cx, unsigned argc, Value *vp)
2067 CallArgs args = CallArgsFromVp(argc, vp);
2068 return CallNonGenericMethod<IsDate, date_setDate_impl>(cx, args);
2071 MOZ_ALWAYS_INLINE bool
2072 date_setUTCDate_impl(JSContext *cx, CallArgs args)
2074 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2076 /* Step 1. */
2077 double t = dateObj->UTCTime().toNumber();
2079 /* Step 2. */
2080 double date;
2081 if (!ToNumber(cx, args.get(0), &date))
2082 return false;
2084 /* Step 3. */
2085 double newDate = MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t));
2087 /* Step 4. */
2088 double v = TimeClip(newDate);
2090 /* Steps 5-6. */
2091 dateObj->setUTCTime(v, args.rval().address());
2092 return true;
2095 static bool
2096 date_setUTCDate(JSContext *cx, unsigned argc, Value *vp)
2098 CallArgs args = CallArgsFromVp(argc, vp);
2099 return CallNonGenericMethod<IsDate, date_setUTCDate_impl>(cx, args);
2102 static bool
2103 GetDateOrDefault(JSContext *cx, const CallArgs &args, unsigned i, double t, double *date)
2105 if (args.length() <= i) {
2106 *date = DateFromTime(t);
2107 return true;
2109 return ToNumber(cx, args[i], date);
2112 static bool
2113 GetMonthOrDefault(JSContext *cx, const CallArgs &args, unsigned i, double t, double *month)
2115 if (args.length() <= i) {
2116 *month = MonthFromTime(t);
2117 return true;
2119 return ToNumber(cx, args[i], month);
2122 /* ES5 15.9.5.38. */
2123 MOZ_ALWAYS_INLINE bool
2124 date_setMonth_impl(JSContext *cx, CallArgs args)
2126 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2128 /* Step 1. */
2129 double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo);
2131 /* Step 2. */
2132 double m;
2133 if (!ToNumber(cx, args.get(0), &m))
2134 return false;
2136 /* Step 3. */
2137 double date;
2138 if (!GetDateOrDefault(cx, args, 1, t, &date))
2139 return false;
2141 /* Step 4. */
2142 double newDate = MakeDate(MakeDay(YearFromTime(t), m, date), TimeWithinDay(t));
2144 /* Step 5. */
2145 double u = TimeClip(UTC(newDate, &cx->runtime()->dateTimeInfo));
2147 /* Steps 6-7. */
2148 dateObj->setUTCTime(u, args.rval().address());
2149 return true;
2152 static bool
2153 date_setMonth(JSContext *cx, unsigned argc, Value *vp)
2155 CallArgs args = CallArgsFromVp(argc, vp);
2156 return CallNonGenericMethod<IsDate, date_setMonth_impl>(cx, args);
2159 /* ES5 15.9.5.39. */
2160 MOZ_ALWAYS_INLINE bool
2161 date_setUTCMonth_impl(JSContext *cx, CallArgs args)
2163 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2165 /* Step 1. */
2166 double t = dateObj->UTCTime().toNumber();
2168 /* Step 2. */
2169 double m;
2170 if (!ToNumber(cx, args.get(0), &m))
2171 return false;
2173 /* Step 3. */
2174 double date;
2175 if (!GetDateOrDefault(cx, args, 1, t, &date))
2176 return false;
2178 /* Step 4. */
2179 double newDate = MakeDate(MakeDay(YearFromTime(t), m, date), TimeWithinDay(t));
2181 /* Step 5. */
2182 double v = TimeClip(newDate);
2184 /* Steps 6-7. */
2185 dateObj->setUTCTime(v, args.rval().address());
2186 return true;
2189 static bool
2190 date_setUTCMonth(JSContext *cx, unsigned argc, Value *vp)
2192 CallArgs args = CallArgsFromVp(argc, vp);
2193 return CallNonGenericMethod<IsDate, date_setUTCMonth_impl>(cx, args);
2196 static double
2197 ThisLocalTimeOrZero(Handle<DateObject*> dateObj, DateTimeInfo *dtInfo)
2199 double t = dateObj->UTCTime().toNumber();
2200 if (IsNaN(t))
2201 return +0;
2202 return LocalTime(t, dtInfo);
2205 static double
2206 ThisUTCTimeOrZero(Handle<DateObject*> dateObj)
2208 double t = dateObj->as<DateObject>().UTCTime().toNumber();
2209 return IsNaN(t) ? +0 : t;
2212 /* ES5 15.9.5.40. */
2213 MOZ_ALWAYS_INLINE bool
2214 date_setFullYear_impl(JSContext *cx, CallArgs args)
2216 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2218 /* Step 1. */
2219 double t = ThisLocalTimeOrZero(dateObj, &cx->runtime()->dateTimeInfo);
2221 /* Step 2. */
2222 double y;
2223 if (!ToNumber(cx, args.get(0), &y))
2224 return false;
2226 /* Step 3. */
2227 double m;
2228 if (!GetMonthOrDefault(cx, args, 1, t, &m))
2229 return false;
2231 /* Step 4. */
2232 double date;
2233 if (!GetDateOrDefault(cx, args, 2, t, &date))
2234 return false;
2236 /* Step 5. */
2237 double newDate = MakeDate(MakeDay(y, m, date), TimeWithinDay(t));
2239 /* Step 6. */
2240 double u = TimeClip(UTC(newDate, &cx->runtime()->dateTimeInfo));
2242 /* Steps 7-8. */
2243 dateObj->setUTCTime(u, args.rval().address());
2244 return true;
2247 static bool
2248 date_setFullYear(JSContext *cx, unsigned argc, Value *vp)
2250 CallArgs args = CallArgsFromVp(argc, vp);
2251 return CallNonGenericMethod<IsDate, date_setFullYear_impl>(cx, args);
2254 /* ES5 15.9.5.41. */
2255 MOZ_ALWAYS_INLINE bool
2256 date_setUTCFullYear_impl(JSContext *cx, CallArgs args)
2258 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2260 /* Step 1. */
2261 double t = ThisUTCTimeOrZero(dateObj);
2263 /* Step 2. */
2264 double y;
2265 if (!ToNumber(cx, args.get(0), &y))
2266 return false;
2268 /* Step 3. */
2269 double m;
2270 if (!GetMonthOrDefault(cx, args, 1, t, &m))
2271 return false;
2273 /* Step 4. */
2274 double date;
2275 if (!GetDateOrDefault(cx, args, 2, t, &date))
2276 return false;
2278 /* Step 5. */
2279 double newDate = MakeDate(MakeDay(y, m, date), TimeWithinDay(t));
2281 /* Step 6. */
2282 double v = TimeClip(newDate);
2284 /* Steps 7-8. */
2285 dateObj->setUTCTime(v, args.rval().address());
2286 return true;
2289 static bool
2290 date_setUTCFullYear(JSContext *cx, unsigned argc, Value *vp)
2292 CallArgs args = CallArgsFromVp(argc, vp);
2293 return CallNonGenericMethod<IsDate, date_setUTCFullYear_impl>(cx, args);
2296 /* ES5 Annex B.2.5. */
2297 MOZ_ALWAYS_INLINE bool
2298 date_setYear_impl(JSContext *cx, CallArgs args)
2300 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2302 /* Step 1. */
2303 double t = ThisLocalTimeOrZero(dateObj, &cx->runtime()->dateTimeInfo);
2305 /* Step 2. */
2306 double y;
2307 if (!ToNumber(cx, args.get(0), &y))
2308 return false;
2310 /* Step 3. */
2311 if (IsNaN(y)) {
2312 dateObj->setUTCTime(GenericNaN(), args.rval().address());
2313 return true;
2316 /* Step 4. */
2317 double yint = ToInteger(y);
2318 if (0 <= yint && yint <= 99)
2319 yint += 1900;
2321 /* Step 5. */
2322 double day = MakeDay(yint, MonthFromTime(t), DateFromTime(t));
2324 /* Step 6. */
2325 double u = UTC(MakeDate(day, TimeWithinDay(t)), &cx->runtime()->dateTimeInfo);
2327 /* Steps 7-8. */
2328 dateObj->setUTCTime(TimeClip(u), args.rval().address());
2329 return true;
2332 static bool
2333 date_setYear(JSContext *cx, unsigned argc, Value *vp)
2335 CallArgs args = CallArgsFromVp(argc, vp);
2336 return CallNonGenericMethod<IsDate, date_setYear_impl>(cx, args);
2339 /* constants for toString, toUTCString */
2340 static const char js_NaN_date_str[] = "Invalid Date";
2341 static const char * const days[] =
2343 "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
2345 static const char * const months[] =
2347 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
2351 // Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
2352 // requires a PRMJTime... which only has 16-bit years. Sub-ECMA.
2353 static void
2354 print_gmt_string(char* buf, size_t size, double utctime)
2356 JS_ASSERT(TimeClip(utctime) == utctime);
2357 JS_snprintf(buf, size, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
2358 days[int(WeekDay(utctime))],
2359 int(DateFromTime(utctime)),
2360 months[int(MonthFromTime(utctime))],
2361 int(YearFromTime(utctime)),
2362 int(HourFromTime(utctime)),
2363 int(MinFromTime(utctime)),
2364 int(SecFromTime(utctime)));
2367 static void
2368 print_iso_string(char* buf, size_t size, double utctime)
2370 JS_ASSERT(TimeClip(utctime) == utctime);
2371 JS_snprintf(buf, size, "%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.3dZ",
2372 int(YearFromTime(utctime)),
2373 int(MonthFromTime(utctime)) + 1,
2374 int(DateFromTime(utctime)),
2375 int(HourFromTime(utctime)),
2376 int(MinFromTime(utctime)),
2377 int(SecFromTime(utctime)),
2378 int(msFromTime(utctime)));
2381 /* ES5 B.2.6. */
2382 MOZ_ALWAYS_INLINE bool
2383 date_toGMTString_impl(JSContext *cx, CallArgs args)
2385 double utctime = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
2387 char buf[100];
2388 if (!IsFinite(utctime))
2389 JS_snprintf(buf, sizeof buf, js_NaN_date_str);
2390 else
2391 print_gmt_string(buf, sizeof buf, utctime);
2393 JSString *str = JS_NewStringCopyZ(cx, buf);
2394 if (!str)
2395 return false;
2396 args.rval().setString(str);
2397 return true;
2400 /* ES5 15.9.5.43. */
2401 static bool
2402 date_toGMTString(JSContext *cx, unsigned argc, Value *vp)
2404 CallArgs args = CallArgsFromVp(argc, vp);
2405 return CallNonGenericMethod<IsDate, date_toGMTString_impl>(cx, args);
2408 MOZ_ALWAYS_INLINE bool
2409 date_toISOString_impl(JSContext *cx, CallArgs args)
2411 double utctime = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
2412 if (!IsFinite(utctime)) {
2413 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INVALID_DATE);
2414 return false;
2417 char buf[100];
2418 print_iso_string(buf, sizeof buf, utctime);
2420 JSString *str = JS_NewStringCopyZ(cx, buf);
2421 if (!str)
2422 return false;
2423 args.rval().setString(str);
2424 return true;
2428 static bool
2429 date_toISOString(JSContext *cx, unsigned argc, Value *vp)
2431 CallArgs args = CallArgsFromVp(argc, vp);
2432 return CallNonGenericMethod<IsDate, date_toISOString_impl>(cx, args);
2435 /* ES5 15.9.5.44. */
2436 static bool
2437 date_toJSON(JSContext *cx, unsigned argc, Value *vp)
2439 CallArgs args = CallArgsFromVp(argc, vp);
2441 /* Step 1. */
2442 RootedObject obj(cx, ToObject(cx, args.thisv()));
2443 if (!obj)
2444 return false;
2446 /* Step 2. */
2447 RootedValue tv(cx, ObjectValue(*obj));
2448 if (!ToPrimitive(cx, JSTYPE_NUMBER, &tv))
2449 return false;
2451 /* Step 3. */
2452 if (tv.isDouble() && !IsFinite(tv.toDouble())) {
2453 args.rval().setNull();
2454 return true;
2457 /* Step 4. */
2458 RootedValue toISO(cx);
2459 if (!JSObject::getProperty(cx, obj, obj, cx->names().toISOString, &toISO))
2460 return false;
2462 /* Step 5. */
2463 if (!IsCallable(toISO)) {
2464 JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, nullptr,
2465 JSMSG_BAD_TOISOSTRING_PROP);
2466 return false;
2469 /* Step 6. */
2470 InvokeArgs args2(cx);
2471 if (!args2.init(0))
2472 return false;
2474 args2.setCallee(toISO);
2475 args2.setThis(ObjectValue(*obj));
2477 if (!Invoke(cx, args2))
2478 return false;
2479 args.rval().set(args2.rval());
2480 return true;
2483 /* for Date.toLocaleFormat; interface to PRMJTime date struct.
2485 static void
2486 new_explode(double timeval, PRMJTime *split, DateTimeInfo *dtInfo)
2488 double year = YearFromTime(timeval);
2490 split->tm_usec = int32_t(msFromTime(timeval)) * 1000;
2491 split->tm_sec = int8_t(SecFromTime(timeval));
2492 split->tm_min = int8_t(MinFromTime(timeval));
2493 split->tm_hour = int8_t(HourFromTime(timeval));
2494 split->tm_mday = int8_t(DateFromTime(timeval));
2495 split->tm_mon = int8_t(MonthFromTime(timeval));
2496 split->tm_wday = int8_t(WeekDay(timeval));
2497 split->tm_year = year;
2498 split->tm_yday = int16_t(DayWithinYear(timeval, year));
2500 /* not sure how this affects things, but it doesn't seem
2501 to matter. */
2502 split->tm_isdst = (DaylightSavingTA(timeval, dtInfo) != 0);
2505 typedef enum formatspec {
2506 FORMATSPEC_FULL, FORMATSPEC_DATE, FORMATSPEC_TIME
2507 } formatspec;
2509 /* helper function */
2510 static bool
2511 date_format(JSContext *cx, double date, formatspec format, MutableHandleValue rval)
2513 char buf[100];
2514 char tzbuf[100];
2515 bool usetz;
2516 size_t i, tzlen;
2517 PRMJTime split;
2519 if (!IsFinite(date)) {
2520 JS_snprintf(buf, sizeof buf, js_NaN_date_str);
2521 } else {
2522 JS_ASSERT(TimeClip(date) == date);
2524 double local = LocalTime(date, &cx->runtime()->dateTimeInfo);
2526 /* offset from GMT in minutes. The offset includes daylight savings,
2527 if it applies. */
2528 int minutes = (int) floor(AdjustTime(date, &cx->runtime()->dateTimeInfo) / msPerMinute);
2530 /* map 510 minutes to 0830 hours */
2531 int offset = (minutes / 60) * 100 + minutes % 60;
2533 /* print as "Wed Nov 05 19:38:03 GMT-0800 (PST) 1997" The TZA is
2534 * printed as 'GMT-0800' rather than as 'PST' to avoid
2535 * operating-system dependence on strftime (which
2536 * PRMJ_FormatTimeUSEnglish calls, for %Z only.) win32 prints
2537 * PST as 'Pacific Standard Time.' This way we always know
2538 * what we're getting, and can parse it if we produce it.
2539 * The OS TZA string is included as a comment.
2542 /* get a timezone string from the OS to include as a
2543 comment. */
2544 new_explode(date, &split, &cx->runtime()->dateTimeInfo);
2545 if (PRMJ_FormatTime(tzbuf, sizeof tzbuf, "(%Z)", &split) != 0) {
2547 /* Decide whether to use the resulting timezone string.
2549 * Reject it if it contains any non-ASCII, non-alphanumeric
2550 * characters. It's then likely in some other character
2551 * encoding, and we probably won't display it correctly.
2553 usetz = true;
2554 tzlen = strlen(tzbuf);
2555 if (tzlen > 100) {
2556 usetz = false;
2557 } else {
2558 for (i = 0; i < tzlen; i++) {
2559 jschar c = tzbuf[i];
2560 if (c > 127 ||
2561 !(isalpha(c) || isdigit(c) ||
2562 c == ' ' || c == '(' || c == ')')) {
2563 usetz = false;
2568 /* Also reject it if it's not parenthesized or if it's '()'. */
2569 if (tzbuf[0] != '(' || tzbuf[1] == ')')
2570 usetz = false;
2571 } else
2572 usetz = false;
2574 switch (format) {
2575 case FORMATSPEC_FULL:
2577 * Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
2578 * requires a PRMJTime... which only has 16-bit years. Sub-ECMA.
2580 /* Tue Oct 31 2000 09:41:40 GMT-0800 (PST) */
2581 JS_snprintf(buf, sizeof buf,
2582 "%s %s %.2d %.4d %.2d:%.2d:%.2d GMT%+.4d%s%s",
2583 days[int(WeekDay(local))],
2584 months[int(MonthFromTime(local))],
2585 int(DateFromTime(local)),
2586 int(YearFromTime(local)),
2587 int(HourFromTime(local)),
2588 int(MinFromTime(local)),
2589 int(SecFromTime(local)),
2590 offset,
2591 usetz ? " " : "",
2592 usetz ? tzbuf : "");
2593 break;
2594 case FORMATSPEC_DATE:
2595 /* Tue Oct 31 2000 */
2596 JS_snprintf(buf, sizeof buf,
2597 "%s %s %.2d %.4d",
2598 days[int(WeekDay(local))],
2599 months[int(MonthFromTime(local))],
2600 int(DateFromTime(local)),
2601 int(YearFromTime(local)));
2602 break;
2603 case FORMATSPEC_TIME:
2604 /* 09:41:40 GMT-0800 (PST) */
2605 JS_snprintf(buf, sizeof buf,
2606 "%.2d:%.2d:%.2d GMT%+.4d%s%s",
2607 int(HourFromTime(local)),
2608 int(MinFromTime(local)),
2609 int(SecFromTime(local)),
2610 offset,
2611 usetz ? " " : "",
2612 usetz ? tzbuf : "");
2613 break;
2617 JSString *str = JS_NewStringCopyZ(cx, buf);
2618 if (!str)
2619 return false;
2620 rval.setString(str);
2621 return true;
2624 static bool
2625 ToLocaleFormatHelper(JSContext *cx, HandleObject obj, const char *format, MutableHandleValue rval)
2627 double utctime = obj->as<DateObject>().UTCTime().toNumber();
2629 char buf[100];
2630 if (!IsFinite(utctime)) {
2631 JS_snprintf(buf, sizeof buf, js_NaN_date_str);
2632 } else {
2633 int result_len;
2634 double local = LocalTime(utctime, &cx->runtime()->dateTimeInfo);
2635 PRMJTime split;
2636 new_explode(local, &split, &cx->runtime()->dateTimeInfo);
2638 /* Let PRMJTime format it. */
2639 result_len = PRMJ_FormatTime(buf, sizeof buf, format, &split);
2641 /* If it failed, default to toString. */
2642 if (result_len == 0)
2643 return date_format(cx, utctime, FORMATSPEC_FULL, rval);
2645 /* Hacked check against undesired 2-digit year 00/00/00 form. */
2646 if (strcmp(format, "%x") == 0 && result_len >= 6 &&
2647 /* Format %x means use OS settings, which may have 2-digit yr, so
2648 hack end of 3/11/22 or 11.03.22 or 11Mar22 to use 4-digit yr...*/
2649 !isdigit(buf[result_len - 3]) &&
2650 isdigit(buf[result_len - 2]) && isdigit(buf[result_len - 1]) &&
2651 /* ...but not if starts with 4-digit year, like 2022/3/11. */
2652 !(isdigit(buf[0]) && isdigit(buf[1]) &&
2653 isdigit(buf[2]) && isdigit(buf[3]))) {
2654 JS_snprintf(buf + (result_len - 2), (sizeof buf) - (result_len - 2),
2655 "%d", js_DateGetYear(cx, obj));
2660 if (cx->runtime()->localeCallbacks && cx->runtime()->localeCallbacks->localeToUnicode)
2661 return cx->runtime()->localeCallbacks->localeToUnicode(cx, buf, rval);
2663 JSString *str = JS_NewStringCopyZ(cx, buf);
2664 if (!str)
2665 return false;
2666 rval.setString(str);
2667 return true;
2670 #if !EXPOSE_INTL_API
2671 static bool
2672 ToLocaleStringHelper(JSContext *cx, Handle<DateObject*> dateObj, MutableHandleValue rval)
2675 * Use '%#c' for windows, because '%c' is backward-compatible and non-y2k
2676 * with msvc; '%#c' requests that a full year be used in the result string.
2678 return ToLocaleFormatHelper(cx, dateObj,
2679 #if defined(_WIN32) && !defined(__MWERKS__)
2680 "%#c"
2681 #else
2682 "%c"
2683 #endif
2684 , rval);
2687 /* ES5 15.9.5.5. */
2688 MOZ_ALWAYS_INLINE bool
2689 date_toLocaleString_impl(JSContext *cx, CallArgs args)
2691 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2692 return ToLocaleStringHelper(cx, dateObj, args.rval());
2695 static bool
2696 date_toLocaleString(JSContext *cx, unsigned argc, Value *vp)
2698 CallArgs args = CallArgsFromVp(argc, vp);
2699 return CallNonGenericMethod<IsDate, date_toLocaleString_impl>(cx, args);
2702 /* ES5 15.9.5.6. */
2703 MOZ_ALWAYS_INLINE bool
2704 date_toLocaleDateString_impl(JSContext *cx, CallArgs args)
2707 * Use '%#x' for windows, because '%x' is backward-compatible and non-y2k
2708 * with msvc; '%#x' requests that a full year be used in the result string.
2710 static const char format[] =
2711 #if defined(_WIN32) && !defined(__MWERKS__)
2712 "%#x"
2713 #else
2714 "%x"
2715 #endif
2718 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2719 return ToLocaleFormatHelper(cx, dateObj, format, args.rval());
2722 static bool
2723 date_toLocaleDateString(JSContext *cx, unsigned argc, Value *vp)
2725 CallArgs args = CallArgsFromVp(argc, vp);
2726 return CallNonGenericMethod<IsDate, date_toLocaleDateString_impl>(cx, args);
2729 /* ES5 15.9.5.7. */
2730 MOZ_ALWAYS_INLINE bool
2731 date_toLocaleTimeString_impl(JSContext *cx, CallArgs args)
2733 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2734 return ToLocaleFormatHelper(cx, dateObj, "%X", args.rval());
2737 static bool
2738 date_toLocaleTimeString(JSContext *cx, unsigned argc, Value *vp)
2740 CallArgs args = CallArgsFromVp(argc, vp);
2741 return CallNonGenericMethod<IsDate, date_toLocaleTimeString_impl>(cx, args);
2743 #endif /* !EXPOSE_INTL_API */
2745 MOZ_ALWAYS_INLINE bool
2746 date_toLocaleFormat_impl(JSContext *cx, CallArgs args)
2748 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2750 if (args.length() == 0) {
2752 * Use '%#c' for windows, because '%c' is backward-compatible and non-y2k
2753 * with msvc; '%#c' requests that a full year be used in the result string.
2755 return ToLocaleFormatHelper(cx, dateObj,
2756 #if defined(_WIN32) && !defined(__MWERKS__)
2757 "%#c"
2758 #else
2759 "%c"
2760 #endif
2761 , args.rval());
2764 RootedString fmt(cx, ToString<CanGC>(cx, args[0]));
2765 if (!fmt)
2766 return false;
2768 JSAutoByteString fmtbytes(cx, fmt);
2769 if (!fmtbytes)
2770 return false;
2772 return ToLocaleFormatHelper(cx, dateObj, fmtbytes.ptr(), args.rval());
2775 static bool
2776 date_toLocaleFormat(JSContext *cx, unsigned argc, Value *vp)
2778 CallArgs args = CallArgsFromVp(argc, vp);
2779 return CallNonGenericMethod<IsDate, date_toLocaleFormat_impl>(cx, args);
2782 /* ES5 15.9.5.4. */
2783 MOZ_ALWAYS_INLINE bool
2784 date_toTimeString_impl(JSContext *cx, CallArgs args)
2786 return date_format(cx, args.thisv().toObject().as<DateObject>().UTCTime().toNumber(),
2787 FORMATSPEC_TIME, args.rval());
2790 static bool
2791 date_toTimeString(JSContext *cx, unsigned argc, Value *vp)
2793 CallArgs args = CallArgsFromVp(argc, vp);
2794 return CallNonGenericMethod<IsDate, date_toTimeString_impl>(cx, args);
2797 /* ES5 15.9.5.3. */
2798 MOZ_ALWAYS_INLINE bool
2799 date_toDateString_impl(JSContext *cx, CallArgs args)
2801 return date_format(cx, args.thisv().toObject().as<DateObject>().UTCTime().toNumber(),
2802 FORMATSPEC_DATE, args.rval());
2805 static bool
2806 date_toDateString(JSContext *cx, unsigned argc, Value *vp)
2808 CallArgs args = CallArgsFromVp(argc, vp);
2809 return CallNonGenericMethod<IsDate, date_toDateString_impl>(cx, args);
2812 #if JS_HAS_TOSOURCE
2813 MOZ_ALWAYS_INLINE bool
2814 date_toSource_impl(JSContext *cx, CallArgs args)
2816 StringBuffer sb(cx);
2817 if (!sb.append("(new Date(") ||
2818 !NumberValueToStringBuffer(cx, args.thisv().toObject().as<DateObject>().UTCTime(), sb) ||
2819 !sb.append("))"))
2821 return false;
2824 JSString *str = sb.finishString();
2825 if (!str)
2826 return false;
2827 args.rval().setString(str);
2828 return true;
2831 static bool
2832 date_toSource(JSContext *cx, unsigned argc, Value *vp)
2834 CallArgs args = CallArgsFromVp(argc, vp);
2835 return CallNonGenericMethod<IsDate, date_toSource_impl>(cx, args);
2837 #endif
2839 MOZ_ALWAYS_INLINE bool
2840 date_toString_impl(JSContext *cx, CallArgs args)
2842 return date_format(cx, args.thisv().toObject().as<DateObject>().UTCTime().toNumber(),
2843 FORMATSPEC_FULL, args.rval());
2846 static bool
2847 date_toString(JSContext *cx, unsigned argc, Value *vp)
2849 CallArgs args = CallArgsFromVp(argc, vp);
2850 return CallNonGenericMethod<IsDate, date_toString_impl>(cx, args);
2853 MOZ_ALWAYS_INLINE bool
2854 date_valueOf_impl(JSContext *cx, CallArgs args)
2856 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2857 args.rval().set(dateObj->UTCTime());
2858 return true;
2861 static bool
2862 date_valueOf(JSContext *cx, unsigned argc, Value *vp)
2864 CallArgs args = CallArgsFromVp(argc, vp);
2865 return CallNonGenericMethod<IsDate, date_valueOf_impl>(cx, args);
2868 static const JSFunctionSpec date_static_methods[] = {
2869 JS_FN("UTC", date_UTC, MAXARGS,0),
2870 JS_FN("parse", date_parse, 1,0),
2871 JS_FN("now", date_now, 0,0),
2872 JS_FS_END
2875 static const JSFunctionSpec date_methods[] = {
2876 JS_FN("getTime", date_getTime, 0,0),
2877 JS_FN("getTimezoneOffset", date_getTimezoneOffset, 0,0),
2878 JS_FN("getYear", date_getYear, 0,0),
2879 JS_FN("getFullYear", date_getFullYear, 0,0),
2880 JS_FN("getUTCFullYear", date_getUTCFullYear, 0,0),
2881 JS_FN("getMonth", date_getMonth, 0,0),
2882 JS_FN("getUTCMonth", date_getUTCMonth, 0,0),
2883 JS_FN("getDate", date_getDate, 0,0),
2884 JS_FN("getUTCDate", date_getUTCDate, 0,0),
2885 JS_FN("getDay", date_getDay, 0,0),
2886 JS_FN("getUTCDay", date_getUTCDay, 0,0),
2887 JS_FN("getHours", date_getHours, 0,0),
2888 JS_FN("getUTCHours", date_getUTCHours, 0,0),
2889 JS_FN("getMinutes", date_getMinutes, 0,0),
2890 JS_FN("getUTCMinutes", date_getUTCMinutes, 0,0),
2891 JS_FN("getSeconds", date_getUTCSeconds, 0,0),
2892 JS_FN("getUTCSeconds", date_getUTCSeconds, 0,0),
2893 JS_FN("getMilliseconds", date_getUTCMilliseconds, 0,0),
2894 JS_FN("getUTCMilliseconds", date_getUTCMilliseconds, 0,0),
2895 JS_FN("setTime", date_setTime, 1,0),
2896 JS_FN("setYear", date_setYear, 1,0),
2897 JS_FN("setFullYear", date_setFullYear, 3,0),
2898 JS_FN("setUTCFullYear", date_setUTCFullYear, 3,0),
2899 JS_FN("setMonth", date_setMonth, 2,0),
2900 JS_FN("setUTCMonth", date_setUTCMonth, 2,0),
2901 JS_FN("setDate", date_setDate, 1,0),
2902 JS_FN("setUTCDate", date_setUTCDate, 1,0),
2903 JS_FN("setHours", date_setHours, 4,0),
2904 JS_FN("setUTCHours", date_setUTCHours, 4,0),
2905 JS_FN("setMinutes", date_setMinutes, 3,0),
2906 JS_FN("setUTCMinutes", date_setUTCMinutes, 3,0),
2907 JS_FN("setSeconds", date_setSeconds, 2,0),
2908 JS_FN("setUTCSeconds", date_setUTCSeconds, 2,0),
2909 JS_FN("setMilliseconds", date_setMilliseconds, 1,0),
2910 JS_FN("setUTCMilliseconds", date_setUTCMilliseconds, 1,0),
2911 JS_FN("toUTCString", date_toGMTString, 0,0),
2912 JS_FN("toLocaleFormat", date_toLocaleFormat, 0,0),
2913 #if EXPOSE_INTL_API
2914 JS_SELF_HOSTED_FN(js_toLocaleString_str, "Date_toLocaleString", 0,0),
2915 JS_SELF_HOSTED_FN("toLocaleDateString", "Date_toLocaleDateString", 0,0),
2916 JS_SELF_HOSTED_FN("toLocaleTimeString", "Date_toLocaleTimeString", 0,0),
2917 #else
2918 JS_FN(js_toLocaleString_str, date_toLocaleString, 0,0),
2919 JS_FN("toLocaleDateString", date_toLocaleDateString, 0,0),
2920 JS_FN("toLocaleTimeString", date_toLocaleTimeString, 0,0),
2921 #endif
2922 JS_FN("toDateString", date_toDateString, 0,0),
2923 JS_FN("toTimeString", date_toTimeString, 0,0),
2924 JS_FN("toISOString", date_toISOString, 0,0),
2925 JS_FN(js_toJSON_str, date_toJSON, 1,0),
2926 #if JS_HAS_TOSOURCE
2927 JS_FN(js_toSource_str, date_toSource, 0,0),
2928 #endif
2929 JS_FN(js_toString_str, date_toString, 0,0),
2930 JS_FN(js_valueOf_str, date_valueOf, 0,0),
2931 JS_FS_END
2934 bool
2935 js_Date(JSContext *cx, unsigned argc, Value *vp)
2937 CallArgs args = CallArgsFromVp(argc, vp);
2939 /* Date called as function. */
2940 if (!args.isConstructing())
2941 return date_format(cx, NowAsMillis(), FORMATSPEC_FULL, args.rval());
2943 /* Date called as constructor. */
2944 double d;
2945 if (args.length() == 0) {
2946 /* ES5 15.9.3.3. */
2947 d = NowAsMillis();
2948 } else if (args.length() == 1) {
2949 /* ES5 15.9.3.2. */
2951 /* Step 1. */
2952 if (!ToPrimitive(cx, args[0]))
2953 return false;
2955 if (args[0].isString()) {
2956 /* Step 2. */
2957 JSString *str = args[0].toString();
2958 if (!str)
2959 return false;
2961 JSLinearString *linearStr = str->ensureLinear(cx);
2962 if (!linearStr)
2963 return false;
2965 if (!ParseDate(linearStr, &d, &cx->runtime()->dateTimeInfo))
2966 d = GenericNaN();
2967 else
2968 d = TimeClip(d);
2969 } else {
2970 /* Step 3. */
2971 if (!ToNumber(cx, args[0], &d))
2972 return false;
2973 d = TimeClip(d);
2975 } else {
2976 double msec_time;
2977 if (!date_msecFromArgs(cx, args, &msec_time))
2978 return false;
2980 if (IsFinite(msec_time)) {
2981 msec_time = UTC(msec_time, &cx->runtime()->dateTimeInfo);
2982 msec_time = TimeClip(msec_time);
2984 d = msec_time;
2987 JSObject *obj = js_NewDateObjectMsec(cx, d);
2988 if (!obj)
2989 return false;
2991 args.rval().setObject(*obj);
2992 return true;
2995 static bool
2996 FinishDateClassInit(JSContext *cx, HandleObject ctor, HandleObject proto)
2998 proto->as<DateObject>().setUTCTime(GenericNaN());
3001 * Date.prototype.toGMTString has the same initial value as
3002 * Date.prototype.toUTCString.
3004 RootedValue toUTCStringFun(cx);
3005 RootedId toUTCStringId(cx, NameToId(cx->names().toUTCString));
3006 RootedId toGMTStringId(cx, NameToId(cx->names().toGMTString));
3007 return baseops::GetProperty(cx, proto, toUTCStringId, &toUTCStringFun) &&
3008 baseops::DefineGeneric(cx, proto, toGMTStringId, toUTCStringFun,
3009 JS_PropertyStub, JS_StrictPropertyStub, 0);
3012 const Class DateObject::class_ = {
3013 js_Date_str,
3014 JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) |
3015 JSCLASS_HAS_CACHED_PROTO(JSProto_Date),
3016 JS_PropertyStub, /* addProperty */
3017 JS_DeletePropertyStub, /* delProperty */
3018 JS_PropertyStub, /* getProperty */
3019 JS_StrictPropertyStub, /* setProperty */
3020 JS_EnumerateStub,
3021 JS_ResolveStub,
3022 date_convert,
3023 nullptr, /* finalize */
3024 nullptr, /* call */
3025 nullptr, /* hasInstance */
3026 nullptr, /* construct */
3027 nullptr, /* trace */
3029 GenericCreateConstructor<js_Date, MAXARGS, JSFunction::FinalizeKind>,
3030 GenericCreatePrototype<&DateObject::class_>,
3031 date_static_methods,
3032 date_methods,
3033 nullptr,
3034 FinishDateClassInit
3038 JS_FRIEND_API(JSObject *)
3039 js_NewDateObjectMsec(JSContext *cx, double msec_time)
3041 JSObject *obj = NewBuiltinClassInstance(cx, &DateObject::class_);
3042 if (!obj)
3043 return nullptr;
3044 obj->as<DateObject>().setUTCTime(msec_time);
3045 return obj;
3048 JS_FRIEND_API(JSObject *)
3049 js_NewDateObject(JSContext *cx, int year, int mon, int mday,
3050 int hour, int min, int sec)
3052 JS_ASSERT(mon < 12);
3053 double msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
3054 return js_NewDateObjectMsec(cx, UTC(msec_time, &cx->runtime()->dateTimeInfo));
3057 JS_FRIEND_API(bool)
3058 js_DateIsValid(JSObject *obj)
3060 return obj->is<DateObject>() && !IsNaN(obj->as<DateObject>().UTCTime().toNumber());
3063 JS_FRIEND_API(int)
3064 js_DateGetYear(JSContext *cx, JSObject *obj)
3066 /* Preserve legacy API behavior of returning 0 for invalid dates. */
3067 JS_ASSERT(obj);
3068 double localtime = obj->as<DateObject>().cachedLocalTime(&cx->runtime()->dateTimeInfo);
3069 if (IsNaN(localtime))
3070 return 0;
3072 return (int) YearFromTime(localtime);
3075 JS_FRIEND_API(int)
3076 js_DateGetMonth(JSContext *cx, JSObject *obj)
3078 JS_ASSERT(obj);
3079 double localtime = obj->as<DateObject>().cachedLocalTime(&cx->runtime()->dateTimeInfo);
3080 if (IsNaN(localtime))
3081 return 0;
3083 return (int) MonthFromTime(localtime);
3086 JS_FRIEND_API(int)
3087 js_DateGetDate(JSContext *cx, JSObject *obj)
3089 JS_ASSERT(obj);
3090 double localtime = obj->as<DateObject>().cachedLocalTime(&cx->runtime()->dateTimeInfo);
3091 if (IsNaN(localtime))
3092 return 0;
3094 return (int) DateFromTime(localtime);
3097 JS_FRIEND_API(int)
3098 js_DateGetHours(JSContext *cx, JSObject *obj)
3100 JS_ASSERT(obj);
3101 double localtime = obj->as<DateObject>().cachedLocalTime(&cx->runtime()->dateTimeInfo);
3102 if (IsNaN(localtime))
3103 return 0;
3105 return (int) HourFromTime(localtime);
3108 JS_FRIEND_API(int)
3109 js_DateGetMinutes(JSContext *cx, JSObject *obj)
3111 JS_ASSERT(obj);
3112 double localtime = obj->as<DateObject>().cachedLocalTime(&cx->runtime()->dateTimeInfo);
3113 if (IsNaN(localtime))
3114 return 0;
3116 return (int) MinFromTime(localtime);
3119 JS_FRIEND_API(int)
3120 js_DateGetSeconds(JSObject *obj)
3122 if (!obj->is<DateObject>())
3123 return 0;
3125 double utctime = obj->as<DateObject>().UTCTime().toNumber();
3126 if (IsNaN(utctime))
3127 return 0;
3128 return (int) SecFromTime(utctime);
3131 JS_FRIEND_API(double)
3132 js_DateGetMsecSinceEpoch(JSObject *obj)
3134 obj = CheckedUnwrap(obj);
3135 if (!obj || !obj->is<DateObject>())
3136 return 0;
3137 return obj->as<DateObject>().UTCTime().toNumber();