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 <math.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include "jstypes.h"
59 #include "jsprf.h"
60 #include "prmjtime.h"
61 #include "jsutil.h" /* Added by JSIFY */
62 #include "jsapi.h"
63 #include "jsconfig.h"
64 #include "jscntxt.h"
65 #include "jsdate.h"
66 #include "jsinterp.h"
67 #include "jsnum.h"
68 #include "jsobj.h"
69 #include "jsstr.h"
71 /*
72 * The JS 'Date' object is patterned after the Java 'Date' object.
73 * Here is an script:
74 *
75 * today = new Date();
76 *
77 * print(today.toLocaleString());
78 *
79 * weekDay = today.getDay();
80 *
81 *
82 * These Java (and ECMA-262) methods are supported:
83 *
84 * UTC
85 * getDate (getUTCDate)
86 * getDay (getUTCDay)
87 * getHours (getUTCHours)
88 * getMinutes (getUTCMinutes)
89 * getMonth (getUTCMonth)
90 * getSeconds (getUTCSeconds)
91 * getMilliseconds (getUTCMilliseconds)
92 * getTime
93 * getTimezoneOffset
94 * getYear
95 * getFullYear (getUTCFullYear)
96 * parse
97 * setDate (setUTCDate)
98 * setHours (setUTCHours)
99 * setMinutes (setUTCMinutes)
100 * setMonth (setUTCMonth)
101 * setSeconds (setUTCSeconds)
102 * setMilliseconds (setUTCMilliseconds)
103 * setTime
104 * setYear (setFullYear, setUTCFullYear)
105 * toGMTString (toUTCString)
106 * toLocaleString
107 * toString
108 *
109 *
110 * These Java methods are not supported
111 *
112 * setDay
113 * before
114 * after
115 * equals
116 * hashCode
117 */
119 /*
120 * 11/97 - jsdate.c has been rewritten to conform to the ECMA-262 language
121 * definition and reduce dependence on NSPR. NSPR is used to get the current
122 * time in milliseconds, the time zone offset, and the daylight savings time
123 * offset for a given time. NSPR is also used for Date.toLocaleString(), for
124 * locale-specific formatting, and to get a string representing the timezone.
125 * (Which turns out to be platform-dependent.)
126 *
127 * To do:
128 * (I did some performance tests by timing how long it took to run what
129 * I had of the js ECMA conformance tests.)
130 *
131 * - look at saving results across multiple calls to supporting
132 * functions; the toString functions compute some of the same values
133 * multiple times. Although - I took a quick stab at this, and I lost
134 * rather than gained. (Fractionally.) Hard to tell what compilers/processors
135 * are doing these days.
136 *
137 * - look at tweaking function return types to return double instead
138 * of int; this seems to make things run slightly faster sometimes.
139 * (though it could be architecture-dependent.) It'd be good to see
140 * how this does on win32. (Tried it on irix.) Types could use a
141 * general going-over.
142 */
144 /*
145 * Supporting functions - ECMA 15.9.1.*
146 */
148 #define HalfTimeDomain 8.64e15
149 #define HoursPerDay 24.0
150 #define MinutesPerDay (HoursPerDay * MinutesPerHour)
151 #define MinutesPerHour 60.0
152 #define SecondsPerDay (MinutesPerDay * SecondsPerMinute)
153 #define SecondsPerHour (MinutesPerHour * SecondsPerMinute)
154 #define SecondsPerMinute 60.0
156 #if defined(XP_WIN) || defined(XP_OS2)
157 /* Work around msvc double optimization bug by making these runtime values; if
158 * they're available at compile time, msvc optimizes division by them by
159 * computing the reciprocal and multiplying instead of dividing - this loses
160 * when the reciprocal isn't representable in a double.
161 */
162 static jsdouble msPerSecond = 1000.0;
163 static jsdouble msPerDay = SecondsPerDay * 1000.0;
164 static jsdouble msPerHour = SecondsPerHour * 1000.0;
165 static jsdouble msPerMinute = SecondsPerMinute * 1000.0;
166 #else
167 #define msPerDay (SecondsPerDay * msPerSecond)
168 #define msPerHour (SecondsPerHour * msPerSecond)
169 #define msPerMinute (SecondsPerMinute * msPerSecond)
170 #define msPerSecond 1000.0
171 #endif
173 #define Day(t) floor((t) / msPerDay)
175 static jsdouble
176 TimeWithinDay(jsdouble t)
177 {
178 jsdouble result;
179 result = fmod(t, msPerDay);
180 if (result < 0)
181 result += msPerDay;
182 return result;
183 }
185 #define DaysInYear(y) ((y) % 4 == 0 && ((y) % 100 || ((y) % 400 == 0)) \
186 ? 366 : 365)
188 /* math here has to be f.p, because we need
189 * floor((1968 - 1969) / 4) == -1
190 */
191 #define DayFromYear(y) (365 * ((y)-1970) + floor(((y)-1969)/4.0) \
192 - floor(((y)-1901)/100.0) + floor(((y)-1601)/400.0))
193 #define TimeFromYear(y) (DayFromYear(y) * msPerDay)
195 static jsint
196 YearFromTime(jsdouble t)
197 {
198 jsint y = (jsint) floor(t /(msPerDay*365.2425)) + 1970;
199 jsdouble t2 = (jsdouble) TimeFromYear(y);
201 if (t2 > t) {
202 y--;
203 } else {
204 if (t2 + msPerDay * DaysInYear(y) <= t)
205 y++;
206 }
207 return y;
208 }
210 #define InLeapYear(t) (JSBool) (DaysInYear(YearFromTime(t)) == 366)
212 #define DayWithinYear(t, year) ((intN) (Day(t) - DayFromYear(year)))
214 /*
215 * The following array contains the day of year for the first day of
216 * each month, where index 0 is January, and day 0 is January 1.
217 */
218 static jsdouble firstDayOfMonth[2][12] = {
219 {0.0, 31.0, 59.0, 90.0, 120.0, 151.0, 181.0, 212.0, 243.0, 273.0, 304.0, 334.0},
220 {0.0, 31.0, 60.0, 91.0, 121.0, 152.0, 182.0, 213.0, 244.0, 274.0, 305.0, 335.0}
221 };
223 #define DayFromMonth(m, leap) firstDayOfMonth[leap][(intN)m];
225 static intN
226 MonthFromTime(jsdouble t)
227 {
228 intN d, step;
229 jsint year = YearFromTime(t);
230 d = DayWithinYear(t, year);
232 if (d < (step = 31))
233 return 0;
234 step += (InLeapYear(t) ? 29 : 28);
235 if (d < step)
236 return 1;
237 if (d < (step += 31))
238 return 2;
239 if (d < (step += 30))
240 return 3;
241 if (d < (step += 31))
242 return 4;
243 if (d < (step += 30))
244 return 5;
245 if (d < (step += 31))
246 return 6;
247 if (d < (step += 31))
248 return 7;
249 if (d < (step += 30))
250 return 8;
251 if (d < (step += 31))
252 return 9;
253 if (d < (step += 30))
254 return 10;
255 return 11;
256 }
258 static intN
259 DateFromTime(jsdouble t)
260 {
261 intN d, step, next;
262 jsint year = YearFromTime(t);
263 d = DayWithinYear(t, year);
265 if (d <= (next = 30))
266 return d + 1;
267 step = next;
268 next += (InLeapYear(t) ? 29 : 28);
269 if (d <= next)
270 return d - step;
271 step = next;
272 if (d <= (next += 31))
273 return d - step;
274 step = next;
275 if (d <= (next += 30))
276 return d - step;
277 step = next;
278 if (d <= (next += 31))
279 return d - step;
280 step = next;
281 if (d <= (next += 30))
282 return d - step;
283 step = next;
284 if (d <= (next += 31))
285 return d - step;
286 step = next;
287 if (d <= (next += 31))
288 return d - step;
289 step = next;
290 if (d <= (next += 30))
291 return d - step;
292 step = next;
293 if (d <= (next += 31))
294 return d - step;
295 step = next;
296 if (d <= (next += 30))
297 return d - step;
298 step = next;
299 return d - step;
300 }
302 static intN
303 WeekDay(jsdouble t)
304 {
305 jsint result;
306 result = (jsint) Day(t) + 4;
307 result = result % 7;
308 if (result < 0)
309 result += 7;
310 return (intN) result;
311 }
313 /* LocalTZA gets set by js_InitDateClass() */
314 static jsdouble LocalTZA;
316 static jsdouble
317 DaylightSavingTA(jsdouble t)
318 {
319 volatile int64 PR_t;
320 int64 ms2us;
321 int64 offset;
322 jsdouble result;
324 /* abort if NaN */
325 if (JSDOUBLE_IS_NaN(t))
326 return t;
328 /* put our t in an LL, and map it to usec for prtime */
329 JSLL_D2L(PR_t, t);
330 JSLL_I2L(ms2us, PRMJ_USEC_PER_MSEC);
331 JSLL_MUL(PR_t, PR_t, ms2us);
333 offset = PRMJ_DSTOffset(PR_t);
335 JSLL_DIV(offset, offset, ms2us);
336 JSLL_L2D(result, offset);
337 return result;
338 }
341 #define AdjustTime(t) fmod(LocalTZA + DaylightSavingTA(t), msPerDay)
343 #define LocalTime(t) ((t) + AdjustTime(t))
345 static jsdouble
346 UTC(jsdouble t)
347 {
348 return t - AdjustTime(t - LocalTZA);
349 }
351 static intN
352 HourFromTime(jsdouble t)
353 {
354 intN result = (intN) fmod(floor(t/msPerHour), HoursPerDay);
355 if (result < 0)
356 result += (intN)HoursPerDay;
357 return result;
358 }
360 static intN
361 MinFromTime(jsdouble t)
362 {
363 intN result = (intN) fmod(floor(t / msPerMinute), MinutesPerHour);
364 if (result < 0)
365 result += (intN)MinutesPerHour;
366 return result;
367 }
369 static intN
370 SecFromTime(jsdouble t)
371 {
372 intN result = (intN) fmod(floor(t / msPerSecond), SecondsPerMinute);
373 if (result < 0)
374 result += (intN)SecondsPerMinute;
375 return result;
376 }
378 static intN
379 msFromTime(jsdouble t)
380 {
381 intN result = (intN) fmod(t, msPerSecond);
382 if (result < 0)
383 result += (intN)msPerSecond;
384 return result;
385 }
387 #define MakeTime(hour, min, sec, ms) \
388 (((hour * MinutesPerHour + min) * SecondsPerMinute + sec) * msPerSecond + ms)
390 static jsdouble
391 MakeDay(jsdouble year, jsdouble month, jsdouble date)
392 {
393 jsdouble result;
394 JSBool leap;
395 jsdouble yearday;
396 jsdouble monthday;
398 year += floor(month / 12);
400 month = fmod(month, 12.0);
401 if (month < 0)
402 month += 12;
404 leap = (DaysInYear((jsint) year) == 366);
406 yearday = floor(TimeFromYear(year) / msPerDay);
407 monthday = DayFromMonth(month, leap);
409 result = yearday
410 + monthday
411 + date - 1;
412 return result;
413 }
415 #define MakeDate(day, time) (day * msPerDay + time)
417 #define TIMECLIP(d) ((JSDOUBLE_IS_FINITE(d) \
418 && !((d < 0 ? -d : d) > HalfTimeDomain)) \
419 ? js_DoubleToInteger(d + (+0.)) : *cx->runtime->jsNaN)
421 /**
422 * end of ECMA 'support' functions
423 */
424 \f
425 /*
426 * Other Support routines and definitions
427 */
429 static JSClass date_class = {
430 js_Date_str,
431 JSCLASS_HAS_PRIVATE,
432 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
433 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
434 JSCLASS_NO_OPTIONAL_MEMBERS
435 };
437 /* for use by date_parse */
439 static const char* wtb[] = {
440 "am", "pm",
441 "monday", "tuesday", "wednesday", "thursday", "friday",
442 "saturday", "sunday",
443 "january", "february", "march", "april", "may", "june",
444 "july", "august", "september", "october", "november", "december",
445 "gmt", "ut", "utc",
446 "est", "edt",
447 "cst", "cdt",
448 "mst", "mdt",
449 "pst", "pdt"
450 /* time zone table needs to be expanded */
451 };
453 static int ttb[] = {
454 -1, -2, 0, 0, 0, 0, 0, 0, 0, /* AM/PM */
455 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
456 10000 + 0, 10000 + 0, 10000 + 0, /* GMT/UT/UTC */
457 10000 + 5 * 60, 10000 + 4 * 60, /* EST/EDT */
458 10000 + 6 * 60, 10000 + 5 * 60, /* CST/CDT */
459 10000 + 7 * 60, 10000 + 6 * 60, /* MST/MDT */
460 10000 + 8 * 60, 10000 + 7 * 60 /* PST/PDT */
461 };
463 /* helper for date_parse */
464 static JSBool
465 date_regionMatches(const char* s1, int s1off, const jschar* s2, int s2off,
466 int count, int ignoreCase)
467 {
468 JSBool result = JS_FALSE;
469 /* return true if matches, otherwise, false */
471 while (count > 0 && s1[s1off] && s2[s2off]) {
472 if (ignoreCase) {
473 if (JS_TOLOWER((jschar)s1[s1off]) != JS_TOLOWER(s2[s2off])) {
474 break;
475 }
476 } else {
477 if ((jschar)s1[s1off] != s2[s2off]) {
478 break;
479 }
480 }
481 s1off++;
482 s2off++;
483 count--;
484 }
486 if (count == 0) {
487 result = JS_TRUE;
488 }
490 return result;
491 }
493 /* find UTC time from given date... no 1900 correction! */
494 static jsdouble
495 date_msecFromDate(jsdouble year, jsdouble mon, jsdouble mday, jsdouble hour,
496 jsdouble min, jsdouble sec, jsdouble msec)
497 {
498 jsdouble day;
499 jsdouble msec_time;
500 jsdouble result;
502 day = MakeDay(year, mon, mday);
503 msec_time = MakeTime(hour, min, sec, msec);
504 result = MakeDate(day, msec_time);
505 return result;
506 }
507 \f
508 /*
509 * See ECMA 15.9.4.[3-10];
510 */
511 /* XXX this function must be above date_parseString to avoid a
512 horrid bug in the Win16 1.52 compiler */
513 #define MAXARGS 7
514 static JSBool
515 date_UTC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
516 {
517 jsdouble array[MAXARGS];
518 uintN loop;
519 jsdouble d;
521 for (loop = 0; loop < MAXARGS; loop++) {
522 if (loop < argc) {
523 if (!js_ValueToNumber(cx, argv[loop], &d))
524 return JS_FALSE;
525 /* return NaN if any arg is NaN */
526 if (!JSDOUBLE_IS_FINITE(d)) {
527 return js_NewNumberValue(cx, d, rval);
528 }
529 array[loop] = floor(d);
530 } else {
531 array[loop] = 0;
532 }
533 }
535 /* adjust 2-digit years into the 20th century */
536 if (array[0] >= 0 && array[0] <= 99)
537 array[0] += 1900;
539 /* if we got a 0 for 'date' (which is out of range)
540 * pretend it's a 1. (So Date.UTC(1972, 5) works) */
541 if (array[2] < 1)
542 array[2] = 1;
544 d = date_msecFromDate(array[0], array[1], array[2],
545 array[3], array[4], array[5], array[6]);
546 d = TIMECLIP(d);
548 return js_NewNumberValue(cx, d, rval);
549 }
551 static JSBool
552 date_parseString(JSString *str, jsdouble *result)
553 {
554 jsdouble msec;
556 const jschar *s = JSSTRING_CHARS(str);
557 size_t limit = JSSTRING_LENGTH(str);
558 size_t i = 0;
559 int year = -1;
560 int mon = -1;
561 int mday = -1;
562 int hour = -1;
563 int min = -1;
564 int sec = -1;
565 int c = -1;
566 int n = -1;
567 jsdouble tzoffset = -1; /* was an int, overflowed on win16!!! */
568 int prevc = 0;
569 JSBool seenplusminus = JS_FALSE;
571 if (limit == 0)
572 goto syntax;
573 while (i < limit) {
574 c = s[i];
575 i++;
576 if (c <= ' ' || c == ',' || c == '-') {
577 if (c == '-' && '0' <= s[i] && s[i] <= '9') {
578 prevc = c;
579 }
580 continue;
581 }
582 if (c == '(') { /* comments) */
583 int depth = 1;
584 while (i < limit) {
585 c = s[i];
586 i++;
587 if (c == '(') depth++;
588 else if (c == ')')
589 if (--depth <= 0)
590 break;
591 }
592 continue;
593 }
594 if ('0' <= c && c <= '9') {
595 n = c - '0';
596 while (i < limit && '0' <= (c = s[i]) && c <= '9') {
597 n = n * 10 + c - '0';
598 i++;
599 }
601 /* allow TZA before the year, so
602 * 'Wed Nov 05 21:49:11 GMT-0800 1997'
603 * works */
605 /* uses of seenplusminus allow : in TZA, so Java
606 * no-timezone style of GMT+4:30 works
607 */
609 if ((prevc == '+' || prevc == '-')/* && year>=0 */) {
610 /* make ':' case below change tzoffset */
611 seenplusminus = JS_TRUE;
613 /* offset */
614 if (n < 24)
615 n = n * 60; /* EG. "GMT-3" */
616 else
617 n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */
618 if (prevc == '+') /* plus means east of GMT */
619 n = -n;
620 if (tzoffset != 0 && tzoffset != -1)
621 goto syntax;
622 tzoffset = n;
623 } else if (n >= 70 ||
624 (prevc == '/' && mon >= 0 && mday >= 0 && year < 0)) {
625 if (year >= 0)
626 goto syntax;
627 else if (c <= ' ' || c == ',' || c == '/' || i >= limit)
628 year = n < 100 ? n + 1900 : n;
629 else
630 goto syntax;
631 } else if (c == ':') {
632 if (hour < 0)
633 hour = /*byte*/ n;
634 else if (min < 0)
635 min = /*byte*/ n;
636 else
637 goto syntax;
638 } else if (c == '/') {
639 if (mon < 0)
640 mon = /*byte*/ n-1;
641 else if (mday < 0)
642 mday = /*byte*/ n;
643 else
644 goto syntax;
645 } else if (i < limit && c != ',' && c > ' ' && c != '-') {
646 goto syntax;
647 } else if (seenplusminus && n < 60) { /* handle GMT-3:30 */
648 if (tzoffset < 0)
649 tzoffset -= n;
650 else
651 tzoffset += n;
652 } else if (hour >= 0 && min < 0) {
653 min = /*byte*/ n;
654 } else if (min >= 0 && sec < 0) {
655 sec = /*byte*/ n;
656 } else if (mday < 0) {
657 mday = /*byte*/ n;
658 } else {
659 goto syntax;
660 }
661 prevc = 0;
662 } else if (c == '/' || c == ':' || c == '+' || c == '-') {
663 prevc = c;
664 } else {
665 size_t st = i - 1;
666 int k;
667 while (i < limit) {
668 c = s[i];
669 if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')))
670 break;
671 i++;
672 }
673 if (i <= st + 1)
674 goto syntax;
675 for (k = (sizeof(wtb)/sizeof(char*)); --k >= 0;)
676 if (date_regionMatches(wtb[k], 0, s, st, i-st, 1)) {
677 int action = ttb[k];
678 if (action != 0) {
679 if (action < 0) {
680 /*
681 * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as
682 * 12:30, instead of blindly adding 12 if PM.
683 */
684 JS_ASSERT(action == -1 || action == -2);
685 if (hour > 12 || hour < 0) {
686 goto syntax;
687 } else {
688 if (action == -1 && hour == 12) { /* am */
689 hour = 0;
690 } else if (action == -2 && hour != 12) { /* pm */
691 hour += 12;
692 }
693 }
694 } else if (action <= 13) { /* month! */
695 if (mon < 0) {
696 mon = /*byte*/ (action - 2);
697 } else {
698 goto syntax;
699 }
700 } else {
701 tzoffset = action - 10000;
702 }
703 }
704 break;
705 }
706 if (k < 0)
707 goto syntax;
708 prevc = 0;
709 }
710 }
711 if (year < 0 || mon < 0 || mday < 0)
712 goto syntax;
713 if (sec < 0)
714 sec = 0;
715 if (min < 0)
716 min = 0;
717 if (hour < 0)
718 hour = 0;
719 if (tzoffset == -1) { /* no time zone specified, have to use local */
720 jsdouble msec_time;
721 msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
723 *result = UTC(msec_time);
724 return JS_TRUE;
725 }
727 msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
728 msec += tzoffset * msPerMinute;
729 *result = msec;
730 return JS_TRUE;
732 syntax:
733 /* syntax error */
734 *result = 0;
735 return JS_FALSE;
736 }
738 static JSBool
739 date_parse(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
740 {
741 JSString *str;
742 jsdouble result;
744 str = js_ValueToString(cx, argv[0]);
745 if (!str)
746 return JS_FALSE;
747 if (!date_parseString(str, &result)) {
748 *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
749 return JS_TRUE;
750 }
752 result = TIMECLIP(result);
753 return js_NewNumberValue(cx, result, rval);
754 }
756 static JSBool
757 date_now(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
758 {
759 int64 us, ms, us2ms;
760 jsdouble msec_time;
762 us = PRMJ_Now();
763 JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC);
764 JSLL_DIV(ms, us, us2ms);
765 JSLL_L2D(msec_time, ms);
767 return js_NewDoubleValue(cx, msec_time, rval);
768 }
770 /*
771 * Check that obj is an object of class Date, and get the date value.
772 * Return NULL on failure.
773 */
774 static jsdouble *
775 date_getProlog(JSContext *cx, JSObject *obj, jsval *argv)
776 {
777 if (!JS_InstanceOf(cx, obj, &date_class, argv))
778 return NULL;
779 return JSVAL_TO_DOUBLE(OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE));
780 }
782 /*
783 * See ECMA 15.9.5.4 thru 15.9.5.23
784 */
785 static JSBool
786 date_getTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
787 {
788 jsdouble *date = date_getProlog(cx, obj, argv);
789 if (!date)
790 return JS_FALSE;
792 return js_NewNumberValue(cx, *date, rval);
793 }
795 static JSBool
796 date_getYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
797 {
798 jsdouble result;
799 jsdouble *date = date_getProlog(cx, obj, argv);
800 if (!date)
801 return JS_FALSE;
802 result = *date;
804 if (!JSDOUBLE_IS_FINITE(result))
805 return js_NewNumberValue(cx, result, rval);
807 result = YearFromTime(LocalTime(result));
809 /*
810 * During the great date rewrite of 1.3, we tried to track the evolving ECMA
811 * standard, which then had a definition of getYear which always subtracted
812 * 1900. Which we implemented, not realizing that it was incompatible with
813 * the old behavior... now, rather than thrash the behavior yet again,
814 * we've decided to leave it with the - 1900 behavior and point people to
815 * the getFullYear method. But we try to protect existing scripts that
816 * have specified a version...
817 */
818 if (cx->version == JSVERSION_1_0 ||
819 cx->version == JSVERSION_1_1 ||
820 cx->version == JSVERSION_1_2)
821 {
822 if (result >= 1900 && result < 2000)
823 result -= 1900;
824 } else {
825 result -= 1900;
826 }
827 return js_NewNumberValue(cx, result, rval);
828 }
830 static JSBool
831 date_getFullYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
832 jsval *rval)
833 {
834 jsdouble result;
835 jsdouble *date = date_getProlog(cx, obj, argv);
836 if (!date)
837 return JS_FALSE;
838 result = *date;
840 if (!JSDOUBLE_IS_FINITE(result))
841 return js_NewNumberValue(cx, result, rval);
843 result = YearFromTime(LocalTime(result));
844 return js_NewNumberValue(cx, result, rval);
845 }
847 static JSBool
848 date_getUTCFullYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
849 jsval *rval)
850 {
851 jsdouble result;
852 jsdouble *date = date_getProlog(cx, obj, argv);
853 if (!date)
854 return JS_FALSE;
855 result = *date;
857 if (!JSDOUBLE_IS_FINITE(result))
858 return js_NewNumberValue(cx, result, rval);
860 result = YearFromTime(result);
861 return js_NewNumberValue(cx, result, rval);
862 }
864 static JSBool
865 date_getMonth(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
866 jsval *rval)
867 {
868 jsdouble result;
869 jsdouble *date = date_getProlog(cx, obj, argv);
870 if (!date)
871 return JS_FALSE;
872 result = *date;
874 if (!JSDOUBLE_IS_FINITE(result))
875 return js_NewNumberValue(cx, result, rval);
877 result = MonthFromTime(LocalTime(result));
878 return js_NewNumberValue(cx, result, rval);
879 }
881 static JSBool
882 date_getUTCMonth(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
883 jsval *rval)
884 {
885 jsdouble result;
886 jsdouble *date = date_getProlog(cx, obj, argv);
887 if (!date)
888 return JS_FALSE;
889 result = *date;
891 if (!JSDOUBLE_IS_FINITE(result))
892 return js_NewNumberValue(cx, result, rval);
894 result = MonthFromTime(result);
895 return js_NewNumberValue(cx, result, rval);
896 }
898 static JSBool
899 date_getDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
900 {
901 jsdouble result;
902 jsdouble *date = date_getProlog(cx, obj, argv);
903 if (!date)
904 return JS_FALSE;
905 result = *date;
907 if (!JSDOUBLE_IS_FINITE(result))
908 return js_NewNumberValue(cx, result, rval);
910 result = LocalTime(result);
911 result = DateFromTime(result);
912 return js_NewNumberValue(cx, result, rval);
913 }
915 static JSBool
916 date_getUTCDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
917 jsval *rval)
918 {
919 jsdouble result;
920 jsdouble *date = date_getProlog(cx, obj, argv);
921 if (!date)
922 return JS_FALSE;
923 result = *date;
925 if (!JSDOUBLE_IS_FINITE(result))
926 return js_NewNumberValue(cx, result, rval);
928 result = DateFromTime(result);
929 return js_NewNumberValue(cx, result, rval);
930 }
932 static JSBool
933 date_getDay(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
934 {
935 jsdouble result;
936 jsdouble *date = date_getProlog(cx, obj, argv);
937 if (!date)
938 return JS_FALSE;
939 result = *date;
941 if (!JSDOUBLE_IS_FINITE(result))
942 return js_NewNumberValue(cx, result, rval);
944 result = LocalTime(result);
945 result = WeekDay(result);
946 return js_NewNumberValue(cx, result, rval);
947 }
949 static JSBool
950 date_getUTCDay(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
951 jsval *rval)
952 {
953 jsdouble result;
954 jsdouble *date = date_getProlog(cx, obj, argv);
955 if (!date)
956 return JS_FALSE;
957 result = *date;
959 if (!JSDOUBLE_IS_FINITE(result))
960 return js_NewNumberValue(cx, result, rval);
962 result = WeekDay(result);
963 return js_NewNumberValue(cx, result, rval);
964 }
966 static JSBool
967 date_getHours(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
968 jsval *rval)
969 {
970 jsdouble result;
971 jsdouble *date = date_getProlog(cx, obj, argv);
972 if (!date)
973 return JS_FALSE;
974 result = *date;
976 if (!JSDOUBLE_IS_FINITE(result))
977 return js_NewNumberValue(cx, result, rval);
979 result = HourFromTime(LocalTime(result));
980 return js_NewNumberValue(cx, result, rval);
981 }
983 static JSBool
984 date_getUTCHours(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
985 jsval *rval)
986 {
987 jsdouble result;
988 jsdouble *date = date_getProlog(cx, obj, argv);
989 if (!date)
990 return JS_FALSE;
991 result = *date;
993 if (!JSDOUBLE_IS_FINITE(result))
994 return js_NewNumberValue(cx, result, rval);
996 result = HourFromTime(result);
997 return js_NewNumberValue(cx, result, rval);
998 }
1000 static JSBool
1001 date_getMinutes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1002 jsval *rval)
1003 {
1004 jsdouble result;
1005 jsdouble *date = date_getProlog(cx, obj, argv);
1006 if (!date)
1007 return JS_FALSE;
1008 result = *date;
1010 if (!JSDOUBLE_IS_FINITE(result))
1011 return js_NewNumberValue(cx, result, rval);
1013 result = MinFromTime(LocalTime(result));
1014 return js_NewNumberValue(cx, result, rval);
1015 }
1017 static JSBool
1018 date_getUTCMinutes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1019 jsval *rval)
1020 {
1021 jsdouble result;
1022 jsdouble *date = date_getProlog(cx, obj, argv);
1023 if (!date)
1024 return JS_FALSE;
1025 result = *date;
1027 if (!JSDOUBLE_IS_FINITE(result))
1028 return js_NewNumberValue(cx, result, rval);
1030 result = MinFromTime(result);
1031 return js_NewNumberValue(cx, result, rval);
1032 }
1034 /* Date.getSeconds is mapped to getUTCSeconds */
1036 static JSBool
1037 date_getUTCSeconds(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1038 jsval *rval)
1039 {
1040 jsdouble result;
1041 jsdouble *date = date_getProlog(cx, obj, argv);
1042 if (!date)
1043 return JS_FALSE;
1044 result = *date;
1046 if (!JSDOUBLE_IS_FINITE(result))
1047 return js_NewNumberValue(cx, result, rval);
1049 result = SecFromTime(result);
1050 return js_NewNumberValue(cx, result, rval);
1051 }
1053 /* Date.getMilliseconds is mapped to getUTCMilliseconds */
1055 static JSBool
1056 date_getUTCMilliseconds(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1057 jsval *rval)
1058 {
1059 jsdouble result;
1060 jsdouble *date = date_getProlog(cx, obj, argv);
1061 if (!date)
1062 return JS_FALSE;
1063 result = *date;
1065 if (!JSDOUBLE_IS_FINITE(result))
1066 return js_NewNumberValue(cx, result, rval);
1068 result = msFromTime(result);
1069 return js_NewNumberValue(cx, result, rval);
1070 }
1072 static JSBool
1073 date_getTimezoneOffset(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1074 jsval *rval)
1075 {
1076 jsdouble result;
1077 jsdouble *date = date_getProlog(cx, obj, argv);
1078 if (!date)
1079 return JS_FALSE;
1080 result = *date;
1082 /*
1083 * Return the time zone offset in minutes for the current locale
1084 * that is appropriate for this time. This value would be a
1085 * constant except for daylight savings time.
1086 */
1087 result = (result - LocalTime(result)) / msPerMinute;
1088 return js_NewNumberValue(cx, result, rval);
1089 }
1091 static JSBool
1092 date_setTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1093 {
1094 jsdouble result;
1095 jsdouble *date = date_getProlog(cx, obj, argv);
1096 if (!date)
1097 return JS_FALSE;
1099 if (!js_ValueToNumber(cx, argv[0], &result))
1100 return JS_FALSE;
1102 result = TIMECLIP(result);
1104 *date = result;
1105 return js_NewNumberValue(cx, result, rval);
1106 }
1108 static JSBool
1109 date_makeTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1110 uintN maxargs, JSBool local, jsval *rval)
1111 {
1112 uintN i;
1113 jsdouble args[4], *argp, *stop;
1114 jsdouble hour, min, sec, msec;
1115 jsdouble lorutime; /* Local or UTC version of *date */
1117 jsdouble msec_time;
1118 jsdouble result;
1120 jsdouble *date = date_getProlog(cx, obj, argv);
1121 if (!date)
1122 return JS_FALSE;
1124 result = *date;
1126 /* just return NaN if the date is already NaN */
1127 if (!JSDOUBLE_IS_FINITE(result))
1128 return js_NewNumberValue(cx, result, rval);
1130 /* Satisfy the ECMA rule that if a function is called with
1131 * fewer arguments than the specified formal arguments, the
1132 * remaining arguments are set to undefined. Seems like all
1133 * the Date.setWhatever functions in ECMA are only varargs
1134 * beyond the first argument; this should be set to undefined
1135 * if it's not given. This means that "d = new Date();
1136 * d.setMilliseconds()" returns NaN. Blech.
1137 */
1138 if (argc == 0)
1139 argc = 1; /* should be safe, because length of all setters is 1 */
1140 else if (argc > maxargs)
1141 argc = maxargs; /* clamp argc */
1143 for (i = 0; i < argc; i++) {
1144 if (!js_ValueToNumber(cx, argv[i], &args[i]))
1145 return JS_FALSE;
1146 if (!JSDOUBLE_IS_FINITE(args[i])) {
1147 *date = *cx->runtime->jsNaN;
1148 return js_NewNumberValue(cx, *date, rval);
1149 }
1150 args[i] = js_DoubleToInteger(args[i]);
1151 }
1153 if (local)
1154 lorutime = LocalTime(result);
1155 else
1156 lorutime = result;
1158 argp = args;
1159 stop = argp + argc;
1160 if (maxargs >= 4 && argp < stop)
1161 hour = *argp++;
1162 else
1163 hour = HourFromTime(lorutime);
1165 if (maxargs >= 3 && argp < stop)
1166 min = *argp++;
1167 else
1168 min = MinFromTime(lorutime);
1170 if (maxargs >= 2 && argp < stop)
1171 sec = *argp++;
1172 else
1173 sec = SecFromTime(lorutime);
1175 if (maxargs >= 1 && argp < stop)
1176 msec = *argp;
1177 else
1178 msec = msFromTime(lorutime);
1180 msec_time = MakeTime(hour, min, sec, msec);
1181 result = MakeDate(Day(lorutime), msec_time);
1183 /* fprintf(stderr, "%f\n", result); */
1185 if (local)
1186 result = UTC(result);
1188 /* fprintf(stderr, "%f\n", result); */
1190 *date = TIMECLIP(result);
1191 return js_NewNumberValue(cx, *date, rval);
1192 }
1194 static JSBool
1195 date_setMilliseconds(JSContext *cx, JSObject *obj, uintN argc,
1196 jsval *argv, jsval *rval)
1197 {
1198 return date_makeTime(cx, obj, argc, argv, 1, JS_TRUE, rval);
1199 }
1201 static JSBool
1202 date_setUTCMilliseconds(JSContext *cx, JSObject *obj, uintN argc,
1203 jsval *argv, jsval *rval)
1204 {
1205 return date_makeTime(cx, obj, argc, argv, 1, JS_FALSE, rval);
1206 }
1208 static JSBool
1209 date_setSeconds(JSContext *cx, JSObject *obj, uintN argc,
1210 jsval *argv, jsval *rval)
1211 {
1212 return date_makeTime(cx, obj, argc, argv, 2, JS_TRUE, rval);
1213 }
1215 static JSBool
1216 date_setUTCSeconds(JSContext *cx, JSObject *obj, uintN argc,
1217 jsval *argv, jsval *rval)
1218 {
1219 return date_makeTime(cx, obj, argc, argv, 2, JS_FALSE, rval);
1220 }
1222 static JSBool
1223 date_setMinutes(JSContext *cx, JSObject *obj, uintN argc,
1224 jsval *argv, jsval *rval)
1225 {
1226 return date_makeTime(cx, obj, argc, argv, 3, JS_TRUE, rval);
1227 }
1229 static JSBool
1230 date_setUTCMinutes(JSContext *cx, JSObject *obj, uintN argc,
1231 jsval *argv, jsval *rval)
1232 {
1233 return date_makeTime(cx, obj, argc, argv, 3, JS_FALSE, rval);
1234 }
1236 static JSBool
1237 date_setHours(JSContext *cx, JSObject *obj, uintN argc,
1238 jsval *argv, jsval *rval)
1239 {
1240 return date_makeTime(cx, obj, argc, argv, 4, JS_TRUE, rval);
1241 }
1243 static JSBool
1244 date_setUTCHours(JSContext *cx, JSObject *obj, uintN argc,
1245 jsval *argv, jsval *rval)
1246 {
1247 return date_makeTime(cx, obj, argc, argv, 4, JS_FALSE, rval);
1248 }
1250 static JSBool
1251 date_makeDate(JSContext *cx, JSObject *obj, uintN argc,
1252 jsval *argv, uintN maxargs, JSBool local, jsval *rval)
1253 {
1254 uintN i;
1255 jsdouble lorutime; /* local or UTC version of *date */
1256 jsdouble args[3], *argp, *stop;
1257 jsdouble year, month, day;
1258 jsdouble result;
1260 jsdouble *date = date_getProlog(cx, obj, argv);
1261 if (!date)
1262 return JS_FALSE;
1264 result = *date;
1266 /* see complaint about ECMA in date_MakeTime */
1267 if (argc == 0)
1268 argc = 1; /* should be safe, because length of all setters is 1 */
1269 else if (argc > maxargs)
1270 argc = maxargs; /* clamp argc */
1272 for (i = 0; i < argc; i++) {
1273 if (!js_ValueToNumber(cx, argv[i], &args[i]))
1274 return JS_FALSE;
1275 if (!JSDOUBLE_IS_FINITE(args[i])) {
1276 *date = *cx->runtime->jsNaN;
1277 return js_NewNumberValue(cx, *date, rval);
1278 }
1279 args[i] = js_DoubleToInteger(args[i]);
1280 }
1282 /* return NaN if date is NaN and we're not setting the year,
1283 * If we are, use 0 as the time. */
1284 if (!(JSDOUBLE_IS_FINITE(result))) {
1285 if (argc < 3)
1286 return js_NewNumberValue(cx, result, rval);
1287 else
1288 lorutime = +0.;
1289 } else {
1290 if (local)
1291 lorutime = LocalTime(result);
1292 else
1293 lorutime = result;
1294 }
1296 argp = args;
1297 stop = argp + argc;
1298 if (maxargs >= 3 && argp < stop)
1299 year = *argp++;
1300 else
1301 year = YearFromTime(lorutime);
1303 if (maxargs >= 2 && argp < stop)
1304 month = *argp++;
1305 else
1306 month = MonthFromTime(lorutime);
1308 if (maxargs >= 1 && argp < stop)
1309 day = *argp++;
1310 else
1311 day = DateFromTime(lorutime);
1313 day = MakeDay(year, month, day); /* day within year */
1314 result = MakeDate(day, TimeWithinDay(lorutime));
1316 if (local)
1317 result = UTC(result);
1319 *date = TIMECLIP(result);
1320 return js_NewNumberValue(cx, *date, rval);
1321 }
1323 static JSBool
1324 date_setDate(JSContext *cx, JSObject *obj, uintN argc,
1325 jsval *argv, jsval *rval)
1326 {
1327 return date_makeDate(cx, obj, argc, argv, 1, JS_TRUE, rval);
1328 }
1330 static JSBool
1331 date_setUTCDate(JSContext *cx, JSObject *obj, uintN argc,
1332 jsval *argv, jsval *rval)
1333 {
1334 return date_makeDate(cx, obj, argc, argv, 1, JS_FALSE, rval);
1335 }
1337 static JSBool
1338 date_setMonth(JSContext *cx, JSObject *obj, uintN argc,
1339 jsval *argv, jsval *rval)
1340 {
1341 return date_makeDate(cx, obj, argc, argv, 2, JS_TRUE, rval);
1342 }
1344 static JSBool
1345 date_setUTCMonth(JSContext *cx, JSObject *obj, uintN argc,
1346 jsval *argv, jsval *rval)
1347 {
1348 return date_makeDate(cx, obj, argc, argv, 2, JS_FALSE, rval);
1349 }
1351 static JSBool
1352 date_setFullYear(JSContext *cx, JSObject *obj, uintN argc,
1353 jsval *argv, jsval *rval)
1354 {
1355 return date_makeDate(cx, obj, argc, argv, 3, JS_TRUE, rval);
1356 }
1358 static JSBool
1359 date_setUTCFullYear(JSContext *cx, JSObject *obj, uintN argc,
1360 jsval *argv, jsval *rval)
1361 {
1362 return date_makeDate(cx, obj, argc, argv, 3, JS_FALSE, rval);
1363 }
1365 static JSBool
1366 date_setYear(JSContext *cx, JSObject *obj, uintN argc,
1367 jsval *argv, jsval *rval)
1368 {
1369 jsdouble t;
1370 jsdouble year;
1371 jsdouble day;
1372 jsdouble result;
1374 jsdouble *date = date_getProlog(cx, obj, argv);
1375 if (!date)
1376 return JS_FALSE;
1378 result = *date;
1380 if (!js_ValueToNumber(cx, argv[0], &year))
1381 return JS_FALSE;
1382 if (!JSDOUBLE_IS_FINITE(year)) {
1383 *date = *cx->runtime->jsNaN;
1384 return js_NewNumberValue(cx, *date, rval);
1385 }
1387 year = js_DoubleToInteger(year);
1389 if (!JSDOUBLE_IS_FINITE(result)) {
1390 t = +0.0;
1391 } else {
1392 t = LocalTime(result);
1393 }
1395 if (year >= 0 && year <= 99)
1396 year += 1900;
1398 day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
1399 result = MakeDate(day, TimeWithinDay(t));
1400 result = UTC(result);
1402 *date = TIMECLIP(result);
1403 return js_NewNumberValue(cx, *date, rval);
1404 }
1406 /* constants for toString, toUTCString */
1407 static char js_NaN_date_str[] = "Invalid Date";
1408 static const char* days[] =
1409 {
1410 "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
1411 };
1412 static const char* months[] =
1413 {
1414 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1415 };
1417 static JSBool
1418 date_toGMTString(JSContext *cx, JSObject *obj, uintN argc,
1419 jsval *argv, jsval *rval)
1420 {
1421 char buf[100];
1422 JSString *str;
1423 jsdouble *date = date_getProlog(cx, obj, argv);
1424 if (!date)
1425 return JS_FALSE;
1427 if (!JSDOUBLE_IS_FINITE(*date)) {
1428 JS_snprintf(buf, sizeof buf, js_NaN_date_str);
1429 } else {
1430 jsdouble temp = *date;
1432 /* Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
1433 * requires a PRMJTime... which only has 16-bit years. Sub-ECMA.
1434 */
1435 JS_snprintf(buf, sizeof buf, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
1436 days[WeekDay(temp)],
1437 DateFromTime(temp),
1438 months[MonthFromTime(temp)],
1439 YearFromTime(temp),
1440 HourFromTime(temp),
1441 MinFromTime(temp),
1442 SecFromTime(temp));
1443 }
1444 str = JS_NewStringCopyZ(cx, buf);
1445 if (!str)
1446 return JS_FALSE;
1447 *rval = STRING_TO_JSVAL(str);
1448 return JS_TRUE;
1449 }
1451 /* for Date.toLocaleString; interface to PRMJTime date struct.
1452 * If findEquivalent is true, then try to map the year to an equivalent year
1453 * that's in range.
1454 */
1455 static void
1456 new_explode(jsdouble timeval, PRMJTime *split, JSBool findEquivalent)
1457 {
1458 jsint year = YearFromTime(timeval);
1459 int16 adjustedYear;
1461 /* If the year doesn't fit in a PRMJTime, find something to do about it. */
1462 if (year > 32767 || year < -32768) {
1463 if (findEquivalent) {
1464 /* We're really just trying to get a timezone string; map the year
1465 * to some equivalent year in the range 0 to 2800. Borrowed from
1466 * A. D. Olsen.
1467 */
1468 jsint cycles;
1469 #define CYCLE_YEARS 2800L
1470 cycles = (year >= 0) ? year / CYCLE_YEARS
1471 : -1 - (-1 - year) / CYCLE_YEARS;
1472 adjustedYear = (int16)(year - cycles * CYCLE_YEARS);
1473 } else {
1474 /* Clamp it to the nearest representable year. */
1475 adjustedYear = (int16)((year > 0) ? 32767 : - 32768);
1476 }
1477 } else {
1478 adjustedYear = (int16)year;
1479 }
1481 split->tm_usec = (int32) msFromTime(timeval) * 1000;
1482 split->tm_sec = (int8) SecFromTime(timeval);
1483 split->tm_min = (int8) MinFromTime(timeval);
1484 split->tm_hour = (int8) HourFromTime(timeval);
1485 split->tm_mday = (int8) DateFromTime(timeval);
1486 split->tm_mon = (int8) MonthFromTime(timeval);
1487 split->tm_wday = (int8) WeekDay(timeval);
1488 split->tm_year = (int16) adjustedYear;
1489 split->tm_yday = (int16) DayWithinYear(timeval, year);
1491 /* not sure how this affects things, but it doesn't seem
1492 to matter. */
1493 split->tm_isdst = (DaylightSavingTA(timeval) != 0);
1494 }
1496 typedef enum formatspec {
1497 FORMATSPEC_FULL, FORMATSPEC_DATE, FORMATSPEC_TIME
1498 } formatspec;
1500 /* helper function */
1501 static JSBool
1502 date_format(JSContext *cx, jsdouble date, formatspec format, jsval *rval)
1503 {
1504 char buf[100];
1505 JSString *str;
1506 char tzbuf[100];
1507 JSBool usetz;
1508 size_t i, tzlen;
1509 PRMJTime split;
1511 if (!JSDOUBLE_IS_FINITE(date)) {
1512 JS_snprintf(buf, sizeof buf, js_NaN_date_str);
1513 } else {
1514 jsdouble local = LocalTime(date);
1516 /* offset from GMT in minutes. The offset includes daylight savings,
1517 if it applies. */
1518 jsint minutes = (jsint) floor(AdjustTime(date) / msPerMinute);
1520 /* map 510 minutes to 0830 hours */
1521 intN offset = (minutes / 60) * 100 + minutes % 60;
1523 /* print as "Wed Nov 05 19:38:03 GMT-0800 (PST) 1997" The TZA is
1524 * printed as 'GMT-0800' rather than as 'PST' to avoid
1525 * operating-system dependence on strftime (which
1526 * PRMJ_FormatTimeUSEnglish calls, for %Z only.) win32 prints
1527 * PST as 'Pacific Standard Time.' This way we always know
1528 * what we're getting, and can parse it if we produce it.
1529 * The OS TZA string is included as a comment.
1530 */
1532 /* get a timezone string from the OS to include as a
1533 comment. */
1534 new_explode(date, &split, JS_TRUE);
1535 if (PRMJ_FormatTime(tzbuf, sizeof tzbuf, "(%Z)", &split) != 0) {
1537 /* Decide whether to use the resulting timezone string.
1538 *
1539 * Reject it if it contains any non-ASCII, non-alphanumeric
1540 * characters. It's then likely in some other character
1541 * encoding, and we probably won't display it correctly.
1542 */
1543 usetz = JS_TRUE;
1544 tzlen = strlen(tzbuf);
1545 if (tzlen > 100) {
1546 usetz = JS_FALSE;
1547 } else {
1548 for (i = 0; i < tzlen; i++) {
1549 jschar c = tzbuf[i];
1550 if (c > 127 ||
1551 !(isalpha(c) || isdigit(c) ||
1552 c == ' ' || c == '(' || c == ')')) {
1553 usetz = JS_FALSE;
1554 }
1555 }
1556 }
1558 /* Also reject it if it's not parenthesized or if it's '()'. */
1559 if (tzbuf[0] != '(' || tzbuf[1] == ')')
1560 usetz = JS_FALSE;
1561 } else
1562 usetz = JS_FALSE;
1564 switch (format) {
1565 case FORMATSPEC_FULL:
1566 /*
1567 * Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
1568 * requires a PRMJTime... which only has 16-bit years. Sub-ECMA.
1569 */
1570 /* Tue Oct 31 2000 09:41:40 GMT-0800 (PST) */
1571 JS_snprintf(buf, sizeof buf,
1572 "%s %s %.2d %.4d %.2d:%.2d:%.2d GMT%+.4d%s%s",
1573 days[WeekDay(local)],
1574 months[MonthFromTime(local)],
1575 DateFromTime(local),
1576 YearFromTime(local),
1577 HourFromTime(local),
1578 MinFromTime(local),
1579 SecFromTime(local),
1580 offset,
1581 usetz ? " " : "",
1582 usetz ? tzbuf : "");
1583 break;
1584 case FORMATSPEC_DATE:
1585 /* Tue Oct 31 2000 */
1586 JS_snprintf(buf, sizeof buf,
1587 "%s %s %.2d %.4d",
1588 days[WeekDay(local)],
1589 months[MonthFromTime(local)],
1590 DateFromTime(local),
1591 YearFromTime(local));
1592 break;
1593 case FORMATSPEC_TIME:
1594 /* 09:41:40 GMT-0800 (PST) */
1595 JS_snprintf(buf, sizeof buf,
1596 "%.2d:%.2d:%.2d GMT%+.4d%s%s",
1597 HourFromTime(local),
1598 MinFromTime(local),
1599 SecFromTime(local),
1600 offset,
1601 usetz ? " " : "",
1602 usetz ? tzbuf : "");
1603 break;
1604 }
1605 }
1607 str = JS_NewStringCopyZ(cx, buf);
1608 if (!str)
1609 return JS_FALSE;
1610 *rval = STRING_TO_JSVAL(str);
1611 return JS_TRUE;
1612 }
1614 static JSBool
1615 date_toLocaleHelper(JSContext *cx, JSObject *obj, uintN argc,
1616 jsval *argv, jsval *rval, char *format)
1617 {
1618 char buf[100];
1619 JSString *str;
1620 PRMJTime split;
1621 jsdouble *date = date_getProlog(cx, obj, argv);
1622 if (!date)
1623 return JS_FALSE;
1625 if (!JSDOUBLE_IS_FINITE(*date)) {
1626 JS_snprintf(buf, sizeof buf, js_NaN_date_str);
1627 } else {
1628 intN result_len;
1629 jsdouble local = LocalTime(*date);
1630 new_explode(local, &split, JS_FALSE);
1632 /* let PRMJTime format it. */
1633 result_len = PRMJ_FormatTime(buf, sizeof buf, format, &split);
1635 /* If it failed, default to toString. */
1636 if (result_len == 0)
1637 return date_format(cx, *date, FORMATSPEC_FULL, rval);
1639 /* Hacked check against undesired 2-digit year 00/00/00 form. */
1640 if (buf[result_len - 3] == '/' &&
1641 isdigit(buf[result_len - 2]) && isdigit(buf[result_len - 1])) {
1642 JS_snprintf(buf + (result_len - 2), (sizeof buf) - (result_len - 2),
1643 "%d", js_DateGetYear(cx, obj));
1644 }
1646 }
1648 str = JS_NewStringCopyZ(cx, buf);
1649 if (!str)
1650 return JS_FALSE;
1651 *rval = STRING_TO_JSVAL(str);
1652 return JS_TRUE;
1653 }
1655 static JSBool
1656 date_toLocaleString(JSContext *cx, JSObject *obj, uintN argc,
1657 jsval *argv, jsval *rval)
1658 {
1659 /* Use '%#c' for windows, because '%c' is
1660 * backward-compatible and non-y2k with msvc; '%#c' requests that a
1661 * full year be used in the result string.
1662 */
1663 return date_toLocaleHelper(cx, obj, argc, argv, rval,
1664 #if defined(_WIN32) && !defined(__MWERKS__)
1665 "%#c"
1666 #else
1667 "%c"
1668 #endif
1669 );
1670 }
1672 static JSBool
1673 date_toLocaleDateString(JSContext *cx, JSObject *obj, uintN argc,
1674 jsval *argv, jsval *rval)
1675 {
1676 /* Use '%#x' for windows, because '%x' is
1677 * backward-compatible and non-y2k with msvc; '%#x' requests that a
1678 * full year be used in the result string.
1679 */
1680 return date_toLocaleHelper(cx, obj, argc, argv, rval,
1681 #if defined(_WIN32) && !defined(__MWERKS__)
1682 "%#x"
1683 #else
1684 "%x"
1685 #endif
1686 );
1687 }
1689 static JSBool
1690 date_toLocaleTimeString(JSContext *cx, JSObject *obj, uintN argc,
1691 jsval *argv, jsval *rval)
1692 {
1693 return date_toLocaleHelper(cx, obj, argc, argv, rval, "%X");
1694 }
1696 static JSBool
1697 date_toTimeString(JSContext *cx, JSObject *obj, uintN argc,
1698 jsval *argv, jsval *rval)
1699 {
1700 jsdouble *date = date_getProlog(cx, obj, argv);
1701 if (!date)
1702 return JS_FALSE;
1703 return date_format(cx, *date, FORMATSPEC_TIME, rval);
1704 }
1706 static JSBool
1707 date_toDateString(JSContext *cx, JSObject *obj, uintN argc,
1708 jsval *argv, jsval *rval)
1709 {
1710 jsdouble *date = date_getProlog(cx, obj, argv);
1711 if (!date)
1712 return JS_FALSE;
1713 return date_format(cx, *date, FORMATSPEC_DATE, rval);
1714 }
1716 #if JS_HAS_TOSOURCE
1717 #include <string.h>
1718 #include "jsdtoa.h"
1720 static JSBool
1721 date_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1722 jsval *rval)
1723 {
1724 jsdouble *date;
1725 char buf[DTOSTR_STANDARD_BUFFER_SIZE], *numStr, *bytes;
1726 JSString *str;
1728 date = date_getProlog(cx, obj, argv);
1729 if (!date)
1730 return JS_FALSE;
1732 numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, *date);
1733 if (!numStr) {
1734 JS_ReportOutOfMemory(cx);
1735 return JS_FALSE;
1736 }
1738 bytes = JS_smprintf("(new %s(%s))", date_class.name, numStr);
1739 if (!bytes) {
1740 JS_ReportOutOfMemory(cx);
1741 return JS_FALSE;
1742 }
1744 str = JS_NewString(cx, bytes, strlen(bytes));
1745 if (!str) {
1746 free(bytes);
1747 return JS_FALSE;
1748 }
1749 *rval = STRING_TO_JSVAL(str);
1750 return JS_TRUE;
1751 }
1752 #endif
1754 static JSBool
1755 date_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1756 jsval *rval)
1757 {
1758 jsdouble *date = date_getProlog(cx, obj, argv);
1759 if (!date)
1760 return JS_FALSE;
1761 return date_format(cx, *date, FORMATSPEC_FULL, rval);
1762 }
1764 #if JS_HAS_VALUEOF_HINT
1765 static JSBool
1766 date_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1767 jsval *rval)
1768 {
1769 /* It is an error to call date_valueOf on a non-date object, but we don't
1770 * need to check for that explicitly here because every path calls
1771 * date_getProlog, which does the check.
1772 */
1774 /* If called directly with no arguments, convert to a time number. */
1775 if (argc == 0)
1776 return date_getTime(cx, obj, argc, argv, rval);
1778 /* Convert to number only if the hint was given, otherwise favor string. */
1779 if (argc == 1) {
1780 JSString *str, *str2;
1782 str = js_ValueToString(cx, argv[0]);
1783 if (!str)
1784 return JS_FALSE;
1785 str2 = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]);
1786 if (!js_CompareStrings(str, str2))
1787 return date_getTime(cx, obj, argc, argv, rval);
1788 }
1789 return date_toString(cx, obj, argc, argv, rval);
1790 }
1791 #else
1792 #define date_valueOf date_getTime
1793 #endif
1795 \f
1796 /*
1797 * creation and destruction
1798 */
1800 static JSFunctionSpec date_static_methods[] = {
1801 {"UTC", date_UTC, MAXARGS,0,0 },
1802 {"parse", date_parse, 1,0,0 },
1803 {"now", date_now, 0,0,0 },
1804 {0,0,0,0,0}
1805 };
1807 static JSFunctionSpec date_methods[] = {
1808 {"getTime", date_getTime, 0,0,0 },
1809 {"getTimezoneOffset", date_getTimezoneOffset, 0,0,0 },
1810 {"getYear", date_getYear, 0,0,0 },
1811 {"getFullYear", date_getFullYear, 0,0,0 },
1812 {"getUTCFullYear", date_getUTCFullYear, 0,0,0 },
1813 {"getMonth", date_getMonth, 0,0,0 },
1814 {"getUTCMonth", date_getUTCMonth, 0,0,0 },
1815 {"getDate", date_getDate, 0,0,0 },
1816 {"getUTCDate", date_getUTCDate, 0,0,0 },
1817 {"getDay", date_getDay, 0,0,0 },
1818 {"getUTCDay", date_getUTCDay, 0,0,0 },
1819 {"getHours", date_getHours, 0,0,0 },
1820 {"getUTCHours", date_getUTCHours, 0,0,0 },
1821 {"getMinutes", date_getMinutes, 0,0,0 },
1822 {"getUTCMinutes", date_getUTCMinutes, 0,0,0 },
1823 {"getSeconds", date_getUTCSeconds, 0,0,0 },
1824 {"getUTCSeconds", date_getUTCSeconds, 0,0,0 },
1825 {"getMilliseconds", date_getUTCMilliseconds,0,0,0 },
1826 {"getUTCMilliseconds", date_getUTCMilliseconds,0,0,0 },
1827 {"setTime", date_setTime, 1,0,0 },
1828 {"setYear", date_setYear, 1,0,0 },
1829 {"setFullYear", date_setFullYear, 3,0,0 },
1830 {"setUTCFullYear", date_setUTCFullYear, 3,0,0 },
1831 {"setMonth", date_setMonth, 2,0,0 },
1832 {"setUTCMonth", date_setUTCMonth, 2,0,0 },
1833 {"setDate", date_setDate, 1,0,0 },
1834 {"setUTCDate", date_setUTCDate, 1,0,0 },
1835 {"setHours", date_setHours, 4,0,0 },
1836 {"setUTCHours", date_setUTCHours, 4,0,0 },
1837 {"setMinutes", date_setMinutes, 3,0,0 },
1838 {"setUTCMinutes", date_setUTCMinutes, 3,0,0 },
1839 {"setSeconds", date_setSeconds, 2,0,0 },
1840 {"setUTCSeconds", date_setUTCSeconds, 2,0,0 },
1841 {"setMilliseconds", date_setMilliseconds, 1,0,0 },
1842 {"setUTCMilliseconds", date_setUTCMilliseconds,1,0,0 },
1843 {"toUTCString", date_toGMTString, 0,0,0 },
1844 {js_toLocaleString_str, date_toLocaleString, 0,0,0 },
1845 {"toLocaleDateString", date_toLocaleDateString,0,0,0 },
1846 {"toLocaleTimeString", date_toLocaleTimeString,0,0,0 },
1847 {"toDateString", date_toDateString, 0,0,0 },
1848 {"toTimeString", date_toTimeString, 0,0,0 },
1849 #if JS_HAS_TOSOURCE
1850 {js_toSource_str, date_toSource, 0,0,0 },
1851 #endif
1852 {js_toString_str, date_toString, 0,0,0 },
1853 {js_valueOf_str, date_valueOf, 0,0,0 },
1854 {0,0,0,0,0}
1855 };
1857 static jsdouble *
1858 date_constructor(JSContext *cx, JSObject* obj)
1859 {
1860 jsdouble *date;
1862 date = js_NewDouble(cx, 0.0);
1863 if (!date)
1864 return NULL;
1865 OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, DOUBLE_TO_JSVAL(date));
1866 return date;
1867 }
1869 static JSBool
1870 Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1871 {
1872 jsdouble *date;
1873 JSString *str;
1874 jsdouble d;
1876 /* Date called as function */
1877 if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
1878 int64 us, ms, us2ms;
1879 jsdouble msec_time;
1881 /* NSPR 2.0 docs say 'We do not support PRMJ_NowMS and PRMJ_NowS',
1882 * so compute ms from PRMJ_Now.
1883 */
1884 us = PRMJ_Now();
1885 JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC);
1886 JSLL_DIV(ms, us, us2ms);
1887 JSLL_L2D(msec_time, ms);
1889 return date_format(cx, msec_time, FORMATSPEC_FULL, rval);
1890 }
1892 /* Date called as constructor */
1893 if (argc == 0) {
1894 int64 us, ms, us2ms;
1895 jsdouble msec_time;
1897 date = date_constructor(cx, obj);
1898 if (!date)
1899 return JS_FALSE;
1901 us = PRMJ_Now();
1902 JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC);
1903 JSLL_DIV(ms, us, us2ms);
1904 JSLL_L2D(msec_time, ms);
1906 *date = msec_time;
1907 } else if (argc == 1) {
1908 if (!JSVAL_IS_STRING(argv[0])) {
1909 /* the argument is a millisecond number */
1910 if (!js_ValueToNumber(cx, argv[0], &d))
1911 return JS_FALSE;
1912 date = date_constructor(cx, obj);
1913 if (!date)
1914 return JS_FALSE;
1915 *date = TIMECLIP(d);
1916 } else {
1917 /* the argument is a string; parse it. */
1918 date = date_constructor(cx, obj);
1919 if (!date)
1920 return JS_FALSE;
1922 str = js_ValueToString(cx, argv[0]);
1923 if (!str)
1924 return JS_FALSE;
1926 if (!date_parseString(str, date))
1927 *date = *cx->runtime->jsNaN;
1928 *date = TIMECLIP(*date);
1929 }
1930 } else {
1931 jsdouble array[MAXARGS];
1932 uintN loop;
1933 jsdouble double_arg;
1934 jsdouble day;
1935 jsdouble msec_time;
1937 for (loop = 0; loop < MAXARGS; loop++) {
1938 if (loop < argc) {
1939 if (!js_ValueToNumber(cx, argv[loop], &double_arg))
1940 return JS_FALSE;
1941 /* if any arg is NaN, make a NaN date object
1942 and return */
1943 if (!JSDOUBLE_IS_FINITE(double_arg)) {
1944 date = date_constructor(cx, obj);
1945 if (!date)
1946 return JS_FALSE;
1947 *date = *cx->runtime->jsNaN;
1948 return JS_TRUE;
1949 }
1950 array[loop] = js_DoubleToInteger(double_arg);
1951 } else {
1952 if (loop == 2) {
1953 array[loop] = 1; /* Default the date argument to 1. */
1954 } else {
1955 array[loop] = 0;
1956 }
1957 }
1958 }
1960 date = date_constructor(cx, obj);
1961 if (!date)
1962 return JS_FALSE;
1964 /* adjust 2-digit years into the 20th century */
1965 if (array[0] >= 0 && array[0] <= 99)
1966 array[0] += 1900;
1968 day = MakeDay(array[0], array[1], array[2]);
1969 msec_time = MakeTime(array[3], array[4], array[5], array[6]);
1970 msec_time = MakeDate(day, msec_time);
1971 msec_time = UTC(msec_time);
1972 *date = TIMECLIP(msec_time);
1973 }
1974 return JS_TRUE;
1975 }
1977 JSObject *
1978 js_InitDateClass(JSContext *cx, JSObject *obj)
1979 {
1980 JSObject *proto;
1981 jsdouble *proto_date;
1983 /* set static LocalTZA */
1984 LocalTZA = -(PRMJ_LocalGMTDifference() * msPerSecond);
1985 proto = JS_InitClass(cx, obj, NULL, &date_class, Date, MAXARGS,
1986 NULL, date_methods, NULL, date_static_methods);
1987 if (!proto)
1988 return NULL;
1990 /* Alias toUTCString with toGMTString. (ECMA B.2.6) */
1991 if (!JS_AliasProperty(cx, proto, "toUTCString", "toGMTString"))
1992 return NULL;
1994 /* Set the value of the Date.prototype date to NaN */
1995 proto_date = date_constructor(cx, proto);
1996 if (!proto_date)
1997 return NULL;
1998 *proto_date = *cx->runtime->jsNaN;
2000 return proto;
2001 }
2003 JS_FRIEND_API(JSObject *)
2004 js_NewDateObjectMsec(JSContext *cx, jsdouble msec_time)
2005 {
2006 JSObject *obj;
2007 jsdouble *date;
2009 obj = js_NewObject(cx, &date_class, NULL, NULL);
2010 if (!obj)
2011 return NULL;
2013 date = date_constructor(cx, obj);
2014 if (!date)
2015 return NULL;
2017 *date = msec_time;
2018 return obj;
2019 }
2021 JS_FRIEND_API(JSObject *)
2022 js_NewDateObject(JSContext* cx, int year, int mon, int mday,
2023 int hour, int min, int sec)
2024 {
2025 JSObject *obj;
2026 jsdouble msec_time;
2028 msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
2029 obj = js_NewDateObjectMsec(cx, UTC(msec_time));
2030 return obj;
2031 }
2033 JS_FRIEND_API(JSBool)
2034 js_DateIsValid(JSContext *cx, JSObject* obj)
2035 {
2036 jsdouble *date = date_getProlog(cx, obj, NULL);
2038 if (!date || JSDOUBLE_IS_NaN(*date))
2039 return JS_FALSE;
2040 else
2041 return JS_TRUE;
2042 }
2044 JS_FRIEND_API(int)
2045 js_DateGetYear(JSContext *cx, JSObject* obj)
2046 {
2047 jsdouble *date = date_getProlog(cx, obj, NULL);
2049 /* Preserve legacy API behavior of returning 0 for invalid dates. */
2050 if (!date || JSDOUBLE_IS_NaN(*date))
2051 return 0;
2052 return (int) YearFromTime(LocalTime(*date));
2053 }
2055 JS_FRIEND_API(int)
2056 js_DateGetMonth(JSContext *cx, JSObject* obj)
2057 {
2058 jsdouble *date = date_getProlog(cx, obj, NULL);
2060 if (!date || JSDOUBLE_IS_NaN(*date))
2061 return 0;
2062 return (int) MonthFromTime(LocalTime(*date));
2063 }
2065 JS_FRIEND_API(int)
2066 js_DateGetDate(JSContext *cx, JSObject* obj)
2067 {
2068 jsdouble *date = date_getProlog(cx, obj, NULL);
2070 if (!date || JSDOUBLE_IS_NaN(*date))
2071 return 0;
2072 return (int) DateFromTime(LocalTime(*date));
2073 }
2075 JS_FRIEND_API(int)
2076 js_DateGetHours(JSContext *cx, JSObject* obj)
2077 {
2078 jsdouble *date = date_getProlog(cx, obj, NULL);
2080 if (!date || JSDOUBLE_IS_NaN(*date))
2081 return 0;
2082 return (int) HourFromTime(LocalTime(*date));
2083 }
2085 JS_FRIEND_API(int)
2086 js_DateGetMinutes(JSContext *cx, JSObject* obj)
2087 {
2088 jsdouble *date = date_getProlog(cx, obj, NULL);
2090 if (!date || JSDOUBLE_IS_NaN(*date))
2091 return 0;
2092 return (int) MinFromTime(LocalTime(*date));
2093 }
2095 JS_FRIEND_API(int)
2096 js_DateGetSeconds(JSContext *cx, JSObject* obj)
2097 {
2098 jsdouble *date = date_getProlog(cx, obj, NULL);
2100 if (!date || JSDOUBLE_IS_NaN(*date))
2101 return 0;
2102 return (int) SecFromTime(*date);
2103 }
2105 JS_FRIEND_API(void)
2106 js_DateSetYear(JSContext *cx, JSObject *obj, int year)
2107 {
2108 jsdouble local;
2109 jsdouble *date = date_getProlog(cx, obj, NULL);
2110 if (!date)
2111 return;
2112 local = LocalTime(*date);
2113 /* reset date if it was NaN */
2114 if (JSDOUBLE_IS_NaN(local))
2115 local = 0;
2116 local = date_msecFromDate(year,
2117 MonthFromTime(local),
2118 DateFromTime(local),
2119 HourFromTime(local),
2120 MinFromTime(local),
2121 SecFromTime(local),
2122 msFromTime(local));
2123 *date = UTC(local);
2124 }
2126 JS_FRIEND_API(void)
2127 js_DateSetMonth(JSContext *cx, JSObject *obj, int month)
2128 {
2129 jsdouble local;
2130 jsdouble *date = date_getProlog(cx, obj, NULL);
2131 if (!date)
2132 return;
2133 local = LocalTime(*date);
2134 /* bail if date was NaN */
2135 if (JSDOUBLE_IS_NaN(local))
2136 return;
2137 local = date_msecFromDate(YearFromTime(local),
2138 month,
2139 DateFromTime(local),
2140 HourFromTime(local),
2141 MinFromTime(local),
2142 SecFromTime(local),
2143 msFromTime(local));
2144 *date = UTC(local);
2145 }
2147 JS_FRIEND_API(void)
2148 js_DateSetDate(JSContext *cx, JSObject *obj, int date)
2149 {
2150 jsdouble local;
2151 jsdouble *datep = date_getProlog(cx, obj, NULL);
2152 if (!datep)
2153 return;
2154 local = LocalTime(*datep);
2155 if (JSDOUBLE_IS_NaN(local))
2156 return;
2157 local = date_msecFromDate(YearFromTime(local),
2158 MonthFromTime(local),
2159 date,
2160 HourFromTime(local),
2161 MinFromTime(local),
2162 SecFromTime(local),
2163 msFromTime(local));
2164 *datep = UTC(local);
2165 }
2167 JS_FRIEND_API(void)
2168 js_DateSetHours(JSContext *cx, JSObject *obj, int hours)
2169 {
2170 jsdouble local;
2171 jsdouble *date = date_getProlog(cx, obj, NULL);
2172 if (!date)
2173 return;
2174 local = LocalTime(*date);
2175 if (JSDOUBLE_IS_NaN(local))
2176 return;
2177 local = date_msecFromDate(YearFromTime(local),
2178 MonthFromTime(local),
2179 DateFromTime(local),
2180 hours,
2181 MinFromTime(local),
2182 SecFromTime(local),
2183 msFromTime(local));
2184 *date = UTC(local);
2185 }
2187 JS_FRIEND_API(void)
2188 js_DateSetMinutes(JSContext *cx, JSObject *obj, int minutes)
2189 {
2190 jsdouble local;
2191 jsdouble *date = date_getProlog(cx, obj, NULL);
2192 if (!date)
2193 return;
2194 local = LocalTime(*date);
2195 if (JSDOUBLE_IS_NaN(local))
2196 return;
2197 local = date_msecFromDate(YearFromTime(local),
2198 MonthFromTime(local),
2199 DateFromTime(local),
2200 HourFromTime(local),
2201 minutes,
2202 SecFromTime(local),
2203 msFromTime(local));
2204 *date = UTC(local);
2205 }
2207 JS_FRIEND_API(void)
2208 js_DateSetSeconds(JSContext *cx, JSObject *obj, int seconds)
2209 {
2210 jsdouble local;
2211 jsdouble *date = date_getProlog(cx, obj, NULL);
2212 if (!date)
2213 return;
2214 local = LocalTime(*date);
2215 if (JSDOUBLE_IS_NaN(local))
2216 return;
2217 local = date_msecFromDate(YearFromTime(local),
2218 MonthFromTime(local),
2219 DateFromTime(local),
2220 HourFromTime(local),
2221 MinFromTime(local),
2222 seconds,
2223 msFromTime(local));
2224 *date = UTC(local);
2225 }
2227 JS_FRIEND_API(jsdouble)
2228 js_DateGetMsecSinceEpoch(JSContext *cx, JSObject *obj)
2229 {
2230 jsdouble *date = date_getProlog(cx, obj, NULL);
2231 if (!date || JSDOUBLE_IS_NaN(*date))
2232 return 0;
2233 return (*date);
2234 }