Save all modification
[mozilla-1.9/m8.git] / js / src / jsdate.c
blob4a5229013612cc23ad1ffd734613ecc52556b76e
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 "jsconfig.h"
66 #include "jscntxt.h"
67 #include "jsdate.h"
68 #include "jsinterp.h"
69 #include "jsnum.h"
70 #include "jsobj.h"
71 #include "jsstr.h"
74 * The JS 'Date' object is patterned after the Java 'Date' object.
75 * Here is an script:
77 * today = new Date();
79 * print(today.toLocaleString());
81 * weekDay = today.getDay();
84 * These Java (and ECMA-262) methods are supported:
86 * UTC
87 * getDate (getUTCDate)
88 * getDay (getUTCDay)
89 * getHours (getUTCHours)
90 * getMinutes (getUTCMinutes)
91 * getMonth (getUTCMonth)
92 * getSeconds (getUTCSeconds)
93 * getMilliseconds (getUTCMilliseconds)
94 * getTime
95 * getTimezoneOffset
96 * getYear
97 * getFullYear (getUTCFullYear)
98 * parse
99 * setDate (setUTCDate)
100 * setHours (setUTCHours)
101 * setMinutes (setUTCMinutes)
102 * setMonth (setUTCMonth)
103 * setSeconds (setUTCSeconds)
104 * setMilliseconds (setUTCMilliseconds)
105 * setTime
106 * setYear (setFullYear, setUTCFullYear)
107 * toGMTString (toUTCString)
108 * toLocaleString
109 * toString
112 * These Java methods are not supported
114 * setDay
115 * before
116 * after
117 * equals
118 * hashCode
122 * 11/97 - jsdate.c has been rewritten to conform to the ECMA-262 language
123 * definition and reduce dependence on NSPR. NSPR is used to get the current
124 * time in milliseconds, the time zone offset, and the daylight savings time
125 * offset for a given time. NSPR is also used for Date.toLocaleString(), for
126 * locale-specific formatting, and to get a string representing the timezone.
127 * (Which turns out to be platform-dependent.)
129 * To do:
130 * (I did some performance tests by timing how long it took to run what
131 * I had of the js ECMA conformance tests.)
133 * - look at saving results across multiple calls to supporting
134 * functions; the toString functions compute some of the same values
135 * multiple times. Although - I took a quick stab at this, and I lost
136 * rather than gained. (Fractionally.) Hard to tell what compilers/processors
137 * are doing these days.
139 * - look at tweaking function return types to return double instead
140 * of int; this seems to make things run slightly faster sometimes.
141 * (though it could be architecture-dependent.) It'd be good to see
142 * how this does on win32. (Tried it on irix.) Types could use a
143 * general going-over.
147 * Supporting functions - ECMA 15.9.1.*
150 #define HalfTimeDomain 8.64e15
151 #define HoursPerDay 24.0
152 #define MinutesPerDay (HoursPerDay * MinutesPerHour)
153 #define MinutesPerHour 60.0
154 #define SecondsPerDay (MinutesPerDay * SecondsPerMinute)
155 #define SecondsPerHour (MinutesPerHour * SecondsPerMinute)
156 #define SecondsPerMinute 60.0
158 #if defined(XP_WIN) || defined(XP_OS2)
159 /* Work around msvc double optimization bug by making these runtime values; if
160 * they're available at compile time, msvc optimizes division by them by
161 * computing the reciprocal and multiplying instead of dividing - this loses
162 * when the reciprocal isn't representable in a double.
164 static jsdouble msPerSecond = 1000.0;
165 static jsdouble msPerDay = SecondsPerDay * 1000.0;
166 static jsdouble msPerHour = SecondsPerHour * 1000.0;
167 static jsdouble msPerMinute = SecondsPerMinute * 1000.0;
168 #else
169 #define msPerDay (SecondsPerDay * msPerSecond)
170 #define msPerHour (SecondsPerHour * msPerSecond)
171 #define msPerMinute (SecondsPerMinute * msPerSecond)
172 #define msPerSecond 1000.0
173 #endif
175 #define Day(t) floor((t) / msPerDay)
177 static jsdouble
178 TimeWithinDay(jsdouble t)
180 jsdouble result;
181 result = fmod(t, msPerDay);
182 if (result < 0)
183 result += msPerDay;
184 return result;
187 #define DaysInYear(y) ((y) % 4 == 0 && ((y) % 100 || ((y) % 400 == 0)) \
188 ? 366 : 365)
190 /* math here has to be f.p, because we need
191 * floor((1968 - 1969) / 4) == -1
193 #define DayFromYear(y) (365 * ((y)-1970) + floor(((y)-1969)/4.0) \
194 - floor(((y)-1901)/100.0) + floor(((y)-1601)/400.0))
195 #define TimeFromYear(y) (DayFromYear(y) * msPerDay)
197 static jsint
198 YearFromTime(jsdouble t)
200 jsint y = (jsint) floor(t /(msPerDay*365.2425)) + 1970;
201 jsdouble t2 = (jsdouble) TimeFromYear(y);
203 if (t2 > t) {
204 y--;
205 } else {
206 if (t2 + msPerDay * DaysInYear(y) <= t)
207 y++;
209 return y;
212 #define InLeapYear(t) (JSBool) (DaysInYear(YearFromTime(t)) == 366)
214 #define DayWithinYear(t, year) ((intN) (Day(t) - DayFromYear(year)))
217 * The following array contains the day of year for the first day of
218 * each month, where index 0 is January, and day 0 is January 1.
220 static jsdouble firstDayOfMonth[2][12] = {
221 {0.0, 31.0, 59.0, 90.0, 120.0, 151.0, 181.0, 212.0, 243.0, 273.0, 304.0, 334.0},
222 {0.0, 31.0, 60.0, 91.0, 121.0, 152.0, 182.0, 213.0, 244.0, 274.0, 305.0, 335.0}
225 #define DayFromMonth(m, leap) firstDayOfMonth[leap][(intN)m];
227 static intN
228 MonthFromTime(jsdouble t)
230 intN d, step;
231 jsint year = YearFromTime(t);
232 d = DayWithinYear(t, year);
234 if (d < (step = 31))
235 return 0;
236 step += (InLeapYear(t) ? 29 : 28);
237 if (d < step)
238 return 1;
239 if (d < (step += 31))
240 return 2;
241 if (d < (step += 30))
242 return 3;
243 if (d < (step += 31))
244 return 4;
245 if (d < (step += 30))
246 return 5;
247 if (d < (step += 31))
248 return 6;
249 if (d < (step += 31))
250 return 7;
251 if (d < (step += 30))
252 return 8;
253 if (d < (step += 31))
254 return 9;
255 if (d < (step += 30))
256 return 10;
257 return 11;
260 static intN
261 DateFromTime(jsdouble t)
263 intN d, step, next;
264 jsint year = YearFromTime(t);
265 d = DayWithinYear(t, year);
267 if (d <= (next = 30))
268 return d + 1;
269 step = next;
270 next += (InLeapYear(t) ? 29 : 28);
271 if (d <= next)
272 return d - step;
273 step = next;
274 if (d <= (next += 31))
275 return d - step;
276 step = next;
277 if (d <= (next += 30))
278 return d - step;
279 step = next;
280 if (d <= (next += 31))
281 return d - step;
282 step = next;
283 if (d <= (next += 30))
284 return d - step;
285 step = next;
286 if (d <= (next += 31))
287 return d - step;
288 step = next;
289 if (d <= (next += 31))
290 return d - step;
291 step = next;
292 if (d <= (next += 30))
293 return d - step;
294 step = next;
295 if (d <= (next += 31))
296 return d - step;
297 step = next;
298 if (d <= (next += 30))
299 return d - step;
300 step = next;
301 return d - step;
304 static intN
305 WeekDay(jsdouble t)
307 jsint result;
308 result = (jsint) Day(t) + 4;
309 result = result % 7;
310 if (result < 0)
311 result += 7;
312 return (intN) result;
315 #define MakeTime(hour, min, sec, ms) \
316 ((((hour) * MinutesPerHour + (min)) * SecondsPerMinute + (sec)) * msPerSecond + (ms))
318 static jsdouble
319 MakeDay(jsdouble year, jsdouble month, jsdouble date)
321 JSBool leap;
322 jsdouble yearday;
323 jsdouble monthday;
325 year += floor(month / 12);
327 month = fmod(month, 12.0);
328 if (month < 0)
329 month += 12;
331 leap = (DaysInYear((jsint) year) == 366);
333 yearday = floor(TimeFromYear(year) / msPerDay);
334 monthday = DayFromMonth(month, leap);
336 return yearday + monthday + date - 1;
339 #define MakeDate(day, time) ((day) * msPerDay + (time))
342 * Years and leap years on which Jan 1 is a Sunday, Monday, etc.
344 * yearStartingWith[0][i] is an example non-leap year where
345 * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
347 * yearStartingWith[1][i] is an example leap year where
348 * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
350 static jsint yearStartingWith[2][7] = {
351 {1978, 1973, 1974, 1975, 1981, 1971, 1977},
352 {1984, 1996, 1980, 1992, 1976, 1988, 1972}
356 * Find a year for which any given date will fall on the same weekday.
358 * This function should be used with caution when used other than
359 * for determining DST; it hasn't been proven not to produce an
360 * incorrect year for times near year boundaries.
362 static jsint
363 EquivalentYearForDST(jsint year)
365 jsint day;
366 JSBool isLeapYear;
368 day = (jsint) DayFromYear(year) + 4;
369 day = day % 7;
370 if (day < 0)
371 day += 7;
373 isLeapYear = (DaysInYear(year) == 366);
375 return yearStartingWith[isLeapYear][day];
378 /* LocalTZA gets set by js_InitDateClass() */
379 static jsdouble LocalTZA;
381 static jsdouble
382 DaylightSavingTA(jsdouble t)
384 volatile int64 PR_t;
385 int64 ms2us;
386 int64 offset;
387 jsdouble result;
389 /* abort if NaN */
390 if (JSDOUBLE_IS_NaN(t))
391 return t;
394 * If earlier than 1970 or after 2038, potentially beyond the ken of
395 * many OSes, map it to an equivalent year before asking.
397 if (t < 0.0 || t > 2145916800000.0) {
398 jsint year;
399 jsdouble day;
401 year = EquivalentYearForDST(YearFromTime(t));
402 day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
403 t = MakeDate(day, TimeWithinDay(t));
406 /* put our t in an LL, and map it to usec for prtime */
407 JSLL_D2L(PR_t, t);
408 JSLL_I2L(ms2us, PRMJ_USEC_PER_MSEC);
409 JSLL_MUL(PR_t, PR_t, ms2us);
411 offset = PRMJ_DSTOffset(PR_t);
413 JSLL_DIV(offset, offset, ms2us);
414 JSLL_L2D(result, offset);
415 return result;
419 #define AdjustTime(t) fmod(LocalTZA + DaylightSavingTA(t), msPerDay)
421 #define LocalTime(t) ((t) + AdjustTime(t))
423 static jsdouble
424 UTC(jsdouble t)
426 return t - AdjustTime(t - LocalTZA);
429 static intN
430 HourFromTime(jsdouble t)
432 intN result = (intN) fmod(floor(t/msPerHour), HoursPerDay);
433 if (result < 0)
434 result += (intN)HoursPerDay;
435 return result;
438 static intN
439 MinFromTime(jsdouble t)
441 intN result = (intN) fmod(floor(t / msPerMinute), MinutesPerHour);
442 if (result < 0)
443 result += (intN)MinutesPerHour;
444 return result;
447 static intN
448 SecFromTime(jsdouble t)
450 intN result = (intN) fmod(floor(t / msPerSecond), SecondsPerMinute);
451 if (result < 0)
452 result += (intN)SecondsPerMinute;
453 return result;
456 static intN
457 msFromTime(jsdouble t)
459 intN result = (intN) fmod(t, msPerSecond);
460 if (result < 0)
461 result += (intN)msPerSecond;
462 return result;
465 #define TIMECLIP(d) ((JSDOUBLE_IS_FINITE(d) \
466 && !((d < 0 ? -d : d) > HalfTimeDomain)) \
467 ? js_DoubleToInteger(d + (+0.)) : *cx->runtime->jsNaN)
470 * end of ECMA 'support' functions
474 * Other Support routines and definitions
478 * We use the first reseved slot to store UTC time, and the second for caching
479 * the local time. The initial value of the cache entry is NaN.
481 #define UTC_TIME_SLOT 0
482 #define LOCAL_TIME_SLOT 1
484 JSClass js_DateClass = {
485 js_Date_str,
486 JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_HAS_CACHED_PROTO(JSProto_Date),
487 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
488 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
489 JSCLASS_NO_OPTIONAL_MEMBERS
492 /* for use by date_parse */
494 static const char* wtb[] = {
495 "am", "pm",
496 "monday", "tuesday", "wednesday", "thursday", "friday",
497 "saturday", "sunday",
498 "january", "february", "march", "april", "may", "june",
499 "july", "august", "september", "october", "november", "december",
500 "gmt", "ut", "utc",
501 "est", "edt",
502 "cst", "cdt",
503 "mst", "mdt",
504 "pst", "pdt"
505 /* time zone table needs to be expanded */
508 static int ttb[] = {
509 -1, -2, 0, 0, 0, 0, 0, 0, 0, /* AM/PM */
510 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
511 10000 + 0, 10000 + 0, 10000 + 0, /* GMT/UT/UTC */
512 10000 + 5 * 60, 10000 + 4 * 60, /* EST/EDT */
513 10000 + 6 * 60, 10000 + 5 * 60, /* CST/CDT */
514 10000 + 7 * 60, 10000 + 6 * 60, /* MST/MDT */
515 10000 + 8 * 60, 10000 + 7 * 60 /* PST/PDT */
518 /* helper for date_parse */
519 static JSBool
520 date_regionMatches(const char* s1, int s1off, const jschar* s2, int s2off,
521 int count, int ignoreCase)
523 JSBool result = JS_FALSE;
524 /* return true if matches, otherwise, false */
526 while (count > 0 && s1[s1off] && s2[s2off]) {
527 if (ignoreCase) {
528 if (JS_TOLOWER((jschar)s1[s1off]) != JS_TOLOWER(s2[s2off])) {
529 break;
531 } else {
532 if ((jschar)s1[s1off] != s2[s2off]) {
533 break;
536 s1off++;
537 s2off++;
538 count--;
541 if (count == 0) {
542 result = JS_TRUE;
545 return result;
548 /* find UTC time from given date... no 1900 correction! */
549 static jsdouble
550 date_msecFromDate(jsdouble year, jsdouble mon, jsdouble mday, jsdouble hour,
551 jsdouble min, jsdouble sec, jsdouble msec)
553 jsdouble day;
554 jsdouble msec_time;
555 jsdouble result;
557 day = MakeDay(year, mon, mday);
558 msec_time = MakeTime(hour, min, sec, msec);
559 result = MakeDate(day, msec_time);
560 return result;
563 /* compute the time in msec (unclipped) from the given args */
564 #define MAXARGS 7
566 static JSBool
567 date_msecFromArgs(JSContext *cx, uintN argc, jsval *argv, jsdouble *rval)
569 uintN loop;
570 jsdouble array[MAXARGS];
571 jsdouble d;
572 jsdouble msec_time;
574 for (loop = 0; loop < MAXARGS; loop++) {
575 if (loop < argc) {
576 if (!js_ValueToNumber(cx, argv[loop], &d))
577 return JS_FALSE;
578 /* return NaN if any arg is not finite */
579 if (!JSDOUBLE_IS_FINITE(d)) {
580 *rval = *cx->runtime->jsNaN;
581 return JS_TRUE;
583 array[loop] = js_DoubleToInteger(d);
584 } else {
585 if (loop == 2) {
586 array[loop] = 1; /* Default the date argument to 1. */
587 } else {
588 array[loop] = 0;
593 /* adjust 2-digit years into the 20th century */
594 if (array[0] >= 0 && array[0] <= 99)
595 array[0] += 1900;
597 msec_time = date_msecFromDate(array[0], array[1], array[2],
598 array[3], array[4], array[5], array[6]);
599 *rval = msec_time;
600 return JS_TRUE;
605 * See ECMA 15.9.4.[3-10];
607 static JSBool
608 date_UTC(JSContext *cx, uintN argc, jsval *vp)
610 jsdouble msec_time;
612 if (!date_msecFromArgs(cx, argc, vp + 2, &msec_time))
613 return JS_FALSE;
615 msec_time = TIMECLIP(msec_time);
617 return js_NewNumberValue(cx, msec_time, vp);
620 static JSBool
621 date_parseString(JSString *str, jsdouble *result)
623 jsdouble msec;
625 const jschar *s;
626 size_t limit;
627 size_t i = 0;
628 int year = -1;
629 int mon = -1;
630 int mday = -1;
631 int hour = -1;
632 int min = -1;
633 int sec = -1;
634 int c = -1;
635 int n = -1;
636 int tzoffset = -1;
637 int prevc = 0;
638 JSBool seenplusminus = JS_FALSE;
639 int temp;
640 JSBool seenmonthname = JS_FALSE;
642 JSSTRING_CHARS_AND_LENGTH(str, s, limit);
643 if (limit == 0)
644 goto syntax;
645 while (i < limit) {
646 c = s[i];
647 i++;
648 if (c <= ' ' || c == ',' || c == '-') {
649 if (c == '-' && '0' <= s[i] && s[i] <= '9') {
650 prevc = c;
652 continue;
654 if (c == '(') { /* comments) */
655 int depth = 1;
656 while (i < limit) {
657 c = s[i];
658 i++;
659 if (c == '(') depth++;
660 else if (c == ')')
661 if (--depth <= 0)
662 break;
664 continue;
666 if ('0' <= c && c <= '9') {
667 n = c - '0';
668 while (i < limit && '0' <= (c = s[i]) && c <= '9') {
669 n = n * 10 + c - '0';
670 i++;
673 /* allow TZA before the year, so
674 * 'Wed Nov 05 21:49:11 GMT-0800 1997'
675 * works */
677 /* uses of seenplusminus allow : in TZA, so Java
678 * no-timezone style of GMT+4:30 works
681 if ((prevc == '+' || prevc == '-')/* && year>=0 */) {
682 /* make ':' case below change tzoffset */
683 seenplusminus = JS_TRUE;
685 /* offset */
686 if (n < 24)
687 n = n * 60; /* EG. "GMT-3" */
688 else
689 n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */
690 if (prevc == '+') /* plus means east of GMT */
691 n = -n;
692 if (tzoffset != 0 && tzoffset != -1)
693 goto syntax;
694 tzoffset = n;
695 } else if (prevc == '/' && mon >= 0 && mday >= 0 && year < 0) {
696 if (c <= ' ' || c == ',' || c == '/' || i >= limit)
697 year = n;
698 else
699 goto syntax;
700 } else if (c == ':') {
701 if (hour < 0)
702 hour = /*byte*/ n;
703 else if (min < 0)
704 min = /*byte*/ n;
705 else
706 goto syntax;
707 } else if (c == '/') {
708 /* until it is determined that mon is the actual
709 month, keep it as 1-based rather than 0-based */
710 if (mon < 0)
711 mon = /*byte*/ n;
712 else if (mday < 0)
713 mday = /*byte*/ n;
714 else
715 goto syntax;
716 } else if (i < limit && c != ',' && c > ' ' && c != '-' && c != '(') {
717 goto syntax;
718 } else if (seenplusminus && n < 60) { /* handle GMT-3:30 */
719 if (tzoffset < 0)
720 tzoffset -= n;
721 else
722 tzoffset += n;
723 } else if (hour >= 0 && min < 0) {
724 min = /*byte*/ n;
725 } else if (prevc == ':' && min >= 0 && sec < 0) {
726 sec = /*byte*/ n;
727 } else if (mon < 0) {
728 mon = /*byte*/n;
729 } else if (mon >= 0 && mday < 0) {
730 mday = /*byte*/ n;
731 } else if (mon >= 0 && mday >= 0 && year < 0) {
732 year = n;
733 } else {
734 goto syntax;
736 prevc = 0;
737 } else if (c == '/' || c == ':' || c == '+' || c == '-') {
738 prevc = c;
739 } else {
740 size_t st = i - 1;
741 int k;
742 while (i < limit) {
743 c = s[i];
744 if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')))
745 break;
746 i++;
748 if (i <= st + 1)
749 goto syntax;
750 for (k = (sizeof(wtb)/sizeof(char*)); --k >= 0;)
751 if (date_regionMatches(wtb[k], 0, s, st, i-st, 1)) {
752 int action = ttb[k];
753 if (action != 0) {
754 if (action < 0) {
756 * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as
757 * 12:30, instead of blindly adding 12 if PM.
759 JS_ASSERT(action == -1 || action == -2);
760 if (hour > 12 || hour < 0) {
761 goto syntax;
762 } else {
763 if (action == -1 && hour == 12) { /* am */
764 hour = 0;
765 } else if (action == -2 && hour != 12) { /* pm */
766 hour += 12;
769 } else if (action <= 13) { /* month! */
770 /* Adjust mon to be 1-based until the final values
771 for mon, mday and year are adjusted below */
772 if (seenmonthname) {
773 goto syntax;
775 seenmonthname = JS_TRUE;
776 temp = /*byte*/ (action - 2) + 1;
778 if (mon < 0) {
779 mon = temp;
780 } else if (mday < 0) {
781 mday = mon;
782 mon = temp;
783 } else if (year < 0) {
784 year = mon;
785 mon = temp;
786 } else {
787 goto syntax;
789 } else {
790 tzoffset = action - 10000;
793 break;
795 if (k < 0)
796 goto syntax;
797 prevc = 0;
800 if (year < 0 || mon < 0 || mday < 0)
801 goto syntax;
803 Case 1. The input string contains an English month name.
804 The form of the string can be month f l, or f month l, or
805 f l month which each evaluate to the same date.
806 If f and l are both greater than or equal to 70, or
807 both less than 70, the date is invalid.
808 The year is taken to be the greater of the values f, l.
809 If the year is greater than or equal to 70 and less than 100,
810 it is considered to be the number of years after 1900.
811 Case 2. The input string is of the form "f/m/l" where f, m and l are
812 integers, e.g. 7/16/45.
813 Adjust the mon, mday and year values to achieve 100% MSIE
814 compatibility.
815 a. If 0 <= f < 70, f/m/l is interpreted as month/day/year.
816 i. If year < 100, it is the number of years after 1900
817 ii. If year >= 100, it is the number of years after 0.
818 b. If 70 <= f < 100
819 i. If m < 70, f/m/l is interpreted as
820 year/month/day where year is the number of years after
821 1900.
822 ii. If m >= 70, the date is invalid.
823 c. If f >= 100
824 i. If m < 70, f/m/l is interpreted as
825 year/month/day where year is the number of years after 0.
826 ii. If m >= 70, the date is invalid.
828 if (seenmonthname) {
829 if ((mday >= 70 && year >= 70) || (mday < 70 && year < 70)) {
830 goto syntax;
832 if (mday > year) {
833 temp = year;
834 year = mday;
835 mday = temp;
837 if (year >= 70 && year < 100) {
838 year += 1900;
840 } else if (mon < 70) { /* (a) month/day/year */
841 if (year < 100) {
842 year += 1900;
844 } else if (mon < 100) { /* (b) year/month/day */
845 if (mday < 70) {
846 temp = year;
847 year = mon + 1900;
848 mon = mday;
849 mday = temp;
850 } else {
851 goto syntax;
853 } else { /* (c) year/month/day */
854 if (mday < 70) {
855 temp = year;
856 year = mon;
857 mon = mday;
858 mday = temp;
859 } else {
860 goto syntax;
863 mon -= 1; /* convert month to 0-based */
864 if (sec < 0)
865 sec = 0;
866 if (min < 0)
867 min = 0;
868 if (hour < 0)
869 hour = 0;
870 if (tzoffset == -1) { /* no time zone specified, have to use local */
871 jsdouble msec_time;
872 msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
874 *result = UTC(msec_time);
875 return JS_TRUE;
878 msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
879 msec += tzoffset * msPerMinute;
880 *result = msec;
881 return JS_TRUE;
883 syntax:
884 /* syntax error */
885 *result = 0;
886 return JS_FALSE;
889 static JSBool
890 date_parse(JSContext *cx, uintN argc, jsval *vp)
892 JSString *str;
893 jsdouble result;
895 str = js_ValueToString(cx, vp[2]);
896 if (!str)
897 return JS_FALSE;
898 if (!date_parseString(str, &result)) {
899 *vp = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
900 return JS_TRUE;
903 result = TIMECLIP(result);
904 return js_NewNumberValue(cx, result, vp);
907 static JSBool
908 date_now(JSContext *cx, uintN argc, jsval *vp)
910 int64 us, ms, us2ms;
911 jsdouble msec_time;
913 us = PRMJ_Now();
914 JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC);
915 JSLL_DIV(ms, us, us2ms);
916 JSLL_L2D(msec_time, ms);
918 return js_NewDoubleValue(cx, msec_time, vp);
922 * Get UTC time from the date object. Returns false if the object is not
923 * Date type.
925 static JSBool
926 GetUTCTime(JSContext *cx, JSObject *obj, jsval *vp, jsdouble *dp)
928 jsval v;
930 if (vp && !JS_InstanceOf(cx, obj, &js_DateClass, vp + 2))
931 return JS_FALSE;
932 if (!JS_GetReservedSlot(cx, obj, UTC_TIME_SLOT, &v))
933 return JS_FALSE;
935 *dp = *JSVAL_TO_DOUBLE(v);
936 return JS_TRUE;
940 * Set UTC time slot with a pointer pointing to a jsdouble. This function is
941 * used only for setting UTC time to some predefined values, such as NaN.
943 * It also invalidates cached local time.
945 static JSBool
946 SetUTCTimePtr(JSContext *cx, JSObject *obj, jsval *vp, jsdouble *dp)
948 if (vp && !JS_InstanceOf(cx, obj, &js_DateClass, vp + 2))
949 return JS_FALSE;
951 /* Invalidate local time cache. */
952 if (!JS_SetReservedSlot(cx, obj, LOCAL_TIME_SLOT,
953 DOUBLE_TO_JSVAL(cx->runtime->jsNaN))) {
954 return JS_FALSE;
957 return JS_SetReservedSlot(cx, obj, UTC_TIME_SLOT, DOUBLE_TO_JSVAL(dp));
961 * Set UTC time to a given time.
963 static JSBool
964 SetUTCTime(JSContext *cx, JSObject *obj, jsval *vp, jsdouble t)
966 jsdouble *dp = js_NewDouble(cx, t, 0);
967 if (!dp)
968 return JS_FALSE;
969 return SetUTCTimePtr(cx, obj, vp, dp);
973 * Get the local time, cache it if necessary. If UTC time is not finite
974 * (e.g., NaN), the local time slot is set to the UTC time without conversion.
976 static JSBool
977 GetLocalTime(JSContext *cx, JSObject *obj, jsval *vp, jsdouble *dp)
979 jsval v;
980 jsdouble result;
981 jsdouble *cached;
983 if (!JS_GetReservedSlot(cx, obj, LOCAL_TIME_SLOT, &v))
984 return JS_FALSE;
986 result = *JSVAL_TO_DOUBLE(v);
988 if (JSDOUBLE_IS_NaN(result)) {
989 if (!GetUTCTime(cx, obj, vp, &result))
990 return JS_FALSE;
992 /* if result is NaN, it couldn't be finite. */
993 if (JSDOUBLE_IS_FINITE(result))
994 result = LocalTime(result);
996 cached = js_NewDouble(cx, result, 0);
997 if (!cached)
998 return JS_FALSE;
1000 if (!JS_SetReservedSlot(cx, obj, LOCAL_TIME_SLOT,
1001 DOUBLE_TO_JSVAL(cached))) {
1002 return JS_FALSE;
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, JSVAL_TO_OBJECT(vp[1]), vp, &result) &&
1019 js_NewNumberValue(cx, result, vp);
1022 static JSBool
1023 GetYear(JSContext *cx, JSBool fullyear, jsval *vp)
1025 jsdouble result;
1027 if (!GetLocalTime(cx, JSVAL_TO_OBJECT(vp[1]), 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_NewNumberValue(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, JSVAL_TO_OBJECT(vp[1]), vp, &result))
1059 return JS_FALSE;
1061 if (JSDOUBLE_IS_FINITE(result))
1062 result = YearFromTime(result);
1064 return js_NewNumberValue(cx, result, vp);
1067 static JSBool
1068 date_getMonth(JSContext *cx, uintN argc, jsval *vp)
1070 jsdouble result;
1072 if (!GetLocalTime(cx, JSVAL_TO_OBJECT(vp[1]), vp, &result))
1073 return JS_FALSE;
1075 if (JSDOUBLE_IS_FINITE(result))
1076 result = MonthFromTime(result);
1078 return js_NewNumberValue(cx, result, vp);
1081 static JSBool
1082 date_getUTCMonth(JSContext *cx, uintN argc, jsval *vp)
1084 jsdouble result;
1086 if (!GetUTCTime(cx, JSVAL_TO_OBJECT(vp[1]), vp, &result))
1087 return JS_FALSE;
1089 if (JSDOUBLE_IS_FINITE(result))
1090 result = MonthFromTime(result);
1092 return js_NewNumberValue(cx, result, vp);
1095 static JSBool
1096 date_getDate(JSContext *cx, uintN argc, jsval *vp)
1098 jsdouble result;
1100 if (!GetLocalTime(cx, JSVAL_TO_OBJECT(vp[1]), vp, &result))
1101 return JS_FALSE;
1103 if (JSDOUBLE_IS_FINITE(result))
1104 result = DateFromTime(result);
1106 return js_NewNumberValue(cx, result, vp);
1109 static JSBool
1110 date_getUTCDate(JSContext *cx, uintN argc, jsval *vp)
1112 jsdouble result;
1114 if (!GetUTCTime(cx, JSVAL_TO_OBJECT(vp[1]), vp, &result))
1115 return JS_FALSE;
1117 if (JSDOUBLE_IS_FINITE(result))
1118 result = DateFromTime(result);
1120 return js_NewNumberValue(cx, result, vp);
1123 static JSBool
1124 date_getDay(JSContext *cx, uintN argc, jsval *vp)
1126 jsdouble result;
1128 if (!GetLocalTime(cx, JSVAL_TO_OBJECT(vp[1]), vp, &result))
1129 return JS_FALSE;
1131 if (JSDOUBLE_IS_FINITE(result))
1132 result = WeekDay(result);
1134 return js_NewNumberValue(cx, result, vp);
1137 static JSBool
1138 date_getUTCDay(JSContext *cx, uintN argc, jsval *vp)
1140 jsdouble result;
1142 if (!GetUTCTime(cx, JSVAL_TO_OBJECT(vp[1]), vp, &result))
1143 return JS_FALSE;
1145 if (JSDOUBLE_IS_FINITE(result))
1146 result = WeekDay(result);
1148 return js_NewNumberValue(cx, result, vp);
1151 static JSBool
1152 date_getHours(JSContext *cx, uintN argc, jsval *vp)
1154 jsdouble result;
1156 if (!GetLocalTime(cx, JSVAL_TO_OBJECT(vp[1]), vp, &result))
1157 return JS_FALSE;
1159 if (JSDOUBLE_IS_FINITE(result))
1160 result = HourFromTime(result);
1162 return js_NewNumberValue(cx, result, vp);
1165 static JSBool
1166 date_getUTCHours(JSContext *cx, uintN argc, jsval *vp)
1168 jsdouble result;
1170 if (!GetUTCTime(cx, JSVAL_TO_OBJECT(vp[1]), vp, &result))
1171 return JS_FALSE;
1173 if (JSDOUBLE_IS_FINITE(result))
1174 result = HourFromTime(result);
1176 return js_NewNumberValue(cx, result, vp);
1179 static JSBool
1180 date_getMinutes(JSContext *cx, uintN argc, jsval *vp)
1182 jsdouble result;
1184 if (!GetLocalTime(cx, JSVAL_TO_OBJECT(vp[1]), vp, &result))
1185 return JS_FALSE;
1187 if (JSDOUBLE_IS_FINITE(result))
1188 result = MinFromTime(result);
1190 return js_NewNumberValue(cx, result, vp);
1193 static JSBool
1194 date_getUTCMinutes(JSContext *cx, uintN argc, jsval *vp)
1196 jsdouble result;
1198 if (!GetUTCTime(cx, JSVAL_TO_OBJECT(vp[1]), vp, &result))
1199 return JS_FALSE;
1201 if (JSDOUBLE_IS_FINITE(result))
1202 result = MinFromTime(result);
1204 return js_NewNumberValue(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, JSVAL_TO_OBJECT(vp[1]), vp, &result))
1215 return JS_FALSE;
1217 if (JSDOUBLE_IS_FINITE(result))
1218 result = SecFromTime(result);
1220 return js_NewNumberValue(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, JSVAL_TO_OBJECT(vp[1]), vp, &result))
1231 return JS_FALSE;
1233 if (JSDOUBLE_IS_FINITE(result))
1234 result = msFromTime(result);
1236 return js_NewNumberValue(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 = JSVAL_TO_OBJECT(vp[1]);
1246 if (!GetUTCTime(cx, obj, vp, &utctime))
1247 return JS_FALSE;
1248 if (!GetLocalTime(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_NewNumberValue(cx, result, vp);
1260 static JSBool
1261 date_setTime(JSContext *cx, uintN argc, jsval *vp)
1263 jsdouble result;
1265 if (!js_ValueToNumber(cx, vp[2], &result))
1266 return JS_FALSE;
1268 result = TIMECLIP(result);
1270 if (!SetUTCTime(cx, JSVAL_TO_OBJECT(vp[1]), vp, result))
1271 return JS_FALSE;
1273 return js_NewNumberValue(cx, result, vp);
1276 static JSBool
1277 date_makeTime(JSContext *cx, uintN maxargs, JSBool local, uintN argc, jsval *vp)
1279 JSObject *obj;
1280 jsval *argv;
1281 uintN i;
1282 jsdouble args[4], *argp, *stop;
1283 jsdouble hour, min, sec, msec;
1284 jsdouble lorutime; /* Local or UTC version of *date */
1286 jsdouble msec_time;
1287 jsdouble result;
1289 obj = JSVAL_TO_OBJECT(vp[1]);
1290 if (!GetUTCTime(cx, obj, vp, &result))
1291 return JS_FALSE;
1293 /* just return NaN if the date is already NaN */
1294 if (!JSDOUBLE_IS_FINITE(result))
1295 return js_NewNumberValue(cx, result, vp);
1298 * Satisfy the ECMA rule that if a function is called with
1299 * fewer arguments than the specified formal arguments, the
1300 * remaining arguments are set to undefined. Seems like all
1301 * the Date.setWhatever functions in ECMA are only varargs
1302 * beyond the first argument; this should be set to undefined
1303 * if it's not given. This means that "d = new Date();
1304 * d.setMilliseconds()" returns NaN. Blech.
1306 if (argc == 0)
1307 argc = 1; /* should be safe, because length of all setters is 1 */
1308 else if (argc > maxargs)
1309 argc = maxargs; /* clamp argc */
1310 JS_ASSERT(1 <= argc && argc <= 4);
1312 argv = vp + 2;
1313 for (i = 0; i < argc; i++) {
1314 if (!js_ValueToNumber(cx, argv[i], &args[i]))
1315 return JS_FALSE;
1316 if (!JSDOUBLE_IS_FINITE(args[i])) {
1317 if (!SetUTCTimePtr(cx, obj, NULL, cx->runtime->jsNaN))
1318 return JS_FALSE;
1319 *vp = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
1320 return JS_TRUE;
1322 args[i] = js_DoubleToInteger(args[i]);
1325 if (local)
1326 lorutime = LocalTime(result);
1327 else
1328 lorutime = result;
1330 argp = args;
1331 stop = argp + argc;
1332 if (maxargs >= 4 && argp < stop)
1333 hour = *argp++;
1334 else
1335 hour = HourFromTime(lorutime);
1337 if (maxargs >= 3 && argp < stop)
1338 min = *argp++;
1339 else
1340 min = MinFromTime(lorutime);
1342 if (maxargs >= 2 && argp < stop)
1343 sec = *argp++;
1344 else
1345 sec = SecFromTime(lorutime);
1347 if (maxargs >= 1 && argp < stop)
1348 msec = *argp;
1349 else
1350 msec = msFromTime(lorutime);
1352 msec_time = MakeTime(hour, min, sec, msec);
1353 result = MakeDate(Day(lorutime), msec_time);
1355 /* fprintf(stderr, "%f\n", result); */
1357 if (local)
1358 result = UTC(result);
1360 /* fprintf(stderr, "%f\n", result); */
1362 result = TIMECLIP(result);
1363 if (!SetUTCTime(cx, obj, NULL, result))
1364 return JS_FALSE;
1366 return js_NewNumberValue(cx, result, vp);
1369 static JSBool
1370 date_setMilliseconds(JSContext *cx, uintN argc, jsval *vp)
1372 return date_makeTime(cx, 1, JS_TRUE, argc, vp);
1375 static JSBool
1376 date_setUTCMilliseconds(JSContext *cx, uintN argc, jsval *vp)
1378 return date_makeTime(cx, 1, JS_FALSE, argc, vp);
1381 static JSBool
1382 date_setSeconds(JSContext *cx, uintN argc, jsval *vp)
1384 return date_makeTime(cx, 2, JS_TRUE, argc, vp);
1387 static JSBool
1388 date_setUTCSeconds(JSContext *cx, uintN argc, jsval *vp)
1390 return date_makeTime(cx, 2, JS_FALSE, argc, vp);
1393 static JSBool
1394 date_setMinutes(JSContext *cx, uintN argc, jsval *vp)
1396 return date_makeTime(cx, 3, JS_TRUE, argc, vp);
1399 static JSBool
1400 date_setUTCMinutes(JSContext *cx, uintN argc, jsval *vp)
1402 return date_makeTime(cx, 3, JS_FALSE, argc, vp);
1405 static JSBool
1406 date_setHours(JSContext *cx, uintN argc, jsval *vp)
1408 return date_makeTime(cx, 4, JS_TRUE, argc, vp);
1411 static JSBool
1412 date_setUTCHours(JSContext *cx, uintN argc, jsval *vp)
1414 return date_makeTime(cx, 4, JS_FALSE, argc, vp);
1417 static JSBool
1418 date_makeDate(JSContext *cx, uintN maxargs, JSBool local, uintN argc, jsval *vp)
1420 JSObject *obj;
1421 jsval *argv;
1422 uintN i;
1423 jsdouble lorutime; /* local or UTC version of *date */
1424 jsdouble args[3], *argp, *stop;
1425 jsdouble year, month, day;
1426 jsdouble result;
1428 obj = JSVAL_TO_OBJECT(vp[1]);
1429 if (!GetUTCTime(cx, obj, vp, &result))
1430 return JS_FALSE;
1432 /* see complaint about ECMA in date_MakeTime */
1433 if (argc == 0)
1434 argc = 1; /* should be safe, because length of all setters is 1 */
1435 else if (argc > maxargs)
1436 argc = maxargs; /* clamp argc */
1437 JS_ASSERT(1 <= argc && argc <= 3);
1439 argv = vp + 2;
1440 for (i = 0; i < argc; i++) {
1441 if (!js_ValueToNumber(cx, argv[i], &args[i]))
1442 return JS_FALSE;
1443 if (!JSDOUBLE_IS_FINITE(args[i])) {
1444 if (!SetUTCTimePtr(cx, obj, NULL, cx->runtime->jsNaN))
1445 return JS_FALSE;
1446 *vp = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
1447 return JS_TRUE;
1449 args[i] = js_DoubleToInteger(args[i]);
1452 /* return NaN if date is NaN and we're not setting the year,
1453 * If we are, use 0 as the time. */
1454 if (!(JSDOUBLE_IS_FINITE(result))) {
1455 if (maxargs < 3)
1456 return js_NewNumberValue(cx, result, vp);
1457 lorutime = +0.;
1458 } else {
1459 lorutime = local ? LocalTime(result) : result;
1462 argp = args;
1463 stop = argp + argc;
1464 if (maxargs >= 3 && argp < stop)
1465 year = *argp++;
1466 else
1467 year = YearFromTime(lorutime);
1469 if (maxargs >= 2 && argp < stop)
1470 month = *argp++;
1471 else
1472 month = MonthFromTime(lorutime);
1474 if (maxargs >= 1 && argp < stop)
1475 day = *argp++;
1476 else
1477 day = DateFromTime(lorutime);
1479 day = MakeDay(year, month, day); /* day within year */
1480 result = MakeDate(day, TimeWithinDay(lorutime));
1482 if (local)
1483 result = UTC(result);
1485 result = TIMECLIP(result);
1486 if (!SetUTCTime(cx, obj, NULL, result))
1487 return JS_FALSE;
1489 return js_NewNumberValue(cx, result, vp);
1492 static JSBool
1493 date_setDate(JSContext *cx, uintN argc, jsval *vp)
1495 return date_makeDate(cx, 1, JS_TRUE, argc, vp);
1498 static JSBool
1499 date_setUTCDate(JSContext *cx, uintN argc, jsval *vp)
1501 return date_makeDate(cx, 1, JS_FALSE, argc, vp);
1504 static JSBool
1505 date_setMonth(JSContext *cx, uintN argc, jsval *vp)
1507 return date_makeDate(cx, 2, JS_TRUE, argc, vp);
1510 static JSBool
1511 date_setUTCMonth(JSContext *cx, uintN argc, jsval *vp)
1513 return date_makeDate(cx, 2, JS_FALSE, argc, vp);
1516 static JSBool
1517 date_setFullYear(JSContext *cx, uintN argc, jsval *vp)
1519 return date_makeDate(cx, 3, JS_TRUE, argc, vp);
1522 static JSBool
1523 date_setUTCFullYear(JSContext *cx, uintN argc, jsval *vp)
1525 return date_makeDate(cx, 3, JS_FALSE, argc, vp);
1528 static JSBool
1529 date_setYear(JSContext *cx, uintN argc, jsval *vp)
1531 JSObject *obj;
1532 jsdouble t;
1533 jsdouble year;
1534 jsdouble day;
1535 jsdouble result;
1537 obj = JSVAL_TO_OBJECT(vp[1]);
1538 if (!GetUTCTime(cx, obj, vp, &result))
1539 return JS_FALSE;
1541 if (!js_ValueToNumber(cx, vp[2], &year))
1542 return JS_FALSE;
1543 if (!JSDOUBLE_IS_FINITE(year)) {
1544 if (!SetUTCTimePtr(cx, obj, NULL, cx->runtime->jsNaN))
1545 return JS_FALSE;
1546 return js_NewNumberValue(cx, *cx->runtime->jsNaN, vp);
1549 year = js_DoubleToInteger(year);
1551 if (!JSDOUBLE_IS_FINITE(result)) {
1552 t = +0.0;
1553 } else {
1554 t = LocalTime(result);
1557 if (year >= 0 && year <= 99)
1558 year += 1900;
1560 day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
1561 result = MakeDate(day, TimeWithinDay(t));
1562 result = UTC(result);
1564 result = TIMECLIP(result);
1565 if (!SetUTCTime(cx, obj, NULL, result))
1566 return JS_FALSE;
1568 return js_NewNumberValue(cx, result, vp);
1571 /* constants for toString, toUTCString */
1572 static char js_NaN_date_str[] = "Invalid Date";
1573 static const char* days[] =
1575 "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
1577 static const char* months[] =
1579 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1582 static JSBool
1583 date_toGMTString(JSContext *cx, uintN argc, jsval *vp)
1585 char buf[100];
1586 JSString *str;
1587 jsdouble utctime;
1589 if (!GetUTCTime(cx, JSVAL_TO_OBJECT(vp[1]), vp, &utctime))
1590 return JS_FALSE;
1592 if (!JSDOUBLE_IS_FINITE(utctime)) {
1593 JS_snprintf(buf, sizeof buf, js_NaN_date_str);
1594 } else {
1595 /* Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
1596 * requires a PRMJTime... which only has 16-bit years. Sub-ECMA.
1598 JS_snprintf(buf, sizeof buf, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
1599 days[WeekDay(utctime)],
1600 DateFromTime(utctime),
1601 months[MonthFromTime(utctime)],
1602 YearFromTime(utctime),
1603 HourFromTime(utctime),
1604 MinFromTime(utctime),
1605 SecFromTime(utctime));
1607 str = JS_NewStringCopyZ(cx, buf);
1608 if (!str)
1609 return JS_FALSE;
1610 *vp = STRING_TO_JSVAL(str);
1611 return JS_TRUE;
1614 /* for Date.toLocaleString; interface to PRMJTime date struct.
1615 * If findEquivalent is true, then try to map the year to an equivalent year
1616 * that's in range.
1618 static void
1619 new_explode(jsdouble timeval, PRMJTime *split, JSBool findEquivalent)
1621 jsint year = YearFromTime(timeval);
1622 int16 adjustedYear;
1624 /* If the year doesn't fit in a PRMJTime, find something to do about it. */
1625 if (year > 32767 || year < -32768) {
1626 if (findEquivalent) {
1627 /* We're really just trying to get a timezone string; map the year
1628 * to some equivalent year in the range 0 to 2800. Borrowed from
1629 * A. D. Olsen.
1631 jsint cycles;
1632 #define CYCLE_YEARS 2800L
1633 cycles = (year >= 0) ? year / CYCLE_YEARS
1634 : -1 - (-1 - year) / CYCLE_YEARS;
1635 adjustedYear = (int16)(year - cycles * CYCLE_YEARS);
1636 } else {
1637 /* Clamp it to the nearest representable year. */
1638 adjustedYear = (int16)((year > 0) ? 32767 : - 32768);
1640 } else {
1641 adjustedYear = (int16)year;
1644 split->tm_usec = (int32) msFromTime(timeval) * 1000;
1645 split->tm_sec = (int8) SecFromTime(timeval);
1646 split->tm_min = (int8) MinFromTime(timeval);
1647 split->tm_hour = (int8) HourFromTime(timeval);
1648 split->tm_mday = (int8) DateFromTime(timeval);
1649 split->tm_mon = (int8) MonthFromTime(timeval);
1650 split->tm_wday = (int8) WeekDay(timeval);
1651 split->tm_year = (int16) adjustedYear;
1652 split->tm_yday = (int16) DayWithinYear(timeval, year);
1654 /* not sure how this affects things, but it doesn't seem
1655 to matter. */
1656 split->tm_isdst = (DaylightSavingTA(timeval) != 0);
1659 typedef enum formatspec {
1660 FORMATSPEC_FULL, FORMATSPEC_DATE, FORMATSPEC_TIME
1661 } formatspec;
1663 /* helper function */
1664 static JSBool
1665 date_format(JSContext *cx, jsdouble date, formatspec format, jsval *rval)
1667 char buf[100];
1668 JSString *str;
1669 char tzbuf[100];
1670 JSBool usetz;
1671 size_t i, tzlen;
1672 PRMJTime split;
1674 if (!JSDOUBLE_IS_FINITE(date)) {
1675 JS_snprintf(buf, sizeof buf, js_NaN_date_str);
1676 } else {
1677 jsdouble local = LocalTime(date);
1679 /* offset from GMT in minutes. The offset includes daylight savings,
1680 if it applies. */
1681 jsint minutes = (jsint) floor(AdjustTime(date) / msPerMinute);
1683 /* map 510 minutes to 0830 hours */
1684 intN offset = (minutes / 60) * 100 + minutes % 60;
1686 /* print as "Wed Nov 05 19:38:03 GMT-0800 (PST) 1997" The TZA is
1687 * printed as 'GMT-0800' rather than as 'PST' to avoid
1688 * operating-system dependence on strftime (which
1689 * PRMJ_FormatTimeUSEnglish calls, for %Z only.) win32 prints
1690 * PST as 'Pacific Standard Time.' This way we always know
1691 * what we're getting, and can parse it if we produce it.
1692 * The OS TZA string is included as a comment.
1695 /* get a timezone string from the OS to include as a
1696 comment. */
1697 new_explode(date, &split, JS_TRUE);
1698 if (PRMJ_FormatTime(tzbuf, sizeof tzbuf, "(%Z)", &split) != 0) {
1700 /* Decide whether to use the resulting timezone string.
1702 * Reject it if it contains any non-ASCII, non-alphanumeric
1703 * characters. It's then likely in some other character
1704 * encoding, and we probably won't display it correctly.
1706 usetz = JS_TRUE;
1707 tzlen = strlen(tzbuf);
1708 if (tzlen > 100) {
1709 usetz = JS_FALSE;
1710 } else {
1711 for (i = 0; i < tzlen; i++) {
1712 jschar c = tzbuf[i];
1713 if (c > 127 ||
1714 !(isalpha(c) || isdigit(c) ||
1715 c == ' ' || c == '(' || c == ')')) {
1716 usetz = JS_FALSE;
1721 /* Also reject it if it's not parenthesized or if it's '()'. */
1722 if (tzbuf[0] != '(' || tzbuf[1] == ')')
1723 usetz = JS_FALSE;
1724 } else
1725 usetz = JS_FALSE;
1727 switch (format) {
1728 case FORMATSPEC_FULL:
1730 * Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
1731 * requires a PRMJTime... which only has 16-bit years. Sub-ECMA.
1733 /* Tue Oct 31 2000 09:41:40 GMT-0800 (PST) */
1734 JS_snprintf(buf, sizeof buf,
1735 "%s %s %.2d %.4d %.2d:%.2d:%.2d GMT%+.4d%s%s",
1736 days[WeekDay(local)],
1737 months[MonthFromTime(local)],
1738 DateFromTime(local),
1739 YearFromTime(local),
1740 HourFromTime(local),
1741 MinFromTime(local),
1742 SecFromTime(local),
1743 offset,
1744 usetz ? " " : "",
1745 usetz ? tzbuf : "");
1746 break;
1747 case FORMATSPEC_DATE:
1748 /* Tue Oct 31 2000 */
1749 JS_snprintf(buf, sizeof buf,
1750 "%s %s %.2d %.4d",
1751 days[WeekDay(local)],
1752 months[MonthFromTime(local)],
1753 DateFromTime(local),
1754 YearFromTime(local));
1755 break;
1756 case FORMATSPEC_TIME:
1757 /* 09:41:40 GMT-0800 (PST) */
1758 JS_snprintf(buf, sizeof buf,
1759 "%.2d:%.2d:%.2d GMT%+.4d%s%s",
1760 HourFromTime(local),
1761 MinFromTime(local),
1762 SecFromTime(local),
1763 offset,
1764 usetz ? " " : "",
1765 usetz ? tzbuf : "");
1766 break;
1770 str = JS_NewStringCopyZ(cx, buf);
1771 if (!str)
1772 return JS_FALSE;
1773 *rval = STRING_TO_JSVAL(str);
1774 return JS_TRUE;
1777 static JSBool
1778 date_toLocaleHelper(JSContext *cx, const char *format, jsval *vp)
1780 JSObject *obj;
1781 char buf[100];
1782 JSString *str;
1783 PRMJTime split;
1784 jsdouble utctime;
1786 obj = JSVAL_TO_OBJECT(vp[1]);
1787 if (!GetUTCTime(cx, obj, vp, &utctime))
1788 return JS_FALSE;
1790 if (!JSDOUBLE_IS_FINITE(utctime)) {
1791 JS_snprintf(buf, sizeof buf, js_NaN_date_str);
1792 } else {
1793 intN result_len;
1794 jsdouble local = LocalTime(utctime);
1795 new_explode(local, &split, JS_FALSE);
1797 /* let PRMJTime format it. */
1798 result_len = PRMJ_FormatTime(buf, sizeof buf, format, &split);
1800 /* If it failed, default to toString. */
1801 if (result_len == 0)
1802 return date_format(cx, utctime, FORMATSPEC_FULL, vp);
1804 /* Hacked check against undesired 2-digit year 00/00/00 form. */
1805 if (strcmp(format, "%x") == 0 && result_len >= 6 &&
1806 /* Format %x means use OS settings, which may have 2-digit yr, so
1807 hack end of 3/11/22 or 11.03.22 or 11Mar22 to use 4-digit yr...*/
1808 !isdigit(buf[result_len - 3]) &&
1809 isdigit(buf[result_len - 2]) && isdigit(buf[result_len - 1]) &&
1810 /* ...but not if starts with 4-digit year, like 2022/3/11. */
1811 !(isdigit(buf[0]) && isdigit(buf[1]) &&
1812 isdigit(buf[2]) && isdigit(buf[3]))) {
1813 JS_snprintf(buf + (result_len - 2), (sizeof buf) - (result_len - 2),
1814 "%d", js_DateGetYear(cx, obj));
1819 if (cx->localeCallbacks && cx->localeCallbacks->localeToUnicode)
1820 return cx->localeCallbacks->localeToUnicode(cx, buf, vp);
1822 str = JS_NewStringCopyZ(cx, buf);
1823 if (!str)
1824 return JS_FALSE;
1825 *vp = STRING_TO_JSVAL(str);
1826 return JS_TRUE;
1829 static JSBool
1830 date_toLocaleString(JSContext *cx, uintN argc, jsval *vp)
1832 /* Use '%#c' for windows, because '%c' is
1833 * backward-compatible and non-y2k with msvc; '%#c' requests that a
1834 * full year be used in the result string.
1836 return date_toLocaleHelper(cx,
1837 #if defined(_WIN32) && !defined(__MWERKS__)
1838 "%#c"
1839 #else
1840 "%c"
1841 #endif
1842 , vp);
1845 static JSBool
1846 date_toLocaleDateString(JSContext *cx, uintN argc, jsval *vp)
1848 /* Use '%#x' for windows, because '%x' is
1849 * backward-compatible and non-y2k with msvc; '%#x' requests that a
1850 * full year be used in the result string.
1852 return date_toLocaleHelper(cx,
1853 #if defined(_WIN32) && !defined(__MWERKS__)
1854 "%#x"
1855 #else
1856 "%x"
1857 #endif
1858 , vp);
1861 static JSBool
1862 date_toLocaleTimeString(JSContext *cx, uintN argc, jsval *vp)
1864 return date_toLocaleHelper(cx, "%X", vp);
1867 static JSBool
1868 date_toLocaleFormat(JSContext *cx, uintN argc, jsval *vp)
1870 JSString *fmt;
1871 const char *fmtbytes;
1873 if (argc == 0)
1874 return date_toLocaleString(cx, argc, vp);
1876 fmt = js_ValueToString(cx, vp[2]);
1877 if (!fmt)
1878 return JS_FALSE;
1879 vp[2] = STRING_TO_JSVAL(fmt);
1880 fmtbytes = js_GetStringBytes(cx, fmt);
1881 if (!fmtbytes)
1882 return JS_FALSE;
1884 return date_toLocaleHelper(cx, fmtbytes, vp);
1887 static JSBool
1888 date_toTimeString(JSContext *cx, uintN argc, jsval *vp)
1890 jsdouble utctime;
1892 if (!GetUTCTime(cx, JSVAL_TO_OBJECT(vp[1]), vp, &utctime))
1893 return JS_FALSE;
1894 return date_format(cx, utctime, FORMATSPEC_TIME, vp);
1897 static JSBool
1898 date_toDateString(JSContext *cx, uintN argc, jsval *vp)
1900 jsdouble utctime;
1902 if (!GetUTCTime(cx, JSVAL_TO_OBJECT(vp[1]), vp, &utctime))
1903 return JS_FALSE;
1904 return date_format(cx, utctime, FORMATSPEC_DATE, vp);
1907 #if JS_HAS_TOSOURCE
1908 #include <string.h>
1909 #include "jsdtoa.h"
1911 static JSBool
1912 date_toSource(JSContext *cx, uintN argc, jsval *vp)
1914 jsdouble utctime;
1915 char buf[DTOSTR_STANDARD_BUFFER_SIZE], *numStr, *bytes;
1916 JSString *str;
1918 if (!GetUTCTime(cx, JSVAL_TO_OBJECT(vp[1]), vp, &utctime))
1919 return JS_FALSE;
1921 numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, utctime);
1922 if (!numStr) {
1923 JS_ReportOutOfMemory(cx);
1924 return JS_FALSE;
1927 bytes = JS_smprintf("(new %s(%s))", js_Date_str, numStr);
1928 if (!bytes) {
1929 JS_ReportOutOfMemory(cx);
1930 return JS_FALSE;
1933 str = JS_NewString(cx, bytes, strlen(bytes));
1934 if (!str) {
1935 free(bytes);
1936 return JS_FALSE;
1938 *vp = STRING_TO_JSVAL(str);
1939 return JS_TRUE;
1941 #endif
1943 static JSBool
1944 date_toString(JSContext *cx, uintN argc, jsval *vp)
1946 jsdouble utctime;
1948 if (!GetUTCTime(cx, JSVAL_TO_OBJECT(vp[1]), vp, &utctime))
1949 return JS_FALSE;
1950 return date_format(cx, utctime, FORMATSPEC_FULL, vp);
1953 static JSBool
1954 date_valueOf(JSContext *cx, uintN argc, jsval *vp)
1956 JSString *str, *str2;
1958 /* It is an error to call date_valueOf on a non-date object, but we don't
1959 * need to check for that explicitly here because every path calls
1960 * GetUTCTime, which does the check.
1963 /* If called directly with no arguments, convert to a time number. */
1964 if (argc == 0)
1965 return date_getTime(cx, argc, vp);
1967 /* Convert to number only if the hint was given, otherwise favor string. */
1968 str = js_ValueToString(cx, vp[2]);
1969 if (!str)
1970 return JS_FALSE;
1971 str2 = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]);
1972 if (js_EqualStrings(str, str2))
1973 return date_getTime(cx, argc, vp);
1974 return date_toString(cx, argc, vp);
1979 * creation and destruction
1982 static JSFunctionSpec date_static_methods[] = {
1983 JS_FN("UTC", date_UTC, 2,MAXARGS,0,0),
1984 JS_FN("parse", date_parse, 1,1,0,0),
1985 JS_FN("now", date_now, 0,0,0,0),
1986 JS_FS_END
1989 static JSFunctionSpec date_methods[] = {
1990 JS_FN("getTime", date_getTime, 0,0,0,0),
1991 JS_FN("getTimezoneOffset", date_getTimezoneOffset, 0,0,0,0),
1992 JS_FN("getYear", date_getYear, 0,0,0,0),
1993 JS_FN("getFullYear", date_getFullYear, 0,0,0,0),
1994 JS_FN("getUTCFullYear", date_getUTCFullYear, 0,0,0,0),
1995 JS_FN("getMonth", date_getMonth, 0,0,0,0),
1996 JS_FN("getUTCMonth", date_getUTCMonth, 0,0,0,0),
1997 JS_FN("getDate", date_getDate, 0,0,0,0),
1998 JS_FN("getUTCDate", date_getUTCDate, 0,0,0,0),
1999 JS_FN("getDay", date_getDay, 0,0,0,0),
2000 JS_FN("getUTCDay", date_getUTCDay, 0,0,0,0),
2001 JS_FN("getHours", date_getHours, 0,0,0,0),
2002 JS_FN("getUTCHours", date_getUTCHours, 0,0,0,0),
2003 JS_FN("getMinutes", date_getMinutes, 0,0,0,0),
2004 JS_FN("getUTCMinutes", date_getUTCMinutes, 0,0,0,0),
2005 JS_FN("getSeconds", date_getUTCSeconds, 0,0,0,0),
2006 JS_FN("getUTCSeconds", date_getUTCSeconds, 0,0,0,0),
2007 JS_FN("getMilliseconds", date_getUTCMilliseconds, 0,0,0,0),
2008 JS_FN("getUTCMilliseconds", date_getUTCMilliseconds, 0,0,0,0),
2009 JS_FN("setTime", date_setTime, 1,1,0,0),
2010 JS_FN("setYear", date_setYear, 1,1,0,0),
2011 JS_FN("setFullYear", date_setFullYear, 1,3,0,0),
2012 JS_FN("setUTCFullYear", date_setUTCFullYear, 1,3,0,0),
2013 JS_FN("setMonth", date_setMonth, 1,2,0,0),
2014 JS_FN("setUTCMonth", date_setUTCMonth, 1,2,0,0),
2015 JS_FN("setDate", date_setDate, 1,1,0,0),
2016 JS_FN("setUTCDate", date_setUTCDate, 1,1,0,0),
2017 JS_FN("setHours", date_setHours, 1,4,0,0),
2018 JS_FN("setUTCHours", date_setUTCHours, 1,4,0,0),
2019 JS_FN("setMinutes", date_setMinutes, 1,3,0,0),
2020 JS_FN("setUTCMinutes", date_setUTCMinutes, 1,3,0,0),
2021 JS_FN("setSeconds", date_setSeconds, 1,2,0,0),
2022 JS_FN("setUTCSeconds", date_setUTCSeconds, 1,2,0,0),
2023 JS_FN("setMilliseconds", date_setMilliseconds, 1,1,0,0),
2024 JS_FN("setUTCMilliseconds", date_setUTCMilliseconds, 1,1,0,0),
2025 JS_FN("toUTCString", date_toGMTString, 0,0,0,0),
2026 JS_FN(js_toLocaleString_str, date_toLocaleString, 0,0,0,0),
2027 JS_FN("toLocaleDateString", date_toLocaleDateString, 0,0,0,0),
2028 JS_FN("toLocaleTimeString", date_toLocaleTimeString, 0,0,0,0),
2029 JS_FN("toLocaleFormat", date_toLocaleFormat, 0,0,0,0),
2030 JS_FN("toDateString", date_toDateString, 0,0,0,0),
2031 JS_FN("toTimeString", date_toTimeString, 0,0,0,0),
2032 #if JS_HAS_TOSOURCE
2033 JS_FN(js_toSource_str, date_toSource, 0,0,0,0),
2034 #endif
2035 JS_FN(js_toString_str, date_toString, 0,0,0,0),
2036 JS_FN(js_valueOf_str, date_valueOf, 0,0,0,0),
2037 JS_FS_END
2040 static jsdouble *
2041 date_constructor(JSContext *cx, JSObject* obj)
2043 jsdouble *date;
2045 date = js_NewDouble(cx, 0.0, 0);
2046 if (!date)
2047 return NULL;
2049 JS_SetReservedSlot(cx, obj, UTC_TIME_SLOT,
2050 DOUBLE_TO_JSVAL(date));
2051 JS_SetReservedSlot(cx, obj, LOCAL_TIME_SLOT,
2052 DOUBLE_TO_JSVAL(cx->runtime->jsNaN));
2053 return date;
2056 static JSBool
2057 Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2059 jsdouble *date;
2060 JSString *str;
2061 jsdouble d;
2063 /* Date called as function. */
2064 if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
2065 int64 us, ms, us2ms;
2066 jsdouble msec_time;
2068 /* NSPR 2.0 docs say 'We do not support PRMJ_NowMS and PRMJ_NowS',
2069 * so compute ms from PRMJ_Now.
2071 us = PRMJ_Now();
2072 JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC);
2073 JSLL_DIV(ms, us, us2ms);
2074 JSLL_L2D(msec_time, ms);
2076 return date_format(cx, msec_time, FORMATSPEC_FULL, rval);
2079 /* Date called as constructor. */
2080 if (argc == 0) {
2081 int64 us, ms, us2ms;
2082 jsdouble msec_time;
2084 date = date_constructor(cx, obj);
2085 if (!date)
2086 return JS_FALSE;
2088 us = PRMJ_Now();
2089 JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC);
2090 JSLL_DIV(ms, us, us2ms);
2091 JSLL_L2D(msec_time, ms);
2093 *date = msec_time;
2094 } else if (argc == 1) {
2095 if (!JSVAL_IS_STRING(argv[0])) {
2096 /* the argument is a millisecond number */
2097 if (!js_ValueToNumber(cx, argv[0], &d))
2098 return JS_FALSE;
2099 date = date_constructor(cx, obj);
2100 if (!date)
2101 return JS_FALSE;
2102 *date = TIMECLIP(d);
2103 } else {
2104 /* the argument is a string; parse it. */
2105 date = date_constructor(cx, obj);
2106 if (!date)
2107 return JS_FALSE;
2109 str = js_ValueToString(cx, argv[0]);
2110 if (!str)
2111 return JS_FALSE;
2113 if (!date_parseString(str, date))
2114 *date = *cx->runtime->jsNaN;
2115 *date = TIMECLIP(*date);
2117 } else {
2118 jsdouble *date;
2119 jsdouble msec_time;
2121 if (!date_msecFromArgs(cx, argc, argv, &msec_time))
2122 return JS_FALSE;
2124 date = date_constructor(cx, obj);
2125 if (!date)
2126 return JS_FALSE;
2128 if (JSDOUBLE_IS_FINITE(msec_time)) {
2129 msec_time = UTC(msec_time);
2130 msec_time = TIMECLIP(msec_time);
2133 *date = msec_time;
2135 return JS_TRUE;
2138 JSObject *
2139 js_InitDateClass(JSContext *cx, JSObject *obj)
2141 JSObject *proto;
2142 jsdouble *proto_date;
2144 /* set static LocalTZA */
2145 LocalTZA = -(PRMJ_LocalGMTDifference() * msPerSecond);
2146 proto = JS_InitClass(cx, obj, NULL, &js_DateClass, Date, MAXARGS,
2147 NULL, date_methods, NULL, date_static_methods);
2148 if (!proto)
2149 return NULL;
2151 /* Alias toUTCString with toGMTString. (ECMA B.2.6) */
2152 if (!JS_AliasProperty(cx, proto, "toUTCString", "toGMTString"))
2153 return NULL;
2155 /* Set the value of the Date.prototype date to NaN */
2156 proto_date = date_constructor(cx, proto);
2157 if (!proto_date)
2158 return NULL;
2159 *proto_date = *cx->runtime->jsNaN;
2161 return proto;
2164 JS_FRIEND_API(JSObject *)
2165 js_NewDateObjectMsec(JSContext *cx, jsdouble msec_time)
2167 JSObject *obj;
2168 jsdouble *date;
2170 obj = js_NewObject(cx, &js_DateClass, NULL, NULL);
2171 if (!obj)
2172 return NULL;
2174 date = date_constructor(cx, obj);
2175 if (!date)
2176 return NULL;
2178 *date = msec_time;
2179 return obj;
2182 JS_FRIEND_API(JSObject *)
2183 js_NewDateObject(JSContext* cx, int year, int mon, int mday,
2184 int hour, int min, int sec)
2186 JSObject *obj;
2187 jsdouble msec_time;
2189 msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
2190 obj = js_NewDateObjectMsec(cx, UTC(msec_time));
2191 return obj;
2194 JS_FRIEND_API(JSBool)
2195 js_DateIsValid(JSContext *cx, JSObject* obj)
2197 jsdouble utctime;
2198 return GetUTCTime(cx, obj, NULL, &utctime) && !JSDOUBLE_IS_NaN(utctime);
2201 JS_FRIEND_API(int)
2202 js_DateGetYear(JSContext *cx, JSObject* obj)
2204 jsdouble localtime;
2206 /* Preserve legacy API behavior of returning 0 for invalid dates. */
2207 if (!GetLocalTime(cx, obj, NULL, &localtime) || JSDOUBLE_IS_NaN(localtime))
2208 return 0;
2210 return (int) YearFromTime(localtime);
2213 JS_FRIEND_API(int)
2214 js_DateGetMonth(JSContext *cx, JSObject* obj)
2216 jsdouble localtime;
2218 if (!GetLocalTime(cx, obj, NULL, &localtime) || JSDOUBLE_IS_NaN(localtime))
2219 return 0;
2221 return (int) MonthFromTime(localtime);
2224 JS_FRIEND_API(int)
2225 js_DateGetDate(JSContext *cx, JSObject* obj)
2227 jsdouble localtime;
2229 if (!GetLocalTime(cx, obj, NULL, &localtime) || JSDOUBLE_IS_NaN(localtime))
2230 return 0;
2232 return (int) DateFromTime(localtime);
2235 JS_FRIEND_API(int)
2236 js_DateGetHours(JSContext *cx, JSObject* obj)
2238 jsdouble localtime;
2240 if (!GetLocalTime(cx, obj, NULL, &localtime) || JSDOUBLE_IS_NaN(localtime))
2241 return 0;
2243 return (int) HourFromTime(localtime);
2246 JS_FRIEND_API(int)
2247 js_DateGetMinutes(JSContext *cx, JSObject* obj)
2249 jsdouble localtime;
2251 if (!GetLocalTime(cx, obj, NULL, &localtime) || JSDOUBLE_IS_NaN(localtime))
2252 return 0;
2254 return (int) MinFromTime(localtime);
2257 JS_FRIEND_API(int)
2258 js_DateGetSeconds(JSContext *cx, JSObject* obj)
2260 jsdouble utctime;
2262 if (!GetUTCTime(cx, obj, NULL, &utctime) || JSDOUBLE_IS_NaN(utctime))
2263 return 0;
2265 return (int) SecFromTime(utctime);
2268 JS_FRIEND_API(void)
2269 js_DateSetYear(JSContext *cx, JSObject *obj, int year)
2271 jsdouble local;
2273 if (!GetLocalTime(cx, obj, NULL, &local))
2274 return;
2276 /* reset date if it was NaN */
2277 if (JSDOUBLE_IS_NaN(local))
2278 local = 0;
2280 local = date_msecFromDate(year,
2281 MonthFromTime(local),
2282 DateFromTime(local),
2283 HourFromTime(local),
2284 MinFromTime(local),
2285 SecFromTime(local),
2286 msFromTime(local));
2288 /* SetUTCTime also invalidates local time cache. */
2289 SetUTCTime(cx, obj, NULL, UTC(local));
2292 JS_FRIEND_API(void)
2293 js_DateSetMonth(JSContext *cx, JSObject *obj, int month)
2295 jsdouble local;
2297 if (!GetLocalTime(cx, obj, NULL, &local))
2298 return;
2300 /* bail if date was NaN */
2301 if (JSDOUBLE_IS_NaN(local))
2302 return;
2304 local = date_msecFromDate(YearFromTime(local),
2305 month,
2306 DateFromTime(local),
2307 HourFromTime(local),
2308 MinFromTime(local),
2309 SecFromTime(local),
2310 msFromTime(local));
2311 SetUTCTime(cx, obj, NULL, UTC(local));
2314 JS_FRIEND_API(void)
2315 js_DateSetDate(JSContext *cx, JSObject *obj, int date)
2317 jsdouble local;
2319 if (!GetLocalTime(cx, obj, NULL, &local))
2320 return;
2322 if (JSDOUBLE_IS_NaN(local))
2323 return;
2325 local = date_msecFromDate(YearFromTime(local),
2326 MonthFromTime(local),
2327 date,
2328 HourFromTime(local),
2329 MinFromTime(local),
2330 SecFromTime(local),
2331 msFromTime(local));
2332 SetUTCTime(cx, obj, NULL, UTC(local));
2335 JS_FRIEND_API(void)
2336 js_DateSetHours(JSContext *cx, JSObject *obj, int hours)
2338 jsdouble local;
2340 if (!GetLocalTime(cx, obj, NULL, &local))
2341 return;
2343 if (JSDOUBLE_IS_NaN(local))
2344 return;
2345 local = date_msecFromDate(YearFromTime(local),
2346 MonthFromTime(local),
2347 DateFromTime(local),
2348 hours,
2349 MinFromTime(local),
2350 SecFromTime(local),
2351 msFromTime(local));
2352 SetUTCTime(cx, obj, NULL, UTC(local));
2355 JS_FRIEND_API(void)
2356 js_DateSetMinutes(JSContext *cx, JSObject *obj, int minutes)
2358 jsdouble local;
2360 if (!GetLocalTime(cx, obj, NULL, &local))
2361 return;
2363 if (JSDOUBLE_IS_NaN(local))
2364 return;
2365 local = date_msecFromDate(YearFromTime(local),
2366 MonthFromTime(local),
2367 DateFromTime(local),
2368 HourFromTime(local),
2369 minutes,
2370 SecFromTime(local),
2371 msFromTime(local));
2372 SetUTCTime(cx, obj, NULL, UTC(local));
2375 JS_FRIEND_API(void)
2376 js_DateSetSeconds(JSContext *cx, JSObject *obj, int seconds)
2378 jsdouble local;
2380 if (!GetLocalTime(cx, obj, NULL, &local))
2381 return;
2383 if (JSDOUBLE_IS_NaN(local))
2384 return;
2385 local = date_msecFromDate(YearFromTime(local),
2386 MonthFromTime(local),
2387 DateFromTime(local),
2388 HourFromTime(local),
2389 MinFromTime(local),
2390 seconds,
2391 msFromTime(local));
2392 SetUTCTime(cx, obj, NULL, UTC(local));
2395 JS_FRIEND_API(jsdouble)
2396 js_DateGetMsecSinceEpoch(JSContext *cx, JSObject *obj)
2398 jsdouble utctime;
2399 if (!GetUTCTime(cx, obj, NULL, &utctime))
2400 return 0;
2401 return utctime;