js/src/jsdate.cpp

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:765d42099e91
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 /*
8 * JS date methods.
9 *
10 * "For example, OS/360 devotes 26 bytes of the permanently
11 * resident date-turnover routine to the proper handling of
12 * December 31 on leap years (when it is Day 366). That
13 * might have been left to the operator."
14 *
15 * Frederick Brooks, 'The Second-System Effect'.
16 */
17
18 #include "jsdate.h"
19
20 #include "mozilla/ArrayUtils.h"
21 #include "mozilla/FloatingPoint.h"
22
23 #include <ctype.h>
24 #include <math.h>
25 #include <string.h>
26
27 #include "jsapi.h"
28 #include "jscntxt.h"
29 #include "jsnum.h"
30 #include "jsobj.h"
31 #include "jsprf.h"
32 #include "jsstr.h"
33 #include "jstypes.h"
34 #include "jsutil.h"
35 #include "prmjtime.h"
36
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"
44
45 #include "jsobjinlines.h"
46
47 using namespace js;
48 using namespace js::types;
49
50 using mozilla::ArrayLength;
51 using mozilla::IsFinite;
52 using mozilla::IsNaN;
53 using JS::GenericNaN;
54
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 */
102
103 static inline double
104 Day(double t)
105 {
106 return floor(t / msPerDay);
107 }
108
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 }
117
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 }
125
126 static inline double
127 DaysInYear(double year)
128 {
129 if (!IsFinite(year))
130 return GenericNaN();
131 return IsLeapYear(year) ? 366 : 365;
132 }
133
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 }
142
143 static inline double
144 TimeFromYear(double y)
145 {
146 return DayFromYear(y) * msPerDay;
147 }
148
149 static double
150 YearFromTime(double t)
151 {
152 if (!IsFinite(t))
153 return GenericNaN();
154
155 JS_ASSERT(ToInteger(t) == t);
156
157 double y = floor(t / (msPerDay * 365.2425)) + 1970;
158 double t2 = TimeFromYear(y);
159
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 }
173
174 static inline int
175 DaysInFebruary(double year)
176 {
177 return IsLeapYear(year) ? 29 : 28;
178 }
179
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 }
187
188 static double
189 MonthFromTime(double t)
190 {
191 if (!IsFinite(t))
192 return GenericNaN();
193
194 double year = YearFromTime(t);
195 double d = DayWithinYear(t, year);
196
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 }
222
223 /* ES5 15.9.1.5. */
224 static double
225 DateFromTime(double t)
226 {
227 if (!IsFinite(t))
228 return GenericNaN();
229
230 double year = YearFromTime(t);
231 double d = DayWithinYear(t, year);
232
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 }
269
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 }
284
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 };
296
297 JS_ASSERT(0 <= month && month <= 12);
298 return firstDayOfMonth[isLeapYear][month];
299 }
300
301 template<typename T>
302 static inline int
303 DayFromMonth(T month, bool isLeapYear) MOZ_DELETE;
304
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();
312
313 /* Steps 2-4. */
314 double y = ToInteger(year);
315 double m = ToInteger(month);
316 double dt = ToInteger(date);
317
318 /* Step 5. */
319 double ym = y + floor(m / 12);
320
321 /* Step 6. */
322 int mn = int(fmod(m, 12.0));
323 if (mn < 0)
324 mn += 12;
325
326 /* Steps 7-8. */
327 bool leap = IsLeapYear(ym);
328
329 double yearday = floor(TimeFromYear(ym) / msPerDay);
330 double monthday = DayFromMonth(mn, leap);
331
332 return yearday + monthday + dt - 1;
333 }
334
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();
342
343 /* Step 2. */
344 return day * msPerDay + time;
345 }
346
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 }
352
353 JS_PUBLIC_API(double)
354 JS::YearFromTime(double time)
355 {
356 return ::YearFromTime(time);
357 }
358
359 JS_PUBLIC_API(double)
360 JS::MonthFromTime(double time)
361 {
362 return ::MonthFromTime(time);
363 }
364
365 JS_PUBLIC_API(double)
366 JS::DayFromTime(double time)
367 {
368 return DateFromTime(time);
369 }
370
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 };
394
395 int day = int(DayFromYear(year) + 4) % 7;
396 if (day < 0)
397 day += 7;
398
399 return yearStartingWith[IsLeapYear(year)][day];
400 }
401
402 /* ES5 15.9.1.8. */
403 static double
404 DaylightSavingTA(double t, DateTimeInfo *dtInfo)
405 {
406 if (!IsFinite(t))
407 return GenericNaN();
408
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 }
418
419 int64_t utcMilliseconds = static_cast<int64_t>(t);
420 int64_t offsetMilliseconds = dtInfo->getDSTOffsetMilliseconds(utcMilliseconds);
421 return static_cast<double>(offsetMilliseconds);
422 }
423
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 }
431
432 /* ES5 15.9.1.9. */
433 static double
434 LocalTime(double t, DateTimeInfo *dtInfo)
435 {
436 return t + AdjustTime(t, dtInfo);
437 }
438
439 static double
440 UTC(double t, DateTimeInfo *dtInfo)
441 {
442 return t - AdjustTime(t - dtInfo->localTZA(), dtInfo);
443 }
444
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 }
454
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 }
463
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 }
472
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 }
481
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 }
494
495 /* Step 2. */
496 double h = ToInteger(hour);
497
498 /* Step 3. */
499 double m = ToInteger(min);
500
501 /* Step 4. */
502 double s = ToInteger(sec);
503
504 /* Step 5. */
505 double milli = ToInteger(ms);
506
507 /* Steps 6-7. */
508 return h * msPerHour + m * msPerMinute + s * msPerSecond + milli;
509 }
510
511 /**
512 * end of ECMA 'support' functions
513 */
514
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>());
520
521 return DefaultValue(cx, obj, (hint == JSTYPE_VOID) ? JSTYPE_STRING : hint, vp);
522 }
523
524 /* for use by date_parse */
525
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 };
539
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 };
549
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 */
557
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 }
571
572 if (count == 0) {
573 result = true;
574 }
575
576 return result;
577 }
578
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 }
586
587 /* compute the time in msec (unclipped) from the given args */
588 #define MAXARGS 7
589
590 static bool
591 date_msecFromArgs(JSContext *cx, CallArgs args, double *rval)
592 {
593 unsigned loop;
594 double array[MAXARGS];
595 double msec_time;
596
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 }
616
617 /* adjust 2-digit years into the 20th century */
618 if (array[0] >= 0 && array[0] <= 99)
619 array[0] += 1900;
620
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 }
626
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);
634
635 double msec_time;
636 if (!date_msecFromArgs(cx, args, &msec_time))
637 return false;
638
639 msec_time = TimeClip(msec_time);
640
641 args.rval().setNumber(msec_time);
642 return true;
643 }
644
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 }
665
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 }
688
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;
700
701 if (digits(result, s, i, Min(limit, init+n)))
702 return (*i - init) == n;
703
704 *i = init;
705 return false;
706 }
707
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 }
715
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 */
771
772 static bool
773 date_parseISOString(JSLinearString *str, double *result, DateTimeInfo *dtInfo)
774 {
775 double msec;
776
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;
792
793 #define PEEK(ch) (i < limit && s[i] == ch)
794
795 #define NEED(ch) \
796 JS_BEGIN_MACRO \
797 if (i >= limit || s[i] != ch) { goto syntax; } else { ++i; } \
798 JS_END_MACRO
799
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
804
805 #define DONE_UNLESS(ch) \
806 JS_BEGIN_MACRO \
807 if (i >= limit || s[i] != ch) { goto done; } else { ++i; } \
808 JS_END_MACRO
809
810 #define NEED_NDIGITS(n, field) \
811 JS_BEGIN_MACRO \
812 if (!ndigits(n, &field, s, &i, limit)) { goto syntax; } \
813 JS_END_MACRO
814
815 s = str->chars();
816 limit = str->length();
817
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);
830
831 done_date:
832 DONE_UNLESS('T');
833 NEED_NDIGITS(2, hour);
834 NEED(':');
835 NEED_NDIGITS(2, min);
836
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 }
846
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 }
864
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;
876
877 if (i != limit)
878 goto syntax;
879
880 month -= 1; /* convert month to 0-based */
881
882 msec = date_msecFromDate(dateMul * (double)year, month, day,
883 hour, min, sec,
884 frac * 1000.0);;
885
886 if (isLocalTime) {
887 msec = UTC(msec, dtInfo);
888 } else {
889 msec -= ((tzMul) * ((tzHour * msPerHour)
890 + (tzMin * msPerMinute)));
891 }
892
893 if (msec < -8.64e15 || msec > 8.64e15)
894 goto syntax;
895
896 *result = msec;
897
898 return true;
899
900 syntax:
901 /* syntax error */
902 *result = 0;
903 return false;
904
905 #undef PEEK
906 #undef NEED
907 #undef DONE_UNLESS
908 #undef NEED_NDIGITS
909 }
910
911 static bool
912 date_parseString(JSLinearString *str, double *result, DateTimeInfo *dtInfo)
913 {
914 double msec;
915
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;
932
933 if (date_parseISOString(str, result, dtInfo))
934 return true;
935
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 }
967
968 /* allow TZA before the year, so
969 * 'Wed Nov 05 21:49:11 GMT-0800 1997'
970 * works */
971
972 /* uses of seenplusminus allow : in TZA, so Java
973 * no-timezone style of GMT+4:30 works
974 */
975
976 if ((prevc == '+' || prevc == '-')/* && year>=0 */) {
977 /* make ':' case below change tzoffset */
978 seenplusminus = true;
979
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;
1072
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;
1165
1166 msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
1167
1168 if (tzoffset == -1) { /* no time zone specified, have to use local */
1169 msec = UTC(msec, dtInfo);
1170 } else {
1171 msec += tzoffset * msPerMinute;
1172 }
1173
1174 *result = msec;
1175 return true;
1176
1177 syntax:
1178 /* syntax error */
1179 *result = 0;
1180 return false;
1181 }
1182
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 }
1191
1192 JSString *str = ToString<CanGC>(cx, args[0]);
1193 if (!str)
1194 return false;
1195
1196 JSLinearString *linearStr = str->ensureLinear(cx);
1197 if (!linearStr)
1198 return false;
1199
1200 double result;
1201 if (!date_parseString(linearStr, &result, &cx->runtime()->dateTimeInfo)) {
1202 args.rval().setNaN();
1203 return true;
1204 }
1205
1206 result = TimeClip(result);
1207 args.rval().setNumber(result);
1208 return true;
1209 }
1210
1211 static inline double
1212 NowAsMillis()
1213 {
1214 return (double) (PRMJ_Now() / PRMJ_USEC_PER_MSEC);
1215 }
1216
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 }
1224
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());
1230
1231 setFixedSlot(UTC_TIME_SLOT, DoubleValue(t));
1232 if (vp)
1233 vp->setDouble(t);
1234 }
1235
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 }
1245
1246 /* Remember timezone used to generate the local cache. */
1247 setReservedSlot(TZA_SLOT, DoubleValue(dtInfo->localTZA()));
1248
1249 double utcTime = UTCTime().toNumber();
1250
1251 if (!IsFinite(utcTime)) {
1252 for (size_t ind = COMPONENTS_START_SLOT; ind < RESERVED_SLOTS; ind++)
1253 setReservedSlot(ind, DoubleValue(utcTime));
1254 return;
1255 }
1256
1257 double localTime = LocalTime(utcTime, dtInfo);
1258
1259 setReservedSlot(LOCAL_TIME_SLOT, DoubleValue(localTime));
1260
1261 int year = (int) floor(localTime /(msPerDay * 365.2425)) + 1970;
1262 double yearStartTime = TimeFromYear(year);
1263
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 }
1279
1280 setReservedSlot(LOCAL_YEAR_SLOT, Int32Value(year));
1281
1282 uint64_t yearTime = uint64_t(localTime - yearStartTime);
1283 int yearSeconds = uint32_t(yearTime / 1000);
1284
1285 int day = yearSeconds / int(SecondsPerDay);
1286
1287 int step = -1, next = 30;
1288 int month;
1289
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);
1349
1350 setReservedSlot(LOCAL_MONTH_SLOT, Int32Value(month));
1351 setReservedSlot(LOCAL_DATE_SLOT, Int32Value(day - step));
1352
1353 int weekday = WeekDay(localTime);
1354 setReservedSlot(LOCAL_DAY_SLOT, Int32Value(weekday));
1355
1356 int seconds = yearSeconds % 60;
1357 setReservedSlot(LOCAL_SECONDS_SLOT, Int32Value(seconds));
1358
1359 int minutes = (yearSeconds / 60) % 60;
1360 setReservedSlot(LOCAL_MINUTES_SLOT, Int32Value(minutes));
1361
1362 int hours = (yearSeconds / (60 * 60)) % 24;
1363 setReservedSlot(LOCAL_HOURS_SLOT, Int32Value(hours));
1364 }
1365
1366 inline double
1367 DateObject::cachedLocalTime(DateTimeInfo *dtInfo)
1368 {
1369 fillLocalTimeSlots(dtInfo);
1370 return getReservedSlot(LOCAL_TIME_SLOT).toDouble();
1371 }
1372
1373 MOZ_ALWAYS_INLINE bool
1374 IsDate(HandleValue v)
1375 {
1376 return v.isObject() && v.toObject().is<DateObject>();
1377 }
1378
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 }
1388
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 }
1395
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);
1401
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 }
1410
1411 return true;
1412 }
1413
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 }
1420
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);
1426
1427 args.rval().set(dateObj->getReservedSlot(LOCAL_YEAR_SLOT));
1428 return true;
1429 }
1430
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 }
1437
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);
1444
1445 args.rval().setNumber(result);
1446 return true;
1447 }
1448
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 }
1455
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);
1461
1462 args.rval().set(dateObj->getReservedSlot(LOCAL_MONTH_SLOT));
1463 return true;
1464 }
1465
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 }
1472
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 }
1480
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 }
1487
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);
1493
1494 args.rval().set(dateObj->getReservedSlot(LOCAL_DATE_SLOT));
1495 return true;
1496 }
1497
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 }
1504
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);
1511
1512 args.rval().setNumber(result);
1513 return true;
1514 }
1515
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 }
1522
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);
1528
1529 args.rval().set(dateObj->getReservedSlot(LOCAL_DAY_SLOT));
1530 return true;
1531 }
1532
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 }
1539
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);
1546
1547 args.rval().setNumber(result);
1548 return true;
1549 }
1550
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 }
1557
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);
1563
1564 args.rval().set(dateObj->getReservedSlot(LOCAL_HOURS_SLOT));
1565 return true;
1566 }
1567
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 }
1574
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);
1581
1582 args.rval().setNumber(result);
1583 return true;
1584 }
1585
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 }
1592
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);
1598
1599 args.rval().set(dateObj->getReservedSlot(LOCAL_MINUTES_SLOT));
1600 return true;
1601 }
1602
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 }
1609
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);
1616
1617 args.rval().setNumber(result);
1618 return true;
1619 }
1620
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 }
1627
1628 /* Date.getSeconds is mapped to getUTCSeconds */
1629
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);
1635
1636 args.rval().set(dateObj->getReservedSlot(LOCAL_SECONDS_SLOT));
1637 return true;
1638 }
1639
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 }
1646
1647 /* Date.getMilliseconds is mapped to getUTCMilliseconds */
1648
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);
1655
1656 args.rval().setNumber(result);
1657 return true;
1658 }
1659
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 }
1666
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);
1673
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 }
1683
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 }
1690
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 }
1699
1700 double result;
1701 if (!ToNumber(cx, args[0], &result))
1702 return false;
1703
1704 dateObj->setUTCTime(TimeClip(result), args.rval().address());
1705 return true;
1706 }
1707
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 }
1714
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 }
1724
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 }
1734
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 }
1744
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>());
1750
1751 /* Step 1. */
1752 double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo);
1753
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);
1759
1760 /* Step 3. */
1761 double u = TimeClip(UTC(MakeDate(Day(t), time), &cx->runtime()->dateTimeInfo));
1762
1763 /* Steps 4-5. */
1764 dateObj->setUTCTime(u, args.rval().address());
1765 return true;
1766 }
1767
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 }
1774
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>());
1780
1781 /* Step 1. */
1782 double t = dateObj->UTCTime().toNumber();
1783
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);
1789
1790 /* Step 3. */
1791 double v = TimeClip(MakeDate(Day(t), time));
1792
1793 /* Steps 4-5. */
1794 dateObj->setUTCTime(v, args.rval().address());
1795 return true;
1796 }
1797
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 }
1804
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>());
1810
1811 /* Step 1. */
1812 double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo);
1813
1814 /* Step 2. */
1815 double s;
1816 if (!ToNumber(cx, args.get(0), &s))
1817 return false;
1818
1819 /* Step 3. */
1820 double milli;
1821 if (!GetMsecsOrDefault(cx, args, 1, t, &milli))
1822 return false;
1823
1824 /* Step 4. */
1825 double date = MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli));
1826
1827 /* Step 5. */
1828 double u = TimeClip(UTC(date, &cx->runtime()->dateTimeInfo));
1829
1830 /* Steps 6-7. */
1831 dateObj->setUTCTime(u, args.rval().address());
1832 return true;
1833 }
1834
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 }
1842
1843 MOZ_ALWAYS_INLINE bool
1844 date_setUTCSeconds_impl(JSContext *cx, CallArgs args)
1845 {
1846 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1847
1848 /* Step 1. */
1849 double t = dateObj->UTCTime().toNumber();
1850
1851 /* Step 2. */
1852 double s;
1853 if (!ToNumber(cx, args.get(0), &s))
1854 return false;
1855
1856 /* Step 3. */
1857 double milli;
1858 if (!GetMsecsOrDefault(cx, args, 1, t, &milli))
1859 return false;
1860
1861 /* Step 4. */
1862 double date = MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli));
1863
1864 /* Step 5. */
1865 double v = TimeClip(date);
1866
1867 /* Steps 6-7. */
1868 dateObj->setUTCTime(v, args.rval().address());
1869 return true;
1870 }
1871
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 }
1879
1880 MOZ_ALWAYS_INLINE bool
1881 date_setMinutes_impl(JSContext *cx, CallArgs args)
1882 {
1883 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1884
1885 /* Step 1. */
1886 double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo);
1887
1888 /* Step 2. */
1889 double m;
1890 if (!ToNumber(cx, args.get(0), &m))
1891 return false;
1892
1893 /* Step 3. */
1894 double s;
1895 if (!GetSecsOrDefault(cx, args, 1, t, &s))
1896 return false;
1897
1898 /* Step 4. */
1899 double milli;
1900 if (!GetMsecsOrDefault(cx, args, 2, t, &milli))
1901 return false;
1902
1903 /* Step 5. */
1904 double date = MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli));
1905
1906 /* Step 6. */
1907 double u = TimeClip(UTC(date, &cx->runtime()->dateTimeInfo));
1908
1909 /* Steps 7-8. */
1910 dateObj->setUTCTime(u, args.rval().address());
1911 return true;
1912 }
1913
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 }
1921
1922 MOZ_ALWAYS_INLINE bool
1923 date_setUTCMinutes_impl(JSContext *cx, CallArgs args)
1924 {
1925 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1926
1927 /* Step 1. */
1928 double t = dateObj->UTCTime().toNumber();
1929
1930 /* Step 2. */
1931 double m;
1932 if (!ToNumber(cx, args.get(0), &m))
1933 return false;
1934
1935 /* Step 3. */
1936 double s;
1937 if (!GetSecsOrDefault(cx, args, 1, t, &s))
1938 return false;
1939
1940 /* Step 4. */
1941 double milli;
1942 if (!GetMsecsOrDefault(cx, args, 2, t, &milli))
1943 return false;
1944
1945 /* Step 5. */
1946 double date = MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli));
1947
1948 /* Step 6. */
1949 double v = TimeClip(date);
1950
1951 /* Steps 7-8. */
1952 dateObj->setUTCTime(v, args.rval().address());
1953 return true;
1954 }
1955
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 }
1963
1964 MOZ_ALWAYS_INLINE bool
1965 date_setHours_impl(JSContext *cx, CallArgs args)
1966 {
1967 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1968
1969 /* Step 1. */
1970 double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo);
1971
1972 /* Step 2. */
1973 double h;
1974 if (!ToNumber(cx, args.get(0), &h))
1975 return false;
1976
1977 /* Step 3. */
1978 double m;
1979 if (!GetMinsOrDefault(cx, args, 1, t, &m))
1980 return false;
1981
1982 /* Step 4. */
1983 double s;
1984 if (!GetSecsOrDefault(cx, args, 2, t, &s))
1985 return false;
1986
1987 /* Step 5. */
1988 double milli;
1989 if (!GetMsecsOrDefault(cx, args, 3, t, &milli))
1990 return false;
1991
1992 /* Step 6. */
1993 double date = MakeDate(Day(t), MakeTime(h, m, s, milli));
1994
1995 /* Step 6. */
1996 double u = TimeClip(UTC(date, &cx->runtime()->dateTimeInfo));
1997
1998 /* Steps 7-8. */
1999 dateObj->setUTCTime(u, args.rval().address());
2000 return true;
2001 }
2002
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 }
2010
2011 MOZ_ALWAYS_INLINE bool
2012 date_setUTCHours_impl(JSContext *cx, CallArgs args)
2013 {
2014 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2015
2016 /* Step 1. */
2017 double t = dateObj->UTCTime().toNumber();
2018
2019 /* Step 2. */
2020 double h;
2021 if (!ToNumber(cx, args.get(0), &h))
2022 return false;
2023
2024 /* Step 3. */
2025 double m;
2026 if (!GetMinsOrDefault(cx, args, 1, t, &m))
2027 return false;
2028
2029 /* Step 4. */
2030 double s;
2031 if (!GetSecsOrDefault(cx, args, 2, t, &s))
2032 return false;
2033
2034 /* Step 5. */
2035 double milli;
2036 if (!GetMsecsOrDefault(cx, args, 3, t, &milli))
2037 return false;
2038
2039 /* Step 6. */
2040 double newDate = MakeDate(Day(t), MakeTime(h, m, s, milli));
2041
2042 /* Step 7. */
2043 double v = TimeClip(newDate);
2044
2045 /* Steps 8-9. */
2046 dateObj->setUTCTime(v, args.rval().address());
2047 return true;
2048 }
2049
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 }
2057
2058 MOZ_ALWAYS_INLINE bool
2059 date_setDate_impl(JSContext *cx, CallArgs args)
2060 {
2061 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2062
2063 /* Step 1. */
2064 double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo);
2065
2066 /* Step 2. */
2067 double date;
2068 if (!ToNumber(cx, args.get(0), &date))
2069 return false;
2070
2071 /* Step 3. */
2072 double newDate = MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t));
2073
2074 /* Step 4. */
2075 double u = TimeClip(UTC(newDate, &cx->runtime()->dateTimeInfo));
2076
2077 /* Steps 5-6. */
2078 dateObj->setUTCTime(u, args.rval().address());
2079 return true;
2080 }
2081
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 }
2089
2090 MOZ_ALWAYS_INLINE bool
2091 date_setUTCDate_impl(JSContext *cx, CallArgs args)
2092 {
2093 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2094
2095 /* Step 1. */
2096 double t = dateObj->UTCTime().toNumber();
2097
2098 /* Step 2. */
2099 double date;
2100 if (!ToNumber(cx, args.get(0), &date))
2101 return false;
2102
2103 /* Step 3. */
2104 double newDate = MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t));
2105
2106 /* Step 4. */
2107 double v = TimeClip(newDate);
2108
2109 /* Steps 5-6. */
2110 dateObj->setUTCTime(v, args.rval().address());
2111 return true;
2112 }
2113
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 }
2120
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 }
2130
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 }
2140
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>());
2146
2147 /* Step 1. */
2148 double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo);
2149
2150 /* Step 2. */
2151 double m;
2152 if (!ToNumber(cx, args.get(0), &m))
2153 return false;
2154
2155 /* Step 3. */
2156 double date;
2157 if (!GetDateOrDefault(cx, args, 1, t, &date))
2158 return false;
2159
2160 /* Step 4. */
2161 double newDate = MakeDate(MakeDay(YearFromTime(t), m, date), TimeWithinDay(t));
2162
2163 /* Step 5. */
2164 double u = TimeClip(UTC(newDate, &cx->runtime()->dateTimeInfo));
2165
2166 /* Steps 6-7. */
2167 dateObj->setUTCTime(u, args.rval().address());
2168 return true;
2169 }
2170
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 }
2177
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>());
2183
2184 /* Step 1. */
2185 double t = dateObj->UTCTime().toNumber();
2186
2187 /* Step 2. */
2188 double m;
2189 if (!ToNumber(cx, args.get(0), &m))
2190 return false;
2191
2192 /* Step 3. */
2193 double date;
2194 if (!GetDateOrDefault(cx, args, 1, t, &date))
2195 return false;
2196
2197 /* Step 4. */
2198 double newDate = MakeDate(MakeDay(YearFromTime(t), m, date), TimeWithinDay(t));
2199
2200 /* Step 5. */
2201 double v = TimeClip(newDate);
2202
2203 /* Steps 6-7. */
2204 dateObj->setUTCTime(v, args.rval().address());
2205 return true;
2206 }
2207
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 }
2214
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 }
2223
2224 static double
2225 ThisUTCTimeOrZero(Handle<DateObject*> dateObj)
2226 {
2227 double t = dateObj->as<DateObject>().UTCTime().toNumber();
2228 return IsNaN(t) ? +0 : t;
2229 }
2230
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>());
2236
2237 /* Step 1. */
2238 double t = ThisLocalTimeOrZero(dateObj, &cx->runtime()->dateTimeInfo);
2239
2240 /* Step 2. */
2241 double y;
2242 if (!ToNumber(cx, args.get(0), &y))
2243 return false;
2244
2245 /* Step 3. */
2246 double m;
2247 if (!GetMonthOrDefault(cx, args, 1, t, &m))
2248 return false;
2249
2250 /* Step 4. */
2251 double date;
2252 if (!GetDateOrDefault(cx, args, 2, t, &date))
2253 return false;
2254
2255 /* Step 5. */
2256 double newDate = MakeDate(MakeDay(y, m, date), TimeWithinDay(t));
2257
2258 /* Step 6. */
2259 double u = TimeClip(UTC(newDate, &cx->runtime()->dateTimeInfo));
2260
2261 /* Steps 7-8. */
2262 dateObj->setUTCTime(u, args.rval().address());
2263 return true;
2264 }
2265
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 }
2272
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>());
2278
2279 /* Step 1. */
2280 double t = ThisUTCTimeOrZero(dateObj);
2281
2282 /* Step 2. */
2283 double y;
2284 if (!ToNumber(cx, args.get(0), &y))
2285 return false;
2286
2287 /* Step 3. */
2288 double m;
2289 if (!GetMonthOrDefault(cx, args, 1, t, &m))
2290 return false;
2291
2292 /* Step 4. */
2293 double date;
2294 if (!GetDateOrDefault(cx, args, 2, t, &date))
2295 return false;
2296
2297 /* Step 5. */
2298 double newDate = MakeDate(MakeDay(y, m, date), TimeWithinDay(t));
2299
2300 /* Step 6. */
2301 double v = TimeClip(newDate);
2302
2303 /* Steps 7-8. */
2304 dateObj->setUTCTime(v, args.rval().address());
2305 return true;
2306 }
2307
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 }
2314
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>());
2320
2321 /* Step 1. */
2322 double t = ThisLocalTimeOrZero(dateObj, &cx->runtime()->dateTimeInfo);
2323
2324 /* Step 2. */
2325 double y;
2326 if (!ToNumber(cx, args.get(0), &y))
2327 return false;
2328
2329 /* Step 3. */
2330 if (IsNaN(y)) {
2331 dateObj->setUTCTime(GenericNaN(), args.rval().address());
2332 return true;
2333 }
2334
2335 /* Step 4. */
2336 double yint = ToInteger(y);
2337 if (0 <= yint && yint <= 99)
2338 yint += 1900;
2339
2340 /* Step 5. */
2341 double day = MakeDay(yint, MonthFromTime(t), DateFromTime(t));
2342
2343 /* Step 6. */
2344 double u = UTC(MakeDate(day, TimeWithinDay(t)), &cx->runtime()->dateTimeInfo);
2345
2346 /* Steps 7-8. */
2347 dateObj->setUTCTime(TimeClip(u), args.rval().address());
2348 return true;
2349 }
2350
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 }
2357
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 };
2368
2369
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 }
2385
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 }
2399
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();
2405
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);
2411
2412 JSString *str = JS_NewStringCopyZ(cx, buf);
2413 if (!str)
2414 return false;
2415 args.rval().setString(str);
2416 return true;
2417 }
2418
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 }
2426
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 }
2435
2436 char buf[100];
2437 print_iso_string(buf, sizeof buf, utctime);
2438
2439 JSString *str = JS_NewStringCopyZ(cx, buf);
2440 if (!str)
2441 return false;
2442 args.rval().setString(str);
2443 return true;
2444
2445 }
2446
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 }
2453
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);
2459
2460 /* Step 1. */
2461 RootedObject obj(cx, ToObject(cx, args.thisv()));
2462 if (!obj)
2463 return false;
2464
2465 /* Step 2. */
2466 RootedValue tv(cx, ObjectValue(*obj));
2467 if (!ToPrimitive(cx, JSTYPE_NUMBER, &tv))
2468 return false;
2469
2470 /* Step 3. */
2471 if (tv.isDouble() && !IsFinite(tv.toDouble())) {
2472 args.rval().setNull();
2473 return true;
2474 }
2475
2476 /* Step 4. */
2477 RootedValue toISO(cx);
2478 if (!JSObject::getProperty(cx, obj, obj, cx->names().toISOString, &toISO))
2479 return false;
2480
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 }
2487
2488 /* Step 6. */
2489 InvokeArgs args2(cx);
2490 if (!args2.init(0))
2491 return false;
2492
2493 args2.setCallee(toISO);
2494 args2.setThis(ObjectValue(*obj));
2495
2496 if (!Invoke(cx, args2))
2497 return false;
2498 args.rval().set(args2.rval());
2499 return true;
2500 }
2501
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);
2508
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));
2518
2519 /* not sure how this affects things, but it doesn't seem
2520 to matter. */
2521 split->tm_isdst = (DaylightSavingTA(timeval, dtInfo) != 0);
2522 }
2523
2524 typedef enum formatspec {
2525 FORMATSPEC_FULL, FORMATSPEC_DATE, FORMATSPEC_TIME
2526 } formatspec;
2527
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;
2537
2538 if (!IsFinite(date)) {
2539 JS_snprintf(buf, sizeof buf, js_NaN_date_str);
2540 } else {
2541 JS_ASSERT(TimeClip(date) == date);
2542
2543 double local = LocalTime(date, &cx->runtime()->dateTimeInfo);
2544
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);
2548
2549 /* map 510 minutes to 0830 hours */
2550 int offset = (minutes / 60) * 100 + minutes % 60;
2551
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 */
2560
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) {
2565
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 }
2586
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;
2592
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 }
2635
2636 JSString *str = JS_NewStringCopyZ(cx, buf);
2637 if (!str)
2638 return false;
2639 rval.setString(str);
2640 return true;
2641 }
2642
2643 static bool
2644 ToLocaleFormatHelper(JSContext *cx, HandleObject obj, const char *format, MutableHandleValue rval)
2645 {
2646 double utctime = obj->as<DateObject>().UTCTime().toNumber();
2647
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);
2656
2657 /* Let PRMJTime format it. */
2658 result_len = PRMJ_FormatTime(buf, sizeof buf, format, &split);
2659
2660 /* If it failed, default to toString. */
2661 if (result_len == 0)
2662 return date_format(cx, utctime, FORMATSPEC_FULL, rval);
2663
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 }
2676
2677 }
2678
2679 if (cx->runtime()->localeCallbacks && cx->runtime()->localeCallbacks->localeToUnicode)
2680 return cx->runtime()->localeCallbacks->localeToUnicode(cx, buf, rval);
2681
2682 JSString *str = JS_NewStringCopyZ(cx, buf);
2683 if (!str)
2684 return false;
2685 rval.setString(str);
2686 return true;
2687 }
2688
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 }
2705
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 }
2713
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 }
2720
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 ;
2736
2737 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2738 return ToLocaleFormatHelper(cx, dateObj, format, args.rval());
2739 }
2740
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 }
2747
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 }
2755
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 */
2763
2764 MOZ_ALWAYS_INLINE bool
2765 date_toLocaleFormat_impl(JSContext *cx, CallArgs args)
2766 {
2767 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2768
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 }
2782
2783 RootedString fmt(cx, ToString<CanGC>(cx, args[0]));
2784 if (!fmt)
2785 return false;
2786
2787 JSAutoByteString fmtbytes(cx, fmt);
2788 if (!fmtbytes)
2789 return false;
2790
2791 return ToLocaleFormatHelper(cx, dateObj, fmtbytes.ptr(), args.rval());
2792 }
2793
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 }
2800
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 }
2808
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 }
2815
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 }
2823
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 }
2830
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 }
2842
2843 JSString *str = sb.finishString();
2844 if (!str)
2845 return false;
2846 args.rval().setString(str);
2847 return true;
2848 }
2849
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
2857
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 }
2864
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 }
2871
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 }
2879
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 }
2886
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 };
2893
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 };
2952
2953 bool
2954 js_Date(JSContext *cx, unsigned argc, Value *vp)
2955 {
2956 CallArgs args = CallArgsFromVp(argc, vp);
2957
2958 /* Date called as function. */
2959 if (!args.isConstructing())
2960 return date_format(cx, NowAsMillis(), FORMATSPEC_FULL, args.rval());
2961
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. */
2969
2970 /* Step 1. */
2971 if (!ToPrimitive(cx, args[0]))
2972 return false;
2973
2974 if (args[0].isString()) {
2975 /* Step 2. */
2976 JSString *str = args[0].toString();
2977 if (!str)
2978 return false;
2979
2980 JSLinearString *linearStr = str->ensureLinear(cx);
2981 if (!linearStr)
2982 return false;
2983
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;
2998
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 }
3005
3006 JSObject *obj = js_NewDateObjectMsec(cx, d);
3007 if (!obj)
3008 return false;
3009
3010 args.rval().setObject(*obj);
3011 return true;
3012 }
3013
3014 static bool
3015 FinishDateClassInit(JSContext *cx, HandleObject ctor, HandleObject proto)
3016 {
3017 proto->as<DateObject>().setUTCTime(GenericNaN());
3018
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 }
3030
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 };
3055
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 }
3065
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 }
3074
3075 JS_FRIEND_API(bool)
3076 js_DateIsValid(JSObject *obj)
3077 {
3078 return obj->is<DateObject>() && !IsNaN(obj->as<DateObject>().UTCTime().toNumber());
3079 }
3080
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;
3089
3090 return (int) YearFromTime(localtime);
3091 }
3092
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;
3100
3101 return (int) MonthFromTime(localtime);
3102 }
3103
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;
3111
3112 return (int) DateFromTime(localtime);
3113 }
3114
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;
3122
3123 return (int) HourFromTime(localtime);
3124 }
3125
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;
3133
3134 return (int) MinFromTime(localtime);
3135 }
3136
3137 JS_FRIEND_API(int)
3138 js_DateGetSeconds(JSObject *obj)
3139 {
3140 if (!obj->is<DateObject>())
3141 return 0;
3142
3143 double utctime = obj->as<DateObject>().UTCTime().toNumber();
3144 if (IsNaN(utctime))
3145 return 0;
3146 return (int) SecFromTime(utctime);
3147 }
3148
3149 JS_FRIEND_API(double)
3150 js_DateGetMsecSinceEpoch(JSObject *obj)
3151 {
3152 return obj->is<DateObject>() ? obj->as<DateObject>().UTCTime().toNumber() : 0;
3153 }
3154
3155
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 };

mercurial