Bug 469739 - Add support for displaying Vista UAC shield icon; r=joe sr=vladimir
[wine-gecko.git] / js / src / jsdate.cpp
bloba99724b0fe733d7d38be9ee4a397c75de67fc263
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=78:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
17 * The Original Code is Mozilla Communicator client code, released
18 * March 31, 1998.
20 * The Initial Developer of the Original Code is
21 * Netscape Communications Corporation.
22 * Portions created by the Initial Developer are Copyright (C) 1998
23 * the Initial Developer. All Rights Reserved.
25 * Contributor(s):
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
42 * JS date methods.
46 * "For example, OS/360 devotes 26 bytes of the permanently
47 * resident date-turnover routine to the proper handling of
48 * December 31 on leap years (when it is Day 366). That
49 * might have been left to the operator."
51 * Frederick Brooks, 'The Second-System Effect'.
54 #include "jsstddef.h"
55 #include <ctype.h>
56 #include <locale.h>
57 #include <math.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include "jstypes.h"
61 #include "jsprf.h"
62 #include "prmjtime.h"
63 #include "jsutil.h" /* Added by JSIFY */
64 #include "jsapi.h"
65 #include "jsversion.h"
66 #include "jsbuiltins.h"
67 #include "jscntxt.h"
68 #include "jsdate.h"
69 #include "jsinterp.h"
70 #include "jsnum.h"
71 #include "jsobj.h"
72 #include "jsstr.h"
75 * The JS 'Date' object is patterned after the Java 'Date' object.
76 * Here is an script:
78 * today = new Date();
80 * print(today.toLocaleString());
82 * weekDay = today.getDay();
85 * These Java (and ECMA-262) methods are supported:
87 * UTC
88 * getDate (getUTCDate)
89 * getDay (getUTCDay)
90 * getHours (getUTCHours)
91 * getMinutes (getUTCMinutes)
92 * getMonth (getUTCMonth)
93 * getSeconds (getUTCSeconds)
94 * getMilliseconds (getUTCMilliseconds)
95 * getTime
96 * getTimezoneOffset
97 * getYear
98 * getFullYear (getUTCFullYear)
99 * parse
100 * setDate (setUTCDate)
101 * setHours (setUTCHours)
102 * setMinutes (setUTCMinutes)
103 * setMonth (setUTCMonth)
104 * setSeconds (setUTCSeconds)
105 * setMilliseconds (setUTCMilliseconds)
106 * setTime
107 * setYear (setFullYear, setUTCFullYear)
108 * toGMTString (toUTCString)
109 * toLocaleString
110 * toString
113 * These Java methods are not supported
115 * setDay
116 * before
117 * after
118 * equals
119 * hashCode
123 * 11/97 - jsdate.c has been rewritten to conform to the ECMA-262 language
124 * definition and reduce dependence on NSPR. NSPR is used to get the current
125 * time in milliseconds, the time zone offset, and the daylight savings time
126 * offset for a given time. NSPR is also used for Date.toLocaleString(), for
127 * locale-specific formatting, and to get a string representing the timezone.
128 * (Which turns out to be platform-dependent.)
130 * To do:
131 * (I did some performance tests by timing how long it took to run what
132 * I had of the js ECMA conformance tests.)
134 * - look at saving results across multiple calls to supporting
135 * functions; the toString functions compute some of the same values
136 * multiple times. Although - I took a quick stab at this, and I lost
137 * rather than gained. (Fractionally.) Hard to tell what compilers/processors
138 * are doing these days.
140 * - look at tweaking function return types to return double instead
141 * of int; this seems to make things run slightly faster sometimes.
142 * (though it could be architecture-dependent.) It'd be good to see
143 * how this does on win32. (Tried it on irix.) Types could use a
144 * general going-over.
148 * Supporting functions - ECMA 15.9.1.*
151 #define HalfTimeDomain 8.64e15
152 #define HoursPerDay 24.0
153 #define MinutesPerDay (HoursPerDay * MinutesPerHour)
154 #define MinutesPerHour 60.0
155 #define SecondsPerDay (MinutesPerDay * SecondsPerMinute)
156 #define SecondsPerHour (MinutesPerHour * SecondsPerMinute)
157 #define SecondsPerMinute 60.0
159 #if defined(XP_WIN) || defined(XP_OS2)
160 /* Work around msvc double optimization bug by making these runtime values; if
161 * they're available at compile time, msvc optimizes division by them by
162 * computing the reciprocal and multiplying instead of dividing - this loses
163 * when the reciprocal isn't representable in a double.
165 static jsdouble msPerSecond = 1000.0;
166 static jsdouble msPerDay = SecondsPerDay * 1000.0;
167 static jsdouble msPerHour = SecondsPerHour * 1000.0;
168 static jsdouble msPerMinute = SecondsPerMinute * 1000.0;
169 #else
170 #define msPerDay (SecondsPerDay * msPerSecond)
171 #define msPerHour (SecondsPerHour * msPerSecond)
172 #define msPerMinute (SecondsPerMinute * msPerSecond)
173 #define msPerSecond 1000.0
174 #endif
176 #define Day(t) floor((t) / msPerDay)
178 static jsdouble
179 TimeWithinDay(jsdouble t)
181 jsdouble result;
182 result = fmod(t, msPerDay);
183 if (result < 0)
184 result += msPerDay;
185 return result;
188 #define DaysInYear(y) ((y) % 4 == 0 && ((y) % 100 || ((y) % 400 == 0)) \
189 ? 366 : 365)
191 /* math here has to be f.p, because we need
192 * floor((1968 - 1969) / 4) == -1
194 #define DayFromYear(y) (365 * ((y)-1970) + floor(((y)-1969)/4.0) \
195 - floor(((y)-1901)/100.0) + floor(((y)-1601)/400.0))
196 #define TimeFromYear(y) (DayFromYear(y) * msPerDay)
198 static jsint
199 YearFromTime(jsdouble t)
201 jsint y = (jsint) floor(t /(msPerDay*365.2425)) + 1970;
202 jsdouble t2 = (jsdouble) TimeFromYear(y);
204 if (t2 > t) {
205 y--;
206 } else {
207 if (t2 + msPerDay * DaysInYear(y) <= t)
208 y++;
210 return y;
213 #define InLeapYear(t) (JSBool) (DaysInYear(YearFromTime(t)) == 366)
215 #define DayWithinYear(t, year) ((intN) (Day(t) - DayFromYear(year)))
218 * The following array contains the day of year for the first day of
219 * each month, where index 0 is January, and day 0 is January 1.
221 static jsdouble firstDayOfMonth[2][12] = {
222 {0.0, 31.0, 59.0, 90.0, 120.0, 151.0, 181.0, 212.0, 243.0, 273.0, 304.0, 334.0},
223 {0.0, 31.0, 60.0, 91.0, 121.0, 152.0, 182.0, 213.0, 244.0, 274.0, 305.0, 335.0}
226 #define DayFromMonth(m, leap) firstDayOfMonth[leap][(intN)m];
228 static intN
229 MonthFromTime(jsdouble t)
231 intN d, step;
232 jsint year = YearFromTime(t);
233 d = DayWithinYear(t, year);
235 if (d < (step = 31))
236 return 0;
237 step += (InLeapYear(t) ? 29 : 28);
238 if (d < step)
239 return 1;
240 if (d < (step += 31))
241 return 2;
242 if (d < (step += 30))
243 return 3;
244 if (d < (step += 31))
245 return 4;
246 if (d < (step += 30))
247 return 5;
248 if (d < (step += 31))
249 return 6;
250 if (d < (step += 31))
251 return 7;
252 if (d < (step += 30))
253 return 8;
254 if (d < (step += 31))
255 return 9;
256 if (d < (step += 30))
257 return 10;
258 return 11;
261 static intN
262 DateFromTime(jsdouble t)
264 intN d, step, next;
265 jsint year = YearFromTime(t);
266 d = DayWithinYear(t, year);
268 if (d <= (next = 30))
269 return d + 1;
270 step = next;
271 next += (InLeapYear(t) ? 29 : 28);
272 if (d <= next)
273 return d - step;
274 step = next;
275 if (d <= (next += 31))
276 return d - step;
277 step = next;
278 if (d <= (next += 30))
279 return d - step;
280 step = next;
281 if (d <= (next += 31))
282 return d - step;
283 step = next;
284 if (d <= (next += 30))
285 return d - step;
286 step = next;
287 if (d <= (next += 31))
288 return d - step;
289 step = next;
290 if (d <= (next += 31))
291 return d - step;
292 step = next;
293 if (d <= (next += 30))
294 return d - step;
295 step = next;
296 if (d <= (next += 31))
297 return d - step;
298 step = next;
299 if (d <= (next += 30))
300 return d - step;
301 step = next;
302 return d - step;
305 static intN
306 WeekDay(jsdouble t)
308 jsint result;
309 result = (jsint) Day(t) + 4;
310 result = result % 7;
311 if (result < 0)
312 result += 7;
313 return (intN) result;
316 #define MakeTime(hour, min, sec, ms) \
317 ((((hour) * MinutesPerHour + (min)) * SecondsPerMinute + (sec)) * msPerSecond + (ms))
319 static jsdouble
320 MakeDay(jsdouble year, jsdouble month, jsdouble date)
322 JSBool leap;
323 jsdouble yearday;
324 jsdouble monthday;
326 year += floor(month / 12);
328 month = fmod(month, 12.0);
329 if (month < 0)
330 month += 12;
332 leap = (DaysInYear((jsint) year) == 366);
334 yearday = floor(TimeFromYear(year) / msPerDay);
335 monthday = DayFromMonth(month, leap);
337 return yearday + monthday + date - 1;
340 #define MakeDate(day, time) ((day) * msPerDay + (time))
343 * Years and leap years on which Jan 1 is a Sunday, Monday, etc.
345 * yearStartingWith[0][i] is an example non-leap year where
346 * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
348 * yearStartingWith[1][i] is an example leap year where
349 * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
351 static jsint yearStartingWith[2][7] = {
352 {1978, 1973, 1974, 1975, 1981, 1971, 1977},
353 {1984, 1996, 1980, 1992, 1976, 1988, 1972}
357 * Find a year for which any given date will fall on the same weekday.
359 * This function should be used with caution when used other than
360 * for determining DST; it hasn't been proven not to produce an
361 * incorrect year for times near year boundaries.
363 static jsint
364 EquivalentYearForDST(jsint year)
366 jsint day;
367 JSBool isLeapYear;
369 day = (jsint) DayFromYear(year) + 4;
370 day = day % 7;
371 if (day < 0)
372 day += 7;
374 isLeapYear = (DaysInYear(year) == 366);
376 return yearStartingWith[isLeapYear][day];
379 /* LocalTZA gets set by js_InitDateClass() */
380 static jsdouble LocalTZA;
382 static jsdouble
383 DaylightSavingTA(jsdouble t)
385 volatile int64 PR_t;
386 int64 ms2us;
387 int64 offset;
388 jsdouble result;
390 /* abort if NaN */
391 if (JSDOUBLE_IS_NaN(t))
392 return t;
395 * If earlier than 1970 or after 2038, potentially beyond the ken of
396 * many OSes, map it to an equivalent year before asking.
398 if (t < 0.0 || t > 2145916800000.0) {
399 jsint year;
400 jsdouble day;
402 year = EquivalentYearForDST(YearFromTime(t));
403 day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
404 t = MakeDate(day, TimeWithinDay(t));
407 /* put our t in an LL, and map it to usec for prtime */
408 JSLL_D2L(PR_t, t);
409 JSLL_I2L(ms2us, PRMJ_USEC_PER_MSEC);
410 JSLL_MUL(PR_t, PR_t, ms2us);
412 offset = PRMJ_DSTOffset(PR_t);
414 JSLL_DIV(offset, offset, ms2us);
415 JSLL_L2D(result, offset);
416 return result;
420 #define AdjustTime(t) fmod(LocalTZA + DaylightSavingTA(t), msPerDay)
422 #define LocalTime(t) ((t) + AdjustTime(t))
424 static jsdouble
425 UTC(jsdouble t)
427 return t - AdjustTime(t - LocalTZA);
430 static intN
431 HourFromTime(jsdouble t)
433 intN result = (intN) fmod(floor(t/msPerHour), HoursPerDay);
434 if (result < 0)
435 result += (intN)HoursPerDay;
436 return result;
439 static intN
440 MinFromTime(jsdouble t)
442 intN result = (intN) fmod(floor(t / msPerMinute), MinutesPerHour);
443 if (result < 0)
444 result += (intN)MinutesPerHour;
445 return result;
448 static intN
449 SecFromTime(jsdouble t)
451 intN result = (intN) fmod(floor(t / msPerSecond), SecondsPerMinute);
452 if (result < 0)
453 result += (intN)SecondsPerMinute;
454 return result;
457 static intN
458 msFromTime(jsdouble t)
460 intN result = (intN) fmod(t, msPerSecond);
461 if (result < 0)
462 result += (intN)msPerSecond;
463 return result;
466 #define TIMECLIP(d) ((JSDOUBLE_IS_FINITE(d) \
467 && !((d < 0 ? -d : d) > HalfTimeDomain)) \
468 ? js_DoubleToInteger(d + (+0.)) : *cx->runtime->jsNaN)
471 * end of ECMA 'support' functions
475 * Other Support routines and definitions
479 * We use the first reseved slot to store UTC time, and the second for caching
480 * the local time. The initial value of the cache entry is NaN.
482 const uint32 JSSLOT_UTC_TIME = JSSLOT_PRIVATE;
483 const uint32 JSSLOT_LOCAL_TIME = JSSLOT_PRIVATE + 1;
485 const uint32 DATE_RESERVED_SLOTS = 2;
487 JSClass js_DateClass = {
488 js_Date_str,
489 JSCLASS_HAS_RESERVED_SLOTS(DATE_RESERVED_SLOTS) |
490 JSCLASS_HAS_CACHED_PROTO(JSProto_Date),
491 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
492 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
493 JSCLASS_NO_OPTIONAL_MEMBERS
496 /* for use by date_parse */
498 static const char* wtb[] = {
499 "am", "pm",
500 "monday", "tuesday", "wednesday", "thursday", "friday",
501 "saturday", "sunday",
502 "january", "february", "march", "april", "may", "june",
503 "july", "august", "september", "october", "november", "december",
504 "gmt", "ut", "utc",
505 "est", "edt",
506 "cst", "cdt",
507 "mst", "mdt",
508 "pst", "pdt"
509 /* time zone table needs to be expanded */
512 static int ttb[] = {
513 -1, -2, 0, 0, 0, 0, 0, 0, 0, /* AM/PM */
514 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
515 10000 + 0, 10000 + 0, 10000 + 0, /* GMT/UT/UTC */
516 10000 + 5 * 60, 10000 + 4 * 60, /* EST/EDT */
517 10000 + 6 * 60, 10000 + 5 * 60, /* CST/CDT */
518 10000 + 7 * 60, 10000 + 6 * 60, /* MST/MDT */
519 10000 + 8 * 60, 10000 + 7 * 60 /* PST/PDT */
522 /* helper for date_parse */
523 static JSBool
524 date_regionMatches(const char* s1, int s1off, const jschar* s2, int s2off,
525 int count, int ignoreCase)
527 JSBool result = JS_FALSE;
528 /* return true if matches, otherwise, false */
530 while (count > 0 && s1[s1off] && s2[s2off]) {
531 if (ignoreCase) {
532 if (JS_TOLOWER((jschar)s1[s1off]) != JS_TOLOWER(s2[s2off])) {
533 break;
535 } else {
536 if ((jschar)s1[s1off] != s2[s2off]) {
537 break;
540 s1off++;
541 s2off++;
542 count--;
545 if (count == 0) {
546 result = JS_TRUE;
549 return result;
552 /* find UTC time from given date... no 1900 correction! */
553 static jsdouble
554 date_msecFromDate(jsdouble year, jsdouble mon, jsdouble mday, jsdouble hour,
555 jsdouble min, jsdouble sec, jsdouble msec)
557 jsdouble day;
558 jsdouble msec_time;
559 jsdouble result;
561 day = MakeDay(year, mon, mday);
562 msec_time = MakeTime(hour, min, sec, msec);
563 result = MakeDate(day, msec_time);
564 return result;
567 /* compute the time in msec (unclipped) from the given args */
568 #define MAXARGS 7
570 static JSBool
571 date_msecFromArgs(JSContext *cx, uintN argc, jsval *argv, jsdouble *rval)
573 uintN loop;
574 jsdouble array[MAXARGS];
575 jsdouble d;
576 jsdouble msec_time;
578 for (loop = 0; loop < MAXARGS; loop++) {
579 if (loop < argc) {
580 d = js_ValueToNumber(cx, &argv[loop]);
581 if (JSVAL_IS_NULL(argv[loop]))
582 return JS_FALSE;
583 /* return NaN if any arg is not finite */
584 if (!JSDOUBLE_IS_FINITE(d)) {
585 *rval = *cx->runtime->jsNaN;
586 return JS_TRUE;
588 array[loop] = js_DoubleToInteger(d);
589 } else {
590 if (loop == 2) {
591 array[loop] = 1; /* Default the date argument to 1. */
592 } else {
593 array[loop] = 0;
598 /* adjust 2-digit years into the 20th century */
599 if (array[0] >= 0 && array[0] <= 99)
600 array[0] += 1900;
602 msec_time = date_msecFromDate(array[0], array[1], array[2],
603 array[3], array[4], array[5], array[6]);
604 *rval = msec_time;
605 return JS_TRUE;
609 * See ECMA 15.9.4.[3-10];
611 static JSBool
612 date_UTC(JSContext *cx, uintN argc, jsval *vp)
614 jsdouble msec_time;
616 if (!date_msecFromArgs(cx, argc, vp + 2, &msec_time))
617 return JS_FALSE;
619 msec_time = TIMECLIP(msec_time);
621 return js_NewNumberInRootedValue(cx, msec_time, vp);
624 static JSBool
625 date_parseString(JSString *str, jsdouble *result)
627 jsdouble msec;
629 const jschar *s;
630 size_t limit;
631 size_t i = 0;
632 int year = -1;
633 int mon = -1;
634 int mday = -1;
635 int hour = -1;
636 int min = -1;
637 int sec = -1;
638 int c = -1;
639 int n = -1;
640 int tzoffset = -1;
641 int prevc = 0;
642 JSBool seenplusminus = JS_FALSE;
643 int temp;
644 JSBool seenmonthname = JS_FALSE;
646 JSSTRING_CHARS_AND_LENGTH(str, s, limit);
647 if (limit == 0)
648 goto syntax;
649 while (i < limit) {
650 c = s[i];
651 i++;
652 if (c <= ' ' || c == ',' || c == '-') {
653 if (c == '-' && '0' <= s[i] && s[i] <= '9') {
654 prevc = c;
656 continue;
658 if (c == '(') { /* comments) */
659 int depth = 1;
660 while (i < limit) {
661 c = s[i];
662 i++;
663 if (c == '(') depth++;
664 else if (c == ')')
665 if (--depth <= 0)
666 break;
668 continue;
670 if ('0' <= c && c <= '9') {
671 n = c - '0';
672 while (i < limit && '0' <= (c = s[i]) && c <= '9') {
673 n = n * 10 + c - '0';
674 i++;
677 /* allow TZA before the year, so
678 * 'Wed Nov 05 21:49:11 GMT-0800 1997'
679 * works */
681 /* uses of seenplusminus allow : in TZA, so Java
682 * no-timezone style of GMT+4:30 works
685 if ((prevc == '+' || prevc == '-')/* && year>=0 */) {
686 /* make ':' case below change tzoffset */
687 seenplusminus = JS_TRUE;
689 /* offset */
690 if (n < 24)
691 n = n * 60; /* EG. "GMT-3" */
692 else
693 n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */
694 if (prevc == '+') /* plus means east of GMT */
695 n = -n;
696 if (tzoffset != 0 && tzoffset != -1)
697 goto syntax;
698 tzoffset = n;
699 } else if (prevc == '/' && mon >= 0 && mday >= 0 && year < 0) {
700 if (c <= ' ' || c == ',' || c == '/' || i >= limit)
701 year = n;
702 else
703 goto syntax;
704 } else if (c == ':') {
705 if (hour < 0)
706 hour = /*byte*/ n;
707 else if (min < 0)
708 min = /*byte*/ n;
709 else
710 goto syntax;
711 } else if (c == '/') {
712 /* until it is determined that mon is the actual
713 month, keep it as 1-based rather than 0-based */
714 if (mon < 0)
715 mon = /*byte*/ n;
716 else if (mday < 0)
717 mday = /*byte*/ n;
718 else
719 goto syntax;
720 } else if (i < limit && c != ',' && c > ' ' && c != '-' && c != '(') {
721 goto syntax;
722 } else if (seenplusminus && n < 60) { /* handle GMT-3:30 */
723 if (tzoffset < 0)
724 tzoffset -= n;
725 else
726 tzoffset += n;
727 } else if (hour >= 0 && min < 0) {
728 min = /*byte*/ n;
729 } else if (prevc == ':' && min >= 0 && sec < 0) {
730 sec = /*byte*/ n;
731 } else if (mon < 0) {
732 mon = /*byte*/n;
733 } else if (mon >= 0 && mday < 0) {
734 mday = /*byte*/ n;
735 } else if (mon >= 0 && mday >= 0 && year < 0) {
736 year = n;
737 } else {
738 goto syntax;
740 prevc = 0;
741 } else if (c == '/' || c == ':' || c == '+' || c == '-') {
742 prevc = c;
743 } else {
744 size_t st = i - 1;
745 int k;
746 while (i < limit) {
747 c = s[i];
748 if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')))
749 break;
750 i++;
752 if (i <= st + 1)
753 goto syntax;
754 for (k = JS_ARRAY_LENGTH(wtb); --k >= 0;)
755 if (date_regionMatches(wtb[k], 0, s, st, i-st, 1)) {
756 int action = ttb[k];
757 if (action != 0) {
758 if (action < 0) {
760 * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as
761 * 12:30, instead of blindly adding 12 if PM.
763 JS_ASSERT(action == -1 || action == -2);
764 if (hour > 12 || hour < 0) {
765 goto syntax;
766 } else {
767 if (action == -1 && hour == 12) { /* am */
768 hour = 0;
769 } else if (action == -2 && hour != 12) { /* pm */
770 hour += 12;
773 } else if (action <= 13) { /* month! */
774 /* Adjust mon to be 1-based until the final values
775 for mon, mday and year are adjusted below */
776 if (seenmonthname) {
777 goto syntax;
779 seenmonthname = JS_TRUE;
780 temp = /*byte*/ (action - 2) + 1;
782 if (mon < 0) {
783 mon = temp;
784 } else if (mday < 0) {
785 mday = mon;
786 mon = temp;
787 } else if (year < 0) {
788 year = mon;
789 mon = temp;
790 } else {
791 goto syntax;
793 } else {
794 tzoffset = action - 10000;
797 break;
799 if (k < 0)
800 goto syntax;
801 prevc = 0;
804 if (year < 0 || mon < 0 || mday < 0)
805 goto syntax;
807 Case 1. The input string contains an English month name.
808 The form of the string can be month f l, or f month l, or
809 f l month which each evaluate to the same date.
810 If f and l are both greater than or equal to 70, or
811 both less than 70, the date is invalid.
812 The year is taken to be the greater of the values f, l.
813 If the year is greater than or equal to 70 and less than 100,
814 it is considered to be the number of years after 1900.
815 Case 2. The input string is of the form "f/m/l" where f, m and l are
816 integers, e.g. 7/16/45.
817 Adjust the mon, mday and year values to achieve 100% MSIE
818 compatibility.
819 a. If 0 <= f < 70, f/m/l is interpreted as month/day/year.
820 i. If year < 100, it is the number of years after 1900
821 ii. If year >= 100, it is the number of years after 0.
822 b. If 70 <= f < 100
823 i. If m < 70, f/m/l is interpreted as
824 year/month/day where year is the number of years after
825 1900.
826 ii. If m >= 70, the date is invalid.
827 c. If f >= 100
828 i. If m < 70, f/m/l is interpreted as
829 year/month/day where year is the number of years after 0.
830 ii. If m >= 70, the date is invalid.
832 if (seenmonthname) {
833 if ((mday >= 70 && year >= 70) || (mday < 70 && year < 70)) {
834 goto syntax;
836 if (mday > year) {
837 temp = year;
838 year = mday;
839 mday = temp;
841 if (year >= 70 && year < 100) {
842 year += 1900;
844 } else if (mon < 70) { /* (a) month/day/year */
845 if (year < 100) {
846 year += 1900;
848 } else if (mon < 100) { /* (b) year/month/day */
849 if (mday < 70) {
850 temp = year;
851 year = mon + 1900;
852 mon = mday;
853 mday = temp;
854 } else {
855 goto syntax;
857 } else { /* (c) year/month/day */
858 if (mday < 70) {
859 temp = year;
860 year = mon;
861 mon = mday;
862 mday = temp;
863 } else {
864 goto syntax;
867 mon -= 1; /* convert month to 0-based */
868 if (sec < 0)
869 sec = 0;
870 if (min < 0)
871 min = 0;
872 if (hour < 0)
873 hour = 0;
874 if (tzoffset == -1) { /* no time zone specified, have to use local */
875 jsdouble msec_time;
876 msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
878 *result = UTC(msec_time);
879 return JS_TRUE;
882 msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
883 msec += tzoffset * msPerMinute;
884 *result = msec;
885 return JS_TRUE;
887 syntax:
888 /* syntax error */
889 *result = 0;
890 return JS_FALSE;
893 static JSBool
894 date_parse(JSContext *cx, uintN argc, jsval *vp)
896 JSString *str;
897 jsdouble result;
899 if (argc == 0) {
900 *vp = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
901 return JS_TRUE;
903 str = js_ValueToString(cx, vp[2]);
904 if (!str)
905 return JS_FALSE;
906 vp[2] = STRING_TO_JSVAL(str);
907 if (!date_parseString(str, &result)) {
908 *vp = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
909 return JS_TRUE;
912 result = TIMECLIP(result);
913 return js_NewNumberInRootedValue(cx, result, vp);
916 static JSBool
917 date_now(JSContext *cx, uintN argc, jsval *vp)
919 return js_NewDoubleInRootedValue(cx, PRMJ_Now() / PRMJ_USEC_PER_MSEC, vp);
922 #ifdef JS_TRACER
923 static jsdouble FASTCALL
924 date_now_tn(JSContext*)
926 return PRMJ_Now() / PRMJ_USEC_PER_MSEC;
928 #endif
931 * Get UTC time from the date object. Returns false if the object is not
932 * Date type.
934 static JSBool
935 GetUTCTime(JSContext *cx, JSObject *obj, jsval *vp, jsdouble *dp)
937 if (!JS_InstanceOf(cx, obj, &js_DateClass, vp ? vp + 2 : NULL))
938 return JS_FALSE;
939 *dp = *JSVAL_TO_DOUBLE(obj->fslots[JSSLOT_UTC_TIME]);
940 return JS_TRUE;
944 * Set UTC time slot with a pointer pointing to a jsdouble. This function is
945 * used only for setting UTC time to some predefined values, such as NaN.
947 * It also invalidates cached local time.
949 static JSBool
950 SetUTCTimePtr(JSContext *cx, JSObject *obj, jsval *vp, jsdouble *dp)
952 if (vp && !JS_InstanceOf(cx, obj, &js_DateClass, vp + 2))
953 return JS_FALSE;
954 JS_ASSERT_IF(!vp, STOBJ_GET_CLASS(obj) == &js_DateClass);
956 /* Invalidate local time cache. */
957 obj->fslots[JSSLOT_LOCAL_TIME] = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
958 obj->fslots[JSSLOT_UTC_TIME] = DOUBLE_TO_JSVAL(dp);
959 return JS_TRUE;
963 * Set UTC time to a given time.
965 static JSBool
966 SetUTCTime(JSContext *cx, JSObject *obj, jsval *vp, jsdouble t)
968 jsdouble *dp = js_NewWeaklyRootedDouble(cx, t);
969 if (!dp)
970 return JS_FALSE;
971 return SetUTCTimePtr(cx, obj, vp, dp);
975 * Get the local time, cache it if necessary. If UTC time is not finite
976 * (e.g., NaN), the local time slot is set to the UTC time without conversion.
978 static JSBool
979 GetAndCacheLocalTime(JSContext *cx, JSObject *obj, jsval *vp, jsdouble *dp)
981 jsval v;
982 jsdouble result;
983 jsdouble *cached;
985 if (!obj || !JS_InstanceOf(cx, obj, &js_DateClass, vp ? vp + 2 : NULL))
986 return JS_FALSE;
987 v = obj->fslots[JSSLOT_LOCAL_TIME];
989 result = *JSVAL_TO_DOUBLE(v);
991 if (JSDOUBLE_IS_NaN(result)) {
992 if (!GetUTCTime(cx, obj, vp, &result))
993 return JS_FALSE;
995 /* if result is NaN, it couldn't be finite. */
996 if (JSDOUBLE_IS_FINITE(result))
997 result = LocalTime(result);
999 cached = js_NewWeaklyRootedDouble(cx, result);
1000 if (!cached)
1001 return JS_FALSE;
1003 obj->fslots[JSSLOT_LOCAL_TIME] = DOUBLE_TO_JSVAL(cached);
1006 *dp = result;
1007 return JS_TRUE;
1011 * See ECMA 15.9.5.4 thru 15.9.5.23
1013 static JSBool
1014 date_getTime(JSContext *cx, uintN argc, jsval *vp)
1016 jsdouble result;
1018 return GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result) &&
1019 js_NewNumberInRootedValue(cx, result, vp);
1022 static JSBool
1023 GetYear(JSContext *cx, JSBool fullyear, jsval *vp)
1025 jsdouble result;
1027 if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1028 return JS_FALSE;
1030 if (JSDOUBLE_IS_FINITE(result)) {
1031 result = YearFromTime(result);
1033 /* Follow ECMA-262 to the letter, contrary to IE JScript. */
1034 if (!fullyear)
1035 result -= 1900;
1038 return js_NewNumberInRootedValue(cx, result, vp);
1041 static JSBool
1042 date_getYear(JSContext *cx, uintN argc, jsval *vp)
1044 return GetYear(cx, JS_FALSE, vp);
1047 static JSBool
1048 date_getFullYear(JSContext *cx, uintN argc, jsval *vp)
1050 return GetYear(cx, JS_TRUE, vp);
1053 static JSBool
1054 date_getUTCFullYear(JSContext *cx, uintN argc, jsval *vp)
1056 jsdouble result;
1058 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1059 return JS_FALSE;
1061 if (JSDOUBLE_IS_FINITE(result))
1062 result = YearFromTime(result);
1064 return js_NewNumberInRootedValue(cx, result, vp);
1067 static JSBool
1068 date_getMonth(JSContext *cx, uintN argc, jsval *vp)
1070 jsdouble result;
1072 if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1073 return JS_FALSE;
1075 if (JSDOUBLE_IS_FINITE(result))
1076 result = MonthFromTime(result);
1078 return js_NewNumberInRootedValue(cx, result, vp);
1081 static JSBool
1082 date_getUTCMonth(JSContext *cx, uintN argc, jsval *vp)
1084 jsdouble result;
1086 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1087 return JS_FALSE;
1089 if (JSDOUBLE_IS_FINITE(result))
1090 result = MonthFromTime(result);
1092 return js_NewNumberInRootedValue(cx, result, vp);
1095 static JSBool
1096 date_getDate(JSContext *cx, uintN argc, jsval *vp)
1098 jsdouble result;
1100 if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1101 return JS_FALSE;
1103 if (JSDOUBLE_IS_FINITE(result))
1104 result = DateFromTime(result);
1106 return js_NewNumberInRootedValue(cx, result, vp);
1109 static JSBool
1110 date_getUTCDate(JSContext *cx, uintN argc, jsval *vp)
1112 jsdouble result;
1114 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1115 return JS_FALSE;
1117 if (JSDOUBLE_IS_FINITE(result))
1118 result = DateFromTime(result);
1120 return js_NewNumberInRootedValue(cx, result, vp);
1123 static JSBool
1124 date_getDay(JSContext *cx, uintN argc, jsval *vp)
1126 jsdouble result;
1128 if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1129 return JS_FALSE;
1131 if (JSDOUBLE_IS_FINITE(result))
1132 result = WeekDay(result);
1134 return js_NewNumberInRootedValue(cx, result, vp);
1137 static JSBool
1138 date_getUTCDay(JSContext *cx, uintN argc, jsval *vp)
1140 jsdouble result;
1142 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1143 return JS_FALSE;
1145 if (JSDOUBLE_IS_FINITE(result))
1146 result = WeekDay(result);
1148 return js_NewNumberInRootedValue(cx, result, vp);
1151 static JSBool
1152 date_getHours(JSContext *cx, uintN argc, jsval *vp)
1154 jsdouble result;
1156 if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1157 return JS_FALSE;
1159 if (JSDOUBLE_IS_FINITE(result))
1160 result = HourFromTime(result);
1162 return js_NewNumberInRootedValue(cx, result, vp);
1165 static JSBool
1166 date_getUTCHours(JSContext *cx, uintN argc, jsval *vp)
1168 jsdouble result;
1170 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1171 return JS_FALSE;
1173 if (JSDOUBLE_IS_FINITE(result))
1174 result = HourFromTime(result);
1176 return js_NewNumberInRootedValue(cx, result, vp);
1179 static JSBool
1180 date_getMinutes(JSContext *cx, uintN argc, jsval *vp)
1182 jsdouble result;
1184 if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1185 return JS_FALSE;
1187 if (JSDOUBLE_IS_FINITE(result))
1188 result = MinFromTime(result);
1190 return js_NewNumberInRootedValue(cx, result, vp);
1193 static JSBool
1194 date_getUTCMinutes(JSContext *cx, uintN argc, jsval *vp)
1196 jsdouble result;
1198 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1199 return JS_FALSE;
1201 if (JSDOUBLE_IS_FINITE(result))
1202 result = MinFromTime(result);
1204 return js_NewNumberInRootedValue(cx, result, vp);
1207 /* Date.getSeconds is mapped to getUTCSeconds */
1209 static JSBool
1210 date_getUTCSeconds(JSContext *cx, uintN argc, jsval *vp)
1212 jsdouble result;
1214 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1215 return JS_FALSE;
1217 if (JSDOUBLE_IS_FINITE(result))
1218 result = SecFromTime(result);
1220 return js_NewNumberInRootedValue(cx, result, vp);
1223 /* Date.getMilliseconds is mapped to getUTCMilliseconds */
1225 static JSBool
1226 date_getUTCMilliseconds(JSContext *cx, uintN argc, jsval *vp)
1228 jsdouble result;
1230 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1231 return JS_FALSE;
1233 if (JSDOUBLE_IS_FINITE(result))
1234 result = msFromTime(result);
1236 return js_NewNumberInRootedValue(cx, result, vp);
1239 static JSBool
1240 date_getTimezoneOffset(JSContext *cx, uintN argc, jsval *vp)
1242 JSObject *obj;
1243 jsdouble utctime, localtime, result;
1245 obj = JS_THIS_OBJECT(cx, vp);
1246 if (!GetUTCTime(cx, obj, vp, &utctime))
1247 return JS_FALSE;
1248 if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime))
1249 return JS_FALSE;
1252 * Return the time zone offset in minutes for the current locale that is
1253 * appropriate for this time. This value would be a constant except for
1254 * daylight savings time.
1256 result = (utctime - localtime) / msPerMinute;
1257 return js_NewNumberInRootedValue(cx, result, vp);
1260 static JSBool
1261 SetDateToNaN(JSContext *cx, jsval *vp)
1263 JSObject *obj;
1265 obj = JS_THIS_OBJECT(cx, vp);
1266 if (!SetUTCTimePtr(cx, obj, NULL, cx->runtime->jsNaN))
1267 return JS_FALSE;
1268 *vp = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
1269 return JS_TRUE;
1272 static JSBool
1273 date_setTime(JSContext *cx, uintN argc, jsval *vp)
1275 jsdouble result;
1277 if (argc == 0)
1278 return SetDateToNaN(cx, vp);
1279 result = js_ValueToNumber(cx, &vp[2]);
1280 if (JSVAL_IS_NULL(vp[2]))
1281 return JS_FALSE;
1283 result = TIMECLIP(result);
1285 if (!SetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, result))
1286 return JS_FALSE;
1288 return js_NewNumberInRootedValue(cx, result, vp);
1291 static JSBool
1292 date_makeTime(JSContext *cx, uintN maxargs, JSBool local, uintN argc, jsval *vp)
1294 JSObject *obj;
1295 jsval *argv;
1296 uintN i;
1297 jsdouble args[4], *argp, *stop;
1298 jsdouble hour, min, sec, msec;
1299 jsdouble lorutime; /* Local or UTC version of *date */
1301 jsdouble msec_time;
1302 jsdouble result;
1304 obj = JS_THIS_OBJECT(cx, vp);
1305 if (!GetUTCTime(cx, obj, vp, &result))
1306 return JS_FALSE;
1308 /* just return NaN if the date is already NaN */
1309 if (!JSDOUBLE_IS_FINITE(result))
1310 return js_NewNumberInRootedValue(cx, result, vp);
1313 * Satisfy the ECMA rule that if a function is called with
1314 * fewer arguments than the specified formal arguments, the
1315 * remaining arguments are set to undefined. Seems like all
1316 * the Date.setWhatever functions in ECMA are only varargs
1317 * beyond the first argument; this should be set to undefined
1318 * if it's not given. This means that "d = new Date();
1319 * d.setMilliseconds()" returns NaN. Blech.
1321 if (argc == 0)
1322 return SetDateToNaN(cx, vp);
1323 if (argc > maxargs)
1324 argc = maxargs; /* clamp argc */
1325 JS_ASSERT(argc <= 4);
1327 argv = vp + 2;
1328 for (i = 0; i < argc; i++) {
1329 args[i] = js_ValueToNumber(cx, &argv[i]);
1330 if (JSVAL_IS_NULL(argv[i]))
1331 return JS_FALSE;
1332 if (!JSDOUBLE_IS_FINITE(args[i]))
1333 return SetDateToNaN(cx, vp);
1334 args[i] = js_DoubleToInteger(args[i]);
1337 if (local)
1338 lorutime = LocalTime(result);
1339 else
1340 lorutime = result;
1342 argp = args;
1343 stop = argp + argc;
1344 if (maxargs >= 4 && argp < stop)
1345 hour = *argp++;
1346 else
1347 hour = HourFromTime(lorutime);
1349 if (maxargs >= 3 && argp < stop)
1350 min = *argp++;
1351 else
1352 min = MinFromTime(lorutime);
1354 if (maxargs >= 2 && argp < stop)
1355 sec = *argp++;
1356 else
1357 sec = SecFromTime(lorutime);
1359 if (maxargs >= 1 && argp < stop)
1360 msec = *argp;
1361 else
1362 msec = msFromTime(lorutime);
1364 msec_time = MakeTime(hour, min, sec, msec);
1365 result = MakeDate(Day(lorutime), msec_time);
1367 /* fprintf(stderr, "%f\n", result); */
1369 if (local)
1370 result = UTC(result);
1372 /* fprintf(stderr, "%f\n", result); */
1374 result = TIMECLIP(result);
1375 if (!SetUTCTime(cx, obj, NULL, result))
1376 return JS_FALSE;
1378 return js_NewNumberInRootedValue(cx, result, vp);
1381 static JSBool
1382 date_setMilliseconds(JSContext *cx, uintN argc, jsval *vp)
1384 return date_makeTime(cx, 1, JS_TRUE, argc, vp);
1387 static JSBool
1388 date_setUTCMilliseconds(JSContext *cx, uintN argc, jsval *vp)
1390 return date_makeTime(cx, 1, JS_FALSE, argc, vp);
1393 static JSBool
1394 date_setSeconds(JSContext *cx, uintN argc, jsval *vp)
1396 return date_makeTime(cx, 2, JS_TRUE, argc, vp);
1399 static JSBool
1400 date_setUTCSeconds(JSContext *cx, uintN argc, jsval *vp)
1402 return date_makeTime(cx, 2, JS_FALSE, argc, vp);
1405 static JSBool
1406 date_setMinutes(JSContext *cx, uintN argc, jsval *vp)
1408 return date_makeTime(cx, 3, JS_TRUE, argc, vp);
1411 static JSBool
1412 date_setUTCMinutes(JSContext *cx, uintN argc, jsval *vp)
1414 return date_makeTime(cx, 3, JS_FALSE, argc, vp);
1417 static JSBool
1418 date_setHours(JSContext *cx, uintN argc, jsval *vp)
1420 return date_makeTime(cx, 4, JS_TRUE, argc, vp);
1423 static JSBool
1424 date_setUTCHours(JSContext *cx, uintN argc, jsval *vp)
1426 return date_makeTime(cx, 4, JS_FALSE, argc, vp);
1429 static JSBool
1430 date_makeDate(JSContext *cx, uintN maxargs, JSBool local, uintN argc, jsval *vp)
1432 JSObject *obj;
1433 jsval *argv;
1434 uintN i;
1435 jsdouble lorutime; /* local or UTC version of *date */
1436 jsdouble args[3], *argp, *stop;
1437 jsdouble year, month, day;
1438 jsdouble result;
1440 obj = JS_THIS_OBJECT(cx, vp);
1441 if (!GetUTCTime(cx, obj, vp, &result))
1442 return JS_FALSE;
1444 /* see complaint about ECMA in date_MakeTime */
1445 if (argc == 0)
1446 return SetDateToNaN(cx, vp);
1447 if (argc > maxargs)
1448 argc = maxargs; /* clamp argc */
1449 JS_ASSERT(1 <= argc && argc <= 3);
1451 argv = vp + 2;
1452 for (i = 0; i < argc; i++) {
1453 args[i] = js_ValueToNumber(cx, &argv[i]);
1454 if (JSVAL_IS_NULL(argv[i]))
1455 return JS_FALSE;
1456 if (!JSDOUBLE_IS_FINITE(args[i]))
1457 return SetDateToNaN(cx, vp);
1458 args[i] = js_DoubleToInteger(args[i]);
1461 /* return NaN if date is NaN and we're not setting the year,
1462 * If we are, use 0 as the time. */
1463 if (!(JSDOUBLE_IS_FINITE(result))) {
1464 if (maxargs < 3)
1465 return js_NewNumberInRootedValue(cx, result, vp);
1466 lorutime = +0.;
1467 } else {
1468 lorutime = local ? LocalTime(result) : result;
1471 argp = args;
1472 stop = argp + argc;
1473 if (maxargs >= 3 && argp < stop)
1474 year = *argp++;
1475 else
1476 year = YearFromTime(lorutime);
1478 if (maxargs >= 2 && argp < stop)
1479 month = *argp++;
1480 else
1481 month = MonthFromTime(lorutime);
1483 if (maxargs >= 1 && argp < stop)
1484 day = *argp++;
1485 else
1486 day = DateFromTime(lorutime);
1488 day = MakeDay(year, month, day); /* day within year */
1489 result = MakeDate(day, TimeWithinDay(lorutime));
1491 if (local)
1492 result = UTC(result);
1494 result = TIMECLIP(result);
1495 if (!SetUTCTime(cx, obj, NULL, result))
1496 return JS_FALSE;
1498 return js_NewNumberInRootedValue(cx, result, vp);
1501 static JSBool
1502 date_setDate(JSContext *cx, uintN argc, jsval *vp)
1504 return date_makeDate(cx, 1, JS_TRUE, argc, vp);
1507 static JSBool
1508 date_setUTCDate(JSContext *cx, uintN argc, jsval *vp)
1510 return date_makeDate(cx, 1, JS_FALSE, argc, vp);
1513 static JSBool
1514 date_setMonth(JSContext *cx, uintN argc, jsval *vp)
1516 return date_makeDate(cx, 2, JS_TRUE, argc, vp);
1519 static JSBool
1520 date_setUTCMonth(JSContext *cx, uintN argc, jsval *vp)
1522 return date_makeDate(cx, 2, JS_FALSE, argc, vp);
1525 static JSBool
1526 date_setFullYear(JSContext *cx, uintN argc, jsval *vp)
1528 return date_makeDate(cx, 3, JS_TRUE, argc, vp);
1531 static JSBool
1532 date_setUTCFullYear(JSContext *cx, uintN argc, jsval *vp)
1534 return date_makeDate(cx, 3, JS_FALSE, argc, vp);
1537 static JSBool
1538 date_setYear(JSContext *cx, uintN argc, jsval *vp)
1540 JSObject *obj;
1541 jsdouble t;
1542 jsdouble year;
1543 jsdouble day;
1544 jsdouble result;
1546 obj = JS_THIS_OBJECT(cx, vp);
1547 if (!GetUTCTime(cx, obj, vp, &result))
1548 return JS_FALSE;
1550 if (argc == 0)
1551 return SetDateToNaN(cx, vp);
1552 year = js_ValueToNumber(cx, &vp[2]);
1553 if (JSVAL_IS_NULL(vp[2]))
1554 return JS_FALSE;
1555 if (!JSDOUBLE_IS_FINITE(year))
1556 return SetDateToNaN(cx, vp);
1558 year = js_DoubleToInteger(year);
1560 if (!JSDOUBLE_IS_FINITE(result)) {
1561 t = +0.0;
1562 } else {
1563 t = LocalTime(result);
1566 if (year >= 0 && year <= 99)
1567 year += 1900;
1569 day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
1570 result = MakeDate(day, TimeWithinDay(t));
1571 result = UTC(result);
1573 result = TIMECLIP(result);
1574 if (!SetUTCTime(cx, obj, NULL, result))
1575 return JS_FALSE;
1577 return js_NewNumberInRootedValue(cx, result, vp);
1580 /* constants for toString, toUTCString */
1581 static char js_NaN_date_str[] = "Invalid Date";
1582 static const char* days[] =
1584 "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
1586 static const char* months[] =
1588 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1592 // Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
1593 // requires a PRMJTime... which only has 16-bit years. Sub-ECMA.
1594 static void
1595 print_gmt_string(char* buf, size_t size, jsdouble utctime)
1597 JS_snprintf(buf, size, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
1598 days[WeekDay(utctime)],
1599 DateFromTime(utctime),
1600 months[MonthFromTime(utctime)],
1601 YearFromTime(utctime),
1602 HourFromTime(utctime),
1603 MinFromTime(utctime),
1604 SecFromTime(utctime));
1607 static void
1608 print_iso_string(char* buf, size_t size, jsdouble utctime)
1610 JS_snprintf(buf, size, "%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.3dZ",
1611 YearFromTime(utctime),
1612 MonthFromTime(utctime) + 1,
1613 DateFromTime(utctime),
1614 HourFromTime(utctime),
1615 MinFromTime(utctime),
1616 SecFromTime(utctime),
1617 msFromTime(utctime));
1620 static JSBool
1621 date_utc_format(JSContext *cx, jsval *vp,
1622 void (*printFunc)(char*, size_t, jsdouble))
1624 char buf[100];
1625 JSString *str;
1626 jsdouble utctime;
1628 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &utctime))
1629 return JS_FALSE;
1631 if (!JSDOUBLE_IS_FINITE(utctime)) {
1632 JS_snprintf(buf, sizeof buf, js_NaN_date_str);
1633 } else {
1634 (*printFunc)(buf, sizeof buf, utctime);
1636 str = JS_NewStringCopyZ(cx, buf);
1637 if (!str)
1638 return JS_FALSE;
1639 *vp = STRING_TO_JSVAL(str);
1640 return JS_TRUE;
1643 static JSBool
1644 date_toGMTString(JSContext *cx, uintN argc, jsval *vp)
1646 return date_utc_format(cx, vp, print_gmt_string);
1649 static JSBool
1650 date_toISOString(JSContext *cx, uintN argc, jsval *vp)
1652 return date_utc_format(cx, vp, print_iso_string);
1655 /* for Date.toLocaleString; interface to PRMJTime date struct.
1657 static void
1658 new_explode(jsdouble timeval, PRMJTime *split)
1660 jsint year = YearFromTime(timeval);
1662 split->tm_usec = (int32) msFromTime(timeval) * 1000;
1663 split->tm_sec = (int8) SecFromTime(timeval);
1664 split->tm_min = (int8) MinFromTime(timeval);
1665 split->tm_hour = (int8) HourFromTime(timeval);
1666 split->tm_mday = (int8) DateFromTime(timeval);
1667 split->tm_mon = (int8) MonthFromTime(timeval);
1668 split->tm_wday = (int8) WeekDay(timeval);
1669 split->tm_year = year;
1670 split->tm_yday = (int16) DayWithinYear(timeval, year);
1672 /* not sure how this affects things, but it doesn't seem
1673 to matter. */
1674 split->tm_isdst = (DaylightSavingTA(timeval) != 0);
1677 typedef enum formatspec {
1678 FORMATSPEC_FULL, FORMATSPEC_DATE, FORMATSPEC_TIME
1679 } formatspec;
1681 /* helper function */
1682 static JSBool
1683 date_format(JSContext *cx, jsdouble date, formatspec format, jsval *rval)
1685 char buf[100];
1686 JSString *str;
1687 char tzbuf[100];
1688 JSBool usetz;
1689 size_t i, tzlen;
1690 PRMJTime split;
1692 if (!JSDOUBLE_IS_FINITE(date)) {
1693 JS_snprintf(buf, sizeof buf, js_NaN_date_str);
1694 } else {
1695 jsdouble local = LocalTime(date);
1697 /* offset from GMT in minutes. The offset includes daylight savings,
1698 if it applies. */
1699 jsint minutes = (jsint) floor(AdjustTime(date) / msPerMinute);
1701 /* map 510 minutes to 0830 hours */
1702 intN offset = (minutes / 60) * 100 + minutes % 60;
1704 /* print as "Wed Nov 05 19:38:03 GMT-0800 (PST) 1997" The TZA is
1705 * printed as 'GMT-0800' rather than as 'PST' to avoid
1706 * operating-system dependence on strftime (which
1707 * PRMJ_FormatTimeUSEnglish calls, for %Z only.) win32 prints
1708 * PST as 'Pacific Standard Time.' This way we always know
1709 * what we're getting, and can parse it if we produce it.
1710 * The OS TZA string is included as a comment.
1713 /* get a timezone string from the OS to include as a
1714 comment. */
1715 new_explode(date, &split);
1716 if (PRMJ_FormatTime(tzbuf, sizeof tzbuf, "(%Z)", &split) != 0) {
1718 /* Decide whether to use the resulting timezone string.
1720 * Reject it if it contains any non-ASCII, non-alphanumeric
1721 * characters. It's then likely in some other character
1722 * encoding, and we probably won't display it correctly.
1724 usetz = JS_TRUE;
1725 tzlen = strlen(tzbuf);
1726 if (tzlen > 100) {
1727 usetz = JS_FALSE;
1728 } else {
1729 for (i = 0; i < tzlen; i++) {
1730 jschar c = tzbuf[i];
1731 if (c > 127 ||
1732 !(isalpha(c) || isdigit(c) ||
1733 c == ' ' || c == '(' || c == ')')) {
1734 usetz = JS_FALSE;
1739 /* Also reject it if it's not parenthesized or if it's '()'. */
1740 if (tzbuf[0] != '(' || tzbuf[1] == ')')
1741 usetz = JS_FALSE;
1742 } else
1743 usetz = JS_FALSE;
1745 switch (format) {
1746 case FORMATSPEC_FULL:
1748 * Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
1749 * requires a PRMJTime... which only has 16-bit years. Sub-ECMA.
1751 /* Tue Oct 31 2000 09:41:40 GMT-0800 (PST) */
1752 JS_snprintf(buf, sizeof buf,
1753 "%s %s %.2d %.4d %.2d:%.2d:%.2d GMT%+.4d%s%s",
1754 days[WeekDay(local)],
1755 months[MonthFromTime(local)],
1756 DateFromTime(local),
1757 YearFromTime(local),
1758 HourFromTime(local),
1759 MinFromTime(local),
1760 SecFromTime(local),
1761 offset,
1762 usetz ? " " : "",
1763 usetz ? tzbuf : "");
1764 break;
1765 case FORMATSPEC_DATE:
1766 /* Tue Oct 31 2000 */
1767 JS_snprintf(buf, sizeof buf,
1768 "%s %s %.2d %.4d",
1769 days[WeekDay(local)],
1770 months[MonthFromTime(local)],
1771 DateFromTime(local),
1772 YearFromTime(local));
1773 break;
1774 case FORMATSPEC_TIME:
1775 /* 09:41:40 GMT-0800 (PST) */
1776 JS_snprintf(buf, sizeof buf,
1777 "%.2d:%.2d:%.2d GMT%+.4d%s%s",
1778 HourFromTime(local),
1779 MinFromTime(local),
1780 SecFromTime(local),
1781 offset,
1782 usetz ? " " : "",
1783 usetz ? tzbuf : "");
1784 break;
1788 str = JS_NewStringCopyZ(cx, buf);
1789 if (!str)
1790 return JS_FALSE;
1791 *rval = STRING_TO_JSVAL(str);
1792 return JS_TRUE;
1795 static JSBool
1796 date_toLocaleHelper(JSContext *cx, const char *format, jsval *vp)
1798 JSObject *obj;
1799 char buf[100];
1800 JSString *str;
1801 PRMJTime split;
1802 jsdouble utctime;
1804 obj = JS_THIS_OBJECT(cx, vp);
1805 if (!GetUTCTime(cx, obj, vp, &utctime))
1806 return JS_FALSE;
1808 if (!JSDOUBLE_IS_FINITE(utctime)) {
1809 JS_snprintf(buf, sizeof buf, js_NaN_date_str);
1810 } else {
1811 intN result_len;
1812 jsdouble local = LocalTime(utctime);
1813 new_explode(local, &split);
1815 /* let PRMJTime format it. */
1816 result_len = PRMJ_FormatTime(buf, sizeof buf, format, &split);
1818 /* If it failed, default to toString. */
1819 if (result_len == 0)
1820 return date_format(cx, utctime, FORMATSPEC_FULL, vp);
1822 /* Hacked check against undesired 2-digit year 00/00/00 form. */
1823 if (strcmp(format, "%x") == 0 && result_len >= 6 &&
1824 /* Format %x means use OS settings, which may have 2-digit yr, so
1825 hack end of 3/11/22 or 11.03.22 or 11Mar22 to use 4-digit yr...*/
1826 !isdigit(buf[result_len - 3]) &&
1827 isdigit(buf[result_len - 2]) && isdigit(buf[result_len - 1]) &&
1828 /* ...but not if starts with 4-digit year, like 2022/3/11. */
1829 !(isdigit(buf[0]) && isdigit(buf[1]) &&
1830 isdigit(buf[2]) && isdigit(buf[3]))) {
1831 JS_snprintf(buf + (result_len - 2), (sizeof buf) - (result_len - 2),
1832 "%d", js_DateGetYear(cx, obj));
1837 if (cx->localeCallbacks && cx->localeCallbacks->localeToUnicode)
1838 return cx->localeCallbacks->localeToUnicode(cx, buf, vp);
1840 str = JS_NewStringCopyZ(cx, buf);
1841 if (!str)
1842 return JS_FALSE;
1843 *vp = STRING_TO_JSVAL(str);
1844 return JS_TRUE;
1847 static JSBool
1848 date_toLocaleString(JSContext *cx, uintN argc, jsval *vp)
1850 /* Use '%#c' for windows, because '%c' is
1851 * backward-compatible and non-y2k with msvc; '%#c' requests that a
1852 * full year be used in the result string.
1854 return date_toLocaleHelper(cx,
1855 #if defined(_WIN32) && !defined(__MWERKS__)
1856 "%#c"
1857 #else
1858 "%c"
1859 #endif
1860 , vp);
1863 static JSBool
1864 date_toLocaleDateString(JSContext *cx, uintN argc, jsval *vp)
1866 /* Use '%#x' for windows, because '%x' is
1867 * backward-compatible and non-y2k with msvc; '%#x' requests that a
1868 * full year be used in the result string.
1870 return date_toLocaleHelper(cx,
1871 #if defined(_WIN32) && !defined(__MWERKS__)
1872 "%#x"
1873 #else
1874 "%x"
1875 #endif
1876 , vp);
1879 static JSBool
1880 date_toLocaleTimeString(JSContext *cx, uintN argc, jsval *vp)
1882 return date_toLocaleHelper(cx, "%X", vp);
1885 static JSBool
1886 date_toLocaleFormat(JSContext *cx, uintN argc, jsval *vp)
1888 JSString *fmt;
1889 const char *fmtbytes;
1891 if (argc == 0)
1892 return date_toLocaleString(cx, argc, vp);
1894 fmt = js_ValueToString(cx, vp[2]);
1895 if (!fmt)
1896 return JS_FALSE;
1897 vp[2] = STRING_TO_JSVAL(fmt);
1898 fmtbytes = js_GetStringBytes(cx, fmt);
1899 if (!fmtbytes)
1900 return JS_FALSE;
1902 return date_toLocaleHelper(cx, fmtbytes, vp);
1905 static JSBool
1906 date_toTimeString(JSContext *cx, uintN argc, jsval *vp)
1908 jsdouble utctime;
1910 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &utctime))
1911 return JS_FALSE;
1912 return date_format(cx, utctime, FORMATSPEC_TIME, vp);
1915 static JSBool
1916 date_toDateString(JSContext *cx, uintN argc, jsval *vp)
1918 jsdouble utctime;
1920 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &utctime))
1921 return JS_FALSE;
1922 return date_format(cx, utctime, FORMATSPEC_DATE, vp);
1925 #if JS_HAS_TOSOURCE
1926 #include <string.h>
1927 #include "jsdtoa.h"
1929 static JSBool
1930 date_toSource(JSContext *cx, uintN argc, jsval *vp)
1932 jsdouble utctime;
1933 char buf[DTOSTR_STANDARD_BUFFER_SIZE], *numStr, *bytes;
1934 JSString *str;
1936 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &utctime))
1937 return JS_FALSE;
1939 numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, utctime);
1940 if (!numStr) {
1941 JS_ReportOutOfMemory(cx);
1942 return JS_FALSE;
1945 bytes = JS_smprintf("(new %s(%s))", js_Date_str, numStr);
1946 if (!bytes) {
1947 JS_ReportOutOfMemory(cx);
1948 return JS_FALSE;
1951 str = JS_NewString(cx, bytes, strlen(bytes));
1952 if (!str) {
1953 free(bytes);
1954 return JS_FALSE;
1956 *vp = STRING_TO_JSVAL(str);
1957 return JS_TRUE;
1959 #endif
1961 static JSBool
1962 date_toString(JSContext *cx, uintN argc, jsval *vp)
1964 jsdouble utctime;
1966 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &utctime))
1967 return JS_FALSE;
1968 return date_format(cx, utctime, FORMATSPEC_FULL, vp);
1971 #ifdef JS_TRACER
1972 static jsval FASTCALL
1973 date_valueOf_tn(JSContext* cx, JSObject* obj, JSString* str)
1975 JS_ASSERT(JS_InstanceOf(cx, obj, &js_DateClass, NULL));
1976 jsdouble t = *JSVAL_TO_DOUBLE(obj->fslots[JSSLOT_UTC_TIME]);
1978 JSString* number_str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]);
1979 jsval v;
1980 if (js_EqualStrings(str, number_str)) {
1981 if (!js_NewNumberInRootedValue(cx, t, &v))
1982 return JSVAL_ERROR_COOKIE;
1983 } else {
1984 if (!date_format(cx, t, FORMATSPEC_FULL, &v))
1985 return JSVAL_ERROR_COOKIE;
1987 return v;
1989 #endif
1991 static JSBool
1992 date_valueOf(JSContext *cx, uintN argc, jsval *vp)
1994 JSString *str, *number_str;
1996 /* It is an error to call date_valueOf on a non-date object, but we don't
1997 * need to check for that explicitly here because every path calls
1998 * GetUTCTime, which does the check.
2001 /* If called directly with no arguments, convert to a time number. */
2002 if (argc == 0)
2003 return date_getTime(cx, argc, vp);
2005 /* Convert to number only if the hint was given, otherwise favor string. */
2006 str = js_ValueToString(cx, vp[2]);
2007 if (!str)
2008 return JS_FALSE;
2009 number_str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]);
2010 if (js_EqualStrings(str, number_str))
2011 return date_getTime(cx, argc, vp);
2012 return date_toString(cx, argc, vp);
2015 JS_DEFINE_CALLINFO_2(extern, OBJECT, js_FastNewDate, CONTEXT, OBJECT, 0, 0)
2017 // Don't really need an argument here, but we don't support arg-less builtins
2018 JS_DEFINE_TRCINFO_1(date_now,
2019 (1, (static, DOUBLE, date_now_tn, CONTEXT, 0, 0)))
2021 static JSFunctionSpec date_static_methods[] = {
2022 JS_FN("UTC", date_UTC, MAXARGS,0),
2023 JS_FN("parse", date_parse, 1,0),
2024 JS_TN("now", date_now, 0,0, date_now_trcinfo),
2025 JS_FS_END
2028 JS_DEFINE_TRCINFO_1(date_valueOf,
2029 (3, (static, JSVAL_FAIL, date_valueOf_tn, CONTEXT, THIS, STRING, 0, 0)))
2031 static JSFunctionSpec date_methods[] = {
2032 JS_FN("getTime", date_getTime, 0,0),
2033 JS_FN("getTimezoneOffset", date_getTimezoneOffset, 0,0),
2034 JS_FN("getYear", date_getYear, 0,0),
2035 JS_FN("getFullYear", date_getFullYear, 0,0),
2036 JS_FN("getUTCFullYear", date_getUTCFullYear, 0,0),
2037 JS_FN("getMonth", date_getMonth, 0,0),
2038 JS_FN("getUTCMonth", date_getUTCMonth, 0,0),
2039 JS_FN("getDate", date_getDate, 0,0),
2040 JS_FN("getUTCDate", date_getUTCDate, 0,0),
2041 JS_FN("getDay", date_getDay, 0,0),
2042 JS_FN("getUTCDay", date_getUTCDay, 0,0),
2043 JS_FN("getHours", date_getHours, 0,0),
2044 JS_FN("getUTCHours", date_getUTCHours, 0,0),
2045 JS_FN("getMinutes", date_getMinutes, 0,0),
2046 JS_FN("getUTCMinutes", date_getUTCMinutes, 0,0),
2047 JS_FN("getSeconds", date_getUTCSeconds, 0,0),
2048 JS_FN("getUTCSeconds", date_getUTCSeconds, 0,0),
2049 JS_FN("getMilliseconds", date_getUTCMilliseconds, 0,0),
2050 JS_FN("getUTCMilliseconds", date_getUTCMilliseconds, 0,0),
2051 JS_FN("setTime", date_setTime, 1,0),
2052 JS_FN("setYear", date_setYear, 1,0),
2053 JS_FN("setFullYear", date_setFullYear, 3,0),
2054 JS_FN("setUTCFullYear", date_setUTCFullYear, 3,0),
2055 JS_FN("setMonth", date_setMonth, 2,0),
2056 JS_FN("setUTCMonth", date_setUTCMonth, 2,0),
2057 JS_FN("setDate", date_setDate, 1,0),
2058 JS_FN("setUTCDate", date_setUTCDate, 1,0),
2059 JS_FN("setHours", date_setHours, 4,0),
2060 JS_FN("setUTCHours", date_setUTCHours, 4,0),
2061 JS_FN("setMinutes", date_setMinutes, 3,0),
2062 JS_FN("setUTCMinutes", date_setUTCMinutes, 3,0),
2063 JS_FN("setSeconds", date_setSeconds, 2,0),
2064 JS_FN("setUTCSeconds", date_setUTCSeconds, 2,0),
2065 JS_FN("setMilliseconds", date_setMilliseconds, 1,0),
2066 JS_FN("setUTCMilliseconds", date_setUTCMilliseconds, 1,0),
2067 JS_FN("toUTCString", date_toGMTString, 0,0),
2068 JS_FN(js_toLocaleString_str, date_toLocaleString, 0,0),
2069 JS_FN("toLocaleDateString", date_toLocaleDateString, 0,0),
2070 JS_FN("toLocaleTimeString", date_toLocaleTimeString, 0,0),
2071 JS_FN("toLocaleFormat", date_toLocaleFormat, 0,0),
2072 JS_FN("toDateString", date_toDateString, 0,0),
2073 JS_FN("toTimeString", date_toTimeString, 0,0),
2074 JS_FN("toISOString", date_toISOString, 0,0),
2075 JS_FN(js_toJSON_str, date_toISOString, 0,0),
2077 #if JS_HAS_TOSOURCE
2078 JS_FN(js_toSource_str, date_toSource, 0,0),
2079 #endif
2080 JS_FN(js_toString_str, date_toString, 0,0),
2081 JS_TN(js_valueOf_str, date_valueOf, 0,0, date_valueOf_trcinfo),
2082 JS_FS_END
2085 static jsdouble *
2086 date_constructor(JSContext *cx, JSObject* obj)
2088 jsdouble *date;
2090 date = js_NewWeaklyRootedDouble(cx, 0.0);
2091 if (!date)
2092 return NULL;
2094 obj->fslots[JSSLOT_UTC_TIME] = DOUBLE_TO_JSVAL(date);
2095 obj->fslots[JSSLOT_LOCAL_TIME] = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
2096 return date;
2099 JSBool
2100 js_Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2102 jsdouble *date;
2103 JSString *str;
2104 jsdouble d;
2106 /* Date called as function. */
2107 if (!JS_IsConstructing(cx)) {
2108 return date_format(cx, PRMJ_Now() / PRMJ_USEC_PER_MSEC,
2109 FORMATSPEC_FULL, rval);
2112 /* Date called as constructor. */
2113 if (argc == 0) {
2114 date = date_constructor(cx, obj);
2115 if (!date)
2116 return JS_FALSE;
2117 *date = PRMJ_Now() / PRMJ_USEC_PER_MSEC;
2118 } else if (argc == 1) {
2119 if (!JSVAL_IS_STRING(argv[0])) {
2120 /* the argument is a millisecond number */
2121 d = js_ValueToNumber(cx, &argv[0]);
2122 if (JSVAL_IS_NULL(argv[0]))
2123 return JS_FALSE;
2124 date = date_constructor(cx, obj);
2125 if (!date)
2126 return JS_FALSE;
2127 *date = TIMECLIP(d);
2128 } else {
2129 /* the argument is a string; parse it. */
2130 date = date_constructor(cx, obj);
2131 if (!date)
2132 return JS_FALSE;
2134 str = js_ValueToString(cx, argv[0]);
2135 if (!str)
2136 return JS_FALSE;
2138 if (!date_parseString(str, date))
2139 *date = *cx->runtime->jsNaN;
2140 *date = TIMECLIP(*date);
2142 } else {
2143 jsdouble *date;
2144 jsdouble msec_time;
2146 if (!date_msecFromArgs(cx, argc, argv, &msec_time))
2147 return JS_FALSE;
2149 date = date_constructor(cx, obj);
2150 if (!date)
2151 return JS_FALSE;
2153 if (JSDOUBLE_IS_FINITE(msec_time)) {
2154 msec_time = UTC(msec_time);
2155 msec_time = TIMECLIP(msec_time);
2158 *date = msec_time;
2160 return JS_TRUE;
2163 JS_STATIC_ASSERT(JSSLOT_PRIVATE == JSSLOT_UTC_TIME);
2164 JS_STATIC_ASSERT(JSSLOT_UTC_TIME + 1 == JSSLOT_LOCAL_TIME);
2166 #ifdef JS_TRACER
2167 JSObject* FASTCALL
2168 js_FastNewDate(JSContext* cx, JSObject* proto)
2170 JS_ASSERT(JS_ON_TRACE(cx));
2171 JSObject* obj = (JSObject*) js_NewGCThing(cx, GCX_OBJECT, sizeof(JSObject));
2172 if (!obj)
2173 return NULL;
2175 JSClass* clasp = &js_DateClass;
2176 obj->classword = jsuword(clasp);
2178 obj->fslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto);
2179 obj->fslots[JSSLOT_PARENT] = proto->fslots[JSSLOT_PARENT];
2181 jsdouble* date = js_NewWeaklyRootedDouble(cx, 0.0);
2182 if (!date)
2183 return NULL;
2184 *date = date_now_tn(cx);
2185 obj->fslots[JSSLOT_UTC_TIME] = DOUBLE_TO_JSVAL(date);
2186 obj->fslots[JSSLOT_LOCAL_TIME] = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
2187 for (unsigned i = JSSLOT_LOCAL_TIME + 1; i != JS_INITIAL_NSLOTS; ++i)
2188 obj->fslots[i] = JSVAL_VOID;
2190 JS_ASSERT(!clasp->getObjectOps);
2191 JS_ASSERT(proto->map->ops == &js_ObjectOps);
2192 obj->map = js_HoldObjectMap(cx, proto->map);
2193 obj->dslots = NULL;
2194 return obj;
2196 #endif
2198 JSObject *
2199 js_InitDateClass(JSContext *cx, JSObject *obj)
2201 JSObject *proto;
2202 jsdouble *proto_date;
2204 /* set static LocalTZA */
2205 LocalTZA = -(PRMJ_LocalGMTDifference() * msPerSecond);
2206 proto = JS_InitClass(cx, obj, NULL, &js_DateClass, js_Date, MAXARGS,
2207 NULL, date_methods, NULL, date_static_methods);
2208 if (!proto)
2209 return NULL;
2211 /* Alias toUTCString with toGMTString. (ECMA B.2.6) */
2212 if (!JS_AliasProperty(cx, proto, "toUTCString", "toGMTString"))
2213 return NULL;
2215 /* Set the value of the Date.prototype date to NaN */
2216 proto_date = date_constructor(cx, proto);
2217 if (!proto_date)
2218 return NULL;
2219 *proto_date = *cx->runtime->jsNaN;
2221 return proto;
2224 JS_FRIEND_API(JSObject *)
2225 js_NewDateObjectMsec(JSContext *cx, jsdouble msec_time)
2227 JSObject *obj;
2228 jsdouble *date;
2230 obj = js_NewObject(cx, &js_DateClass, NULL, NULL, 0);
2231 if (!obj)
2232 return NULL;
2234 date = date_constructor(cx, obj);
2235 if (!date)
2236 return NULL;
2238 *date = msec_time;
2239 return obj;
2242 JS_FRIEND_API(JSObject *)
2243 js_NewDateObject(JSContext* cx, int year, int mon, int mday,
2244 int hour, int min, int sec)
2246 JSObject *obj;
2247 jsdouble msec_time;
2249 JS_ASSERT(mon < 12);
2250 msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
2251 obj = js_NewDateObjectMsec(cx, UTC(msec_time));
2252 return obj;
2255 JS_FRIEND_API(JSBool)
2256 js_DateIsValid(JSContext *cx, JSObject* obj)
2258 jsdouble utctime;
2259 return GetUTCTime(cx, obj, NULL, &utctime) && !JSDOUBLE_IS_NaN(utctime);
2262 JS_FRIEND_API(int)
2263 js_DateGetYear(JSContext *cx, JSObject* obj)
2265 jsdouble localtime;
2267 /* Preserve legacy API behavior of returning 0 for invalid dates. */
2268 if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) ||
2269 JSDOUBLE_IS_NaN(localtime)) {
2270 return 0;
2273 return (int) YearFromTime(localtime);
2276 JS_FRIEND_API(int)
2277 js_DateGetMonth(JSContext *cx, JSObject* obj)
2279 jsdouble localtime;
2281 if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) ||
2282 JSDOUBLE_IS_NaN(localtime)) {
2283 return 0;
2286 return (int) MonthFromTime(localtime);
2289 JS_FRIEND_API(int)
2290 js_DateGetDate(JSContext *cx, JSObject* obj)
2292 jsdouble localtime;
2294 if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) ||
2295 JSDOUBLE_IS_NaN(localtime)) {
2296 return 0;
2299 return (int) DateFromTime(localtime);
2302 JS_FRIEND_API(int)
2303 js_DateGetHours(JSContext *cx, JSObject* obj)
2305 jsdouble localtime;
2307 if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) ||
2308 JSDOUBLE_IS_NaN(localtime)) {
2309 return 0;
2312 return (int) HourFromTime(localtime);
2315 JS_FRIEND_API(int)
2316 js_DateGetMinutes(JSContext *cx, JSObject* obj)
2318 jsdouble localtime;
2320 if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) ||
2321 JSDOUBLE_IS_NaN(localtime)) {
2322 return 0;
2325 return (int) MinFromTime(localtime);
2328 JS_FRIEND_API(int)
2329 js_DateGetSeconds(JSContext *cx, JSObject* obj)
2331 jsdouble utctime;
2333 if (!GetUTCTime(cx, obj, NULL, &utctime) || JSDOUBLE_IS_NaN(utctime))
2334 return 0;
2336 return (int) SecFromTime(utctime);
2339 JS_FRIEND_API(void)
2340 js_DateSetYear(JSContext *cx, JSObject *obj, int year)
2342 jsdouble local;
2344 if (!GetAndCacheLocalTime(cx, obj, NULL, &local))
2345 return;
2347 /* reset date if it was NaN */
2348 if (JSDOUBLE_IS_NaN(local))
2349 local = 0;
2351 local = date_msecFromDate(year,
2352 MonthFromTime(local),
2353 DateFromTime(local),
2354 HourFromTime(local),
2355 MinFromTime(local),
2356 SecFromTime(local),
2357 msFromTime(local));
2359 /* SetUTCTime also invalidates local time cache. */
2360 SetUTCTime(cx, obj, NULL, UTC(local));
2363 JS_FRIEND_API(void)
2364 js_DateSetMonth(JSContext *cx, JSObject *obj, int month)
2366 jsdouble local;
2368 JS_ASSERT(month < 12);
2370 if (!GetAndCacheLocalTime(cx, obj, NULL, &local))
2371 return;
2373 /* bail if date was NaN */
2374 if (JSDOUBLE_IS_NaN(local))
2375 return;
2377 local = date_msecFromDate(YearFromTime(local),
2378 month,
2379 DateFromTime(local),
2380 HourFromTime(local),
2381 MinFromTime(local),
2382 SecFromTime(local),
2383 msFromTime(local));
2384 SetUTCTime(cx, obj, NULL, UTC(local));
2387 JS_FRIEND_API(void)
2388 js_DateSetDate(JSContext *cx, JSObject *obj, int date)
2390 jsdouble local;
2392 if (!GetAndCacheLocalTime(cx, obj, NULL, &local))
2393 return;
2395 if (JSDOUBLE_IS_NaN(local))
2396 return;
2398 local = date_msecFromDate(YearFromTime(local),
2399 MonthFromTime(local),
2400 date,
2401 HourFromTime(local),
2402 MinFromTime(local),
2403 SecFromTime(local),
2404 msFromTime(local));
2405 SetUTCTime(cx, obj, NULL, UTC(local));
2408 JS_FRIEND_API(void)
2409 js_DateSetHours(JSContext *cx, JSObject *obj, int hours)
2411 jsdouble local;
2413 if (!GetAndCacheLocalTime(cx, obj, NULL, &local))
2414 return;
2416 if (JSDOUBLE_IS_NaN(local))
2417 return;
2418 local = date_msecFromDate(YearFromTime(local),
2419 MonthFromTime(local),
2420 DateFromTime(local),
2421 hours,
2422 MinFromTime(local),
2423 SecFromTime(local),
2424 msFromTime(local));
2425 SetUTCTime(cx, obj, NULL, UTC(local));
2428 JS_FRIEND_API(void)
2429 js_DateSetMinutes(JSContext *cx, JSObject *obj, int minutes)
2431 jsdouble local;
2433 if (!GetAndCacheLocalTime(cx, obj, NULL, &local))
2434 return;
2436 if (JSDOUBLE_IS_NaN(local))
2437 return;
2438 local = date_msecFromDate(YearFromTime(local),
2439 MonthFromTime(local),
2440 DateFromTime(local),
2441 HourFromTime(local),
2442 minutes,
2443 SecFromTime(local),
2444 msFromTime(local));
2445 SetUTCTime(cx, obj, NULL, UTC(local));
2448 JS_FRIEND_API(void)
2449 js_DateSetSeconds(JSContext *cx, JSObject *obj, int seconds)
2451 jsdouble local;
2453 if (!GetAndCacheLocalTime(cx, obj, NULL, &local))
2454 return;
2456 if (JSDOUBLE_IS_NaN(local))
2457 return;
2458 local = date_msecFromDate(YearFromTime(local),
2459 MonthFromTime(local),
2460 DateFromTime(local),
2461 HourFromTime(local),
2462 MinFromTime(local),
2463 seconds,
2464 msFromTime(local));
2465 SetUTCTime(cx, obj, NULL, UTC(local));
2468 JS_FRIEND_API(jsdouble)
2469 js_DateGetMsecSinceEpoch(JSContext *cx, JSObject *obj)
2471 jsdouble utctime;
2472 if (!GetUTCTime(cx, obj, NULL, &utctime))
2473 return 0;
2474 return utctime;