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