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 /* LocalTZA gets set by js_InitDateClass() */
315 static jsdouble LocalTZA;
317 static jsdouble
318 DaylightSavingTA(jsdouble t)
319 {
320 volatile int64 PR_t;
321 int64 ms2us;
322 int64 offset;
323 jsdouble result;
325 /* abort if NaN */
326 if (JSDOUBLE_IS_NaN(t))
327 return t;
329 /* put our t in an LL, and map it to usec for prtime */
330 JSLL_D2L(PR_t, t);
331 JSLL_I2L(ms2us, PRMJ_USEC_PER_MSEC);
332 JSLL_MUL(PR_t, PR_t, ms2us);
334 offset = PRMJ_DSTOffset(PR_t);
336 JSLL_DIV(offset, offset, ms2us);
337 JSLL_L2D(result, offset);
338 return result;
339 }
342 #define AdjustTime(t) fmod(LocalTZA + DaylightSavingTA(t), msPerDay)
344 #define LocalTime(t) ((t) + AdjustTime(t))
346 static jsdouble
347 UTC(jsdouble t)
348 {
349 return t - AdjustTime(t - LocalTZA);
350 }
352 static intN
353 HourFromTime(jsdouble t)
354 {
355 intN result = (intN) fmod(floor(t/msPerHour), HoursPerDay);
356 if (result < 0)
357 result += (intN)HoursPerDay;
358 return result;
359 }
361 static intN
362 MinFromTime(jsdouble t)
363 {
364 intN result = (intN) fmod(floor(t / msPerMinute), MinutesPerHour);
365 if (result < 0)
366 result += (intN)MinutesPerHour;
367 return result;
368 }
370 static intN
371 SecFromTime(jsdouble t)
372 {
373 intN result = (intN) fmod(floor(t / msPerSecond), SecondsPerMinute);
374 if (result < 0)
375 result += (intN)SecondsPerMinute;
376 return result;
377 }
379 static intN
380 msFromTime(jsdouble t)
381 {
382 intN result = (intN) fmod(t, msPerSecond);
383 if (result < 0)
384 result += (intN)msPerSecond;
385 return result;
386 }
388 #define MakeTime(hour, min, sec, ms) \
389 (((hour * MinutesPerHour + min) * SecondsPerMinute + sec) * msPerSecond + ms)
391 static jsdouble
392 MakeDay(jsdouble year, jsdouble month, jsdouble date)
393 {
394 jsdouble result;
395 JSBool leap;
396 jsdouble yearday;
397 jsdouble monthday;
399 year += floor(month / 12);
401 month = fmod(month, 12.0);
402 if (month < 0)
403 month += 12;
405 leap = (DaysInYear((jsint) year) == 366);
407 yearday = floor(TimeFromYear(year) / msPerDay);
408 monthday = DayFromMonth(month, leap);
410 result = yearday
411 + monthday
412 + date - 1;
413 return result;
414 }
416 #define MakeDate(day, time) (day * msPerDay + time)
418 #define TIMECLIP(d) ((JSDOUBLE_IS_FINITE(d) \
419 && !((d < 0 ? -d : d) > HalfTimeDomain)) \
420 ? js_DoubleToInteger(d + (+0.)) : *cx->runtime->jsNaN)
422 /**
423 * end of ECMA 'support' functions
424 */
425 \f
426 /*
427 * Other Support routines and definitions
428 */
430 static JSClass date_class = {
431 js_Date_str,
432 JSCLASS_HAS_PRIVATE,
433 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
434 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
435 JSCLASS_NO_OPTIONAL_MEMBERS
436 };
438 /* for use by date_parse */
440 static const char* wtb[] = {
441 "am", "pm",
442 "monday", "tuesday", "wednesday", "thursday", "friday",
443 "saturday", "sunday",
444 "january", "february", "march", "april", "may", "june",
445 "july", "august", "september", "october", "november", "december",
446 "gmt", "ut", "utc",
447 "est", "edt",
448 "cst", "cdt",
449 "mst", "mdt",
450 "pst", "pdt"
451 /* time zone table needs to be expanded */
452 };
454 static int ttb[] = {
455 -1, -2, 0, 0, 0, 0, 0, 0, 0, /* AM/PM */
456 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
457 10000 + 0, 10000 + 0, 10000 + 0, /* GMT/UT/UTC */
458 10000 + 5 * 60, 10000 + 4 * 60, /* EST/EDT */
459 10000 + 6 * 60, 10000 + 5 * 60, /* CST/CDT */
460 10000 + 7 * 60, 10000 + 6 * 60, /* MST/MDT */
461 10000 + 8 * 60, 10000 + 7 * 60 /* PST/PDT */
462 };
464 /* helper for date_parse */
465 static JSBool
466 date_regionMatches(const char* s1, int s1off, const jschar* s2, int s2off,
467 int count, int ignoreCase)
468 {
469 JSBool result = JS_FALSE;
470 /* return true if matches, otherwise, false */
472 while (count > 0 && s1[s1off] && s2[s2off]) {
473 if (ignoreCase) {
474 if (JS_TOLOWER((jschar)s1[s1off]) != JS_TOLOWER(s2[s2off])) {
475 break;
476 }
477 } else {
478 if ((jschar)s1[s1off] != s2[s2off]) {
479 break;
480 }
481 }
482 s1off++;
483 s2off++;
484 count--;
485 }
487 if (count == 0) {
488 result = JS_TRUE;
489 }
491 return result;
492 }
494 /* find UTC time from given date... no 1900 correction! */
495 static jsdouble
496 date_msecFromDate(jsdouble year, jsdouble mon, jsdouble mday, jsdouble hour,
497 jsdouble min, jsdouble sec, jsdouble msec)
498 {
499 jsdouble day;
500 jsdouble msec_time;
501 jsdouble result;
503 day = MakeDay(year, mon, mday);
504 msec_time = MakeTime(hour, min, sec, msec);
505 result = MakeDate(day, msec_time);
506 return result;
507 }
508 \f
509 /*
510 * See ECMA 15.9.4.[3-10];
511 */
512 /* XXX this function must be above date_parseString to avoid a
513 horrid bug in the Win16 1.52 compiler */
514 #define MAXARGS 7
515 static JSBool
516 date_UTC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
517 {
518 jsdouble array[MAXARGS];
519 uintN loop;
520 jsdouble d;
522 for (loop = 0; loop < MAXARGS; loop++) {
523 if (loop < argc) {
524 if (!js_ValueToNumber(cx, argv[loop], &d))
525 return JS_FALSE;
526 /* return NaN if any arg is NaN */
527 if (!JSDOUBLE_IS_FINITE(d)) {
528 return js_NewNumberValue(cx, d, rval);
529 }
530 array[loop] = floor(d);
531 } else {
532 array[loop] = 0;
533 }
534 }
536 /* adjust 2-digit years into the 20th century */
537 if (array[0] >= 0 && array[0] <= 99)
538 array[0] += 1900;
540 /* if we got a 0 for 'date' (which is out of range)
541 * pretend it's a 1. (So Date.UTC(1972, 5) works) */
542 if (array[2] < 1)
543 array[2] = 1;
545 d = date_msecFromDate(array[0], array[1], array[2],
546 array[3], array[4], array[5], array[6]);
547 d = TIMECLIP(d);
549 return js_NewNumberValue(cx, d, rval);
550 }
552 static JSBool
553 date_parseString(JSString *str, jsdouble *result)
554 {
555 jsdouble msec;
557 const jschar *s = JSSTRING_CHARS(str);
558 size_t limit = JSSTRING_LENGTH(str);
559 size_t i = 0;
560 int year = -1;
561 int mon = -1;
562 int mday = -1;
563 int hour = -1;
564 int min = -1;
565 int sec = -1;
566 int c = -1;
567 int n = -1;
568 jsdouble tzoffset = -1; /* was an int, overflowed on win16!!! */
569 int prevc = 0;
570 JSBool seenplusminus = JS_FALSE;
572 if (limit == 0)
573 goto syntax;
574 while (i < limit) {
575 c = s[i];
576 i++;
577 if (c <= ' ' || c == ',' || c == '-') {
578 if (c == '-' && '0' <= s[i] && s[i] <= '9') {
579 prevc = c;
580 }
581 continue;
582 }
583 if (c == '(') { /* comments) */
584 int depth = 1;
585 while (i < limit) {
586 c = s[i];
587 i++;
588 if (c == '(') depth++;
589 else if (c == ')')
590 if (--depth <= 0)
591 break;
592 }
593 continue;
594 }
595 if ('0' <= c && c <= '9') {
596 n = c - '0';
597 while (i < limit && '0' <= (c = s[i]) && c <= '9') {
598 n = n * 10 + c - '0';
599 i++;
600 }
602 /* allow TZA before the year, so
603 * 'Wed Nov 05 21:49:11 GMT-0800 1997'
604 * works */
606 /* uses of seenplusminus allow : in TZA, so Java
607 * no-timezone style of GMT+4:30 works
608 */
610 if ((prevc == '+' || prevc == '-')/* && year>=0 */) {
611 /* make ':' case below change tzoffset */
612 seenplusminus = JS_TRUE;
614 /* offset */
615 if (n < 24)
616 n = n * 60; /* EG. "GMT-3" */
617 else
618 n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */
619 if (prevc == '+') /* plus means east of GMT */
620 n = -n;
621 if (tzoffset != 0 && tzoffset != -1)
622 goto syntax;
623 tzoffset = n;
624 } else if (n >= 70 ||
625 (prevc == '/' && mon >= 0 && mday >= 0 && year < 0)) {
626 if (year >= 0)
627 goto syntax;
628 else if (c <= ' ' || c == ',' || c == '/' || i >= limit)
629 year = n < 100 ? n + 1900 : n;
630 else
631 goto syntax;
632 } else if (c == ':') {
633 if (hour < 0)
634 hour = /*byte*/ n;
635 else if (min < 0)
636 min = /*byte*/ n;
637 else
638 goto syntax;
639 } else if (c == '/') {
640 if (mon < 0)
641 mon = /*byte*/ n-1;
642 else if (mday < 0)
643 mday = /*byte*/ n;
644 else
645 goto syntax;
646 } else if (i < limit && c != ',' && c > ' ' && c != '-') {
647 goto syntax;
648 } else if (seenplusminus && n < 60) { /* handle GMT-3:30 */
649 if (tzoffset < 0)
650 tzoffset -= n;
651 else
652 tzoffset += n;
653 } else if (hour >= 0 && min < 0) {
654 min = /*byte*/ n;
655 } else if (min >= 0 && sec < 0) {
656 sec = /*byte*/ n;
657 } else if (mday < 0) {
658 mday = /*byte*/ n;
659 } else {
660 goto syntax;
661 }
662 prevc = 0;
663 } else if (c == '/' || c == ':' || c == '+' || c == '-') {
664 prevc = c;
665 } else {
666 size_t st = i - 1;
667 int k;
668 while (i < limit) {
669 c = s[i];
670 if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')))
671 break;
672 i++;
673 }
674 if (i <= st + 1)
675 goto syntax;
676 for (k = (sizeof(wtb)/sizeof(char*)); --k >= 0;)
677 if (date_regionMatches(wtb[k], 0, s, st, i-st, 1)) {
678 int action = ttb[k];
679 if (action != 0) {
680 if (action < 0) {
681 /*
682 * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as
683 * 12:30, instead of blindly adding 12 if PM.
684 */
685 JS_ASSERT(action == -1 || action == -2);
686 if (hour > 12 || hour < 0) {
687 goto syntax;
688 } else {
689 if (action == -1 && hour == 12) { /* am */
690 hour = 0;
691 } else if (action == -2 && hour != 12) { /* pm */
692 hour += 12;
693 }
694 }
695 } else if (action <= 13) { /* month! */
696 if (mon < 0) {
697 mon = /*byte*/ (action - 2);
698 } else {
699 goto syntax;
700 }
701 } else {
702 tzoffset = action - 10000;
703 }
704 }
705 break;
706 }
707 if (k < 0)
708 goto syntax;
709 prevc = 0;
710 }
711 }
712 if (year < 0 || mon < 0 || mday < 0)
713 goto syntax;
714 if (sec < 0)
715 sec = 0;
716 if (min < 0)
717 min = 0;
718 if (hour < 0)
719 hour = 0;
720 if (tzoffset == -1) { /* no time zone specified, have to use local */
721 jsdouble msec_time;
722 msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
724 *result = UTC(msec_time);
725 return JS_TRUE;
726 }
728 msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
729 msec += tzoffset * msPerMinute;
730 *result = msec;
731 return JS_TRUE;
733 syntax:
734 /* syntax error */
735 *result = 0;
736 return JS_FALSE;
737 }
739 static JSBool
740 date_parse(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
741 {
742 JSString *str;
743 jsdouble result;
745 str = js_ValueToString(cx, argv[0]);
746 if (!str)
747 return JS_FALSE;
748 if (!date_parseString(str, &result)) {
749 *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
750 return JS_TRUE;
751 }
753 result = TIMECLIP(result);
754 return js_NewNumberValue(cx, result, rval);
755 }
757 static JSBool
758 date_now(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
759 {
760 int64 us, ms, us2ms;
761 jsdouble msec_time;
763 us = PRMJ_Now();
764 JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC);
765 JSLL_DIV(ms, us, us2ms);
766 JSLL_L2D(msec_time, ms);
768 return js_NewDoubleValue(cx, msec_time, rval);
769 }
771 /*
772 * Check that obj is an object of class Date, and get the date value.
773 * Return NULL on failure.
774 */
775 static jsdouble *
776 date_getProlog(JSContext *cx, JSObject *obj, jsval *argv)
777 {
778 if (!JS_InstanceOf(cx, obj, &date_class, argv))
779 return NULL;
780 return JSVAL_TO_DOUBLE(OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE));
781 }
783 /*
784 * See ECMA 15.9.5.4 thru 15.9.5.23
785 */
786 static JSBool
787 date_getTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
788 {
789 jsdouble *date = date_getProlog(cx, obj, argv);
790 if (!date)
791 return JS_FALSE;
793 return js_NewNumberValue(cx, *date, rval);
794 }
796 static JSBool
797 date_getYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
798 {
799 jsdouble result;
800 jsdouble *date = date_getProlog(cx, obj, argv);
801 if (!date)
802 return JS_FALSE;
803 result = *date;
805 if (!JSDOUBLE_IS_FINITE(result))
806 return js_NewNumberValue(cx, result, rval);
808 result = YearFromTime(LocalTime(result));
810 /*
811 * During the great date rewrite of 1.3, we tried to track the evolving ECMA
812 * standard, which then had a definition of getYear which always subtracted
813 * 1900. Which we implemented, not realizing that it was incompatible with
814 * the old behavior... now, rather than thrash the behavior yet again,
815 * we've decided to leave it with the - 1900 behavior and point people to
816 * the getFullYear method. But we try to protect existing scripts that
817 * have specified a version...
818 */
819 if (cx->version == JSVERSION_1_0 ||
820 cx->version == JSVERSION_1_1 ||
821 cx->version == JSVERSION_1_2)
822 {
823 if (result >= 1900 && result < 2000)
824 result -= 1900;
825 } else {
826 result -= 1900;
827 }
828 return js_NewNumberValue(cx, result, rval);
829 }
831 static JSBool
832 date_getFullYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
833 jsval *rval)
834 {
835 jsdouble result;
836 jsdouble *date = date_getProlog(cx, obj, argv);
837 if (!date)
838 return JS_FALSE;
839 result = *date;
841 if (!JSDOUBLE_IS_FINITE(result))
842 return js_NewNumberValue(cx, result, rval);
844 result = YearFromTime(LocalTime(result));
845 return js_NewNumberValue(cx, result, rval);
846 }
848 static JSBool
849 date_getUTCFullYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
850 jsval *rval)
851 {
852 jsdouble result;
853 jsdouble *date = date_getProlog(cx, obj, argv);
854 if (!date)
855 return JS_FALSE;
856 result = *date;
858 if (!JSDOUBLE_IS_FINITE(result))
859 return js_NewNumberValue(cx, result, rval);
861 result = YearFromTime(result);
862 return js_NewNumberValue(cx, result, rval);
863 }
865 static JSBool
866 date_getMonth(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
867 jsval *rval)
868 {
869 jsdouble result;
870 jsdouble *date = date_getProlog(cx, obj, argv);
871 if (!date)
872 return JS_FALSE;
873 result = *date;
875 if (!JSDOUBLE_IS_FINITE(result))
876 return js_NewNumberValue(cx, result, rval);
878 result = MonthFromTime(LocalTime(result));
879 return js_NewNumberValue(cx, result, rval);
880 }
882 static JSBool
883 date_getUTCMonth(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
884 jsval *rval)
885 {
886 jsdouble result;
887 jsdouble *date = date_getProlog(cx, obj, argv);
888 if (!date)
889 return JS_FALSE;
890 result = *date;
892 if (!JSDOUBLE_IS_FINITE(result))
893 return js_NewNumberValue(cx, result, rval);
895 result = MonthFromTime(result);
896 return js_NewNumberValue(cx, result, rval);
897 }
899 static JSBool
900 date_getDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
901 {
902 jsdouble result;
903 jsdouble *date = date_getProlog(cx, obj, argv);
904 if (!date)
905 return JS_FALSE;
906 result = *date;
908 if (!JSDOUBLE_IS_FINITE(result))
909 return js_NewNumberValue(cx, result, rval);
911 result = LocalTime(result);
912 result = DateFromTime(result);
913 return js_NewNumberValue(cx, result, rval);
914 }
916 static JSBool
917 date_getUTCDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
918 jsval *rval)
919 {
920 jsdouble result;
921 jsdouble *date = date_getProlog(cx, obj, argv);
922 if (!date)
923 return JS_FALSE;
924 result = *date;
926 if (!JSDOUBLE_IS_FINITE(result))
927 return js_NewNumberValue(cx, result, rval);
929 result = DateFromTime(result);
930 return js_NewNumberValue(cx, result, rval);
931 }
933 static JSBool
934 date_getDay(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
935 {
936 jsdouble result;
937 jsdouble *date = date_getProlog(cx, obj, argv);
938 if (!date)
939 return JS_FALSE;
940 result = *date;
942 if (!JSDOUBLE_IS_FINITE(result))
943 return js_NewNumberValue(cx, result, rval);
945 result = LocalTime(result);
946 result = WeekDay(result);
947 return js_NewNumberValue(cx, result, rval);
948 }
950 static JSBool
951 date_getUTCDay(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
952 jsval *rval)
953 {
954 jsdouble result;
955 jsdouble *date = date_getProlog(cx, obj, argv);
956 if (!date)
957 return JS_FALSE;
958 result = *date;
960 if (!JSDOUBLE_IS_FINITE(result))
961 return js_NewNumberValue(cx, result, rval);
963 result = WeekDay(result);
964 return js_NewNumberValue(cx, result, rval);
965 }
967 static JSBool
968 date_getHours(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
969 jsval *rval)
970 {
971 jsdouble result;
972 jsdouble *date = date_getProlog(cx, obj, argv);
973 if (!date)
974 return JS_FALSE;
975 result = *date;
977 if (!JSDOUBLE_IS_FINITE(result))
978 return js_NewNumberValue(cx, result, rval);
980 result = HourFromTime(LocalTime(result));
981 return js_NewNumberValue(cx, result, rval);
982 }
984 static JSBool
985 date_getUTCHours(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
986 jsval *rval)
987 {
988 jsdouble result;
989 jsdouble *date = date_getProlog(cx, obj, argv);
990 if (!date)
991 return JS_FALSE;
992 result = *date;
994 if (!JSDOUBLE_IS_FINITE(result))
995 return js_NewNumberValue(cx, result, rval);
997 result = HourFromTime(result);
998 return js_NewNumberValue(cx, result, rval);
999 }
1001 static JSBool
1002 date_getMinutes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1003 jsval *rval)
1004 {
1005 jsdouble result;
1006 jsdouble *date = date_getProlog(cx, obj, argv);
1007 if (!date)
1008 return JS_FALSE;
1009 result = *date;
1011 if (!JSDOUBLE_IS_FINITE(result))
1012 return js_NewNumberValue(cx, result, rval);
1014 result = MinFromTime(LocalTime(result));
1015 return js_NewNumberValue(cx, result, rval);
1016 }
1018 static JSBool
1019 date_getUTCMinutes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1020 jsval *rval)
1021 {
1022 jsdouble result;
1023 jsdouble *date = date_getProlog(cx, obj, argv);
1024 if (!date)
1025 return JS_FALSE;
1026 result = *date;
1028 if (!JSDOUBLE_IS_FINITE(result))
1029 return js_NewNumberValue(cx, result, rval);
1031 result = MinFromTime(result);
1032 return js_NewNumberValue(cx, result, rval);
1033 }
1035 /* Date.getSeconds is mapped to getUTCSeconds */
1037 static JSBool
1038 date_getUTCSeconds(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1039 jsval *rval)
1040 {
1041 jsdouble result;
1042 jsdouble *date = date_getProlog(cx, obj, argv);
1043 if (!date)
1044 return JS_FALSE;
1045 result = *date;
1047 if (!JSDOUBLE_IS_FINITE(result))
1048 return js_NewNumberValue(cx, result, rval);
1050 result = SecFromTime(result);
1051 return js_NewNumberValue(cx, result, rval);
1052 }
1054 /* Date.getMilliseconds is mapped to getUTCMilliseconds */
1056 static JSBool
1057 date_getUTCMilliseconds(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1058 jsval *rval)
1059 {
1060 jsdouble result;
1061 jsdouble *date = date_getProlog(cx, obj, argv);
1062 if (!date)
1063 return JS_FALSE;
1064 result = *date;
1066 if (!JSDOUBLE_IS_FINITE(result))
1067 return js_NewNumberValue(cx, result, rval);
1069 result = msFromTime(result);
1070 return js_NewNumberValue(cx, result, rval);
1071 }
1073 static JSBool
1074 date_getTimezoneOffset(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1075 jsval *rval)
1076 {
1077 jsdouble result;
1078 jsdouble *date = date_getProlog(cx, obj, argv);
1079 if (!date)
1080 return JS_FALSE;
1081 result = *date;
1083 /*
1084 * Return the time zone offset in minutes for the current locale
1085 * that is appropriate for this time. This value would be a
1086 * constant except for daylight savings time.
1087 */
1088 result = (result - LocalTime(result)) / msPerMinute;
1089 return js_NewNumberValue(cx, result, rval);
1090 }
1092 static JSBool
1093 date_setTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1094 {
1095 jsdouble result;
1096 jsdouble *date = date_getProlog(cx, obj, argv);
1097 if (!date)
1098 return JS_FALSE;
1100 if (!js_ValueToNumber(cx, argv[0], &result))
1101 return JS_FALSE;
1103 result = TIMECLIP(result);
1105 *date = result;
1106 return js_NewNumberValue(cx, result, rval);
1107 }
1109 static JSBool
1110 date_makeTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1111 uintN maxargs, JSBool local, jsval *rval)
1112 {
1113 uintN i;
1114 jsdouble args[4], *argp, *stop;
1115 jsdouble hour, min, sec, msec;
1116 jsdouble lorutime; /* Local or UTC version of *date */
1118 jsdouble msec_time;
1119 jsdouble result;
1121 jsdouble *date = date_getProlog(cx, obj, argv);
1122 if (!date)
1123 return JS_FALSE;
1125 result = *date;
1127 /* just return NaN if the date is already NaN */
1128 if (!JSDOUBLE_IS_FINITE(result))
1129 return js_NewNumberValue(cx, result, rval);
1131 /* Satisfy the ECMA rule that if a function is called with
1132 * fewer arguments than the specified formal arguments, the
1133 * remaining arguments are set to undefined. Seems like all
1134 * the Date.setWhatever functions in ECMA are only varargs
1135 * beyond the first argument; this should be set to undefined
1136 * if it's not given. This means that "d = new Date();
1137 * d.setMilliseconds()" returns NaN. Blech.
1138 */
1139 if (argc == 0)
1140 argc = 1; /* should be safe, because length of all setters is 1 */
1141 else if (argc > maxargs)
1142 argc = maxargs; /* clamp argc */
1144 for (i = 0; i < argc; i++) {
1145 if (!js_ValueToNumber(cx, argv[i], &args[i]))
1146 return JS_FALSE;
1147 if (!JSDOUBLE_IS_FINITE(args[i])) {
1148 *date = *cx->runtime->jsNaN;
1149 return js_NewNumberValue(cx, *date, rval);
1150 }
1151 args[i] = js_DoubleToInteger(args[i]);
1152 }
1154 if (local)
1155 lorutime = LocalTime(result);
1156 else
1157 lorutime = result;
1159 argp = args;
1160 stop = argp + argc;
1161 if (maxargs >= 4 && argp < stop)
1162 hour = *argp++;
1163 else
1164 hour = HourFromTime(lorutime);
1166 if (maxargs >= 3 && argp < stop)
1167 min = *argp++;
1168 else
1169 min = MinFromTime(lorutime);
1171 if (maxargs >= 2 && argp < stop)
1172 sec = *argp++;
1173 else
1174 sec = SecFromTime(lorutime);
1176 if (maxargs >= 1 && argp < stop)
1177 msec = *argp;
1178 else
1179 msec = msFromTime(lorutime);
1181 msec_time = MakeTime(hour, min, sec, msec);
1182 result = MakeDate(Day(lorutime), msec_time);
1184 /* fprintf(stderr, "%f\n", result); */
1186 if (local)
1187 result = UTC(result);
1189 /* fprintf(stderr, "%f\n", result); */
1191 *date = TIMECLIP(result);
1192 return js_NewNumberValue(cx, *date, rval);
1193 }
1195 static JSBool
1196 date_setMilliseconds(JSContext *cx, JSObject *obj, uintN argc,
1197 jsval *argv, jsval *rval)
1198 {
1199 return date_makeTime(cx, obj, argc, argv, 1, JS_TRUE, rval);
1200 }
1202 static JSBool
1203 date_setUTCMilliseconds(JSContext *cx, JSObject *obj, uintN argc,
1204 jsval *argv, jsval *rval)
1205 {
1206 return date_makeTime(cx, obj, argc, argv, 1, JS_FALSE, rval);
1207 }
1209 static JSBool
1210 date_setSeconds(JSContext *cx, JSObject *obj, uintN argc,
1211 jsval *argv, jsval *rval)
1212 {
1213 return date_makeTime(cx, obj, argc, argv, 2, JS_TRUE, rval);
1214 }
1216 static JSBool
1217 date_setUTCSeconds(JSContext *cx, JSObject *obj, uintN argc,
1218 jsval *argv, jsval *rval)
1219 {
1220 return date_makeTime(cx, obj, argc, argv, 2, JS_FALSE, rval);
1221 }
1223 static JSBool
1224 date_setMinutes(JSContext *cx, JSObject *obj, uintN argc,
1225 jsval *argv, jsval *rval)
1226 {
1227 return date_makeTime(cx, obj, argc, argv, 3, JS_TRUE, rval);
1228 }
1230 static JSBool
1231 date_setUTCMinutes(JSContext *cx, JSObject *obj, uintN argc,
1232 jsval *argv, jsval *rval)
1233 {
1234 return date_makeTime(cx, obj, argc, argv, 3, JS_FALSE, rval);
1235 }
1237 static JSBool
1238 date_setHours(JSContext *cx, JSObject *obj, uintN argc,
1239 jsval *argv, jsval *rval)
1240 {
1241 return date_makeTime(cx, obj, argc, argv, 4, JS_TRUE, rval);
1242 }
1244 static JSBool
1245 date_setUTCHours(JSContext *cx, JSObject *obj, uintN argc,
1246 jsval *argv, jsval *rval)
1247 {
1248 return date_makeTime(cx, obj, argc, argv, 4, JS_FALSE, rval);
1249 }
1251 static JSBool
1252 date_makeDate(JSContext *cx, JSObject *obj, uintN argc,
1253 jsval *argv, uintN maxargs, JSBool local, jsval *rval)
1254 {
1255 uintN i;
1256 jsdouble lorutime; /* local or UTC version of *date */
1257 jsdouble args[3], *argp, *stop;
1258 jsdouble year, month, day;
1259 jsdouble result;
1261 jsdouble *date = date_getProlog(cx, obj, argv);
1262 if (!date)
1263 return JS_FALSE;
1265 result = *date;
1267 /* see complaint about ECMA in date_MakeTime */
1268 if (argc == 0)
1269 argc = 1; /* should be safe, because length of all setters is 1 */
1270 else if (argc > maxargs)
1271 argc = maxargs; /* clamp argc */
1273 for (i = 0; i < argc; i++) {
1274 if (!js_ValueToNumber(cx, argv[i], &args[i]))
1275 return JS_FALSE;
1276 if (!JSDOUBLE_IS_FINITE(args[i])) {
1277 *date = *cx->runtime->jsNaN;
1278 return js_NewNumberValue(cx, *date, rval);
1279 }
1280 args[i] = js_DoubleToInteger(args[i]);
1281 }
1283 /* return NaN if date is NaN and we're not setting the year,
1284 * If we are, use 0 as the time. */
1285 if (!(JSDOUBLE_IS_FINITE(result))) {
1286 if (argc < 3)
1287 return js_NewNumberValue(cx, result, rval);
1288 else
1289 lorutime = +0.;
1290 } else {
1291 if (local)
1292 lorutime = LocalTime(result);
1293 else
1294 lorutime = result;
1295 }
1297 argp = args;
1298 stop = argp + argc;
1299 if (maxargs >= 3 && argp < stop)
1300 year = *argp++;
1301 else
1302 year = YearFromTime(lorutime);
1304 if (maxargs >= 2 && argp < stop)
1305 month = *argp++;
1306 else
1307 month = MonthFromTime(lorutime);
1309 if (maxargs >= 1 && argp < stop)
1310 day = *argp++;
1311 else
1312 day = DateFromTime(lorutime);
1314 day = MakeDay(year, month, day); /* day within year */
1315 result = MakeDate(day, TimeWithinDay(lorutime));
1317 if (local)
1318 result = UTC(result);
1320 *date = TIMECLIP(result);
1321 return js_NewNumberValue(cx, *date, rval);
1322 }
1324 static JSBool
1325 date_setDate(JSContext *cx, JSObject *obj, uintN argc,
1326 jsval *argv, jsval *rval)
1327 {
1328 return date_makeDate(cx, obj, argc, argv, 1, JS_TRUE, rval);
1329 }
1331 static JSBool
1332 date_setUTCDate(JSContext *cx, JSObject *obj, uintN argc,
1333 jsval *argv, jsval *rval)
1334 {
1335 return date_makeDate(cx, obj, argc, argv, 1, JS_FALSE, rval);
1336 }
1338 static JSBool
1339 date_setMonth(JSContext *cx, JSObject *obj, uintN argc,
1340 jsval *argv, jsval *rval)
1341 {
1342 return date_makeDate(cx, obj, argc, argv, 2, JS_TRUE, rval);
1343 }
1345 static JSBool
1346 date_setUTCMonth(JSContext *cx, JSObject *obj, uintN argc,
1347 jsval *argv, jsval *rval)
1348 {
1349 return date_makeDate(cx, obj, argc, argv, 2, JS_FALSE, rval);
1350 }
1352 static JSBool
1353 date_setFullYear(JSContext *cx, JSObject *obj, uintN argc,
1354 jsval *argv, jsval *rval)
1355 {
1356 return date_makeDate(cx, obj, argc, argv, 3, JS_TRUE, rval);
1357 }
1359 static JSBool
1360 date_setUTCFullYear(JSContext *cx, JSObject *obj, uintN argc,
1361 jsval *argv, jsval *rval)
1362 {
1363 return date_makeDate(cx, obj, argc, argv, 3, JS_FALSE, rval);
1364 }
1366 static JSBool
1367 date_setYear(JSContext *cx, JSObject *obj, uintN argc,
1368 jsval *argv, jsval *rval)
1369 {
1370 jsdouble t;
1371 jsdouble year;
1372 jsdouble day;
1373 jsdouble result;
1375 jsdouble *date = date_getProlog(cx, obj, argv);
1376 if (!date)
1377 return JS_FALSE;
1379 result = *date;
1381 if (!js_ValueToNumber(cx, argv[0], &year))
1382 return JS_FALSE;
1383 if (!JSDOUBLE_IS_FINITE(year)) {
1384 *date = *cx->runtime->jsNaN;
1385 return js_NewNumberValue(cx, *date, rval);
1386 }
1388 year = js_DoubleToInteger(year);
1390 if (!JSDOUBLE_IS_FINITE(result)) {
1391 t = +0.0;
1392 } else {
1393 t = LocalTime(result);
1394 }
1396 if (year >= 0 && year <= 99)
1397 year += 1900;
1399 day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
1400 result = MakeDate(day, TimeWithinDay(t));
1401 result = UTC(result);
1403 *date = TIMECLIP(result);
1404 return js_NewNumberValue(cx, *date, rval);
1405 }
1407 /* constants for toString, toUTCString */
1408 static char js_NaN_date_str[] = "Invalid Date";
1409 static const char* days[] =
1410 {
1411 "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
1412 };
1413 static const char* months[] =
1414 {
1415 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1416 };
1418 static JSBool
1419 date_toGMTString(JSContext *cx, JSObject *obj, uintN argc,
1420 jsval *argv, jsval *rval)
1421 {
1422 char buf[100];
1423 JSString *str;
1424 jsdouble *date = date_getProlog(cx, obj, argv);
1425 if (!date)
1426 return JS_FALSE;
1428 if (!JSDOUBLE_IS_FINITE(*date)) {
1429 JS_snprintf(buf, sizeof buf, js_NaN_date_str);
1430 } else {
1431 jsdouble temp = *date;
1433 /* Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
1434 * requires a PRMJTime... which only has 16-bit years. Sub-ECMA.
1435 */
1436 JS_snprintf(buf, sizeof buf, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
1437 days[WeekDay(temp)],
1438 DateFromTime(temp),
1439 months[MonthFromTime(temp)],
1440 YearFromTime(temp),
1441 HourFromTime(temp),
1442 MinFromTime(temp),
1443 SecFromTime(temp));
1444 }
1445 str = JS_NewStringCopyZ(cx, buf);
1446 if (!str)
1447 return JS_FALSE;
1448 *rval = STRING_TO_JSVAL(str);
1449 return JS_TRUE;
1450 }
1452 /* for Date.toLocaleString; interface to PRMJTime date struct.
1453 * If findEquivalent is true, then try to map the year to an equivalent year
1454 * that's in range.
1455 */
1456 static void
1457 new_explode(jsdouble timeval, PRMJTime *split, JSBool findEquivalent)
1458 {
1459 jsint year = YearFromTime(timeval);
1460 int16 adjustedYear;
1462 /* If the year doesn't fit in a PRMJTime, find something to do about it. */
1463 if (year > 32767 || year < -32768) {
1464 if (findEquivalent) {
1465 /* We're really just trying to get a timezone string; map the year
1466 * to some equivalent year in the range 0 to 2800. Borrowed from
1467 * A. D. Olsen.
1468 */
1469 jsint cycles;
1470 #define CYCLE_YEARS 2800L
1471 cycles = (year >= 0) ? year / CYCLE_YEARS
1472 : -1 - (-1 - year) / CYCLE_YEARS;
1473 adjustedYear = (int16)(year - cycles * CYCLE_YEARS);
1474 } else {
1475 /* Clamp it to the nearest representable year. */
1476 adjustedYear = (int16)((year > 0) ? 32767 : - 32768);
1477 }
1478 } else {
1479 adjustedYear = (int16)year;
1480 }
1482 split->tm_usec = (int32) msFromTime(timeval) * 1000;
1483 split->tm_sec = (int8) SecFromTime(timeval);
1484 split->tm_min = (int8) MinFromTime(timeval);
1485 split->tm_hour = (int8) HourFromTime(timeval);
1486 split->tm_mday = (int8) DateFromTime(timeval);
1487 split->tm_mon = (int8) MonthFromTime(timeval);
1488 split->tm_wday = (int8) WeekDay(timeval);
1489 split->tm_year = (int16) adjustedYear;
1490 split->tm_yday = (int16) DayWithinYear(timeval, year);
1492 /* not sure how this affects things, but it doesn't seem
1493 to matter. */
1494 split->tm_isdst = (DaylightSavingTA(timeval) != 0);
1495 }
1497 typedef enum formatspec {
1498 FORMATSPEC_FULL, FORMATSPEC_DATE, FORMATSPEC_TIME
1499 } formatspec;
1501 /* helper function */
1502 static JSBool
1503 date_format(JSContext *cx, jsdouble date, formatspec format, jsval *rval)
1504 {
1505 char buf[100];
1506 JSString *str;
1507 char tzbuf[100];
1508 JSBool usetz;
1509 size_t i, tzlen;
1510 PRMJTime split;
1512 if (!JSDOUBLE_IS_FINITE(date)) {
1513 JS_snprintf(buf, sizeof buf, js_NaN_date_str);
1514 } else {
1515 jsdouble local = LocalTime(date);
1517 /* offset from GMT in minutes. The offset includes daylight savings,
1518 if it applies. */
1519 jsint minutes = (jsint) floor(AdjustTime(date) / msPerMinute);
1521 /* map 510 minutes to 0830 hours */
1522 intN offset = (minutes / 60) * 100 + minutes % 60;
1524 /* print as "Wed Nov 05 19:38:03 GMT-0800 (PST) 1997" The TZA is
1525 * printed as 'GMT-0800' rather than as 'PST' to avoid
1526 * operating-system dependence on strftime (which
1527 * PRMJ_FormatTimeUSEnglish calls, for %Z only.) win32 prints
1528 * PST as 'Pacific Standard Time.' This way we always know
1529 * what we're getting, and can parse it if we produce it.
1530 * The OS TZA string is included as a comment.
1531 */
1533 /* get a timezone string from the OS to include as a
1534 comment. */
1535 new_explode(date, &split, JS_TRUE);
1536 if (PRMJ_FormatTime(tzbuf, sizeof tzbuf, "(%Z)", &split) != 0) {
1538 /* Decide whether to use the resulting timezone string.
1539 *
1540 * Reject it if it contains any non-ASCII, non-alphanumeric
1541 * characters. It's then likely in some other character
1542 * encoding, and we probably won't display it correctly.
1543 */
1544 usetz = JS_TRUE;
1545 tzlen = strlen(tzbuf);
1546 if (tzlen > 100) {
1547 usetz = JS_FALSE;
1548 } else {
1549 for (i = 0; i < tzlen; i++) {
1550 jschar c = tzbuf[i];
1551 if (c > 127 ||
1552 !(isalpha(c) || isdigit(c) ||
1553 c == ' ' || c == '(' || c == ')')) {
1554 usetz = JS_FALSE;
1555 }
1556 }
1557 }
1559 /* Also reject it if it's not parenthesized or if it's '()'. */
1560 if (tzbuf[0] != '(' || tzbuf[1] == ')')
1561 usetz = JS_FALSE;
1562 } else
1563 usetz = JS_FALSE;
1565 switch (format) {
1566 case FORMATSPEC_FULL:
1567 /*
1568 * Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
1569 * requires a PRMJTime... which only has 16-bit years. Sub-ECMA.
1570 */
1571 /* Tue Oct 31 2000 09:41:40 GMT-0800 (PST) */
1572 JS_snprintf(buf, sizeof buf,
1573 "%s %s %.2d %.4d %.2d:%.2d:%.2d GMT%+.4d%s%s",
1574 days[WeekDay(local)],
1575 months[MonthFromTime(local)],
1576 DateFromTime(local),
1577 YearFromTime(local),
1578 HourFromTime(local),
1579 MinFromTime(local),
1580 SecFromTime(local),
1581 offset,
1582 usetz ? " " : "",
1583 usetz ? tzbuf : "");
1584 break;
1585 case FORMATSPEC_DATE:
1586 /* Tue Oct 31 2000 */
1587 JS_snprintf(buf, sizeof buf,
1588 "%s %s %.2d %.4d",
1589 days[WeekDay(local)],
1590 months[MonthFromTime(local)],
1591 DateFromTime(local),
1592 YearFromTime(local));
1593 break;
1594 case FORMATSPEC_TIME:
1595 /* 09:41:40 GMT-0800 (PST) */
1596 JS_snprintf(buf, sizeof buf,
1597 "%.2d:%.2d:%.2d GMT%+.4d%s%s",
1598 HourFromTime(local),
1599 MinFromTime(local),
1600 SecFromTime(local),
1601 offset,
1602 usetz ? " " : "",
1603 usetz ? tzbuf : "");
1604 break;
1605 }
1606 }
1608 str = JS_NewStringCopyZ(cx, buf);
1609 if (!str)
1610 return JS_FALSE;
1611 *rval = STRING_TO_JSVAL(str);
1612 return JS_TRUE;
1613 }
1615 static JSBool
1616 date_toLocaleHelper(JSContext *cx, JSObject *obj, uintN argc,
1617 jsval *argv, jsval *rval, char *format)
1618 {
1619 char buf[100];
1620 JSString *str;
1621 PRMJTime split;
1622 jsdouble *date = date_getProlog(cx, obj, argv);
1623 if (!date)
1624 return JS_FALSE;
1626 if (!JSDOUBLE_IS_FINITE(*date)) {
1627 JS_snprintf(buf, sizeof buf, js_NaN_date_str);
1628 } else {
1629 intN result_len;
1630 jsdouble local = LocalTime(*date);
1631 new_explode(local, &split, JS_FALSE);
1633 /* let PRMJTime format it. */
1634 result_len = PRMJ_FormatTime(buf, sizeof buf, format, &split);
1636 /* If it failed, default to toString. */
1637 if (result_len == 0)
1638 return date_format(cx, *date, FORMATSPEC_FULL, rval);
1640 /* Hacked check against undesired 2-digit year 00/00/00 form. */
1641 if (buf[result_len - 3] == '/' &&
1642 isdigit(buf[result_len - 2]) && isdigit(buf[result_len - 1])) {
1643 JS_snprintf(buf + (result_len - 2), (sizeof buf) - (result_len - 2),
1644 "%d", js_DateGetYear(cx, obj));
1645 }
1647 }
1649 if (cx->localeCallbacks && cx->localeCallbacks->localeToUnicode)
1650 return cx->localeCallbacks->localeToUnicode(cx, buf, rval);
1652 str = JS_NewStringCopyZ(cx, buf);
1653 if (!str)
1654 return JS_FALSE;
1655 *rval = STRING_TO_JSVAL(str);
1656 return JS_TRUE;
1657 }
1659 static JSBool
1660 date_toLocaleString(JSContext *cx, JSObject *obj, uintN argc,
1661 jsval *argv, jsval *rval)
1662 {
1663 /* Use '%#c' for windows, because '%c' is
1664 * backward-compatible and non-y2k with msvc; '%#c' requests that a
1665 * full year be used in the result string.
1666 */
1667 return date_toLocaleHelper(cx, obj, argc, argv, rval,
1668 #if defined(_WIN32) && !defined(__MWERKS__)
1669 "%#c"
1670 #else
1671 "%c"
1672 #endif
1673 );
1674 }
1676 static JSBool
1677 date_toLocaleDateString(JSContext *cx, JSObject *obj, uintN argc,
1678 jsval *argv, jsval *rval)
1679 {
1680 /* Use '%#x' for windows, because '%x' is
1681 * backward-compatible and non-y2k with msvc; '%#x' requests that a
1682 * full year be used in the result string.
1683 */
1684 return date_toLocaleHelper(cx, obj, argc, argv, rval,
1685 #if defined(_WIN32) && !defined(__MWERKS__)
1686 "%#x"
1687 #else
1688 "%x"
1689 #endif
1690 );
1691 }
1693 static JSBool
1694 date_toLocaleTimeString(JSContext *cx, JSObject *obj, uintN argc,
1695 jsval *argv, jsval *rval)
1696 {
1697 return date_toLocaleHelper(cx, obj, argc, argv, rval, "%X");
1698 }
1700 static JSBool
1701 date_toTimeString(JSContext *cx, JSObject *obj, uintN argc,
1702 jsval *argv, jsval *rval)
1703 {
1704 jsdouble *date = date_getProlog(cx, obj, argv);
1705 if (!date)
1706 return JS_FALSE;
1707 return date_format(cx, *date, FORMATSPEC_TIME, rval);
1708 }
1710 static JSBool
1711 date_toDateString(JSContext *cx, JSObject *obj, uintN argc,
1712 jsval *argv, jsval *rval)
1713 {
1714 jsdouble *date = date_getProlog(cx, obj, argv);
1715 if (!date)
1716 return JS_FALSE;
1717 return date_format(cx, *date, FORMATSPEC_DATE, rval);
1718 }
1720 #if JS_HAS_TOSOURCE
1721 #include <string.h>
1722 #include "jsdtoa.h"
1724 static JSBool
1725 date_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1726 jsval *rval)
1727 {
1728 jsdouble *date;
1729 char buf[DTOSTR_STANDARD_BUFFER_SIZE], *numStr, *bytes;
1730 JSString *str;
1732 date = date_getProlog(cx, obj, argv);
1733 if (!date)
1734 return JS_FALSE;
1736 numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, *date);
1737 if (!numStr) {
1738 JS_ReportOutOfMemory(cx);
1739 return JS_FALSE;
1740 }
1742 bytes = JS_smprintf("(new %s(%s))", date_class.name, numStr);
1743 if (!bytes) {
1744 JS_ReportOutOfMemory(cx);
1745 return JS_FALSE;
1746 }
1748 str = JS_NewString(cx, bytes, strlen(bytes));
1749 if (!str) {
1750 free(bytes);
1751 return JS_FALSE;
1752 }
1753 *rval = STRING_TO_JSVAL(str);
1754 return JS_TRUE;
1755 }
1756 #endif
1758 static JSBool
1759 date_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1760 jsval *rval)
1761 {
1762 jsdouble *date = date_getProlog(cx, obj, argv);
1763 if (!date)
1764 return JS_FALSE;
1765 return date_format(cx, *date, FORMATSPEC_FULL, rval);
1766 }
1768 #if JS_HAS_VALUEOF_HINT
1769 static JSBool
1770 date_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1771 jsval *rval)
1772 {
1773 /* It is an error to call date_valueOf on a non-date object, but we don't
1774 * need to check for that explicitly here because every path calls
1775 * date_getProlog, which does the check.
1776 */
1778 /* If called directly with no arguments, convert to a time number. */
1779 if (argc == 0)
1780 return date_getTime(cx, obj, argc, argv, rval);
1782 /* Convert to number only if the hint was given, otherwise favor string. */
1783 if (argc == 1) {
1784 JSString *str, *str2;
1786 str = js_ValueToString(cx, argv[0]);
1787 if (!str)
1788 return JS_FALSE;
1789 str2 = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]);
1790 if (!js_CompareStrings(str, str2))
1791 return date_getTime(cx, obj, argc, argv, rval);
1792 }
1793 return date_toString(cx, obj, argc, argv, rval);
1794 }
1795 #else
1796 #define date_valueOf date_getTime
1797 #endif
1799 \f
1800 /*
1801 * creation and destruction
1802 */
1804 static JSFunctionSpec date_static_methods[] = {
1805 {"UTC", date_UTC, MAXARGS,0,0 },
1806 {"parse", date_parse, 1,0,0 },
1807 {"now", date_now, 0,0,0 },
1808 {0,0,0,0,0}
1809 };
1811 static JSFunctionSpec date_methods[] = {
1812 {"getTime", date_getTime, 0,0,0 },
1813 {"getTimezoneOffset", date_getTimezoneOffset, 0,0,0 },
1814 {"getYear", date_getYear, 0,0,0 },
1815 {"getFullYear", date_getFullYear, 0,0,0 },
1816 {"getUTCFullYear", date_getUTCFullYear, 0,0,0 },
1817 {"getMonth", date_getMonth, 0,0,0 },
1818 {"getUTCMonth", date_getUTCMonth, 0,0,0 },
1819 {"getDate", date_getDate, 0,0,0 },
1820 {"getUTCDate", date_getUTCDate, 0,0,0 },
1821 {"getDay", date_getDay, 0,0,0 },
1822 {"getUTCDay", date_getUTCDay, 0,0,0 },
1823 {"getHours", date_getHours, 0,0,0 },
1824 {"getUTCHours", date_getUTCHours, 0,0,0 },
1825 {"getMinutes", date_getMinutes, 0,0,0 },
1826 {"getUTCMinutes", date_getUTCMinutes, 0,0,0 },
1827 {"getSeconds", date_getUTCSeconds, 0,0,0 },
1828 {"getUTCSeconds", date_getUTCSeconds, 0,0,0 },
1829 {"getMilliseconds", date_getUTCMilliseconds,0,0,0 },
1830 {"getUTCMilliseconds", date_getUTCMilliseconds,0,0,0 },
1831 {"setTime", date_setTime, 1,0,0 },
1832 {"setYear", date_setYear, 1,0,0 },
1833 {"setFullYear", date_setFullYear, 3,0,0 },
1834 {"setUTCFullYear", date_setUTCFullYear, 3,0,0 },
1835 {"setMonth", date_setMonth, 2,0,0 },
1836 {"setUTCMonth", date_setUTCMonth, 2,0,0 },
1837 {"setDate", date_setDate, 1,0,0 },
1838 {"setUTCDate", date_setUTCDate, 1,0,0 },
1839 {"setHours", date_setHours, 4,0,0 },
1840 {"setUTCHours", date_setUTCHours, 4,0,0 },
1841 {"setMinutes", date_setMinutes, 3,0,0 },
1842 {"setUTCMinutes", date_setUTCMinutes, 3,0,0 },
1843 {"setSeconds", date_setSeconds, 2,0,0 },
1844 {"setUTCSeconds", date_setUTCSeconds, 2,0,0 },
1845 {"setMilliseconds", date_setMilliseconds, 1,0,0 },
1846 {"setUTCMilliseconds", date_setUTCMilliseconds,1,0,0 },
1847 {"toUTCString", date_toGMTString, 0,0,0 },
1848 {js_toLocaleString_str, date_toLocaleString, 0,0,0 },
1849 {"toLocaleDateString", date_toLocaleDateString,0,0,0 },
1850 {"toLocaleTimeString", date_toLocaleTimeString,0,0,0 },
1851 {"toDateString", date_toDateString, 0,0,0 },
1852 {"toTimeString", date_toTimeString, 0,0,0 },
1853 #if JS_HAS_TOSOURCE
1854 {js_toSource_str, date_toSource, 0,0,0 },
1855 #endif
1856 {js_toString_str, date_toString, 0,0,0 },
1857 {js_valueOf_str, date_valueOf, 0,0,0 },
1858 {0,0,0,0,0}
1859 };
1861 static jsdouble *
1862 date_constructor(JSContext *cx, JSObject* obj)
1863 {
1864 jsdouble *date;
1866 date = js_NewDouble(cx, 0.0);
1867 if (!date)
1868 return NULL;
1869 OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, DOUBLE_TO_JSVAL(date));
1870 return date;
1871 }
1873 static JSBool
1874 Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1875 {
1876 jsdouble *date;
1877 JSString *str;
1878 jsdouble d;
1880 /* Date called as function */
1881 if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
1882 int64 us, ms, us2ms;
1883 jsdouble msec_time;
1885 /* NSPR 2.0 docs say 'We do not support PRMJ_NowMS and PRMJ_NowS',
1886 * so compute ms from PRMJ_Now.
1887 */
1888 us = PRMJ_Now();
1889 JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC);
1890 JSLL_DIV(ms, us, us2ms);
1891 JSLL_L2D(msec_time, ms);
1893 return date_format(cx, msec_time, FORMATSPEC_FULL, rval);
1894 }
1896 /* Date called as constructor */
1897 if (argc == 0) {
1898 int64 us, ms, us2ms;
1899 jsdouble msec_time;
1901 date = date_constructor(cx, obj);
1902 if (!date)
1903 return JS_FALSE;
1905 us = PRMJ_Now();
1906 JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC);
1907 JSLL_DIV(ms, us, us2ms);
1908 JSLL_L2D(msec_time, ms);
1910 *date = msec_time;
1911 } else if (argc == 1) {
1912 if (!JSVAL_IS_STRING(argv[0])) {
1913 /* the argument is a millisecond number */
1914 if (!js_ValueToNumber(cx, argv[0], &d))
1915 return JS_FALSE;
1916 date = date_constructor(cx, obj);
1917 if (!date)
1918 return JS_FALSE;
1919 *date = TIMECLIP(d);
1920 } else {
1921 /* the argument is a string; parse it. */
1922 date = date_constructor(cx, obj);
1923 if (!date)
1924 return JS_FALSE;
1926 str = js_ValueToString(cx, argv[0]);
1927 if (!str)
1928 return JS_FALSE;
1930 if (!date_parseString(str, date))
1931 *date = *cx->runtime->jsNaN;
1932 *date = TIMECLIP(*date);
1933 }
1934 } else {
1935 jsdouble array[MAXARGS];
1936 uintN loop;
1937 jsdouble double_arg;
1938 jsdouble day;
1939 jsdouble msec_time;
1941 for (loop = 0; loop < MAXARGS; loop++) {
1942 if (loop < argc) {
1943 if (!js_ValueToNumber(cx, argv[loop], &double_arg))
1944 return JS_FALSE;
1945 /* if any arg is NaN, make a NaN date object
1946 and return */
1947 if (!JSDOUBLE_IS_FINITE(double_arg)) {
1948 date = date_constructor(cx, obj);
1949 if (!date)
1950 return JS_FALSE;
1951 *date = *cx->runtime->jsNaN;
1952 return JS_TRUE;
1953 }
1954 array[loop] = js_DoubleToInteger(double_arg);
1955 } else {
1956 if (loop == 2) {
1957 array[loop] = 1; /* Default the date argument to 1. */
1958 } else {
1959 array[loop] = 0;
1960 }
1961 }
1962 }
1964 date = date_constructor(cx, obj);
1965 if (!date)
1966 return JS_FALSE;
1968 /* adjust 2-digit years into the 20th century */
1969 if (array[0] >= 0 && array[0] <= 99)
1970 array[0] += 1900;
1972 day = MakeDay(array[0], array[1], array[2]);
1973 msec_time = MakeTime(array[3], array[4], array[5], array[6]);
1974 msec_time = MakeDate(day, msec_time);
1975 msec_time = UTC(msec_time);
1976 *date = TIMECLIP(msec_time);
1977 }
1978 return JS_TRUE;
1979 }
1981 JSObject *
1982 js_InitDateClass(JSContext *cx, JSObject *obj)
1983 {
1984 JSObject *proto;
1985 jsdouble *proto_date;
1987 /* set static LocalTZA */
1988 LocalTZA = -(PRMJ_LocalGMTDifference() * msPerSecond);
1989 proto = JS_InitClass(cx, obj, NULL, &date_class, Date, MAXARGS,
1990 NULL, date_methods, NULL, date_static_methods);
1991 if (!proto)
1992 return NULL;
1994 /* Alias toUTCString with toGMTString. (ECMA B.2.6) */
1995 if (!JS_AliasProperty(cx, proto, "toUTCString", "toGMTString"))
1996 return NULL;
1998 /* Set the value of the Date.prototype date to NaN */
1999 proto_date = date_constructor(cx, proto);
2000 if (!proto_date)
2001 return NULL;
2002 *proto_date = *cx->runtime->jsNaN;
2004 return proto;
2005 }
2007 JS_FRIEND_API(JSObject *)
2008 js_NewDateObjectMsec(JSContext *cx, jsdouble msec_time)
2009 {
2010 JSObject *obj;
2011 jsdouble *date;
2013 obj = js_NewObject(cx, &date_class, NULL, NULL);
2014 if (!obj)
2015 return NULL;
2017 date = date_constructor(cx, obj);
2018 if (!date)
2019 return NULL;
2021 *date = msec_time;
2022 return obj;
2023 }
2025 JS_FRIEND_API(JSObject *)
2026 js_NewDateObject(JSContext* cx, int year, int mon, int mday,
2027 int hour, int min, int sec)
2028 {
2029 JSObject *obj;
2030 jsdouble msec_time;
2032 msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
2033 obj = js_NewDateObjectMsec(cx, UTC(msec_time));
2034 return obj;
2035 }
2037 JS_FRIEND_API(JSBool)
2038 js_DateIsValid(JSContext *cx, JSObject* obj)
2039 {
2040 jsdouble *date = date_getProlog(cx, obj, NULL);
2042 if (!date || JSDOUBLE_IS_NaN(*date))
2043 return JS_FALSE;
2044 else
2045 return JS_TRUE;
2046 }
2048 JS_FRIEND_API(int)
2049 js_DateGetYear(JSContext *cx, JSObject* obj)
2050 {
2051 jsdouble *date = date_getProlog(cx, obj, NULL);
2053 /* Preserve legacy API behavior of returning 0 for invalid dates. */
2054 if (!date || JSDOUBLE_IS_NaN(*date))
2055 return 0;
2056 return (int) YearFromTime(LocalTime(*date));
2057 }
2059 JS_FRIEND_API(int)
2060 js_DateGetMonth(JSContext *cx, JSObject* obj)
2061 {
2062 jsdouble *date = date_getProlog(cx, obj, NULL);
2064 if (!date || JSDOUBLE_IS_NaN(*date))
2065 return 0;
2066 return (int) MonthFromTime(LocalTime(*date));
2067 }
2069 JS_FRIEND_API(int)
2070 js_DateGetDate(JSContext *cx, JSObject* obj)
2071 {
2072 jsdouble *date = date_getProlog(cx, obj, NULL);
2074 if (!date || JSDOUBLE_IS_NaN(*date))
2075 return 0;
2076 return (int) DateFromTime(LocalTime(*date));
2077 }
2079 JS_FRIEND_API(int)
2080 js_DateGetHours(JSContext *cx, JSObject* obj)
2081 {
2082 jsdouble *date = date_getProlog(cx, obj, NULL);
2084 if (!date || JSDOUBLE_IS_NaN(*date))
2085 return 0;
2086 return (int) HourFromTime(LocalTime(*date));
2087 }
2089 JS_FRIEND_API(int)
2090 js_DateGetMinutes(JSContext *cx, JSObject* obj)
2091 {
2092 jsdouble *date = date_getProlog(cx, obj, NULL);
2094 if (!date || JSDOUBLE_IS_NaN(*date))
2095 return 0;
2096 return (int) MinFromTime(LocalTime(*date));
2097 }
2099 JS_FRIEND_API(int)
2100 js_DateGetSeconds(JSContext *cx, JSObject* obj)
2101 {
2102 jsdouble *date = date_getProlog(cx, obj, NULL);
2104 if (!date || JSDOUBLE_IS_NaN(*date))
2105 return 0;
2106 return (int) SecFromTime(*date);
2107 }
2109 JS_FRIEND_API(void)
2110 js_DateSetYear(JSContext *cx, JSObject *obj, int year)
2111 {
2112 jsdouble local;
2113 jsdouble *date = date_getProlog(cx, obj, NULL);
2114 if (!date)
2115 return;
2116 local = LocalTime(*date);
2117 /* reset date if it was NaN */
2118 if (JSDOUBLE_IS_NaN(local))
2119 local = 0;
2120 local = date_msecFromDate(year,
2121 MonthFromTime(local),
2122 DateFromTime(local),
2123 HourFromTime(local),
2124 MinFromTime(local),
2125 SecFromTime(local),
2126 msFromTime(local));
2127 *date = UTC(local);
2128 }
2130 JS_FRIEND_API(void)
2131 js_DateSetMonth(JSContext *cx, JSObject *obj, int month)
2132 {
2133 jsdouble local;
2134 jsdouble *date = date_getProlog(cx, obj, NULL);
2135 if (!date)
2136 return;
2137 local = LocalTime(*date);
2138 /* bail if date was NaN */
2139 if (JSDOUBLE_IS_NaN(local))
2140 return;
2141 local = date_msecFromDate(YearFromTime(local),
2142 month,
2143 DateFromTime(local),
2144 HourFromTime(local),
2145 MinFromTime(local),
2146 SecFromTime(local),
2147 msFromTime(local));
2148 *date = UTC(local);
2149 }
2151 JS_FRIEND_API(void)
2152 js_DateSetDate(JSContext *cx, JSObject *obj, int date)
2153 {
2154 jsdouble local;
2155 jsdouble *datep = date_getProlog(cx, obj, NULL);
2156 if (!datep)
2157 return;
2158 local = LocalTime(*datep);
2159 if (JSDOUBLE_IS_NaN(local))
2160 return;
2161 local = date_msecFromDate(YearFromTime(local),
2162 MonthFromTime(local),
2163 date,
2164 HourFromTime(local),
2165 MinFromTime(local),
2166 SecFromTime(local),
2167 msFromTime(local));
2168 *datep = UTC(local);
2169 }
2171 JS_FRIEND_API(void)
2172 js_DateSetHours(JSContext *cx, JSObject *obj, int hours)
2173 {
2174 jsdouble local;
2175 jsdouble *date = date_getProlog(cx, obj, NULL);
2176 if (!date)
2177 return;
2178 local = LocalTime(*date);
2179 if (JSDOUBLE_IS_NaN(local))
2180 return;
2181 local = date_msecFromDate(YearFromTime(local),
2182 MonthFromTime(local),
2183 DateFromTime(local),
2184 hours,
2185 MinFromTime(local),
2186 SecFromTime(local),
2187 msFromTime(local));
2188 *date = UTC(local);
2189 }
2191 JS_FRIEND_API(void)
2192 js_DateSetMinutes(JSContext *cx, JSObject *obj, int minutes)
2193 {
2194 jsdouble local;
2195 jsdouble *date = date_getProlog(cx, obj, NULL);
2196 if (!date)
2197 return;
2198 local = LocalTime(*date);
2199 if (JSDOUBLE_IS_NaN(local))
2200 return;
2201 local = date_msecFromDate(YearFromTime(local),
2202 MonthFromTime(local),
2203 DateFromTime(local),
2204 HourFromTime(local),
2205 minutes,
2206 SecFromTime(local),
2207 msFromTime(local));
2208 *date = UTC(local);
2209 }
2211 JS_FRIEND_API(void)
2212 js_DateSetSeconds(JSContext *cx, JSObject *obj, int seconds)
2213 {
2214 jsdouble local;
2215 jsdouble *date = date_getProlog(cx, obj, NULL);
2216 if (!date)
2217 return;
2218 local = LocalTime(*date);
2219 if (JSDOUBLE_IS_NaN(local))
2220 return;
2221 local = date_msecFromDate(YearFromTime(local),
2222 MonthFromTime(local),
2223 DateFromTime(local),
2224 HourFromTime(local),
2225 MinFromTime(local),
2226 seconds,
2227 msFromTime(local));
2228 *date = UTC(local);
2229 }
2231 JS_FRIEND_API(jsdouble)
2232 js_DateGetMsecSinceEpoch(JSContext *cx, JSObject *obj)
2233 {
2234 jsdouble *date = date_getProlog(cx, obj, NULL);
2235 if (!date || JSDOUBLE_IS_NaN(*date))
2236 return 0;
2237 return (*date);
2238 }