Bug 438861 Autocomplete does not pass back previous results to a second search. r...
[mozilla-central.git] / js / src / jsdate.cpp
blobb635445690c6cb28569c9db09455c5ce53043f63
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 d = js_ValueToNumber(cx, &argv[loop]);
577 if (JSVAL_IS_NULL(argv[loop]))
578 return JS_FALSE;
579 /* return NaN if any arg is not finite */
580 if (!JSDOUBLE_IS_FINITE(d)) {
581 *rval = *cx->runtime->jsNaN;
582 return JS_TRUE;
584 array[loop] = js_DoubleToInteger(d);
585 } else {
586 if (loop == 2) {
587 array[loop] = 1; /* Default the date argument to 1. */
588 } else {
589 array[loop] = 0;
594 /* adjust 2-digit years into the 20th century */
595 if (array[0] >= 0 && array[0] <= 99)
596 array[0] += 1900;
598 msec_time = date_msecFromDate(array[0], array[1], array[2],
599 array[3], array[4], array[5], array[6]);
600 *rval = msec_time;
601 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_NewNumberInRootedValue(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 = JS_ARRAY_LENGTH(wtb); --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 if (argc == 0) {
896 *vp = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
897 return JS_TRUE;
899 str = js_ValueToString(cx, vp[2]);
900 if (!str)
901 return JS_FALSE;
902 vp[2] = STRING_TO_JSVAL(str);
903 if (!date_parseString(str, &result)) {
904 *vp = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
905 return JS_TRUE;
908 result = TIMECLIP(result);
909 return js_NewNumberInRootedValue(cx, result, vp);
912 static JSBool
913 date_now(JSContext *cx, uintN argc, jsval *vp)
915 int64 us, ms, us2ms;
916 jsdouble msec_time;
918 us = PRMJ_Now();
919 JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC);
920 JSLL_DIV(ms, us, us2ms);
921 JSLL_L2D(msec_time, ms);
923 return js_NewDoubleInRootedValue(cx, msec_time, vp);
927 * Get UTC time from the date object. Returns false if the object is not
928 * Date type.
930 static JSBool
931 GetUTCTime(JSContext *cx, JSObject *obj, jsval *vp, jsdouble *dp)
933 jsval v;
935 if (!JS_InstanceOf(cx, obj, &js_DateClass, vp ? vp + 2 : NULL))
936 return JS_FALSE;
937 if (!JS_GetReservedSlot(cx, obj, UTC_TIME_SLOT, &v))
938 return JS_FALSE;
940 *dp = *JSVAL_TO_DOUBLE(v);
941 return JS_TRUE;
945 * Set UTC time slot with a pointer pointing to a jsdouble. This function is
946 * used only for setting UTC time to some predefined values, such as NaN.
948 * It also invalidates cached local time.
950 static JSBool
951 SetUTCTimePtr(JSContext *cx, JSObject *obj, jsval *vp, jsdouble *dp)
953 if (vp && !JS_InstanceOf(cx, obj, &js_DateClass, vp + 2))
954 return JS_FALSE;
956 /* Invalidate local time cache. */
957 if (!JS_SetReservedSlot(cx, obj, LOCAL_TIME_SLOT,
958 DOUBLE_TO_JSVAL(cx->runtime->jsNaN))) {
959 return JS_FALSE;
962 return JS_SetReservedSlot(cx, obj, UTC_TIME_SLOT, DOUBLE_TO_JSVAL(dp));
966 * Set UTC time to a given time.
968 static JSBool
969 SetUTCTime(JSContext *cx, JSObject *obj, jsval *vp, jsdouble t)
971 jsdouble *dp = js_NewWeaklyRootedDouble(cx, t);
972 if (!dp)
973 return JS_FALSE;
974 return SetUTCTimePtr(cx, obj, vp, dp);
978 * Get the local time, cache it if necessary. If UTC time is not finite
979 * (e.g., NaN), the local time slot is set to the UTC time without conversion.
981 static JSBool
982 GetAndCacheLocalTime(JSContext *cx, JSObject *obj, jsval *vp, jsdouble *dp)
984 jsval v;
985 jsdouble result;
986 jsdouble *cached;
988 if (!obj || !JS_GetReservedSlot(cx, obj, LOCAL_TIME_SLOT, &v))
989 return JS_FALSE;
991 result = *JSVAL_TO_DOUBLE(v);
993 if (JSDOUBLE_IS_NaN(result)) {
994 if (!GetUTCTime(cx, obj, vp, &result))
995 return JS_FALSE;
997 /* if result is NaN, it couldn't be finite. */
998 if (JSDOUBLE_IS_FINITE(result))
999 result = LocalTime(result);
1001 cached = js_NewWeaklyRootedDouble(cx, result);
1002 if (!cached)
1003 return JS_FALSE;
1005 if (!JS_SetReservedSlot(cx, obj, LOCAL_TIME_SLOT,
1006 DOUBLE_TO_JSVAL(cached))) {
1007 return JS_FALSE;
1011 *dp = result;
1012 return JS_TRUE;
1016 * See ECMA 15.9.5.4 thru 15.9.5.23
1018 static JSBool
1019 date_getTime(JSContext *cx, uintN argc, jsval *vp)
1021 jsdouble result;
1023 return GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result) &&
1024 js_NewNumberInRootedValue(cx, result, vp);
1027 static JSBool
1028 GetYear(JSContext *cx, JSBool fullyear, jsval *vp)
1030 jsdouble result;
1032 if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1033 return JS_FALSE;
1035 if (JSDOUBLE_IS_FINITE(result)) {
1036 result = YearFromTime(result);
1038 /* Follow ECMA-262 to the letter, contrary to IE JScript. */
1039 if (!fullyear)
1040 result -= 1900;
1043 return js_NewNumberInRootedValue(cx, result, vp);
1046 static JSBool
1047 date_getYear(JSContext *cx, uintN argc, jsval *vp)
1049 return GetYear(cx, JS_FALSE, vp);
1052 static JSBool
1053 date_getFullYear(JSContext *cx, uintN argc, jsval *vp)
1055 return GetYear(cx, JS_TRUE, vp);
1058 static JSBool
1059 date_getUTCFullYear(JSContext *cx, uintN argc, jsval *vp)
1061 jsdouble result;
1063 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1064 return JS_FALSE;
1066 if (JSDOUBLE_IS_FINITE(result))
1067 result = YearFromTime(result);
1069 return js_NewNumberInRootedValue(cx, result, vp);
1072 static JSBool
1073 date_getMonth(JSContext *cx, uintN argc, jsval *vp)
1075 jsdouble result;
1077 if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1078 return JS_FALSE;
1080 if (JSDOUBLE_IS_FINITE(result))
1081 result = MonthFromTime(result);
1083 return js_NewNumberInRootedValue(cx, result, vp);
1086 static JSBool
1087 date_getUTCMonth(JSContext *cx, uintN argc, jsval *vp)
1089 jsdouble result;
1091 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1092 return JS_FALSE;
1094 if (JSDOUBLE_IS_FINITE(result))
1095 result = MonthFromTime(result);
1097 return js_NewNumberInRootedValue(cx, result, vp);
1100 static JSBool
1101 date_getDate(JSContext *cx, uintN argc, jsval *vp)
1103 jsdouble result;
1105 if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1106 return JS_FALSE;
1108 if (JSDOUBLE_IS_FINITE(result))
1109 result = DateFromTime(result);
1111 return js_NewNumberInRootedValue(cx, result, vp);
1114 static JSBool
1115 date_getUTCDate(JSContext *cx, uintN argc, jsval *vp)
1117 jsdouble result;
1119 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1120 return JS_FALSE;
1122 if (JSDOUBLE_IS_FINITE(result))
1123 result = DateFromTime(result);
1125 return js_NewNumberInRootedValue(cx, result, vp);
1128 static JSBool
1129 date_getDay(JSContext *cx, uintN argc, jsval *vp)
1131 jsdouble result;
1133 if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1134 return JS_FALSE;
1136 if (JSDOUBLE_IS_FINITE(result))
1137 result = WeekDay(result);
1139 return js_NewNumberInRootedValue(cx, result, vp);
1142 static JSBool
1143 date_getUTCDay(JSContext *cx, uintN argc, jsval *vp)
1145 jsdouble result;
1147 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1148 return JS_FALSE;
1150 if (JSDOUBLE_IS_FINITE(result))
1151 result = WeekDay(result);
1153 return js_NewNumberInRootedValue(cx, result, vp);
1156 static JSBool
1157 date_getHours(JSContext *cx, uintN argc, jsval *vp)
1159 jsdouble result;
1161 if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1162 return JS_FALSE;
1164 if (JSDOUBLE_IS_FINITE(result))
1165 result = HourFromTime(result);
1167 return js_NewNumberInRootedValue(cx, result, vp);
1170 static JSBool
1171 date_getUTCHours(JSContext *cx, uintN argc, jsval *vp)
1173 jsdouble result;
1175 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1176 return JS_FALSE;
1178 if (JSDOUBLE_IS_FINITE(result))
1179 result = HourFromTime(result);
1181 return js_NewNumberInRootedValue(cx, result, vp);
1184 static JSBool
1185 date_getMinutes(JSContext *cx, uintN argc, jsval *vp)
1187 jsdouble result;
1189 if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1190 return JS_FALSE;
1192 if (JSDOUBLE_IS_FINITE(result))
1193 result = MinFromTime(result);
1195 return js_NewNumberInRootedValue(cx, result, vp);
1198 static JSBool
1199 date_getUTCMinutes(JSContext *cx, uintN argc, jsval *vp)
1201 jsdouble result;
1203 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1204 return JS_FALSE;
1206 if (JSDOUBLE_IS_FINITE(result))
1207 result = MinFromTime(result);
1209 return js_NewNumberInRootedValue(cx, result, vp);
1212 /* Date.getSeconds is mapped to getUTCSeconds */
1214 static JSBool
1215 date_getUTCSeconds(JSContext *cx, uintN argc, jsval *vp)
1217 jsdouble result;
1219 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1220 return JS_FALSE;
1222 if (JSDOUBLE_IS_FINITE(result))
1223 result = SecFromTime(result);
1225 return js_NewNumberInRootedValue(cx, result, vp);
1228 /* Date.getMilliseconds is mapped to getUTCMilliseconds */
1230 static JSBool
1231 date_getUTCMilliseconds(JSContext *cx, uintN argc, jsval *vp)
1233 jsdouble result;
1235 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1236 return JS_FALSE;
1238 if (JSDOUBLE_IS_FINITE(result))
1239 result = msFromTime(result);
1241 return js_NewNumberInRootedValue(cx, result, vp);
1244 static JSBool
1245 date_getTimezoneOffset(JSContext *cx, uintN argc, jsval *vp)
1247 JSObject *obj;
1248 jsdouble utctime, localtime, result;
1250 obj = JS_THIS_OBJECT(cx, vp);
1251 if (!GetUTCTime(cx, obj, vp, &utctime))
1252 return JS_FALSE;
1253 if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime))
1254 return JS_FALSE;
1257 * Return the time zone offset in minutes for the current locale that is
1258 * appropriate for this time. This value would be a constant except for
1259 * daylight savings time.
1261 result = (utctime - localtime) / msPerMinute;
1262 return js_NewNumberInRootedValue(cx, result, vp);
1265 static JSBool
1266 SetDateToNaN(JSContext *cx, jsval *vp)
1268 JSObject *obj;
1270 obj = JS_THIS_OBJECT(cx, vp);
1271 if (!SetUTCTimePtr(cx, obj, NULL, cx->runtime->jsNaN))
1272 return JS_FALSE;
1273 *vp = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
1274 return JS_TRUE;
1277 static JSBool
1278 date_setTime(JSContext *cx, uintN argc, jsval *vp)
1280 jsdouble result;
1282 if (argc == 0)
1283 return SetDateToNaN(cx, vp);
1284 result = js_ValueToNumber(cx, &vp[2]);
1285 if (JSVAL_IS_NULL(vp[2]))
1286 return JS_FALSE;
1288 result = TIMECLIP(result);
1290 if (!SetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, result))
1291 return JS_FALSE;
1293 return js_NewNumberInRootedValue(cx, result, vp);
1296 static JSBool
1297 date_makeTime(JSContext *cx, uintN maxargs, JSBool local, uintN argc, jsval *vp)
1299 JSObject *obj;
1300 jsval *argv;
1301 uintN i;
1302 jsdouble args[4], *argp, *stop;
1303 jsdouble hour, min, sec, msec;
1304 jsdouble lorutime; /* Local or UTC version of *date */
1306 jsdouble msec_time;
1307 jsdouble result;
1309 obj = JS_THIS_OBJECT(cx, vp);
1310 if (!GetUTCTime(cx, obj, vp, &result))
1311 return JS_FALSE;
1313 /* just return NaN if the date is already NaN */
1314 if (!JSDOUBLE_IS_FINITE(result))
1315 return js_NewNumberInRootedValue(cx, result, vp);
1318 * Satisfy the ECMA rule that if a function is called with
1319 * fewer arguments than the specified formal arguments, the
1320 * remaining arguments are set to undefined. Seems like all
1321 * the Date.setWhatever functions in ECMA are only varargs
1322 * beyond the first argument; this should be set to undefined
1323 * if it's not given. This means that "d = new Date();
1324 * d.setMilliseconds()" returns NaN. Blech.
1326 if (argc == 0)
1327 return SetDateToNaN(cx, vp);
1328 if (argc > maxargs)
1329 argc = maxargs; /* clamp argc */
1330 JS_ASSERT(argc <= 4);
1332 argv = vp + 2;
1333 for (i = 0; i < argc; i++) {
1334 args[i] = js_ValueToNumber(cx, &argv[i]);
1335 if (JSVAL_IS_NULL(argv[i]))
1336 return JS_FALSE;
1337 if (!JSDOUBLE_IS_FINITE(args[i]))
1338 return SetDateToNaN(cx, vp);
1339 args[i] = js_DoubleToInteger(args[i]);
1342 if (local)
1343 lorutime = LocalTime(result);
1344 else
1345 lorutime = result;
1347 argp = args;
1348 stop = argp + argc;
1349 if (maxargs >= 4 && argp < stop)
1350 hour = *argp++;
1351 else
1352 hour = HourFromTime(lorutime);
1354 if (maxargs >= 3 && argp < stop)
1355 min = *argp++;
1356 else
1357 min = MinFromTime(lorutime);
1359 if (maxargs >= 2 && argp < stop)
1360 sec = *argp++;
1361 else
1362 sec = SecFromTime(lorutime);
1364 if (maxargs >= 1 && argp < stop)
1365 msec = *argp;
1366 else
1367 msec = msFromTime(lorutime);
1369 msec_time = MakeTime(hour, min, sec, msec);
1370 result = MakeDate(Day(lorutime), msec_time);
1372 /* fprintf(stderr, "%f\n", result); */
1374 if (local)
1375 result = UTC(result);
1377 /* fprintf(stderr, "%f\n", result); */
1379 result = TIMECLIP(result);
1380 if (!SetUTCTime(cx, obj, NULL, result))
1381 return JS_FALSE;
1383 return js_NewNumberInRootedValue(cx, result, vp);
1386 static JSBool
1387 date_setMilliseconds(JSContext *cx, uintN argc, jsval *vp)
1389 return date_makeTime(cx, 1, JS_TRUE, argc, vp);
1392 static JSBool
1393 date_setUTCMilliseconds(JSContext *cx, uintN argc, jsval *vp)
1395 return date_makeTime(cx, 1, JS_FALSE, argc, vp);
1398 static JSBool
1399 date_setSeconds(JSContext *cx, uintN argc, jsval *vp)
1401 return date_makeTime(cx, 2, JS_TRUE, argc, vp);
1404 static JSBool
1405 date_setUTCSeconds(JSContext *cx, uintN argc, jsval *vp)
1407 return date_makeTime(cx, 2, JS_FALSE, argc, vp);
1410 static JSBool
1411 date_setMinutes(JSContext *cx, uintN argc, jsval *vp)
1413 return date_makeTime(cx, 3, JS_TRUE, argc, vp);
1416 static JSBool
1417 date_setUTCMinutes(JSContext *cx, uintN argc, jsval *vp)
1419 return date_makeTime(cx, 3, JS_FALSE, argc, vp);
1422 static JSBool
1423 date_setHours(JSContext *cx, uintN argc, jsval *vp)
1425 return date_makeTime(cx, 4, JS_TRUE, argc, vp);
1428 static JSBool
1429 date_setUTCHours(JSContext *cx, uintN argc, jsval *vp)
1431 return date_makeTime(cx, 4, JS_FALSE, argc, vp);
1434 static JSBool
1435 date_makeDate(JSContext *cx, uintN maxargs, JSBool local, uintN argc, jsval *vp)
1437 JSObject *obj;
1438 jsval *argv;
1439 uintN i;
1440 jsdouble lorutime; /* local or UTC version of *date */
1441 jsdouble args[3], *argp, *stop;
1442 jsdouble year, month, day;
1443 jsdouble result;
1445 obj = JS_THIS_OBJECT(cx, vp);
1446 if (!GetUTCTime(cx, obj, vp, &result))
1447 return JS_FALSE;
1449 /* see complaint about ECMA in date_MakeTime */
1450 if (argc == 0)
1451 return SetDateToNaN(cx, vp);
1452 if (argc > maxargs)
1453 argc = maxargs; /* clamp argc */
1454 JS_ASSERT(1 <= argc && argc <= 3);
1456 argv = vp + 2;
1457 for (i = 0; i < argc; i++) {
1458 args[i] = js_ValueToNumber(cx, &argv[i]);
1459 if (JSVAL_IS_NULL(argv[i]))
1460 return JS_FALSE;
1461 if (!JSDOUBLE_IS_FINITE(args[i]))
1462 return SetDateToNaN(cx, vp);
1463 args[i] = js_DoubleToInteger(args[i]);
1466 /* return NaN if date is NaN and we're not setting the year,
1467 * If we are, use 0 as the time. */
1468 if (!(JSDOUBLE_IS_FINITE(result))) {
1469 if (maxargs < 3)
1470 return js_NewNumberInRootedValue(cx, result, vp);
1471 lorutime = +0.;
1472 } else {
1473 lorutime = local ? LocalTime(result) : result;
1476 argp = args;
1477 stop = argp + argc;
1478 if (maxargs >= 3 && argp < stop)
1479 year = *argp++;
1480 else
1481 year = YearFromTime(lorutime);
1483 if (maxargs >= 2 && argp < stop)
1484 month = *argp++;
1485 else
1486 month = MonthFromTime(lorutime);
1488 if (maxargs >= 1 && argp < stop)
1489 day = *argp++;
1490 else
1491 day = DateFromTime(lorutime);
1493 day = MakeDay(year, month, day); /* day within year */
1494 result = MakeDate(day, TimeWithinDay(lorutime));
1496 if (local)
1497 result = UTC(result);
1499 result = TIMECLIP(result);
1500 if (!SetUTCTime(cx, obj, NULL, result))
1501 return JS_FALSE;
1503 return js_NewNumberInRootedValue(cx, result, vp);
1506 static JSBool
1507 date_setDate(JSContext *cx, uintN argc, jsval *vp)
1509 return date_makeDate(cx, 1, JS_TRUE, argc, vp);
1512 static JSBool
1513 date_setUTCDate(JSContext *cx, uintN argc, jsval *vp)
1515 return date_makeDate(cx, 1, JS_FALSE, argc, vp);
1518 static JSBool
1519 date_setMonth(JSContext *cx, uintN argc, jsval *vp)
1521 return date_makeDate(cx, 2, JS_TRUE, argc, vp);
1524 static JSBool
1525 date_setUTCMonth(JSContext *cx, uintN argc, jsval *vp)
1527 return date_makeDate(cx, 2, JS_FALSE, argc, vp);
1530 static JSBool
1531 date_setFullYear(JSContext *cx, uintN argc, jsval *vp)
1533 return date_makeDate(cx, 3, JS_TRUE, argc, vp);
1536 static JSBool
1537 date_setUTCFullYear(JSContext *cx, uintN argc, jsval *vp)
1539 return date_makeDate(cx, 3, JS_FALSE, argc, vp);
1542 static JSBool
1543 date_setYear(JSContext *cx, uintN argc, jsval *vp)
1545 JSObject *obj;
1546 jsdouble t;
1547 jsdouble year;
1548 jsdouble day;
1549 jsdouble result;
1551 obj = JS_THIS_OBJECT(cx, vp);
1552 if (!GetUTCTime(cx, obj, vp, &result))
1553 return JS_FALSE;
1555 if (argc == 0)
1556 return SetDateToNaN(cx, vp);
1557 year = js_ValueToNumber(cx, &vp[2]);
1558 if (JSVAL_IS_NULL(vp[2]))
1559 return JS_FALSE;
1560 if (!JSDOUBLE_IS_FINITE(year))
1561 return SetDateToNaN(cx, vp);
1563 year = js_DoubleToInteger(year);
1565 if (!JSDOUBLE_IS_FINITE(result)) {
1566 t = +0.0;
1567 } else {
1568 t = LocalTime(result);
1571 if (year >= 0 && year <= 99)
1572 year += 1900;
1574 day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
1575 result = MakeDate(day, TimeWithinDay(t));
1576 result = UTC(result);
1578 result = TIMECLIP(result);
1579 if (!SetUTCTime(cx, obj, NULL, result))
1580 return JS_FALSE;
1582 return js_NewNumberInRootedValue(cx, result, vp);
1585 /* constants for toString, toUTCString */
1586 static char js_NaN_date_str[] = "Invalid Date";
1587 static const char* days[] =
1589 "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
1591 static const char* months[] =
1593 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1596 static JSBool
1597 date_toGMTString(JSContext *cx, uintN argc, jsval *vp)
1599 char buf[100];
1600 JSString *str;
1601 jsdouble utctime;
1603 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &utctime))
1604 return JS_FALSE;
1606 if (!JSDOUBLE_IS_FINITE(utctime)) {
1607 JS_snprintf(buf, sizeof buf, js_NaN_date_str);
1608 } else {
1609 /* Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
1610 * requires a PRMJTime... which only has 16-bit years. Sub-ECMA.
1612 JS_snprintf(buf, sizeof buf, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
1613 days[WeekDay(utctime)],
1614 DateFromTime(utctime),
1615 months[MonthFromTime(utctime)],
1616 YearFromTime(utctime),
1617 HourFromTime(utctime),
1618 MinFromTime(utctime),
1619 SecFromTime(utctime));
1621 str = JS_NewStringCopyZ(cx, buf);
1622 if (!str)
1623 return JS_FALSE;
1624 *vp = STRING_TO_JSVAL(str);
1625 return JS_TRUE;
1628 /* for Date.toLocaleString; interface to PRMJTime date struct.
1630 static void
1631 new_explode(jsdouble timeval, PRMJTime *split)
1633 jsint year = YearFromTime(timeval);
1635 split->tm_usec = (int32) msFromTime(timeval) * 1000;
1636 split->tm_sec = (int8) SecFromTime(timeval);
1637 split->tm_min = (int8) MinFromTime(timeval);
1638 split->tm_hour = (int8) HourFromTime(timeval);
1639 split->tm_mday = (int8) DateFromTime(timeval);
1640 split->tm_mon = (int8) MonthFromTime(timeval);
1641 split->tm_wday = (int8) WeekDay(timeval);
1642 split->tm_year = year;
1643 split->tm_yday = (int16) DayWithinYear(timeval, year);
1645 /* not sure how this affects things, but it doesn't seem
1646 to matter. */
1647 split->tm_isdst = (DaylightSavingTA(timeval) != 0);
1650 typedef enum formatspec {
1651 FORMATSPEC_FULL, FORMATSPEC_DATE, FORMATSPEC_TIME
1652 } formatspec;
1654 /* helper function */
1655 static JSBool
1656 date_format(JSContext *cx, jsdouble date, formatspec format, jsval *rval)
1658 char buf[100];
1659 JSString *str;
1660 char tzbuf[100];
1661 JSBool usetz;
1662 size_t i, tzlen;
1663 PRMJTime split;
1665 if (!JSDOUBLE_IS_FINITE(date)) {
1666 JS_snprintf(buf, sizeof buf, js_NaN_date_str);
1667 } else {
1668 jsdouble local = LocalTime(date);
1670 /* offset from GMT in minutes. The offset includes daylight savings,
1671 if it applies. */
1672 jsint minutes = (jsint) floor(AdjustTime(date) / msPerMinute);
1674 /* map 510 minutes to 0830 hours */
1675 intN offset = (minutes / 60) * 100 + minutes % 60;
1677 /* print as "Wed Nov 05 19:38:03 GMT-0800 (PST) 1997" The TZA is
1678 * printed as 'GMT-0800' rather than as 'PST' to avoid
1679 * operating-system dependence on strftime (which
1680 * PRMJ_FormatTimeUSEnglish calls, for %Z only.) win32 prints
1681 * PST as 'Pacific Standard Time.' This way we always know
1682 * what we're getting, and can parse it if we produce it.
1683 * The OS TZA string is included as a comment.
1686 /* get a timezone string from the OS to include as a
1687 comment. */
1688 new_explode(date, &split);
1689 if (PRMJ_FormatTime(tzbuf, sizeof tzbuf, "(%Z)", &split) != 0) {
1691 /* Decide whether to use the resulting timezone string.
1693 * Reject it if it contains any non-ASCII, non-alphanumeric
1694 * characters. It's then likely in some other character
1695 * encoding, and we probably won't display it correctly.
1697 usetz = JS_TRUE;
1698 tzlen = strlen(tzbuf);
1699 if (tzlen > 100) {
1700 usetz = JS_FALSE;
1701 } else {
1702 for (i = 0; i < tzlen; i++) {
1703 jschar c = tzbuf[i];
1704 if (c > 127 ||
1705 !(isalpha(c) || isdigit(c) ||
1706 c == ' ' || c == '(' || c == ')')) {
1707 usetz = JS_FALSE;
1712 /* Also reject it if it's not parenthesized or if it's '()'. */
1713 if (tzbuf[0] != '(' || tzbuf[1] == ')')
1714 usetz = JS_FALSE;
1715 } else
1716 usetz = JS_FALSE;
1718 switch (format) {
1719 case FORMATSPEC_FULL:
1721 * Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
1722 * requires a PRMJTime... which only has 16-bit years. Sub-ECMA.
1724 /* Tue Oct 31 2000 09:41:40 GMT-0800 (PST) */
1725 JS_snprintf(buf, sizeof buf,
1726 "%s %s %.2d %.4d %.2d:%.2d:%.2d GMT%+.4d%s%s",
1727 days[WeekDay(local)],
1728 months[MonthFromTime(local)],
1729 DateFromTime(local),
1730 YearFromTime(local),
1731 HourFromTime(local),
1732 MinFromTime(local),
1733 SecFromTime(local),
1734 offset,
1735 usetz ? " " : "",
1736 usetz ? tzbuf : "");
1737 break;
1738 case FORMATSPEC_DATE:
1739 /* Tue Oct 31 2000 */
1740 JS_snprintf(buf, sizeof buf,
1741 "%s %s %.2d %.4d",
1742 days[WeekDay(local)],
1743 months[MonthFromTime(local)],
1744 DateFromTime(local),
1745 YearFromTime(local));
1746 break;
1747 case FORMATSPEC_TIME:
1748 /* 09:41:40 GMT-0800 (PST) */
1749 JS_snprintf(buf, sizeof buf,
1750 "%.2d:%.2d:%.2d GMT%+.4d%s%s",
1751 HourFromTime(local),
1752 MinFromTime(local),
1753 SecFromTime(local),
1754 offset,
1755 usetz ? " " : "",
1756 usetz ? tzbuf : "");
1757 break;
1761 str = JS_NewStringCopyZ(cx, buf);
1762 if (!str)
1763 return JS_FALSE;
1764 *rval = STRING_TO_JSVAL(str);
1765 return JS_TRUE;
1768 static JSBool
1769 date_toLocaleHelper(JSContext *cx, const char *format, jsval *vp)
1771 JSObject *obj;
1772 char buf[100];
1773 JSString *str;
1774 PRMJTime split;
1775 jsdouble utctime;
1777 obj = JS_THIS_OBJECT(cx, vp);
1778 if (!GetUTCTime(cx, obj, vp, &utctime))
1779 return JS_FALSE;
1781 if (!JSDOUBLE_IS_FINITE(utctime)) {
1782 JS_snprintf(buf, sizeof buf, js_NaN_date_str);
1783 } else {
1784 intN result_len;
1785 jsdouble local = LocalTime(utctime);
1786 new_explode(local, &split);
1788 /* let PRMJTime format it. */
1789 result_len = PRMJ_FormatTime(buf, sizeof buf, format, &split);
1791 /* If it failed, default to toString. */
1792 if (result_len == 0)
1793 return date_format(cx, utctime, FORMATSPEC_FULL, vp);
1795 /* Hacked check against undesired 2-digit year 00/00/00 form. */
1796 if (strcmp(format, "%x") == 0 && result_len >= 6 &&
1797 /* Format %x means use OS settings, which may have 2-digit yr, so
1798 hack end of 3/11/22 or 11.03.22 or 11Mar22 to use 4-digit yr...*/
1799 !isdigit(buf[result_len - 3]) &&
1800 isdigit(buf[result_len - 2]) && isdigit(buf[result_len - 1]) &&
1801 /* ...but not if starts with 4-digit year, like 2022/3/11. */
1802 !(isdigit(buf[0]) && isdigit(buf[1]) &&
1803 isdigit(buf[2]) && isdigit(buf[3]))) {
1804 JS_snprintf(buf + (result_len - 2), (sizeof buf) - (result_len - 2),
1805 "%d", js_DateGetYear(cx, obj));
1810 if (cx->localeCallbacks && cx->localeCallbacks->localeToUnicode)
1811 return cx->localeCallbacks->localeToUnicode(cx, buf, vp);
1813 str = JS_NewStringCopyZ(cx, buf);
1814 if (!str)
1815 return JS_FALSE;
1816 *vp = STRING_TO_JSVAL(str);
1817 return JS_TRUE;
1820 static JSBool
1821 date_toLocaleString(JSContext *cx, uintN argc, jsval *vp)
1823 /* Use '%#c' for windows, because '%c' is
1824 * backward-compatible and non-y2k with msvc; '%#c' requests that a
1825 * full year be used in the result string.
1827 return date_toLocaleHelper(cx,
1828 #if defined(_WIN32) && !defined(__MWERKS__)
1829 "%#c"
1830 #else
1831 "%c"
1832 #endif
1833 , vp);
1836 static JSBool
1837 date_toLocaleDateString(JSContext *cx, uintN argc, jsval *vp)
1839 /* Use '%#x' for windows, because '%x' is
1840 * backward-compatible and non-y2k with msvc; '%#x' requests that a
1841 * full year be used in the result string.
1843 return date_toLocaleHelper(cx,
1844 #if defined(_WIN32) && !defined(__MWERKS__)
1845 "%#x"
1846 #else
1847 "%x"
1848 #endif
1849 , vp);
1852 static JSBool
1853 date_toLocaleTimeString(JSContext *cx, uintN argc, jsval *vp)
1855 return date_toLocaleHelper(cx, "%X", vp);
1858 static JSBool
1859 date_toLocaleFormat(JSContext *cx, uintN argc, jsval *vp)
1861 JSString *fmt;
1862 const char *fmtbytes;
1864 if (argc == 0)
1865 return date_toLocaleString(cx, argc, vp);
1867 fmt = js_ValueToString(cx, vp[2]);
1868 if (!fmt)
1869 return JS_FALSE;
1870 vp[2] = STRING_TO_JSVAL(fmt);
1871 fmtbytes = js_GetStringBytes(cx, fmt);
1872 if (!fmtbytes)
1873 return JS_FALSE;
1875 return date_toLocaleHelper(cx, fmtbytes, vp);
1878 static JSBool
1879 date_toTimeString(JSContext *cx, uintN argc, jsval *vp)
1881 jsdouble utctime;
1883 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &utctime))
1884 return JS_FALSE;
1885 return date_format(cx, utctime, FORMATSPEC_TIME, vp);
1888 static JSBool
1889 date_toDateString(JSContext *cx, uintN argc, jsval *vp)
1891 jsdouble utctime;
1893 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &utctime))
1894 return JS_FALSE;
1895 return date_format(cx, utctime, FORMATSPEC_DATE, vp);
1898 #if JS_HAS_TOSOURCE
1899 #include <string.h>
1900 #include "jsdtoa.h"
1902 static JSBool
1903 date_toSource(JSContext *cx, uintN argc, jsval *vp)
1905 jsdouble utctime;
1906 char buf[DTOSTR_STANDARD_BUFFER_SIZE], *numStr, *bytes;
1907 JSString *str;
1909 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &utctime))
1910 return JS_FALSE;
1912 numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, utctime);
1913 if (!numStr) {
1914 JS_ReportOutOfMemory(cx);
1915 return JS_FALSE;
1918 bytes = JS_smprintf("(new %s(%s))", js_Date_str, numStr);
1919 if (!bytes) {
1920 JS_ReportOutOfMemory(cx);
1921 return JS_FALSE;
1924 str = JS_NewString(cx, bytes, strlen(bytes));
1925 if (!str) {
1926 free(bytes);
1927 return JS_FALSE;
1929 *vp = STRING_TO_JSVAL(str);
1930 return JS_TRUE;
1932 #endif
1934 static JSBool
1935 date_toString(JSContext *cx, uintN argc, jsval *vp)
1937 jsdouble utctime;
1939 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &utctime))
1940 return JS_FALSE;
1941 return date_format(cx, utctime, FORMATSPEC_FULL, vp);
1944 static JSBool
1945 date_valueOf(JSContext *cx, uintN argc, jsval *vp)
1947 JSString *str, *str2;
1949 /* It is an error to call date_valueOf on a non-date object, but we don't
1950 * need to check for that explicitly here because every path calls
1951 * GetUTCTime, which does the check.
1954 /* If called directly with no arguments, convert to a time number. */
1955 if (argc == 0)
1956 return date_getTime(cx, argc, vp);
1958 /* Convert to number only if the hint was given, otherwise favor string. */
1959 str = js_ValueToString(cx, vp[2]);
1960 if (!str)
1961 return JS_FALSE;
1962 str2 = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]);
1963 if (js_EqualStrings(str, str2))
1964 return date_getTime(cx, argc, vp);
1965 return date_toString(cx, argc, vp);
1969 * creation and destruction
1972 static JSFunctionSpec date_static_methods[] = {
1973 JS_FN("UTC", date_UTC, MAXARGS,0),
1974 JS_FN("parse", date_parse, 1,0),
1975 JS_FN("now", date_now, 0,0),
1976 JS_FS_END
1979 static JSFunctionSpec date_methods[] = {
1980 JS_FN("getTime", date_getTime, 0,0),
1981 JS_FN("getTimezoneOffset", date_getTimezoneOffset, 0,0),
1982 JS_FN("getYear", date_getYear, 0,0),
1983 JS_FN("getFullYear", date_getFullYear, 0,0),
1984 JS_FN("getUTCFullYear", date_getUTCFullYear, 0,0),
1985 JS_FN("getMonth", date_getMonth, 0,0),
1986 JS_FN("getUTCMonth", date_getUTCMonth, 0,0),
1987 JS_FN("getDate", date_getDate, 0,0),
1988 JS_FN("getUTCDate", date_getUTCDate, 0,0),
1989 JS_FN("getDay", date_getDay, 0,0),
1990 JS_FN("getUTCDay", date_getUTCDay, 0,0),
1991 JS_FN("getHours", date_getHours, 0,0),
1992 JS_FN("getUTCHours", date_getUTCHours, 0,0),
1993 JS_FN("getMinutes", date_getMinutes, 0,0),
1994 JS_FN("getUTCMinutes", date_getUTCMinutes, 0,0),
1995 JS_FN("getSeconds", date_getUTCSeconds, 0,0),
1996 JS_FN("getUTCSeconds", date_getUTCSeconds, 0,0),
1997 JS_FN("getMilliseconds", date_getUTCMilliseconds, 0,0),
1998 JS_FN("getUTCMilliseconds", date_getUTCMilliseconds, 0,0),
1999 JS_FN("setTime", date_setTime, 1,0),
2000 JS_FN("setYear", date_setYear, 1,0),
2001 JS_FN("setFullYear", date_setFullYear, 3,0),
2002 JS_FN("setUTCFullYear", date_setUTCFullYear, 3,0),
2003 JS_FN("setMonth", date_setMonth, 2,0),
2004 JS_FN("setUTCMonth", date_setUTCMonth, 2,0),
2005 JS_FN("setDate", date_setDate, 1,0),
2006 JS_FN("setUTCDate", date_setUTCDate, 1,0),
2007 JS_FN("setHours", date_setHours, 4,0),
2008 JS_FN("setUTCHours", date_setUTCHours, 4,0),
2009 JS_FN("setMinutes", date_setMinutes, 3,0),
2010 JS_FN("setUTCMinutes", date_setUTCMinutes, 3,0),
2011 JS_FN("setSeconds", date_setSeconds, 2,0),
2012 JS_FN("setUTCSeconds", date_setUTCSeconds, 2,0),
2013 JS_FN("setMilliseconds", date_setMilliseconds, 1,0),
2014 JS_FN("setUTCMilliseconds", date_setUTCMilliseconds, 1,0),
2015 JS_FN("toUTCString", date_toGMTString, 0,0),
2016 JS_FN(js_toLocaleString_str, date_toLocaleString, 0,0),
2017 JS_FN("toLocaleDateString", date_toLocaleDateString, 0,0),
2018 JS_FN("toLocaleTimeString", date_toLocaleTimeString, 0,0),
2019 JS_FN("toLocaleFormat", date_toLocaleFormat, 0,0),
2020 JS_FN("toDateString", date_toDateString, 0,0),
2021 JS_FN("toTimeString", date_toTimeString, 0,0),
2022 #if JS_HAS_TOSOURCE
2023 JS_FN(js_toSource_str, date_toSource, 0,0),
2024 #endif
2025 JS_FN(js_toString_str, date_toString, 0,0),
2026 JS_FN(js_valueOf_str, date_valueOf, 0,0),
2027 JS_FS_END
2030 static jsdouble *
2031 date_constructor(JSContext *cx, JSObject* obj)
2033 jsdouble *date;
2035 date = js_NewWeaklyRootedDouble(cx, 0.0);
2036 if (!date)
2037 return NULL;
2039 JS_SetReservedSlot(cx, obj, UTC_TIME_SLOT,
2040 DOUBLE_TO_JSVAL(date));
2041 JS_SetReservedSlot(cx, obj, LOCAL_TIME_SLOT,
2042 DOUBLE_TO_JSVAL(cx->runtime->jsNaN));
2043 return date;
2046 static JSBool
2047 Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2049 jsdouble *date;
2050 JSString *str;
2051 jsdouble d;
2053 /* Date called as function. */
2054 if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
2055 int64 us, ms, us2ms;
2056 jsdouble msec_time;
2058 /* NSPR 2.0 docs say 'We do not support PRMJ_NowMS and PRMJ_NowS',
2059 * so compute ms from PRMJ_Now.
2061 us = PRMJ_Now();
2062 JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC);
2063 JSLL_DIV(ms, us, us2ms);
2064 JSLL_L2D(msec_time, ms);
2066 return date_format(cx, msec_time, FORMATSPEC_FULL, rval);
2069 /* Date called as constructor. */
2070 if (argc == 0) {
2071 int64 us, ms, us2ms;
2072 jsdouble msec_time;
2074 date = date_constructor(cx, obj);
2075 if (!date)
2076 return JS_FALSE;
2078 us = PRMJ_Now();
2079 JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC);
2080 JSLL_DIV(ms, us, us2ms);
2081 JSLL_L2D(msec_time, ms);
2083 *date = msec_time;
2084 } else if (argc == 1) {
2085 if (!JSVAL_IS_STRING(argv[0])) {
2086 /* the argument is a millisecond number */
2087 d = js_ValueToNumber(cx, &argv[0]);
2088 if (JSVAL_IS_NULL(argv[0]))
2089 return JS_FALSE;
2090 date = date_constructor(cx, obj);
2091 if (!date)
2092 return JS_FALSE;
2093 *date = TIMECLIP(d);
2094 } else {
2095 /* the argument is a string; parse it. */
2096 date = date_constructor(cx, obj);
2097 if (!date)
2098 return JS_FALSE;
2100 str = js_ValueToString(cx, argv[0]);
2101 if (!str)
2102 return JS_FALSE;
2104 if (!date_parseString(str, date))
2105 *date = *cx->runtime->jsNaN;
2106 *date = TIMECLIP(*date);
2108 } else {
2109 jsdouble *date;
2110 jsdouble msec_time;
2112 if (!date_msecFromArgs(cx, argc, argv, &msec_time))
2113 return JS_FALSE;
2115 date = date_constructor(cx, obj);
2116 if (!date)
2117 return JS_FALSE;
2119 if (JSDOUBLE_IS_FINITE(msec_time)) {
2120 msec_time = UTC(msec_time);
2121 msec_time = TIMECLIP(msec_time);
2124 *date = msec_time;
2126 return JS_TRUE;
2129 JSObject *
2130 js_InitDateClass(JSContext *cx, JSObject *obj)
2132 JSObject *proto;
2133 jsdouble *proto_date;
2135 /* set static LocalTZA */
2136 LocalTZA = -(PRMJ_LocalGMTDifference() * msPerSecond);
2137 proto = JS_InitClass(cx, obj, NULL, &js_DateClass, Date, MAXARGS,
2138 NULL, date_methods, NULL, date_static_methods);
2139 if (!proto)
2140 return NULL;
2142 /* Alias toUTCString with toGMTString. (ECMA B.2.6) */
2143 if (!JS_AliasProperty(cx, proto, "toUTCString", "toGMTString"))
2144 return NULL;
2146 /* Set the value of the Date.prototype date to NaN */
2147 proto_date = date_constructor(cx, proto);
2148 if (!proto_date)
2149 return NULL;
2150 *proto_date = *cx->runtime->jsNaN;
2152 return proto;
2155 JS_FRIEND_API(JSObject *)
2156 js_NewDateObjectMsec(JSContext *cx, jsdouble msec_time)
2158 JSObject *obj;
2159 jsdouble *date;
2161 obj = js_NewObject(cx, &js_DateClass, NULL, NULL, 0);
2162 if (!obj)
2163 return NULL;
2165 date = date_constructor(cx, obj);
2166 if (!date)
2167 return NULL;
2169 *date = msec_time;
2170 return obj;
2173 JS_FRIEND_API(JSObject *)
2174 js_NewDateObject(JSContext* cx, int year, int mon, int mday,
2175 int hour, int min, int sec)
2177 JSObject *obj;
2178 jsdouble msec_time;
2180 JS_ASSERT(mon < 12);
2181 msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
2182 obj = js_NewDateObjectMsec(cx, UTC(msec_time));
2183 return obj;
2186 JS_FRIEND_API(JSBool)
2187 js_DateIsValid(JSContext *cx, JSObject* obj)
2189 jsdouble utctime;
2190 return GetUTCTime(cx, obj, NULL, &utctime) && !JSDOUBLE_IS_NaN(utctime);
2193 JS_FRIEND_API(int)
2194 js_DateGetYear(JSContext *cx, JSObject* obj)
2196 jsdouble localtime;
2198 /* Preserve legacy API behavior of returning 0 for invalid dates. */
2199 if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) ||
2200 JSDOUBLE_IS_NaN(localtime)) {
2201 return 0;
2204 return (int) YearFromTime(localtime);
2207 JS_FRIEND_API(int)
2208 js_DateGetMonth(JSContext *cx, JSObject* obj)
2210 jsdouble localtime;
2212 if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) ||
2213 JSDOUBLE_IS_NaN(localtime)) {
2214 return 0;
2217 return (int) MonthFromTime(localtime);
2220 JS_FRIEND_API(int)
2221 js_DateGetDate(JSContext *cx, JSObject* obj)
2223 jsdouble localtime;
2225 if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) ||
2226 JSDOUBLE_IS_NaN(localtime)) {
2227 return 0;
2230 return (int) DateFromTime(localtime);
2233 JS_FRIEND_API(int)
2234 js_DateGetHours(JSContext *cx, JSObject* obj)
2236 jsdouble localtime;
2238 if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) ||
2239 JSDOUBLE_IS_NaN(localtime)) {
2240 return 0;
2243 return (int) HourFromTime(localtime);
2246 JS_FRIEND_API(int)
2247 js_DateGetMinutes(JSContext *cx, JSObject* obj)
2249 jsdouble localtime;
2251 if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) ||
2252 JSDOUBLE_IS_NaN(localtime)) {
2253 return 0;
2256 return (int) MinFromTime(localtime);
2259 JS_FRIEND_API(int)
2260 js_DateGetSeconds(JSContext *cx, JSObject* obj)
2262 jsdouble utctime;
2264 if (!GetUTCTime(cx, obj, NULL, &utctime) || JSDOUBLE_IS_NaN(utctime))
2265 return 0;
2267 return (int) SecFromTime(utctime);
2270 JS_FRIEND_API(void)
2271 js_DateSetYear(JSContext *cx, JSObject *obj, int year)
2273 jsdouble local;
2275 if (!GetAndCacheLocalTime(cx, obj, NULL, &local))
2276 return;
2278 /* reset date if it was NaN */
2279 if (JSDOUBLE_IS_NaN(local))
2280 local = 0;
2282 local = date_msecFromDate(year,
2283 MonthFromTime(local),
2284 DateFromTime(local),
2285 HourFromTime(local),
2286 MinFromTime(local),
2287 SecFromTime(local),
2288 msFromTime(local));
2290 /* SetUTCTime also invalidates local time cache. */
2291 SetUTCTime(cx, obj, NULL, UTC(local));
2294 JS_FRIEND_API(void)
2295 js_DateSetMonth(JSContext *cx, JSObject *obj, int month)
2297 jsdouble local;
2299 JS_ASSERT(month < 12);
2301 if (!GetAndCacheLocalTime(cx, obj, NULL, &local))
2302 return;
2304 /* bail if date was NaN */
2305 if (JSDOUBLE_IS_NaN(local))
2306 return;
2308 local = date_msecFromDate(YearFromTime(local),
2309 month,
2310 DateFromTime(local),
2311 HourFromTime(local),
2312 MinFromTime(local),
2313 SecFromTime(local),
2314 msFromTime(local));
2315 SetUTCTime(cx, obj, NULL, UTC(local));
2318 JS_FRIEND_API(void)
2319 js_DateSetDate(JSContext *cx, JSObject *obj, int date)
2321 jsdouble local;
2323 if (!GetAndCacheLocalTime(cx, obj, NULL, &local))
2324 return;
2326 if (JSDOUBLE_IS_NaN(local))
2327 return;
2329 local = date_msecFromDate(YearFromTime(local),
2330 MonthFromTime(local),
2331 date,
2332 HourFromTime(local),
2333 MinFromTime(local),
2334 SecFromTime(local),
2335 msFromTime(local));
2336 SetUTCTime(cx, obj, NULL, UTC(local));
2339 JS_FRIEND_API(void)
2340 js_DateSetHours(JSContext *cx, JSObject *obj, int hours)
2342 jsdouble local;
2344 if (!GetAndCacheLocalTime(cx, obj, NULL, &local))
2345 return;
2347 if (JSDOUBLE_IS_NaN(local))
2348 return;
2349 local = date_msecFromDate(YearFromTime(local),
2350 MonthFromTime(local),
2351 DateFromTime(local),
2352 hours,
2353 MinFromTime(local),
2354 SecFromTime(local),
2355 msFromTime(local));
2356 SetUTCTime(cx, obj, NULL, UTC(local));
2359 JS_FRIEND_API(void)
2360 js_DateSetMinutes(JSContext *cx, JSObject *obj, int minutes)
2362 jsdouble local;
2364 if (!GetAndCacheLocalTime(cx, obj, NULL, &local))
2365 return;
2367 if (JSDOUBLE_IS_NaN(local))
2368 return;
2369 local = date_msecFromDate(YearFromTime(local),
2370 MonthFromTime(local),
2371 DateFromTime(local),
2372 HourFromTime(local),
2373 minutes,
2374 SecFromTime(local),
2375 msFromTime(local));
2376 SetUTCTime(cx, obj, NULL, UTC(local));
2379 JS_FRIEND_API(void)
2380 js_DateSetSeconds(JSContext *cx, JSObject *obj, int seconds)
2382 jsdouble local;
2384 if (!GetAndCacheLocalTime(cx, obj, NULL, &local))
2385 return;
2387 if (JSDOUBLE_IS_NaN(local))
2388 return;
2389 local = date_msecFromDate(YearFromTime(local),
2390 MonthFromTime(local),
2391 DateFromTime(local),
2392 HourFromTime(local),
2393 MinFromTime(local),
2394 seconds,
2395 msFromTime(local));
2396 SetUTCTime(cx, obj, NULL, UTC(local));
2399 JS_FRIEND_API(jsdouble)
2400 js_DateGetMsecSinceEpoch(JSContext *cx, JSObject *obj)
2402 jsdouble utctime;
2403 if (!GetUTCTime(cx, obj, NULL, &utctime))
2404 return 0;
2405 return utctime;