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/. */
6 
7 /*
8  * JS date methods.
9  *
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."
14  *
15  * Frederick Brooks, 'The Second-System Effect'.
16  */
17 
18 #include "jsdate.h"
19 
20 #include "mozilla/ArrayUtils.h"
21 #include "mozilla/FloatingPoint.h"
22 
23 #include <ctype.h>
24 #include <math.h>
25 #include <string.h>
26 
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 
37 #include "js/Conversions.h"
38 #include "js/Date.h"
39 #include "vm/DateTime.h"
40 #include "vm/GlobalObject.h"
41 #include "vm/Interpreter.h"
42 #include "vm/String.h"
43 #include "vm/StringBuffer.h"
44 #include "vm/Time.h"
45 
46 #include "jsobjinlines.h"
47 
48 using namespace js;
49 
50 using mozilla::ArrayLength;
51 using mozilla::IsFinite;
52 using mozilla::IsNaN;
53 using mozilla::NumbersAreIdentical;
54 
55 using JS::AutoCheckCannotGC;
56 using JS::ClippedTime;
57 using JS::GenericNaN;
58 using JS::TimeClip;
59 using JS::ToInteger;
60 
61 /*
62  * The JS 'Date' object is patterned after the Java 'Date' object.
63  * Here is a script:
64  *
65  *    today = new Date();
66  *
67  *    print(today.toLocaleString());
68  *
69  *    weekDay = today.getDay();
70  *
71  *
72  * These Java (and ECMA-262) methods are supported:
73  *
74  *     UTC
75  *     getDate (getUTCDate)
76  *     getDay (getUTCDay)
77  *     getHours (getUTCHours)
78  *     getMinutes (getUTCMinutes)
79  *     getMonth (getUTCMonth)
80  *     getSeconds (getUTCSeconds)
81  *     getMilliseconds (getUTCMilliseconds)
82  *     getTime
83  *     getTimezoneOffset
84  *     getYear
85  *     getFullYear (getUTCFullYear)
86  *     parse
87  *     setDate (setUTCDate)
88  *     setHours (setUTCHours)
89  *     setMinutes (setUTCMinutes)
90  *     setMonth (setUTCMonth)
91  *     setSeconds (setUTCSeconds)
92  *     setMilliseconds (setUTCMilliseconds)
93  *     setTime
94  *     setYear (setFullYear, setUTCFullYear)
95  *     toGMTString (toUTCString)
96  *     toLocaleString
97  *     toString
98  *
99  *
100  * These Java methods are not supported
101  *
102  *     setDay
103  *     before
104  *     after
105  *     equals
106  *     hashCode
107  */
108 
109 static inline double
Day(double t)110 Day(double t)
111 {
112     return floor(t / msPerDay);
113 }
114 
115 static double
TimeWithinDay(double t)116 TimeWithinDay(double t)
117 {
118     double result = fmod(t, msPerDay);
119     if (result < 0)
120         result += msPerDay;
121     return result;
122 }
123 
124 /* ES5 15.9.1.3. */
125 static inline bool
IsLeapYear(double year)126 IsLeapYear(double year)
127 {
128     MOZ_ASSERT(ToInteger(year) == year);
129     return fmod(year, 4) == 0 && (fmod(year, 100) != 0 || fmod(year, 400) == 0);
130 }
131 
132 static inline double
DaysInYear(double year)133 DaysInYear(double year)
134 {
135     if (!IsFinite(year))
136         return GenericNaN();
137     return IsLeapYear(year) ? 366 : 365;
138 }
139 
140 static inline double
DayFromYear(double y)141 DayFromYear(double y)
142 {
143     return 365 * (y - 1970) +
144            floor((y - 1969) / 4.0) -
145            floor((y - 1901) / 100.0) +
146            floor((y - 1601) / 400.0);
147 }
148 
149 static inline double
TimeFromYear(double y)150 TimeFromYear(double y)
151 {
152     return DayFromYear(y) * msPerDay;
153 }
154 
155 static double
YearFromTime(double t)156 YearFromTime(double t)
157 {
158     if (!IsFinite(t))
159         return GenericNaN();
160 
161     MOZ_ASSERT(ToInteger(t) == t);
162 
163     double y = floor(t / (msPerDay * 365.2425)) + 1970;
164     double t2 = TimeFromYear(y);
165 
166     /*
167      * Adjust the year if the approximation was wrong.  Since the year was
168      * computed using the average number of ms per year, it will usually
169      * be wrong for dates within several hours of a year transition.
170      */
171     if (t2 > t) {
172         y--;
173     } else {
174         if (t2 + msPerDay * DaysInYear(y) <= t)
175             y++;
176     }
177     return y;
178 }
179 
180 static inline int
DaysInFebruary(double year)181 DaysInFebruary(double year)
182 {
183     return IsLeapYear(year) ? 29 : 28;
184 }
185 
186 /* ES5 15.9.1.4. */
187 static inline double
DayWithinYear(double t,double year)188 DayWithinYear(double t, double year)
189 {
190     MOZ_ASSERT_IF(IsFinite(t), YearFromTime(t) == year);
191     return Day(t) - DayFromYear(year);
192 }
193 
194 static double
MonthFromTime(double t)195 MonthFromTime(double t)
196 {
197     if (!IsFinite(t))
198         return GenericNaN();
199 
200     double year = YearFromTime(t);
201     double d = DayWithinYear(t, year);
202 
203     int step;
204     if (d < (step = 31))
205         return 0;
206     if (d < (step += DaysInFebruary(year)))
207         return 1;
208     if (d < (step += 31))
209         return 2;
210     if (d < (step += 30))
211         return 3;
212     if (d < (step += 31))
213         return 4;
214     if (d < (step += 30))
215         return 5;
216     if (d < (step += 31))
217         return 6;
218     if (d < (step += 31))
219         return 7;
220     if (d < (step += 30))
221         return 8;
222     if (d < (step += 31))
223         return 9;
224     if (d < (step += 30))
225         return 10;
226     return 11;
227 }
228 
229 /* ES5 15.9.1.5. */
230 static double
DateFromTime(double t)231 DateFromTime(double t)
232 {
233     if (!IsFinite(t))
234         return GenericNaN();
235 
236     double year = YearFromTime(t);
237     double d = DayWithinYear(t, year);
238 
239     int next;
240     if (d <= (next = 30))
241         return d + 1;
242     int step = next;
243     if (d <= (next += DaysInFebruary(year)))
244         return d - step;
245     step = next;
246     if (d <= (next += 31))
247         return d - step;
248     step = next;
249     if (d <= (next += 30))
250         return d - step;
251     step = next;
252     if (d <= (next += 31))
253         return d - step;
254     step = next;
255     if (d <= (next += 30))
256         return d - step;
257     step = next;
258     if (d <= (next += 31))
259         return d - step;
260     step = next;
261     if (d <= (next += 31))
262         return d - step;
263     step = next;
264     if (d <= (next += 30))
265         return d - step;
266     step = next;
267     if (d <= (next += 31))
268         return d - step;
269     step = next;
270     if (d <= (next += 30))
271         return d - step;
272     step = next;
273     return d - step;
274 }
275 
276 /* ES5 15.9.1.6. */
277 static int
WeekDay(double t)278 WeekDay(double t)
279 {
280     /*
281      * We can't assert TimeClip(t) == t because we call this function with
282      * local times, which can be offset outside TimeClip's permitted range.
283      */
284     MOZ_ASSERT(ToInteger(t) == t);
285     int result = (int(Day(t)) + 4) % 7;
286     if (result < 0)
287         result += 7;
288     return result;
289 }
290 
291 static inline int
DayFromMonth(int month,bool isLeapYear)292 DayFromMonth(int month, bool isLeapYear)
293 {
294     /*
295      * The following array contains the day of year for the first day of
296      * each month, where index 0 is January, and day 0 is January 1.
297      */
298     static const int firstDayOfMonth[2][13] = {
299         {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
300         {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}
301     };
302 
303     MOZ_ASSERT(0 <= month && month <= 12);
304     return firstDayOfMonth[isLeapYear][month];
305 }
306 
307 template<typename T>
308 static inline int
309 DayFromMonth(T month, bool isLeapYear) = delete;
310 
311 /* ES5 15.9.1.12 (out of order to accommodate DaylightSavingTA). */
312 static double
MakeDay(double year,double month,double date)313 MakeDay(double year, double month, double date)
314 {
315     /* Step 1. */
316     if (!IsFinite(year) || !IsFinite(month) || !IsFinite(date))
317         return GenericNaN();
318 
319     /* Steps 2-4. */
320     double y = ToInteger(year);
321     double m = ToInteger(month);
322     double dt = ToInteger(date);
323 
324     /* Step 5. */
325     double ym = y + floor(m / 12);
326 
327     /* Step 6. */
328     int mn = int(fmod(m, 12.0));
329     if (mn < 0)
330         mn += 12;
331 
332     /* Steps 7-8. */
333     bool leap = IsLeapYear(ym);
334 
335     double yearday = floor(TimeFromYear(ym) / msPerDay);
336     double monthday = DayFromMonth(mn, leap);
337 
338     return yearday + monthday + dt - 1;
339 }
340 
341 /* ES5 15.9.1.13 (out of order to accommodate DaylightSavingTA). */
342 static inline double
MakeDate(double day,double time)343 MakeDate(double day, double time)
344 {
345     /* Step 1. */
346     if (!IsFinite(day) || !IsFinite(time))
347         return GenericNaN();
348 
349     /* Step 2. */
350     return day * msPerDay + time;
351 }
352 
JS_PUBLIC_API(double)353 JS_PUBLIC_API(double)
354 JS::MakeDate(double year, unsigned month, unsigned day)
355 {
356     return ::MakeDate(MakeDay(year, month, day), 0);
357 }
358 
JS_PUBLIC_API(double)359 JS_PUBLIC_API(double)
360 JS::YearFromTime(double time)
361 {
362     return ::YearFromTime(time);
363 }
364 
JS_PUBLIC_API(double)365 JS_PUBLIC_API(double)
366 JS::MonthFromTime(double time)
367 {
368     return ::MonthFromTime(time);
369 }
370 
JS_PUBLIC_API(double)371 JS_PUBLIC_API(double)
372 JS::DayFromTime(double time)
373 {
374     return DateFromTime(time);
375 }
376 
377 /*
378  * Find a year for which any given date will fall on the same weekday.
379  *
380  * This function should be used with caution when used other than
381  * for determining DST; it hasn't been proven not to produce an
382  * incorrect year for times near year boundaries.
383  */
384 static int
EquivalentYearForDST(int year)385 EquivalentYearForDST(int year)
386 {
387     /*
388      * Years and leap years on which Jan 1 is a Sunday, Monday, etc.
389      *
390      * yearStartingWith[0][i] is an example non-leap year where
391      * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
392      *
393      * yearStartingWith[1][i] is an example leap year where
394      * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
395      */
396     static const int yearStartingWith[2][7] = {
397         {1978, 1973, 1974, 1975, 1981, 1971, 1977},
398         {1984, 1996, 1980, 1992, 1976, 1988, 1972}
399     };
400 
401     int day = int(DayFromYear(year) + 4) % 7;
402     if (day < 0)
403         day += 7;
404 
405     return yearStartingWith[IsLeapYear(year)][day];
406 }
407 
408 /* ES5 15.9.1.8. */
409 static double
DaylightSavingTA(double t)410 DaylightSavingTA(double t)
411 {
412     if (!IsFinite(t))
413         return GenericNaN();
414 
415     /*
416      * If earlier than 1970 or after 2038, potentially beyond the ken of
417      * many OSes, map it to an equivalent year before asking.
418      */
419     if (t < 0.0 || t > 2145916800000.0) {
420         int year = EquivalentYearForDST(int(YearFromTime(t)));
421         double day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
422         t = MakeDate(day, TimeWithinDay(t));
423     }
424 
425     int64_t utcMilliseconds = static_cast<int64_t>(t);
426     int64_t offsetMilliseconds = DateTimeInfo::getDSTOffsetMilliseconds(utcMilliseconds);
427     return static_cast<double>(offsetMilliseconds);
428 }
429 
430 static double
AdjustTime(double date)431 AdjustTime(double date)
432 {
433     double localTZA = DateTimeInfo::localTZA();
434     double t = DaylightSavingTA(date) + localTZA;
435     t = (localTZA >= 0) ? fmod(t, msPerDay) : -fmod(msPerDay - t, msPerDay);
436     return t;
437 }
438 
439 /* ES5 15.9.1.9. */
440 static double
LocalTime(double t)441 LocalTime(double t)
442 {
443     return t + AdjustTime(t);
444 }
445 
446 static double
UTC(double t)447 UTC(double t)
448 {
449     return t - AdjustTime(t - DateTimeInfo::localTZA());
450 }
451 
452 /* ES5 15.9.1.10. */
453 static double
HourFromTime(double t)454 HourFromTime(double t)
455 {
456     double result = fmod(floor(t/msPerHour), HoursPerDay);
457     if (result < 0)
458         result += HoursPerDay;
459     return result;
460 }
461 
462 static double
MinFromTime(double t)463 MinFromTime(double t)
464 {
465     double result = fmod(floor(t / msPerMinute), MinutesPerHour);
466     if (result < 0)
467         result += MinutesPerHour;
468     return result;
469 }
470 
471 static double
SecFromTime(double t)472 SecFromTime(double t)
473 {
474     double result = fmod(floor(t / msPerSecond), SecondsPerMinute);
475     if (result < 0)
476         result += SecondsPerMinute;
477     return result;
478 }
479 
480 static double
msFromTime(double t)481 msFromTime(double t)
482 {
483     double result = fmod(t, msPerSecond);
484     if (result < 0)
485         result += msPerSecond;
486     return result;
487 }
488 
489 /* ES5 15.9.1.11. */
490 static double
MakeTime(double hour,double min,double sec,double ms)491 MakeTime(double hour, double min, double sec, double ms)
492 {
493     /* Step 1. */
494     if (!IsFinite(hour) ||
495         !IsFinite(min) ||
496         !IsFinite(sec) ||
497         !IsFinite(ms))
498     {
499         return GenericNaN();
500     }
501 
502     /* Step 2. */
503     double h = ToInteger(hour);
504 
505     /* Step 3. */
506     double m = ToInteger(min);
507 
508     /* Step 4. */
509     double s = ToInteger(sec);
510 
511     /* Step 5. */
512     double milli = ToInteger(ms);
513 
514     /* Steps 6-7. */
515     return h * msPerHour + m * msPerMinute + s * msPerSecond + milli;
516 }
517 
518 /**
519  * end of ECMA 'support' functions
520  */
521 
522 /* for use by date_parse */
523 
524 static const char* const wtb[] = {
525     "am", "pm",
526     "monday", "tuesday", "wednesday", "thursday", "friday",
527     "saturday", "sunday",
528     "january", "february", "march", "april", "may", "june",
529     "july", "august", "september", "october", "november", "december",
530     "gmt", "ut", "utc",
531     "est", "edt",
532     "cst", "cdt",
533     "mst", "mdt",
534     "pst", "pdt"
535     /* time zone table needs to be expanded */
536 };
537 
538 static const int ttb[] = {
539     -1, -2, 0, 0, 0, 0, 0, 0, 0,       /* AM/PM */
540     2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
541     10000 + 0, 10000 + 0, 10000 + 0,   /* GMT/UT/UTC */
542     10000 + 5 * 60, 10000 + 4 * 60,    /* EST/EDT */
543     10000 + 6 * 60, 10000 + 5 * 60,    /* CST/CDT */
544     10000 + 7 * 60, 10000 + 6 * 60,    /* MST/MDT */
545     10000 + 8 * 60, 10000 + 7 * 60     /* PST/PDT */
546 };
547 
548 template <typename CharT>
549 static bool
RegionMatches(const char * s1,int s1off,const CharT * s2,int s2off,int count)550 RegionMatches(const char* s1, int s1off, const CharT* s2, int s2off, int count)
551 {
552     while (count > 0 && s1[s1off] && s2[s2off]) {
553         if (unicode::ToLowerCase(s1[s1off]) != unicode::ToLowerCase(s2[s2off]))
554             break;
555 
556         s1off++;
557         s2off++;
558         count--;
559     }
560 
561     return count == 0;
562 }
563 
564 /* ES6 20.3.3.4. */
565 static bool
date_UTC(JSContext * cx,unsigned argc,Value * vp)566 date_UTC(JSContext* cx, unsigned argc, Value* vp)
567 {
568     CallArgs args = CallArgsFromVp(argc, vp);
569 
570     // Steps 1-2.
571     double y;
572     if (!ToNumber(cx, args.get(0), &y))
573         return false;
574 
575     // Steps 3-4.
576     double m;
577     if (!ToNumber(cx, args.get(1), &m))
578         return false;
579 
580     // Steps 5-6.
581     double dt;
582     if (args.length() >= 3) {
583         if (!ToNumber(cx, args[2], &dt))
584             return false;
585     } else {
586         dt = 1;
587     }
588 
589     // Steps 7-8.
590     double h;
591     if (args.length() >= 4) {
592         if (!ToNumber(cx, args[3], &h))
593             return false;
594     } else {
595         h = 0;
596     }
597 
598     // Steps 9-10.
599     double min;
600     if (args.length() >= 5) {
601         if (!ToNumber(cx, args[4], &min))
602             return false;
603     } else {
604         min = 0;
605     }
606 
607     // Steps 11-12.
608     double s;
609     if (args.length() >= 6) {
610         if (!ToNumber(cx, args[5], &s))
611             return false;
612     } else {
613         s = 0;
614     }
615 
616     // Steps 13-14.
617     double milli;
618     if (args.length() >= 7) {
619         if (!ToNumber(cx, args[6], &milli))
620             return false;
621     } else {
622         milli = 0;
623     }
624 
625     // Step 15.
626     double yr = y;
627     if (!IsNaN(y)) {
628         double yint = ToInteger(y);
629         if (0 <= yint && yint <= 99)
630             yr = 1900 + yint;
631     }
632 
633     // Step 16.
634     ClippedTime time = TimeClip(MakeDate(MakeDay(yr, m, dt), MakeTime(h, min, s, milli)));
635     args.rval().set(TimeValue(time));
636     return true;
637 }
638 
639 /*
640  * Read and convert decimal digits from s[*i] into *result
641  * while *i < limit.
642  *
643  * Succeed if any digits are converted. Advance *i only
644  * as digits are consumed.
645  */
646 template <typename CharT>
647 static bool
ParseDigits(size_t * result,const CharT * s,size_t * i,size_t limit)648 ParseDigits(size_t* result, const CharT* s, size_t* i, size_t limit)
649 {
650     size_t init = *i;
651     *result = 0;
652     while (*i < limit && ('0' <= s[*i] && s[*i] <= '9')) {
653         *result *= 10;
654         *result += (s[*i] - '0');
655         ++(*i);
656     }
657     return *i != init;
658 }
659 
660 /*
661  * Read and convert decimal digits to the right of a decimal point,
662  * representing a fractional integer, from s[*i] into *result
663  * while *i < limit.
664  *
665  * Succeed if any digits are converted. Advance *i only
666  * as digits are consumed.
667  */
668 template <typename CharT>
669 static bool
ParseFractional(double * result,const CharT * s,size_t * i,size_t limit)670 ParseFractional(double* result, const CharT* s, size_t* i, size_t limit)
671 {
672     double factor = 0.1;
673     size_t init = *i;
674     *result = 0.0;
675     while (*i < limit && ('0' <= s[*i] && s[*i] <= '9')) {
676         *result += (s[*i] - '0') * factor;
677         factor *= 0.1;
678         ++(*i);
679     }
680     return *i != init;
681 }
682 
683 /*
684  * Read and convert exactly n decimal digits from s[*i]
685  * to s[min(*i+n,limit)] into *result.
686  *
687  * Succeed if exactly n digits are converted. Advance *i only
688  * on success.
689  */
690 template <typename CharT>
691 static bool
ParseDigitsN(size_t n,size_t * result,const CharT * s,size_t * i,size_t limit)692 ParseDigitsN(size_t n, size_t* result, const CharT* s, size_t* i, size_t limit)
693 {
694     size_t init = *i;
695 
696     if (ParseDigits(result, s, i, Min(limit, init + n)))
697         return (*i - init) == n;
698 
699     *i = init;
700     return false;
701 }
702 
703 static int
DaysInMonth(int year,int month)704 DaysInMonth(int year, int month)
705 {
706     bool leap = IsLeapYear(year);
707     int result = int(DayFromMonth(month, leap) - DayFromMonth(month - 1, leap));
708     return result;
709 }
710 
711 /*
712  * Parse a string in one of the date-time formats given by the W3C
713  * "NOTE-datetime" specification. These formats make up a restricted
714  * profile of the ISO 8601 format. Quoted here:
715  *
716  *   The formats are as follows. Exactly the components shown here
717  *   must be present, with exactly this punctuation. Note that the "T"
718  *   appears literally in the string, to indicate the beginning of the
719  *   time element, as specified in ISO 8601.
720  *
721  *   Any combination of the date formats with the time formats is
722  *   allowed, and also either the date or the time can be missing.
723  *
724  *   The specification is silent on the meaning when fields are
725  *   ommitted so the interpretations are a guess, but hopefully a
726  *   reasonable one. We default the month to January, the day to the
727  *   1st, and hours minutes and seconds all to 0. If the date is
728  *   missing entirely then we assume 1970-01-01 so that the time can
729  *   be aded to a date later. If the time is missing then we assume
730  *   00:00 UTC.  If the time is present but the time zone field is
731  *   missing then we use local time.
732  *
733  * Date part:
734  *
735  *  Year:
736  *     YYYY (eg 1997)
737  *
738  *  Year and month:
739  *     YYYY-MM (eg 1997-07)
740  *
741  *  Complete date:
742  *     YYYY-MM-DD (eg 1997-07-16)
743  *
744  * Time part:
745  *
746  *  Hours and minutes:
747  *     Thh:mmTZD (eg T19:20+01:00)
748  *
749  *  Hours, minutes and seconds:
750  *     Thh:mm:ssTZD (eg T19:20:30+01:00)
751  *
752  *  Hours, minutes, seconds and a decimal fraction of a second:
753  *     Thh:mm:ss.sTZD (eg T19:20:30.45+01:00)
754  *
755  * where:
756  *
757  *   YYYY = four-digit year or six digit year as +YYYYYY or -YYYYYY
758  *   MM   = two-digit month (01=January, etc.)
759  *   DD   = two-digit day of month (01 through 31)
760  *   hh   = two digits of hour (00 through 23) (am/pm NOT allowed)
761  *   mm   = two digits of minute (00 through 59)
762  *   ss   = two digits of second (00 through 59)
763  *   s    = one or more digits representing a decimal fraction of a second
764  *   TZD  = time zone designator (Z or +hh:mm or -hh:mm or missing for local)
765  */
766 template <typename CharT>
767 static bool
ParseISODate(const CharT * s,size_t length,ClippedTime * result)768 ParseISODate(const CharT* s, size_t length, ClippedTime* result)
769 {
770     size_t i = 0;
771     int tzMul = 1;
772     int dateMul = 1;
773     size_t year = 1970;
774     size_t month = 1;
775     size_t day = 1;
776     size_t hour = 0;
777     size_t min = 0;
778     size_t sec = 0;
779     double frac = 0;
780     bool isLocalTime = false;
781     size_t tzHour = 0;
782     size_t tzMin = 0;
783 
784 #define PEEK(ch) (i < length && s[i] == ch)
785 
786 #define NEED(ch)                                                               \
787     if (i >= length || s[i] != ch) { return false; } else { ++i; }
788 
789 #define DONE_DATE_UNLESS(ch)                                                   \
790     if (i >= length || s[i] != ch) { goto done_date; } else { ++i; }
791 
792 #define DONE_UNLESS(ch)                                                        \
793     if (i >= length || s[i] != ch) { goto done; } else { ++i; }
794 
795 #define NEED_NDIGITS(n, field)                                                 \
796     if (!ParseDigitsN(n, &field, s, &i, length)) { return false; }
797 
798     if (PEEK('+') || PEEK('-')) {
799         if (PEEK('-'))
800             dateMul = -1;
801         ++i;
802         NEED_NDIGITS(6, year);
803     } else if (!PEEK('T')) {
804         NEED_NDIGITS(4, year);
805     }
806     DONE_DATE_UNLESS('-');
807     NEED_NDIGITS(2, month);
808     DONE_DATE_UNLESS('-');
809     NEED_NDIGITS(2, day);
810 
811  done_date:
812     DONE_UNLESS('T');
813     NEED_NDIGITS(2, hour);
814     NEED(':');
815     NEED_NDIGITS(2, min);
816 
817     if (PEEK(':')) {
818         ++i;
819         NEED_NDIGITS(2, sec);
820         if (PEEK('.')) {
821             ++i;
822             if (!ParseFractional(&frac, s, &i, length))
823                 return false;
824         }
825     }
826 
827     if (PEEK('Z')) {
828         ++i;
829     } else if (PEEK('+') || PEEK('-')) {
830         if (PEEK('-'))
831             tzMul = -1;
832         ++i;
833         NEED_NDIGITS(2, tzHour);
834         /*
835          * Non-standard extension to the ISO date format (permitted by ES5):
836          * allow "-0700" as a time zone offset, not just "-07:00".
837          */
838         if (PEEK(':'))
839             ++i;
840         NEED_NDIGITS(2, tzMin);
841     } else {
842         isLocalTime = true;
843     }
844 
845  done:
846     if (year > 275943 // ceil(1e8/365) + 1970
847         || (month == 0 || month > 12)
848         || (day == 0 || day > size_t(DaysInMonth(year,month)))
849         || hour > 24
850         || ((hour == 24) && (min > 0 || sec > 0))
851         || min > 59
852         || sec > 59
853         || tzHour > 23
854         || tzMin > 59)
855     {
856         return false;
857     }
858 
859     if (i != length)
860         return false;
861 
862     month -= 1; /* convert month to 0-based */
863 
864     double msec = MakeDate(MakeDay(dateMul * double(year), month, day),
865                            MakeTime(hour, min, sec, frac * 1000.0));
866 
867     if (isLocalTime)
868         msec = UTC(msec);
869     else
870         msec -= tzMul * (tzHour * msPerHour + tzMin * msPerMinute);
871 
872     *result = TimeClip(msec);
873     return NumbersAreIdentical(msec, result->toDouble());
874 
875 #undef PEEK
876 #undef NEED
877 #undef DONE_UNLESS
878 #undef NEED_NDIGITS
879 }
880 
881 template <typename CharT>
882 static bool
ParseDate(const CharT * s,size_t length,ClippedTime * result)883 ParseDate(const CharT* s, size_t length, ClippedTime* result)
884 {
885     if (ParseISODate(s, length, result))
886         return true;
887 
888     if (length == 0)
889         return false;
890 
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;
898 
899     int prevc = 0;
900 
901     bool seenPlusMinus = false;
902     bool seenMonthName = false;
903 
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;
912         }
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;
923                 }
924             }
925             continue;
926         }
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++;
932             }
933 
934             /*
935              * Allow TZA before the year, so 'Wed Nov 05 21:49:11 GMT-0800 1997'
936              * works.
937              *
938              * Uses of seenPlusMinus allow ':' in TZA, so Java no-timezone style
939              * of GMT+4:30 works.
940              */
941 
942             if ((prevc == '+' || prevc == '-')/*  && year>=0 */) {
943                 /* Make ':' case below change tzOffset. */
944                 seenPlusMinus = true;
945 
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" */
951 
952                 if (prevc == '+')       /* plus means east of GMT */
953                     n = -n;
954 
955                 if (tzOffset != 0 && tzOffset != -1)
956                     return false;
957 
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 == '/') {
972                 /*
973                  * Until it is determined that mon is the actual month, keep
974                  * it as 1-based rather than 0-based.
975                  */
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;
1001             }
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++;
1013             }
1014 
1015             if (i <= st + 1)
1016                 return false;
1017 
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) {
1023                             /*
1024                              * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as
1025                              * 12:30, instead of blindly adding 12 if PM.
1026                              */
1027                             MOZ_ASSERT(action == -1 || action == -2);
1028                             if (hour > 12 || hour < 0)
1029                                 return false;
1030 
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! */
1036                             /*
1037                              * Adjust mon to be 1-based until the final values
1038                              * for mon, mday and year are adjusted below.
1039                              */
1040                             if (seenMonthName)
1041                                 return false;
1042 
1043                             seenMonthName = true;
1044                             int temp = /*byte*/ (action - 2) + 1;
1045 
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;
1056                             }
1057                         } else {
1058                             tzOffset = action - 10000;
1059                         }
1060                     }
1061                     break;
1062                 }
1063             }
1064 
1065             if (k < 0)
1066                 return false;
1067 
1068             prevc = 0;
1069         }
1070     }
1071 
1072     if (year < 0 || mon < 0 || mday < 0)
1073         return false;
1074 
1075     /*
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.
1100      */
1101     if (seenMonthName) {
1102         if ((mday >= 70 && year >= 70) || (mday < 70 && year < 70))
1103             return false;
1104 
1105         if (mday > year) {
1106             int temp = year;
1107             year = mday;
1108             mday = temp;
1109         }
1110         if (year >= 70 && year < 100) {
1111             year += 1900;
1112         }
1113     } else if (mon < 70) { /* (a) month/day/year */
1114         if (year < 100) {
1115             year += 1900;
1116         }
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;
1125         }
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;
1134         }
1135     }
1136 
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;
1144 
1145     double msec = MakeDate(MakeDay(year, mon, mday), MakeTime(hour, min, sec, 0));
1146 
1147     if (tzOffset == -1) /* no time zone specified, have to use local */
1148         msec = UTC(msec);
1149     else
1150         msec += tzOffset * msPerMinute;
1151 
1152     *result = TimeClip(msec);
1153     return true;
1154 }
1155 
1156 static bool
ParseDate(JSLinearString * s,ClippedTime * result)1157 ParseDate(JSLinearString* s, ClippedTime* result)
1158 {
1159     AutoCheckCannotGC nogc;
1160     return s->hasLatin1Chars()
1161            ? ParseDate(s->latin1Chars(nogc), s->length(), result)
1162            : ParseDate(s->twoByteChars(nogc), s->length(), result);
1163 }
1164 
1165 static bool
date_parse(JSContext * cx,unsigned argc,Value * vp)1166 date_parse(JSContext* cx, unsigned argc, Value* vp)
1167 {
1168     CallArgs args = CallArgsFromVp(argc, vp);
1169     if (args.length() == 0) {
1170         args.rval().setNaN();
1171         return true;
1172     }
1173 
1174     JSString* str = ToString<CanGC>(cx, args[0]);
1175     if (!str)
1176         return false;
1177 
1178     JSLinearString* linearStr = str->ensureLinear(cx);
1179     if (!linearStr)
1180         return false;
1181 
1182     ClippedTime result;
1183     if (!ParseDate(linearStr, &result)) {
1184         args.rval().setNaN();
1185         return true;
1186     }
1187 
1188     args.rval().set(TimeValue(result));
1189     return true;
1190 }
1191 
1192 static ClippedTime
NowAsMillis()1193 NowAsMillis()
1194 {
1195     return TimeClip(static_cast<double>(PRMJ_Now()) / PRMJ_USEC_PER_MSEC);
1196 }
1197 
1198 bool
date_now(JSContext * cx,unsigned argc,Value * vp)1199 js::date_now(JSContext* cx, unsigned argc, Value* vp)
1200 {
1201     CallArgs args = CallArgsFromVp(argc, vp);
1202     args.rval().set(TimeValue(NowAsMillis()));
1203     return true;
1204 }
1205 
1206 void
setUTCTime(ClippedTime t)1207 DateObject::setUTCTime(ClippedTime t)
1208 {
1209     for (size_t ind = COMPONENTS_START_SLOT; ind < RESERVED_SLOTS; ind++)
1210         setReservedSlot(ind, UndefinedValue());
1211 
1212     setFixedSlot(UTC_TIME_SLOT, TimeValue(t));
1213 }
1214 
1215 void
setUTCTime(ClippedTime t,MutableHandleValue vp)1216 DateObject::setUTCTime(ClippedTime t, MutableHandleValue vp)
1217 {
1218     setUTCTime(t);
1219     vp.set(TimeValue(t));
1220 }
1221 
1222 void
fillLocalTimeSlots()1223 DateObject::fillLocalTimeSlots()
1224 {
1225     /* Check if the cache is already populated. */
1226     if (!getReservedSlot(LOCAL_TIME_SLOT).isUndefined() &&
1227         getReservedSlot(TZA_SLOT).toDouble() == DateTimeInfo::localTZA())
1228     {
1229         return;
1230     }
1231 
1232     /* Remember timezone used to generate the local cache. */
1233     setReservedSlot(TZA_SLOT, DoubleValue(DateTimeInfo::localTZA()));
1234 
1235     double utcTime = UTCTime().toNumber();
1236 
1237     if (!IsFinite(utcTime)) {
1238         for (size_t ind = COMPONENTS_START_SLOT; ind < RESERVED_SLOTS; ind++)
1239             setReservedSlot(ind, DoubleValue(utcTime));
1240         return;
1241     }
1242 
1243     double localTime = LocalTime(utcTime);
1244 
1245     setReservedSlot(LOCAL_TIME_SLOT, DoubleValue(localTime));
1246 
1247     int year = (int) floor(localTime /(msPerDay * 365.2425)) + 1970;
1248     double yearStartTime = TimeFromYear(year);
1249 
1250     /* Adjust the year in case the approximation was wrong, as in YearFromTime. */
1251     int yearDays;
1252     if (yearStartTime > localTime) {
1253         year--;
1254         yearStartTime -= (msPerDay * DaysInYear(year));
1255         yearDays = DaysInYear(year);
1256     } else {
1257         yearDays = DaysInYear(year);
1258         double nextStart = yearStartTime + (msPerDay * yearDays);
1259         if (nextStart <= localTime) {
1260             year++;
1261             yearStartTime = nextStart;
1262             yearDays = DaysInYear(year);
1263         }
1264     }
1265 
1266     setReservedSlot(LOCAL_YEAR_SLOT, Int32Value(year));
1267 
1268     uint64_t yearTime = uint64_t(localTime - yearStartTime);
1269     int yearSeconds = uint32_t(yearTime / 1000);
1270 
1271     int day = yearSeconds / int(SecondsPerDay);
1272 
1273     int step = -1, next = 30;
1274     int month;
1275 
1276     do {
1277         if (day <= next) {
1278             month = 0;
1279             break;
1280         }
1281         step = next;
1282         next += ((yearDays == 366) ? 29 : 28);
1283         if (day <= next) {
1284             month = 1;
1285             break;
1286         }
1287         step = next;
1288         if (day <= (next += 31)) {
1289             month = 2;
1290             break;
1291         }
1292         step = next;
1293         if (day <= (next += 30)) {
1294             month = 3;
1295             break;
1296         }
1297         step = next;
1298         if (day <= (next += 31)) {
1299             month = 4;
1300             break;
1301         }
1302         step = next;
1303         if (day <= (next += 30)) {
1304             month = 5;
1305             break;
1306         }
1307         step = next;
1308         if (day <= (next += 31)) {
1309             month = 6;
1310             break;
1311         }
1312         step = next;
1313         if (day <= (next += 31)) {
1314             month = 7;
1315             break;
1316         }
1317         step = next;
1318         if (day <= (next += 30)) {
1319             month = 8;
1320             break;
1321         }
1322         step = next;
1323         if (day <= (next += 31)) {
1324             month = 9;
1325             break;
1326         }
1327         step = next;
1328         if (day <= (next += 30)) {
1329             month = 10;
1330             break;
1331         }
1332         step = next;
1333         month = 11;
1334     } while (0);
1335 
1336     setReservedSlot(LOCAL_MONTH_SLOT, Int32Value(month));
1337     setReservedSlot(LOCAL_DATE_SLOT, Int32Value(day - step));
1338 
1339     int weekday = WeekDay(localTime);
1340     setReservedSlot(LOCAL_DAY_SLOT, Int32Value(weekday));
1341 
1342     int seconds = yearSeconds % 60;
1343     setReservedSlot(LOCAL_SECONDS_SLOT, Int32Value(seconds));
1344 
1345     int minutes = (yearSeconds / 60) % 60;
1346     setReservedSlot(LOCAL_MINUTES_SLOT, Int32Value(minutes));
1347 
1348     int hours = (yearSeconds / (60 * 60)) % 24;
1349     setReservedSlot(LOCAL_HOURS_SLOT, Int32Value(hours));
1350 }
1351 
1352 inline double
cachedLocalTime()1353 DateObject::cachedLocalTime()
1354 {
1355     fillLocalTimeSlots();
1356     return getReservedSlot(LOCAL_TIME_SLOT).toDouble();
1357 }
1358 
1359 MOZ_ALWAYS_INLINE bool
IsDate(HandleValue v)1360 IsDate(HandleValue v)
1361 {
1362     return v.isObject() && v.toObject().is<DateObject>();
1363 }
1364 
1365 /*
1366  * See ECMA 15.9.5.4 thru 15.9.5.23
1367  */
1368 /* static */ MOZ_ALWAYS_INLINE bool
getTime_impl(JSContext * cx,const CallArgs & args)1369 DateObject::getTime_impl(JSContext* cx, const CallArgs& args)
1370 {
1371     args.rval().set(args.thisv().toObject().as<DateObject>().UTCTime());
1372     return true;
1373 }
1374 
1375 static bool
date_getTime(JSContext * cx,unsigned argc,Value * vp)1376 date_getTime(JSContext* cx, unsigned argc, Value* vp)
1377 {
1378     CallArgs args = CallArgsFromVp(argc, vp);
1379     return CallNonGenericMethod<IsDate, DateObject::getTime_impl>(cx, args);
1380 }
1381 
1382 /* static */ MOZ_ALWAYS_INLINE bool
getYear_impl(JSContext * cx,const CallArgs & args)1383 DateObject::getYear_impl(JSContext* cx, const CallArgs& args)
1384 {
1385     DateObject* dateObj = &args.thisv().toObject().as<DateObject>();
1386     dateObj->fillLocalTimeSlots();
1387 
1388     Value yearVal = dateObj->getReservedSlot(LOCAL_YEAR_SLOT);
1389     if (yearVal.isInt32()) {
1390         /* Follow ECMA-262 to the letter, contrary to IE JScript. */
1391         int year = yearVal.toInt32() - 1900;
1392         args.rval().setInt32(year);
1393     } else {
1394         args.rval().set(yearVal);
1395     }
1396 
1397     return true;
1398 }
1399 
1400 static bool
date_getYear(JSContext * cx,unsigned argc,Value * vp)1401 date_getYear(JSContext* cx, unsigned argc, Value* vp)
1402 {
1403     CallArgs args = CallArgsFromVp(argc, vp);
1404     return CallNonGenericMethod<IsDate, DateObject::getYear_impl>(cx, args);
1405 }
1406 
1407 /* static */ MOZ_ALWAYS_INLINE bool
getFullYear_impl(JSContext * cx,const CallArgs & args)1408 DateObject::getFullYear_impl(JSContext* cx, const CallArgs& args)
1409 {
1410     DateObject* dateObj = &args.thisv().toObject().as<DateObject>();
1411     dateObj->fillLocalTimeSlots();
1412 
1413     args.rval().set(dateObj->getReservedSlot(LOCAL_YEAR_SLOT));
1414     return true;
1415 }
1416 
1417 static bool
date_getFullYear(JSContext * cx,unsigned argc,Value * vp)1418 date_getFullYear(JSContext* cx, unsigned argc, Value* vp)
1419 {
1420     CallArgs args = CallArgsFromVp(argc, vp);
1421     return CallNonGenericMethod<IsDate, DateObject::getFullYear_impl>(cx, args);
1422 }
1423 
1424 /* static */ MOZ_ALWAYS_INLINE bool
getUTCFullYear_impl(JSContext * cx,const CallArgs & args)1425 DateObject::getUTCFullYear_impl(JSContext* cx, const CallArgs& args)
1426 {
1427     double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
1428     if (IsFinite(result))
1429         result = YearFromTime(result);
1430 
1431     args.rval().setNumber(result);
1432     return true;
1433 }
1434 
1435 static bool
date_getUTCFullYear(JSContext * cx,unsigned argc,Value * vp)1436 date_getUTCFullYear(JSContext* cx, unsigned argc, Value* vp)
1437 {
1438     CallArgs args = CallArgsFromVp(argc, vp);
1439     return CallNonGenericMethod<IsDate, DateObject::getUTCFullYear_impl>(cx, args);
1440 }
1441 
1442 /* static */ MOZ_ALWAYS_INLINE bool
getMonth_impl(JSContext * cx,const CallArgs & args)1443 DateObject::getMonth_impl(JSContext* cx, const CallArgs& args)
1444 {
1445     DateObject* dateObj = &args.thisv().toObject().as<DateObject>();
1446     dateObj->fillLocalTimeSlots();
1447 
1448     args.rval().set(dateObj->getReservedSlot(LOCAL_MONTH_SLOT));
1449     return true;
1450 }
1451 
1452 static bool
date_getMonth(JSContext * cx,unsigned argc,Value * vp)1453 date_getMonth(JSContext* cx, unsigned argc, Value* vp)
1454 {
1455     CallArgs args = CallArgsFromVp(argc, vp);
1456     return CallNonGenericMethod<IsDate, DateObject::getMonth_impl>(cx, args);
1457 }
1458 
1459 /* static */ MOZ_ALWAYS_INLINE bool
getUTCMonth_impl(JSContext * cx,const CallArgs & args)1460 DateObject::getUTCMonth_impl(JSContext* cx, const CallArgs& args)
1461 {
1462     double d = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
1463     args.rval().setNumber(MonthFromTime(d));
1464     return true;
1465 }
1466 
1467 static bool
date_getUTCMonth(JSContext * cx,unsigned argc,Value * vp)1468 date_getUTCMonth(JSContext* cx, unsigned argc, Value* vp)
1469 {
1470     CallArgs args = CallArgsFromVp(argc, vp);
1471     return CallNonGenericMethod<IsDate, DateObject::getUTCMonth_impl>(cx, args);
1472 }
1473 
1474 /* static */ MOZ_ALWAYS_INLINE bool
getDate_impl(JSContext * cx,const CallArgs & args)1475 DateObject::getDate_impl(JSContext* cx, const CallArgs& args)
1476 {
1477     DateObject* dateObj = &args.thisv().toObject().as<DateObject>();
1478     dateObj->fillLocalTimeSlots();
1479 
1480     args.rval().set(dateObj->getReservedSlot(LOCAL_DATE_SLOT));
1481     return true;
1482 }
1483 
1484 static bool
date_getDate(JSContext * cx,unsigned argc,Value * vp)1485 date_getDate(JSContext* cx, unsigned argc, Value* vp)
1486 {
1487     CallArgs args = CallArgsFromVp(argc, vp);
1488     return CallNonGenericMethod<IsDate, DateObject::getDate_impl>(cx, args);
1489 }
1490 
1491 /* static */ MOZ_ALWAYS_INLINE bool
getUTCDate_impl(JSContext * cx,const CallArgs & args)1492 DateObject::getUTCDate_impl(JSContext* cx, const CallArgs& args)
1493 {
1494     double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
1495     if (IsFinite(result))
1496         result = DateFromTime(result);
1497 
1498     args.rval().setNumber(result);
1499     return true;
1500 }
1501 
1502 static bool
date_getUTCDate(JSContext * cx,unsigned argc,Value * vp)1503 date_getUTCDate(JSContext* cx, unsigned argc, Value* vp)
1504 {
1505     CallArgs args = CallArgsFromVp(argc, vp);
1506     return CallNonGenericMethod<IsDate, DateObject::getUTCDate_impl>(cx, args);
1507 }
1508 
1509 /* static */ MOZ_ALWAYS_INLINE bool
getDay_impl(JSContext * cx,const CallArgs & args)1510 DateObject::getDay_impl(JSContext* cx, const CallArgs& args)
1511 {
1512     DateObject* dateObj = &args.thisv().toObject().as<DateObject>();
1513     dateObj->fillLocalTimeSlots();
1514 
1515     args.rval().set(dateObj->getReservedSlot(LOCAL_DAY_SLOT));
1516     return true;
1517 }
1518 
1519 static bool
date_getDay(JSContext * cx,unsigned argc,Value * vp)1520 date_getDay(JSContext* cx, unsigned argc, Value* vp)
1521 {
1522     CallArgs args = CallArgsFromVp(argc, vp);
1523     return CallNonGenericMethod<IsDate, DateObject::getDay_impl>(cx, args);
1524 }
1525 
1526 /* static */ MOZ_ALWAYS_INLINE bool
getUTCDay_impl(JSContext * cx,const CallArgs & args)1527 DateObject::getUTCDay_impl(JSContext* cx, const CallArgs& args)
1528 {
1529     double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
1530     if (IsFinite(result))
1531         result = WeekDay(result);
1532 
1533     args.rval().setNumber(result);
1534     return true;
1535 }
1536 
1537 static bool
date_getUTCDay(JSContext * cx,unsigned argc,Value * vp)1538 date_getUTCDay(JSContext* cx, unsigned argc, Value* vp)
1539 {
1540     CallArgs args = CallArgsFromVp(argc, vp);
1541     return CallNonGenericMethod<IsDate, DateObject::getUTCDay_impl>(cx, args);
1542 }
1543 
1544 /* static */ MOZ_ALWAYS_INLINE bool
getHours_impl(JSContext * cx,const CallArgs & args)1545 DateObject::getHours_impl(JSContext* cx, const CallArgs& args)
1546 {
1547     DateObject* dateObj = &args.thisv().toObject().as<DateObject>();
1548     dateObj->fillLocalTimeSlots();
1549 
1550     args.rval().set(dateObj->getReservedSlot(LOCAL_HOURS_SLOT));
1551     return true;
1552 }
1553 
1554 static bool
date_getHours(JSContext * cx,unsigned argc,Value * vp)1555 date_getHours(JSContext* cx, unsigned argc, Value* vp)
1556 {
1557     CallArgs args = CallArgsFromVp(argc, vp);
1558     return CallNonGenericMethod<IsDate, DateObject::getHours_impl>(cx, args);
1559 }
1560 
1561 /* static */ MOZ_ALWAYS_INLINE bool
getUTCHours_impl(JSContext * cx,const CallArgs & args)1562 DateObject::getUTCHours_impl(JSContext* cx, const CallArgs& args)
1563 {
1564     double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
1565     if (IsFinite(result))
1566         result = HourFromTime(result);
1567 
1568     args.rval().setNumber(result);
1569     return true;
1570 }
1571 
1572 static bool
date_getUTCHours(JSContext * cx,unsigned argc,Value * vp)1573 date_getUTCHours(JSContext* cx, unsigned argc, Value* vp)
1574 {
1575     CallArgs args = CallArgsFromVp(argc, vp);
1576     return CallNonGenericMethod<IsDate, DateObject::getUTCHours_impl>(cx, args);
1577 }
1578 
1579 /* static */ MOZ_ALWAYS_INLINE bool
getMinutes_impl(JSContext * cx,const CallArgs & args)1580 DateObject::getMinutes_impl(JSContext* cx, const CallArgs& args)
1581 {
1582     DateObject* dateObj = &args.thisv().toObject().as<DateObject>();
1583     dateObj->fillLocalTimeSlots();
1584 
1585     args.rval().set(dateObj->getReservedSlot(LOCAL_MINUTES_SLOT));
1586     return true;
1587 }
1588 
1589 static bool
date_getMinutes(JSContext * cx,unsigned argc,Value * vp)1590 date_getMinutes(JSContext* cx, unsigned argc, Value* vp)
1591 {
1592     CallArgs args = CallArgsFromVp(argc, vp);
1593     return CallNonGenericMethod<IsDate, DateObject::getMinutes_impl>(cx, args);
1594 }
1595 
1596 /* static */ MOZ_ALWAYS_INLINE bool
getUTCMinutes_impl(JSContext * cx,const CallArgs & args)1597 DateObject::getUTCMinutes_impl(JSContext* cx, const CallArgs& args)
1598 {
1599     double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
1600     if (IsFinite(result))
1601         result = MinFromTime(result);
1602 
1603     args.rval().setNumber(result);
1604     return true;
1605 }
1606 
1607 static bool
date_getUTCMinutes(JSContext * cx,unsigned argc,Value * vp)1608 date_getUTCMinutes(JSContext* cx, unsigned argc, Value* vp)
1609 {
1610     CallArgs args = CallArgsFromVp(argc, vp);
1611     return CallNonGenericMethod<IsDate, DateObject::getUTCMinutes_impl>(cx, args);
1612 }
1613 
1614 /* Date.getSeconds is mapped to getUTCSeconds */
1615 
1616 /* static */ MOZ_ALWAYS_INLINE bool
getUTCSeconds_impl(JSContext * cx,const CallArgs & args)1617 DateObject::getUTCSeconds_impl(JSContext* cx, const CallArgs& args)
1618 {
1619     DateObject* dateObj = &args.thisv().toObject().as<DateObject>();
1620     dateObj->fillLocalTimeSlots();
1621 
1622     args.rval().set(dateObj->getReservedSlot(LOCAL_SECONDS_SLOT));
1623     return true;
1624 }
1625 
1626 static bool
date_getUTCSeconds(JSContext * cx,unsigned argc,Value * vp)1627 date_getUTCSeconds(JSContext* cx, unsigned argc, Value* vp)
1628 {
1629     CallArgs args = CallArgsFromVp(argc, vp);
1630     return CallNonGenericMethod<IsDate, DateObject::getUTCSeconds_impl>(cx, args);
1631 }
1632 
1633 /* Date.getMilliseconds is mapped to getUTCMilliseconds */
1634 
1635 /* static */ MOZ_ALWAYS_INLINE bool
getUTCMilliseconds_impl(JSContext * cx,const CallArgs & args)1636 DateObject::getUTCMilliseconds_impl(JSContext* cx, const CallArgs& args)
1637 {
1638     double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
1639     if (IsFinite(result))
1640         result = msFromTime(result);
1641 
1642     args.rval().setNumber(result);
1643     return true;
1644 }
1645 
1646 static bool
date_getUTCMilliseconds(JSContext * cx,unsigned argc,Value * vp)1647 date_getUTCMilliseconds(JSContext* cx, unsigned argc, Value* vp)
1648 {
1649     CallArgs args = CallArgsFromVp(argc, vp);
1650     return CallNonGenericMethod<IsDate, DateObject::getUTCMilliseconds_impl>(cx, args);
1651 }
1652 
1653 /* static */ MOZ_ALWAYS_INLINE bool
getTimezoneOffset_impl(JSContext * cx,const CallArgs & args)1654 DateObject::getTimezoneOffset_impl(JSContext* cx, const CallArgs& args)
1655 {
1656     DateObject* dateObj = &args.thisv().toObject().as<DateObject>();
1657     double utctime = dateObj->UTCTime().toNumber();
1658     double localtime = dateObj->cachedLocalTime();
1659 
1660     /*
1661      * Return the time zone offset in minutes for the current locale that is
1662      * appropriate for this time. This value would be a constant except for
1663      * daylight savings time.
1664      */
1665     double result = (utctime - localtime) / msPerMinute;
1666     args.rval().setNumber(result);
1667     return true;
1668 }
1669 
1670 static bool
date_getTimezoneOffset(JSContext * cx,unsigned argc,Value * vp)1671 date_getTimezoneOffset(JSContext* cx, unsigned argc, Value* vp)
1672 {
1673     CallArgs args = CallArgsFromVp(argc, vp);
1674     return CallNonGenericMethod<IsDate, DateObject::getTimezoneOffset_impl>(cx, args);
1675 }
1676 
1677 MOZ_ALWAYS_INLINE bool
date_setTime_impl(JSContext * cx,const CallArgs & args)1678 date_setTime_impl(JSContext* cx, const CallArgs& args)
1679 {
1680     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1681     if (args.length() == 0) {
1682         dateObj->setUTCTime(ClippedTime::invalid(), args.rval());
1683         return true;
1684     }
1685 
1686     double result;
1687     if (!ToNumber(cx, args[0], &result))
1688         return false;
1689 
1690     dateObj->setUTCTime(TimeClip(result), args.rval());
1691     return true;
1692 }
1693 
1694 static bool
date_setTime(JSContext * cx,unsigned argc,Value * vp)1695 date_setTime(JSContext* cx, unsigned argc, Value* vp)
1696 {
1697     CallArgs args = CallArgsFromVp(argc, vp);
1698     return CallNonGenericMethod<IsDate, date_setTime_impl>(cx, args);
1699 }
1700 
1701 static bool
GetMsecsOrDefault(JSContext * cx,const CallArgs & args,unsigned i,double t,double * millis)1702 GetMsecsOrDefault(JSContext* cx, const CallArgs& args, unsigned i, double t, double* millis)
1703 {
1704     if (args.length() <= i) {
1705         *millis = msFromTime(t);
1706         return true;
1707     }
1708     return ToNumber(cx, args[i], millis);
1709 }
1710 
1711 static bool
GetSecsOrDefault(JSContext * cx,const CallArgs & args,unsigned i,double t,double * sec)1712 GetSecsOrDefault(JSContext* cx, const CallArgs& args, unsigned i, double t, double* sec)
1713 {
1714     if (args.length() <= i) {
1715         *sec = SecFromTime(t);
1716         return true;
1717     }
1718     return ToNumber(cx, args[i], sec);
1719 }
1720 
1721 static bool
GetMinsOrDefault(JSContext * cx,const CallArgs & args,unsigned i,double t,double * mins)1722 GetMinsOrDefault(JSContext* cx, const CallArgs& args, unsigned i, double t, double* mins)
1723 {
1724     if (args.length() <= i) {
1725         *mins = MinFromTime(t);
1726         return true;
1727     }
1728     return ToNumber(cx, args[i], mins);
1729 }
1730 
1731 /* ES6 20.3.4.23. */
1732 MOZ_ALWAYS_INLINE bool
date_setMilliseconds_impl(JSContext * cx,const CallArgs & args)1733 date_setMilliseconds_impl(JSContext* cx, const CallArgs& args)
1734 {
1735     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1736 
1737     // Steps 1-2.
1738     double t = LocalTime(dateObj->UTCTime().toNumber());
1739 
1740     // Steps 3-4.
1741     double ms;
1742     if (!ToNumber(cx, args.get(0), &ms))
1743         return false;
1744 
1745     // Step 5.
1746     double time = MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms);
1747 
1748     // Step 6.
1749     ClippedTime u = TimeClip(UTC(MakeDate(Day(t), time)));
1750 
1751     // Steps 7-8.
1752     dateObj->setUTCTime(u, args.rval());
1753     return true;
1754 }
1755 
1756 static bool
date_setMilliseconds(JSContext * cx,unsigned argc,Value * vp)1757 date_setMilliseconds(JSContext* cx, unsigned argc, Value* vp)
1758 {
1759     CallArgs args = CallArgsFromVp(argc, vp);
1760     return CallNonGenericMethod<IsDate, date_setMilliseconds_impl>(cx, args);
1761 }
1762 
1763 /* ES5 15.9.5.29. */
1764 MOZ_ALWAYS_INLINE bool
date_setUTCMilliseconds_impl(JSContext * cx,const CallArgs & args)1765 date_setUTCMilliseconds_impl(JSContext* cx, const CallArgs& args)
1766 {
1767     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1768 
1769     /* Step 1. */
1770     double t = dateObj->UTCTime().toNumber();
1771 
1772     /* Step 2. */
1773     double milli;
1774     if (!ToNumber(cx, args.get(0), &milli))
1775         return false;
1776     double time = MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), milli);
1777 
1778     /* Step 3. */
1779     ClippedTime v = TimeClip(MakeDate(Day(t), time));
1780 
1781     /* Steps 4-5. */
1782     dateObj->setUTCTime(v, args.rval());
1783     return true;
1784 }
1785 
1786 static bool
date_setUTCMilliseconds(JSContext * cx,unsigned argc,Value * vp)1787 date_setUTCMilliseconds(JSContext* cx, unsigned argc, Value* vp)
1788 {
1789     CallArgs args = CallArgsFromVp(argc, vp);
1790     return CallNonGenericMethod<IsDate, date_setUTCMilliseconds_impl>(cx, args);
1791 }
1792 
1793 /* ES5 15.9.5.30. */
1794 MOZ_ALWAYS_INLINE bool
date_setSeconds_impl(JSContext * cx,const CallArgs & args)1795 date_setSeconds_impl(JSContext* cx, const CallArgs& args)
1796 {
1797     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1798 
1799     // Steps 1-2.
1800     double t = LocalTime(dateObj->UTCTime().toNumber());
1801 
1802     // Steps 3-4.
1803     double s;
1804     if (!ToNumber(cx, args.get(0), &s))
1805         return false;
1806 
1807     // Steps 5-6.
1808     double milli;
1809     if (!GetMsecsOrDefault(cx, args, 1, t, &milli))
1810         return false;
1811 
1812     // Step 7.
1813     double date = MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli));
1814 
1815     // Step 8.
1816     ClippedTime u = TimeClip(UTC(date));
1817 
1818     // Step 9.
1819     dateObj->setUTCTime(u, args.rval());
1820     return true;
1821 }
1822 
1823 /* ES6 20.3.4.26. */
1824 static bool
date_setSeconds(JSContext * cx,unsigned argc,Value * vp)1825 date_setSeconds(JSContext* cx, unsigned argc, Value* vp)
1826 {
1827     CallArgs args = CallArgsFromVp(argc, vp);
1828     return CallNonGenericMethod<IsDate, date_setSeconds_impl>(cx, args);
1829 }
1830 
1831 MOZ_ALWAYS_INLINE bool
date_setUTCSeconds_impl(JSContext * cx,const CallArgs & args)1832 date_setUTCSeconds_impl(JSContext* cx, const CallArgs& args)
1833 {
1834     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1835 
1836     /* Step 1. */
1837     double t = dateObj->UTCTime().toNumber();
1838 
1839     /* Step 2. */
1840     double s;
1841     if (!ToNumber(cx, args.get(0), &s))
1842         return false;
1843 
1844     /* Step 3. */
1845     double milli;
1846     if (!GetMsecsOrDefault(cx, args, 1, t, &milli))
1847         return false;
1848 
1849     /* Step 4. */
1850     double date = MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli));
1851 
1852     /* Step 5. */
1853     ClippedTime v = TimeClip(date);
1854 
1855     /* Steps 6-7. */
1856     dateObj->setUTCTime(v, args.rval());
1857     return true;
1858 }
1859 
1860 /* ES5 15.9.5.32. */
1861 static bool
date_setUTCSeconds(JSContext * cx,unsigned argc,Value * vp)1862 date_setUTCSeconds(JSContext* cx, unsigned argc, Value* vp)
1863 {
1864     CallArgs args = CallArgsFromVp(argc, vp);
1865     return CallNonGenericMethod<IsDate, date_setUTCSeconds_impl>(cx, args);
1866 }
1867 
1868 MOZ_ALWAYS_INLINE bool
date_setMinutes_impl(JSContext * cx,const CallArgs & args)1869 date_setMinutes_impl(JSContext* cx, const CallArgs& args)
1870 {
1871     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1872 
1873     // Steps 1-2.
1874     double t = LocalTime(dateObj->UTCTime().toNumber());
1875 
1876     // Steps 3-4.
1877     double m;
1878     if (!ToNumber(cx, args.get(0), &m))
1879         return false;
1880 
1881     // Steps 5-6.
1882     double s;
1883     if (!GetSecsOrDefault(cx, args, 1, t, &s))
1884         return false;
1885 
1886     // Steps 7-8.
1887     double milli;
1888     if (!GetMsecsOrDefault(cx, args, 2, t, &milli))
1889         return false;
1890 
1891     // Step 9.
1892     double date = MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli));
1893 
1894     // Step 10.
1895     ClippedTime u = TimeClip(UTC(date));
1896 
1897     // Steps 11-12.
1898     dateObj->setUTCTime(u, args.rval());
1899     return true;
1900 }
1901 
1902 /* ES6 20.3.4.24. */
1903 static bool
date_setMinutes(JSContext * cx,unsigned argc,Value * vp)1904 date_setMinutes(JSContext* cx, unsigned argc, Value* vp)
1905 {
1906     // Steps 1-2 (the effectful parts).
1907     CallArgs args = CallArgsFromVp(argc, vp);
1908     return CallNonGenericMethod<IsDate, date_setMinutes_impl>(cx, args);
1909 }
1910 
1911 MOZ_ALWAYS_INLINE bool
date_setUTCMinutes_impl(JSContext * cx,const CallArgs & args)1912 date_setUTCMinutes_impl(JSContext* cx, const CallArgs& args)
1913 {
1914     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1915 
1916     /* Step 1. */
1917     double t = dateObj->UTCTime().toNumber();
1918 
1919     /* Step 2. */
1920     double m;
1921     if (!ToNumber(cx, args.get(0), &m))
1922         return false;
1923 
1924     /* Step 3. */
1925     double s;
1926     if (!GetSecsOrDefault(cx, args, 1, t, &s))
1927         return false;
1928 
1929     /* Step 4. */
1930     double milli;
1931     if (!GetMsecsOrDefault(cx, args, 2, t, &milli))
1932         return false;
1933 
1934     /* Step 5. */
1935     double date = MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli));
1936 
1937     /* Step 6. */
1938     ClippedTime v = TimeClip(date);
1939 
1940     /* Steps 7-8. */
1941     dateObj->setUTCTime(v, args.rval());
1942     return true;
1943 }
1944 
1945 /* ES5 15.9.5.34. */
1946 static bool
date_setUTCMinutes(JSContext * cx,unsigned argc,Value * vp)1947 date_setUTCMinutes(JSContext* cx, unsigned argc, Value* vp)
1948 {
1949     CallArgs args = CallArgsFromVp(argc, vp);
1950     return CallNonGenericMethod<IsDate, date_setUTCMinutes_impl>(cx, args);
1951 }
1952 
1953 MOZ_ALWAYS_INLINE bool
date_setHours_impl(JSContext * cx,const CallArgs & args)1954 date_setHours_impl(JSContext* cx, const CallArgs& args)
1955 {
1956     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1957 
1958     // Steps 1-2.
1959     double t = LocalTime(dateObj->UTCTime().toNumber());
1960 
1961     // Steps 3-4.
1962     double h;
1963     if (!ToNumber(cx, args.get(0), &h))
1964         return false;
1965 
1966     // Steps 5-6.
1967     double m;
1968     if (!GetMinsOrDefault(cx, args, 1, t, &m))
1969         return false;
1970 
1971     // Steps 7-8.
1972     double s;
1973     if (!GetSecsOrDefault(cx, args, 2, t, &s))
1974         return false;
1975 
1976     // Steps 9-10.
1977     double milli;
1978     if (!GetMsecsOrDefault(cx, args, 3, t, &milli))
1979         return false;
1980 
1981     // Step 11.
1982     double date = MakeDate(Day(t), MakeTime(h, m, s, milli));
1983 
1984     // Step 12.
1985     ClippedTime u = TimeClip(UTC(date));
1986 
1987     // Steps 13-14.
1988     dateObj->setUTCTime(u, args.rval());
1989     return true;
1990 }
1991 
1992 /* ES5 15.9.5.35. */
1993 static bool
date_setHours(JSContext * cx,unsigned argc,Value * vp)1994 date_setHours(JSContext* cx, unsigned argc, Value* vp)
1995 {
1996     CallArgs args = CallArgsFromVp(argc, vp);
1997     return CallNonGenericMethod<IsDate, date_setHours_impl>(cx, args);
1998 }
1999 
2000 MOZ_ALWAYS_INLINE bool
date_setUTCHours_impl(JSContext * cx,const CallArgs & args)2001 date_setUTCHours_impl(JSContext* cx, const CallArgs& args)
2002 {
2003     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2004 
2005     /* Step 1. */
2006     double t = dateObj->UTCTime().toNumber();
2007 
2008     /* Step 2. */
2009     double h;
2010     if (!ToNumber(cx, args.get(0), &h))
2011         return false;
2012 
2013     /* Step 3. */
2014     double m;
2015     if (!GetMinsOrDefault(cx, args, 1, t, &m))
2016         return false;
2017 
2018     /* Step 4. */
2019     double s;
2020     if (!GetSecsOrDefault(cx, args, 2, t, &s))
2021         return false;
2022 
2023     /* Step 5. */
2024     double milli;
2025     if (!GetMsecsOrDefault(cx, args, 3, t, &milli))
2026         return false;
2027 
2028     /* Step 6. */
2029     double newDate = MakeDate(Day(t), MakeTime(h, m, s, milli));
2030 
2031     /* Step 7. */
2032     ClippedTime v = TimeClip(newDate);
2033 
2034     /* Steps 8-9. */
2035     dateObj->setUTCTime(v, args.rval());
2036     return true;
2037 }
2038 
2039 /* ES5 15.9.5.36. */
2040 static bool
date_setUTCHours(JSContext * cx,unsigned argc,Value * vp)2041 date_setUTCHours(JSContext* cx, unsigned argc, Value* vp)
2042 {
2043     CallArgs args = CallArgsFromVp(argc, vp);
2044     return CallNonGenericMethod<IsDate, date_setUTCHours_impl>(cx, args);
2045 }
2046 
2047 MOZ_ALWAYS_INLINE bool
date_setDate_impl(JSContext * cx,const CallArgs & args)2048 date_setDate_impl(JSContext* cx, const CallArgs& args)
2049 {
2050     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2051 
2052     /* Step 1. */
2053     double t = LocalTime(dateObj->UTCTime().toNumber());
2054 
2055     /* Step 2. */
2056     double date;
2057     if (!ToNumber(cx, args.get(0), &date))
2058         return false;
2059 
2060     /* Step 3. */
2061     double newDate = MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t));
2062 
2063     /* Step 4. */
2064     ClippedTime u = TimeClip(UTC(newDate));
2065 
2066     /* Steps 5-6. */
2067     dateObj->setUTCTime(u, args.rval());
2068     return true;
2069 }
2070 
2071 /* ES5 15.9.5.37. */
2072 static bool
date_setDate(JSContext * cx,unsigned argc,Value * vp)2073 date_setDate(JSContext* cx, unsigned argc, Value* vp)
2074 {
2075     CallArgs args = CallArgsFromVp(argc, vp);
2076     return CallNonGenericMethod<IsDate, date_setDate_impl>(cx, args);
2077 }
2078 
2079 MOZ_ALWAYS_INLINE bool
date_setUTCDate_impl(JSContext * cx,const CallArgs & args)2080 date_setUTCDate_impl(JSContext* cx, const CallArgs& args)
2081 {
2082     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2083 
2084     /* Step 1. */
2085     double t = dateObj->UTCTime().toNumber();
2086 
2087     /* Step 2. */
2088     double date;
2089     if (!ToNumber(cx, args.get(0), &date))
2090         return false;
2091 
2092     /* Step 3. */
2093     double newDate = MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t));
2094 
2095     /* Step 4. */
2096     ClippedTime v = TimeClip(newDate);
2097 
2098     /* Steps 5-6. */
2099     dateObj->setUTCTime(v, args.rval());
2100     return true;
2101 }
2102 
2103 static bool
date_setUTCDate(JSContext * cx,unsigned argc,Value * vp)2104 date_setUTCDate(JSContext* cx, unsigned argc, Value* vp)
2105 {
2106     CallArgs args = CallArgsFromVp(argc, vp);
2107     return CallNonGenericMethod<IsDate, date_setUTCDate_impl>(cx, args);
2108 }
2109 
2110 static bool
GetDateOrDefault(JSContext * cx,const CallArgs & args,unsigned i,double t,double * date)2111 GetDateOrDefault(JSContext* cx, const CallArgs& args, unsigned i, double t, double* date)
2112 {
2113     if (args.length() <= i) {
2114         *date = DateFromTime(t);
2115         return true;
2116     }
2117     return ToNumber(cx, args[i], date);
2118 }
2119 
2120 static bool
GetMonthOrDefault(JSContext * cx,const CallArgs & args,unsigned i,double t,double * month)2121 GetMonthOrDefault(JSContext* cx, const CallArgs& args, unsigned i, double t, double* month)
2122 {
2123     if (args.length() <= i) {
2124         *month = MonthFromTime(t);
2125         return true;
2126     }
2127     return ToNumber(cx, args[i], month);
2128 }
2129 
2130 /* ES5 15.9.5.38. */
2131 MOZ_ALWAYS_INLINE bool
date_setMonth_impl(JSContext * cx,const CallArgs & args)2132 date_setMonth_impl(JSContext* cx, const CallArgs& args)
2133 {
2134     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2135 
2136     /* Step 1. */
2137     double t = LocalTime(dateObj->UTCTime().toNumber());
2138 
2139     /* Step 2. */
2140     double m;
2141     if (!ToNumber(cx, args.get(0), &m))
2142         return false;
2143 
2144     /* Step 3. */
2145     double date;
2146     if (!GetDateOrDefault(cx, args, 1, t, &date))
2147         return false;
2148 
2149     /* Step 4. */
2150     double newDate = MakeDate(MakeDay(YearFromTime(t), m, date), TimeWithinDay(t));
2151 
2152     /* Step 5. */
2153     ClippedTime u = TimeClip(UTC(newDate));
2154 
2155     /* Steps 6-7. */
2156     dateObj->setUTCTime(u, args.rval());
2157     return true;
2158 }
2159 
2160 static bool
date_setMonth(JSContext * cx,unsigned argc,Value * vp)2161 date_setMonth(JSContext* cx, unsigned argc, Value* vp)
2162 {
2163     CallArgs args = CallArgsFromVp(argc, vp);
2164     return CallNonGenericMethod<IsDate, date_setMonth_impl>(cx, args);
2165 }
2166 
2167 /* ES5 15.9.5.39. */
2168 MOZ_ALWAYS_INLINE bool
date_setUTCMonth_impl(JSContext * cx,const CallArgs & args)2169 date_setUTCMonth_impl(JSContext* cx, const CallArgs& args)
2170 {
2171     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2172 
2173     /* Step 1. */
2174     double t = dateObj->UTCTime().toNumber();
2175 
2176     /* Step 2. */
2177     double m;
2178     if (!ToNumber(cx, args.get(0), &m))
2179         return false;
2180 
2181     /* Step 3. */
2182     double date;
2183     if (!GetDateOrDefault(cx, args, 1, t, &date))
2184         return false;
2185 
2186     /* Step 4. */
2187     double newDate = MakeDate(MakeDay(YearFromTime(t), m, date), TimeWithinDay(t));
2188 
2189     /* Step 5. */
2190     ClippedTime v = TimeClip(newDate);
2191 
2192     /* Steps 6-7. */
2193     dateObj->setUTCTime(v, args.rval());
2194     return true;
2195 }
2196 
2197 static bool
date_setUTCMonth(JSContext * cx,unsigned argc,Value * vp)2198 date_setUTCMonth(JSContext* cx, unsigned argc, Value* vp)
2199 {
2200     CallArgs args = CallArgsFromVp(argc, vp);
2201     return CallNonGenericMethod<IsDate, date_setUTCMonth_impl>(cx, args);
2202 }
2203 
2204 static double
ThisLocalTimeOrZero(Handle<DateObject * > dateObj)2205 ThisLocalTimeOrZero(Handle<DateObject*> dateObj)
2206 {
2207     double t = dateObj->UTCTime().toNumber();
2208     if (IsNaN(t))
2209         return +0;
2210     return LocalTime(t);
2211 }
2212 
2213 static double
ThisUTCTimeOrZero(Handle<DateObject * > dateObj)2214 ThisUTCTimeOrZero(Handle<DateObject*> dateObj)
2215 {
2216     double t = dateObj->as<DateObject>().UTCTime().toNumber();
2217     return IsNaN(t) ? +0 : t;
2218 }
2219 
2220 /* ES5 15.9.5.40. */
2221 MOZ_ALWAYS_INLINE bool
date_setFullYear_impl(JSContext * cx,const CallArgs & args)2222 date_setFullYear_impl(JSContext* cx, const CallArgs& args)
2223 {
2224     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2225 
2226     /* Step 1. */
2227     double t = ThisLocalTimeOrZero(dateObj);
2228 
2229     /* Step 2. */
2230     double y;
2231     if (!ToNumber(cx, args.get(0), &y))
2232         return false;
2233 
2234     /* Step 3. */
2235     double m;
2236     if (!GetMonthOrDefault(cx, args, 1, t, &m))
2237         return false;
2238 
2239     /* Step 4. */
2240     double date;
2241     if (!GetDateOrDefault(cx, args, 2, t, &date))
2242         return false;
2243 
2244     /* Step 5. */
2245     double newDate = MakeDate(MakeDay(y, m, date), TimeWithinDay(t));
2246 
2247     /* Step 6. */
2248     ClippedTime u = TimeClip(UTC(newDate));
2249 
2250     /* Steps 7-8. */
2251     dateObj->setUTCTime(u, args.rval());
2252     return true;
2253 }
2254 
2255 static bool
date_setFullYear(JSContext * cx,unsigned argc,Value * vp)2256 date_setFullYear(JSContext* cx, unsigned argc, Value* vp)
2257 {
2258     CallArgs args = CallArgsFromVp(argc, vp);
2259     return CallNonGenericMethod<IsDate, date_setFullYear_impl>(cx, args);
2260 }
2261 
2262 /* ES5 15.9.5.41. */
2263 MOZ_ALWAYS_INLINE bool
date_setUTCFullYear_impl(JSContext * cx,const CallArgs & args)2264 date_setUTCFullYear_impl(JSContext* cx, const CallArgs& args)
2265 {
2266     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2267 
2268     /* Step 1. */
2269     double t = ThisUTCTimeOrZero(dateObj);
2270 
2271     /* Step 2. */
2272     double y;
2273     if (!ToNumber(cx, args.get(0), &y))
2274         return false;
2275 
2276     /* Step 3. */
2277     double m;
2278     if (!GetMonthOrDefault(cx, args, 1, t, &m))
2279         return false;
2280 
2281     /* Step 4. */
2282     double date;
2283     if (!GetDateOrDefault(cx, args, 2, t, &date))
2284         return false;
2285 
2286     /* Step 5. */
2287     double newDate = MakeDate(MakeDay(y, m, date), TimeWithinDay(t));
2288 
2289     /* Step 6. */
2290     ClippedTime v = TimeClip(newDate);
2291 
2292     /* Steps 7-8. */
2293     dateObj->setUTCTime(v, args.rval());
2294     return true;
2295 }
2296 
2297 static bool
date_setUTCFullYear(JSContext * cx,unsigned argc,Value * vp)2298 date_setUTCFullYear(JSContext* cx, unsigned argc, Value* vp)
2299 {
2300     CallArgs args = CallArgsFromVp(argc, vp);
2301     return CallNonGenericMethod<IsDate, date_setUTCFullYear_impl>(cx, args);
2302 }
2303 
2304 /* ES5 Annex B.2.5. */
2305 MOZ_ALWAYS_INLINE bool
date_setYear_impl(JSContext * cx,const CallArgs & args)2306 date_setYear_impl(JSContext* cx, const CallArgs& args)
2307 {
2308     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2309 
2310     /* Step 1. */
2311     double t = ThisLocalTimeOrZero(dateObj);
2312 
2313     /* Step 2. */
2314     double y;
2315     if (!ToNumber(cx, args.get(0), &y))
2316         return false;
2317 
2318     /* Step 3. */
2319     if (IsNaN(y)) {
2320         dateObj->setUTCTime(ClippedTime::invalid(), args.rval());
2321         return true;
2322     }
2323 
2324     /* Step 4. */
2325     double yint = ToInteger(y);
2326     if (0 <= yint && yint <= 99)
2327         yint += 1900;
2328 
2329     /* Step 5. */
2330     double day = MakeDay(yint, MonthFromTime(t), DateFromTime(t));
2331 
2332     /* Step 6. */
2333     double u = UTC(MakeDate(day, TimeWithinDay(t)));
2334 
2335     /* Steps 7-8. */
2336     dateObj->setUTCTime(TimeClip(u), args.rval());
2337     return true;
2338 }
2339 
2340 static bool
date_setYear(JSContext * cx,unsigned argc,Value * vp)2341 date_setYear(JSContext* cx, unsigned argc, Value* vp)
2342 {
2343     CallArgs args = CallArgsFromVp(argc, vp);
2344     return CallNonGenericMethod<IsDate, date_setYear_impl>(cx, args);
2345 }
2346 
2347 /* constants for toString, toUTCString */
2348 static const char js_NaN_date_str[] = "Invalid Date";
2349 static const char * const days[] =
2350 {
2351    "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
2352 };
2353 static const char * const months[] =
2354 {
2355    "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
2356 };
2357 
2358 
2359 // Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
2360 // requires a PRMJTime... which only has 16-bit years.  Sub-ECMA.
2361 static void
print_gmt_string(char * buf,size_t size,double utctime)2362 print_gmt_string(char* buf, size_t size, double utctime)
2363 {
2364     MOZ_ASSERT(NumbersAreIdentical(TimeClip(utctime).toDouble(), utctime));
2365     JS_snprintf(buf, size, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
2366                 days[int(WeekDay(utctime))],
2367                 int(DateFromTime(utctime)),
2368                 months[int(MonthFromTime(utctime))],
2369                 int(YearFromTime(utctime)),
2370                 int(HourFromTime(utctime)),
2371                 int(MinFromTime(utctime)),
2372                 int(SecFromTime(utctime)));
2373 }
2374 
2375 static void
print_iso_string(char * buf,size_t size,double utctime)2376 print_iso_string(char* buf, size_t size, double utctime)
2377 {
2378     MOZ_ASSERT(NumbersAreIdentical(TimeClip(utctime).toDouble(), utctime));
2379     JS_snprintf(buf, size, "%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.3dZ",
2380                 int(YearFromTime(utctime)),
2381                 int(MonthFromTime(utctime)) + 1,
2382                 int(DateFromTime(utctime)),
2383                 int(HourFromTime(utctime)),
2384                 int(MinFromTime(utctime)),
2385                 int(SecFromTime(utctime)),
2386                 int(msFromTime(utctime)));
2387 }
2388 
2389 static void
print_iso_extended_string(char * buf,size_t size,double utctime)2390 print_iso_extended_string(char* buf, size_t size, double utctime)
2391 {
2392     MOZ_ASSERT(NumbersAreIdentical(TimeClip(utctime).toDouble(), utctime));
2393     JS_snprintf(buf, size, "%+.6d-%.2d-%.2dT%.2d:%.2d:%.2d.%.3dZ",
2394                 int(YearFromTime(utctime)),
2395                 int(MonthFromTime(utctime)) + 1,
2396                 int(DateFromTime(utctime)),
2397                 int(HourFromTime(utctime)),
2398                 int(MinFromTime(utctime)),
2399                 int(SecFromTime(utctime)),
2400                 int(msFromTime(utctime)));
2401 }
2402 
2403 /* ES5 B.2.6. */
2404 MOZ_ALWAYS_INLINE bool
date_toGMTString_impl(JSContext * cx,const CallArgs & args)2405 date_toGMTString_impl(JSContext* cx, const CallArgs& args)
2406 {
2407     double utctime = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
2408 
2409     char buf[100];
2410     if (!IsFinite(utctime))
2411         JS_snprintf(buf, sizeof buf, js_NaN_date_str);
2412     else
2413         print_gmt_string(buf, sizeof buf, utctime);
2414 
2415     JSString* str = JS_NewStringCopyZ(cx, buf);
2416     if (!str)
2417         return false;
2418     args.rval().setString(str);
2419     return true;
2420 }
2421 
2422 static bool
date_toGMTString(JSContext * cx,unsigned argc,Value * vp)2423 date_toGMTString(JSContext* cx, unsigned argc, Value* vp)
2424 {
2425     CallArgs args = CallArgsFromVp(argc, vp);
2426     return CallNonGenericMethod<IsDate, date_toGMTString_impl>(cx, args);
2427 }
2428 
2429 /* ES6 draft 2015-01-15 20.3.4.36. */
2430 MOZ_ALWAYS_INLINE bool
date_toISOString_impl(JSContext * cx,const CallArgs & args)2431 date_toISOString_impl(JSContext* cx, const CallArgs& args)
2432 {
2433     double utctime = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
2434     if (!IsFinite(utctime)) {
2435         JS_ReportErrorNumber(cx, js::GetErrorMessage, nullptr, JSMSG_INVALID_DATE);
2436         return false;
2437     }
2438 
2439     char buf[100];
2440     int year = int(YearFromTime(utctime));
2441     if (year < 0 || year > 9999)
2442         print_iso_extended_string(buf, sizeof buf, utctime);
2443     else
2444         print_iso_string(buf, sizeof buf, utctime);
2445 
2446     JSString* str = JS_NewStringCopyZ(cx, buf);
2447     if (!str)
2448         return false;
2449     args.rval().setString(str);
2450     return true;
2451 
2452 }
2453 
2454 static bool
date_toISOString(JSContext * cx,unsigned argc,Value * vp)2455 date_toISOString(JSContext* cx, unsigned argc, Value* vp)
2456 {
2457     CallArgs args = CallArgsFromVp(argc, vp);
2458     return CallNonGenericMethod<IsDate, date_toISOString_impl>(cx, args);
2459 }
2460 
2461 /* ES5 15.9.5.44. */
2462 static bool
date_toJSON(JSContext * cx,unsigned argc,Value * vp)2463 date_toJSON(JSContext* cx, unsigned argc, Value* vp)
2464 {
2465     CallArgs args = CallArgsFromVp(argc, vp);
2466 
2467     /* Step 1. */
2468     RootedObject obj(cx, ToObject(cx, args.thisv()));
2469     if (!obj)
2470         return false;
2471 
2472     /* Step 2. */
2473     RootedValue tv(cx, ObjectValue(*obj));
2474     if (!ToPrimitive(cx, JSTYPE_NUMBER, &tv))
2475         return false;
2476 
2477     /* Step 3. */
2478     if (tv.isDouble() && !IsFinite(tv.toDouble())) {
2479         args.rval().setNull();
2480         return true;
2481     }
2482 
2483     /* Step 4. */
2484     RootedValue toISO(cx);
2485     if (!GetProperty(cx, obj, obj, cx->names().toISOString, &toISO))
2486         return false;
2487 
2488     /* Step 5. */
2489     if (!IsCallable(toISO)) {
2490         JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js::GetErrorMessage, nullptr,
2491                                      JSMSG_BAD_TOISOSTRING_PROP);
2492         return false;
2493     }
2494 
2495     /* Step 6. */
2496     InvokeArgs args2(cx);
2497     if (!args2.init(cx, 0))
2498         return false;
2499 
2500     args2.setCallee(toISO);
2501     args2.setThis(ObjectValue(*obj));
2502 
2503     if (!Invoke(cx, args2))
2504         return false;
2505     args.rval().set(args2.rval());
2506     return true;
2507 }
2508 
2509 /* for Date.toLocaleFormat; interface to PRMJTime date struct.
2510  */
2511 static void
new_explode(double timeval,PRMJTime * split)2512 new_explode(double timeval, PRMJTime* split)
2513 {
2514     double year = YearFromTime(timeval);
2515 
2516     split->tm_usec = int32_t(msFromTime(timeval)) * 1000;
2517     split->tm_sec = int8_t(SecFromTime(timeval));
2518     split->tm_min = int8_t(MinFromTime(timeval));
2519     split->tm_hour = int8_t(HourFromTime(timeval));
2520     split->tm_mday = int8_t(DateFromTime(timeval));
2521     split->tm_mon = int8_t(MonthFromTime(timeval));
2522     split->tm_wday = int8_t(WeekDay(timeval));
2523     split->tm_year = year;
2524     split->tm_yday = int16_t(DayWithinYear(timeval, year));
2525 
2526     /* not sure how this affects things, but it doesn't seem
2527        to matter. */
2528     split->tm_isdst = (DaylightSavingTA(timeval) != 0);
2529 }
2530 
2531 typedef enum formatspec {
2532     FORMATSPEC_FULL, FORMATSPEC_DATE, FORMATSPEC_TIME
2533 } formatspec;
2534 
2535 /* helper function */
2536 static bool
date_format(JSContext * cx,double date,formatspec format,MutableHandleValue rval)2537 date_format(JSContext* cx, double date, formatspec format, MutableHandleValue rval)
2538 {
2539     char buf[100];
2540     char tzbuf[100];
2541     bool usetz;
2542     size_t i, tzlen;
2543     PRMJTime split;
2544 
2545     if (!IsFinite(date)) {
2546         JS_snprintf(buf, sizeof buf, js_NaN_date_str);
2547     } else {
2548         MOZ_ASSERT(NumbersAreIdentical(TimeClip(date).toDouble(), date));
2549 
2550         double local = LocalTime(date);
2551 
2552         /* offset from GMT in minutes.  The offset includes daylight savings,
2553            if it applies. */
2554         int minutes = (int) floor(AdjustTime(date) / msPerMinute);
2555 
2556         /* map 510 minutes to 0830 hours */
2557         int offset = (minutes / 60) * 100 + minutes % 60;
2558 
2559         /* print as "Wed Nov 05 19:38:03 GMT-0800 (PST) 1997" The TZA is
2560          * printed as 'GMT-0800' rather than as 'PST' to avoid
2561          * operating-system dependence on strftime (which
2562          * PRMJ_FormatTimeUSEnglish calls, for %Z only.)  win32 prints
2563          * PST as 'Pacific Standard Time.'  This way we always know
2564          * what we're getting, and can parse it if we produce it.
2565          * The OS TZA string is included as a comment.
2566          */
2567 
2568         /* get a timezone string from the OS to include as a
2569            comment. */
2570         new_explode(date, &split);
2571         if (PRMJ_FormatTime(tzbuf, sizeof tzbuf, "(%Z)", &split) != 0) {
2572 
2573             /* Decide whether to use the resulting timezone string.
2574              *
2575              * Reject it if it contains any non-ASCII, non-alphanumeric
2576              * characters.  It's then likely in some other character
2577              * encoding, and we probably won't display it correctly.
2578              */
2579             usetz = true;
2580             tzlen = strlen(tzbuf);
2581             if (tzlen > 100) {
2582                 usetz = false;
2583             } else {
2584                 for (i = 0; i < tzlen; i++) {
2585                     char16_t c = tzbuf[i];
2586                     if (c > 127 ||
2587                         !(isalpha(c) || isdigit(c) ||
2588                           c == ' ' || c == '(' || c == ')' || c == '.')) {
2589                         usetz = false;
2590                     }
2591                 }
2592             }
2593 
2594             /* Also reject it if it's not parenthesized or if it's '()'. */
2595             if (tzbuf[0] != '(' || tzbuf[1] == ')')
2596                 usetz = false;
2597         } else
2598             usetz = false;
2599 
2600         switch (format) {
2601           case FORMATSPEC_FULL:
2602             /*
2603              * Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
2604              * requires a PRMJTime... which only has 16-bit years.  Sub-ECMA.
2605              */
2606             /* Tue Oct 31 2000 09:41:40 GMT-0800 (PST) */
2607             JS_snprintf(buf, sizeof buf,
2608                         "%s %s %.2d %.4d %.2d:%.2d:%.2d GMT%+.4d%s%s",
2609                         days[int(WeekDay(local))],
2610                         months[int(MonthFromTime(local))],
2611                         int(DateFromTime(local)),
2612                         int(YearFromTime(local)),
2613                         int(HourFromTime(local)),
2614                         int(MinFromTime(local)),
2615                         int(SecFromTime(local)),
2616                         offset,
2617                         usetz ? " " : "",
2618                         usetz ? tzbuf : "");
2619             break;
2620           case FORMATSPEC_DATE:
2621             /* Tue Oct 31 2000 */
2622             JS_snprintf(buf, sizeof buf,
2623                         "%s %s %.2d %.4d",
2624                         days[int(WeekDay(local))],
2625                         months[int(MonthFromTime(local))],
2626                         int(DateFromTime(local)),
2627                         int(YearFromTime(local)));
2628             break;
2629           case FORMATSPEC_TIME:
2630             /* 09:41:40 GMT-0800 (PST) */
2631             JS_snprintf(buf, sizeof buf,
2632                         "%.2d:%.2d:%.2d GMT%+.4d%s%s",
2633                         int(HourFromTime(local)),
2634                         int(MinFromTime(local)),
2635                         int(SecFromTime(local)),
2636                         offset,
2637                         usetz ? " " : "",
2638                         usetz ? tzbuf : "");
2639             break;
2640         }
2641     }
2642 
2643     JSString* str = JS_NewStringCopyZ(cx, buf);
2644     if (!str)
2645         return false;
2646     rval.setString(str);
2647     return true;
2648 }
2649 
2650 static bool
ToLocaleFormatHelper(JSContext * cx,HandleObject obj,const char * format,MutableHandleValue rval)2651 ToLocaleFormatHelper(JSContext* cx, HandleObject obj, const char* format, MutableHandleValue rval)
2652 {
2653     double utctime = obj->as<DateObject>().UTCTime().toNumber();
2654 
2655     char buf[100];
2656     if (!IsFinite(utctime)) {
2657         JS_snprintf(buf, sizeof buf, js_NaN_date_str);
2658     } else {
2659         int result_len;
2660         double local = LocalTime(utctime);
2661         PRMJTime split;
2662         new_explode(local, &split);
2663 
2664         /* Let PRMJTime format it. */
2665         result_len = PRMJ_FormatTime(buf, sizeof buf, format, &split);
2666 
2667         /* If it failed, default to toString. */
2668         if (result_len == 0)
2669             return date_format(cx, utctime, FORMATSPEC_FULL, rval);
2670 
2671         /* Hacked check against undesired 2-digit year 00/00/00 form. */
2672         if (strcmp(format, "%x") == 0 && result_len >= 6 &&
2673             /* Format %x means use OS settings, which may have 2-digit yr, so
2674                hack end of 3/11/22 or 11.03.22 or 11Mar22 to use 4-digit yr...*/
2675             !isdigit(buf[result_len - 3]) &&
2676             isdigit(buf[result_len - 2]) && isdigit(buf[result_len - 1]) &&
2677             /* ...but not if starts with 4-digit year, like 2022/3/11. */
2678             !(isdigit(buf[0]) && isdigit(buf[1]) &&
2679               isdigit(buf[2]) && isdigit(buf[3]))) {
2680             double localtime = obj->as<DateObject>().cachedLocalTime();
2681             int year = IsNaN(localtime) ? 0 : (int) YearFromTime(localtime);
2682             JS_snprintf(buf + (result_len - 2), (sizeof buf) - (result_len - 2),
2683                         "%d", year);
2684         }
2685 
2686     }
2687 
2688     if (cx->runtime()->localeCallbacks && cx->runtime()->localeCallbacks->localeToUnicode)
2689         return cx->runtime()->localeCallbacks->localeToUnicode(cx, buf, rval);
2690 
2691     JSString* str = JS_NewStringCopyZ(cx, buf);
2692     if (!str)
2693         return false;
2694     rval.setString(str);
2695     return true;
2696 }
2697 
2698 #if !EXPOSE_INTL_API
2699 static bool
ToLocaleStringHelper(JSContext * cx,Handle<DateObject * > dateObj,MutableHandleValue rval)2700 ToLocaleStringHelper(JSContext* cx, Handle<DateObject*> dateObj, MutableHandleValue rval)
2701 {
2702     /*
2703      * Use '%#c' for windows, because '%c' is backward-compatible and non-y2k
2704      * with msvc; '%#c' requests that a full year be used in the result string.
2705      */
2706     return ToLocaleFormatHelper(cx, dateObj,
2707 #if defined(_WIN32) && !defined(__MWERKS__)
2708                           "%#c"
2709 #else
2710                           "%c"
2711 #endif
2712                          , rval);
2713 }
2714 
2715 /* ES5 15.9.5.5. */
2716 MOZ_ALWAYS_INLINE bool
date_toLocaleString_impl(JSContext * cx,const CallArgs & args)2717 date_toLocaleString_impl(JSContext* cx, const CallArgs& args)
2718 {
2719     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2720     return ToLocaleStringHelper(cx, dateObj, args.rval());
2721 }
2722 
2723 static bool
date_toLocaleString(JSContext * cx,unsigned argc,Value * vp)2724 date_toLocaleString(JSContext* cx, unsigned argc, Value* vp)
2725 {
2726     CallArgs args = CallArgsFromVp(argc, vp);
2727     return CallNonGenericMethod<IsDate, date_toLocaleString_impl>(cx, args);
2728 }
2729 
2730 /* ES5 15.9.5.6. */
2731 MOZ_ALWAYS_INLINE bool
date_toLocaleDateString_impl(JSContext * cx,const CallArgs & args)2732 date_toLocaleDateString_impl(JSContext* cx, const CallArgs& args)
2733 {
2734     /*
2735      * Use '%#x' for windows, because '%x' is backward-compatible and non-y2k
2736      * with msvc; '%#x' requests that a full year be used in the result string.
2737      */
2738     static const char format[] =
2739 #if defined(_WIN32) && !defined(__MWERKS__)
2740                                    "%#x"
2741 #else
2742                                    "%x"
2743 #endif
2744                                    ;
2745 
2746     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2747     return ToLocaleFormatHelper(cx, dateObj, format, args.rval());
2748 }
2749 
2750 static bool
date_toLocaleDateString(JSContext * cx,unsigned argc,Value * vp)2751 date_toLocaleDateString(JSContext* cx, unsigned argc, Value* vp)
2752 {
2753     CallArgs args = CallArgsFromVp(argc, vp);
2754     return CallNonGenericMethod<IsDate, date_toLocaleDateString_impl>(cx, args);
2755 }
2756 
2757 /* ES5 15.9.5.7. */
2758 MOZ_ALWAYS_INLINE bool
date_toLocaleTimeString_impl(JSContext * cx,const CallArgs & args)2759 date_toLocaleTimeString_impl(JSContext* cx, const CallArgs& args)
2760 {
2761     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2762     return ToLocaleFormatHelper(cx, dateObj, "%X", args.rval());
2763 }
2764 
2765 static bool
date_toLocaleTimeString(JSContext * cx,unsigned argc,Value * vp)2766 date_toLocaleTimeString(JSContext* cx, unsigned argc, Value* vp)
2767 {
2768     CallArgs args = CallArgsFromVp(argc, vp);
2769     return CallNonGenericMethod<IsDate, date_toLocaleTimeString_impl>(cx, args);
2770 }
2771 #endif /* !EXPOSE_INTL_API */
2772 
2773 MOZ_ALWAYS_INLINE bool
date_toLocaleFormat_impl(JSContext * cx,const CallArgs & args)2774 date_toLocaleFormat_impl(JSContext* cx, const CallArgs& args)
2775 {
2776     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2777 
2778     if (args.length() == 0) {
2779         /*
2780          * Use '%#c' for windows, because '%c' is backward-compatible and non-y2k
2781          * with msvc; '%#c' requests that a full year be used in the result string.
2782          */
2783         return ToLocaleFormatHelper(cx, dateObj,
2784 #if defined(_WIN32) && !defined(__MWERKS__)
2785                               "%#c"
2786 #else
2787                               "%c"
2788 #endif
2789                              , args.rval());
2790     }
2791 
2792     RootedString fmt(cx, ToString<CanGC>(cx, args[0]));
2793     if (!fmt)
2794         return false;
2795 
2796     JSAutoByteString fmtbytes(cx, fmt);
2797     if (!fmtbytes)
2798         return false;
2799 
2800     return ToLocaleFormatHelper(cx, dateObj, fmtbytes.ptr(), args.rval());
2801 }
2802 
2803 static bool
date_toLocaleFormat(JSContext * cx,unsigned argc,Value * vp)2804 date_toLocaleFormat(JSContext* cx, unsigned argc, Value* vp)
2805 {
2806     CallArgs args = CallArgsFromVp(argc, vp);
2807     return CallNonGenericMethod<IsDate, date_toLocaleFormat_impl>(cx, args);
2808 }
2809 
2810 /* ES5 15.9.5.4. */
2811 MOZ_ALWAYS_INLINE bool
date_toTimeString_impl(JSContext * cx,const CallArgs & args)2812 date_toTimeString_impl(JSContext* cx, const CallArgs& args)
2813 {
2814     return date_format(cx, args.thisv().toObject().as<DateObject>().UTCTime().toNumber(),
2815                        FORMATSPEC_TIME, args.rval());
2816 }
2817 
2818 static bool
date_toTimeString(JSContext * cx,unsigned argc,Value * vp)2819 date_toTimeString(JSContext* cx, unsigned argc, Value* vp)
2820 {
2821     CallArgs args = CallArgsFromVp(argc, vp);
2822     return CallNonGenericMethod<IsDate, date_toTimeString_impl>(cx, args);
2823 }
2824 
2825 /* ES5 15.9.5.3. */
2826 MOZ_ALWAYS_INLINE bool
date_toDateString_impl(JSContext * cx,const CallArgs & args)2827 date_toDateString_impl(JSContext* cx, const CallArgs& args)
2828 {
2829     return date_format(cx, args.thisv().toObject().as<DateObject>().UTCTime().toNumber(),
2830                        FORMATSPEC_DATE, args.rval());
2831 }
2832 
2833 static bool
date_toDateString(JSContext * cx,unsigned argc,Value * vp)2834 date_toDateString(JSContext* cx, unsigned argc, Value* vp)
2835 {
2836     CallArgs args = CallArgsFromVp(argc, vp);
2837     return CallNonGenericMethod<IsDate, date_toDateString_impl>(cx, args);
2838 }
2839 
2840 #if JS_HAS_TOSOURCE
2841 MOZ_ALWAYS_INLINE bool
date_toSource_impl(JSContext * cx,const CallArgs & args)2842 date_toSource_impl(JSContext* cx, const CallArgs& args)
2843 {
2844     StringBuffer sb(cx);
2845     if (!sb.append("(new Date(") ||
2846         !NumberValueToStringBuffer(cx, args.thisv().toObject().as<DateObject>().UTCTime(), sb) ||
2847         !sb.append("))"))
2848     {
2849         return false;
2850     }
2851 
2852     JSString* str = sb.finishString();
2853     if (!str)
2854         return false;
2855     args.rval().setString(str);
2856     return true;
2857 }
2858 
2859 static bool
date_toSource(JSContext * cx,unsigned argc,Value * vp)2860 date_toSource(JSContext* cx, unsigned argc, Value* vp)
2861 {
2862     CallArgs args = CallArgsFromVp(argc, vp);
2863     return CallNonGenericMethod<IsDate, date_toSource_impl>(cx, args);
2864 }
2865 #endif
2866 
2867 MOZ_ALWAYS_INLINE bool
IsObject(HandleValue v)2868 IsObject(HandleValue v)
2869 {
2870     return v.isObject();
2871 }
2872 
2873 // ES6 20.3.4.41.
2874 MOZ_ALWAYS_INLINE bool
date_toString_impl(JSContext * cx,const CallArgs & args)2875 date_toString_impl(JSContext* cx, const CallArgs& args)
2876 {
2877     // Step 1.
2878     RootedObject obj(cx, &args.thisv().toObject());
2879 
2880     // Step 2.
2881     ESClassValue cls;
2882     if (!GetBuiltinClass(cx, obj, &cls))
2883         return false;
2884 
2885     double tv;
2886     if (cls != ESClass_Date) {
2887         // Step 2.
2888         tv = GenericNaN();
2889     } else {
2890         // Step 3.
2891         RootedValue unboxed(cx);
2892         if (!Unbox(cx, obj, &unboxed))
2893             return false;
2894 
2895         tv = unboxed.toNumber();
2896     }
2897 
2898     // Step 4.
2899     return date_format(cx, tv, FORMATSPEC_FULL, args.rval());
2900 }
2901 
2902 bool
date_toString(JSContext * cx,unsigned argc,Value * vp)2903 date_toString(JSContext* cx, unsigned argc, Value* vp)
2904 {
2905     // Step 1.
2906     CallArgs args = CallArgsFromVp(argc, vp);
2907     return CallNonGenericMethod<IsObject, date_toString_impl>(cx, args);
2908 }
2909 
2910 MOZ_ALWAYS_INLINE bool
date_valueOf_impl(JSContext * cx,const CallArgs & args)2911 date_valueOf_impl(JSContext* cx, const CallArgs& args)
2912 {
2913     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2914     args.rval().set(dateObj->UTCTime());
2915     return true;
2916 }
2917 
2918 bool
date_valueOf(JSContext * cx,unsigned argc,Value * vp)2919 js::date_valueOf(JSContext* cx, unsigned argc, Value* vp)
2920 {
2921     CallArgs args = CallArgsFromVp(argc, vp);
2922     return CallNonGenericMethod<IsDate, date_valueOf_impl>(cx, args);
2923 }
2924 
2925 // ES6 20.3.4.45 Date.prototype[@@toPrimitive]
2926 static bool
date_toPrimitive(JSContext * cx,unsigned argc,Value * vp)2927 date_toPrimitive(JSContext* cx, unsigned argc, Value* vp)
2928 {
2929     CallArgs args = CallArgsFromVp(argc, vp);
2930 
2931     // Steps 1-2.
2932     if (!args.thisv().isObject()) {
2933         ReportIncompatible(cx, args);
2934         return false;
2935     }
2936 
2937     // Steps 3-5.
2938     JSType hint;
2939     if (!GetFirstArgumentAsTypeHint(cx, args, &hint))
2940         return false;
2941     if (hint == JSTYPE_VOID)
2942         hint = JSTYPE_STRING;
2943 
2944     args.rval().set(args.thisv());
2945     RootedObject obj(cx, &args.thisv().toObject());
2946     return OrdinaryToPrimitive(cx, obj, hint, args.rval());
2947 }
2948 
2949 static const JSFunctionSpec date_static_methods[] = {
2950     JS_FN("UTC",                 date_UTC,                7,0),
2951     JS_FN("parse",               date_parse,              1,0),
2952     JS_FN("now",                 date_now,                0,0),
2953     JS_FS_END
2954 };
2955 
2956 static const JSFunctionSpec date_methods[] = {
2957     JS_FN("getTime",             date_getTime,            0,0),
2958     JS_FN("getTimezoneOffset",   date_getTimezoneOffset,  0,0),
2959     JS_FN("getYear",             date_getYear,            0,0),
2960     JS_FN("getFullYear",         date_getFullYear,        0,0),
2961     JS_FN("getUTCFullYear",      date_getUTCFullYear,     0,0),
2962     JS_FN("getMonth",            date_getMonth,           0,0),
2963     JS_FN("getUTCMonth",         date_getUTCMonth,        0,0),
2964     JS_FN("getDate",             date_getDate,            0,0),
2965     JS_FN("getUTCDate",          date_getUTCDate,         0,0),
2966     JS_FN("getDay",              date_getDay,             0,0),
2967     JS_FN("getUTCDay",           date_getUTCDay,          0,0),
2968     JS_FN("getHours",            date_getHours,           0,0),
2969     JS_FN("getUTCHours",         date_getUTCHours,        0,0),
2970     JS_FN("getMinutes",          date_getMinutes,         0,0),
2971     JS_FN("getUTCMinutes",       date_getUTCMinutes,      0,0),
2972     JS_FN("getSeconds",          date_getUTCSeconds,      0,0),
2973     JS_FN("getUTCSeconds",       date_getUTCSeconds,      0,0),
2974     JS_FN("getMilliseconds",     date_getUTCMilliseconds, 0,0),
2975     JS_FN("getUTCMilliseconds",  date_getUTCMilliseconds, 0,0),
2976     JS_FN("setTime",             date_setTime,            1,0),
2977     JS_FN("setYear",             date_setYear,            1,0),
2978     JS_FN("setFullYear",         date_setFullYear,        3,0),
2979     JS_FN("setUTCFullYear",      date_setUTCFullYear,     3,0),
2980     JS_FN("setMonth",            date_setMonth,           2,0),
2981     JS_FN("setUTCMonth",         date_setUTCMonth,        2,0),
2982     JS_FN("setDate",             date_setDate,            1,0),
2983     JS_FN("setUTCDate",          date_setUTCDate,         1,0),
2984     JS_FN("setHours",            date_setHours,           4,0),
2985     JS_FN("setUTCHours",         date_setUTCHours,        4,0),
2986     JS_FN("setMinutes",          date_setMinutes,         3,0),
2987     JS_FN("setUTCMinutes",       date_setUTCMinutes,      3,0),
2988     JS_FN("setSeconds",          date_setSeconds,         2,0),
2989     JS_FN("setUTCSeconds",       date_setUTCSeconds,      2,0),
2990     JS_FN("setMilliseconds",     date_setMilliseconds,    1,0),
2991     JS_FN("setUTCMilliseconds",  date_setUTCMilliseconds, 1,0),
2992     JS_FN("toUTCString",         date_toGMTString,        0,0),
2993     JS_FN("toLocaleFormat",      date_toLocaleFormat,     0,0),
2994 #if EXPOSE_INTL_API
2995     JS_SELF_HOSTED_FN(js_toLocaleString_str, "Date_toLocaleString", 0,0),
2996     JS_SELF_HOSTED_FN("toLocaleDateString", "Date_toLocaleDateString", 0,0),
2997     JS_SELF_HOSTED_FN("toLocaleTimeString", "Date_toLocaleTimeString", 0,0),
2998 #else
2999     JS_FN(js_toLocaleString_str, date_toLocaleString,     0,0),
3000     JS_FN("toLocaleDateString",  date_toLocaleDateString, 0,0),
3001     JS_FN("toLocaleTimeString",  date_toLocaleTimeString, 0,0),
3002 #endif
3003     JS_FN("toDateString",        date_toDateString,       0,0),
3004     JS_FN("toTimeString",        date_toTimeString,       0,0),
3005     JS_FN("toISOString",         date_toISOString,        0,0),
3006     JS_FN(js_toJSON_str,         date_toJSON,             1,0),
3007 #if JS_HAS_TOSOURCE
3008     JS_FN(js_toSource_str,       date_toSource,           0,0),
3009 #endif
3010     JS_FN(js_toString_str,       date_toString,           0,0),
3011     JS_FN(js_valueOf_str,        date_valueOf,            0,0),
3012     JS_SYM_FN(toPrimitive,       date_toPrimitive,        1,JSPROP_READONLY),
3013     JS_FS_END
3014 };
3015 
3016 static bool
NewDateObject(JSContext * cx,const CallArgs & args,ClippedTime t)3017 NewDateObject(JSContext* cx, const CallArgs& args, ClippedTime t)
3018 {
3019     MOZ_ASSERT(args.isConstructing());
3020 
3021     RootedObject proto(cx);
3022     RootedObject newTarget(cx, &args.newTarget().toObject());
3023     if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
3024         return false;
3025 
3026     JSObject* obj = NewDateObjectMsec(cx, t, proto);
3027     if (!obj)
3028         return false;
3029 
3030     args.rval().setObject(*obj);
3031     return true;
3032 }
3033 
3034 static bool
ToDateString(JSContext * cx,const CallArgs & args,ClippedTime t)3035 ToDateString(JSContext* cx, const CallArgs& args, ClippedTime t)
3036 {
3037     return date_format(cx, t.toDouble(), FORMATSPEC_FULL, args.rval());
3038 }
3039 
3040 static bool
DateNoArguments(JSContext * cx,const CallArgs & args)3041 DateNoArguments(JSContext* cx, const CallArgs& args)
3042 {
3043     MOZ_ASSERT(args.length() == 0);
3044 
3045     ClippedTime now = NowAsMillis();
3046 
3047     if (args.isConstructing())
3048         return NewDateObject(cx, args, now);
3049 
3050     return ToDateString(cx, args, now);
3051 }
3052 
3053 static bool
DateOneArgument(JSContext * cx,const CallArgs & args)3054 DateOneArgument(JSContext* cx, const CallArgs& args)
3055 {
3056     MOZ_ASSERT(args.length() == 1);
3057 
3058     if (args.isConstructing()) {
3059         if (args[0].isObject()) {
3060             RootedObject obj(cx, &args[0].toObject());
3061 
3062             ESClassValue cls;
3063             if (!GetBuiltinClass(cx, obj, &cls))
3064                 return false;
3065 
3066             if (cls == ESClass_Date) {
3067                 RootedValue unboxed(cx);
3068                 if (!Unbox(cx, obj, &unboxed))
3069                     return false;
3070 
3071                 return NewDateObject(cx, args, TimeClip(unboxed.toNumber()));
3072             }
3073         }
3074 
3075         if (!ToPrimitive(cx, args[0]))
3076             return false;
3077 
3078         ClippedTime t;
3079         if (args[0].isString()) {
3080             JSLinearString* linearStr = args[0].toString()->ensureLinear(cx);
3081             if (!linearStr)
3082                 return false;
3083 
3084             if (!ParseDate(linearStr, &t))
3085                 t = ClippedTime::invalid();
3086         } else {
3087             double d;
3088             if (!ToNumber(cx, args[0], &d))
3089                 return false;
3090             t = TimeClip(d);
3091         }
3092 
3093         return NewDateObject(cx, args, t);
3094     }
3095 
3096     return ToDateString(cx, args, NowAsMillis());
3097 }
3098 
3099 static bool
DateMultipleArguments(JSContext * cx,const CallArgs & args)3100 DateMultipleArguments(JSContext* cx, const CallArgs& args)
3101 {
3102     MOZ_ASSERT(args.length() >= 2);
3103 
3104     // Step 3.
3105     if (args.isConstructing()) {
3106         // Steps 3a-b.
3107         double y;
3108         if (!ToNumber(cx, args[0], &y))
3109             return false;
3110 
3111         // Steps 3c-d.
3112         double m;
3113         if (!ToNumber(cx, args[1], &m))
3114             return false;
3115 
3116         // Steps 3e-f.
3117         double dt;
3118         if (args.length() >= 3) {
3119             if (!ToNumber(cx, args[2], &dt))
3120                 return false;
3121         } else {
3122             dt = 1;
3123         }
3124 
3125         // Steps 3g-h.
3126         double h;
3127         if (args.length() >= 4) {
3128             if (!ToNumber(cx, args[3], &h))
3129                 return false;
3130         } else {
3131             h = 0;
3132         }
3133 
3134         // Steps 3i-j.
3135         double min;
3136         if (args.length() >= 5) {
3137             if (!ToNumber(cx, args[4], &min))
3138                 return false;
3139         } else {
3140             min = 0;
3141         }
3142 
3143         // Steps 3k-l.
3144         double s;
3145         if (args.length() >= 6) {
3146             if (!ToNumber(cx, args[5], &s))
3147                 return false;
3148         } else {
3149             s = 0;
3150         }
3151 
3152         // Steps 3m-n.
3153         double milli;
3154         if (args.length() >= 7) {
3155             if (!ToNumber(cx, args[6], &milli))
3156                 return false;
3157         } else {
3158             milli = 0;
3159         }
3160 
3161         // Step 3o.
3162         double yr = y;
3163         if (!IsNaN(y)) {
3164             double yint = ToInteger(y);
3165             if (0 <= yint && yint <= 99)
3166                 yr = 1900 + yint;
3167         }
3168 
3169         // Step 3p.
3170         double finalDate = MakeDate(MakeDay(yr, m, dt), MakeTime(h, min, s, milli));
3171 
3172         // Steps 3q-t.
3173         return NewDateObject(cx, args, TimeClip(UTC(finalDate)));
3174     }
3175 
3176     return ToDateString(cx, args, NowAsMillis());
3177 }
3178 
3179 bool
DateConstructor(JSContext * cx,unsigned argc,Value * vp)3180 js::DateConstructor(JSContext* cx, unsigned argc, Value* vp)
3181 {
3182     CallArgs args = CallArgsFromVp(argc, vp);
3183 
3184     if (args.length() == 0)
3185         return DateNoArguments(cx, args);
3186 
3187     if (args.length() == 1)
3188         return DateOneArgument(cx, args);
3189 
3190     return DateMultipleArguments(cx, args);
3191 }
3192 
3193 // ES6 final draft 20.3.4.
3194 static JSObject*
CreateDatePrototype(JSContext * cx,JSProtoKey key)3195 CreateDatePrototype(JSContext* cx, JSProtoKey key)
3196 {
3197     return cx->global()->createBlankPrototype(cx, &DateObject::protoClass_);
3198 }
3199 
3200 static bool
FinishDateClassInit(JSContext * cx,HandleObject ctor,HandleObject proto)3201 FinishDateClassInit(JSContext* cx, HandleObject ctor, HandleObject proto)
3202 {
3203     /*
3204      * Date.prototype.toGMTString has the same initial value as
3205      * Date.prototype.toUTCString.
3206      */
3207     RootedValue toUTCStringFun(cx);
3208     RootedId toUTCStringId(cx, NameToId(cx->names().toUTCString));
3209     RootedId toGMTStringId(cx, NameToId(cx->names().toGMTString));
3210     return NativeGetProperty(cx, proto.as<NativeObject>(), toUTCStringId, &toUTCStringFun) &&
3211            NativeDefineProperty(cx, proto.as<NativeObject>(), toGMTStringId, toUTCStringFun,
3212                                 nullptr, nullptr, 0);
3213 }
3214 
3215 const Class DateObject::class_ = {
3216     js_Date_str,
3217     JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) |
3218     JSCLASS_HAS_CACHED_PROTO(JSProto_Date),
3219     nullptr, /* addProperty */
3220     nullptr, /* delProperty */
3221     nullptr, /* getProperty */
3222     nullptr, /* setProperty */
3223     nullptr, /* enumerate */
3224     nullptr, /* resolve */
3225     nullptr, /* mayResolve */
3226     nullptr, /* finalize */
3227     nullptr, /* call */
3228     nullptr, /* hasInstance */
3229     nullptr, /* construct */
3230     nullptr, /* trace */
3231     {
3232         GenericCreateConstructor<DateConstructor, 7, gc::AllocKind::FUNCTION>,
3233         CreateDatePrototype,
3234         date_static_methods,
3235         nullptr,
3236         date_methods,
3237         nullptr,
3238         FinishDateClassInit
3239     }
3240 };
3241 
3242 const Class DateObject::protoClass_ = {
3243     js_Object_str,
3244     JSCLASS_HAS_CACHED_PROTO(JSProto_Date),
3245     nullptr, /* addProperty */
3246     nullptr, /* delProperty */
3247     nullptr, /* getProperty */
3248     nullptr, /* setProperty */
3249     nullptr, /* enumerate */
3250     nullptr, /* resolve */
3251     nullptr, /* mayResolve */
3252     nullptr, /* finalize */
3253     nullptr, /* call */
3254     nullptr, /* hasInstance */
3255     nullptr, /* construct */
3256     nullptr, /* trace  */
3257     {
3258         DELEGATED_CLASSSPEC(&DateObject::class_.spec),
3259         nullptr,
3260         nullptr,
3261         nullptr,
3262         nullptr,
3263         nullptr,
3264         nullptr,
3265         ClassSpec::IsDelegated
3266     }
3267 };
3268 
3269 JSObject*
NewDateObjectMsec(JSContext * cx,ClippedTime t,HandleObject proto)3270 js::NewDateObjectMsec(JSContext* cx, ClippedTime t, HandleObject proto /* = nullptr */)
3271 {
3272     JSObject* obj = NewObjectWithClassProto(cx, &DateObject::class_, proto);
3273     if (!obj)
3274         return nullptr;
3275     obj->as<DateObject>().setUTCTime(t);
3276     return obj;
3277 }
3278 
JS_FRIEND_API(JSObject *)3279 JS_FRIEND_API(JSObject*)
3280 js::NewDateObject(JSContext* cx, int year, int mon, int mday,
3281                   int hour, int min, int sec)
3282 {
3283     MOZ_ASSERT(mon < 12);
3284     double msec_time = MakeDate(MakeDay(year, mon, mday), MakeTime(hour, min, sec, 0.0));
3285     return NewDateObjectMsec(cx, TimeClip(UTC(msec_time)));
3286 }
3287 
JS_FRIEND_API(bool)3288 JS_FRIEND_API(bool)
3289 js::DateIsValid(JSContext* cx, HandleObject obj, bool* isValid)
3290 {
3291     ESClassValue cls;
3292     if (!GetBuiltinClass(cx, obj, &cls))
3293         return false;
3294 
3295     if (cls != ESClass_Date) {
3296         *isValid = false;
3297         return true;
3298     }
3299 
3300     RootedValue unboxed(cx);
3301     if (!Unbox(cx, obj, &unboxed))
3302         return false;
3303 
3304     *isValid = !IsNaN(unboxed.toNumber());
3305     return true;
3306 }
3307 
JS_FRIEND_API(bool)3308 JS_FRIEND_API(bool)
3309 js::DateGetMsecSinceEpoch(JSContext* cx, HandleObject obj, double* msecsSinceEpoch)
3310 {
3311     ESClassValue cls;
3312     if (!GetBuiltinClass(cx, obj, &cls))
3313         return false;
3314 
3315     if (cls != ESClass_Date) {
3316         *msecsSinceEpoch = 0;
3317         return true;
3318     }
3319 
3320     RootedValue unboxed(cx);
3321     if (!Unbox(cx, obj, &unboxed))
3322         return false;
3323 
3324     *msecsSinceEpoch = unboxed.toNumber();
3325     return true;
3326 }
3327