Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /*
8 * JS date methods.
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 */
18 #include "jsdate.h"
20 #include "mozilla/ArrayUtils.h"
21 #include "mozilla/FloatingPoint.h"
23 #include <ctype.h>
24 #include <math.h>
25 #include <string.h>
27 #include "jsapi.h"
28 #include "jscntxt.h"
29 #include "jsnum.h"
30 #include "jsobj.h"
31 #include "jsprf.h"
32 #include "jsstr.h"
33 #include "jstypes.h"
34 #include "jsutil.h"
35 #include "prmjtime.h"
37 #include "js/Date.h"
38 #include "vm/DateTime.h"
39 #include "vm/GlobalObject.h"
40 #include "vm/Interpreter.h"
41 #include "vm/NumericConversions.h"
42 #include "vm/String.h"
43 #include "vm/StringBuffer.h"
45 #include "jsobjinlines.h"
47 using namespace js;
48 using namespace js::types;
50 using mozilla::ArrayLength;
51 using mozilla::IsFinite;
52 using mozilla::IsNaN;
53 using JS::GenericNaN;
55 /*
56 * The JS 'Date' object is patterned after the Java 'Date' object.
57 * Here is a script:
58 *
59 * today = new Date();
60 *
61 * print(today.toLocaleString());
62 *
63 * weekDay = today.getDay();
64 *
65 *
66 * These Java (and ECMA-262) methods are supported:
67 *
68 * UTC
69 * getDate (getUTCDate)
70 * getDay (getUTCDay)
71 * getHours (getUTCHours)
72 * getMinutes (getUTCMinutes)
73 * getMonth (getUTCMonth)
74 * getSeconds (getUTCSeconds)
75 * getMilliseconds (getUTCMilliseconds)
76 * getTime
77 * getTimezoneOffset
78 * getYear
79 * getFullYear (getUTCFullYear)
80 * parse
81 * setDate (setUTCDate)
82 * setHours (setUTCHours)
83 * setMinutes (setUTCMinutes)
84 * setMonth (setUTCMonth)
85 * setSeconds (setUTCSeconds)
86 * setMilliseconds (setUTCMilliseconds)
87 * setTime
88 * setYear (setFullYear, setUTCFullYear)
89 * toGMTString (toUTCString)
90 * toLocaleString
91 * toString
92 *
93 *
94 * These Java methods are not supported
95 *
96 * setDay
97 * before
98 * after
99 * equals
100 * hashCode
101 */
103 static inline double
104 Day(double t)
105 {
106 return floor(t / msPerDay);
107 }
109 static double
110 TimeWithinDay(double t)
111 {
112 double result = fmod(t, msPerDay);
113 if (result < 0)
114 result += msPerDay;
115 return result;
116 }
118 /* ES5 15.9.1.3. */
119 static inline bool
120 IsLeapYear(double year)
121 {
122 JS_ASSERT(ToInteger(year) == year);
123 return fmod(year, 4) == 0 && (fmod(year, 100) != 0 || fmod(year, 400) == 0);
124 }
126 static inline double
127 DaysInYear(double year)
128 {
129 if (!IsFinite(year))
130 return GenericNaN();
131 return IsLeapYear(year) ? 366 : 365;
132 }
134 static inline double
135 DayFromYear(double y)
136 {
137 return 365 * (y - 1970) +
138 floor((y - 1969) / 4.0) -
139 floor((y - 1901) / 100.0) +
140 floor((y - 1601) / 400.0);
141 }
143 static inline double
144 TimeFromYear(double y)
145 {
146 return DayFromYear(y) * msPerDay;
147 }
149 static double
150 YearFromTime(double t)
151 {
152 if (!IsFinite(t))
153 return GenericNaN();
155 JS_ASSERT(ToInteger(t) == t);
157 double y = floor(t / (msPerDay * 365.2425)) + 1970;
158 double t2 = TimeFromYear(y);
160 /*
161 * Adjust the year if the approximation was wrong. Since the year was
162 * computed using the average number of ms per year, it will usually
163 * be wrong for dates within several hours of a year transition.
164 */
165 if (t2 > t) {
166 y--;
167 } else {
168 if (t2 + msPerDay * DaysInYear(y) <= t)
169 y++;
170 }
171 return y;
172 }
174 static inline int
175 DaysInFebruary(double year)
176 {
177 return IsLeapYear(year) ? 29 : 28;
178 }
180 /* ES5 15.9.1.4. */
181 static inline double
182 DayWithinYear(double t, double year)
183 {
184 JS_ASSERT_IF(IsFinite(t), YearFromTime(t) == year);
185 return Day(t) - DayFromYear(year);
186 }
188 static double
189 MonthFromTime(double t)
190 {
191 if (!IsFinite(t))
192 return GenericNaN();
194 double year = YearFromTime(t);
195 double d = DayWithinYear(t, year);
197 int step;
198 if (d < (step = 31))
199 return 0;
200 if (d < (step += DaysInFebruary(year)))
201 return 1;
202 if (d < (step += 31))
203 return 2;
204 if (d < (step += 30))
205 return 3;
206 if (d < (step += 31))
207 return 4;
208 if (d < (step += 30))
209 return 5;
210 if (d < (step += 31))
211 return 6;
212 if (d < (step += 31))
213 return 7;
214 if (d < (step += 30))
215 return 8;
216 if (d < (step += 31))
217 return 9;
218 if (d < (step += 30))
219 return 10;
220 return 11;
221 }
223 /* ES5 15.9.1.5. */
224 static double
225 DateFromTime(double t)
226 {
227 if (!IsFinite(t))
228 return GenericNaN();
230 double year = YearFromTime(t);
231 double d = DayWithinYear(t, year);
233 int next;
234 if (d <= (next = 30))
235 return d + 1;
236 int step = next;
237 if (d <= (next += DaysInFebruary(year)))
238 return d - step;
239 step = next;
240 if (d <= (next += 31))
241 return d - step;
242 step = next;
243 if (d <= (next += 30))
244 return d - step;
245 step = next;
246 if (d <= (next += 31))
247 return d - step;
248 step = next;
249 if (d <= (next += 30))
250 return d - step;
251 step = next;
252 if (d <= (next += 31))
253 return d - step;
254 step = next;
255 if (d <= (next += 31))
256 return d - step;
257 step = next;
258 if (d <= (next += 30))
259 return d - step;
260 step = next;
261 if (d <= (next += 31))
262 return d - step;
263 step = next;
264 if (d <= (next += 30))
265 return d - step;
266 step = next;
267 return d - step;
268 }
270 /* ES5 15.9.1.6. */
271 static int
272 WeekDay(double t)
273 {
274 /*
275 * We can't assert TimeClip(t) == t because we call this function with
276 * local times, which can be offset outside TimeClip's permitted range.
277 */
278 JS_ASSERT(ToInteger(t) == t);
279 int result = (int(Day(t)) + 4) % 7;
280 if (result < 0)
281 result += 7;
282 return result;
283 }
285 static inline int
286 DayFromMonth(int month, bool isLeapYear)
287 {
288 /*
289 * The following array contains the day of year for the first day of
290 * each month, where index 0 is January, and day 0 is January 1.
291 */
292 static const int firstDayOfMonth[2][13] = {
293 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
294 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}
295 };
297 JS_ASSERT(0 <= month && month <= 12);
298 return firstDayOfMonth[isLeapYear][month];
299 }
301 template<typename T>
302 static inline int
303 DayFromMonth(T month, bool isLeapYear) MOZ_DELETE;
305 /* ES5 15.9.1.12 (out of order to accommodate DaylightSavingTA). */
306 static double
307 MakeDay(double year, double month, double date)
308 {
309 /* Step 1. */
310 if (!IsFinite(year) || !IsFinite(month) || !IsFinite(date))
311 return GenericNaN();
313 /* Steps 2-4. */
314 double y = ToInteger(year);
315 double m = ToInteger(month);
316 double dt = ToInteger(date);
318 /* Step 5. */
319 double ym = y + floor(m / 12);
321 /* Step 6. */
322 int mn = int(fmod(m, 12.0));
323 if (mn < 0)
324 mn += 12;
326 /* Steps 7-8. */
327 bool leap = IsLeapYear(ym);
329 double yearday = floor(TimeFromYear(ym) / msPerDay);
330 double monthday = DayFromMonth(mn, leap);
332 return yearday + monthday + dt - 1;
333 }
335 /* ES5 15.9.1.13 (out of order to accommodate DaylightSavingTA). */
336 static inline double
337 MakeDate(double day, double time)
338 {
339 /* Step 1. */
340 if (!IsFinite(day) || !IsFinite(time))
341 return GenericNaN();
343 /* Step 2. */
344 return day * msPerDay + time;
345 }
347 JS_PUBLIC_API(double)
348 JS::MakeDate(double year, unsigned month, unsigned day)
349 {
350 return TimeClip(::MakeDate(MakeDay(year, month, day), 0));
351 }
353 JS_PUBLIC_API(double)
354 JS::YearFromTime(double time)
355 {
356 return ::YearFromTime(time);
357 }
359 JS_PUBLIC_API(double)
360 JS::MonthFromTime(double time)
361 {
362 return ::MonthFromTime(time);
363 }
365 JS_PUBLIC_API(double)
366 JS::DayFromTime(double time)
367 {
368 return DateFromTime(time);
369 }
371 /*
372 * Find a year for which any given date will fall on the same weekday.
373 *
374 * This function should be used with caution when used other than
375 * for determining DST; it hasn't been proven not to produce an
376 * incorrect year for times near year boundaries.
377 */
378 static int
379 EquivalentYearForDST(int year)
380 {
381 /*
382 * Years and leap years on which Jan 1 is a Sunday, Monday, etc.
383 *
384 * yearStartingWith[0][i] is an example non-leap year where
385 * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
386 *
387 * yearStartingWith[1][i] is an example leap year where
388 * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
389 */
390 static const int yearStartingWith[2][7] = {
391 {1978, 1973, 1974, 1975, 1981, 1971, 1977},
392 {1984, 1996, 1980, 1992, 1976, 1988, 1972}
393 };
395 int day = int(DayFromYear(year) + 4) % 7;
396 if (day < 0)
397 day += 7;
399 return yearStartingWith[IsLeapYear(year)][day];
400 }
402 /* ES5 15.9.1.8. */
403 static double
404 DaylightSavingTA(double t, DateTimeInfo *dtInfo)
405 {
406 if (!IsFinite(t))
407 return GenericNaN();
409 /*
410 * If earlier than 1970 or after 2038, potentially beyond the ken of
411 * many OSes, map it to an equivalent year before asking.
412 */
413 if (t < 0.0 || t > 2145916800000.0) {
414 int year = EquivalentYearForDST(int(YearFromTime(t)));
415 double day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
416 t = MakeDate(day, TimeWithinDay(t));
417 }
419 int64_t utcMilliseconds = static_cast<int64_t>(t);
420 int64_t offsetMilliseconds = dtInfo->getDSTOffsetMilliseconds(utcMilliseconds);
421 return static_cast<double>(offsetMilliseconds);
422 }
424 static double
425 AdjustTime(double date, DateTimeInfo *dtInfo)
426 {
427 double t = DaylightSavingTA(date, dtInfo) + dtInfo->localTZA();
428 t = (dtInfo->localTZA() >= 0) ? fmod(t, msPerDay) : -fmod(msPerDay - t, msPerDay);
429 return t;
430 }
432 /* ES5 15.9.1.9. */
433 static double
434 LocalTime(double t, DateTimeInfo *dtInfo)
435 {
436 return t + AdjustTime(t, dtInfo);
437 }
439 static double
440 UTC(double t, DateTimeInfo *dtInfo)
441 {
442 return t - AdjustTime(t - dtInfo->localTZA(), dtInfo);
443 }
445 /* ES5 15.9.1.10. */
446 static double
447 HourFromTime(double t)
448 {
449 double result = fmod(floor(t/msPerHour), HoursPerDay);
450 if (result < 0)
451 result += HoursPerDay;
452 return result;
453 }
455 static double
456 MinFromTime(double t)
457 {
458 double result = fmod(floor(t / msPerMinute), MinutesPerHour);
459 if (result < 0)
460 result += MinutesPerHour;
461 return result;
462 }
464 static double
465 SecFromTime(double t)
466 {
467 double result = fmod(floor(t / msPerSecond), SecondsPerMinute);
468 if (result < 0)
469 result += SecondsPerMinute;
470 return result;
471 }
473 static double
474 msFromTime(double t)
475 {
476 double result = fmod(t, msPerSecond);
477 if (result < 0)
478 result += msPerSecond;
479 return result;
480 }
482 /* ES5 15.9.1.11. */
483 static double
484 MakeTime(double hour, double min, double sec, double ms)
485 {
486 /* Step 1. */
487 if (!IsFinite(hour) ||
488 !IsFinite(min) ||
489 !IsFinite(sec) ||
490 !IsFinite(ms))
491 {
492 return GenericNaN();
493 }
495 /* Step 2. */
496 double h = ToInteger(hour);
498 /* Step 3. */
499 double m = ToInteger(min);
501 /* Step 4. */
502 double s = ToInteger(sec);
504 /* Step 5. */
505 double milli = ToInteger(ms);
507 /* Steps 6-7. */
508 return h * msPerHour + m * msPerMinute + s * msPerSecond + milli;
509 }
511 /**
512 * end of ECMA 'support' functions
513 */
515 static bool
516 date_convert(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp)
517 {
518 JS_ASSERT(hint == JSTYPE_NUMBER || hint == JSTYPE_STRING || hint == JSTYPE_VOID);
519 JS_ASSERT(obj->is<DateObject>());
521 return DefaultValue(cx, obj, (hint == JSTYPE_VOID) ? JSTYPE_STRING : hint, vp);
522 }
524 /* for use by date_parse */
526 static const char* const wtb[] = {
527 "am", "pm",
528 "monday", "tuesday", "wednesday", "thursday", "friday",
529 "saturday", "sunday",
530 "january", "february", "march", "april", "may", "june",
531 "july", "august", "september", "october", "november", "december",
532 "gmt", "ut", "utc",
533 "est", "edt",
534 "cst", "cdt",
535 "mst", "mdt",
536 "pst", "pdt"
537 /* time zone table needs to be expanded */
538 };
540 static const int ttb[] = {
541 -1, -2, 0, 0, 0, 0, 0, 0, 0, /* AM/PM */
542 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
543 10000 + 0, 10000 + 0, 10000 + 0, /* GMT/UT/UTC */
544 10000 + 5 * 60, 10000 + 4 * 60, /* EST/EDT */
545 10000 + 6 * 60, 10000 + 5 * 60, /* CST/CDT */
546 10000 + 7 * 60, 10000 + 6 * 60, /* MST/MDT */
547 10000 + 8 * 60, 10000 + 7 * 60 /* PST/PDT */
548 };
550 /* helper for date_parse */
551 static bool
552 date_regionMatches(const char* s1, int s1off, const jschar* s2, int s2off,
553 int count, int ignoreCase)
554 {
555 bool result = false;
556 /* return true if matches, otherwise, false */
558 while (count > 0 && s1[s1off] && s2[s2off]) {
559 if (ignoreCase) {
560 if (unicode::ToLowerCase(s1[s1off]) != unicode::ToLowerCase(s2[s2off]))
561 break;
562 } else {
563 if ((jschar)s1[s1off] != s2[s2off]) {
564 break;
565 }
566 }
567 s1off++;
568 s2off++;
569 count--;
570 }
572 if (count == 0) {
573 result = true;
574 }
576 return result;
577 }
579 /* find UTC time from given date... no 1900 correction! */
580 static double
581 date_msecFromDate(double year, double mon, double mday, double hour,
582 double min, double sec, double msec)
583 {
584 return MakeDate(MakeDay(year, mon, mday), MakeTime(hour, min, sec, msec));
585 }
587 /* compute the time in msec (unclipped) from the given args */
588 #define MAXARGS 7
590 static bool
591 date_msecFromArgs(JSContext *cx, CallArgs args, double *rval)
592 {
593 unsigned loop;
594 double array[MAXARGS];
595 double msec_time;
597 for (loop = 0; loop < MAXARGS; loop++) {
598 if (loop < args.length()) {
599 double d;
600 if (!ToNumber(cx, args[loop], &d))
601 return false;
602 /* return NaN if any arg is not finite */
603 if (!IsFinite(d)) {
604 *rval = GenericNaN();
605 return true;
606 }
607 array[loop] = ToInteger(d);
608 } else {
609 if (loop == 2) {
610 array[loop] = 1; /* Default the date argument to 1. */
611 } else {
612 array[loop] = 0;
613 }
614 }
615 }
617 /* adjust 2-digit years into the 20th century */
618 if (array[0] >= 0 && array[0] <= 99)
619 array[0] += 1900;
621 msec_time = date_msecFromDate(array[0], array[1], array[2],
622 array[3], array[4], array[5], array[6]);
623 *rval = msec_time;
624 return true;
625 }
627 /*
628 * See ECMA 15.9.4.[3-10];
629 */
630 static bool
631 date_UTC(JSContext *cx, unsigned argc, Value *vp)
632 {
633 CallArgs args = CallArgsFromVp(argc, vp);
635 double msec_time;
636 if (!date_msecFromArgs(cx, args, &msec_time))
637 return false;
639 msec_time = TimeClip(msec_time);
641 args.rval().setNumber(msec_time);
642 return true;
643 }
645 /*
646 * Read and convert decimal digits from s[*i] into *result
647 * while *i < limit.
648 *
649 * Succeed if any digits are converted. Advance *i only
650 * as digits are consumed.
651 */
652 static bool
653 digits(size_t *result, const jschar *s, size_t *i, size_t limit)
654 {
655 size_t init = *i;
656 *result = 0;
657 while (*i < limit &&
658 ('0' <= s[*i] && s[*i] <= '9')) {
659 *result *= 10;
660 *result += (s[*i] - '0');
661 ++(*i);
662 }
663 return *i != init;
664 }
666 /*
667 * Read and convert decimal digits to the right of a decimal point,
668 * representing a fractional integer, from s[*i] into *result
669 * while *i < limit.
670 *
671 * Succeed if any digits are converted. Advance *i only
672 * as digits are consumed.
673 */
674 static bool
675 fractional(double *result, const jschar *s, size_t *i, size_t limit)
676 {
677 double factor = 0.1;
678 size_t init = *i;
679 *result = 0.0;
680 while (*i < limit &&
681 ('0' <= s[*i] && s[*i] <= '9')) {
682 *result += (s[*i] - '0') * factor;
683 factor *= 0.1;
684 ++(*i);
685 }
686 return *i != init;
687 }
689 /*
690 * Read and convert exactly n decimal digits from s[*i]
691 * to s[min(*i+n,limit)] into *result.
692 *
693 * Succeed if exactly n digits are converted. Advance *i only
694 * on success.
695 */
696 static bool
697 ndigits(size_t n, size_t *result, const jschar *s, size_t* i, size_t limit)
698 {
699 size_t init = *i;
701 if (digits(result, s, i, Min(limit, init+n)))
702 return (*i - init) == n;
704 *i = init;
705 return false;
706 }
708 static int
709 DaysInMonth(int year, int month)
710 {
711 bool leap = IsLeapYear(year);
712 int result = int(DayFromMonth(month, leap) - DayFromMonth(month - 1, leap));
713 return result;
714 }
716 /*
717 * Parse a string in one of the date-time formats given by the W3C
718 * "NOTE-datetime" specification. These formats make up a restricted
719 * profile of the ISO 8601 format. Quoted here:
720 *
721 * The formats are as follows. Exactly the components shown here
722 * must be present, with exactly this punctuation. Note that the "T"
723 * appears literally in the string, to indicate the beginning of the
724 * time element, as specified in ISO 8601.
725 *
726 * Any combination of the date formats with the time formats is
727 * allowed, and also either the date or the time can be missing.
728 *
729 * The specification is silent on the meaning when fields are
730 * ommitted so the interpretations are a guess, but hopefully a
731 * reasonable one. We default the month to January, the day to the
732 * 1st, and hours minutes and seconds all to 0. If the date is
733 * missing entirely then we assume 1970-01-01 so that the time can
734 * be aded to a date later. If the time is missing then we assume
735 * 00:00 UTC. If the time is present but the time zone field is
736 * missing then we use local time.
737 *
738 * Date part:
739 *
740 * Year:
741 * YYYY (eg 1997)
742 *
743 * Year and month:
744 * YYYY-MM (eg 1997-07)
745 *
746 * Complete date:
747 * YYYY-MM-DD (eg 1997-07-16)
748 *
749 * Time part:
750 *
751 * Hours and minutes:
752 * Thh:mmTZD (eg T19:20+01:00)
753 *
754 * Hours, minutes and seconds:
755 * Thh:mm:ssTZD (eg T19:20:30+01:00)
756 *
757 * Hours, minutes, seconds and a decimal fraction of a second:
758 * Thh:mm:ss.sTZD (eg T19:20:30.45+01:00)
759 *
760 * where:
761 *
762 * YYYY = four-digit year or six digit year as +YYYYYY or -YYYYYY
763 * MM = two-digit month (01=January, etc.)
764 * DD = two-digit day of month (01 through 31)
765 * hh = two digits of hour (00 through 23) (am/pm NOT allowed)
766 * mm = two digits of minute (00 through 59)
767 * ss = two digits of second (00 through 59)
768 * s = one or more digits representing a decimal fraction of a second
769 * TZD = time zone designator (Z or +hh:mm or -hh:mm or missing for local)
770 */
772 static bool
773 date_parseISOString(JSLinearString *str, double *result, DateTimeInfo *dtInfo)
774 {
775 double msec;
777 const jschar *s;
778 size_t limit;
779 size_t i = 0;
780 int tzMul = 1;
781 int dateMul = 1;
782 size_t year = 1970;
783 size_t month = 1;
784 size_t day = 1;
785 size_t hour = 0;
786 size_t min = 0;
787 size_t sec = 0;
788 double frac = 0;
789 bool isLocalTime = false;
790 size_t tzHour = 0;
791 size_t tzMin = 0;
793 #define PEEK(ch) (i < limit && s[i] == ch)
795 #define NEED(ch) \
796 JS_BEGIN_MACRO \
797 if (i >= limit || s[i] != ch) { goto syntax; } else { ++i; } \
798 JS_END_MACRO
800 #define DONE_DATE_UNLESS(ch) \
801 JS_BEGIN_MACRO \
802 if (i >= limit || s[i] != ch) { goto done_date; } else { ++i; } \
803 JS_END_MACRO
805 #define DONE_UNLESS(ch) \
806 JS_BEGIN_MACRO \
807 if (i >= limit || s[i] != ch) { goto done; } else { ++i; } \
808 JS_END_MACRO
810 #define NEED_NDIGITS(n, field) \
811 JS_BEGIN_MACRO \
812 if (!ndigits(n, &field, s, &i, limit)) { goto syntax; } \
813 JS_END_MACRO
815 s = str->chars();
816 limit = str->length();
818 if (PEEK('+') || PEEK('-')) {
819 if (PEEK('-'))
820 dateMul = -1;
821 ++i;
822 NEED_NDIGITS(6, year);
823 } else if (!PEEK('T')) {
824 NEED_NDIGITS(4, year);
825 }
826 DONE_DATE_UNLESS('-');
827 NEED_NDIGITS(2, month);
828 DONE_DATE_UNLESS('-');
829 NEED_NDIGITS(2, day);
831 done_date:
832 DONE_UNLESS('T');
833 NEED_NDIGITS(2, hour);
834 NEED(':');
835 NEED_NDIGITS(2, min);
837 if (PEEK(':')) {
838 ++i;
839 NEED_NDIGITS(2, sec);
840 if (PEEK('.')) {
841 ++i;
842 if (!fractional(&frac, s, &i, limit))
843 goto syntax;
844 }
845 }
847 if (PEEK('Z')) {
848 ++i;
849 } else if (PEEK('+') || PEEK('-')) {
850 if (PEEK('-'))
851 tzMul = -1;
852 ++i;
853 NEED_NDIGITS(2, tzHour);
854 /*
855 * Non-standard extension to the ISO date format (permitted by ES5):
856 * allow "-0700" as a time zone offset, not just "-07:00".
857 */
858 if (PEEK(':'))
859 ++i;
860 NEED_NDIGITS(2, tzMin);
861 } else {
862 isLocalTime = true;
863 }
865 done:
866 if (year > 275943 // ceil(1e8/365) + 1970
867 || (month == 0 || month > 12)
868 || (day == 0 || day > size_t(DaysInMonth(year,month)))
869 || hour > 24
870 || ((hour == 24) && (min > 0 || sec > 0))
871 || min > 59
872 || sec > 59
873 || tzHour > 23
874 || tzMin > 59)
875 goto syntax;
877 if (i != limit)
878 goto syntax;
880 month -= 1; /* convert month to 0-based */
882 msec = date_msecFromDate(dateMul * (double)year, month, day,
883 hour, min, sec,
884 frac * 1000.0);;
886 if (isLocalTime) {
887 msec = UTC(msec, dtInfo);
888 } else {
889 msec -= ((tzMul) * ((tzHour * msPerHour)
890 + (tzMin * msPerMinute)));
891 }
893 if (msec < -8.64e15 || msec > 8.64e15)
894 goto syntax;
896 *result = msec;
898 return true;
900 syntax:
901 /* syntax error */
902 *result = 0;
903 return false;
905 #undef PEEK
906 #undef NEED
907 #undef DONE_UNLESS
908 #undef NEED_NDIGITS
909 }
911 static bool
912 date_parseString(JSLinearString *str, double *result, DateTimeInfo *dtInfo)
913 {
914 double msec;
916 const jschar *s;
917 size_t limit;
918 size_t i = 0;
919 int year = -1;
920 int mon = -1;
921 int mday = -1;
922 int hour = -1;
923 int min = -1;
924 int sec = -1;
925 int c = -1;
926 int n = -1;
927 int tzoffset = -1;
928 int prevc = 0;
929 bool seenplusminus = false;
930 int temp;
931 bool seenmonthname = false;
933 if (date_parseISOString(str, result, dtInfo))
934 return true;
936 s = str->chars();
937 limit = str->length();
938 if (limit == 0)
939 goto syntax;
940 while (i < limit) {
941 c = s[i];
942 i++;
943 if (c <= ' ' || c == ',' || c == '-') {
944 if (c == '-' && '0' <= s[i] && s[i] <= '9') {
945 prevc = c;
946 }
947 continue;
948 }
949 if (c == '(') { /* comments) */
950 int depth = 1;
951 while (i < limit) {
952 c = s[i];
953 i++;
954 if (c == '(') depth++;
955 else if (c == ')')
956 if (--depth <= 0)
957 break;
958 }
959 continue;
960 }
961 if ('0' <= c && c <= '9') {
962 n = c - '0';
963 while (i < limit && '0' <= (c = s[i]) && c <= '9') {
964 n = n * 10 + c - '0';
965 i++;
966 }
968 /* allow TZA before the year, so
969 * 'Wed Nov 05 21:49:11 GMT-0800 1997'
970 * works */
972 /* uses of seenplusminus allow : in TZA, so Java
973 * no-timezone style of GMT+4:30 works
974 */
976 if ((prevc == '+' || prevc == '-')/* && year>=0 */) {
977 /* make ':' case below change tzoffset */
978 seenplusminus = true;
980 /* offset */
981 if (n < 24)
982 n = n * 60; /* EG. "GMT-3" */
983 else
984 n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */
985 if (prevc == '+') /* plus means east of GMT */
986 n = -n;
987 if (tzoffset != 0 && tzoffset != -1)
988 goto syntax;
989 tzoffset = n;
990 } else if (prevc == '/' && mon >= 0 && mday >= 0 && year < 0) {
991 if (c <= ' ' || c == ',' || c == '/' || i >= limit)
992 year = n;
993 else
994 goto syntax;
995 } else if (c == ':') {
996 if (hour < 0)
997 hour = /*byte*/ n;
998 else if (min < 0)
999 min = /*byte*/ n;
1000 else
1001 goto syntax;
1002 } else if (c == '/') {
1003 /* until it is determined that mon is the actual
1004 month, keep it as 1-based rather than 0-based */
1005 if (mon < 0)
1006 mon = /*byte*/ n;
1007 else if (mday < 0)
1008 mday = /*byte*/ n;
1009 else
1010 goto syntax;
1011 } else if (i < limit && c != ',' && c > ' ' && c != '-' && c != '(') {
1012 goto syntax;
1013 } else if (seenplusminus && n < 60) { /* handle GMT-3:30 */
1014 if (tzoffset < 0)
1015 tzoffset -= n;
1016 else
1017 tzoffset += n;
1018 } else if (hour >= 0 && min < 0) {
1019 min = /*byte*/ n;
1020 } else if (prevc == ':' && min >= 0 && sec < 0) {
1021 sec = /*byte*/ n;
1022 } else if (mon < 0) {
1023 mon = /*byte*/n;
1024 } else if (mon >= 0 && mday < 0) {
1025 mday = /*byte*/ n;
1026 } else if (mon >= 0 && mday >= 0 && year < 0) {
1027 year = n;
1028 } else {
1029 goto syntax;
1030 }
1031 prevc = 0;
1032 } else if (c == '/' || c == ':' || c == '+' || c == '-') {
1033 prevc = c;
1034 } else {
1035 size_t st = i - 1;
1036 int k;
1037 while (i < limit) {
1038 c = s[i];
1039 if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')))
1040 break;
1041 i++;
1042 }
1043 if (i <= st + 1)
1044 goto syntax;
1045 for (k = ArrayLength(wtb); --k >= 0;)
1046 if (date_regionMatches(wtb[k], 0, s, st, i-st, 1)) {
1047 int action = ttb[k];
1048 if (action != 0) {
1049 if (action < 0) {
1050 /*
1051 * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as
1052 * 12:30, instead of blindly adding 12 if PM.
1053 */
1054 JS_ASSERT(action == -1 || action == -2);
1055 if (hour > 12 || hour < 0) {
1056 goto syntax;
1057 } else {
1058 if (action == -1 && hour == 12) { /* am */
1059 hour = 0;
1060 } else if (action == -2 && hour != 12) { /* pm */
1061 hour += 12;
1062 }
1063 }
1064 } else if (action <= 13) { /* month! */
1065 /* Adjust mon to be 1-based until the final values
1066 for mon, mday and year are adjusted below */
1067 if (seenmonthname) {
1068 goto syntax;
1069 }
1070 seenmonthname = true;
1071 temp = /*byte*/ (action - 2) + 1;
1073 if (mon < 0) {
1074 mon = temp;
1075 } else if (mday < 0) {
1076 mday = mon;
1077 mon = temp;
1078 } else if (year < 0) {
1079 year = mon;
1080 mon = temp;
1081 } else {
1082 goto syntax;
1083 }
1084 } else {
1085 tzoffset = action - 10000;
1086 }
1087 }
1088 break;
1089 }
1090 if (k < 0)
1091 goto syntax;
1092 prevc = 0;
1093 }
1094 }
1095 if (year < 0 || mon < 0 || mday < 0)
1096 goto syntax;
1097 /*
1098 Case 1. The input string contains an English month name.
1099 The form of the string can be month f l, or f month l, or
1100 f l month which each evaluate to the same date.
1101 If f and l are both greater than or equal to 70, or
1102 both less than 70, the date is invalid.
1103 The year is taken to be the greater of the values f, l.
1104 If the year is greater than or equal to 70 and less than 100,
1105 it is considered to be the number of years after 1900.
1106 Case 2. The input string is of the form "f/m/l" where f, m and l are
1107 integers, e.g. 7/16/45.
1108 Adjust the mon, mday and year values to achieve 100% MSIE
1109 compatibility.
1110 a. If 0 <= f < 70, f/m/l is interpreted as month/day/year.
1111 i. If year < 100, it is the number of years after 1900
1112 ii. If year >= 100, it is the number of years after 0.
1113 b. If 70 <= f < 100
1114 i. If m < 70, f/m/l is interpreted as
1115 year/month/day where year is the number of years after
1116 1900.
1117 ii. If m >= 70, the date is invalid.
1118 c. If f >= 100
1119 i. If m < 70, f/m/l is interpreted as
1120 year/month/day where year is the number of years after 0.
1121 ii. If m >= 70, the date is invalid.
1122 */
1123 if (seenmonthname) {
1124 if ((mday >= 70 && year >= 70) || (mday < 70 && year < 70)) {
1125 goto syntax;
1126 }
1127 if (mday > year) {
1128 temp = year;
1129 year = mday;
1130 mday = temp;
1131 }
1132 if (year >= 70 && year < 100) {
1133 year += 1900;
1134 }
1135 } else if (mon < 70) { /* (a) month/day/year */
1136 if (year < 100) {
1137 year += 1900;
1138 }
1139 } else if (mon < 100) { /* (b) year/month/day */
1140 if (mday < 70) {
1141 temp = year;
1142 year = mon + 1900;
1143 mon = mday;
1144 mday = temp;
1145 } else {
1146 goto syntax;
1147 }
1148 } else { /* (c) year/month/day */
1149 if (mday < 70) {
1150 temp = year;
1151 year = mon;
1152 mon = mday;
1153 mday = temp;
1154 } else {
1155 goto syntax;
1156 }
1157 }
1158 mon -= 1; /* convert month to 0-based */
1159 if (sec < 0)
1160 sec = 0;
1161 if (min < 0)
1162 min = 0;
1163 if (hour < 0)
1164 hour = 0;
1166 msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
1168 if (tzoffset == -1) { /* no time zone specified, have to use local */
1169 msec = UTC(msec, dtInfo);
1170 } else {
1171 msec += tzoffset * msPerMinute;
1172 }
1174 *result = msec;
1175 return true;
1177 syntax:
1178 /* syntax error */
1179 *result = 0;
1180 return false;
1181 }
1183 static bool
1184 date_parse(JSContext *cx, unsigned argc, Value *vp)
1185 {
1186 CallArgs args = CallArgsFromVp(argc, vp);
1187 if (args.length() == 0) {
1188 args.rval().setNaN();
1189 return true;
1190 }
1192 JSString *str = ToString<CanGC>(cx, args[0]);
1193 if (!str)
1194 return false;
1196 JSLinearString *linearStr = str->ensureLinear(cx);
1197 if (!linearStr)
1198 return false;
1200 double result;
1201 if (!date_parseString(linearStr, &result, &cx->runtime()->dateTimeInfo)) {
1202 args.rval().setNaN();
1203 return true;
1204 }
1206 result = TimeClip(result);
1207 args.rval().setNumber(result);
1208 return true;
1209 }
1211 static inline double
1212 NowAsMillis()
1213 {
1214 return (double) (PRMJ_Now() / PRMJ_USEC_PER_MSEC);
1215 }
1217 static bool
1218 date_now(JSContext *cx, unsigned argc, Value *vp)
1219 {
1220 CallArgs args = CallArgsFromVp(argc, vp);
1221 args.rval().setDouble(NowAsMillis());
1222 return true;
1223 }
1225 void
1226 DateObject::setUTCTime(double t, Value *vp)
1227 {
1228 for (size_t ind = COMPONENTS_START_SLOT; ind < RESERVED_SLOTS; ind++)
1229 setReservedSlot(ind, UndefinedValue());
1231 setFixedSlot(UTC_TIME_SLOT, DoubleValue(t));
1232 if (vp)
1233 vp->setDouble(t);
1234 }
1236 void
1237 DateObject::fillLocalTimeSlots(DateTimeInfo *dtInfo)
1238 {
1239 /* Check if the cache is already populated. */
1240 if (!getReservedSlot(LOCAL_TIME_SLOT).isUndefined() &&
1241 getReservedSlot(TZA_SLOT).toDouble() == dtInfo->localTZA())
1242 {
1243 return;
1244 }
1246 /* Remember timezone used to generate the local cache. */
1247 setReservedSlot(TZA_SLOT, DoubleValue(dtInfo->localTZA()));
1249 double utcTime = UTCTime().toNumber();
1251 if (!IsFinite(utcTime)) {
1252 for (size_t ind = COMPONENTS_START_SLOT; ind < RESERVED_SLOTS; ind++)
1253 setReservedSlot(ind, DoubleValue(utcTime));
1254 return;
1255 }
1257 double localTime = LocalTime(utcTime, dtInfo);
1259 setReservedSlot(LOCAL_TIME_SLOT, DoubleValue(localTime));
1261 int year = (int) floor(localTime /(msPerDay * 365.2425)) + 1970;
1262 double yearStartTime = TimeFromYear(year);
1264 /* Adjust the year in case the approximation was wrong, as in YearFromTime. */
1265 int yearDays;
1266 if (yearStartTime > localTime) {
1267 year--;
1268 yearStartTime -= (msPerDay * DaysInYear(year));
1269 yearDays = DaysInYear(year);
1270 } else {
1271 yearDays = DaysInYear(year);
1272 double nextStart = yearStartTime + (msPerDay * yearDays);
1273 if (nextStart <= localTime) {
1274 year++;
1275 yearStartTime = nextStart;
1276 yearDays = DaysInYear(year);
1277 }
1278 }
1280 setReservedSlot(LOCAL_YEAR_SLOT, Int32Value(year));
1282 uint64_t yearTime = uint64_t(localTime - yearStartTime);
1283 int yearSeconds = uint32_t(yearTime / 1000);
1285 int day = yearSeconds / int(SecondsPerDay);
1287 int step = -1, next = 30;
1288 int month;
1290 do {
1291 if (day <= next) {
1292 month = 0;
1293 break;
1294 }
1295 step = next;
1296 next += ((yearDays == 366) ? 29 : 28);
1297 if (day <= next) {
1298 month = 1;
1299 break;
1300 }
1301 step = next;
1302 if (day <= (next += 31)) {
1303 month = 2;
1304 break;
1305 }
1306 step = next;
1307 if (day <= (next += 30)) {
1308 month = 3;
1309 break;
1310 }
1311 step = next;
1312 if (day <= (next += 31)) {
1313 month = 4;
1314 break;
1315 }
1316 step = next;
1317 if (day <= (next += 30)) {
1318 month = 5;
1319 break;
1320 }
1321 step = next;
1322 if (day <= (next += 31)) {
1323 month = 6;
1324 break;
1325 }
1326 step = next;
1327 if (day <= (next += 31)) {
1328 month = 7;
1329 break;
1330 }
1331 step = next;
1332 if (day <= (next += 30)) {
1333 month = 8;
1334 break;
1335 }
1336 step = next;
1337 if (day <= (next += 31)) {
1338 month = 9;
1339 break;
1340 }
1341 step = next;
1342 if (day <= (next += 30)) {
1343 month = 10;
1344 break;
1345 }
1346 step = next;
1347 month = 11;
1348 } while (0);
1350 setReservedSlot(LOCAL_MONTH_SLOT, Int32Value(month));
1351 setReservedSlot(LOCAL_DATE_SLOT, Int32Value(day - step));
1353 int weekday = WeekDay(localTime);
1354 setReservedSlot(LOCAL_DAY_SLOT, Int32Value(weekday));
1356 int seconds = yearSeconds % 60;
1357 setReservedSlot(LOCAL_SECONDS_SLOT, Int32Value(seconds));
1359 int minutes = (yearSeconds / 60) % 60;
1360 setReservedSlot(LOCAL_MINUTES_SLOT, Int32Value(minutes));
1362 int hours = (yearSeconds / (60 * 60)) % 24;
1363 setReservedSlot(LOCAL_HOURS_SLOT, Int32Value(hours));
1364 }
1366 inline double
1367 DateObject::cachedLocalTime(DateTimeInfo *dtInfo)
1368 {
1369 fillLocalTimeSlots(dtInfo);
1370 return getReservedSlot(LOCAL_TIME_SLOT).toDouble();
1371 }
1373 MOZ_ALWAYS_INLINE bool
1374 IsDate(HandleValue v)
1375 {
1376 return v.isObject() && v.toObject().is<DateObject>();
1377 }
1379 /*
1380 * See ECMA 15.9.5.4 thru 15.9.5.23
1381 */
1382 /* static */ MOZ_ALWAYS_INLINE bool
1383 DateObject::getTime_impl(JSContext *cx, CallArgs args)
1384 {
1385 args.rval().set(args.thisv().toObject().as<DateObject>().UTCTime());
1386 return true;
1387 }
1389 static bool
1390 date_getTime(JSContext *cx, unsigned argc, Value *vp)
1391 {
1392 CallArgs args = CallArgsFromVp(argc, vp);
1393 return CallNonGenericMethod<IsDate, DateObject::getTime_impl>(cx, args);
1394 }
1396 /* static */ MOZ_ALWAYS_INLINE bool
1397 DateObject::getYear_impl(JSContext *cx, CallArgs args)
1398 {
1399 DateObject *dateObj = &args.thisv().toObject().as<DateObject>();
1400 dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo);
1402 Value yearVal = dateObj->getReservedSlot(LOCAL_YEAR_SLOT);
1403 if (yearVal.isInt32()) {
1404 /* Follow ECMA-262 to the letter, contrary to IE JScript. */
1405 int year = yearVal.toInt32() - 1900;
1406 args.rval().setInt32(year);
1407 } else {
1408 args.rval().set(yearVal);
1409 }
1411 return true;
1412 }
1414 static bool
1415 date_getYear(JSContext *cx, unsigned argc, Value *vp)
1416 {
1417 CallArgs args = CallArgsFromVp(argc, vp);
1418 return CallNonGenericMethod<IsDate, DateObject::getYear_impl>(cx, args);
1419 }
1421 /* static */ MOZ_ALWAYS_INLINE bool
1422 DateObject::getFullYear_impl(JSContext *cx, CallArgs args)
1423 {
1424 DateObject *dateObj = &args.thisv().toObject().as<DateObject>();
1425 dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo);
1427 args.rval().set(dateObj->getReservedSlot(LOCAL_YEAR_SLOT));
1428 return true;
1429 }
1431 static bool
1432 date_getFullYear(JSContext *cx, unsigned argc, Value *vp)
1433 {
1434 CallArgs args = CallArgsFromVp(argc, vp);
1435 return CallNonGenericMethod<IsDate, DateObject::getFullYear_impl>(cx, args);
1436 }
1438 /* static */ MOZ_ALWAYS_INLINE bool
1439 DateObject::getUTCFullYear_impl(JSContext *cx, CallArgs args)
1440 {
1441 double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
1442 if (IsFinite(result))
1443 result = YearFromTime(result);
1445 args.rval().setNumber(result);
1446 return true;
1447 }
1449 static bool
1450 date_getUTCFullYear(JSContext *cx, unsigned argc, Value *vp)
1451 {
1452 CallArgs args = CallArgsFromVp(argc, vp);
1453 return CallNonGenericMethod<IsDate, DateObject::getUTCFullYear_impl>(cx, args);
1454 }
1456 /* static */ MOZ_ALWAYS_INLINE bool
1457 DateObject::getMonth_impl(JSContext *cx, CallArgs args)
1458 {
1459 DateObject *dateObj = &args.thisv().toObject().as<DateObject>();
1460 dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo);
1462 args.rval().set(dateObj->getReservedSlot(LOCAL_MONTH_SLOT));
1463 return true;
1464 }
1466 static bool
1467 date_getMonth(JSContext *cx, unsigned argc, Value *vp)
1468 {
1469 CallArgs args = CallArgsFromVp(argc, vp);
1470 return CallNonGenericMethod<IsDate, DateObject::getMonth_impl>(cx, args);
1471 }
1473 /* static */ MOZ_ALWAYS_INLINE bool
1474 DateObject::getUTCMonth_impl(JSContext *cx, CallArgs args)
1475 {
1476 double d = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
1477 args.rval().setNumber(MonthFromTime(d));
1478 return true;
1479 }
1481 static bool
1482 date_getUTCMonth(JSContext *cx, unsigned argc, Value *vp)
1483 {
1484 CallArgs args = CallArgsFromVp(argc, vp);
1485 return CallNonGenericMethod<IsDate, DateObject::getUTCMonth_impl>(cx, args);
1486 }
1488 /* static */ MOZ_ALWAYS_INLINE bool
1489 DateObject::getDate_impl(JSContext *cx, CallArgs args)
1490 {
1491 DateObject *dateObj = &args.thisv().toObject().as<DateObject>();
1492 dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo);
1494 args.rval().set(dateObj->getReservedSlot(LOCAL_DATE_SLOT));
1495 return true;
1496 }
1498 static bool
1499 date_getDate(JSContext *cx, unsigned argc, Value *vp)
1500 {
1501 CallArgs args = CallArgsFromVp(argc, vp);
1502 return CallNonGenericMethod<IsDate, DateObject::getDate_impl>(cx, args);
1503 }
1505 /* static */ MOZ_ALWAYS_INLINE bool
1506 DateObject::getUTCDate_impl(JSContext *cx, CallArgs args)
1507 {
1508 double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
1509 if (IsFinite(result))
1510 result = DateFromTime(result);
1512 args.rval().setNumber(result);
1513 return true;
1514 }
1516 static bool
1517 date_getUTCDate(JSContext *cx, unsigned argc, Value *vp)
1518 {
1519 CallArgs args = CallArgsFromVp(argc, vp);
1520 return CallNonGenericMethod<IsDate, DateObject::getUTCDate_impl>(cx, args);
1521 }
1523 /* static */ MOZ_ALWAYS_INLINE bool
1524 DateObject::getDay_impl(JSContext *cx, CallArgs args)
1525 {
1526 DateObject *dateObj = &args.thisv().toObject().as<DateObject>();
1527 dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo);
1529 args.rval().set(dateObj->getReservedSlot(LOCAL_DAY_SLOT));
1530 return true;
1531 }
1533 static bool
1534 date_getDay(JSContext *cx, unsigned argc, Value *vp)
1535 {
1536 CallArgs args = CallArgsFromVp(argc, vp);
1537 return CallNonGenericMethod<IsDate, DateObject::getDay_impl>(cx, args);
1538 }
1540 /* static */ MOZ_ALWAYS_INLINE bool
1541 DateObject::getUTCDay_impl(JSContext *cx, CallArgs args)
1542 {
1543 double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
1544 if (IsFinite(result))
1545 result = WeekDay(result);
1547 args.rval().setNumber(result);
1548 return true;
1549 }
1551 static bool
1552 date_getUTCDay(JSContext *cx, unsigned argc, Value *vp)
1553 {
1554 CallArgs args = CallArgsFromVp(argc, vp);
1555 return CallNonGenericMethod<IsDate, DateObject::getUTCDay_impl>(cx, args);
1556 }
1558 /* static */ MOZ_ALWAYS_INLINE bool
1559 DateObject::getHours_impl(JSContext *cx, CallArgs args)
1560 {
1561 DateObject *dateObj = &args.thisv().toObject().as<DateObject>();
1562 dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo);
1564 args.rval().set(dateObj->getReservedSlot(LOCAL_HOURS_SLOT));
1565 return true;
1566 }
1568 static bool
1569 date_getHours(JSContext *cx, unsigned argc, Value *vp)
1570 {
1571 CallArgs args = CallArgsFromVp(argc, vp);
1572 return CallNonGenericMethod<IsDate, DateObject::getHours_impl>(cx, args);
1573 }
1575 /* static */ MOZ_ALWAYS_INLINE bool
1576 DateObject::getUTCHours_impl(JSContext *cx, CallArgs args)
1577 {
1578 double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
1579 if (IsFinite(result))
1580 result = HourFromTime(result);
1582 args.rval().setNumber(result);
1583 return true;
1584 }
1586 static bool
1587 date_getUTCHours(JSContext *cx, unsigned argc, Value *vp)
1588 {
1589 CallArgs args = CallArgsFromVp(argc, vp);
1590 return CallNonGenericMethod<IsDate, DateObject::getUTCHours_impl>(cx, args);
1591 }
1593 /* static */ MOZ_ALWAYS_INLINE bool
1594 DateObject::getMinutes_impl(JSContext *cx, CallArgs args)
1595 {
1596 DateObject *dateObj = &args.thisv().toObject().as<DateObject>();
1597 dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo);
1599 args.rval().set(dateObj->getReservedSlot(LOCAL_MINUTES_SLOT));
1600 return true;
1601 }
1603 static bool
1604 date_getMinutes(JSContext *cx, unsigned argc, Value *vp)
1605 {
1606 CallArgs args = CallArgsFromVp(argc, vp);
1607 return CallNonGenericMethod<IsDate, DateObject::getMinutes_impl>(cx, args);
1608 }
1610 /* static */ MOZ_ALWAYS_INLINE bool
1611 DateObject::getUTCMinutes_impl(JSContext *cx, CallArgs args)
1612 {
1613 double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
1614 if (IsFinite(result))
1615 result = MinFromTime(result);
1617 args.rval().setNumber(result);
1618 return true;
1619 }
1621 static bool
1622 date_getUTCMinutes(JSContext *cx, unsigned argc, Value *vp)
1623 {
1624 CallArgs args = CallArgsFromVp(argc, vp);
1625 return CallNonGenericMethod<IsDate, DateObject::getUTCMinutes_impl>(cx, args);
1626 }
1628 /* Date.getSeconds is mapped to getUTCSeconds */
1630 /* static */ MOZ_ALWAYS_INLINE bool
1631 DateObject::getUTCSeconds_impl(JSContext *cx, CallArgs args)
1632 {
1633 DateObject *dateObj = &args.thisv().toObject().as<DateObject>();
1634 dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo);
1636 args.rval().set(dateObj->getReservedSlot(LOCAL_SECONDS_SLOT));
1637 return true;
1638 }
1640 static bool
1641 date_getUTCSeconds(JSContext *cx, unsigned argc, Value *vp)
1642 {
1643 CallArgs args = CallArgsFromVp(argc, vp);
1644 return CallNonGenericMethod<IsDate, DateObject::getUTCSeconds_impl>(cx, args);
1645 }
1647 /* Date.getMilliseconds is mapped to getUTCMilliseconds */
1649 /* static */ MOZ_ALWAYS_INLINE bool
1650 DateObject::getUTCMilliseconds_impl(JSContext *cx, CallArgs args)
1651 {
1652 double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
1653 if (IsFinite(result))
1654 result = msFromTime(result);
1656 args.rval().setNumber(result);
1657 return true;
1658 }
1660 static bool
1661 date_getUTCMilliseconds(JSContext *cx, unsigned argc, Value *vp)
1662 {
1663 CallArgs args = CallArgsFromVp(argc, vp);
1664 return CallNonGenericMethod<IsDate, DateObject::getUTCMilliseconds_impl>(cx, args);
1665 }
1667 /* static */ MOZ_ALWAYS_INLINE bool
1668 DateObject::getTimezoneOffset_impl(JSContext *cx, CallArgs args)
1669 {
1670 DateObject *dateObj = &args.thisv().toObject().as<DateObject>();
1671 double utctime = dateObj->UTCTime().toNumber();
1672 double localtime = dateObj->cachedLocalTime(&cx->runtime()->dateTimeInfo);
1674 /*
1675 * Return the time zone offset in minutes for the current locale that is
1676 * appropriate for this time. This value would be a constant except for
1677 * daylight savings time.
1678 */
1679 double result = (utctime - localtime) / msPerMinute;
1680 args.rval().setNumber(result);
1681 return true;
1682 }
1684 static bool
1685 date_getTimezoneOffset(JSContext *cx, unsigned argc, Value *vp)
1686 {
1687 CallArgs args = CallArgsFromVp(argc, vp);
1688 return CallNonGenericMethod<IsDate, DateObject::getTimezoneOffset_impl>(cx, args);
1689 }
1691 MOZ_ALWAYS_INLINE bool
1692 date_setTime_impl(JSContext *cx, CallArgs args)
1693 {
1694 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1695 if (args.length() == 0) {
1696 dateObj->setUTCTime(GenericNaN(), args.rval().address());
1697 return true;
1698 }
1700 double result;
1701 if (!ToNumber(cx, args[0], &result))
1702 return false;
1704 dateObj->setUTCTime(TimeClip(result), args.rval().address());
1705 return true;
1706 }
1708 static bool
1709 date_setTime(JSContext *cx, unsigned argc, Value *vp)
1710 {
1711 CallArgs args = CallArgsFromVp(argc, vp);
1712 return CallNonGenericMethod<IsDate, date_setTime_impl>(cx, args);
1713 }
1715 static bool
1716 GetMsecsOrDefault(JSContext *cx, const CallArgs &args, unsigned i, double t, double *millis)
1717 {
1718 if (args.length() <= i) {
1719 *millis = msFromTime(t);
1720 return true;
1721 }
1722 return ToNumber(cx, args[i], millis);
1723 }
1725 static bool
1726 GetSecsOrDefault(JSContext *cx, const CallArgs &args, unsigned i, double t, double *sec)
1727 {
1728 if (args.length() <= i) {
1729 *sec = SecFromTime(t);
1730 return true;
1731 }
1732 return ToNumber(cx, args[i], sec);
1733 }
1735 static bool
1736 GetMinsOrDefault(JSContext *cx, const CallArgs &args, unsigned i, double t, double *mins)
1737 {
1738 if (args.length() <= i) {
1739 *mins = MinFromTime(t);
1740 return true;
1741 }
1742 return ToNumber(cx, args[i], mins);
1743 }
1745 /* ES5 15.9.5.28. */
1746 MOZ_ALWAYS_INLINE bool
1747 date_setMilliseconds_impl(JSContext *cx, CallArgs args)
1748 {
1749 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1751 /* Step 1. */
1752 double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo);
1754 /* Step 2. */
1755 double milli;
1756 if (!ToNumber(cx, args.get(0), &milli))
1757 return false;
1758 double time = MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), milli);
1760 /* Step 3. */
1761 double u = TimeClip(UTC(MakeDate(Day(t), time), &cx->runtime()->dateTimeInfo));
1763 /* Steps 4-5. */
1764 dateObj->setUTCTime(u, args.rval().address());
1765 return true;
1766 }
1768 static bool
1769 date_setMilliseconds(JSContext *cx, unsigned argc, Value *vp)
1770 {
1771 CallArgs args = CallArgsFromVp(argc, vp);
1772 return CallNonGenericMethod<IsDate, date_setMilliseconds_impl>(cx, args);
1773 }
1775 /* ES5 15.9.5.29. */
1776 MOZ_ALWAYS_INLINE bool
1777 date_setUTCMilliseconds_impl(JSContext *cx, CallArgs args)
1778 {
1779 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1781 /* Step 1. */
1782 double t = dateObj->UTCTime().toNumber();
1784 /* Step 2. */
1785 double milli;
1786 if (!ToNumber(cx, args.get(0), &milli))
1787 return false;
1788 double time = MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), milli);
1790 /* Step 3. */
1791 double v = TimeClip(MakeDate(Day(t), time));
1793 /* Steps 4-5. */
1794 dateObj->setUTCTime(v, args.rval().address());
1795 return true;
1796 }
1798 static bool
1799 date_setUTCMilliseconds(JSContext *cx, unsigned argc, Value *vp)
1800 {
1801 CallArgs args = CallArgsFromVp(argc, vp);
1802 return CallNonGenericMethod<IsDate, date_setUTCMilliseconds_impl>(cx, args);
1803 }
1805 /* ES5 15.9.5.30. */
1806 MOZ_ALWAYS_INLINE bool
1807 date_setSeconds_impl(JSContext *cx, CallArgs args)
1808 {
1809 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1811 /* Step 1. */
1812 double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo);
1814 /* Step 2. */
1815 double s;
1816 if (!ToNumber(cx, args.get(0), &s))
1817 return false;
1819 /* Step 3. */
1820 double milli;
1821 if (!GetMsecsOrDefault(cx, args, 1, t, &milli))
1822 return false;
1824 /* Step 4. */
1825 double date = MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli));
1827 /* Step 5. */
1828 double u = TimeClip(UTC(date, &cx->runtime()->dateTimeInfo));
1830 /* Steps 6-7. */
1831 dateObj->setUTCTime(u, args.rval().address());
1832 return true;
1833 }
1835 /* ES5 15.9.5.31. */
1836 static bool
1837 date_setSeconds(JSContext *cx, unsigned argc, Value *vp)
1838 {
1839 CallArgs args = CallArgsFromVp(argc, vp);
1840 return CallNonGenericMethod<IsDate, date_setSeconds_impl>(cx, args);
1841 }
1843 MOZ_ALWAYS_INLINE bool
1844 date_setUTCSeconds_impl(JSContext *cx, CallArgs args)
1845 {
1846 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1848 /* Step 1. */
1849 double t = dateObj->UTCTime().toNumber();
1851 /* Step 2. */
1852 double s;
1853 if (!ToNumber(cx, args.get(0), &s))
1854 return false;
1856 /* Step 3. */
1857 double milli;
1858 if (!GetMsecsOrDefault(cx, args, 1, t, &milli))
1859 return false;
1861 /* Step 4. */
1862 double date = MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli));
1864 /* Step 5. */
1865 double v = TimeClip(date);
1867 /* Steps 6-7. */
1868 dateObj->setUTCTime(v, args.rval().address());
1869 return true;
1870 }
1872 /* ES5 15.9.5.32. */
1873 static bool
1874 date_setUTCSeconds(JSContext *cx, unsigned argc, Value *vp)
1875 {
1876 CallArgs args = CallArgsFromVp(argc, vp);
1877 return CallNonGenericMethod<IsDate, date_setUTCSeconds_impl>(cx, args);
1878 }
1880 MOZ_ALWAYS_INLINE bool
1881 date_setMinutes_impl(JSContext *cx, CallArgs args)
1882 {
1883 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1885 /* Step 1. */
1886 double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo);
1888 /* Step 2. */
1889 double m;
1890 if (!ToNumber(cx, args.get(0), &m))
1891 return false;
1893 /* Step 3. */
1894 double s;
1895 if (!GetSecsOrDefault(cx, args, 1, t, &s))
1896 return false;
1898 /* Step 4. */
1899 double milli;
1900 if (!GetMsecsOrDefault(cx, args, 2, t, &milli))
1901 return false;
1903 /* Step 5. */
1904 double date = MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli));
1906 /* Step 6. */
1907 double u = TimeClip(UTC(date, &cx->runtime()->dateTimeInfo));
1909 /* Steps 7-8. */
1910 dateObj->setUTCTime(u, args.rval().address());
1911 return true;
1912 }
1914 /* ES5 15.9.5.33. */
1915 static bool
1916 date_setMinutes(JSContext *cx, unsigned argc, Value *vp)
1917 {
1918 CallArgs args = CallArgsFromVp(argc, vp);
1919 return CallNonGenericMethod<IsDate, date_setMinutes_impl>(cx, args);
1920 }
1922 MOZ_ALWAYS_INLINE bool
1923 date_setUTCMinutes_impl(JSContext *cx, CallArgs args)
1924 {
1925 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1927 /* Step 1. */
1928 double t = dateObj->UTCTime().toNumber();
1930 /* Step 2. */
1931 double m;
1932 if (!ToNumber(cx, args.get(0), &m))
1933 return false;
1935 /* Step 3. */
1936 double s;
1937 if (!GetSecsOrDefault(cx, args, 1, t, &s))
1938 return false;
1940 /* Step 4. */
1941 double milli;
1942 if (!GetMsecsOrDefault(cx, args, 2, t, &milli))
1943 return false;
1945 /* Step 5. */
1946 double date = MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli));
1948 /* Step 6. */
1949 double v = TimeClip(date);
1951 /* Steps 7-8. */
1952 dateObj->setUTCTime(v, args.rval().address());
1953 return true;
1954 }
1956 /* ES5 15.9.5.34. */
1957 static bool
1958 date_setUTCMinutes(JSContext *cx, unsigned argc, Value *vp)
1959 {
1960 CallArgs args = CallArgsFromVp(argc, vp);
1961 return CallNonGenericMethod<IsDate, date_setUTCMinutes_impl>(cx, args);
1962 }
1964 MOZ_ALWAYS_INLINE bool
1965 date_setHours_impl(JSContext *cx, CallArgs args)
1966 {
1967 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1969 /* Step 1. */
1970 double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo);
1972 /* Step 2. */
1973 double h;
1974 if (!ToNumber(cx, args.get(0), &h))
1975 return false;
1977 /* Step 3. */
1978 double m;
1979 if (!GetMinsOrDefault(cx, args, 1, t, &m))
1980 return false;
1982 /* Step 4. */
1983 double s;
1984 if (!GetSecsOrDefault(cx, args, 2, t, &s))
1985 return false;
1987 /* Step 5. */
1988 double milli;
1989 if (!GetMsecsOrDefault(cx, args, 3, t, &milli))
1990 return false;
1992 /* Step 6. */
1993 double date = MakeDate(Day(t), MakeTime(h, m, s, milli));
1995 /* Step 6. */
1996 double u = TimeClip(UTC(date, &cx->runtime()->dateTimeInfo));
1998 /* Steps 7-8. */
1999 dateObj->setUTCTime(u, args.rval().address());
2000 return true;
2001 }
2003 /* ES5 15.9.5.35. */
2004 static bool
2005 date_setHours(JSContext *cx, unsigned argc, Value *vp)
2006 {
2007 CallArgs args = CallArgsFromVp(argc, vp);
2008 return CallNonGenericMethod<IsDate, date_setHours_impl>(cx, args);
2009 }
2011 MOZ_ALWAYS_INLINE bool
2012 date_setUTCHours_impl(JSContext *cx, CallArgs args)
2013 {
2014 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2016 /* Step 1. */
2017 double t = dateObj->UTCTime().toNumber();
2019 /* Step 2. */
2020 double h;
2021 if (!ToNumber(cx, args.get(0), &h))
2022 return false;
2024 /* Step 3. */
2025 double m;
2026 if (!GetMinsOrDefault(cx, args, 1, t, &m))
2027 return false;
2029 /* Step 4. */
2030 double s;
2031 if (!GetSecsOrDefault(cx, args, 2, t, &s))
2032 return false;
2034 /* Step 5. */
2035 double milli;
2036 if (!GetMsecsOrDefault(cx, args, 3, t, &milli))
2037 return false;
2039 /* Step 6. */
2040 double newDate = MakeDate(Day(t), MakeTime(h, m, s, milli));
2042 /* Step 7. */
2043 double v = TimeClip(newDate);
2045 /* Steps 8-9. */
2046 dateObj->setUTCTime(v, args.rval().address());
2047 return true;
2048 }
2050 /* ES5 15.9.5.36. */
2051 static bool
2052 date_setUTCHours(JSContext *cx, unsigned argc, Value *vp)
2053 {
2054 CallArgs args = CallArgsFromVp(argc, vp);
2055 return CallNonGenericMethod<IsDate, date_setUTCHours_impl>(cx, args);
2056 }
2058 MOZ_ALWAYS_INLINE bool
2059 date_setDate_impl(JSContext *cx, CallArgs args)
2060 {
2061 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2063 /* Step 1. */
2064 double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo);
2066 /* Step 2. */
2067 double date;
2068 if (!ToNumber(cx, args.get(0), &date))
2069 return false;
2071 /* Step 3. */
2072 double newDate = MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t));
2074 /* Step 4. */
2075 double u = TimeClip(UTC(newDate, &cx->runtime()->dateTimeInfo));
2077 /* Steps 5-6. */
2078 dateObj->setUTCTime(u, args.rval().address());
2079 return true;
2080 }
2082 /* ES5 15.9.5.37. */
2083 static bool
2084 date_setDate(JSContext *cx, unsigned argc, Value *vp)
2085 {
2086 CallArgs args = CallArgsFromVp(argc, vp);
2087 return CallNonGenericMethod<IsDate, date_setDate_impl>(cx, args);
2088 }
2090 MOZ_ALWAYS_INLINE bool
2091 date_setUTCDate_impl(JSContext *cx, CallArgs args)
2092 {
2093 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2095 /* Step 1. */
2096 double t = dateObj->UTCTime().toNumber();
2098 /* Step 2. */
2099 double date;
2100 if (!ToNumber(cx, args.get(0), &date))
2101 return false;
2103 /* Step 3. */
2104 double newDate = MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t));
2106 /* Step 4. */
2107 double v = TimeClip(newDate);
2109 /* Steps 5-6. */
2110 dateObj->setUTCTime(v, args.rval().address());
2111 return true;
2112 }
2114 static bool
2115 date_setUTCDate(JSContext *cx, unsigned argc, Value *vp)
2116 {
2117 CallArgs args = CallArgsFromVp(argc, vp);
2118 return CallNonGenericMethod<IsDate, date_setUTCDate_impl>(cx, args);
2119 }
2121 static bool
2122 GetDateOrDefault(JSContext *cx, const CallArgs &args, unsigned i, double t, double *date)
2123 {
2124 if (args.length() <= i) {
2125 *date = DateFromTime(t);
2126 return true;
2127 }
2128 return ToNumber(cx, args[i], date);
2129 }
2131 static bool
2132 GetMonthOrDefault(JSContext *cx, const CallArgs &args, unsigned i, double t, double *month)
2133 {
2134 if (args.length() <= i) {
2135 *month = MonthFromTime(t);
2136 return true;
2137 }
2138 return ToNumber(cx, args[i], month);
2139 }
2141 /* ES5 15.9.5.38. */
2142 MOZ_ALWAYS_INLINE bool
2143 date_setMonth_impl(JSContext *cx, CallArgs args)
2144 {
2145 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2147 /* Step 1. */
2148 double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo);
2150 /* Step 2. */
2151 double m;
2152 if (!ToNumber(cx, args.get(0), &m))
2153 return false;
2155 /* Step 3. */
2156 double date;
2157 if (!GetDateOrDefault(cx, args, 1, t, &date))
2158 return false;
2160 /* Step 4. */
2161 double newDate = MakeDate(MakeDay(YearFromTime(t), m, date), TimeWithinDay(t));
2163 /* Step 5. */
2164 double u = TimeClip(UTC(newDate, &cx->runtime()->dateTimeInfo));
2166 /* Steps 6-7. */
2167 dateObj->setUTCTime(u, args.rval().address());
2168 return true;
2169 }
2171 static bool
2172 date_setMonth(JSContext *cx, unsigned argc, Value *vp)
2173 {
2174 CallArgs args = CallArgsFromVp(argc, vp);
2175 return CallNonGenericMethod<IsDate, date_setMonth_impl>(cx, args);
2176 }
2178 /* ES5 15.9.5.39. */
2179 MOZ_ALWAYS_INLINE bool
2180 date_setUTCMonth_impl(JSContext *cx, CallArgs args)
2181 {
2182 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2184 /* Step 1. */
2185 double t = dateObj->UTCTime().toNumber();
2187 /* Step 2. */
2188 double m;
2189 if (!ToNumber(cx, args.get(0), &m))
2190 return false;
2192 /* Step 3. */
2193 double date;
2194 if (!GetDateOrDefault(cx, args, 1, t, &date))
2195 return false;
2197 /* Step 4. */
2198 double newDate = MakeDate(MakeDay(YearFromTime(t), m, date), TimeWithinDay(t));
2200 /* Step 5. */
2201 double v = TimeClip(newDate);
2203 /* Steps 6-7. */
2204 dateObj->setUTCTime(v, args.rval().address());
2205 return true;
2206 }
2208 static bool
2209 date_setUTCMonth(JSContext *cx, unsigned argc, Value *vp)
2210 {
2211 CallArgs args = CallArgsFromVp(argc, vp);
2212 return CallNonGenericMethod<IsDate, date_setUTCMonth_impl>(cx, args);
2213 }
2215 static double
2216 ThisLocalTimeOrZero(Handle<DateObject*> dateObj, DateTimeInfo *dtInfo)
2217 {
2218 double t = dateObj->UTCTime().toNumber();
2219 if (IsNaN(t))
2220 return +0;
2221 return LocalTime(t, dtInfo);
2222 }
2224 static double
2225 ThisUTCTimeOrZero(Handle<DateObject*> dateObj)
2226 {
2227 double t = dateObj->as<DateObject>().UTCTime().toNumber();
2228 return IsNaN(t) ? +0 : t;
2229 }
2231 /* ES5 15.9.5.40. */
2232 MOZ_ALWAYS_INLINE bool
2233 date_setFullYear_impl(JSContext *cx, CallArgs args)
2234 {
2235 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2237 /* Step 1. */
2238 double t = ThisLocalTimeOrZero(dateObj, &cx->runtime()->dateTimeInfo);
2240 /* Step 2. */
2241 double y;
2242 if (!ToNumber(cx, args.get(0), &y))
2243 return false;
2245 /* Step 3. */
2246 double m;
2247 if (!GetMonthOrDefault(cx, args, 1, t, &m))
2248 return false;
2250 /* Step 4. */
2251 double date;
2252 if (!GetDateOrDefault(cx, args, 2, t, &date))
2253 return false;
2255 /* Step 5. */
2256 double newDate = MakeDate(MakeDay(y, m, date), TimeWithinDay(t));
2258 /* Step 6. */
2259 double u = TimeClip(UTC(newDate, &cx->runtime()->dateTimeInfo));
2261 /* Steps 7-8. */
2262 dateObj->setUTCTime(u, args.rval().address());
2263 return true;
2264 }
2266 static bool
2267 date_setFullYear(JSContext *cx, unsigned argc, Value *vp)
2268 {
2269 CallArgs args = CallArgsFromVp(argc, vp);
2270 return CallNonGenericMethod<IsDate, date_setFullYear_impl>(cx, args);
2271 }
2273 /* ES5 15.9.5.41. */
2274 MOZ_ALWAYS_INLINE bool
2275 date_setUTCFullYear_impl(JSContext *cx, CallArgs args)
2276 {
2277 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2279 /* Step 1. */
2280 double t = ThisUTCTimeOrZero(dateObj);
2282 /* Step 2. */
2283 double y;
2284 if (!ToNumber(cx, args.get(0), &y))
2285 return false;
2287 /* Step 3. */
2288 double m;
2289 if (!GetMonthOrDefault(cx, args, 1, t, &m))
2290 return false;
2292 /* Step 4. */
2293 double date;
2294 if (!GetDateOrDefault(cx, args, 2, t, &date))
2295 return false;
2297 /* Step 5. */
2298 double newDate = MakeDate(MakeDay(y, m, date), TimeWithinDay(t));
2300 /* Step 6. */
2301 double v = TimeClip(newDate);
2303 /* Steps 7-8. */
2304 dateObj->setUTCTime(v, args.rval().address());
2305 return true;
2306 }
2308 static bool
2309 date_setUTCFullYear(JSContext *cx, unsigned argc, Value *vp)
2310 {
2311 CallArgs args = CallArgsFromVp(argc, vp);
2312 return CallNonGenericMethod<IsDate, date_setUTCFullYear_impl>(cx, args);
2313 }
2315 /* ES5 Annex B.2.5. */
2316 MOZ_ALWAYS_INLINE bool
2317 date_setYear_impl(JSContext *cx, CallArgs args)
2318 {
2319 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2321 /* Step 1. */
2322 double t = ThisLocalTimeOrZero(dateObj, &cx->runtime()->dateTimeInfo);
2324 /* Step 2. */
2325 double y;
2326 if (!ToNumber(cx, args.get(0), &y))
2327 return false;
2329 /* Step 3. */
2330 if (IsNaN(y)) {
2331 dateObj->setUTCTime(GenericNaN(), args.rval().address());
2332 return true;
2333 }
2335 /* Step 4. */
2336 double yint = ToInteger(y);
2337 if (0 <= yint && yint <= 99)
2338 yint += 1900;
2340 /* Step 5. */
2341 double day = MakeDay(yint, MonthFromTime(t), DateFromTime(t));
2343 /* Step 6. */
2344 double u = UTC(MakeDate(day, TimeWithinDay(t)), &cx->runtime()->dateTimeInfo);
2346 /* Steps 7-8. */
2347 dateObj->setUTCTime(TimeClip(u), args.rval().address());
2348 return true;
2349 }
2351 static bool
2352 date_setYear(JSContext *cx, unsigned argc, Value *vp)
2353 {
2354 CallArgs args = CallArgsFromVp(argc, vp);
2355 return CallNonGenericMethod<IsDate, date_setYear_impl>(cx, args);
2356 }
2358 /* constants for toString, toUTCString */
2359 static const char js_NaN_date_str[] = "Invalid Date";
2360 static const char * const days[] =
2361 {
2362 "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
2363 };
2364 static const char * const months[] =
2365 {
2366 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
2367 };
2370 // Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
2371 // requires a PRMJTime... which only has 16-bit years. Sub-ECMA.
2372 static void
2373 print_gmt_string(char* buf, size_t size, double utctime)
2374 {
2375 JS_ASSERT(TimeClip(utctime) == utctime);
2376 JS_snprintf(buf, size, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
2377 days[int(WeekDay(utctime))],
2378 int(DateFromTime(utctime)),
2379 months[int(MonthFromTime(utctime))],
2380 int(YearFromTime(utctime)),
2381 int(HourFromTime(utctime)),
2382 int(MinFromTime(utctime)),
2383 int(SecFromTime(utctime)));
2384 }
2386 static void
2387 print_iso_string(char* buf, size_t size, double utctime)
2388 {
2389 JS_ASSERT(TimeClip(utctime) == utctime);
2390 JS_snprintf(buf, size, "%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.3dZ",
2391 int(YearFromTime(utctime)),
2392 int(MonthFromTime(utctime)) + 1,
2393 int(DateFromTime(utctime)),
2394 int(HourFromTime(utctime)),
2395 int(MinFromTime(utctime)),
2396 int(SecFromTime(utctime)),
2397 int(msFromTime(utctime)));
2398 }
2400 /* ES5 B.2.6. */
2401 MOZ_ALWAYS_INLINE bool
2402 date_toGMTString_impl(JSContext *cx, CallArgs args)
2403 {
2404 double utctime = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
2406 char buf[100];
2407 if (!IsFinite(utctime))
2408 JS_snprintf(buf, sizeof buf, js_NaN_date_str);
2409 else
2410 print_gmt_string(buf, sizeof buf, utctime);
2412 JSString *str = JS_NewStringCopyZ(cx, buf);
2413 if (!str)
2414 return false;
2415 args.rval().setString(str);
2416 return true;
2417 }
2419 /* ES5 15.9.5.43. */
2420 static bool
2421 date_toGMTString(JSContext *cx, unsigned argc, Value *vp)
2422 {
2423 CallArgs args = CallArgsFromVp(argc, vp);
2424 return CallNonGenericMethod<IsDate, date_toGMTString_impl>(cx, args);
2425 }
2427 MOZ_ALWAYS_INLINE bool
2428 date_toISOString_impl(JSContext *cx, CallArgs args)
2429 {
2430 double utctime = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
2431 if (!IsFinite(utctime)) {
2432 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INVALID_DATE);
2433 return false;
2434 }
2436 char buf[100];
2437 print_iso_string(buf, sizeof buf, utctime);
2439 JSString *str = JS_NewStringCopyZ(cx, buf);
2440 if (!str)
2441 return false;
2442 args.rval().setString(str);
2443 return true;
2445 }
2447 static bool
2448 date_toISOString(JSContext *cx, unsigned argc, Value *vp)
2449 {
2450 CallArgs args = CallArgsFromVp(argc, vp);
2451 return CallNonGenericMethod<IsDate, date_toISOString_impl>(cx, args);
2452 }
2454 /* ES5 15.9.5.44. */
2455 static bool
2456 date_toJSON(JSContext *cx, unsigned argc, Value *vp)
2457 {
2458 CallArgs args = CallArgsFromVp(argc, vp);
2460 /* Step 1. */
2461 RootedObject obj(cx, ToObject(cx, args.thisv()));
2462 if (!obj)
2463 return false;
2465 /* Step 2. */
2466 RootedValue tv(cx, ObjectValue(*obj));
2467 if (!ToPrimitive(cx, JSTYPE_NUMBER, &tv))
2468 return false;
2470 /* Step 3. */
2471 if (tv.isDouble() && !IsFinite(tv.toDouble())) {
2472 args.rval().setNull();
2473 return true;
2474 }
2476 /* Step 4. */
2477 RootedValue toISO(cx);
2478 if (!JSObject::getProperty(cx, obj, obj, cx->names().toISOString, &toISO))
2479 return false;
2481 /* Step 5. */
2482 if (!js_IsCallable(toISO)) {
2483 JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, nullptr,
2484 JSMSG_BAD_TOISOSTRING_PROP);
2485 return false;
2486 }
2488 /* Step 6. */
2489 InvokeArgs args2(cx);
2490 if (!args2.init(0))
2491 return false;
2493 args2.setCallee(toISO);
2494 args2.setThis(ObjectValue(*obj));
2496 if (!Invoke(cx, args2))
2497 return false;
2498 args.rval().set(args2.rval());
2499 return true;
2500 }
2502 /* for Date.toLocaleFormat; interface to PRMJTime date struct.
2503 */
2504 static void
2505 new_explode(double timeval, PRMJTime *split, DateTimeInfo *dtInfo)
2506 {
2507 double year = YearFromTime(timeval);
2509 split->tm_usec = int32_t(msFromTime(timeval)) * 1000;
2510 split->tm_sec = int8_t(SecFromTime(timeval));
2511 split->tm_min = int8_t(MinFromTime(timeval));
2512 split->tm_hour = int8_t(HourFromTime(timeval));
2513 split->tm_mday = int8_t(DateFromTime(timeval));
2514 split->tm_mon = int8_t(MonthFromTime(timeval));
2515 split->tm_wday = int8_t(WeekDay(timeval));
2516 split->tm_year = year;
2517 split->tm_yday = int16_t(DayWithinYear(timeval, year));
2519 /* not sure how this affects things, but it doesn't seem
2520 to matter. */
2521 split->tm_isdst = (DaylightSavingTA(timeval, dtInfo) != 0);
2522 }
2524 typedef enum formatspec {
2525 FORMATSPEC_FULL, FORMATSPEC_DATE, FORMATSPEC_TIME
2526 } formatspec;
2528 /* helper function */
2529 static bool
2530 date_format(JSContext *cx, double date, formatspec format, MutableHandleValue rval)
2531 {
2532 char buf[100];
2533 char tzbuf[100];
2534 bool usetz;
2535 size_t i, tzlen;
2536 PRMJTime split;
2538 if (!IsFinite(date)) {
2539 JS_snprintf(buf, sizeof buf, js_NaN_date_str);
2540 } else {
2541 JS_ASSERT(TimeClip(date) == date);
2543 double local = LocalTime(date, &cx->runtime()->dateTimeInfo);
2545 /* offset from GMT in minutes. The offset includes daylight savings,
2546 if it applies. */
2547 int minutes = (int) floor(AdjustTime(date, &cx->runtime()->dateTimeInfo) / msPerMinute);
2549 /* map 510 minutes to 0830 hours */
2550 int offset = (minutes / 60) * 100 + minutes % 60;
2552 /* print as "Wed Nov 05 19:38:03 GMT-0800 (PST) 1997" The TZA is
2553 * printed as 'GMT-0800' rather than as 'PST' to avoid
2554 * operating-system dependence on strftime (which
2555 * PRMJ_FormatTimeUSEnglish calls, for %Z only.) win32 prints
2556 * PST as 'Pacific Standard Time.' This way we always know
2557 * what we're getting, and can parse it if we produce it.
2558 * The OS TZA string is included as a comment.
2559 */
2561 /* get a timezone string from the OS to include as a
2562 comment. */
2563 new_explode(date, &split, &cx->runtime()->dateTimeInfo);
2564 if (PRMJ_FormatTime(tzbuf, sizeof tzbuf, "(%Z)", &split) != 0) {
2566 /* Decide whether to use the resulting timezone string.
2567 *
2568 * Reject it if it contains any non-ASCII, non-alphanumeric
2569 * characters. It's then likely in some other character
2570 * encoding, and we probably won't display it correctly.
2571 */
2572 usetz = true;
2573 tzlen = strlen(tzbuf);
2574 if (tzlen > 100) {
2575 usetz = false;
2576 } else {
2577 for (i = 0; i < tzlen; i++) {
2578 jschar c = tzbuf[i];
2579 if (c > 127 ||
2580 !(isalpha(c) || isdigit(c) ||
2581 c == ' ' || c == '(' || c == ')')) {
2582 usetz = false;
2583 }
2584 }
2585 }
2587 /* Also reject it if it's not parenthesized or if it's '()'. */
2588 if (tzbuf[0] != '(' || tzbuf[1] == ')')
2589 usetz = false;
2590 } else
2591 usetz = false;
2593 switch (format) {
2594 case FORMATSPEC_FULL:
2595 /*
2596 * Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
2597 * requires a PRMJTime... which only has 16-bit years. Sub-ECMA.
2598 */
2599 /* Tue Oct 31 2000 09:41:40 GMT-0800 (PST) */
2600 JS_snprintf(buf, sizeof buf,
2601 "%s %s %.2d %.4d %.2d:%.2d:%.2d GMT%+.4d%s%s",
2602 days[int(WeekDay(local))],
2603 months[int(MonthFromTime(local))],
2604 int(DateFromTime(local)),
2605 int(YearFromTime(local)),
2606 int(HourFromTime(local)),
2607 int(MinFromTime(local)),
2608 int(SecFromTime(local)),
2609 offset,
2610 usetz ? " " : "",
2611 usetz ? tzbuf : "");
2612 break;
2613 case FORMATSPEC_DATE:
2614 /* Tue Oct 31 2000 */
2615 JS_snprintf(buf, sizeof buf,
2616 "%s %s %.2d %.4d",
2617 days[int(WeekDay(local))],
2618 months[int(MonthFromTime(local))],
2619 int(DateFromTime(local)),
2620 int(YearFromTime(local)));
2621 break;
2622 case FORMATSPEC_TIME:
2623 /* 09:41:40 GMT-0800 (PST) */
2624 JS_snprintf(buf, sizeof buf,
2625 "%.2d:%.2d:%.2d GMT%+.4d%s%s",
2626 int(HourFromTime(local)),
2627 int(MinFromTime(local)),
2628 int(SecFromTime(local)),
2629 offset,
2630 usetz ? " " : "",
2631 usetz ? tzbuf : "");
2632 break;
2633 }
2634 }
2636 JSString *str = JS_NewStringCopyZ(cx, buf);
2637 if (!str)
2638 return false;
2639 rval.setString(str);
2640 return true;
2641 }
2643 static bool
2644 ToLocaleFormatHelper(JSContext *cx, HandleObject obj, const char *format, MutableHandleValue rval)
2645 {
2646 double utctime = obj->as<DateObject>().UTCTime().toNumber();
2648 char buf[100];
2649 if (!IsFinite(utctime)) {
2650 JS_snprintf(buf, sizeof buf, js_NaN_date_str);
2651 } else {
2652 int result_len;
2653 double local = LocalTime(utctime, &cx->runtime()->dateTimeInfo);
2654 PRMJTime split;
2655 new_explode(local, &split, &cx->runtime()->dateTimeInfo);
2657 /* Let PRMJTime format it. */
2658 result_len = PRMJ_FormatTime(buf, sizeof buf, format, &split);
2660 /* If it failed, default to toString. */
2661 if (result_len == 0)
2662 return date_format(cx, utctime, FORMATSPEC_FULL, rval);
2664 /* Hacked check against undesired 2-digit year 00/00/00 form. */
2665 if (strcmp(format, "%x") == 0 && result_len >= 6 &&
2666 /* Format %x means use OS settings, which may have 2-digit yr, so
2667 hack end of 3/11/22 or 11.03.22 or 11Mar22 to use 4-digit yr...*/
2668 !isdigit(buf[result_len - 3]) &&
2669 isdigit(buf[result_len - 2]) && isdigit(buf[result_len - 1]) &&
2670 /* ...but not if starts with 4-digit year, like 2022/3/11. */
2671 !(isdigit(buf[0]) && isdigit(buf[1]) &&
2672 isdigit(buf[2]) && isdigit(buf[3]))) {
2673 JS_snprintf(buf + (result_len - 2), (sizeof buf) - (result_len - 2),
2674 "%d", js_DateGetYear(cx, obj));
2675 }
2677 }
2679 if (cx->runtime()->localeCallbacks && cx->runtime()->localeCallbacks->localeToUnicode)
2680 return cx->runtime()->localeCallbacks->localeToUnicode(cx, buf, rval);
2682 JSString *str = JS_NewStringCopyZ(cx, buf);
2683 if (!str)
2684 return false;
2685 rval.setString(str);
2686 return true;
2687 }
2689 #if !EXPOSE_INTL_API
2690 static bool
2691 ToLocaleStringHelper(JSContext *cx, Handle<DateObject*> dateObj, MutableHandleValue rval)
2692 {
2693 /*
2694 * Use '%#c' for windows, because '%c' is backward-compatible and non-y2k
2695 * with msvc; '%#c' requests that a full year be used in the result string.
2696 */
2697 return ToLocaleFormatHelper(cx, dateObj,
2698 #if defined(_WIN32) && !defined(__MWERKS__)
2699 "%#c"
2700 #else
2701 "%c"
2702 #endif
2703 , rval);
2704 }
2706 /* ES5 15.9.5.5. */
2707 MOZ_ALWAYS_INLINE bool
2708 date_toLocaleString_impl(JSContext *cx, CallArgs args)
2709 {
2710 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2711 return ToLocaleStringHelper(cx, dateObj, args.rval());
2712 }
2714 static bool
2715 date_toLocaleString(JSContext *cx, unsigned argc, Value *vp)
2716 {
2717 CallArgs args = CallArgsFromVp(argc, vp);
2718 return CallNonGenericMethod<IsDate, date_toLocaleString_impl>(cx, args);
2719 }
2721 /* ES5 15.9.5.6. */
2722 MOZ_ALWAYS_INLINE bool
2723 date_toLocaleDateString_impl(JSContext *cx, CallArgs args)
2724 {
2725 /*
2726 * Use '%#x' for windows, because '%x' is backward-compatible and non-y2k
2727 * with msvc; '%#x' requests that a full year be used in the result string.
2728 */
2729 static const char format[] =
2730 #if defined(_WIN32) && !defined(__MWERKS__)
2731 "%#x"
2732 #else
2733 "%x"
2734 #endif
2735 ;
2737 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2738 return ToLocaleFormatHelper(cx, dateObj, format, args.rval());
2739 }
2741 static bool
2742 date_toLocaleDateString(JSContext *cx, unsigned argc, Value *vp)
2743 {
2744 CallArgs args = CallArgsFromVp(argc, vp);
2745 return CallNonGenericMethod<IsDate, date_toLocaleDateString_impl>(cx, args);
2746 }
2748 /* ES5 15.9.5.7. */
2749 MOZ_ALWAYS_INLINE bool
2750 date_toLocaleTimeString_impl(JSContext *cx, CallArgs args)
2751 {
2752 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2753 return ToLocaleFormatHelper(cx, dateObj, "%X", args.rval());
2754 }
2756 static bool
2757 date_toLocaleTimeString(JSContext *cx, unsigned argc, Value *vp)
2758 {
2759 CallArgs args = CallArgsFromVp(argc, vp);
2760 return CallNonGenericMethod<IsDate, date_toLocaleTimeString_impl>(cx, args);
2761 }
2762 #endif /* !EXPOSE_INTL_API */
2764 MOZ_ALWAYS_INLINE bool
2765 date_toLocaleFormat_impl(JSContext *cx, CallArgs args)
2766 {
2767 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2769 if (args.length() == 0) {
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 return ToLocaleFormatHelper(cx, dateObj,
2775 #if defined(_WIN32) && !defined(__MWERKS__)
2776 "%#c"
2777 #else
2778 "%c"
2779 #endif
2780 , args.rval());
2781 }
2783 RootedString fmt(cx, ToString<CanGC>(cx, args[0]));
2784 if (!fmt)
2785 return false;
2787 JSAutoByteString fmtbytes(cx, fmt);
2788 if (!fmtbytes)
2789 return false;
2791 return ToLocaleFormatHelper(cx, dateObj, fmtbytes.ptr(), args.rval());
2792 }
2794 static bool
2795 date_toLocaleFormat(JSContext *cx, unsigned argc, Value *vp)
2796 {
2797 CallArgs args = CallArgsFromVp(argc, vp);
2798 return CallNonGenericMethod<IsDate, date_toLocaleFormat_impl>(cx, args);
2799 }
2801 /* ES5 15.9.5.4. */
2802 MOZ_ALWAYS_INLINE bool
2803 date_toTimeString_impl(JSContext *cx, CallArgs args)
2804 {
2805 return date_format(cx, args.thisv().toObject().as<DateObject>().UTCTime().toNumber(),
2806 FORMATSPEC_TIME, args.rval());
2807 }
2809 static bool
2810 date_toTimeString(JSContext *cx, unsigned argc, Value *vp)
2811 {
2812 CallArgs args = CallArgsFromVp(argc, vp);
2813 return CallNonGenericMethod<IsDate, date_toTimeString_impl>(cx, args);
2814 }
2816 /* ES5 15.9.5.3. */
2817 MOZ_ALWAYS_INLINE bool
2818 date_toDateString_impl(JSContext *cx, CallArgs args)
2819 {
2820 return date_format(cx, args.thisv().toObject().as<DateObject>().UTCTime().toNumber(),
2821 FORMATSPEC_DATE, args.rval());
2822 }
2824 static bool
2825 date_toDateString(JSContext *cx, unsigned argc, Value *vp)
2826 {
2827 CallArgs args = CallArgsFromVp(argc, vp);
2828 return CallNonGenericMethod<IsDate, date_toDateString_impl>(cx, args);
2829 }
2831 #if JS_HAS_TOSOURCE
2832 MOZ_ALWAYS_INLINE bool
2833 date_toSource_impl(JSContext *cx, CallArgs args)
2834 {
2835 StringBuffer sb(cx);
2836 if (!sb.append("(new Date(") ||
2837 !NumberValueToStringBuffer(cx, args.thisv().toObject().as<DateObject>().UTCTime(), sb) ||
2838 !sb.append("))"))
2839 {
2840 return false;
2841 }
2843 JSString *str = sb.finishString();
2844 if (!str)
2845 return false;
2846 args.rval().setString(str);
2847 return true;
2848 }
2850 static bool
2851 date_toSource(JSContext *cx, unsigned argc, Value *vp)
2852 {
2853 CallArgs args = CallArgsFromVp(argc, vp);
2854 return CallNonGenericMethod<IsDate, date_toSource_impl>(cx, args);
2855 }
2856 #endif
2858 MOZ_ALWAYS_INLINE bool
2859 date_toString_impl(JSContext *cx, CallArgs args)
2860 {
2861 return date_format(cx, args.thisv().toObject().as<DateObject>().UTCTime().toNumber(),
2862 FORMATSPEC_FULL, args.rval());
2863 }
2865 static bool
2866 date_toString(JSContext *cx, unsigned argc, Value *vp)
2867 {
2868 CallArgs args = CallArgsFromVp(argc, vp);
2869 return CallNonGenericMethod<IsDate, date_toString_impl>(cx, args);
2870 }
2872 MOZ_ALWAYS_INLINE bool
2873 date_valueOf_impl(JSContext *cx, CallArgs args)
2874 {
2875 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2876 args.rval().set(dateObj->UTCTime());
2877 return true;
2878 }
2880 static bool
2881 date_valueOf(JSContext *cx, unsigned argc, Value *vp)
2882 {
2883 CallArgs args = CallArgsFromVp(argc, vp);
2884 return CallNonGenericMethod<IsDate, date_valueOf_impl>(cx, args);
2885 }
2887 static const JSFunctionSpec date_static_methods[] = {
2888 JS_FN("UTC", date_UTC, MAXARGS,0),
2889 JS_FN("parse", date_parse, 1,0),
2890 JS_FN("now", date_now, 0,0),
2891 JS_FS_END
2892 };
2894 static const JSFunctionSpec date_methods[] = {
2895 JS_FN("getTime", date_getTime, 0,0),
2896 JS_FN("getTimezoneOffset", date_getTimezoneOffset, 0,0),
2897 JS_FN("getYear", date_getYear, 0,0),
2898 JS_FN("getFullYear", date_getFullYear, 0,0),
2899 JS_FN("getUTCFullYear", date_getUTCFullYear, 0,0),
2900 JS_FN("getMonth", date_getMonth, 0,0),
2901 JS_FN("getUTCMonth", date_getUTCMonth, 0,0),
2902 JS_FN("getDate", date_getDate, 0,0),
2903 JS_FN("getUTCDate", date_getUTCDate, 0,0),
2904 JS_FN("getDay", date_getDay, 0,0),
2905 JS_FN("getUTCDay", date_getUTCDay, 0,0),
2906 JS_FN("getHours", date_getHours, 0,0),
2907 JS_FN("getUTCHours", date_getUTCHours, 0,0),
2908 JS_FN("getMinutes", date_getMinutes, 0,0),
2909 JS_FN("getUTCMinutes", date_getUTCMinutes, 0,0),
2910 JS_FN("getSeconds", date_getUTCSeconds, 0,0),
2911 JS_FN("getUTCSeconds", date_getUTCSeconds, 0,0),
2912 JS_FN("getMilliseconds", date_getUTCMilliseconds, 0,0),
2913 JS_FN("getUTCMilliseconds", date_getUTCMilliseconds, 0,0),
2914 JS_FN("setTime", date_setTime, 1,0),
2915 JS_FN("setYear", date_setYear, 1,0),
2916 JS_FN("setFullYear", date_setFullYear, 3,0),
2917 JS_FN("setUTCFullYear", date_setUTCFullYear, 3,0),
2918 JS_FN("setMonth", date_setMonth, 2,0),
2919 JS_FN("setUTCMonth", date_setUTCMonth, 2,0),
2920 JS_FN("setDate", date_setDate, 1,0),
2921 JS_FN("setUTCDate", date_setUTCDate, 1,0),
2922 JS_FN("setHours", date_setHours, 4,0),
2923 JS_FN("setUTCHours", date_setUTCHours, 4,0),
2924 JS_FN("setMinutes", date_setMinutes, 3,0),
2925 JS_FN("setUTCMinutes", date_setUTCMinutes, 3,0),
2926 JS_FN("setSeconds", date_setSeconds, 2,0),
2927 JS_FN("setUTCSeconds", date_setUTCSeconds, 2,0),
2928 JS_FN("setMilliseconds", date_setMilliseconds, 1,0),
2929 JS_FN("setUTCMilliseconds", date_setUTCMilliseconds, 1,0),
2930 JS_FN("toUTCString", date_toGMTString, 0,0),
2931 JS_FN("toLocaleFormat", date_toLocaleFormat, 0,0),
2932 #if EXPOSE_INTL_API
2933 JS_SELF_HOSTED_FN(js_toLocaleString_str, "Date_toLocaleString", 0,0),
2934 JS_SELF_HOSTED_FN("toLocaleDateString", "Date_toLocaleDateString", 0,0),
2935 JS_SELF_HOSTED_FN("toLocaleTimeString", "Date_toLocaleTimeString", 0,0),
2936 #else
2937 JS_FN(js_toLocaleString_str, date_toLocaleString, 0,0),
2938 JS_FN("toLocaleDateString", date_toLocaleDateString, 0,0),
2939 JS_FN("toLocaleTimeString", date_toLocaleTimeString, 0,0),
2940 #endif
2941 JS_FN("toDateString", date_toDateString, 0,0),
2942 JS_FN("toTimeString", date_toTimeString, 0,0),
2943 JS_FN("toISOString", date_toISOString, 0,0),
2944 JS_FN(js_toJSON_str, date_toJSON, 1,0),
2945 #if JS_HAS_TOSOURCE
2946 JS_FN(js_toSource_str, date_toSource, 0,0),
2947 #endif
2948 JS_FN(js_toString_str, date_toString, 0,0),
2949 JS_FN(js_valueOf_str, date_valueOf, 0,0),
2950 JS_FS_END
2951 };
2953 bool
2954 js_Date(JSContext *cx, unsigned argc, Value *vp)
2955 {
2956 CallArgs args = CallArgsFromVp(argc, vp);
2958 /* Date called as function. */
2959 if (!args.isConstructing())
2960 return date_format(cx, NowAsMillis(), FORMATSPEC_FULL, args.rval());
2962 /* Date called as constructor. */
2963 double d;
2964 if (args.length() == 0) {
2965 /* ES5 15.9.3.3. */
2966 d = NowAsMillis();
2967 } else if (args.length() == 1) {
2968 /* ES5 15.9.3.2. */
2970 /* Step 1. */
2971 if (!ToPrimitive(cx, args[0]))
2972 return false;
2974 if (args[0].isString()) {
2975 /* Step 2. */
2976 JSString *str = args[0].toString();
2977 if (!str)
2978 return false;
2980 JSLinearString *linearStr = str->ensureLinear(cx);
2981 if (!linearStr)
2982 return false;
2984 if (!date_parseString(linearStr, &d, &cx->runtime()->dateTimeInfo))
2985 d = GenericNaN();
2986 else
2987 d = TimeClip(d);
2988 } else {
2989 /* Step 3. */
2990 if (!ToNumber(cx, args[0], &d))
2991 return false;
2992 d = TimeClip(d);
2993 }
2994 } else {
2995 double msec_time;
2996 if (!date_msecFromArgs(cx, args, &msec_time))
2997 return false;
2999 if (IsFinite(msec_time)) {
3000 msec_time = UTC(msec_time, &cx->runtime()->dateTimeInfo);
3001 msec_time = TimeClip(msec_time);
3002 }
3003 d = msec_time;
3004 }
3006 JSObject *obj = js_NewDateObjectMsec(cx, d);
3007 if (!obj)
3008 return false;
3010 args.rval().setObject(*obj);
3011 return true;
3012 }
3014 static bool
3015 FinishDateClassInit(JSContext *cx, HandleObject ctor, HandleObject proto)
3016 {
3017 proto->as<DateObject>().setUTCTime(GenericNaN());
3019 /*
3020 * Date.prototype.toGMTString has the same initial value as
3021 * Date.prototype.toUTCString.
3022 */
3023 RootedValue toUTCStringFun(cx);
3024 RootedId toUTCStringId(cx, NameToId(cx->names().toUTCString));
3025 RootedId toGMTStringId(cx, NameToId(cx->names().toGMTString));
3026 return baseops::GetProperty(cx, proto, toUTCStringId, &toUTCStringFun) &&
3027 baseops::DefineGeneric(cx, proto, toGMTStringId, toUTCStringFun,
3028 JS_PropertyStub, JS_StrictPropertyStub, 0);
3029 }
3031 const Class DateObject::class_ = {
3032 js_Date_str,
3033 JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) |
3034 JSCLASS_HAS_CACHED_PROTO(JSProto_Date),
3035 JS_PropertyStub, /* addProperty */
3036 JS_DeletePropertyStub, /* delProperty */
3037 JS_PropertyStub, /* getProperty */
3038 JS_StrictPropertyStub, /* setProperty */
3039 JS_EnumerateStub,
3040 JS_ResolveStub,
3041 date_convert,
3042 nullptr, /* finalize */
3043 nullptr, /* call */
3044 nullptr, /* hasInstance */
3045 nullptr, /* construct */
3046 nullptr, /* trace */
3047 {
3048 GenericCreateConstructor<js_Date, NAME_OFFSET(Date), MAXARGS>,
3049 GenericCreatePrototype<&DateObject::class_>,
3050 date_static_methods,
3051 date_methods,
3052 FinishDateClassInit
3053 }
3054 };
3056 JS_FRIEND_API(JSObject *)
3057 js_NewDateObjectMsec(JSContext *cx, double msec_time)
3058 {
3059 JSObject *obj = NewBuiltinClassInstance(cx, &DateObject::class_);
3060 if (!obj)
3061 return nullptr;
3062 obj->as<DateObject>().setUTCTime(msec_time);
3063 return obj;
3064 }
3066 JS_FRIEND_API(JSObject *)
3067 js_NewDateObject(JSContext *cx, int year, int mon, int mday,
3068 int hour, int min, int sec)
3069 {
3070 JS_ASSERT(mon < 12);
3071 double msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
3072 return js_NewDateObjectMsec(cx, UTC(msec_time, &cx->runtime()->dateTimeInfo));
3073 }
3075 JS_FRIEND_API(bool)
3076 js_DateIsValid(JSObject *obj)
3077 {
3078 return obj->is<DateObject>() && !IsNaN(obj->as<DateObject>().UTCTime().toNumber());
3079 }
3081 JS_FRIEND_API(int)
3082 js_DateGetYear(JSContext *cx, JSObject *obj)
3083 {
3084 /* Preserve legacy API behavior of returning 0 for invalid dates. */
3085 JS_ASSERT(obj);
3086 double localtime = obj->as<DateObject>().cachedLocalTime(&cx->runtime()->dateTimeInfo);
3087 if (IsNaN(localtime))
3088 return 0;
3090 return (int) YearFromTime(localtime);
3091 }
3093 JS_FRIEND_API(int)
3094 js_DateGetMonth(JSContext *cx, JSObject *obj)
3095 {
3096 JS_ASSERT(obj);
3097 double localtime = obj->as<DateObject>().cachedLocalTime(&cx->runtime()->dateTimeInfo);
3098 if (IsNaN(localtime))
3099 return 0;
3101 return (int) MonthFromTime(localtime);
3102 }
3104 JS_FRIEND_API(int)
3105 js_DateGetDate(JSContext *cx, JSObject *obj)
3106 {
3107 JS_ASSERT(obj);
3108 double localtime = obj->as<DateObject>().cachedLocalTime(&cx->runtime()->dateTimeInfo);
3109 if (IsNaN(localtime))
3110 return 0;
3112 return (int) DateFromTime(localtime);
3113 }
3115 JS_FRIEND_API(int)
3116 js_DateGetHours(JSContext *cx, JSObject *obj)
3117 {
3118 JS_ASSERT(obj);
3119 double localtime = obj->as<DateObject>().cachedLocalTime(&cx->runtime()->dateTimeInfo);
3120 if (IsNaN(localtime))
3121 return 0;
3123 return (int) HourFromTime(localtime);
3124 }
3126 JS_FRIEND_API(int)
3127 js_DateGetMinutes(JSContext *cx, JSObject *obj)
3128 {
3129 JS_ASSERT(obj);
3130 double localtime = obj->as<DateObject>().cachedLocalTime(&cx->runtime()->dateTimeInfo);
3131 if (IsNaN(localtime))
3132 return 0;
3134 return (int) MinFromTime(localtime);
3135 }
3137 JS_FRIEND_API(int)
3138 js_DateGetSeconds(JSObject *obj)
3139 {
3140 if (!obj->is<DateObject>())
3141 return 0;
3143 double utctime = obj->as<DateObject>().UTCTime().toNumber();
3144 if (IsNaN(utctime))
3145 return 0;
3146 return (int) SecFromTime(utctime);
3147 }
3149 JS_FRIEND_API(double)
3150 js_DateGetMsecSinceEpoch(JSObject *obj)
3151 {
3152 return obj->is<DateObject>() ? obj->as<DateObject>().UTCTime().toNumber() : 0;
3153 }
3156 static const NativeImpl sReadOnlyDateMethods[] = {
3157 DateObject::getTime_impl,
3158 DateObject::getYear_impl,
3159 DateObject::getFullYear_impl,
3160 DateObject::getUTCFullYear_impl,
3161 DateObject::getMonth_impl,
3162 DateObject::getUTCMonth_impl,
3163 DateObject::getDate_impl,
3164 DateObject::getUTCDate_impl,
3165 DateObject::getDay_impl,
3166 DateObject::getUTCDay_impl,
3167 DateObject::getHours_impl,
3168 DateObject::getUTCHours_impl,
3169 DateObject::getMinutes_impl,
3170 DateObject::getUTCMinutes_impl,
3171 DateObject::getUTCSeconds_impl,
3172 DateObject::getUTCMilliseconds_impl,
3173 DateObject::getTimezoneOffset_impl,
3174 date_toGMTString_impl,
3175 date_toISOString_impl,
3176 date_toLocaleFormat_impl,
3177 date_toTimeString_impl,
3178 date_toDateString_impl,
3179 date_toSource_impl,
3180 date_toString_impl,
3181 date_valueOf_impl
3182 };