1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 *
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 *
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
10 *
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
15 *
16 * The Original Code is Mozilla Communicator client code, released
17 * March 31, 1998.
18 *
19 * The Initial Developer of the Original Code is
20 * Netscape Communications Corporation.
21 * Portions created by the Initial Developer are Copyright (C) 1998
22 * the Initial Developer. All Rights Reserved.
23 *
24 * Contributor(s):
25 *
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
37 *
38 * ***** END LICENSE BLOCK ***** */
40 /*
41 * JS date methods.
42 */
44 /*
45 * "For example, OS/360 devotes 26 bytes of the permanently
46 * resident date-turnover routine to the proper handling of
47 * December 31 on leap years (when it is Day 366). That
48 * might have been left to the operator."
49 *
50 * Frederick Brooks, 'The Second-System Effect'.
51 */
53 #include "jsstddef.h"
54 #include <ctype.h>
55 #include <locale.h>
56 #include <math.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include "jstypes.h"
60 #include "jsprf.h"
61 #include "prmjtime.h"
62 #include "jsutil.h" /* Added by JSIFY */
63 #include "jsapi.h"
64 #include "jsconfig.h"
65 #include "jscntxt.h"
66 #include "jsdate.h"
67 #include "jsinterp.h"
68 #include "jsnum.h"
69 #include "jsobj.h"
70 #include "jsstr.h"
72 /*
73 * The JS 'Date' object is patterned after the Java 'Date' object.
74 * Here is an script:
75 *
76 * today = new Date();
77 *
78 * print(today.toLocaleString());
79 *
80 * weekDay = today.getDay();
81 *
82 *
83 * These Java (and ECMA-262) methods are supported:
84 *
85 * UTC
86 * getDate (getUTCDate)
87 * getDay (getUTCDay)
88 * getHours (getUTCHours)
89 * getMinutes (getUTCMinutes)
90 * getMonth (getUTCMonth)
91 * getSeconds (getUTCSeconds)
92 * getMilliseconds (getUTCMilliseconds)
93 * getTime
94 * getTimezoneOffset
95 * getYear
96 * getFullYear (getUTCFullYear)
97 * parse
98 * setDate (setUTCDate)
99 * setHours (setUTCHours)
100 * setMinutes (setUTCMinutes)
101 * setMonth (setUTCMonth)
102 * setSeconds (setUTCSeconds)
103 * setMilliseconds (setUTCMilliseconds)
104 * setTime
105 * setYear (setFullYear, setUTCFullYear)
106 * toGMTString (toUTCString)
107 * toLocaleString
108 * toString
109 *
110 *
111 * These Java methods are not supported
112 *
113 * setDay
114 * before
115 * after
116 * equals
117 * hashCode
118 */
120 /*
121 * 11/97 - jsdate.c has been rewritten to conform to the ECMA-262 language
122 * definition and reduce dependence on NSPR. NSPR is used to get the current
123 * time in milliseconds, the time zone offset, and the daylight savings time
124 * offset for a given time. NSPR is also used for Date.toLocaleString(), for
125 * locale-specific formatting, and to get a string representing the timezone.
126 * (Which turns out to be platform-dependent.)
127 *
128 * To do:
129 * (I did some performance tests by timing how long it took to run what
130 * I had of the js ECMA conformance tests.)
131 *
132 * - look at saving results across multiple calls to supporting
133 * functions; the toString functions compute some of the same values
134 * multiple times. Although - I took a quick stab at this, and I lost
135 * rather than gained. (Fractionally.) Hard to tell what compilers/processors
136 * are doing these days.
137 *
138 * - look at tweaking function return types to return double instead
139 * of int; this seems to make things run slightly faster sometimes.
140 * (though it could be architecture-dependent.) It'd be good to see
141 * how this does on win32. (Tried it on irix.) Types could use a
142 * general going-over.
143 */
145 /*
146 * Supporting functions - ECMA 15.9.1.*
147 */
149 #define HalfTimeDomain 8.64e15
150 #define HoursPerDay 24.0
151 #define MinutesPerDay (HoursPerDay * MinutesPerHour)
152 #define MinutesPerHour 60.0
153 #define SecondsPerDay (MinutesPerDay * SecondsPerMinute)
154 #define SecondsPerHour (MinutesPerHour * SecondsPerMinute)
155 #define SecondsPerMinute 60.0
157 #if defined(XP_WIN) || defined(XP_OS2)
158 /* Work around msvc double optimization bug by making these runtime values; if
159 * they're available at compile time, msvc optimizes division by them by
160 * computing the reciprocal and multiplying instead of dividing - this loses
161 * when the reciprocal isn't representable in a double.
162 */
163 static jsdouble msPerSecond = 1000.0;
164 static jsdouble msPerDay = SecondsPerDay * 1000.0;
165 static jsdouble msPerHour = SecondsPerHour * 1000.0;
166 static jsdouble msPerMinute = SecondsPerMinute * 1000.0;
167 #else
168 #define msPerDay (SecondsPerDay * msPerSecond)
169 #define msPerHour (SecondsPerHour * msPerSecond)
170 #define msPerMinute (SecondsPerMinute * msPerSecond)
171 #define msPerSecond 1000.0
172 #endif
174 #define Day(t) floor((t) / msPerDay)
176 static jsdouble
177 TimeWithinDay(jsdouble t)
178 {
179 jsdouble result;
180 result = fmod(t, msPerDay);
181 if (result < 0)
182 result += msPerDay;
183 return result;
184 }
186 #define DaysInYear(y) ((y) % 4 == 0 && ((y) % 100 || ((y) % 400 == 0)) \
187 ? 366 : 365)
189 /* math here has to be f.p, because we need
190 * floor((1968 - 1969) / 4) == -1
191 */
192 #define DayFromYear(y) (365 * ((y)-1970) + floor(((y)-1969)/4.0) \
193 - floor(((y)-1901)/100.0) + floor(((y)-1601)/400.0))
194 #define TimeFromYear(y) (DayFromYear(y) * msPerDay)
196 static jsint
197 YearFromTime(jsdouble t)
198 {
199 jsint y = (jsint) floor(t /(msPerDay*365.2425)) + 1970;
200 jsdouble t2 = (jsdouble) TimeFromYear(y);
202 if (t2 > t) {
203 y--;
204 } else {
205 if (t2 + msPerDay * DaysInYear(y) <= t)
206 y++;
207 }
208 return y;
209 }
211 #define InLeapYear(t) (JSBool) (DaysInYear(YearFromTime(t)) == 366)
213 #define DayWithinYear(t, year) ((intN) (Day(t) - DayFromYear(year)))
215 /*
216 * The following array contains the day of year for the first day of
217 * each month, where index 0 is January, and day 0 is January 1.
218 */
219 static jsdouble firstDayOfMonth[2][12] = {
220 {0.0, 31.0, 59.0, 90.0, 120.0, 151.0, 181.0, 212.0, 243.0, 273.0, 304.0, 334.0},
221 {0.0, 31.0, 60.0, 91.0, 121.0, 152.0, 182.0, 213.0, 244.0, 274.0, 305.0, 335.0}
222 };
224 #define DayFromMonth(m, leap) firstDayOfMonth[leap][(intN)m];
226 static intN
227 MonthFromTime(jsdouble t)
228 {
229 intN d, step;
230 jsint year = YearFromTime(t);
231 d = DayWithinYear(t, year);
233 if (d < (step = 31))
234 return 0;
235 step += (InLeapYear(t) ? 29 : 28);
236 if (d < step)
237 return 1;
238 if (d < (step += 31))
239 return 2;
240 if (d < (step += 30))
241 return 3;
242 if (d < (step += 31))
243 return 4;
244 if (d < (step += 30))
245 return 5;
246 if (d < (step += 31))
247 return 6;
248 if (d < (step += 31))
249 return 7;
250 if (d < (step += 30))
251 return 8;
252 if (d < (step += 31))
253 return 9;
254 if (d < (step += 30))
255 return 10;
256 return 11;
257 }
259 static intN
260 DateFromTime(jsdouble t)
261 {
262 intN d, step, next;
263 jsint year = YearFromTime(t);
264 d = DayWithinYear(t, year);
266 if (d <= (next = 30))
267 return d + 1;
268 step = next;
269 next += (InLeapYear(t) ? 29 : 28);
270 if (d <= next)
271 return d - step;
272 step = next;
273 if (d <= (next += 31))
274 return d - step;
275 step = next;
276 if (d <= (next += 30))
277 return d - step;
278 step = next;
279 if (d <= (next += 31))
280 return d - step;
281 step = next;
282 if (d <= (next += 30))
283 return d - step;
284 step = next;
285 if (d <= (next += 31))
286 return d - step;
287 step = next;
288 if (d <= (next += 31))
289 return d - step;
290 step = next;
291 if (d <= (next += 30))
292 return d - step;
293 step = next;
294 if (d <= (next += 31))
295 return d - step;
296 step = next;
297 if (d <= (next += 30))
298 return d - step;
299 step = next;
300 return d - step;
301 }
303 static intN
304 WeekDay(jsdouble t)
305 {
306 jsint result;
307 result = (jsint) Day(t) + 4;
308 result = result % 7;
309 if (result < 0)
310 result += 7;
311 return (intN) result;
312 }
314 #define MakeTime(hour, min, sec, ms) \
315 ((((hour) * MinutesPerHour + (min)) * SecondsPerMinute + (sec)) * msPerSecond + (ms))
317 static jsdouble
318 MakeDay(jsdouble year, jsdouble month, jsdouble date)
319 {
320 JSBool leap;
321 jsdouble yearday;
322 jsdouble monthday;
324 year += floor(month / 12);
326 month = fmod(month, 12.0);
327 if (month < 0)
328 month += 12;
330 leap = (DaysInYear((jsint) year) == 366);
332 yearday = floor(TimeFromYear(year) / msPerDay);
333 monthday = DayFromMonth(month, leap);
335 return yearday + monthday + date - 1;
336 }
338 #define MakeDate(day, time) ((day) * msPerDay + (time))
340 /*
341 * Years and leap years on which Jan 1 is a Sunday, Monday, etc.
342 *
343 * yearStartingWith[0][i] is an example non-leap year where
344 * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
345 *
346 * yearStartingWith[1][i] is an example leap year where
347 * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
348 */
349 static jsint yearStartingWith[2][7] = {
350 {1978, 1973, 1974, 1975, 1981, 1971, 1977},
351 {1984, 1996, 1980, 1992, 1976, 1988, 1972}
352 };
354 /*
355 * Find a year for which any given date will fall on the same weekday.
356 *
357 * This function should be used with caution when used other than
358 * for determining DST; it hasn't been proven not to produce an
359 * incorrect year for times near year boundaries.
360 */
361 static jsint
362 EquivalentYearForDST(jsint year) {
363 jsint day;
364 JSBool isLeapYear;
366 day = (jsint) DayFromYear(year) + 4;
367 day = day % 7;
368 if (day < 0)
369 day += 7;
371 isLeapYear = (DaysInYear(year) == 366);
373 return yearStartingWith[isLeapYear][day];
374 }
376 /* LocalTZA gets set by js_InitDateClass() */
377 static jsdouble LocalTZA;
379 static jsdouble
380 DaylightSavingTA(jsdouble t)
381 {
382 volatile int64 PR_t;
383 int64 ms2us;
384 int64 offset;
385 jsdouble result;
387 /* abort if NaN */
388 if (JSDOUBLE_IS_NaN(t))
389 return t;
391 /*
392 * If earlier than 1970 or after 2038, potentially beyond the ken of
393 * many OSes, map it to an equivalent year before asking.
394 */
395 if (t < 0.0 || t > 2145916800000.0) {
396 jsint year;
397 jsdouble day;
399 year = EquivalentYearForDST(YearFromTime(t));
400 day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
401 t = MakeDate(day, TimeWithinDay(t));
402 }
404 /* put our t in an LL, and map it to usec for prtime */
405 JSLL_D2L(PR_t, t);
406 JSLL_I2L(ms2us, PRMJ_USEC_PER_MSEC);
407 JSLL_MUL(PR_t, PR_t, ms2us);
409 offset = PRMJ_DSTOffset(PR_t);
411 JSLL_DIV(offset, offset, ms2us);
412 JSLL_L2D(result, offset);
413 return result;
414 }
417 #define AdjustTime(t) fmod(LocalTZA + DaylightSavingTA(t), msPerDay)
419 #define LocalTime(t) ((t) + AdjustTime(t))
421 static jsdouble
422 UTC(jsdouble t)
423 {
424 return t - AdjustTime(t - LocalTZA);
425 }
427 static intN
428 HourFromTime(jsdouble t)
429 {
430 intN result = (intN) fmod(floor(t/msPerHour), HoursPerDay);
431 if (result < 0)
432 result += (intN)HoursPerDay;
433 return result;
434 }
436 static intN
437 MinFromTime(jsdouble t)
438 {
439 intN result = (intN) fmod(floor(t / msPerMinute), MinutesPerHour);
440 if (result < 0)
441 result += (intN)MinutesPerHour;
442 return result;
443 }
445 static intN
446 SecFromTime(jsdouble t)
447 {
448 intN result = (intN) fmod(floor(t / msPerSecond), SecondsPerMinute);
449 if (result < 0)
450 result += (intN)SecondsPerMinute;
451 return result;
452 }
454 static intN
455 msFromTime(jsdouble t)
456 {
457 intN result = (intN) fmod(t, msPerSecond);
458 if (result < 0)
459 result += (intN)msPerSecond;
460 return result;
461 }
463 #define TIMECLIP(d) ((JSDOUBLE_IS_FINITE(d) \
464 && !((d < 0 ? -d : d) > HalfTimeDomain)) \
465 ? js_DoubleToInteger(d + (+0.)) : *cx->runtime->jsNaN)
467 /**
468 * end of ECMA 'support' functions
469 */
470 \f
471 /*
472 * Other Support routines and definitions
473 */
475 static JSClass date_class = {
476 js_Date_str,
477 JSCLASS_HAS_PRIVATE,
478 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
479 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
480 JSCLASS_NO_OPTIONAL_MEMBERS
481 };
483 /* for use by date_parse */
485 static const char* wtb[] = {
486 "am", "pm",
487 "monday", "tuesday", "wednesday", "thursday", "friday",
488 "saturday", "sunday",
489 "january", "february", "march", "april", "may", "june",
490 "july", "august", "september", "october", "november", "december",
491 "gmt", "ut", "utc",
492 "est", "edt",
493 "cst", "cdt",
494 "mst", "mdt",
495 "pst", "pdt"
496 /* time zone table needs to be expanded */
497 };
499 static int ttb[] = {
500 -1, -2, 0, 0, 0, 0, 0, 0, 0, /* AM/PM */
501 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
502 10000 + 0, 10000 + 0, 10000 + 0, /* GMT/UT/UTC */
503 10000 + 5 * 60, 10000 + 4 * 60, /* EST/EDT */
504 10000 + 6 * 60, 10000 + 5 * 60, /* CST/CDT */
505 10000 + 7 * 60, 10000 + 6 * 60, /* MST/MDT */
506 10000 + 8 * 60, 10000 + 7 * 60 /* PST/PDT */
507 };
509 /* helper for date_parse */
510 static JSBool
511 date_regionMatches(const char* s1, int s1off, const jschar* s2, int s2off,
512 int count, int ignoreCase)
513 {
514 JSBool result = JS_FALSE;
515 /* return true if matches, otherwise, false */
517 while (count > 0 && s1[s1off] && s2[s2off]) {
518 if (ignoreCase) {
519 if (JS_TOLOWER((jschar)s1[s1off]) != JS_TOLOWER(s2[s2off])) {
520 break;
521 }
522 } else {
523 if ((jschar)s1[s1off] != s2[s2off]) {
524 break;
525 }
526 }
527 s1off++;
528 s2off++;
529 count--;
530 }
532 if (count == 0) {
533 result = JS_TRUE;
534 }
536 return result;
537 }
539 /* find UTC time from given date... no 1900 correction! */
540 static jsdouble
541 date_msecFromDate(jsdouble year, jsdouble mon, jsdouble mday, jsdouble hour,
542 jsdouble min, jsdouble sec, jsdouble msec)
543 {
544 jsdouble day;
545 jsdouble msec_time;
546 jsdouble result;
548 day = MakeDay(year, mon, mday);
549 msec_time = MakeTime(hour, min, sec, msec);
550 result = MakeDate(day, msec_time);
551 return result;
552 }
553 \f
554 /*
555 * See ECMA 15.9.4.[3-10];
556 */
557 /* XXX this function must be above date_parseString to avoid a
558 horrid bug in the Win16 1.52 compiler */
559 #define MAXARGS 7
560 static JSBool
561 date_UTC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
562 {
563 jsdouble array[MAXARGS];
564 uintN loop;
565 jsdouble d;
567 for (loop = 0; loop < MAXARGS; loop++) {
568 if (loop < argc) {
569 if (!js_ValueToNumber(cx, argv[loop], &d))
570 return JS_FALSE;
571 /* return NaN if any arg is NaN */
572 if (!JSDOUBLE_IS_FINITE(d)) {
573 return js_NewNumberValue(cx, d, rval);
574 }
575 array[loop] = floor(d);
576 } else {
577 array[loop] = 0;
578 }
579 }
581 /* adjust 2-digit years into the 20th century */
582 if (array[0] >= 0 && array[0] <= 99)
583 array[0] += 1900;
585 /* if we got a 0 for 'date' (which is out of range)
586 * pretend it's a 1. (So Date.UTC(1972, 5) works) */
587 if (array[2] < 1)
588 array[2] = 1;
590 d = date_msecFromDate(array[0], array[1], array[2],
591 array[3], array[4], array[5], array[6]);
592 d = TIMECLIP(d);
594 return js_NewNumberValue(cx, d, rval);
595 }
597 static JSBool
598 date_parseString(JSString *str, jsdouble *result)
599 {
600 jsdouble msec;
602 const jschar *s = JSSTRING_CHARS(str);
603 size_t limit = JSSTRING_LENGTH(str);
604 size_t i = 0;
605 int year = -1;
606 int mon = -1;
607 int mday = -1;
608 int hour = -1;
609 int min = -1;
610 int sec = -1;
611 int c = -1;
612 int n = -1;
613 jsdouble tzoffset = -1; /* was an int, overflowed on win16!!! */
614 int prevc = 0;
615 JSBool seenplusminus = JS_FALSE;
616 int temp;
617 JSBool seenmonthname = JS_FALSE;
619 if (limit == 0)
620 goto syntax;
621 while (i < limit) {
622 c = s[i];
623 i++;
624 if (c <= ' ' || c == ',' || c == '-') {
625 if (c == '-' && '0' <= s[i] && s[i] <= '9') {
626 prevc = c;
627 }
628 continue;
629 }
630 if (c == '(') { /* comments) */
631 int depth = 1;
632 while (i < limit) {
633 c = s[i];
634 i++;
635 if (c == '(') depth++;
636 else if (c == ')')
637 if (--depth <= 0)
638 break;
639 }
640 continue;
641 }
642 if ('0' <= c && c <= '9') {
643 n = c - '0';
644 while (i < limit && '0' <= (c = s[i]) && c <= '9') {
645 n = n * 10 + c - '0';
646 i++;
647 }
649 /* allow TZA before the year, so
650 * 'Wed Nov 05 21:49:11 GMT-0800 1997'
651 * works */
653 /* uses of seenplusminus allow : in TZA, so Java
654 * no-timezone style of GMT+4:30 works
655 */
657 if ((prevc == '+' || prevc == '-')/* && year>=0 */) {
658 /* make ':' case below change tzoffset */
659 seenplusminus = JS_TRUE;
661 /* offset */
662 if (n < 24)
663 n = n * 60; /* EG. "GMT-3" */
664 else
665 n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */
666 if (prevc == '+') /* plus means east of GMT */
667 n = -n;
668 if (tzoffset != 0 && tzoffset != -1)
669 goto syntax;
670 tzoffset = n;
671 } else if (prevc == '/' && mon >= 0 && mday >= 0 && year < 0) {
672 if (c <= ' ' || c == ',' || c == '/' || i >= limit)
673 year = n;
674 else
675 goto syntax;
676 } else if (c == ':') {
677 if (hour < 0)
678 hour = /*byte*/ n;
679 else if (min < 0)
680 min = /*byte*/ n;
681 else
682 goto syntax;
683 } else if (c == '/') {
684 /* until it is determined that mon is the actual
685 month, keep it as 1-based rather than 0-based */
686 if (mon < 0)
687 mon = /*byte*/ n;
688 else if (mday < 0)
689 mday = /*byte*/ n;
690 else
691 goto syntax;
692 } else if (i < limit && c != ',' && c > ' ' && c != '-' && c != '(') {
693 goto syntax;
694 } else if (seenplusminus && n < 60) { /* handle GMT-3:30 */
695 if (tzoffset < 0)
696 tzoffset -= n;
697 else
698 tzoffset += n;
699 } else if (hour >= 0 && min < 0) {
700 min = /*byte*/ n;
701 } else if (prevc == ':' && min >= 0 && sec < 0) {
702 sec = /*byte*/ n;
703 } else if (mon < 0) {
704 mon = /*byte*/n;
705 } else if (mon >= 0 && mday < 0) {
706 mday = /*byte*/ n;
707 } else if (mon >= 0 && mday >= 0 && year < 0) {
708 year = n;
709 } else {
710 goto syntax;
711 }
712 prevc = 0;
713 } else if (c == '/' || c == ':' || c == '+' || c == '-') {
714 prevc = c;
715 } else {
716 size_t st = i - 1;
717 int k;
718 while (i < limit) {
719 c = s[i];
720 if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')))
721 break;
722 i++;
723 }
724 if (i <= st + 1)
725 goto syntax;
726 for (k = (sizeof(wtb)/sizeof(char*)); --k >= 0;)
727 if (date_regionMatches(wtb[k], 0, s, st, i-st, 1)) {
728 int action = ttb[k];
729 if (action != 0) {
730 if (action < 0) {
731 /*
732 * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as
733 * 12:30, instead of blindly adding 12 if PM.
734 */
735 JS_ASSERT(action == -1 || action == -2);
736 if (hour > 12 || hour < 0) {
737 goto syntax;
738 } else {
739 if (action == -1 && hour == 12) { /* am */
740 hour = 0;
741 } else if (action == -2 && hour != 12) { /* pm */
742 hour += 12;
743 }
744 }
745 } else if (action <= 13) { /* month! */
746 /* Adjust mon to be 1-based until the final values
747 for mon, mday and year are adjusted below */
748 if (seenmonthname) {
749 goto syntax;
750 }
751 seenmonthname = JS_TRUE;
752 temp = /*byte*/ (action - 2) + 1;
754 if (mon < 0) {
755 mon = temp;
756 } else if (mday < 0) {
757 mday = mon;
758 mon = temp;
759 } else if (year < 0) {
760 year = mon;
761 mon = temp;
762 } else {
763 goto syntax;
764 }
765 } else {
766 tzoffset = action - 10000;
767 }
768 }
769 break;
770 }
771 if (k < 0)
772 goto syntax;
773 prevc = 0;
774 }
775 }
776 if (year < 0 || mon < 0 || mday < 0)
777 goto syntax;
778 /*
779 Case 1. The input string contains an English month name.
780 The form of the string can be month f l, or f month l, or
781 f l month which each evaluate to the same date.
782 If f and l are both greater than or equal to 70, or
783 both less than 70, the date is invalid.
784 The year is taken to be the greater of the values f, l.
785 If the year is greater than or equal to 70 and less than 100,
786 it is considered to be the number of years after 1900.
787 Case 2. The input string is of the form "f/m/l" where f, m and l are
788 integers, e.g. 7/16/45.
789 Adjust the mon, mday and year values to achieve 100% MSIE
790 compatibility.
791 a. If 0 <= f < 70, f/m/l is interpreted as month/day/year.
792 i. If year < 100, it is the number of years after 1900
793 ii. If year >= 100, it is the number of years after 0.
794 b. If 70 <= f < 100
795 i. If m < 70, f/m/l is interpreted as
796 year/month/day where year is the number of years after
797 1900.
798 ii. If m >= 70, the date is invalid.
799 c. If f >= 100
800 i. If m < 70, f/m/l is interpreted as
801 year/month/day where year is the number of years after 0.
802 ii. If m >= 70, the date is invalid.
803 */
804 if (seenmonthname) {
805 if ((mday >= 70 && year >= 70) || (mday < 70 && year < 70)) {
806 goto syntax;
807 }
808 if (mday > year) {
809 temp = year;
810 year = mday;
811 mday = temp;
812 }
813 if (year >= 70 && year < 100) {
814 year += 1900;
815 }
816 } else if (mon < 70) { /* (a) month/day/year */
817 if (year < 100) {
818 year += 1900;
819 }
820 } else if (mon < 100) { /* (b) year/month/day */
821 if (mday < 70) {
822 temp = year;
823 year = mon + 1900;
824 mon = mday;
825 mday = temp;
826 } else {
827 goto syntax;
828 }
829 } else { /* (c) year/month/day */
830 if (mday < 70) {
831 temp = year;
832 year = mon;
833 mon = mday;
834 mday = temp;
835 } else {
836 goto syntax;
837 }
838 }
839 mon -= 1; /* convert month to 0-based */
840 if (sec < 0)
841 sec = 0;
842 if (min < 0)
843 min = 0;
844 if (hour < 0)
845 hour = 0;
846 if (tzoffset == -1) { /* no time zone specified, have to use local */
847 jsdouble msec_time;
848 msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
850 *result = UTC(msec_time);
851 return JS_TRUE;
852 }
854 msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
855 msec += tzoffset * msPerMinute;
856 *result = msec;
857 return JS_TRUE;
859 syntax:
860 /* syntax error */
861 *result = 0;
862 return JS_FALSE;
863 }
865 static JSBool
866 date_parse(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
867 {
868 JSString *str;
869 jsdouble result;
871 str = js_ValueToString(cx, argv[0]);
872 if (!str)
873 return JS_FALSE;
874 if (!date_parseString(str, &result)) {
875 *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
876 return JS_TRUE;
877 }
879 result = TIMECLIP(result);
880 return js_NewNumberValue(cx, result, rval);
881 }
883 static JSBool
884 date_now(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
885 {
886 int64 us, ms, us2ms;
887 jsdouble msec_time;
889 us = PRMJ_Now();
890 JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC);
891 JSLL_DIV(ms, us, us2ms);
892 JSLL_L2D(msec_time, ms);
894 return js_NewDoubleValue(cx, msec_time, rval);
895 }
897 /*
898 * Check that obj is an object of class Date, and get the date value.
899 * Return NULL on failure.
900 */
901 static jsdouble *
902 date_getProlog(JSContext *cx, JSObject *obj, jsval *argv)
903 {
904 if (!JS_InstanceOf(cx, obj, &date_class, argv))
905 return NULL;
906 return JSVAL_TO_DOUBLE(OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE));
907 }
909 /*
910 * See ECMA 15.9.5.4 thru 15.9.5.23
911 */
912 static JSBool
913 date_getTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
914 {
915 jsdouble *date = date_getProlog(cx, obj, argv);
916 if (!date)
917 return JS_FALSE;
919 return js_NewNumberValue(cx, *date, rval);
920 }
922 static JSBool
923 date_getYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
924 {
925 jsdouble *date;
926 jsdouble result;
927 JSVersion version;
929 date = date_getProlog(cx, obj, argv);
930 if (!date)
931 return JS_FALSE;
933 result = *date;
934 if (!JSDOUBLE_IS_FINITE(result))
935 return js_NewNumberValue(cx, result, rval);
937 result = YearFromTime(LocalTime(result));
939 /*
940 * During the great date rewrite of 1.3, we tried to track the evolving ECMA
941 * standard, which then had a definition of getYear which always subtracted
942 * 1900. Which we implemented, not realizing that it was incompatible with
943 * the old behavior... now, rather than thrash the behavior yet again,
944 * we've decided to leave it with the - 1900 behavior and point people to
945 * the getFullYear method. But we try to protect existing scripts that
946 * have specified a version...
947 */
948 version = cx->version & JSVERSION_MASK;
949 if (version == JSVERSION_1_0 ||
950 version == JSVERSION_1_1 ||
951 version == JSVERSION_1_2)
952 {
953 if (result >= 1900 && result < 2000)
954 result -= 1900;
955 } else {
956 result -= 1900;
957 }
958 return js_NewNumberValue(cx, result, rval);
959 }
961 static JSBool
962 date_getFullYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
963 jsval *rval)
964 {
965 jsdouble result;
966 jsdouble *date = date_getProlog(cx, obj, argv);
967 if (!date)
968 return JS_FALSE;
969 result = *date;
971 if (!JSDOUBLE_IS_FINITE(result))
972 return js_NewNumberValue(cx, result, rval);
974 result = YearFromTime(LocalTime(result));
975 return js_NewNumberValue(cx, result, rval);
976 }
978 static JSBool
979 date_getUTCFullYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
980 jsval *rval)
981 {
982 jsdouble result;
983 jsdouble *date = date_getProlog(cx, obj, argv);
984 if (!date)
985 return JS_FALSE;
986 result = *date;
988 if (!JSDOUBLE_IS_FINITE(result))
989 return js_NewNumberValue(cx, result, rval);
991 result = YearFromTime(result);
992 return js_NewNumberValue(cx, result, rval);
993 }
995 static JSBool
996 date_getMonth(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
997 jsval *rval)
998 {
999 jsdouble result;
1000 jsdouble *date = date_getProlog(cx, obj, argv);
1001 if (!date)
1002 return JS_FALSE;
1003 result = *date;
1005 if (!JSDOUBLE_IS_FINITE(result))
1006 return js_NewNumberValue(cx, result, rval);
1008 result = MonthFromTime(LocalTime(result));
1009 return js_NewNumberValue(cx, result, rval);
1010 }
1012 static JSBool
1013 date_getUTCMonth(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1014 jsval *rval)
1015 {
1016 jsdouble result;
1017 jsdouble *date = date_getProlog(cx, obj, argv);
1018 if (!date)
1019 return JS_FALSE;
1020 result = *date;
1022 if (!JSDOUBLE_IS_FINITE(result))
1023 return js_NewNumberValue(cx, result, rval);
1025 result = MonthFromTime(result);
1026 return js_NewNumberValue(cx, result, rval);
1027 }
1029 static JSBool
1030 date_getDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1031 {
1032 jsdouble result;
1033 jsdouble *date = date_getProlog(cx, obj, argv);
1034 if (!date)
1035 return JS_FALSE;
1036 result = *date;
1038 if (!JSDOUBLE_IS_FINITE(result))
1039 return js_NewNumberValue(cx, result, rval);
1041 result = LocalTime(result);
1042 result = DateFromTime(result);
1043 return js_NewNumberValue(cx, result, rval);
1044 }
1046 static JSBool
1047 date_getUTCDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1048 jsval *rval)
1049 {
1050 jsdouble result;
1051 jsdouble *date = date_getProlog(cx, obj, argv);
1052 if (!date)
1053 return JS_FALSE;
1054 result = *date;
1056 if (!JSDOUBLE_IS_FINITE(result))
1057 return js_NewNumberValue(cx, result, rval);
1059 result = DateFromTime(result);
1060 return js_NewNumberValue(cx, result, rval);
1061 }
1063 static JSBool
1064 date_getDay(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1065 {
1066 jsdouble result;
1067 jsdouble *date = date_getProlog(cx, obj, argv);
1068 if (!date)
1069 return JS_FALSE;
1070 result = *date;
1072 if (!JSDOUBLE_IS_FINITE(result))
1073 return js_NewNumberValue(cx, result, rval);
1075 result = LocalTime(result);
1076 result = WeekDay(result);
1077 return js_NewNumberValue(cx, result, rval);
1078 }
1080 static JSBool
1081 date_getUTCDay(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1082 jsval *rval)
1083 {
1084 jsdouble result;
1085 jsdouble *date = date_getProlog(cx, obj, argv);
1086 if (!date)
1087 return JS_FALSE;
1088 result = *date;
1090 if (!JSDOUBLE_IS_FINITE(result))
1091 return js_NewNumberValue(cx, result, rval);
1093 result = WeekDay(result);
1094 return js_NewNumberValue(cx, result, rval);
1095 }
1097 static JSBool
1098 date_getHours(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1099 jsval *rval)
1100 {
1101 jsdouble result;
1102 jsdouble *date = date_getProlog(cx, obj, argv);
1103 if (!date)
1104 return JS_FALSE;
1105 result = *date;
1107 if (!JSDOUBLE_IS_FINITE(result))
1108 return js_NewNumberValue(cx, result, rval);
1110 result = HourFromTime(LocalTime(result));
1111 return js_NewNumberValue(cx, result, rval);
1112 }
1114 static JSBool
1115 date_getUTCHours(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1116 jsval *rval)
1117 {
1118 jsdouble result;
1119 jsdouble *date = date_getProlog(cx, obj, argv);
1120 if (!date)
1121 return JS_FALSE;
1122 result = *date;
1124 if (!JSDOUBLE_IS_FINITE(result))
1125 return js_NewNumberValue(cx, result, rval);
1127 result = HourFromTime(result);
1128 return js_NewNumberValue(cx, result, rval);
1129 }
1131 static JSBool
1132 date_getMinutes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1133 jsval *rval)
1134 {
1135 jsdouble result;
1136 jsdouble *date = date_getProlog(cx, obj, argv);
1137 if (!date)
1138 return JS_FALSE;
1139 result = *date;
1141 if (!JSDOUBLE_IS_FINITE(result))
1142 return js_NewNumberValue(cx, result, rval);
1144 result = MinFromTime(LocalTime(result));
1145 return js_NewNumberValue(cx, result, rval);
1146 }
1148 static JSBool
1149 date_getUTCMinutes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1150 jsval *rval)
1151 {
1152 jsdouble result;
1153 jsdouble *date = date_getProlog(cx, obj, argv);
1154 if (!date)
1155 return JS_FALSE;
1156 result = *date;
1158 if (!JSDOUBLE_IS_FINITE(result))
1159 return js_NewNumberValue(cx, result, rval);
1161 result = MinFromTime(result);
1162 return js_NewNumberValue(cx, result, rval);
1163 }
1165 /* Date.getSeconds is mapped to getUTCSeconds */
1167 static JSBool
1168 date_getUTCSeconds(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1169 jsval *rval)
1170 {
1171 jsdouble result;
1172 jsdouble *date = date_getProlog(cx, obj, argv);
1173 if (!date)
1174 return JS_FALSE;
1175 result = *date;
1177 if (!JSDOUBLE_IS_FINITE(result))
1178 return js_NewNumberValue(cx, result, rval);
1180 result = SecFromTime(result);
1181 return js_NewNumberValue(cx, result, rval);
1182 }
1184 /* Date.getMilliseconds is mapped to getUTCMilliseconds */
1186 static JSBool
1187 date_getUTCMilliseconds(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1188 jsval *rval)
1189 {
1190 jsdouble result;
1191 jsdouble *date = date_getProlog(cx, obj, argv);
1192 if (!date)
1193 return JS_FALSE;
1194 result = *date;
1196 if (!JSDOUBLE_IS_FINITE(result))
1197 return js_NewNumberValue(cx, result, rval);
1199 result = msFromTime(result);
1200 return js_NewNumberValue(cx, result, rval);
1201 }
1203 static JSBool
1204 date_getTimezoneOffset(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1205 jsval *rval)
1206 {
1207 jsdouble result;
1208 jsdouble *date = date_getProlog(cx, obj, argv);
1209 if (!date)
1210 return JS_FALSE;
1211 result = *date;
1213 /*
1214 * Return the time zone offset in minutes for the current locale
1215 * that is appropriate for this time. This value would be a
1216 * constant except for daylight savings time.
1217 */
1218 result = (result - LocalTime(result)) / msPerMinute;
1219 return js_NewNumberValue(cx, result, rval);
1220 }
1222 static JSBool
1223 date_setTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1224 {
1225 jsdouble result;
1226 jsdouble *date = date_getProlog(cx, obj, argv);
1227 if (!date)
1228 return JS_FALSE;
1230 if (!js_ValueToNumber(cx, argv[0], &result))
1231 return JS_FALSE;
1233 result = TIMECLIP(result);
1235 *date = result;
1236 return js_NewNumberValue(cx, result, rval);
1237 }
1239 static JSBool
1240 date_makeTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1241 uintN maxargs, JSBool local, jsval *rval)
1242 {
1243 uintN i;
1244 jsdouble args[4], *argp, *stop;
1245 jsdouble hour, min, sec, msec;
1246 jsdouble lorutime; /* Local or UTC version of *date */
1248 jsdouble msec_time;
1249 jsdouble result;
1251 jsdouble *date = date_getProlog(cx, obj, argv);
1252 if (!date)
1253 return JS_FALSE;
1255 result = *date;
1257 /* just return NaN if the date is already NaN */
1258 if (!JSDOUBLE_IS_FINITE(result))
1259 return js_NewNumberValue(cx, result, rval);
1261 /* Satisfy the ECMA rule that if a function is called with
1262 * fewer arguments than the specified formal arguments, the
1263 * remaining arguments are set to undefined. Seems like all
1264 * the Date.setWhatever functions in ECMA are only varargs
1265 * beyond the first argument; this should be set to undefined
1266 * if it's not given. This means that "d = new Date();
1267 * d.setMilliseconds()" returns NaN. Blech.
1268 */
1269 if (argc == 0)
1270 argc = 1; /* should be safe, because length of all setters is 1 */
1271 else if (argc > maxargs)
1272 argc = maxargs; /* clamp argc */
1274 for (i = 0; i < argc; i++) {
1275 if (!js_ValueToNumber(cx, argv[i], &args[i]))
1276 return JS_FALSE;
1277 if (!JSDOUBLE_IS_FINITE(args[i])) {
1278 *date = *cx->runtime->jsNaN;
1279 return js_NewNumberValue(cx, *date, rval);
1280 }
1281 args[i] = js_DoubleToInteger(args[i]);
1282 }
1284 if (local)
1285 lorutime = LocalTime(result);
1286 else
1287 lorutime = result;
1289 argp = args;
1290 stop = argp + argc;
1291 if (maxargs >= 4 && argp < stop)
1292 hour = *argp++;
1293 else
1294 hour = HourFromTime(lorutime);
1296 if (maxargs >= 3 && argp < stop)
1297 min = *argp++;
1298 else
1299 min = MinFromTime(lorutime);
1301 if (maxargs >= 2 && argp < stop)
1302 sec = *argp++;
1303 else
1304 sec = SecFromTime(lorutime);
1306 if (maxargs >= 1 && argp < stop)
1307 msec = *argp;
1308 else
1309 msec = msFromTime(lorutime);
1311 msec_time = MakeTime(hour, min, sec, msec);
1312 result = MakeDate(Day(lorutime), msec_time);
1314 /* fprintf(stderr, "%f\n", result); */
1316 if (local)
1317 result = UTC(result);
1319 /* fprintf(stderr, "%f\n", result); */
1321 *date = TIMECLIP(result);
1322 return js_NewNumberValue(cx, *date, rval);
1323 }
1325 static JSBool
1326 date_setMilliseconds(JSContext *cx, JSObject *obj, uintN argc,
1327 jsval *argv, jsval *rval)
1328 {
1329 return date_makeTime(cx, obj, argc, argv, 1, JS_TRUE, rval);
1330 }
1332 static JSBool
1333 date_setUTCMilliseconds(JSContext *cx, JSObject *obj, uintN argc,
1334 jsval *argv, jsval *rval)
1335 {
1336 return date_makeTime(cx, obj, argc, argv, 1, JS_FALSE, rval);
1337 }
1339 static JSBool
1340 date_setSeconds(JSContext *cx, JSObject *obj, uintN argc,
1341 jsval *argv, jsval *rval)
1342 {
1343 return date_makeTime(cx, obj, argc, argv, 2, JS_TRUE, rval);
1344 }
1346 static JSBool
1347 date_setUTCSeconds(JSContext *cx, JSObject *obj, uintN argc,
1348 jsval *argv, jsval *rval)
1349 {
1350 return date_makeTime(cx, obj, argc, argv, 2, JS_FALSE, rval);
1351 }
1353 static JSBool
1354 date_setMinutes(JSContext *cx, JSObject *obj, uintN argc,
1355 jsval *argv, jsval *rval)
1356 {
1357 return date_makeTime(cx, obj, argc, argv, 3, JS_TRUE, rval);
1358 }
1360 static JSBool
1361 date_setUTCMinutes(JSContext *cx, JSObject *obj, uintN argc,
1362 jsval *argv, jsval *rval)
1363 {
1364 return date_makeTime(cx, obj, argc, argv, 3, JS_FALSE, rval);
1365 }
1367 static JSBool
1368 date_setHours(JSContext *cx, JSObject *obj, uintN argc,
1369 jsval *argv, jsval *rval)
1370 {
1371 return date_makeTime(cx, obj, argc, argv, 4, JS_TRUE, rval);
1372 }
1374 static JSBool
1375 date_setUTCHours(JSContext *cx, JSObject *obj, uintN argc,
1376 jsval *argv, jsval *rval)
1377 {
1378 return date_makeTime(cx, obj, argc, argv, 4, JS_FALSE, rval);
1379 }
1381 static JSBool
1382 date_makeDate(JSContext *cx, JSObject *obj, uintN argc,
1383 jsval *argv, uintN maxargs, JSBool local, jsval *rval)
1384 {
1385 uintN i;
1386 jsdouble lorutime; /* local or UTC version of *date */
1387 jsdouble args[3], *argp, *stop;
1388 jsdouble year, month, day;
1389 jsdouble result;
1391 jsdouble *date = date_getProlog(cx, obj, argv);
1392 if (!date)
1393 return JS_FALSE;
1395 result = *date;
1397 /* see complaint about ECMA in date_MakeTime */
1398 if (argc == 0)
1399 argc = 1; /* should be safe, because length of all setters is 1 */
1400 else if (argc > maxargs)
1401 argc = maxargs; /* clamp argc */
1403 for (i = 0; i < argc; i++) {
1404 if (!js_ValueToNumber(cx, argv[i], &args[i]))
1405 return JS_FALSE;
1406 if (!JSDOUBLE_IS_FINITE(args[i])) {
1407 *date = *cx->runtime->jsNaN;
1408 return js_NewNumberValue(cx, *date, rval);
1409 }
1410 args[i] = js_DoubleToInteger(args[i]);
1411 }
1413 /* return NaN if date is NaN and we're not setting the year,
1414 * If we are, use 0 as the time. */
1415 if (!(JSDOUBLE_IS_FINITE(result))) {
1416 if (argc < 3)
1417 return js_NewNumberValue(cx, result, rval);
1418 else
1419 lorutime = +0.;
1420 } else {
1421 if (local)
1422 lorutime = LocalTime(result);
1423 else
1424 lorutime = result;
1425 }
1427 argp = args;
1428 stop = argp + argc;
1429 if (maxargs >= 3 && argp < stop)
1430 year = *argp++;
1431 else
1432 year = YearFromTime(lorutime);
1434 if (maxargs >= 2 && argp < stop)
1435 month = *argp++;
1436 else
1437 month = MonthFromTime(lorutime);
1439 if (maxargs >= 1 && argp < stop)
1440 day = *argp++;
1441 else
1442 day = DateFromTime(lorutime);
1444 day = MakeDay(year, month, day); /* day within year */
1445 result = MakeDate(day, TimeWithinDay(lorutime));
1447 if (local)
1448 result = UTC(result);
1450 *date = TIMECLIP(result);
1451 return js_NewNumberValue(cx, *date, rval);
1452 }
1454 static JSBool
1455 date_setDate(JSContext *cx, JSObject *obj, uintN argc,
1456 jsval *argv, jsval *rval)
1457 {
1458 return date_makeDate(cx, obj, argc, argv, 1, JS_TRUE, rval);
1459 }
1461 static JSBool
1462 date_setUTCDate(JSContext *cx, JSObject *obj, uintN argc,
1463 jsval *argv, jsval *rval)
1464 {
1465 return date_makeDate(cx, obj, argc, argv, 1, JS_FALSE, rval);
1466 }
1468 static JSBool
1469 date_setMonth(JSContext *cx, JSObject *obj, uintN argc,
1470 jsval *argv, jsval *rval)
1471 {
1472 return date_makeDate(cx, obj, argc, argv, 2, JS_TRUE, rval);
1473 }
1475 static JSBool
1476 date_setUTCMonth(JSContext *cx, JSObject *obj, uintN argc,
1477 jsval *argv, jsval *rval)
1478 {
1479 return date_makeDate(cx, obj, argc, argv, 2, JS_FALSE, rval);
1480 }
1482 static JSBool
1483 date_setFullYear(JSContext *cx, JSObject *obj, uintN argc,
1484 jsval *argv, jsval *rval)
1485 {
1486 return date_makeDate(cx, obj, argc, argv, 3, JS_TRUE, rval);
1487 }
1489 static JSBool
1490 date_setUTCFullYear(JSContext *cx, JSObject *obj, uintN argc,
1491 jsval *argv, jsval *rval)
1492 {
1493 return date_makeDate(cx, obj, argc, argv, 3, JS_FALSE, rval);
1494 }
1496 static JSBool
1497 date_setYear(JSContext *cx, JSObject *obj, uintN argc,
1498 jsval *argv, jsval *rval)
1499 {
1500 jsdouble t;
1501 jsdouble year;
1502 jsdouble day;
1503 jsdouble result;
1505 jsdouble *date = date_getProlog(cx, obj, argv);
1506 if (!date)
1507 return JS_FALSE;
1509 result = *date;
1511 if (!js_ValueToNumber(cx, argv[0], &year))
1512 return JS_FALSE;
1513 if (!JSDOUBLE_IS_FINITE(year)) {
1514 *date = *cx->runtime->jsNaN;
1515 return js_NewNumberValue(cx, *date, rval);
1516 }
1518 year = js_DoubleToInteger(year);
1520 if (!JSDOUBLE_IS_FINITE(result)) {
1521 t = +0.0;
1522 } else {
1523 t = LocalTime(result);
1524 }
1526 if (year >= 0 && year <= 99)
1527 year += 1900;
1529 day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
1530 result = MakeDate(day, TimeWithinDay(t));
1531 result = UTC(result);
1533 *date = TIMECLIP(result);
1534 return js_NewNumberValue(cx, *date, rval);
1535 }
1537 /* constants for toString, toUTCString */
1538 static char js_NaN_date_str[] = "Invalid Date";
1539 static const char* days[] =
1540 {
1541 "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
1542 };
1543 static const char* months[] =
1544 {
1545 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1546 };
1548 static JSBool
1549 date_toGMTString(JSContext *cx, JSObject *obj, uintN argc,
1550 jsval *argv, jsval *rval)
1551 {
1552 char buf[100];
1553 JSString *str;
1554 jsdouble *date = date_getProlog(cx, obj, argv);
1555 if (!date)
1556 return JS_FALSE;
1558 if (!JSDOUBLE_IS_FINITE(*date)) {
1559 JS_snprintf(buf, sizeof buf, js_NaN_date_str);
1560 } else {
1561 jsdouble temp = *date;
1563 /* Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
1564 * requires a PRMJTime... which only has 16-bit years. Sub-ECMA.
1565 */
1566 JS_snprintf(buf, sizeof buf, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
1567 days[WeekDay(temp)],
1568 DateFromTime(temp),
1569 months[MonthFromTime(temp)],
1570 YearFromTime(temp),
1571 HourFromTime(temp),
1572 MinFromTime(temp),
1573 SecFromTime(temp));
1574 }
1575 str = JS_NewStringCopyZ(cx, buf);
1576 if (!str)
1577 return JS_FALSE;
1578 *rval = STRING_TO_JSVAL(str);
1579 return JS_TRUE;
1580 }
1582 /* for Date.toLocaleString; interface to PRMJTime date struct.
1583 * If findEquivalent is true, then try to map the year to an equivalent year
1584 * that's in range.
1585 */
1586 static void
1587 new_explode(jsdouble timeval, PRMJTime *split, JSBool findEquivalent)
1588 {
1589 jsint year = YearFromTime(timeval);
1590 int16 adjustedYear;
1592 /* If the year doesn't fit in a PRMJTime, find something to do about it. */
1593 if (year > 32767 || year < -32768) {
1594 if (findEquivalent) {
1595 /* We're really just trying to get a timezone string; map the year
1596 * to some equivalent year in the range 0 to 2800. Borrowed from
1597 * A. D. Olsen.
1598 */
1599 jsint cycles;
1600 #define CYCLE_YEARS 2800L
1601 cycles = (year >= 0) ? year / CYCLE_YEARS
1602 : -1 - (-1 - year) / CYCLE_YEARS;
1603 adjustedYear = (int16)(year - cycles * CYCLE_YEARS);
1604 } else {
1605 /* Clamp it to the nearest representable year. */
1606 adjustedYear = (int16)((year > 0) ? 32767 : - 32768);
1607 }
1608 } else {
1609 adjustedYear = (int16)year;
1610 }
1612 split->tm_usec = (int32) msFromTime(timeval) * 1000;
1613 split->tm_sec = (int8) SecFromTime(timeval);
1614 split->tm_min = (int8) MinFromTime(timeval);
1615 split->tm_hour = (int8) HourFromTime(timeval);
1616 split->tm_mday = (int8) DateFromTime(timeval);
1617 split->tm_mon = (int8) MonthFromTime(timeval);
1618 split->tm_wday = (int8) WeekDay(timeval);
1619 split->tm_year = (int16) adjustedYear;
1620 split->tm_yday = (int16) DayWithinYear(timeval, year);
1622 /* not sure how this affects things, but it doesn't seem
1623 to matter. */
1624 split->tm_isdst = (DaylightSavingTA(timeval) != 0);
1625 }
1627 typedef enum formatspec {
1628 FORMATSPEC_FULL, FORMATSPEC_DATE, FORMATSPEC_TIME
1629 } formatspec;
1631 /* helper function */
1632 static JSBool
1633 date_format(JSContext *cx, jsdouble date, formatspec format, jsval *rval)
1634 {
1635 char buf[100];
1636 JSString *str;
1637 char tzbuf[100];
1638 JSBool usetz;
1639 size_t i, tzlen;
1640 PRMJTime split;
1642 if (!JSDOUBLE_IS_FINITE(date)) {
1643 JS_snprintf(buf, sizeof buf, js_NaN_date_str);
1644 } else {
1645 jsdouble local = LocalTime(date);
1647 /* offset from GMT in minutes. The offset includes daylight savings,
1648 if it applies. */
1649 jsint minutes = (jsint) floor(AdjustTime(date) / msPerMinute);
1651 /* map 510 minutes to 0830 hours */
1652 intN offset = (minutes / 60) * 100 + minutes % 60;
1654 /* print as "Wed Nov 05 19:38:03 GMT-0800 (PST) 1997" The TZA is
1655 * printed as 'GMT-0800' rather than as 'PST' to avoid
1656 * operating-system dependence on strftime (which
1657 * PRMJ_FormatTimeUSEnglish calls, for %Z only.) win32 prints
1658 * PST as 'Pacific Standard Time.' This way we always know
1659 * what we're getting, and can parse it if we produce it.
1660 * The OS TZA string is included as a comment.
1661 */
1663 /* get a timezone string from the OS to include as a
1664 comment. */
1665 new_explode(date, &split, JS_TRUE);
1666 if (PRMJ_FormatTime(tzbuf, sizeof tzbuf, "(%Z)", &split) != 0) {
1668 /* Decide whether to use the resulting timezone string.
1669 *
1670 * Reject it if it contains any non-ASCII, non-alphanumeric
1671 * characters. It's then likely in some other character
1672 * encoding, and we probably won't display it correctly.
1673 */
1674 usetz = JS_TRUE;
1675 tzlen = strlen(tzbuf);
1676 if (tzlen > 100) {
1677 usetz = JS_FALSE;
1678 } else {
1679 for (i = 0; i < tzlen; i++) {
1680 jschar c = tzbuf[i];
1681 if (c > 127 ||
1682 !(isalpha(c) || isdigit(c) ||
1683 c == ' ' || c == '(' || c == ')')) {
1684 usetz = JS_FALSE;
1685 }
1686 }
1687 }
1689 /* Also reject it if it's not parenthesized or if it's '()'. */
1690 if (tzbuf[0] != '(' || tzbuf[1] == ')')
1691 usetz = JS_FALSE;
1692 } else
1693 usetz = JS_FALSE;
1695 switch (format) {
1696 case FORMATSPEC_FULL:
1697 /*
1698 * Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
1699 * requires a PRMJTime... which only has 16-bit years. Sub-ECMA.
1700 */
1701 /* Tue Oct 31 2000 09:41:40 GMT-0800 (PST) */
1702 JS_snprintf(buf, sizeof buf,
1703 "%s %s %.2d %.4d %.2d:%.2d:%.2d GMT%+.4d%s%s",
1704 days[WeekDay(local)],
1705 months[MonthFromTime(local)],
1706 DateFromTime(local),
1707 YearFromTime(local),
1708 HourFromTime(local),
1709 MinFromTime(local),
1710 SecFromTime(local),
1711 offset,
1712 usetz ? " " : "",
1713 usetz ? tzbuf : "");
1714 break;
1715 case FORMATSPEC_DATE:
1716 /* Tue Oct 31 2000 */
1717 JS_snprintf(buf, sizeof buf,
1718 "%s %s %.2d %.4d",
1719 days[WeekDay(local)],
1720 months[MonthFromTime(local)],
1721 DateFromTime(local),
1722 YearFromTime(local));
1723 break;
1724 case FORMATSPEC_TIME:
1725 /* 09:41:40 GMT-0800 (PST) */
1726 JS_snprintf(buf, sizeof buf,
1727 "%.2d:%.2d:%.2d GMT%+.4d%s%s",
1728 HourFromTime(local),
1729 MinFromTime(local),
1730 SecFromTime(local),
1731 offset,
1732 usetz ? " " : "",
1733 usetz ? tzbuf : "");
1734 break;
1735 }
1736 }
1738 str = JS_NewStringCopyZ(cx, buf);
1739 if (!str)
1740 return JS_FALSE;
1741 *rval = STRING_TO_JSVAL(str);
1742 return JS_TRUE;
1743 }
1745 static JSBool
1746 date_toLocaleHelper(JSContext *cx, JSObject *obj, uintN argc,
1747 jsval *argv, jsval *rval, char *format)
1748 {
1749 char buf[100];
1750 JSString *str;
1751 PRMJTime split;
1752 jsdouble *date = date_getProlog(cx, obj, argv);
1753 if (!date)
1754 return JS_FALSE;
1756 if (!JSDOUBLE_IS_FINITE(*date)) {
1757 JS_snprintf(buf, sizeof buf, js_NaN_date_str);
1758 } else {
1759 intN result_len;
1760 jsdouble local = LocalTime(*date);
1761 new_explode(local, &split, JS_FALSE);
1763 /* let PRMJTime format it. */
1764 result_len = PRMJ_FormatTime(buf, sizeof buf, format, &split);
1766 /* If it failed, default to toString. */
1767 if (result_len == 0)
1768 return date_format(cx, *date, FORMATSPEC_FULL, rval);
1770 /* Hacked check against undesired 2-digit year 00/00/00 form. */
1771 if (buf[result_len - 3] == '/' &&
1772 isdigit(buf[result_len - 2]) && isdigit(buf[result_len - 1])) {
1773 JS_snprintf(buf + (result_len - 2), (sizeof buf) - (result_len - 2),
1774 "%d", js_DateGetYear(cx, obj));
1775 }
1777 }
1779 if (cx->localeCallbacks && cx->localeCallbacks->localeToUnicode)
1780 return cx->localeCallbacks->localeToUnicode(cx, buf, rval);
1782 str = JS_NewStringCopyZ(cx, buf);
1783 if (!str)
1784 return JS_FALSE;
1785 *rval = STRING_TO_JSVAL(str);
1786 return JS_TRUE;
1787 }
1789 static JSBool
1790 date_toLocaleString(JSContext *cx, JSObject *obj, uintN argc,
1791 jsval *argv, jsval *rval)
1792 {
1793 /* Use '%#c' for windows, because '%c' is
1794 * backward-compatible and non-y2k with msvc; '%#c' requests that a
1795 * full year be used in the result string.
1796 */
1797 return date_toLocaleHelper(cx, obj, argc, argv, rval,
1798 #if defined(_WIN32) && !defined(__MWERKS__)
1799 "%#c"
1800 #else
1801 "%c"
1802 #endif
1803 );
1804 }
1806 static JSBool
1807 date_toLocaleDateString(JSContext *cx, JSObject *obj, uintN argc,
1808 jsval *argv, jsval *rval)
1809 {
1810 /* Use '%#x' for windows, because '%x' is
1811 * backward-compatible and non-y2k with msvc; '%#x' requests that a
1812 * full year be used in the result string.
1813 */
1814 return date_toLocaleHelper(cx, obj, argc, argv, rval,
1815 #if defined(_WIN32) && !defined(__MWERKS__)
1816 "%#x"
1817 #else
1818 "%x"
1819 #endif
1820 );
1821 }
1823 static JSBool
1824 date_toLocaleTimeString(JSContext *cx, JSObject *obj, uintN argc,
1825 jsval *argv, jsval *rval)
1826 {
1827 return date_toLocaleHelper(cx, obj, argc, argv, rval, "%X");
1828 }
1830 static JSBool
1831 date_toLocaleFormat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1832 jsval *rval)
1833 {
1834 JSString *fmt;
1836 if (argc == 0)
1837 return date_toLocaleString(cx, obj, argc, argv, rval);
1839 fmt = JS_ValueToString(cx, argv[0]);
1840 if (!fmt)
1841 return JS_FALSE;
1843 return date_toLocaleHelper(cx, obj, argc, argv, rval,
1844 JS_GetStringBytes(fmt));
1845 }
1847 static JSBool
1848 date_toTimeString(JSContext *cx, JSObject *obj, uintN argc,
1849 jsval *argv, jsval *rval)
1850 {
1851 jsdouble *date = date_getProlog(cx, obj, argv);
1852 if (!date)
1853 return JS_FALSE;
1854 return date_format(cx, *date, FORMATSPEC_TIME, rval);
1855 }
1857 static JSBool
1858 date_toDateString(JSContext *cx, JSObject *obj, uintN argc,
1859 jsval *argv, jsval *rval)
1860 {
1861 jsdouble *date = date_getProlog(cx, obj, argv);
1862 if (!date)
1863 return JS_FALSE;
1864 return date_format(cx, *date, FORMATSPEC_DATE, rval);
1865 }
1867 #if JS_HAS_TOSOURCE
1868 #include <string.h>
1869 #include "jsdtoa.h"
1871 static JSBool
1872 date_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1873 jsval *rval)
1874 {
1875 jsdouble *date;
1876 char buf[DTOSTR_STANDARD_BUFFER_SIZE], *numStr, *bytes;
1877 JSString *str;
1879 date = date_getProlog(cx, obj, argv);
1880 if (!date)
1881 return JS_FALSE;
1883 numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, *date);
1884 if (!numStr) {
1885 JS_ReportOutOfMemory(cx);
1886 return JS_FALSE;
1887 }
1889 bytes = JS_smprintf("(new %s(%s))", date_class.name, numStr);
1890 if (!bytes) {
1891 JS_ReportOutOfMemory(cx);
1892 return JS_FALSE;
1893 }
1895 str = JS_NewString(cx, bytes, strlen(bytes));
1896 if (!str) {
1897 free(bytes);
1898 return JS_FALSE;
1899 }
1900 *rval = STRING_TO_JSVAL(str);
1901 return JS_TRUE;
1902 }
1903 #endif
1905 static JSBool
1906 date_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1907 jsval *rval)
1908 {
1909 jsdouble *date = date_getProlog(cx, obj, argv);
1910 if (!date)
1911 return JS_FALSE;
1912 return date_format(cx, *date, FORMATSPEC_FULL, rval);
1913 }
1915 #if JS_HAS_VALUEOF_HINT
1916 static JSBool
1917 date_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1918 jsval *rval)
1919 {
1920 /* It is an error to call date_valueOf on a non-date object, but we don't
1921 * need to check for that explicitly here because every path calls
1922 * date_getProlog, which does the check.
1923 */
1925 /* If called directly with no arguments, convert to a time number. */
1926 if (argc == 0)
1927 return date_getTime(cx, obj, argc, argv, rval);
1929 /* Convert to number only if the hint was given, otherwise favor string. */
1930 if (argc == 1) {
1931 JSString *str, *str2;
1933 str = js_ValueToString(cx, argv[0]);
1934 if (!str)
1935 return JS_FALSE;
1936 str2 = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]);
1937 if (!js_CompareStrings(str, str2))
1938 return date_getTime(cx, obj, argc, argv, rval);
1939 }
1940 return date_toString(cx, obj, argc, argv, rval);
1941 }
1942 #else
1943 #define date_valueOf date_getTime
1944 #endif
1946 \f
1947 /*
1948 * creation and destruction
1949 */
1951 static JSFunctionSpec date_static_methods[] = {
1952 {"UTC", date_UTC, MAXARGS,0,0 },
1953 {"parse", date_parse, 1,0,0 },
1954 {"now", date_now, 0,0,0 },
1955 {0,0,0,0,0}
1956 };
1958 static JSFunctionSpec date_methods[] = {
1959 {"getTime", date_getTime, 0,0,0 },
1960 {"getTimezoneOffset", date_getTimezoneOffset, 0,0,0 },
1961 {"getYear", date_getYear, 0,0,0 },
1962 {"getFullYear", date_getFullYear, 0,0,0 },
1963 {"getUTCFullYear", date_getUTCFullYear, 0,0,0 },
1964 {"getMonth", date_getMonth, 0,0,0 },
1965 {"getUTCMonth", date_getUTCMonth, 0,0,0 },
1966 {"getDate", date_getDate, 0,0,0 },
1967 {"getUTCDate", date_getUTCDate, 0,0,0 },
1968 {"getDay", date_getDay, 0,0,0 },
1969 {"getUTCDay", date_getUTCDay, 0,0,0 },
1970 {"getHours", date_getHours, 0,0,0 },
1971 {"getUTCHours", date_getUTCHours, 0,0,0 },
1972 {"getMinutes", date_getMinutes, 0,0,0 },
1973 {"getUTCMinutes", date_getUTCMinutes, 0,0,0 },
1974 {"getSeconds", date_getUTCSeconds, 0,0,0 },
1975 {"getUTCSeconds", date_getUTCSeconds, 0,0,0 },
1976 {"getMilliseconds", date_getUTCMilliseconds,0,0,0 },
1977 {"getUTCMilliseconds", date_getUTCMilliseconds,0,0,0 },
1978 {"setTime", date_setTime, 1,0,0 },
1979 {"setYear", date_setYear, 1,0,0 },
1980 {"setFullYear", date_setFullYear, 3,0,0 },
1981 {"setUTCFullYear", date_setUTCFullYear, 3,0,0 },
1982 {"setMonth", date_setMonth, 2,0,0 },
1983 {"setUTCMonth", date_setUTCMonth, 2,0,0 },
1984 {"setDate", date_setDate, 1,0,0 },
1985 {"setUTCDate", date_setUTCDate, 1,0,0 },
1986 {"setHours", date_setHours, 4,0,0 },
1987 {"setUTCHours", date_setUTCHours, 4,0,0 },
1988 {"setMinutes", date_setMinutes, 3,0,0 },
1989 {"setUTCMinutes", date_setUTCMinutes, 3,0,0 },
1990 {"setSeconds", date_setSeconds, 2,0,0 },
1991 {"setUTCSeconds", date_setUTCSeconds, 2,0,0 },
1992 {"setMilliseconds", date_setMilliseconds, 1,0,0 },
1993 {"setUTCMilliseconds", date_setUTCMilliseconds,1,0,0 },
1994 {"toUTCString", date_toGMTString, 0,0,0 },
1995 {js_toLocaleString_str, date_toLocaleString, 0,0,0 },
1996 {"toLocaleDateString", date_toLocaleDateString,0,0,0 },
1997 {"toLocaleTimeString", date_toLocaleTimeString,0,0,0 },
1998 {"toLocaleFormat", date_toLocaleFormat, 1,0,0 },
1999 {"toDateString", date_toDateString, 0,0,0 },
2000 {"toTimeString", date_toTimeString, 0,0,0 },
2001 #if JS_HAS_TOSOURCE
2002 {js_toSource_str, date_toSource, 0,0,0 },
2003 #endif
2004 {js_toString_str, date_toString, 0,0,0 },
2005 {js_valueOf_str, date_valueOf, 0,0,0 },
2006 {0,0,0,0,0}
2007 };
2009 static jsdouble *
2010 date_constructor(JSContext *cx, JSObject* obj)
2011 {
2012 jsdouble *date;
2014 date = js_NewDouble(cx, 0.0, 0);
2015 if (!date)
2016 return NULL;
2017 OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, DOUBLE_TO_JSVAL(date));
2018 return date;
2019 }
2021 static JSBool
2022 Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2023 {
2024 jsdouble *date;
2025 JSString *str;
2026 jsdouble d;
2028 /* Date called as function. */
2029 if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
2030 int64 us, ms, us2ms;
2031 jsdouble msec_time;
2033 /* NSPR 2.0 docs say 'We do not support PRMJ_NowMS and PRMJ_NowS',
2034 * so compute ms from PRMJ_Now.
2035 */
2036 us = PRMJ_Now();
2037 JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC);
2038 JSLL_DIV(ms, us, us2ms);
2039 JSLL_L2D(msec_time, ms);
2041 return date_format(cx, msec_time, FORMATSPEC_FULL, rval);
2042 }
2044 /* Date called as constructor. */
2045 if (argc == 0) {
2046 int64 us, ms, us2ms;
2047 jsdouble msec_time;
2049 date = date_constructor(cx, obj);
2050 if (!date)
2051 return JS_FALSE;
2053 us = PRMJ_Now();
2054 JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC);
2055 JSLL_DIV(ms, us, us2ms);
2056 JSLL_L2D(msec_time, ms);
2058 *date = msec_time;
2059 } else if (argc == 1) {
2060 if (!JSVAL_IS_STRING(argv[0])) {
2061 /* the argument is a millisecond number */
2062 if (!js_ValueToNumber(cx, argv[0], &d))
2063 return JS_FALSE;
2064 date = date_constructor(cx, obj);
2065 if (!date)
2066 return JS_FALSE;
2067 *date = TIMECLIP(d);
2068 } else {
2069 /* the argument is a string; parse it. */
2070 date = date_constructor(cx, obj);
2071 if (!date)
2072 return JS_FALSE;
2074 str = js_ValueToString(cx, argv[0]);
2075 if (!str)
2076 return JS_FALSE;
2078 if (!date_parseString(str, date))
2079 *date = *cx->runtime->jsNaN;
2080 *date = TIMECLIP(*date);
2081 }
2082 } else {
2083 jsdouble array[MAXARGS];
2084 uintN loop;
2085 jsdouble double_arg;
2086 jsdouble day;
2087 jsdouble msec_time;
2089 for (loop = 0; loop < MAXARGS; loop++) {
2090 if (loop < argc) {
2091 if (!js_ValueToNumber(cx, argv[loop], &double_arg))
2092 return JS_FALSE;
2093 /* if any arg is NaN, make a NaN date object
2094 and return */
2095 if (!JSDOUBLE_IS_FINITE(double_arg)) {
2096 date = date_constructor(cx, obj);
2097 if (!date)
2098 return JS_FALSE;
2099 *date = *cx->runtime->jsNaN;
2100 return JS_TRUE;
2101 }
2102 array[loop] = js_DoubleToInteger(double_arg);
2103 } else {
2104 if (loop == 2) {
2105 array[loop] = 1; /* Default the date argument to 1. */
2106 } else {
2107 array[loop] = 0;
2108 }
2109 }
2110 }
2112 date = date_constructor(cx, obj);
2113 if (!date)
2114 return JS_FALSE;
2116 /* adjust 2-digit years into the 20th century */
2117 if (array[0] >= 0 && array[0] <= 99)
2118 array[0] += 1900;
2120 day = MakeDay(array[0], array[1], array[2]);
2121 msec_time = MakeTime(array[3], array[4], array[5], array[6]);
2122 msec_time = MakeDate(day, msec_time);
2123 msec_time = UTC(msec_time);
2124 *date = TIMECLIP(msec_time);
2125 }
2126 return JS_TRUE;
2127 }
2129 JSObject *
2130 js_InitDateClass(JSContext *cx, JSObject *obj)
2131 {
2132 JSObject *proto;
2133 jsdouble *proto_date;
2135 /* set static LocalTZA */
2136 LocalTZA = -(PRMJ_LocalGMTDifference() * msPerSecond);
2137 proto = JS_InitClass(cx, obj, NULL, &date_class, 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;
2153 }
2155 JS_FRIEND_API(JSObject *)
2156 js_NewDateObjectMsec(JSContext *cx, jsdouble msec_time)
2157 {
2158 JSObject *obj;
2159 jsdouble *date;
2161 obj = js_NewObject(cx, &date_class, NULL, NULL);
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;
2171 }
2173 JS_FRIEND_API(JSObject *)
2174 js_NewDateObject(JSContext* cx, int year, int mon, int mday,
2175 int hour, int min, int sec)
2176 {
2177 JSObject *obj;
2178 jsdouble msec_time;
2180 msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
2181 obj = js_NewDateObjectMsec(cx, UTC(msec_time));
2182 return obj;
2183 }
2185 JS_FRIEND_API(JSBool)
2186 js_DateIsValid(JSContext *cx, JSObject* obj)
2187 {
2188 jsdouble *date = date_getProlog(cx, obj, NULL);
2190 if (!date || JSDOUBLE_IS_NaN(*date))
2191 return JS_FALSE;
2192 else
2193 return JS_TRUE;
2194 }
2196 JS_FRIEND_API(int)
2197 js_DateGetYear(JSContext *cx, JSObject* obj)
2198 {
2199 jsdouble *date = date_getProlog(cx, obj, NULL);
2201 /* Preserve legacy API behavior of returning 0 for invalid dates. */
2202 if (!date || JSDOUBLE_IS_NaN(*date))
2203 return 0;
2204 return (int) YearFromTime(LocalTime(*date));
2205 }
2207 JS_FRIEND_API(int)
2208 js_DateGetMonth(JSContext *cx, JSObject* obj)
2209 {
2210 jsdouble *date = date_getProlog(cx, obj, NULL);
2212 if (!date || JSDOUBLE_IS_NaN(*date))
2213 return 0;
2214 return (int) MonthFromTime(LocalTime(*date));
2215 }
2217 JS_FRIEND_API(int)
2218 js_DateGetDate(JSContext *cx, JSObject* obj)
2219 {
2220 jsdouble *date = date_getProlog(cx, obj, NULL);
2222 if (!date || JSDOUBLE_IS_NaN(*date))
2223 return 0;
2224 return (int) DateFromTime(LocalTime(*date));
2225 }
2227 JS_FRIEND_API(int)
2228 js_DateGetHours(JSContext *cx, JSObject* obj)
2229 {
2230 jsdouble *date = date_getProlog(cx, obj, NULL);
2232 if (!date || JSDOUBLE_IS_NaN(*date))
2233 return 0;
2234 return (int) HourFromTime(LocalTime(*date));
2235 }
2237 JS_FRIEND_API(int)
2238 js_DateGetMinutes(JSContext *cx, JSObject* obj)
2239 {
2240 jsdouble *date = date_getProlog(cx, obj, NULL);
2242 if (!date || JSDOUBLE_IS_NaN(*date))
2243 return 0;
2244 return (int) MinFromTime(LocalTime(*date));
2245 }
2247 JS_FRIEND_API(int)
2248 js_DateGetSeconds(JSContext *cx, JSObject* obj)
2249 {
2250 jsdouble *date = date_getProlog(cx, obj, NULL);
2252 if (!date || JSDOUBLE_IS_NaN(*date))
2253 return 0;
2254 return (int) SecFromTime(*date);
2255 }
2257 JS_FRIEND_API(void)
2258 js_DateSetYear(JSContext *cx, JSObject *obj, int year)
2259 {
2260 jsdouble local;
2261 jsdouble *date = date_getProlog(cx, obj, NULL);
2262 if (!date)
2263 return;
2264 local = LocalTime(*date);
2265 /* reset date if it was NaN */
2266 if (JSDOUBLE_IS_NaN(local))
2267 local = 0;
2268 local = date_msecFromDate(year,
2269 MonthFromTime(local),
2270 DateFromTime(local),
2271 HourFromTime(local),
2272 MinFromTime(local),
2273 SecFromTime(local),
2274 msFromTime(local));
2275 *date = UTC(local);
2276 }
2278 JS_FRIEND_API(void)
2279 js_DateSetMonth(JSContext *cx, JSObject *obj, int month)
2280 {
2281 jsdouble local;
2282 jsdouble *date = date_getProlog(cx, obj, NULL);
2283 if (!date)
2284 return;
2285 local = LocalTime(*date);
2286 /* bail if date was NaN */
2287 if (JSDOUBLE_IS_NaN(local))
2288 return;
2289 local = date_msecFromDate(YearFromTime(local),
2290 month,
2291 DateFromTime(local),
2292 HourFromTime(local),
2293 MinFromTime(local),
2294 SecFromTime(local),
2295 msFromTime(local));
2296 *date = UTC(local);
2297 }
2299 JS_FRIEND_API(void)
2300 js_DateSetDate(JSContext *cx, JSObject *obj, int date)
2301 {
2302 jsdouble local;
2303 jsdouble *datep = date_getProlog(cx, obj, NULL);
2304 if (!datep)
2305 return;
2306 local = LocalTime(*datep);
2307 if (JSDOUBLE_IS_NaN(local))
2308 return;
2309 local = date_msecFromDate(YearFromTime(local),
2310 MonthFromTime(local),
2311 date,
2312 HourFromTime(local),
2313 MinFromTime(local),
2314 SecFromTime(local),
2315 msFromTime(local));
2316 *datep = UTC(local);
2317 }
2319 JS_FRIEND_API(void)
2320 js_DateSetHours(JSContext *cx, JSObject *obj, int hours)
2321 {
2322 jsdouble local;
2323 jsdouble *date = date_getProlog(cx, obj, NULL);
2324 if (!date)
2325 return;
2326 local = LocalTime(*date);
2327 if (JSDOUBLE_IS_NaN(local))
2328 return;
2329 local = date_msecFromDate(YearFromTime(local),
2330 MonthFromTime(local),
2331 DateFromTime(local),
2332 hours,
2333 MinFromTime(local),
2334 SecFromTime(local),
2335 msFromTime(local));
2336 *date = UTC(local);
2337 }
2339 JS_FRIEND_API(void)
2340 js_DateSetMinutes(JSContext *cx, JSObject *obj, int minutes)
2341 {
2342 jsdouble local;
2343 jsdouble *date = date_getProlog(cx, obj, NULL);
2344 if (!date)
2345 return;
2346 local = LocalTime(*date);
2347 if (JSDOUBLE_IS_NaN(local))
2348 return;
2349 local = date_msecFromDate(YearFromTime(local),
2350 MonthFromTime(local),
2351 DateFromTime(local),
2352 HourFromTime(local),
2353 minutes,
2354 SecFromTime(local),
2355 msFromTime(local));
2356 *date = UTC(local);
2357 }
2359 JS_FRIEND_API(void)
2360 js_DateSetSeconds(JSContext *cx, JSObject *obj, int seconds)
2361 {
2362 jsdouble local;
2363 jsdouble *date = date_getProlog(cx, obj, NULL);
2364 if (!date)
2365 return;
2366 local = LocalTime(*date);
2367 if (JSDOUBLE_IS_NaN(local))
2368 return;
2369 local = date_msecFromDate(YearFromTime(local),
2370 MonthFromTime(local),
2371 DateFromTime(local),
2372 HourFromTime(local),
2373 MinFromTime(local),
2374 seconds,
2375 msFromTime(local));
2376 *date = UTC(local);
2377 }
2379 JS_FRIEND_API(jsdouble)
2380 js_DateGetMsecSinceEpoch(JSContext *cx, JSObject *obj)
2381 {
2382 jsdouble *date = date_getProlog(cx, obj, NULL);
2383 if (!date || JSDOUBLE_IS_NaN(*date))
2384 return 0;
2385 return (*date);
2386 }