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