/[jscoverage]/trunk/js/jsdate.cpp
ViewVC logotype

Contents of /trunk/js/jsdate.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 507 - (show annotations)
Sun Jan 10 07:23:34 2010 UTC (9 years, 8 months ago) by siliconforks
File size: 74886 byte(s)
Update SpiderMonkey from Firefox 3.6rc1.

1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=78:
3 *
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 *
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
11 *
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
16 *
17 * The Original Code is Mozilla Communicator client code, released
18 * March 31, 1998.
19 *
20 * The Initial Developer of the Original Code is
21 * Netscape Communications Corporation.
22 * Portions created by the Initial Developer are Copyright (C) 1998
23 * the Initial Developer. All Rights Reserved.
24 *
25 * Contributor(s):
26 *
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
38 *
39 * ***** END LICENSE BLOCK ***** */
40
41 /*
42 * JS date methods.
43 */
44
45 /*
46 * "For example, OS/360 devotes 26 bytes of the permanently
47 * resident date-turnover routine to the proper handling of
48 * December 31 on leap years (when it is Day 366). That
49 * might have been left to the operator."
50 *
51 * Frederick Brooks, 'The Second-System Effect'.
52 */
53
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 "jsstdint.h"
61 #include "jsprf.h"
62 #include "prmjtime.h"
63 #include "jsutil.h" /* Added by JSIFY */
64 #include "jsapi.h"
65 #include "jsversion.h"
66 #include "jsbuiltins.h"
67 #include "jscntxt.h"
68 #include "jsdate.h"
69 #include "jsinterp.h"
70 #include "jsnum.h"
71 #include "jsobj.h"
72 #include "jsstr.h"
73
74 /*
75 * The JS 'Date' object is patterned after the Java 'Date' object.
76 * Here is an script:
77 *
78 * today = new Date();
79 *
80 * print(today.toLocaleString());
81 *
82 * weekDay = today.getDay();
83 *
84 *
85 * These Java (and ECMA-262) methods are supported:
86 *
87 * UTC
88 * getDate (getUTCDate)
89 * getDay (getUTCDay)
90 * getHours (getUTCHours)
91 * getMinutes (getUTCMinutes)
92 * getMonth (getUTCMonth)
93 * getSeconds (getUTCSeconds)
94 * getMilliseconds (getUTCMilliseconds)
95 * getTime
96 * getTimezoneOffset
97 * getYear
98 * getFullYear (getUTCFullYear)
99 * parse
100 * setDate (setUTCDate)
101 * setHours (setUTCHours)
102 * setMinutes (setUTCMinutes)
103 * setMonth (setUTCMonth)
104 * setSeconds (setUTCSeconds)
105 * setMilliseconds (setUTCMilliseconds)
106 * setTime
107 * setYear (setFullYear, setUTCFullYear)
108 * toGMTString (toUTCString)
109 * toLocaleString
110 * toString
111 *
112 *
113 * These Java methods are not supported
114 *
115 * setDay
116 * before
117 * after
118 * equals
119 * hashCode
120 */
121
122 /*
123 * 11/97 - jsdate.c has been rewritten to conform to the ECMA-262 language
124 * definition and reduce dependence on NSPR. NSPR is used to get the current
125 * time in milliseconds, the time zone offset, and the daylight savings time
126 * offset for a given time. NSPR is also used for Date.toLocaleString(), for
127 * locale-specific formatting, and to get a string representing the timezone.
128 * (Which turns out to be platform-dependent.)
129 *
130 * To do:
131 * (I did some performance tests by timing how long it took to run what
132 * I had of the js ECMA conformance tests.)
133 *
134 * - look at saving results across multiple calls to supporting
135 * functions; the toString functions compute some of the same values
136 * multiple times. Although - I took a quick stab at this, and I lost
137 * rather than gained. (Fractionally.) Hard to tell what compilers/processors
138 * are doing these days.
139 *
140 * - look at tweaking function return types to return double instead
141 * of int; this seems to make things run slightly faster sometimes.
142 * (though it could be architecture-dependent.) It'd be good to see
143 * how this does on win32. (Tried it on irix.) Types could use a
144 * general going-over.
145 */
146
147 /*
148 * Supporting functions - ECMA 15.9.1.*
149 */
150
151 #define HalfTimeDomain 8.64e15
152 #define HoursPerDay 24.0
153 #define MinutesPerDay (HoursPerDay * MinutesPerHour)
154 #define MinutesPerHour 60.0
155 #define SecondsPerDay (MinutesPerDay * SecondsPerMinute)
156 #define SecondsPerHour (MinutesPerHour * SecondsPerMinute)
157 #define SecondsPerMinute 60.0
158
159 #if defined(XP_WIN) || defined(XP_OS2)
160 /* Work around msvc double optimization bug by making these runtime values; if
161 * they're available at compile time, msvc optimizes division by them by
162 * computing the reciprocal and multiplying instead of dividing - this loses
163 * when the reciprocal isn't representable in a double.
164 */
165 static jsdouble msPerSecond = 1000.0;
166 static jsdouble msPerDay = SecondsPerDay * 1000.0;
167 static jsdouble msPerHour = SecondsPerHour * 1000.0;
168 static jsdouble msPerMinute = SecondsPerMinute * 1000.0;
169 #else
170 #define msPerDay (SecondsPerDay * msPerSecond)
171 #define msPerHour (SecondsPerHour * msPerSecond)
172 #define msPerMinute (SecondsPerMinute * msPerSecond)
173 #define msPerSecond 1000.0
174 #endif
175
176 #define Day(t) floor((t) / msPerDay)
177
178 static jsdouble
179 TimeWithinDay(jsdouble t)
180 {
181 jsdouble result;
182 result = fmod(t, msPerDay);
183 if (result < 0)
184 result += msPerDay;
185 return result;
186 }
187
188 #define DaysInYear(y) ((y) % 4 == 0 && ((y) % 100 || ((y) % 400 == 0)) \
189 ? 366 : 365)
190
191 /* math here has to be f.p, because we need
192 * floor((1968 - 1969) / 4) == -1
193 */
194 #define DayFromYear(y) (365 * ((y)-1970) + floor(((y)-1969)/4.0) \
195 - floor(((y)-1901)/100.0) + floor(((y)-1601)/400.0))
196 #define TimeFromYear(y) (DayFromYear(y) * msPerDay)
197
198 static jsint
199 YearFromTime(jsdouble t)
200 {
201 jsint y = (jsint) floor(t /(msPerDay*365.2425)) + 1970;
202 jsdouble t2 = (jsdouble) TimeFromYear(y);
203
204 if (t2 > t) {
205 y--;
206 } else {
207 if (t2 + msPerDay * DaysInYear(y) <= t)
208 y++;
209 }
210 return y;
211 }
212
213 #define InLeapYear(t) (JSBool) (DaysInYear(YearFromTime(t)) == 366)
214
215 #define DayWithinYear(t, year) ((intN) (Day(t) - DayFromYear(year)))
216
217 /*
218 * The following array contains the day of year for the first day of
219 * each month, where index 0 is January, and day 0 is January 1.
220 */
221 static jsdouble firstDayOfMonth[2][13] = {
222 {0.0, 31.0, 59.0, 90.0, 120.0, 151.0, 181.0, 212.0, 243.0, 273.0, 304.0, 334.0, 365.0},
223 {0.0, 31.0, 60.0, 91.0, 121.0, 152.0, 182.0, 213.0, 244.0, 274.0, 305.0, 335.0, 366.0}
224 };
225
226 #define DayFromMonth(m, leap) firstDayOfMonth[leap][(intN)m]
227
228 static intN
229 DaysInMonth(jsint year, jsint month)
230 {
231 JSBool leap = (DaysInYear(year) == 366);
232 intN result = DayFromMonth(month, leap) - DayFromMonth(month-1, leap);
233 return result;
234 }
235
236 static intN
237 MonthFromTime(jsdouble t)
238 {
239 intN d, step;
240 jsint year = YearFromTime(t);
241 d = DayWithinYear(t, year);
242
243 if (d < (step = 31))
244 return 0;
245 step += (InLeapYear(t) ? 29 : 28);
246 if (d < step)
247 return 1;
248 if (d < (step += 31))
249 return 2;
250 if (d < (step += 30))
251 return 3;
252 if (d < (step += 31))
253 return 4;
254 if (d < (step += 30))
255 return 5;
256 if (d < (step += 31))
257 return 6;
258 if (d < (step += 31))
259 return 7;
260 if (d < (step += 30))
261 return 8;
262 if (d < (step += 31))
263 return 9;
264 if (d < (step += 30))
265 return 10;
266 return 11;
267 }
268
269 static intN
270 DateFromTime(jsdouble t)
271 {
272 intN d, step, next;
273 jsint year = YearFromTime(t);
274 d = DayWithinYear(t, year);
275
276 if (d <= (next = 30))
277 return d + 1;
278 step = next;
279 next += (InLeapYear(t) ? 29 : 28);
280 if (d <= next)
281 return d - step;
282 step = next;
283 if (d <= (next += 31))
284 return d - step;
285 step = next;
286 if (d <= (next += 30))
287 return d - step;
288 step = next;
289 if (d <= (next += 31))
290 return d - step;
291 step = next;
292 if (d <= (next += 30))
293 return d - step;
294 step = next;
295 if (d <= (next += 31))
296 return d - step;
297 step = next;
298 if (d <= (next += 31))
299 return d - step;
300 step = next;
301 if (d <= (next += 30))
302 return d - step;
303 step = next;
304 if (d <= (next += 31))
305 return d - step;
306 step = next;
307 if (d <= (next += 30))
308 return d - step;
309 step = next;
310 return d - step;
311 }
312
313 static intN
314 WeekDay(jsdouble t)
315 {
316 jsint result;
317 result = (jsint) Day(t) + 4;
318 result = result % 7;
319 if (result < 0)
320 result += 7;
321 return (intN) result;
322 }
323
324 #define MakeTime(hour, min, sec, ms) \
325 ((((hour) * MinutesPerHour + (min)) * SecondsPerMinute + (sec)) * msPerSecond + (ms))
326
327 static jsdouble
328 MakeDay(jsdouble year, jsdouble month, jsdouble date)
329 {
330 JSBool leap;
331 jsdouble yearday;
332 jsdouble monthday;
333
334 year += floor(month / 12);
335
336 month = fmod(month, 12.0);
337 if (month < 0)
338 month += 12;
339
340 leap = (DaysInYear((jsint) year) == 366);
341
342 yearday = floor(TimeFromYear(year) / msPerDay);
343 monthday = DayFromMonth(month, leap);
344
345 return yearday + monthday + date - 1;
346 }
347
348 #define MakeDate(day, time) ((day) * msPerDay + (time))
349
350 /*
351 * Years and leap years on which Jan 1 is a Sunday, Monday, etc.
352 *
353 * yearStartingWith[0][i] is an example non-leap year where
354 * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
355 *
356 * yearStartingWith[1][i] is an example leap year where
357 * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
358 */
359 static jsint yearStartingWith[2][7] = {
360 {1978, 1973, 1974, 1975, 1981, 1971, 1977},
361 {1984, 1996, 1980, 1992, 1976, 1988, 1972}
362 };
363
364 /*
365 * Find a year for which any given date will fall on the same weekday.
366 *
367 * This function should be used with caution when used other than
368 * for determining DST; it hasn't been proven not to produce an
369 * incorrect year for times near year boundaries.
370 */
371 static jsint
372 EquivalentYearForDST(jsint year)
373 {
374 jsint day;
375 JSBool isLeapYear;
376
377 day = (jsint) DayFromYear(year) + 4;
378 day = day % 7;
379 if (day < 0)
380 day += 7;
381
382 isLeapYear = (DaysInYear(year) == 366);
383
384 return yearStartingWith[isLeapYear][day];
385 }
386
387 /* LocalTZA gets set by js_InitDateClass() */
388 static jsdouble LocalTZA;
389
390 static jsdouble
391 DaylightSavingTA(jsdouble t)
392 {
393 volatile int64 PR_t;
394 int64 ms2us;
395 int64 offset;
396 jsdouble result;
397
398 /* abort if NaN */
399 if (JSDOUBLE_IS_NaN(t))
400 return t;
401
402 /*
403 * If earlier than 1970 or after 2038, potentially beyond the ken of
404 * many OSes, map it to an equivalent year before asking.
405 */
406 if (t < 0.0 || t > 2145916800000.0) {
407 jsint year;
408 jsdouble day;
409
410 year = EquivalentYearForDST(YearFromTime(t));
411 day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
412 t = MakeDate(day, TimeWithinDay(t));
413 }
414
415 /* put our t in an LL, and map it to usec for prtime */
416 JSLL_D2L(PR_t, t);
417 JSLL_I2L(ms2us, PRMJ_USEC_PER_MSEC);
418 JSLL_MUL(PR_t, PR_t, ms2us);
419
420 offset = PRMJ_DSTOffset(PR_t);
421
422 JSLL_DIV(offset, offset, ms2us);
423 JSLL_L2D(result, offset);
424 return result;
425 }
426
427 static jsdouble
428 AdjustTime(jsdouble date)
429 {
430 jsdouble t = DaylightSavingTA(date) + LocalTZA;
431 t = (LocalTZA >= 0) ? fmod(t, msPerDay) : -fmod(msPerDay - t, msPerDay);
432 return t;
433 }
434
435 #define LocalTime(t) ((t) + AdjustTime(t))
436
437 static jsdouble
438 UTC(jsdouble t)
439 {
440 return t - AdjustTime(t - LocalTZA);
441 }
442
443 static intN
444 HourFromTime(jsdouble t)
445 {
446 intN result = (intN) fmod(floor(t/msPerHour), HoursPerDay);
447 if (result < 0)
448 result += (intN)HoursPerDay;
449 return result;
450 }
451
452 static intN
453 MinFromTime(jsdouble t)
454 {
455 intN result = (intN) fmod(floor(t / msPerMinute), MinutesPerHour);
456 if (result < 0)
457 result += (intN)MinutesPerHour;
458 return result;
459 }
460
461 static intN
462 SecFromTime(jsdouble t)
463 {
464 intN result = (intN) fmod(floor(t / msPerSecond), SecondsPerMinute);
465 if (result < 0)
466 result += (intN)SecondsPerMinute;
467 return result;
468 }
469
470 static intN
471 msFromTime(jsdouble t)
472 {
473 intN result = (intN) fmod(t, msPerSecond);
474 if (result < 0)
475 result += (intN)msPerSecond;
476 return result;
477 }
478
479 #define TIMECLIP(d) ((JSDOUBLE_IS_FINITE(d) \
480 && !((d < 0 ? -d : d) > HalfTimeDomain)) \
481 ? js_DoubleToInteger(d + (+0.)) : *cx->runtime->jsNaN)
482
483 /**
484 * end of ECMA 'support' functions
485 */
486
487 /*
488 * Other Support routines and definitions
489 */
490
491 /*
492 * We use the first reseved slot to store UTC time, and the second for caching
493 * the local time. The initial value of the cache entry is NaN.
494 */
495 const uint32 JSSLOT_UTC_TIME = JSSLOT_PRIVATE;
496 const uint32 JSSLOT_LOCAL_TIME = JSSLOT_PRIVATE + 1;
497
498 const uint32 DATE_RESERVED_SLOTS = 2;
499
500 JSClass js_DateClass = {
501 js_Date_str,
502 JSCLASS_HAS_RESERVED_SLOTS(DATE_RESERVED_SLOTS) |
503 JSCLASS_HAS_CACHED_PROTO(JSProto_Date),
504 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
505 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL,
506 JSCLASS_NO_OPTIONAL_MEMBERS
507 };
508
509 /* for use by date_parse */
510
511 static const char* wtb[] = {
512 "am", "pm",
513 "monday", "tuesday", "wednesday", "thursday", "friday",
514 "saturday", "sunday",
515 "january", "february", "march", "april", "may", "june",
516 "july", "august", "september", "october", "november", "december",
517 "gmt", "ut", "utc",
518 "est", "edt",
519 "cst", "cdt",
520 "mst", "mdt",
521 "pst", "pdt"
522 /* time zone table needs to be expanded */
523 };
524
525 static int ttb[] = {
526 -1, -2, 0, 0, 0, 0, 0, 0, 0, /* AM/PM */
527 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
528 10000 + 0, 10000 + 0, 10000 + 0, /* GMT/UT/UTC */
529 10000 + 5 * 60, 10000 + 4 * 60, /* EST/EDT */
530 10000 + 6 * 60, 10000 + 5 * 60, /* CST/CDT */
531 10000 + 7 * 60, 10000 + 6 * 60, /* MST/MDT */
532 10000 + 8 * 60, 10000 + 7 * 60 /* PST/PDT */
533 };
534
535 /* helper for date_parse */
536 static JSBool
537 date_regionMatches(const char* s1, int s1off, const jschar* s2, int s2off,
538 int count, int ignoreCase)
539 {
540 JSBool result = JS_FALSE;
541 /* return true if matches, otherwise, false */
542
543 while (count > 0 && s1[s1off] && s2[s2off]) {
544 if (ignoreCase) {
545 if (JS_TOLOWER((jschar)s1[s1off]) != JS_TOLOWER(s2[s2off])) {
546 break;
547 }
548 } else {
549 if ((jschar)s1[s1off] != s2[s2off]) {
550 break;
551 }
552 }
553 s1off++;
554 s2off++;
555 count--;
556 }
557
558 if (count == 0) {
559 result = JS_TRUE;
560 }
561
562 return result;
563 }
564
565 /* find UTC time from given date... no 1900 correction! */
566 static jsdouble
567 date_msecFromDate(jsdouble year, jsdouble mon, jsdouble mday, jsdouble hour,
568 jsdouble min, jsdouble sec, jsdouble msec)
569 {
570 jsdouble day;
571 jsdouble msec_time;
572 jsdouble result;
573
574 day = MakeDay(year, mon, mday);
575 msec_time = MakeTime(hour, min, sec, msec);
576 result = MakeDate(day, msec_time);
577 return result;
578 }
579
580 /* compute the time in msec (unclipped) from the given args */
581 #define MAXARGS 7
582
583 static JSBool
584 date_msecFromArgs(JSContext *cx, uintN argc, jsval *argv, jsdouble *rval)
585 {
586 uintN loop;
587 jsdouble array[MAXARGS];
588 jsdouble d;
589 jsdouble msec_time;
590
591 for (loop = 0; loop < MAXARGS; loop++) {
592 if (loop < argc) {
593 d = js_ValueToNumber(cx, &argv[loop]);
594 if (JSVAL_IS_NULL(argv[loop]))
595 return JS_FALSE;
596 /* return NaN if any arg is not finite */
597 if (!JSDOUBLE_IS_FINITE(d)) {
598 *rval = *cx->runtime->jsNaN;
599 return JS_TRUE;
600 }
601 array[loop] = js_DoubleToInteger(d);
602 } else {
603 if (loop == 2) {
604 array[loop] = 1; /* Default the date argument to 1. */
605 } else {
606 array[loop] = 0;
607 }
608 }
609 }
610
611 /* adjust 2-digit years into the 20th century */
612 if (array[0] >= 0 && array[0] <= 99)
613 array[0] += 1900;
614
615 msec_time = date_msecFromDate(array[0], array[1], array[2],
616 array[3], array[4], array[5], array[6]);
617 *rval = msec_time;
618 return JS_TRUE;
619 }
620
621 /*
622 * See ECMA 15.9.4.[3-10];
623 */
624 static JSBool
625 date_UTC(JSContext *cx, uintN argc, jsval *vp)
626 {
627 jsdouble msec_time;
628
629 if (!date_msecFromArgs(cx, argc, vp + 2, &msec_time))
630 return JS_FALSE;
631
632 msec_time = TIMECLIP(msec_time);
633
634 return js_NewNumberInRootedValue(cx, msec_time, vp);
635 }
636
637 /*
638 * Read and convert decimal digits from s[*i] into *result
639 * while *i < limit.
640 *
641 * Succeed if any digits are converted. Advance *i only
642 * as digits are consumed.
643 */
644 static JSBool
645 digits(size_t *result, const jschar *s, size_t *i, size_t limit)
646 {
647 size_t init = *i;
648 *result = 0;
649 while (*i < limit &&
650 ('0' <= s[*i] && s[*i] <= '9')) {
651 *result *= 10;
652 *result += (s[*i] - '0');
653 ++(*i);
654 }
655 return (*i != init);
656 }
657
658 /*
659 * Read and convert decimal digits to the right of a decimal point,
660 * representing a fractional integer, from s[*i] into *result
661 * while *i < limit.
662 *
663 * Succeed if any digits are converted. Advance *i only
664 * as digits are consumed.
665 */
666 static JSBool
667 fractional(jsdouble *result, const jschar *s, size_t *i, size_t limit)
668 {
669 jsdouble factor = 0.1;
670 size_t init = *i;
671 *result = 0.0;
672 while (*i < limit &&
673 ('0' <= s[*i] && s[*i] <= '9')) {
674 *result += (s[*i] - '0') * factor;
675 factor *= 0.1;
676 ++(*i);
677 }
678 return (*i != init);
679 }
680
681 /*
682 * Read and convert exactly n decimal digits from s[*i]
683 * to s[min(*i+n,limit)] into *result.
684 *
685 * Succeed if exactly n digits are converted. Advance *i only
686 * on success.
687 */
688 static JSBool
689 ndigits(size_t n, size_t *result, const jschar *s, size_t* i, size_t limit)
690 {
691 size_t init = *i;
692
693 if (digits(result, s, i, JS_MIN(limit, init+n)))
694 return ((*i - init) == n);
695
696 *i = init;
697 return JS_FALSE;
698 }
699
700 /*
701 * Parse a string in one of the date-time formats given by the W3C
702 * "NOTE-datetime" specification. These formats make up a restricted
703 * profile of the ISO 8601 format. Quoted here:
704 *
705 * The formats are as follows. Exactly the components shown here
706 * must be present, with exactly this punctuation. Note that the "T"
707 * appears literally in the string, to indicate the beginning of the
708 * time element, as specified in ISO 8601.
709 *
710 * Any combination of the date formats with the time formats is
711 * allowed, and also either the date or the time can be missing.
712 *
713 * The specification is silent on the meaning when fields are
714 * ommitted so the interpretations are a guess, but hopefully a
715 * reasonable one. We default the month to January, the day to the
716 * 1st, and hours minutes and seconds all to 0. If the date is
717 * missing entirely then we assume 1970-01-01 so that the time can
718 * be aded to a date later. If the time is missing then we assume
719 * 00:00 UTC. If the time is present but the time zone field is
720 * missing then we use local time.
721 *
722 * Date part:
723 *
724 * Year:
725 * YYYY (eg 1997)
726 *
727 * Year and month:
728 * YYYY-MM (eg 1997-07)
729 *
730 * Complete date:
731 * YYYY-MM-DD (eg 1997-07-16)
732 *
733 * Time part:
734 *
735 * Hours and minutes:
736 * Thh:mmTZD (eg T19:20+01:00)
737 *
738 * Hours, minutes and seconds:
739 * Thh:mm:ssTZD (eg T19:20:30+01:00)
740 *
741 * Hours, minutes, seconds and a decimal fraction of a second:
742 * Thh:mm:ss.sTZD (eg T19:20:30.45+01:00)
743 *
744 * where:
745 *
746 * YYYY = four-digit year or six digit year as +YYYYYY or -YYYYYY
747 * MM = two-digit month (01=January, etc.)
748 * DD = two-digit day of month (01 through 31)
749 * hh = two digits of hour (00 through 23) (am/pm NOT allowed)
750 * mm = two digits of minute (00 through 59)
751 * ss = two digits of second (00 through 59)
752 * s = one or more digits representing a decimal fraction of a second
753 * TZD = time zone designator (Z or +hh:mm or -hh:mm or missing for local)
754 */
755
756 static JSBool
757 date_parseISOString(JSString *str, jsdouble *result)
758 {
759 jsdouble msec;
760
761 const jschar *s;
762 size_t limit;
763 size_t i = 0;
764 int tzMul = 1;
765 int dateMul = 1;
766 size_t year = 1970;
767 size_t month = 1;
768 size_t day = 1;
769 size_t hour = 0;
770 size_t min = 0;
771 size_t sec = 0;
772 jsdouble frac = 0;
773 bool isLocalTime = JS_FALSE;
774 size_t tzHour = 0;
775 size_t tzMin = 0;
776
777 #define PEEK(ch) (i < limit && s[i] == ch)
778
779 #define NEED(ch) \
780 JS_BEGIN_MACRO \
781 if (i >= limit || s[i] != ch) { goto syntax; } else { ++i; } \
782 JS_END_MACRO
783
784 #define DONE_DATE_UNLESS(ch) \
785 JS_BEGIN_MACRO \
786 if (i >= limit || s[i] != ch) { goto done_date; } else { ++i; } \
787 JS_END_MACRO
788
789 #define DONE_UNLESS(ch) \
790 JS_BEGIN_MACRO \
791 if (i >= limit || s[i] != ch) { goto done; } else { ++i; } \
792 JS_END_MACRO
793
794 #define NEED_NDIGITS(n, field) \
795 JS_BEGIN_MACRO \
796 if (!ndigits(n, &field, s, &i, limit)) { goto syntax; } \
797 JS_END_MACRO
798
799 str->getCharsAndLength(s, limit);
800
801 if (PEEK('+') || PEEK('-')) {
802 if (PEEK('-'))
803 dateMul = -1;
804 ++i;
805 NEED_NDIGITS(6, year);
806 } else if (!PEEK('T')) {
807 NEED_NDIGITS(4, year);
808 }
809 DONE_DATE_UNLESS('-');
810 NEED_NDIGITS(2, month);
811 DONE_DATE_UNLESS('-');
812 NEED_NDIGITS(2, day);
813
814 done_date:
815 DONE_UNLESS('T');
816 NEED_NDIGITS(2, hour);
817 NEED(':');
818 NEED_NDIGITS(2, min);
819
820 if (PEEK(':')) {
821 ++i;
822 NEED_NDIGITS(2, sec);
823 if (PEEK('.')) {
824 ++i;
825 if (!fractional(&frac, s, &i, limit))
826 goto syntax;
827 }
828 }
829
830 if (PEEK('Z')) {
831 ++i;
832 } else if (PEEK('+') || PEEK('-')) {
833 if (PEEK('-'))
834 tzMul = -1;
835 ++i;
836 NEED_NDIGITS(2, tzHour);
837 NEED(':');
838 NEED_NDIGITS(2, tzMin);
839 } else {
840 isLocalTime = JS_TRUE;
841 }
842
843 done:
844 if (year > 275943 // ceil(1e8/365) + 1970
845 || (month == 0 || month > 12)
846 || (day == 0 || day > size_t(DaysInMonth(year,month)))
847 || hour > 24
848 || ((hour == 24) && (min > 0 || sec > 0))
849 || min > 59
850 || sec > 59
851 || tzHour > 23
852 || tzMin > 59)
853 goto syntax;
854
855 if (i != limit)
856 goto syntax;
857
858 month -= 1; /* convert month to 0-based */
859
860 msec = date_msecFromDate(dateMul * (jsdouble)year, month, day,
861 hour, min, sec,
862 frac * 1000.0);;
863
864 if (isLocalTime) {
865 msec = UTC(msec);
866 } else {
867 msec -= ((tzMul) * ((tzHour * msPerHour)
868 + (tzMin * msPerMinute)));
869 }
870
871 if (msec < -8.64e15 || msec > 8.64e15)
872 goto syntax;
873
874 *result = msec;
875
876 return JS_TRUE;
877
878 syntax:
879 /* syntax error */
880 *result = 0;
881 return JS_FALSE;
882
883 #undef PEEK
884 #undef NEED
885 #undef DONE_UNLESS
886 #undef NEED_NDIGITS
887 }
888
889 static JSBool
890 date_parseString(JSString *str, jsdouble *result)
891 {
892 jsdouble msec;
893
894 const jschar *s;
895 size_t limit;
896 size_t i = 0;
897 int year = -1;
898 int mon = -1;
899 int mday = -1;
900 int hour = -1;
901 int min = -1;
902 int sec = -1;
903 int c = -1;
904 int n = -1;
905 int tzoffset = -1;
906 int prevc = 0;
907 JSBool seenplusminus = JS_FALSE;
908 int temp;
909 JSBool seenmonthname = JS_FALSE;
910
911 if (date_parseISOString(str, result))
912 return JS_TRUE;
913
914 str->getCharsAndLength(s, limit);
915 if (limit == 0)
916 goto syntax;
917 while (i < limit) {
918 c = s[i];
919 i++;
920 if (c <= ' ' || c == ',' || c == '-') {
921 if (c == '-' && '0' <= s[i] && s[i] <= '9') {
922 prevc = c;
923 }
924 continue;
925 }
926 if (c == '(') { /* comments) */
927 int depth = 1;
928 while (i < limit) {
929 c = s[i];
930 i++;
931 if (c == '(') depth++;
932 else if (c == ')')
933 if (--depth <= 0)
934 break;
935 }
936 continue;
937 }
938 if ('0' <= c && c <= '9') {
939 n = c - '0';
940 while (i < limit && '0' <= (c = s[i]) && c <= '9') {
941 n = n * 10 + c - '0';
942 i++;
943 }
944
945 /* allow TZA before the year, so
946 * 'Wed Nov 05 21:49:11 GMT-0800 1997'
947 * works */
948
949 /* uses of seenplusminus allow : in TZA, so Java
950 * no-timezone style of GMT+4:30 works
951 */
952
953 if ((prevc == '+' || prevc == '-')/* && year>=0 */) {
954 /* make ':' case below change tzoffset */
955 seenplusminus = JS_TRUE;
956
957 /* offset */
958 if (n < 24)
959 n = n * 60; /* EG. "GMT-3" */
960 else
961 n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */
962 if (prevc == '+') /* plus means east of GMT */
963 n = -n;
964 if (tzoffset != 0 && tzoffset != -1)
965 goto syntax;
966 tzoffset = n;
967 } else if (prevc == '/' && mon >= 0 && mday >= 0 && year < 0) {
968 if (c <= ' ' || c == ',' || c == '/' || i >= limit)
969 year = n;
970 else
971 goto syntax;
972 } else if (c == ':') {
973 if (hour < 0)
974 hour = /*byte*/ n;
975 else if (min < 0)
976 min = /*byte*/ n;
977 else
978 goto syntax;
979 } else if (c == '/') {
980 /* until it is determined that mon is the actual
981 month, keep it as 1-based rather than 0-based */
982 if (mon < 0)
983 mon = /*byte*/ n;
984 else if (mday < 0)
985 mday = /*byte*/ n;
986 else
987 goto syntax;
988 } else if (i < limit && c != ',' && c > ' ' && c != '-' && c != '(') {
989 goto syntax;
990 } else if (seenplusminus && n < 60) { /* handle GMT-3:30 */
991 if (tzoffset < 0)
992 tzoffset -= n;
993 else
994 tzoffset += n;
995 } else if (hour >= 0 && min < 0) {
996 min = /*byte*/ n;
997 } else if (prevc == ':' && min >= 0 && sec < 0) {
998 sec = /*byte*/ n;
999 } else if (mon < 0) {
1000 mon = /*byte*/n;
1001 } else if (mon >= 0 && mday < 0) {
1002 mday = /*byte*/ n;
1003 } else if (mon >= 0 && mday >= 0 && year < 0) {
1004 year = n;
1005 } else {
1006 goto syntax;
1007 }
1008 prevc = 0;
1009 } else if (c == '/' || c == ':' || c == '+' || c == '-') {
1010 prevc = c;
1011 } else {
1012 size_t st = i - 1;
1013 int k;
1014 while (i < limit) {
1015 c = s[i];
1016 if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')))
1017 break;
1018 i++;
1019 }
1020 if (i <= st + 1)
1021 goto syntax;
1022 for (k = JS_ARRAY_LENGTH(wtb); --k >= 0;)
1023 if (date_regionMatches(wtb[k], 0, s, st, i-st, 1)) {
1024 int action = ttb[k];
1025 if (action != 0) {
1026 if (action < 0) {
1027 /*
1028 * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as
1029 * 12:30, instead of blindly adding 12 if PM.
1030 */
1031 JS_ASSERT(action == -1 || action == -2);
1032 if (hour > 12 || hour < 0) {
1033 goto syntax;
1034 } else {
1035 if (action == -1 && hour == 12) { /* am */
1036 hour = 0;
1037 } else if (action == -2 && hour != 12) { /* pm */
1038 hour += 12;
1039 }
1040 }
1041 } else if (action <= 13) { /* month! */
1042 /* Adjust mon to be 1-based until the final values
1043 for mon, mday and year are adjusted below */
1044 if (seenmonthname) {
1045 goto syntax;
1046 }
1047 seenmonthname = JS_TRUE;
1048 temp = /*byte*/ (action - 2) + 1;
1049
1050 if (mon < 0) {
1051 mon = temp;
1052 } else if (mday < 0) {
1053 mday = mon;
1054 mon = temp;
1055 } else if (year < 0) {
1056 year = mon;
1057 mon = temp;
1058 } else {
1059 goto syntax;
1060 }
1061 } else {
1062 tzoffset = action - 10000;
1063 }
1064 }
1065 break;
1066 }
1067 if (k < 0)
1068 goto syntax;
1069 prevc = 0;
1070 }
1071 }
1072 if (year < 0 || mon < 0 || mday < 0)
1073 goto syntax;
1074 /*
1075 Case 1. The input string contains an English month name.
1076 The form of the string can be month f l, or f month l, or
1077 f l month which each evaluate to the same date.
1078 If f and l are both greater than or equal to 70, or
1079 both less than 70, the date is invalid.
1080 The year is taken to be the greater of the values f, l.
1081 If the year is greater than or equal to 70 and less than 100,
1082 it is considered to be the number of years after 1900.
1083 Case 2. The input string is of the form "f/m/l" where f, m and l are
1084 integers, e.g. 7/16/45.
1085 Adjust the mon, mday and year values to achieve 100% MSIE
1086 compatibility.
1087 a. If 0 <= f < 70, f/m/l is interpreted as month/day/year.
1088 i. If year < 100, it is the number of years after 1900
1089 ii. If year >= 100, it is the number of years after 0.
1090 b. If 70 <= f < 100
1091 i. If m < 70, f/m/l is interpreted as
1092 year/month/day where year is the number of years after
1093 1900.
1094 ii. If m >= 70, the date is invalid.
1095 c. If f >= 100
1096 i. If m < 70, f/m/l is interpreted as
1097 year/month/day where year is the number of years after 0.
1098 ii. If m >= 70, the date is invalid.
1099 */
1100 if (seenmonthname) {
1101 if ((mday >= 70 && year >= 70) || (mday < 70 && year < 70)) {
1102 goto syntax;
1103 }
1104 if (mday > year) {
1105 temp = year;
1106 year = mday;
1107 mday = temp;
1108 }
1109 if (year >= 70 && year < 100) {
1110 year += 1900;
1111 }
1112 } else if (mon < 70) { /* (a) month/day/year */
1113 if (year < 100) {
1114 year += 1900;
1115 }
1116 } else if (mon < 100) { /* (b) year/month/day */
1117 if (mday < 70) {
1118 temp = year;
1119 year = mon + 1900;
1120 mon = mday;
1121 mday = temp;
1122 } else {
1123 goto syntax;
1124 }
1125 } else { /* (c) year/month/day */
1126 if (mday < 70) {
1127 temp = year;
1128 year = mon;
1129 mon = mday;
1130 mday = temp;
1131 } else {
1132 goto syntax;
1133 }
1134 }
1135 mon -= 1; /* convert month to 0-based */
1136 if (sec < 0)
1137 sec = 0;
1138 if (min < 0)
1139 min = 0;
1140 if (hour < 0)
1141 hour = 0;
1142
1143 msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
1144
1145 if (tzoffset == -1) { /* no time zone specified, have to use local */
1146 msec = UTC(msec);
1147 } else {
1148 msec += tzoffset * msPerMinute;
1149 }
1150
1151 *result = msec;
1152 return JS_TRUE;
1153
1154 syntax:
1155 /* syntax error */
1156 *result = 0;
1157 return JS_FALSE;
1158 }
1159
1160 static JSBool
1161 date_parse(JSContext *cx, uintN argc, jsval *vp)
1162 {
1163 JSString *str;
1164 jsdouble result;
1165
1166 if (argc == 0) {
1167 *vp = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
1168 return JS_TRUE;
1169 }
1170 str = js_ValueToString(cx, vp[2]);
1171 if (!str)
1172 return JS_FALSE;
1173 vp[2] = STRING_TO_JSVAL(str);
1174 if (!date_parseString(str, &result)) {
1175 *vp = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
1176 return JS_TRUE;
1177 }
1178
1179 result = TIMECLIP(result);
1180 return js_NewNumberInRootedValue(cx, result, vp);
1181 }
1182
1183 static inline jsdouble
1184 NowAsMillis()
1185 {
1186 return (jsdouble) (PRMJ_Now() / PRMJ_USEC_PER_MSEC);
1187 }
1188
1189 static JSBool
1190 date_now(JSContext *cx, uintN argc, jsval *vp)
1191 {
1192 return js_NewDoubleInRootedValue(cx, NowAsMillis(), vp);
1193 }
1194
1195 #ifdef JS_TRACER
1196 static jsdouble FASTCALL
1197 date_now_tn(JSContext*)
1198 {
1199 return NowAsMillis();
1200 }
1201 #endif
1202
1203 /*
1204 * Get UTC time from the date object. Returns false if the object is not
1205 * Date type.
1206 */
1207 static JSBool
1208 GetUTCTime(JSContext *cx, JSObject *obj, jsval *vp, jsdouble *dp)
1209 {
1210 if (!JS_InstanceOf(cx, obj, &js_DateClass, vp ? vp + 2 : NULL))
1211 return JS_FALSE;
1212 *dp = *JSVAL_TO_DOUBLE(obj->fslots[JSSLOT_UTC_TIME]);
1213 return JS_TRUE;
1214 }
1215
1216 static void
1217 SetDateToNaN(JSContext *cx, JSObject *obj, jsval *vp = NULL)
1218 {
1219 JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_DateClass);
1220
1221 jsval nan = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
1222 obj->fslots[JSSLOT_LOCAL_TIME] = nan;
1223 obj->fslots[JSSLOT_UTC_TIME] = nan;
1224 if (vp)
1225 *vp = nan;
1226 }
1227
1228 /*
1229 * Set UTC time to a given time and invalidate cached local time.
1230 */
1231 static JSBool
1232 SetUTCTime(JSContext *cx, JSObject *obj, jsdouble t, jsval *vp = NULL)
1233 {
1234 JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_DateClass);
1235
1236 obj->fslots[JSSLOT_LOCAL_TIME] = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
1237 if (!js_NewDoubleInRootedValue(cx, t, &obj->fslots[JSSLOT_UTC_TIME]))
1238 return false;
1239 if (vp)
1240 *vp = obj->fslots[JSSLOT_UTC_TIME];
1241 return true;
1242 }
1243
1244 /*
1245 * Get the local time, cache it if necessary. If UTC time is not finite
1246 * (e.g., NaN), the local time slot is set to the UTC time without conversion.
1247 */
1248 static JSBool
1249 GetAndCacheLocalTime(JSContext *cx, JSObject *obj, jsval *vp, jsdouble *dp)
1250 {
1251 jsval v;
1252 jsdouble result;
1253 jsdouble *cached;
1254
1255 if (!obj || !JS_InstanceOf(cx, obj, &js_DateClass, vp ? vp + 2 : NULL))
1256 return JS_FALSE;
1257 v = obj->fslots[JSSLOT_LOCAL_TIME];
1258
1259 result = *JSVAL_TO_DOUBLE(v);
1260
1261 if (JSDOUBLE_IS_NaN(result)) {
1262 if (!GetUTCTime(cx, obj, vp, &result))
1263 return JS_FALSE;
1264
1265 /* if result is NaN, it couldn't be finite. */
1266 if (JSDOUBLE_IS_FINITE(result))
1267 result = LocalTime(result);
1268
1269 cached = js_NewWeaklyRootedDouble(cx, result);
1270 if (!cached)
1271 return JS_FALSE;
1272
1273 obj->fslots[JSSLOT_LOCAL_TIME] = DOUBLE_TO_JSVAL(cached);
1274 }
1275
1276 *dp = result;
1277 return JS_TRUE;
1278 }
1279
1280 /*
1281 * See ECMA 15.9.5.4 thru 15.9.5.23
1282 */
1283 static JSBool
1284 date_getTime(JSContext *cx, uintN argc, jsval *vp)
1285 {
1286 jsdouble result;
1287
1288 return GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result) &&
1289 js_NewNumberInRootedValue(cx, result, vp);
1290 }
1291
1292 static JSBool
1293 GetYear(JSContext *cx, JSBool fullyear, jsval *vp)
1294 {
1295 jsdouble result;
1296
1297 if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1298 return JS_FALSE;
1299
1300 if (JSDOUBLE_IS_FINITE(result)) {
1301 result = YearFromTime(result);
1302
1303 /* Follow ECMA-262 to the letter, contrary to IE JScript. */
1304 if (!fullyear)
1305 result -= 1900;
1306 }
1307
1308 return js_NewNumberInRootedValue(cx, result, vp);
1309 }
1310
1311 static JSBool
1312 date_getYear(JSContext *cx, uintN argc, jsval *vp)
1313 {
1314 return GetYear(cx, JS_FALSE, vp);
1315 }
1316
1317 static JSBool
1318 date_getFullYear(JSContext *cx, uintN argc, jsval *vp)
1319 {
1320 return GetYear(cx, JS_TRUE, vp);
1321 }
1322
1323 static JSBool
1324 date_getUTCFullYear(JSContext *cx, uintN argc, jsval *vp)
1325 {
1326 jsdouble result;
1327
1328 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1329 return JS_FALSE;
1330
1331 if (JSDOUBLE_IS_FINITE(result))
1332 result = YearFromTime(result);
1333
1334 return js_NewNumberInRootedValue(cx, result, vp);
1335 }
1336
1337 static JSBool
1338 date_getMonth(JSContext *cx, uintN argc, jsval *vp)
1339 {
1340 jsdouble result;
1341
1342 if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1343 return JS_FALSE;
1344
1345 if (JSDOUBLE_IS_FINITE(result))
1346 result = MonthFromTime(result);
1347
1348 return js_NewNumberInRootedValue(cx, result, vp);
1349 }
1350
1351 static JSBool
1352 date_getUTCMonth(JSContext *cx, uintN argc, jsval *vp)
1353 {
1354 jsdouble result;
1355
1356 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1357 return JS_FALSE;
1358
1359 if (JSDOUBLE_IS_FINITE(result))
1360 result = MonthFromTime(result);
1361
1362 return js_NewNumberInRootedValue(cx, result, vp);
1363 }
1364
1365 static JSBool
1366 date_getDate(JSContext *cx, uintN argc, jsval *vp)
1367 {
1368 jsdouble result;
1369
1370 if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1371 return JS_FALSE;
1372
1373 if (JSDOUBLE_IS_FINITE(result))
1374 result = DateFromTime(result);
1375
1376 return js_NewNumberInRootedValue(cx, result, vp);
1377 }
1378
1379 static JSBool
1380 date_getUTCDate(JSContext *cx, uintN argc, jsval *vp)
1381 {
1382 jsdouble result;
1383
1384 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1385 return JS_FALSE;
1386
1387 if (JSDOUBLE_IS_FINITE(result))
1388 result = DateFromTime(result);
1389
1390 return js_NewNumberInRootedValue(cx, result, vp);
1391 }
1392
1393 static JSBool
1394 date_getDay(JSContext *cx, uintN argc, jsval *vp)
1395 {
1396 jsdouble result;
1397
1398 if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1399 return JS_FALSE;
1400
1401 if (JSDOUBLE_IS_FINITE(result))
1402 result = WeekDay(result);
1403
1404 return js_NewNumberInRootedValue(cx, result, vp);
1405 }
1406
1407 static JSBool
1408 date_getUTCDay(JSContext *cx, uintN argc, jsval *vp)
1409 {
1410 jsdouble result;
1411
1412 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1413 return JS_FALSE;
1414
1415 if (JSDOUBLE_IS_FINITE(result))
1416 result = WeekDay(result);
1417
1418 return js_NewNumberInRootedValue(cx, result, vp);
1419 }
1420
1421 static JSBool
1422 date_getHours(JSContext *cx, uintN argc, jsval *vp)
1423 {
1424 jsdouble result;
1425
1426 if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1427 return JS_FALSE;
1428
1429 if (JSDOUBLE_IS_FINITE(result))
1430 result = HourFromTime(result);
1431
1432 return js_NewNumberInRootedValue(cx, result, vp);
1433 }
1434
1435 static JSBool
1436 date_getUTCHours(JSContext *cx, uintN argc, jsval *vp)
1437 {
1438 jsdouble result;
1439
1440 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1441 return JS_FALSE;
1442
1443 if (JSDOUBLE_IS_FINITE(result))
1444 result = HourFromTime(result);
1445
1446 return js_NewNumberInRootedValue(cx, result, vp);
1447 }
1448
1449 static JSBool
1450 date_getMinutes(JSContext *cx, uintN argc, jsval *vp)
1451 {
1452 jsdouble result;
1453
1454 if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1455 return JS_FALSE;
1456
1457 if (JSDOUBLE_IS_FINITE(result))
1458 result = MinFromTime(result);
1459
1460 return js_NewNumberInRootedValue(cx, result, vp);
1461 }
1462
1463 static JSBool
1464 date_getUTCMinutes(JSContext *cx, uintN argc, jsval *vp)
1465 {
1466 jsdouble result;
1467
1468 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1469 return JS_FALSE;
1470
1471 if (JSDOUBLE_IS_FINITE(result))
1472 result = MinFromTime(result);
1473
1474 return js_NewNumberInRootedValue(cx, result, vp);
1475 }
1476
1477 /* Date.getSeconds is mapped to getUTCSeconds */
1478
1479 static JSBool
1480 date_getUTCSeconds(JSContext *cx, uintN argc, jsval *vp)
1481 {
1482 jsdouble result;
1483
1484 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1485 return JS_FALSE;
1486
1487 if (JSDOUBLE_IS_FINITE(result))
1488 result = SecFromTime(result);
1489
1490 return js_NewNumberInRootedValue(cx, result, vp);
1491 }
1492
1493 /* Date.getMilliseconds is mapped to getUTCMilliseconds */
1494
1495 static JSBool
1496 date_getUTCMilliseconds(JSContext *cx, uintN argc, jsval *vp)
1497 {
1498 jsdouble result;
1499
1500 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result))
1501 return JS_FALSE;
1502
1503 if (JSDOUBLE_IS_FINITE(result))
1504 result = msFromTime(result);
1505
1506 return js_NewNumberInRootedValue(cx, result, vp);
1507 }
1508
1509 static JSBool
1510 date_getTimezoneOffset(JSContext *cx, uintN argc, jsval *vp)
1511 {
1512 JSObject *obj;
1513 jsdouble utctime, localtime, result;
1514
1515 obj = JS_THIS_OBJECT(cx, vp);
1516 if (!GetUTCTime(cx, obj, vp, &utctime))
1517 return JS_FALSE;
1518 if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime))
1519 return JS_FALSE;
1520
1521 /*
1522 * Return the time zone offset in minutes for the current locale that is
1523 * appropriate for this time. This value would be a constant except for
1524 * daylight savings time.
1525 */
1526 result = (utctime - localtime) / msPerMinute;
1527 return js_NewNumberInRootedValue(cx, result, vp);
1528 }
1529
1530 static JSBool
1531 date_setTime(JSContext *cx, uintN argc, jsval *vp)
1532 {
1533 JSObject *obj = JS_THIS_OBJECT(cx, vp);
1534 if (!JS_InstanceOf(cx, obj, &js_DateClass, vp + 2))
1535 return false;
1536
1537 if (argc == 0) {
1538 SetDateToNaN(cx, obj, vp);
1539 return true;
1540 }
1541
1542 jsdouble result = js_ValueToNumber(cx, &vp[2]);
1543 if (JSVAL_IS_NULL(vp[2]))
1544 return false;
1545
1546 return SetUTCTime(cx, obj, TIMECLIP(result), vp);
1547 }
1548
1549 static JSBool
1550 date_makeTime(JSContext *cx, uintN maxargs, JSBool local, uintN argc, jsval *vp)
1551 {
1552 JSObject *obj;
1553 jsval *argv;
1554 uintN i;
1555 jsdouble args[4], *argp, *stop;
1556 jsdouble hour, min, sec, msec;
1557 jsdouble lorutime; /* Local or UTC version of *date */
1558
1559 jsdouble msec_time;
1560 jsdouble result;
1561
1562 obj = JS_THIS_OBJECT(cx, vp);
1563 if (!GetUTCTime(cx, obj, vp, &result))
1564 return false;
1565
1566 /* just return NaN if the date is already NaN */
1567 if (!JSDOUBLE_IS_FINITE(result))
1568 return js_NewNumberInRootedValue(cx, result, vp);
1569
1570 /*
1571 * Satisfy the ECMA rule that if a function is called with
1572 * fewer arguments than the specified formal arguments, the
1573 * remaining arguments are set to undefined. Seems like all
1574 * the Date.setWhatever functions in ECMA are only varargs
1575 * beyond the first argument; this should be set to undefined
1576 * if it's not given. This means that "d = new Date();
1577 * d.setMilliseconds()" returns NaN. Blech.
1578 */
1579 if (argc == 0) {
1580 SetDateToNaN(cx, obj, vp);
1581 return true;
1582 }
1583 if (argc > maxargs)
1584 argc = maxargs; /* clamp argc */
1585 JS_ASSERT(argc <= 4);
1586
1587 argv = vp + 2;
1588 for (i = 0; i < argc; i++) {
1589 args[i] = js_ValueToNumber(cx, &argv[i]);
1590 if (JSVAL_IS_NULL(argv[i]))
1591 return false;
1592 if (!JSDOUBLE_IS_FINITE(args[i])) {
1593 SetDateToNaN(cx, obj, vp);
1594 return true;
1595 }
1596 args[i] = js_DoubleToInteger(args[i]);
1597 }
1598
1599 if (local)
1600 lorutime = LocalTime(result);
1601 else
1602 lorutime = result;
1603
1604 argp = args;
1605 stop = argp + argc;
1606 if (maxargs >= 4 && argp < stop)
1607 hour = *argp++;
1608 else
1609 hour = HourFromTime(lorutime);
1610
1611 if (maxargs >= 3 && argp < stop)
1612 min = *argp++;
1613 else
1614 min = MinFromTime(lorutime);
1615
1616 if (maxargs >= 2 && argp < stop)
1617 sec = *argp++;
1618 else
1619 sec = SecFromTime(lorutime);
1620
1621 if (maxargs >= 1 && argp < stop)
1622 msec = *argp;
1623 else
1624 msec = msFromTime(lorutime);
1625
1626 msec_time = MakeTime(hour, min, sec, msec);
1627 result = MakeDate(Day(lorutime), msec_time);
1628
1629 /* fprintf(stderr, "%f\n", result); */
1630
1631 if (local)
1632 result = UTC(result);
1633
1634 /* fprintf(stderr, "%f\n", result); */
1635
1636 return SetUTCTime(cx, obj, TIMECLIP(result), vp);
1637 }
1638
1639 static JSBool
1640 date_setMilliseconds(JSContext *cx, uintN argc, jsval *vp)
1641 {
1642 return date_makeTime(cx, 1, JS_TRUE, argc, vp);
1643 }
1644
1645 static JSBool
1646 date_setUTCMilliseconds(JSContext *cx, uintN argc, jsval *vp)
1647 {
1648 return date_makeTime(cx, 1, JS_FALSE, argc, vp);
1649 }
1650
1651 static JSBool
1652 date_setSeconds(JSContext *cx, uintN argc, jsval *vp)
1653 {
1654 return date_makeTime(cx, 2, JS_TRUE, argc, vp);
1655 }
1656
1657 static JSBool
1658 date_setUTCSeconds(JSContext *cx, uintN argc, jsval *vp)
1659 {
1660 return date_makeTime(cx, 2, JS_FALSE, argc, vp);
1661 }
1662
1663 static JSBool
1664 date_setMinutes(JSContext *cx, uintN argc, jsval *vp)
1665 {
1666 return date_makeTime(cx, 3, JS_TRUE, argc, vp);
1667 }
1668
1669 static JSBool
1670 date_setUTCMinutes(JSContext *cx, uintN argc, jsval *vp)
1671 {
1672 return date_makeTime(cx, 3, JS_FALSE, argc, vp);
1673 }
1674
1675 static JSBool
1676 date_setHours(JSContext *cx, uintN argc, jsval *vp)
1677 {
1678 return date_makeTime(cx, 4, JS_TRUE, argc, vp);
1679 }
1680
1681 static JSBool
1682 date_setUTCHours(JSContext *cx, uintN argc, jsval *vp)
1683 {
1684 return date_makeTime(cx, 4, JS_FALSE, argc, vp);
1685 }
1686
1687 static JSBool
1688 date_makeDate(JSContext *cx, uintN maxargs, JSBool local, uintN argc, jsval *vp)
1689 {
1690 JSObject *obj;
1691 jsval *argv;
1692 uintN i;
1693 jsdouble lorutime; /* local or UTC version of *date */
1694 jsdouble args[3], *argp, *stop;
1695 jsdouble year, month, day;
1696 jsdouble result;
1697
1698 obj = JS_THIS_OBJECT(cx, vp);
1699 if (!GetUTCTime(cx, obj, vp, &result))
1700 return false;
1701
1702 /* see complaint about ECMA in date_MakeTime */
1703 if (argc == 0) {
1704 SetDateToNaN(cx, obj, vp);
1705 return true;
1706 }
1707 if (argc > maxargs)
1708 argc = maxargs; /* clamp argc */
1709 JS_ASSERT(1 <= argc && argc <= 3);
1710
1711 argv = vp + 2;
1712 for (i = 0; i < argc; i++) {
1713 args[i] = js_ValueToNumber(cx, &argv[i]);
1714 if (JSVAL_IS_NULL(argv[i]))
1715 return JS_FALSE;
1716 if (!JSDOUBLE_IS_FINITE(args[i])) {
1717 SetDateToNaN(cx, obj, vp);
1718 return true;
1719 }
1720 args[i] = js_DoubleToInteger(args[i]);
1721 }
1722
1723 /* return NaN if date is NaN and we're not setting the year,
1724 * If we are, use 0 as the time. */
1725 if (!(JSDOUBLE_IS_FINITE(result))) {
1726 if (maxargs < 3)
1727 return js_NewNumberInRootedValue(cx, result, vp);
1728 lorutime = +0.;
1729 } else {
1730 lorutime = local ? LocalTime(result) : result;
1731 }
1732
1733 argp = args;
1734 stop = argp + argc;
1735 if (maxargs >= 3 && argp < stop)
1736 year = *argp++;
1737 else
1738 year = YearFromTime(lorutime);
1739
1740 if (maxargs >= 2 && argp < stop)
1741 month = *argp++;
1742 else
1743 month = MonthFromTime(lorutime);
1744
1745 if (maxargs >= 1 && argp < stop)
1746 day = *argp++;
1747 else
1748 day = DateFromTime(lorutime);
1749
1750 day = MakeDay(year, month, day); /* day within year */
1751 result = MakeDate(day, TimeWithinDay(lorutime));
1752
1753 if (local)
1754 result = UTC(result);
1755
1756 return SetUTCTime(cx, obj, TIMECLIP(result), vp);
1757 }
1758
1759 static JSBool
1760 date_setDate(JSContext *cx, uintN argc, jsval *vp)
1761 {
1762 return date_makeDate(cx, 1, JS_TRUE, argc, vp);
1763 }
1764
1765 static JSBool
1766 date_setUTCDate(JSContext *cx, uintN argc, jsval *vp)
1767 {
1768 return date_makeDate(cx, 1, JS_FALSE, argc, vp);
1769 }
1770
1771 static JSBool
1772 date_setMonth(JSContext *cx, uintN argc, jsval *vp)
1773 {
1774 return date_makeDate(cx, 2, JS_TRUE, argc, vp);
1775 }
1776
1777 static JSBool
1778 date_setUTCMonth(JSContext *cx, uintN argc, jsval *vp)
1779 {
1780 return date_makeDate(cx, 2, JS_FALSE, argc, vp);
1781 }
1782
1783 static JSBool
1784 date_setFullYear(JSContext *cx, uintN argc, jsval *vp)
1785 {
1786 return date_makeDate(cx, 3, JS_TRUE, argc, vp);
1787 }
1788
1789 static JSBool
1790 date_setUTCFullYear(JSContext *cx, uintN argc, jsval *vp)
1791 {
1792 return date_makeDate(cx, 3, JS_FALSE, argc, vp);
1793 }
1794
1795 static JSBool
1796 date_setYear(JSContext *cx, uintN argc, jsval *vp)
1797 {
1798 JSObject *obj = JS_THIS_OBJECT(cx, vp);
1799
1800 jsdouble result;
1801 if (!GetUTCTime(cx, obj, vp, &result))
1802 return false;
1803
1804 if (argc == 0) {
1805 /* Call this only after GetUTCTime has verified that obj is Date. */
1806 SetDateToNaN(cx, obj, vp);
1807 return true;
1808 }
1809
1810 jsdouble year = js_ValueToNumber(cx, &vp[2]);
1811 if (JSVAL_IS_NULL(vp[2]))
1812 return false;
1813 if (!JSDOUBLE_IS_FINITE(year)) {
1814 SetDateToNaN(cx, obj, vp);
1815 return true;
1816 }
1817 year = js_DoubleToInteger(year);
1818 if (year >= 0 && year <= 99)
1819 year += 1900;
1820
1821 jsdouble t = JSDOUBLE_IS_FINITE(result) ? LocalTime(result) : +0.0;
1822 jsdouble day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
1823 result = MakeDate(day, TimeWithinDay(t));
1824 result = UTC(result);
1825
1826 return SetUTCTime(cx, obj, TIMECLIP(result), vp);
1827 }
1828
1829 /* constants for toString, toUTCString */
1830 static char js_NaN_date_str[] = "Invalid Date";
1831 static const char* days[] =
1832 {
1833 "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
1834 };
1835 static const char* months[] =
1836 {
1837 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1838 };
1839
1840
1841 // Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
1842 // requires a PRMJTime... which only has 16-bit years. Sub-ECMA.
1843 static void
1844 print_gmt_string(char* buf, size_t size, jsdouble utctime)
1845 {
1846 JS_snprintf(buf, size, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
1847 days[WeekDay(utctime)],
1848 DateFromTime(utctime),
1849 months[MonthFromTime(utctime)],
1850 YearFromTime(utctime),
1851 HourFromTime(utctime),
1852 MinFromTime(utctime),
1853 SecFromTime(utctime));
1854 }
1855
1856 static void
1857 print_iso_string(char* buf, size_t size, jsdouble utctime)
1858 {
1859 JS_snprintf(buf, size, "%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.3dZ",
1860 YearFromTime(utctime),
1861 MonthFromTime(utctime) + 1,
1862 DateFromTime(utctime),
1863 HourFromTime(utctime),
1864 MinFromTime(utctime),
1865 SecFromTime(utctime),
1866 msFromTime(utctime));
1867 }
1868
1869 static JSBool
1870 date_utc_format(JSContext *cx, jsval *vp,
1871 void (*printFunc)(char*, size_t, jsdouble))
1872 {
1873 char buf[100];
1874 JSString *str;
1875 jsdouble utctime;
1876
1877 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &utctime))
1878 return JS_FALSE;
1879
1880 if (!JSDOUBLE_IS_FINITE(utctime)) {
1881 JS_snprintf(buf, sizeof buf, js_NaN_date_str);
1882 } else {
1883 (*printFunc)(buf, sizeof buf, utctime);
1884 }
1885 str = JS_NewStringCopyZ(cx, buf);
1886 if (!str)
1887 return JS_FALSE;
1888 *vp = STRING_TO_JSVAL(str);
1889 return JS_TRUE;
1890 }
1891
1892 static JSBool
1893 date_toGMTString(JSContext *cx, uintN argc, jsval *vp)
1894 {
1895 return date_utc_format(cx, vp, print_gmt_string);
1896 }
1897
1898 static JSBool
1899 date_toISOString(JSContext *cx, uintN argc, jsval *vp)
1900 {
1901 return date_utc_format(cx, vp, print_iso_string);
1902 }
1903
1904 /* for Date.toLocaleString; interface to PRMJTime date struct.
1905 */
1906 static void
1907 new_explode(jsdouble timeval, PRMJTime *split)
1908 {
1909 jsint year = YearFromTime(timeval);
1910
1911 split->tm_usec = (int32) msFromTime(timeval) * 1000;
1912 split->tm_sec = (int8) SecFromTime(timeval);
1913 split->tm_min = (int8) MinFromTime(timeval);
1914 split->tm_hour = (int8) HourFromTime(timeval);
1915 split->tm_mday = (int8) DateFromTime(timeval);
1916 split->tm_mon = (int8) MonthFromTime(timeval);
1917 split->tm_wday = (int8) WeekDay(timeval);
1918 split->tm_year = year;
1919 split->tm_yday = (int16) DayWithinYear(timeval, year);
1920
1921 /* not sure how this affects things, but it doesn't seem
1922 to matter. */
1923 split->tm_isdst = (DaylightSavingTA(timeval) != 0);
1924 }
1925
1926 typedef enum formatspec {
1927 FORMATSPEC_FULL, FORMATSPEC_DATE, FORMATSPEC_TIME
1928 } formatspec;
1929
1930 /* helper function */
1931 static JSBool
1932 date_format(JSContext *cx, jsdouble date, formatspec format, jsval *rval)
1933 {
1934 char buf[100];
1935 JSString *str;
1936 char tzbuf[100];
1937 JSBool usetz;
1938 size_t i, tzlen;
1939 PRMJTime split;
1940
1941 if (!JSDOUBLE_IS_FINITE(date)) {
1942 JS_snprintf(buf, sizeof buf, js_NaN_date_str);
1943 } else {
1944 jsdouble local = LocalTime(date);
1945
1946 /* offset from GMT in minutes. The offset includes daylight savings,
1947 if it applies. */
1948 jsint minutes = (jsint) floor(AdjustTime(date) / msPerMinute);
1949
1950 /* map 510 minutes to 0830 hours */
1951 intN offset = (minutes / 60) * 100 + minutes % 60;
1952
1953 /* print as "Wed Nov 05 19:38:03 GMT-0800 (PST) 1997" The TZA is
1954 * printed as 'GMT-0800' rather than as 'PST' to avoid
1955 * operating-system dependence on strftime (which
1956 * PRMJ_FormatTimeUSEnglish calls, for %Z only.) win32 prints
1957 * PST as 'Pacific Standard Time.' This way we always know
1958 * what we're getting, and can parse it if we produce it.
1959 * The OS TZA string is included as a comment.
1960 */
1961
1962 /* get a timezone string from the OS to include as a
1963 comment. */
1964 new_explode(date, &split);
1965 if (PRMJ_FormatTime(tzbuf, sizeof tzbuf, "(%Z)", &split) != 0) {
1966
1967 /* Decide whether to use the resulting timezone string.
1968 *
1969 * Reject it if it contains any non-ASCII, non-alphanumeric
1970 * characters. It's then likely in some other character
1971 * encoding, and we probably won't display it correctly.
1972 */
1973 usetz = JS_TRUE;
1974 tzlen = strlen(tzbuf);
1975 if (tzlen > 100) {
1976 usetz = JS_FALSE;
1977 } else {
1978 for (i = 0; i < tzlen; i++) {
1979 jschar c = tzbuf[i];
1980 if (c > 127 ||
1981 !(isalpha(c) || isdigit(c) ||
1982 c == ' ' || c == '(' || c == ')')) {
1983 usetz = JS_FALSE;
1984 }
1985 }
1986 }
1987
1988 /* Also reject it if it's not parenthesized or if it's '()'. */
1989 if (tzbuf[0] != '(' || tzbuf[1] == ')')
1990 usetz = JS_FALSE;
1991 } else
1992 usetz = JS_FALSE;
1993
1994 switch (format) {
1995 case FORMATSPEC_FULL:
1996 /*
1997 * Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
1998 * requires a PRMJTime... which only has 16-bit years. Sub-ECMA.
1999 */
2000 /* Tue Oct 31 2000 09:41:40 GMT-0800 (PST) */
2001 JS_snprintf(buf, sizeof buf,
2002 "%s %s %.2d %.4d %.2d:%.2d:%.2d GMT%+.4d%s%s",
2003 days[WeekDay(local)],
2004 months[MonthFromTime(local)],
2005 DateFromTime(local),
2006 YearFromTime(local),
2007 HourFromTime(local),
2008 MinFromTime(local),
2009 SecFromTime(local),
2010 offset,
2011 usetz ? " " : "",
2012 usetz ? tzbuf : "");
2013 break;
2014 case FORMATSPEC_DATE:
2015 /* Tue Oct 31 2000 */
2016 JS_snprintf(buf, sizeof buf,
2017 "%s %s %.2d %.4d",
2018 days[WeekDay(local)],
2019 months[MonthFromTime(local)],
2020 DateFromTime(local),
2021 YearFromTime(local));
2022 break;
2023 case FORMATSPEC_TIME:
2024 /* 09:41:40 GMT-0800 (PST) */
2025 JS_snprintf(buf, sizeof buf,
2026 "%.2d:%.2d:%.2d GMT%+.4d%s%s",
2027 HourFromTime(local),
2028 MinFromTime(local),
2029 SecFromTime(local),
2030 offset,
2031 usetz ? " " : "",
2032 usetz ? tzbuf : "");
2033 break;
2034 }
2035 }
2036
2037 str = JS_NewStringCopyZ(cx, buf);
2038 if (!str)
2039 return JS_FALSE;
2040 *rval = STRING_TO_JSVAL(str);
2041 return JS_TRUE;
2042 }
2043
2044 static JSBool
2045 date_toLocaleHelper(JSContext *cx, const char *format, jsval *vp)
2046 {
2047 JSObject *obj;
2048 char buf[100];
2049 JSString *str;
2050 PRMJTime split;
2051 jsdouble utctime;
2052
2053 obj = JS_THIS_OBJECT(cx, vp);
2054 if (!GetUTCTime(cx, obj, vp, &utctime))
2055 return JS_FALSE;
2056
2057 if (!JSDOUBLE_IS_FINITE(utctime)) {
2058 JS_snprintf(buf, sizeof buf, js_NaN_date_str);
2059 } else {
2060 intN result_len;
2061 jsdouble local = LocalTime(utctime);
2062 new_explode(local, &split);
2063
2064 /* let PRMJTime format it. */
2065 result_len = PRMJ_FormatTime(buf, sizeof buf, format, &split);
2066
2067 /* If it failed, default to toString. */
2068 if (result_len == 0)
2069 return date_format(cx, utctime, FORMATSPEC_FULL, vp);
2070
2071 /* Hacked check against undesired 2-digit year 00/00/00 form. */
2072 if (strcmp(format, "%x") == 0 && result_len >= 6 &&
2073 /* Format %x means use OS settings, which may have 2-digit yr, so
2074 hack end of 3/11/22 or 11.03.22 or 11Mar22 to use 4-digit yr...*/
2075 !isdigit(buf[result_len - 3]) &&
2076 isdigit(buf[result_len - 2]) && isdigit(buf[result_len - 1]) &&
2077 /* ...but not if starts with 4-digit year, like 2022/3/11. */
2078 !(isdigit(buf[0]) && isdigit(buf[1]) &&
2079 isdigit(buf[2]) && isdigit(buf[3]))) {
2080 JS_snprintf(buf + (result_len - 2), (sizeof buf) - (result_len - 2),
2081 "%d", js_DateGetYear(cx, obj));
2082 }
2083
2084 }
2085
2086 if (cx->localeCallbacks && cx->localeCallbacks->localeToUnicode)
2087 return cx->localeCallbacks->localeToUnicode(cx, buf, vp);
2088
2089 str = JS_NewStringCopyZ(cx, buf);
2090 if (!str)
2091 return JS_FALSE;
2092 *vp = STRING_TO_JSVAL(str);
2093 return JS_TRUE;
2094 }
2095
2096 static JSBool
2097 date_toLocaleString(JSContext *cx, uintN argc, jsval *vp)
2098 {
2099 /* Use '%#c' for windows, because '%c' is
2100 * backward-compatible and non-y2k with msvc; '%#c' requests that a
2101 * full year be used in the result string.
2102 */
2103 return date_toLocaleHelper(cx,
2104 #if defined(_WIN32) && !defined(__MWERKS__)
2105 "%#c"
2106 #else
2107 "%c"
2108 #endif
2109 , vp);
2110 }
2111
2112 static JSBool
2113 date_toLocaleDateString(JSContext *cx, uintN argc, jsval *vp)
2114 {
2115 /* Use '%#x' for windows, because '%x' is
2116 * backward-compatible and non-y2k with msvc; '%#x' requests that a
2117 * full year be used in the result string.
2118 */
2119 return date_toLocaleHelper(cx,
2120 #if defined(_WIN32) && !defined(__MWERKS__)
2121 "%#x"
2122 #else
2123 "%x"
2124 #endif
2125 , vp);
2126 }
2127
2128 static JSBool
2129 date_toLocaleTimeString(JSContext *cx, uintN argc, jsval *vp)
2130 {
2131 return date_toLocaleHelper(cx, "%X", vp);
2132 }
2133
2134 static JSBool
2135 date_toLocaleFormat(JSContext *cx, uintN argc, jsval *vp)
2136 {
2137 JSString *fmt;
2138 const char *fmtbytes;
2139
2140 if (argc == 0)
2141 return date_toLocaleString(cx, argc, vp);
2142
2143 fmt = js_ValueToString(cx, vp[2]);
2144 if (!fmt)
2145 return JS_FALSE;
2146 vp[2] = STRING_TO_JSVAL(fmt);
2147 fmtbytes = js_GetStringBytes(cx, fmt);
2148 if (!fmtbytes)
2149 return JS_FALSE;
2150
2151 return date_toLocaleHelper(cx, fmtbytes, vp);
2152 }
2153
2154 static JSBool
2155 date_toTimeString(JSContext *cx, uintN argc, jsval *vp)
2156 {
2157 jsdouble utctime;
2158
2159 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &utctime))
2160 return JS_FALSE;
2161 return date_format(cx, utctime, FORMATSPEC_TIME, vp);
2162 }
2163
2164 static JSBool
2165 date_toDateString(JSContext *cx, uintN argc, jsval *vp)
2166 {
2167 jsdouble utctime;
2168
2169 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &utctime))
2170 return JS_FALSE;
2171 return date_format(cx, utctime, FORMATSPEC_DATE, vp);
2172 }
2173
2174 #if JS_HAS_TOSOURCE
2175 #include <string.h>
2176 #include "jsdtoa.h"
2177
2178 static JSBool
2179 date_toSource(JSContext *cx, uintN argc, jsval *vp)
2180 {
2181 jsdouble utctime;
2182 char buf[DTOSTR_STANDARD_BUFFER_SIZE], *numStr, *bytes;
2183 JSString *str;
2184
2185 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &utctime))
2186 return JS_FALSE;
2187
2188 numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, utctime);
2189 if (!numStr) {
2190 JS_ReportOutOfMemory(cx);
2191 return JS_FALSE;
2192 }
2193
2194 bytes = JS_smprintf("(new %s(%s))", js_Date_str, numStr);
2195 if (!bytes) {
2196 JS_ReportOutOfMemory(cx);
2197 return JS_FALSE;
2198 }
2199
2200 str = JS_NewString(cx, bytes, strlen(bytes));
2201 if (!str) {
2202 js_free(bytes);
2203 return JS_FALSE;
2204 }
2205 *vp = STRING_TO_JSVAL(str);
2206 return JS_TRUE;
2207 }
2208 #endif
2209
2210 static JSBool
2211 date_toString(JSContext *cx, uintN argc, jsval *vp)
2212 {
2213 jsdouble utctime;
2214
2215 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &utctime))
2216 return JS_FALSE;
2217 return date_format(cx, utctime, FORMATSPEC_FULL, vp);
2218 }
2219
2220 #ifdef JS_TRACER
2221 static jsval FASTCALL
2222 date_valueOf_tn(JSContext* cx, JSObject* obj, JSString* str)
2223 {
2224 JS_ASSERT(JS_InstanceOf(cx, obj, &js_DateClass, NULL));
2225 jsdouble t = *JSVAL_TO_DOUBLE(obj->fslots[JSSLOT_UTC_TIME]);
2226
2227 JSString* number_str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]);
2228 jsval v;
2229 if (js_EqualStrings(str, number_str)) {
2230 if (!js_NewNumberInRootedValue(cx, t, &v))
2231 return JSVAL_ERROR_COOKIE;
2232 } else {
2233 if (!date_format(cx, t, FORMATSPEC_FULL, &v))
2234 return JSVAL_ERROR_COOKIE;
2235 }
2236 return v;
2237 }
2238 #endif
2239
2240 static JSBool
2241 date_valueOf(JSContext *cx, uintN argc, jsval *vp)
2242 {
2243 JSString *str, *number_str;
2244
2245 /* It is an error to call date_valueOf on a non-date object, but we don't
2246 * need to check for that explicitly here because every path calls
2247 * GetUTCTime, which does the check.
2248 */
2249
2250 /* If called directly with no arguments, convert to a time number. */
2251 if (argc == 0)
2252 return date_getTime(cx, argc, vp);
2253
2254 /* Convert to number only if the hint was given, otherwise favor string. */
2255 str = js_ValueToString(cx, vp[2]);
2256 if (!str)
2257 return JS_FALSE;
2258 number_str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]);
2259 if (js_EqualStrings(str, number_str))
2260 return date_getTime(cx, argc, vp);
2261 return date_toString(cx, argc, vp);
2262 }
2263
2264 // Don't really need an argument here, but we don't support arg-less builtins
2265 JS_DEFINE_TRCINFO_1(date_now,
2266 (1, (static, DOUBLE, date_now_tn, CONTEXT, 0, 0)))
2267
2268 static JSFunctionSpec date_static_methods[] = {
2269 JS_FN("UTC", date_UTC, MAXARGS,0),
2270 JS_FN("parse", date_parse, 1,0),
2271 JS_TN("now", date_now, 0,0, &date_now_trcinfo),
2272 JS_FS_END
2273 };
2274
2275 JS_DEFINE_TRCINFO_1(date_valueOf,
2276 (3, (static, JSVAL_RETRY, date_valueOf_tn, CONTEXT, THIS, STRING, 0, 0)))
2277
2278 static JSFunctionSpec date_methods[] = {
2279 JS_FN("getTime", date_getTime, 0,0),
2280 JS_FN("getTimezoneOffset", date_getTimezoneOffset, 0,0),
2281 JS_FN("getYear", date_getYear, 0,0),
2282 JS_FN("getFullYear", date_getFullYear, 0,0),
2283 JS_FN("getUTCFullYear", date_getUTCFullYear, 0,0),
2284 JS_FN("getMonth", date_getMonth, 0,0),
2285 JS_FN("getUTCMonth", date_getUTCMonth, 0,0),
2286 JS_FN("getDate", date_getDate, 0,0),
2287 JS_FN("getUTCDate", date_getUTCDate, 0,0),
2288 JS_FN("getDay", date_getDay, 0,0),
2289 JS_FN("getUTCDay", date_getUTCDay, 0,0),
2290 JS_FN("getHours", date_getHours, 0,0),
2291 JS_FN("getUTCHours", date_getUTCHours, 0,0),
2292 JS_FN("getMinutes", date_getMinutes, 0,0),
2293 JS_FN("getUTCMinutes", date_getUTCMinutes, 0,0),
2294 JS_FN("getSeconds", date_getUTCSeconds, 0,0),
2295 JS_FN("getUTCSeconds", date_getUTCSeconds, 0,0),
2296 JS_FN("getMilliseconds", date_getUTCMilliseconds, 0,0),
2297 JS_FN("getUTCMilliseconds", date_getUTCMilliseconds, 0,0),
2298 JS_FN("setTime", date_setTime, 1,0),
2299 JS_FN("setYear", date_setYear, 1,0),
2300 JS_FN("setFullYear", date_setFullYear, 3,0),
2301 JS_FN("setUTCFullYear", date_setUTCFullYear, 3,0),
2302 JS_FN("setMonth", date_setMonth, 2,0),
2303 JS_FN("setUTCMonth", date_setUTCMonth, 2,0),
2304 JS_FN("setDate", date_setDate, 1,0),
2305 JS_FN("setUTCDate", date_setUTCDate, 1,0),
2306 JS_FN("setHours", date_setHours, 4,0),
2307 JS_FN("setUTCHours", date_setUTCHours, 4,0),
2308 JS_FN("setMinutes", date_setMinutes, 3,0),
2309 JS_FN("setUTCMinutes", date_setUTCMinutes, 3,0),
2310 JS_FN("setSeconds", date_setSeconds, 2,0),
2311 JS_FN("setUTCSeconds", date_setUTCSeconds, 2,0),
2312 JS_FN("setMilliseconds", date_setMilliseconds, 1,0),
2313 JS_FN("setUTCMilliseconds", date_setUTCMilliseconds, 1,0),
2314 JS_FN("toUTCString", date_toGMTString, 0,0),
2315 JS_FN(js_toLocaleString_str, date_toLocaleString, 0,0),
2316 JS_FN("toLocaleDateString", date_toLocaleDateString, 0,0),
2317 JS_FN("toLocaleTimeString", date_toLocaleTimeString, 0,0),
2318 JS_FN("toLocaleFormat", date_toLocaleFormat, 0,0),
2319 JS_FN("toDateString", date_toDateString, 0,0),
2320 JS_FN("toTimeString", date_toTimeString, 0,0),
2321 JS_FN("toISOString", date_toISOString, 0,0),
2322 JS_FN(js_toJSON_str, date_toISOString, 0,0),
2323
2324 #if JS_HAS_TOSOURCE
2325 JS_FN(js_toSource_str, date_toSource, 0,0),
2326 #endif
2327 JS_FN(js_toString_str, date_toString, 0,0),
2328 JS_TN(js_valueOf_str, date_valueOf, 0,0, &date_valueOf_trcinfo),
2329 JS_FS_END
2330 };
2331
2332 JSBool
2333 js_Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2334 {
2335 /* Date called as function. */
2336 if (!JS_IsConstructing(cx))
2337 return date_format(cx, NowAsMillis(), FORMATSPEC_FULL, rval);
2338
2339 /* Date called as constructor. */
2340 jsdouble d;
2341 if (argc == 0) {
2342 d = NowAsMillis();
2343 } else if (argc == 1) {
2344 if (!JSVAL_IS_STRING(argv[0])) {
2345 /* the argument is a millisecond number */
2346 d = js_ValueToNumber(cx, &argv[0]);
2347 if (JSVAL_IS_NULL(argv[0]))
2348 return JS_FALSE;
2349 d = TIMECLIP(d);
2350 } else {
2351 /* the argument is a string; parse it. */
2352 JSString *str = js_ValueToString(cx, argv[0]);
2353 if (!str)
2354 return JS_FALSE;
2355 argv[0] = STRING_TO_JSVAL(str);
2356
2357 if (!date_parseString(str, &d))
2358 d = js_NaN;
2359 else
2360 d = TIMECLIP(d);
2361 }
2362 } else {
2363 jsdouble msec_time;
2364 if (!date_msecFromArgs(cx, argc, argv, &msec_time))
2365 return JS_FALSE;
2366
2367 if (JSDOUBLE_IS_FINITE(msec_time)) {
2368 msec_time = UTC(msec_time);
2369 msec_time = TIMECLIP(msec_time);
2370 }
2371 d = msec_time;
2372 }
2373 return SetUTCTime(cx, obj, d);
2374 }
2375
2376 JSObject *
2377 js_InitDateClass(JSContext *cx, JSObject *obj)
2378 {
2379 JSObject *proto;
2380
2381 /* set static LocalTZA */
2382 LocalTZA = -(PRMJ_LocalGMTDifference() * msPerSecond);
2383 proto = js_InitClass(cx, obj, NULL, &js_DateClass, js_Date, MAXARGS,
2384 NULL, date_methods, NULL, date_static_methods);
2385 if (!proto)
2386 return NULL;
2387
2388 SetDateToNaN(cx, proto);
2389
2390 /* Alias toUTCString with toGMTString. (ECMA B.2.6) */
2391 if (!JS_AliasProperty(cx, proto, "toUTCString", "toGMTString"))
2392 return NULL;
2393
2394 return proto;
2395 }
2396
2397 JS_FRIEND_API(JSObject *)
2398 js_NewDateObjectMsec(JSContext *cx, jsdouble msec_time)
2399 {
2400 JSObject *obj = js_NewObject(cx, &js_DateClass, NULL, NULL);
2401 if (!obj || !SetUTCTime(cx, obj, msec_time))
2402 return NULL;
2403 return obj;
2404 }
2405
2406 JS_FRIEND_API(JSObject *)
2407 js_NewDateObject(JSContext* cx, int year, int mon, int mday,
2408 int hour, int min, int sec)
2409 {
2410 JSObject *obj;
2411 jsdouble msec_time;
2412
2413 JS_ASSERT(mon < 12);
2414 msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
2415 obj = js_NewDateObjectMsec(cx, UTC(msec_time));
2416 return obj;
2417 }
2418
2419 JS_FRIEND_API(JSBool)
2420 js_DateIsValid(JSContext *cx, JSObject* obj)
2421 {
2422 jsdouble utctime;
2423 return GetUTCTime(cx, obj, NULL, &utctime) && !JSDOUBLE_IS_NaN(utctime);
2424 }
2425
2426 JS_FRIEND_API(int)
2427 js_DateGetYear(JSContext *cx, JSObject* obj)
2428 {
2429 jsdouble localtime;
2430
2431 /* Preserve legacy API behavior of returning 0 for invalid dates. */
2432 if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) ||
2433 JSDOUBLE_IS_NaN(localtime)) {
2434 return 0;
2435 }
2436
2437 return (int) YearFromTime(localtime);
2438 }
2439
2440 JS_FRIEND_API(int)
2441 js_DateGetMonth(JSContext *cx, JSObject* obj)
2442 {
2443 jsdouble localtime;
2444
2445 if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) ||
2446 JSDOUBLE_IS_NaN(localtime)) {
2447 return 0;
2448 }
2449
2450 return (int) MonthFromTime(localtime);
2451 }
2452
2453 JS_FRIEND_API(int)
2454 js_DateGetDate(JSContext *cx, JSObject* obj)
2455 {
2456 jsdouble localtime;
2457
2458 if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) ||
2459 JSDOUBLE_IS_NaN(localtime)) {
2460 return 0;
2461 }
2462
2463 return (int) DateFromTime(localtime);
2464 }
2465
2466 JS_FRIEND_API(int)
2467 js_DateGetHours(JSContext *cx, JSObject* obj)
2468 {
2469 jsdouble localtime;
2470
2471 if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) ||
2472 JSDOUBLE_IS_NaN(localtime)) {
2473 return 0;
2474 }
2475
2476 return (int) HourFromTime(localtime);
2477 }
2478
2479 JS_FRIEND_API(int)
2480 js_DateGetMinutes(JSContext *cx, JSObject* obj)
2481 {
2482 jsdouble localtime;
2483
2484 if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime) ||
2485 JSDOUBLE_IS_NaN(localtime)) {
2486 return 0;
2487 }
2488
2489 return (int) MinFromTime(localtime);
2490 }
2491
2492 JS_FRIEND_API(int)
2493 js_DateGetSeconds(JSContext *cx, JSObject* obj)
2494 {
2495 jsdouble utctime;
2496
2497 if (!GetUTCTime(cx, obj, NULL, &utctime) || JSDOUBLE_IS_NaN(utctime))
2498 return 0;
2499
2500 return (int) SecFromTime(utctime);
2501 }
2502
2503 JS_FRIEND_API(void)
2504 js_DateSetYear(JSContext *cx, JSObject *obj, int year)
2505 {
2506 jsdouble local;
2507
2508 if (!GetAndCacheLocalTime(cx, obj, NULL, &local))
2509 return;
2510
2511 /* reset date if it was NaN */
2512 if (JSDOUBLE_IS_NaN(local))
2513 local = 0;
2514
2515 local = date_msecFromDate(year,
2516 MonthFromTime(local),
2517 DateFromTime(local),
2518 HourFromTime(local),
2519 MinFromTime(local),
2520 SecFromTime(local),
2521 msFromTime(local));
2522
2523 /* SetUTCTime also invalidates local time cache. */
2524 SetUTCTime(cx, obj, UTC(local));
2525 }
2526
2527 JS_FRIEND_API(void)
2528 js_DateSetMonth(JSContext *cx, JSObject *obj, int month)
2529 {
2530 jsdouble local;
2531
2532 JS_ASSERT(month < 12);
2533
2534 if (!GetAndCacheLocalTime(cx, obj, NULL, &local))
2535 return;
2536
2537 /* bail if date was NaN */
2538 if (JSDOUBLE_IS_NaN(local))
2539 return;
2540
2541 local = date_msecFromDate(YearFromTime(local),
2542 month,
2543 DateFromTime(local),
2544 HourFromTime(local),
2545 MinFromTime(local),
2546 SecFromTime(local),
2547 msFromTime(local));
2548 SetUTCTime(cx, obj, UTC(local));
2549 }
2550
2551 JS_FRIEND_API(void)
2552 js_DateSetDate(JSContext *cx, JSObject *obj, int date)
2553 {
2554 jsdouble local;
2555
2556 if (!GetAndCacheLocalTime(cx, obj, NULL, &local))
2557 return;
2558
2559 if (JSDOUBLE_IS_NaN(local))
2560 return;
2561
2562 local = date_msecFromDate(YearFromTime(local),
2563 MonthFromTime(local),
2564 date,
2565 HourFromTime(local),
2566 MinFromTime(local),
2567 SecFromTime(local),
2568 msFromTime(local));
2569 SetUTCTime(cx, obj, UTC(local));
2570 }
2571
2572 JS_FRIEND_API(void)
2573 js_DateSetHours(JSContext *cx, JSObject *obj, int hours)
2574 {
2575 jsdouble local;
2576
2577 if (!GetAndCacheLocalTime(cx, obj, NULL, &local))
2578 return;
2579
2580 if (JSDOUBLE_IS_NaN(local))
2581 return;
2582 local = date_msecFromDate(YearFromTime(local),
2583 MonthFromTime(local),
2584 DateFromTime(local),
2585 hours,
2586 MinFromTime(local),
2587 SecFromTime(local),
2588 msFromTime(local));
2589 SetUTCTime(cx, obj, UTC(local));
2590 }
2591
2592 JS_FRIEND_API(void)
2593 js_DateSetMinutes(JSContext *cx, JSObject *obj, int minutes)
2594 {
2595 jsdouble local;
2596
2597 if (!GetAndCacheLocalTime(cx, obj, NULL, &local))
2598 return;
2599
2600 if (JSDOUBLE_IS_NaN(local))
2601 return;
2602 local = date_msecFromDate(YearFromTime(local),
2603 MonthFromTime(local),
2604 DateFromTime(local),
2605 HourFromTime(local),
2606 minutes,
2607 SecFromTime(local),
2608 msFromTime(local));
2609 SetUTCTime(cx, obj, UTC(local));
2610 }
2611
2612 JS_FRIEND_API(void)
2613 js_DateSetSeconds(JSContext *cx, JSObject *obj, int seconds)
2614 {
2615 jsdouble local;
2616
2617 if (!GetAndCacheLocalTime(cx, obj, NULL, &local))
2618 return;
2619
2620 if (JSDOUBLE_IS_NaN(local))
2621 return;
2622 local = date_msecFromDate(YearFromTime(local),
2623 MonthFromTime(local),
2624 DateFromTime(local),
2625 HourFromTime(local),
2626 MinFromTime(local),
2627 seconds,
2628 msFromTime(local));
2629 SetUTCTime(cx, obj, UTC(local));
2630 }
2631
2632 JS_FRIEND_API(jsdouble)
2633 js_DateGetMsecSinceEpoch(JSContext *cx, JSObject *obj)
2634 {
2635 jsdouble utctime;
2636 if (!GetUTCTime(cx, obj, NULL, &utctime))
2637 return 0;
2638 return utctime;
2639 }
2640
2641 #ifdef JS_THREADSAFE
2642 #include "prinrval.h"
2643
2644 JS_FRIEND_API(uint32)
2645 js_IntervalNow()
2646 {
2647 return uint32(PR_IntervalToMilliseconds(PR_IntervalNow()));
2648 }
2649
2650 #else /* !JS_THREADSAFE */
2651
2652 JS_FRIEND_API(uint32)
2653 js_IntervalNow()
2654 {
2655 return uint32(PRMJ_Now() / PRMJ_USEC_PER_MSEC);
2656 }
2657 #endif

  ViewVC Help
Powered by ViewVC 1.1.24