|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
2 * vim: set ts=8 sts=4 et sw=4 tw=99: |
|
3 * This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 /* |
|
8 * JS date methods. |
|
9 * |
|
10 * "For example, OS/360 devotes 26 bytes of the permanently |
|
11 * resident date-turnover routine to the proper handling of |
|
12 * December 31 on leap years (when it is Day 366). That |
|
13 * might have been left to the operator." |
|
14 * |
|
15 * Frederick Brooks, 'The Second-System Effect'. |
|
16 */ |
|
17 |
|
18 #include "jsdate.h" |
|
19 |
|
20 #include "mozilla/ArrayUtils.h" |
|
21 #include "mozilla/FloatingPoint.h" |
|
22 |
|
23 #include <ctype.h> |
|
24 #include <math.h> |
|
25 #include <string.h> |
|
26 |
|
27 #include "jsapi.h" |
|
28 #include "jscntxt.h" |
|
29 #include "jsnum.h" |
|
30 #include "jsobj.h" |
|
31 #include "jsprf.h" |
|
32 #include "jsstr.h" |
|
33 #include "jstypes.h" |
|
34 #include "jsutil.h" |
|
35 #include "prmjtime.h" |
|
36 |
|
37 #include "js/Date.h" |
|
38 #include "vm/DateTime.h" |
|
39 #include "vm/GlobalObject.h" |
|
40 #include "vm/Interpreter.h" |
|
41 #include "vm/NumericConversions.h" |
|
42 #include "vm/String.h" |
|
43 #include "vm/StringBuffer.h" |
|
44 |
|
45 #include "jsobjinlines.h" |
|
46 |
|
47 using namespace js; |
|
48 using namespace js::types; |
|
49 |
|
50 using mozilla::ArrayLength; |
|
51 using mozilla::IsFinite; |
|
52 using mozilla::IsNaN; |
|
53 using JS::GenericNaN; |
|
54 |
|
55 /* |
|
56 * The JS 'Date' object is patterned after the Java 'Date' object. |
|
57 * Here is a script: |
|
58 * |
|
59 * today = new Date(); |
|
60 * |
|
61 * print(today.toLocaleString()); |
|
62 * |
|
63 * weekDay = today.getDay(); |
|
64 * |
|
65 * |
|
66 * These Java (and ECMA-262) methods are supported: |
|
67 * |
|
68 * UTC |
|
69 * getDate (getUTCDate) |
|
70 * getDay (getUTCDay) |
|
71 * getHours (getUTCHours) |
|
72 * getMinutes (getUTCMinutes) |
|
73 * getMonth (getUTCMonth) |
|
74 * getSeconds (getUTCSeconds) |
|
75 * getMilliseconds (getUTCMilliseconds) |
|
76 * getTime |
|
77 * getTimezoneOffset |
|
78 * getYear |
|
79 * getFullYear (getUTCFullYear) |
|
80 * parse |
|
81 * setDate (setUTCDate) |
|
82 * setHours (setUTCHours) |
|
83 * setMinutes (setUTCMinutes) |
|
84 * setMonth (setUTCMonth) |
|
85 * setSeconds (setUTCSeconds) |
|
86 * setMilliseconds (setUTCMilliseconds) |
|
87 * setTime |
|
88 * setYear (setFullYear, setUTCFullYear) |
|
89 * toGMTString (toUTCString) |
|
90 * toLocaleString |
|
91 * toString |
|
92 * |
|
93 * |
|
94 * These Java methods are not supported |
|
95 * |
|
96 * setDay |
|
97 * before |
|
98 * after |
|
99 * equals |
|
100 * hashCode |
|
101 */ |
|
102 |
|
103 static inline double |
|
104 Day(double t) |
|
105 { |
|
106 return floor(t / msPerDay); |
|
107 } |
|
108 |
|
109 static double |
|
110 TimeWithinDay(double t) |
|
111 { |
|
112 double result = fmod(t, msPerDay); |
|
113 if (result < 0) |
|
114 result += msPerDay; |
|
115 return result; |
|
116 } |
|
117 |
|
118 /* ES5 15.9.1.3. */ |
|
119 static inline bool |
|
120 IsLeapYear(double year) |
|
121 { |
|
122 JS_ASSERT(ToInteger(year) == year); |
|
123 return fmod(year, 4) == 0 && (fmod(year, 100) != 0 || fmod(year, 400) == 0); |
|
124 } |
|
125 |
|
126 static inline double |
|
127 DaysInYear(double year) |
|
128 { |
|
129 if (!IsFinite(year)) |
|
130 return GenericNaN(); |
|
131 return IsLeapYear(year) ? 366 : 365; |
|
132 } |
|
133 |
|
134 static inline double |
|
135 DayFromYear(double y) |
|
136 { |
|
137 return 365 * (y - 1970) + |
|
138 floor((y - 1969) / 4.0) - |
|
139 floor((y - 1901) / 100.0) + |
|
140 floor((y - 1601) / 400.0); |
|
141 } |
|
142 |
|
143 static inline double |
|
144 TimeFromYear(double y) |
|
145 { |
|
146 return DayFromYear(y) * msPerDay; |
|
147 } |
|
148 |
|
149 static double |
|
150 YearFromTime(double t) |
|
151 { |
|
152 if (!IsFinite(t)) |
|
153 return GenericNaN(); |
|
154 |
|
155 JS_ASSERT(ToInteger(t) == t); |
|
156 |
|
157 double y = floor(t / (msPerDay * 365.2425)) + 1970; |
|
158 double t2 = TimeFromYear(y); |
|
159 |
|
160 /* |
|
161 * Adjust the year if the approximation was wrong. Since the year was |
|
162 * computed using the average number of ms per year, it will usually |
|
163 * be wrong for dates within several hours of a year transition. |
|
164 */ |
|
165 if (t2 > t) { |
|
166 y--; |
|
167 } else { |
|
168 if (t2 + msPerDay * DaysInYear(y) <= t) |
|
169 y++; |
|
170 } |
|
171 return y; |
|
172 } |
|
173 |
|
174 static inline int |
|
175 DaysInFebruary(double year) |
|
176 { |
|
177 return IsLeapYear(year) ? 29 : 28; |
|
178 } |
|
179 |
|
180 /* ES5 15.9.1.4. */ |
|
181 static inline double |
|
182 DayWithinYear(double t, double year) |
|
183 { |
|
184 JS_ASSERT_IF(IsFinite(t), YearFromTime(t) == year); |
|
185 return Day(t) - DayFromYear(year); |
|
186 } |
|
187 |
|
188 static double |
|
189 MonthFromTime(double t) |
|
190 { |
|
191 if (!IsFinite(t)) |
|
192 return GenericNaN(); |
|
193 |
|
194 double year = YearFromTime(t); |
|
195 double d = DayWithinYear(t, year); |
|
196 |
|
197 int step; |
|
198 if (d < (step = 31)) |
|
199 return 0; |
|
200 if (d < (step += DaysInFebruary(year))) |
|
201 return 1; |
|
202 if (d < (step += 31)) |
|
203 return 2; |
|
204 if (d < (step += 30)) |
|
205 return 3; |
|
206 if (d < (step += 31)) |
|
207 return 4; |
|
208 if (d < (step += 30)) |
|
209 return 5; |
|
210 if (d < (step += 31)) |
|
211 return 6; |
|
212 if (d < (step += 31)) |
|
213 return 7; |
|
214 if (d < (step += 30)) |
|
215 return 8; |
|
216 if (d < (step += 31)) |
|
217 return 9; |
|
218 if (d < (step += 30)) |
|
219 return 10; |
|
220 return 11; |
|
221 } |
|
222 |
|
223 /* ES5 15.9.1.5. */ |
|
224 static double |
|
225 DateFromTime(double t) |
|
226 { |
|
227 if (!IsFinite(t)) |
|
228 return GenericNaN(); |
|
229 |
|
230 double year = YearFromTime(t); |
|
231 double d = DayWithinYear(t, year); |
|
232 |
|
233 int next; |
|
234 if (d <= (next = 30)) |
|
235 return d + 1; |
|
236 int step = next; |
|
237 if (d <= (next += DaysInFebruary(year))) |
|
238 return d - step; |
|
239 step = next; |
|
240 if (d <= (next += 31)) |
|
241 return d - step; |
|
242 step = next; |
|
243 if (d <= (next += 30)) |
|
244 return d - step; |
|
245 step = next; |
|
246 if (d <= (next += 31)) |
|
247 return d - step; |
|
248 step = next; |
|
249 if (d <= (next += 30)) |
|
250 return d - step; |
|
251 step = next; |
|
252 if (d <= (next += 31)) |
|
253 return d - step; |
|
254 step = next; |
|
255 if (d <= (next += 31)) |
|
256 return d - step; |
|
257 step = next; |
|
258 if (d <= (next += 30)) |
|
259 return d - step; |
|
260 step = next; |
|
261 if (d <= (next += 31)) |
|
262 return d - step; |
|
263 step = next; |
|
264 if (d <= (next += 30)) |
|
265 return d - step; |
|
266 step = next; |
|
267 return d - step; |
|
268 } |
|
269 |
|
270 /* ES5 15.9.1.6. */ |
|
271 static int |
|
272 WeekDay(double t) |
|
273 { |
|
274 /* |
|
275 * We can't assert TimeClip(t) == t because we call this function with |
|
276 * local times, which can be offset outside TimeClip's permitted range. |
|
277 */ |
|
278 JS_ASSERT(ToInteger(t) == t); |
|
279 int result = (int(Day(t)) + 4) % 7; |
|
280 if (result < 0) |
|
281 result += 7; |
|
282 return result; |
|
283 } |
|
284 |
|
285 static inline int |
|
286 DayFromMonth(int month, bool isLeapYear) |
|
287 { |
|
288 /* |
|
289 * The following array contains the day of year for the first day of |
|
290 * each month, where index 0 is January, and day 0 is January 1. |
|
291 */ |
|
292 static const int firstDayOfMonth[2][13] = { |
|
293 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, |
|
294 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366} |
|
295 }; |
|
296 |
|
297 JS_ASSERT(0 <= month && month <= 12); |
|
298 return firstDayOfMonth[isLeapYear][month]; |
|
299 } |
|
300 |
|
301 template<typename T> |
|
302 static inline int |
|
303 DayFromMonth(T month, bool isLeapYear) MOZ_DELETE; |
|
304 |
|
305 /* ES5 15.9.1.12 (out of order to accommodate DaylightSavingTA). */ |
|
306 static double |
|
307 MakeDay(double year, double month, double date) |
|
308 { |
|
309 /* Step 1. */ |
|
310 if (!IsFinite(year) || !IsFinite(month) || !IsFinite(date)) |
|
311 return GenericNaN(); |
|
312 |
|
313 /* Steps 2-4. */ |
|
314 double y = ToInteger(year); |
|
315 double m = ToInteger(month); |
|
316 double dt = ToInteger(date); |
|
317 |
|
318 /* Step 5. */ |
|
319 double ym = y + floor(m / 12); |
|
320 |
|
321 /* Step 6. */ |
|
322 int mn = int(fmod(m, 12.0)); |
|
323 if (mn < 0) |
|
324 mn += 12; |
|
325 |
|
326 /* Steps 7-8. */ |
|
327 bool leap = IsLeapYear(ym); |
|
328 |
|
329 double yearday = floor(TimeFromYear(ym) / msPerDay); |
|
330 double monthday = DayFromMonth(mn, leap); |
|
331 |
|
332 return yearday + monthday + dt - 1; |
|
333 } |
|
334 |
|
335 /* ES5 15.9.1.13 (out of order to accommodate DaylightSavingTA). */ |
|
336 static inline double |
|
337 MakeDate(double day, double time) |
|
338 { |
|
339 /* Step 1. */ |
|
340 if (!IsFinite(day) || !IsFinite(time)) |
|
341 return GenericNaN(); |
|
342 |
|
343 /* Step 2. */ |
|
344 return day * msPerDay + time; |
|
345 } |
|
346 |
|
347 JS_PUBLIC_API(double) |
|
348 JS::MakeDate(double year, unsigned month, unsigned day) |
|
349 { |
|
350 return TimeClip(::MakeDate(MakeDay(year, month, day), 0)); |
|
351 } |
|
352 |
|
353 JS_PUBLIC_API(double) |
|
354 JS::YearFromTime(double time) |
|
355 { |
|
356 return ::YearFromTime(time); |
|
357 } |
|
358 |
|
359 JS_PUBLIC_API(double) |
|
360 JS::MonthFromTime(double time) |
|
361 { |
|
362 return ::MonthFromTime(time); |
|
363 } |
|
364 |
|
365 JS_PUBLIC_API(double) |
|
366 JS::DayFromTime(double time) |
|
367 { |
|
368 return DateFromTime(time); |
|
369 } |
|
370 |
|
371 /* |
|
372 * Find a year for which any given date will fall on the same weekday. |
|
373 * |
|
374 * This function should be used with caution when used other than |
|
375 * for determining DST; it hasn't been proven not to produce an |
|
376 * incorrect year for times near year boundaries. |
|
377 */ |
|
378 static int |
|
379 EquivalentYearForDST(int year) |
|
380 { |
|
381 /* |
|
382 * Years and leap years on which Jan 1 is a Sunday, Monday, etc. |
|
383 * |
|
384 * yearStartingWith[0][i] is an example non-leap year where |
|
385 * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc. |
|
386 * |
|
387 * yearStartingWith[1][i] is an example leap year where |
|
388 * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc. |
|
389 */ |
|
390 static const int yearStartingWith[2][7] = { |
|
391 {1978, 1973, 1974, 1975, 1981, 1971, 1977}, |
|
392 {1984, 1996, 1980, 1992, 1976, 1988, 1972} |
|
393 }; |
|
394 |
|
395 int day = int(DayFromYear(year) + 4) % 7; |
|
396 if (day < 0) |
|
397 day += 7; |
|
398 |
|
399 return yearStartingWith[IsLeapYear(year)][day]; |
|
400 } |
|
401 |
|
402 /* ES5 15.9.1.8. */ |
|
403 static double |
|
404 DaylightSavingTA(double t, DateTimeInfo *dtInfo) |
|
405 { |
|
406 if (!IsFinite(t)) |
|
407 return GenericNaN(); |
|
408 |
|
409 /* |
|
410 * If earlier than 1970 or after 2038, potentially beyond the ken of |
|
411 * many OSes, map it to an equivalent year before asking. |
|
412 */ |
|
413 if (t < 0.0 || t > 2145916800000.0) { |
|
414 int year = EquivalentYearForDST(int(YearFromTime(t))); |
|
415 double day = MakeDay(year, MonthFromTime(t), DateFromTime(t)); |
|
416 t = MakeDate(day, TimeWithinDay(t)); |
|
417 } |
|
418 |
|
419 int64_t utcMilliseconds = static_cast<int64_t>(t); |
|
420 int64_t offsetMilliseconds = dtInfo->getDSTOffsetMilliseconds(utcMilliseconds); |
|
421 return static_cast<double>(offsetMilliseconds); |
|
422 } |
|
423 |
|
424 static double |
|
425 AdjustTime(double date, DateTimeInfo *dtInfo) |
|
426 { |
|
427 double t = DaylightSavingTA(date, dtInfo) + dtInfo->localTZA(); |
|
428 t = (dtInfo->localTZA() >= 0) ? fmod(t, msPerDay) : -fmod(msPerDay - t, msPerDay); |
|
429 return t; |
|
430 } |
|
431 |
|
432 /* ES5 15.9.1.9. */ |
|
433 static double |
|
434 LocalTime(double t, DateTimeInfo *dtInfo) |
|
435 { |
|
436 return t + AdjustTime(t, dtInfo); |
|
437 } |
|
438 |
|
439 static double |
|
440 UTC(double t, DateTimeInfo *dtInfo) |
|
441 { |
|
442 return t - AdjustTime(t - dtInfo->localTZA(), dtInfo); |
|
443 } |
|
444 |
|
445 /* ES5 15.9.1.10. */ |
|
446 static double |
|
447 HourFromTime(double t) |
|
448 { |
|
449 double result = fmod(floor(t/msPerHour), HoursPerDay); |
|
450 if (result < 0) |
|
451 result += HoursPerDay; |
|
452 return result; |
|
453 } |
|
454 |
|
455 static double |
|
456 MinFromTime(double t) |
|
457 { |
|
458 double result = fmod(floor(t / msPerMinute), MinutesPerHour); |
|
459 if (result < 0) |
|
460 result += MinutesPerHour; |
|
461 return result; |
|
462 } |
|
463 |
|
464 static double |
|
465 SecFromTime(double t) |
|
466 { |
|
467 double result = fmod(floor(t / msPerSecond), SecondsPerMinute); |
|
468 if (result < 0) |
|
469 result += SecondsPerMinute; |
|
470 return result; |
|
471 } |
|
472 |
|
473 static double |
|
474 msFromTime(double t) |
|
475 { |
|
476 double result = fmod(t, msPerSecond); |
|
477 if (result < 0) |
|
478 result += msPerSecond; |
|
479 return result; |
|
480 } |
|
481 |
|
482 /* ES5 15.9.1.11. */ |
|
483 static double |
|
484 MakeTime(double hour, double min, double sec, double ms) |
|
485 { |
|
486 /* Step 1. */ |
|
487 if (!IsFinite(hour) || |
|
488 !IsFinite(min) || |
|
489 !IsFinite(sec) || |
|
490 !IsFinite(ms)) |
|
491 { |
|
492 return GenericNaN(); |
|
493 } |
|
494 |
|
495 /* Step 2. */ |
|
496 double h = ToInteger(hour); |
|
497 |
|
498 /* Step 3. */ |
|
499 double m = ToInteger(min); |
|
500 |
|
501 /* Step 4. */ |
|
502 double s = ToInteger(sec); |
|
503 |
|
504 /* Step 5. */ |
|
505 double milli = ToInteger(ms); |
|
506 |
|
507 /* Steps 6-7. */ |
|
508 return h * msPerHour + m * msPerMinute + s * msPerSecond + milli; |
|
509 } |
|
510 |
|
511 /** |
|
512 * end of ECMA 'support' functions |
|
513 */ |
|
514 |
|
515 static bool |
|
516 date_convert(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp) |
|
517 { |
|
518 JS_ASSERT(hint == JSTYPE_NUMBER || hint == JSTYPE_STRING || hint == JSTYPE_VOID); |
|
519 JS_ASSERT(obj->is<DateObject>()); |
|
520 |
|
521 return DefaultValue(cx, obj, (hint == JSTYPE_VOID) ? JSTYPE_STRING : hint, vp); |
|
522 } |
|
523 |
|
524 /* for use by date_parse */ |
|
525 |
|
526 static const char* const wtb[] = { |
|
527 "am", "pm", |
|
528 "monday", "tuesday", "wednesday", "thursday", "friday", |
|
529 "saturday", "sunday", |
|
530 "january", "february", "march", "april", "may", "june", |
|
531 "july", "august", "september", "october", "november", "december", |
|
532 "gmt", "ut", "utc", |
|
533 "est", "edt", |
|
534 "cst", "cdt", |
|
535 "mst", "mdt", |
|
536 "pst", "pdt" |
|
537 /* time zone table needs to be expanded */ |
|
538 }; |
|
539 |
|
540 static const int ttb[] = { |
|
541 -1, -2, 0, 0, 0, 0, 0, 0, 0, /* AM/PM */ |
|
542 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, |
|
543 10000 + 0, 10000 + 0, 10000 + 0, /* GMT/UT/UTC */ |
|
544 10000 + 5 * 60, 10000 + 4 * 60, /* EST/EDT */ |
|
545 10000 + 6 * 60, 10000 + 5 * 60, /* CST/CDT */ |
|
546 10000 + 7 * 60, 10000 + 6 * 60, /* MST/MDT */ |
|
547 10000 + 8 * 60, 10000 + 7 * 60 /* PST/PDT */ |
|
548 }; |
|
549 |
|
550 /* helper for date_parse */ |
|
551 static bool |
|
552 date_regionMatches(const char* s1, int s1off, const jschar* s2, int s2off, |
|
553 int count, int ignoreCase) |
|
554 { |
|
555 bool result = false; |
|
556 /* return true if matches, otherwise, false */ |
|
557 |
|
558 while (count > 0 && s1[s1off] && s2[s2off]) { |
|
559 if (ignoreCase) { |
|
560 if (unicode::ToLowerCase(s1[s1off]) != unicode::ToLowerCase(s2[s2off])) |
|
561 break; |
|
562 } else { |
|
563 if ((jschar)s1[s1off] != s2[s2off]) { |
|
564 break; |
|
565 } |
|
566 } |
|
567 s1off++; |
|
568 s2off++; |
|
569 count--; |
|
570 } |
|
571 |
|
572 if (count == 0) { |
|
573 result = true; |
|
574 } |
|
575 |
|
576 return result; |
|
577 } |
|
578 |
|
579 /* find UTC time from given date... no 1900 correction! */ |
|
580 static double |
|
581 date_msecFromDate(double year, double mon, double mday, double hour, |
|
582 double min, double sec, double msec) |
|
583 { |
|
584 return MakeDate(MakeDay(year, mon, mday), MakeTime(hour, min, sec, msec)); |
|
585 } |
|
586 |
|
587 /* compute the time in msec (unclipped) from the given args */ |
|
588 #define MAXARGS 7 |
|
589 |
|
590 static bool |
|
591 date_msecFromArgs(JSContext *cx, CallArgs args, double *rval) |
|
592 { |
|
593 unsigned loop; |
|
594 double array[MAXARGS]; |
|
595 double msec_time; |
|
596 |
|
597 for (loop = 0; loop < MAXARGS; loop++) { |
|
598 if (loop < args.length()) { |
|
599 double d; |
|
600 if (!ToNumber(cx, args[loop], &d)) |
|
601 return false; |
|
602 /* return NaN if any arg is not finite */ |
|
603 if (!IsFinite(d)) { |
|
604 *rval = GenericNaN(); |
|
605 return true; |
|
606 } |
|
607 array[loop] = ToInteger(d); |
|
608 } else { |
|
609 if (loop == 2) { |
|
610 array[loop] = 1; /* Default the date argument to 1. */ |
|
611 } else { |
|
612 array[loop] = 0; |
|
613 } |
|
614 } |
|
615 } |
|
616 |
|
617 /* adjust 2-digit years into the 20th century */ |
|
618 if (array[0] >= 0 && array[0] <= 99) |
|
619 array[0] += 1900; |
|
620 |
|
621 msec_time = date_msecFromDate(array[0], array[1], array[2], |
|
622 array[3], array[4], array[5], array[6]); |
|
623 *rval = msec_time; |
|
624 return true; |
|
625 } |
|
626 |
|
627 /* |
|
628 * See ECMA 15.9.4.[3-10]; |
|
629 */ |
|
630 static bool |
|
631 date_UTC(JSContext *cx, unsigned argc, Value *vp) |
|
632 { |
|
633 CallArgs args = CallArgsFromVp(argc, vp); |
|
634 |
|
635 double msec_time; |
|
636 if (!date_msecFromArgs(cx, args, &msec_time)) |
|
637 return false; |
|
638 |
|
639 msec_time = TimeClip(msec_time); |
|
640 |
|
641 args.rval().setNumber(msec_time); |
|
642 return true; |
|
643 } |
|
644 |
|
645 /* |
|
646 * Read and convert decimal digits from s[*i] into *result |
|
647 * while *i < limit. |
|
648 * |
|
649 * Succeed if any digits are converted. Advance *i only |
|
650 * as digits are consumed. |
|
651 */ |
|
652 static bool |
|
653 digits(size_t *result, const jschar *s, size_t *i, size_t limit) |
|
654 { |
|
655 size_t init = *i; |
|
656 *result = 0; |
|
657 while (*i < limit && |
|
658 ('0' <= s[*i] && s[*i] <= '9')) { |
|
659 *result *= 10; |
|
660 *result += (s[*i] - '0'); |
|
661 ++(*i); |
|
662 } |
|
663 return *i != init; |
|
664 } |
|
665 |
|
666 /* |
|
667 * Read and convert decimal digits to the right of a decimal point, |
|
668 * representing a fractional integer, from s[*i] into *result |
|
669 * while *i < limit. |
|
670 * |
|
671 * Succeed if any digits are converted. Advance *i only |
|
672 * as digits are consumed. |
|
673 */ |
|
674 static bool |
|
675 fractional(double *result, const jschar *s, size_t *i, size_t limit) |
|
676 { |
|
677 double factor = 0.1; |
|
678 size_t init = *i; |
|
679 *result = 0.0; |
|
680 while (*i < limit && |
|
681 ('0' <= s[*i] && s[*i] <= '9')) { |
|
682 *result += (s[*i] - '0') * factor; |
|
683 factor *= 0.1; |
|
684 ++(*i); |
|
685 } |
|
686 return *i != init; |
|
687 } |
|
688 |
|
689 /* |
|
690 * Read and convert exactly n decimal digits from s[*i] |
|
691 * to s[min(*i+n,limit)] into *result. |
|
692 * |
|
693 * Succeed if exactly n digits are converted. Advance *i only |
|
694 * on success. |
|
695 */ |
|
696 static bool |
|
697 ndigits(size_t n, size_t *result, const jschar *s, size_t* i, size_t limit) |
|
698 { |
|
699 size_t init = *i; |
|
700 |
|
701 if (digits(result, s, i, Min(limit, init+n))) |
|
702 return (*i - init) == n; |
|
703 |
|
704 *i = init; |
|
705 return false; |
|
706 } |
|
707 |
|
708 static int |
|
709 DaysInMonth(int year, int month) |
|
710 { |
|
711 bool leap = IsLeapYear(year); |
|
712 int result = int(DayFromMonth(month, leap) - DayFromMonth(month - 1, leap)); |
|
713 return result; |
|
714 } |
|
715 |
|
716 /* |
|
717 * Parse a string in one of the date-time formats given by the W3C |
|
718 * "NOTE-datetime" specification. These formats make up a restricted |
|
719 * profile of the ISO 8601 format. Quoted here: |
|
720 * |
|
721 * The formats are as follows. Exactly the components shown here |
|
722 * must be present, with exactly this punctuation. Note that the "T" |
|
723 * appears literally in the string, to indicate the beginning of the |
|
724 * time element, as specified in ISO 8601. |
|
725 * |
|
726 * Any combination of the date formats with the time formats is |
|
727 * allowed, and also either the date or the time can be missing. |
|
728 * |
|
729 * The specification is silent on the meaning when fields are |
|
730 * ommitted so the interpretations are a guess, but hopefully a |
|
731 * reasonable one. We default the month to January, the day to the |
|
732 * 1st, and hours minutes and seconds all to 0. If the date is |
|
733 * missing entirely then we assume 1970-01-01 so that the time can |
|
734 * be aded to a date later. If the time is missing then we assume |
|
735 * 00:00 UTC. If the time is present but the time zone field is |
|
736 * missing then we use local time. |
|
737 * |
|
738 * Date part: |
|
739 * |
|
740 * Year: |
|
741 * YYYY (eg 1997) |
|
742 * |
|
743 * Year and month: |
|
744 * YYYY-MM (eg 1997-07) |
|
745 * |
|
746 * Complete date: |
|
747 * YYYY-MM-DD (eg 1997-07-16) |
|
748 * |
|
749 * Time part: |
|
750 * |
|
751 * Hours and minutes: |
|
752 * Thh:mmTZD (eg T19:20+01:00) |
|
753 * |
|
754 * Hours, minutes and seconds: |
|
755 * Thh:mm:ssTZD (eg T19:20:30+01:00) |
|
756 * |
|
757 * Hours, minutes, seconds and a decimal fraction of a second: |
|
758 * Thh:mm:ss.sTZD (eg T19:20:30.45+01:00) |
|
759 * |
|
760 * where: |
|
761 * |
|
762 * YYYY = four-digit year or six digit year as +YYYYYY or -YYYYYY |
|
763 * MM = two-digit month (01=January, etc.) |
|
764 * DD = two-digit day of month (01 through 31) |
|
765 * hh = two digits of hour (00 through 23) (am/pm NOT allowed) |
|
766 * mm = two digits of minute (00 through 59) |
|
767 * ss = two digits of second (00 through 59) |
|
768 * s = one or more digits representing a decimal fraction of a second |
|
769 * TZD = time zone designator (Z or +hh:mm or -hh:mm or missing for local) |
|
770 */ |
|
771 |
|
772 static bool |
|
773 date_parseISOString(JSLinearString *str, double *result, DateTimeInfo *dtInfo) |
|
774 { |
|
775 double msec; |
|
776 |
|
777 const jschar *s; |
|
778 size_t limit; |
|
779 size_t i = 0; |
|
780 int tzMul = 1; |
|
781 int dateMul = 1; |
|
782 size_t year = 1970; |
|
783 size_t month = 1; |
|
784 size_t day = 1; |
|
785 size_t hour = 0; |
|
786 size_t min = 0; |
|
787 size_t sec = 0; |
|
788 double frac = 0; |
|
789 bool isLocalTime = false; |
|
790 size_t tzHour = 0; |
|
791 size_t tzMin = 0; |
|
792 |
|
793 #define PEEK(ch) (i < limit && s[i] == ch) |
|
794 |
|
795 #define NEED(ch) \ |
|
796 JS_BEGIN_MACRO \ |
|
797 if (i >= limit || s[i] != ch) { goto syntax; } else { ++i; } \ |
|
798 JS_END_MACRO |
|
799 |
|
800 #define DONE_DATE_UNLESS(ch) \ |
|
801 JS_BEGIN_MACRO \ |
|
802 if (i >= limit || s[i] != ch) { goto done_date; } else { ++i; } \ |
|
803 JS_END_MACRO |
|
804 |
|
805 #define DONE_UNLESS(ch) \ |
|
806 JS_BEGIN_MACRO \ |
|
807 if (i >= limit || s[i] != ch) { goto done; } else { ++i; } \ |
|
808 JS_END_MACRO |
|
809 |
|
810 #define NEED_NDIGITS(n, field) \ |
|
811 JS_BEGIN_MACRO \ |
|
812 if (!ndigits(n, &field, s, &i, limit)) { goto syntax; } \ |
|
813 JS_END_MACRO |
|
814 |
|
815 s = str->chars(); |
|
816 limit = str->length(); |
|
817 |
|
818 if (PEEK('+') || PEEK('-')) { |
|
819 if (PEEK('-')) |
|
820 dateMul = -1; |
|
821 ++i; |
|
822 NEED_NDIGITS(6, year); |
|
823 } else if (!PEEK('T')) { |
|
824 NEED_NDIGITS(4, year); |
|
825 } |
|
826 DONE_DATE_UNLESS('-'); |
|
827 NEED_NDIGITS(2, month); |
|
828 DONE_DATE_UNLESS('-'); |
|
829 NEED_NDIGITS(2, day); |
|
830 |
|
831 done_date: |
|
832 DONE_UNLESS('T'); |
|
833 NEED_NDIGITS(2, hour); |
|
834 NEED(':'); |
|
835 NEED_NDIGITS(2, min); |
|
836 |
|
837 if (PEEK(':')) { |
|
838 ++i; |
|
839 NEED_NDIGITS(2, sec); |
|
840 if (PEEK('.')) { |
|
841 ++i; |
|
842 if (!fractional(&frac, s, &i, limit)) |
|
843 goto syntax; |
|
844 } |
|
845 } |
|
846 |
|
847 if (PEEK('Z')) { |
|
848 ++i; |
|
849 } else if (PEEK('+') || PEEK('-')) { |
|
850 if (PEEK('-')) |
|
851 tzMul = -1; |
|
852 ++i; |
|
853 NEED_NDIGITS(2, tzHour); |
|
854 /* |
|
855 * Non-standard extension to the ISO date format (permitted by ES5): |
|
856 * allow "-0700" as a time zone offset, not just "-07:00". |
|
857 */ |
|
858 if (PEEK(':')) |
|
859 ++i; |
|
860 NEED_NDIGITS(2, tzMin); |
|
861 } else { |
|
862 isLocalTime = true; |
|
863 } |
|
864 |
|
865 done: |
|
866 if (year > 275943 // ceil(1e8/365) + 1970 |
|
867 || (month == 0 || month > 12) |
|
868 || (day == 0 || day > size_t(DaysInMonth(year,month))) |
|
869 || hour > 24 |
|
870 || ((hour == 24) && (min > 0 || sec > 0)) |
|
871 || min > 59 |
|
872 || sec > 59 |
|
873 || tzHour > 23 |
|
874 || tzMin > 59) |
|
875 goto syntax; |
|
876 |
|
877 if (i != limit) |
|
878 goto syntax; |
|
879 |
|
880 month -= 1; /* convert month to 0-based */ |
|
881 |
|
882 msec = date_msecFromDate(dateMul * (double)year, month, day, |
|
883 hour, min, sec, |
|
884 frac * 1000.0);; |
|
885 |
|
886 if (isLocalTime) { |
|
887 msec = UTC(msec, dtInfo); |
|
888 } else { |
|
889 msec -= ((tzMul) * ((tzHour * msPerHour) |
|
890 + (tzMin * msPerMinute))); |
|
891 } |
|
892 |
|
893 if (msec < -8.64e15 || msec > 8.64e15) |
|
894 goto syntax; |
|
895 |
|
896 *result = msec; |
|
897 |
|
898 return true; |
|
899 |
|
900 syntax: |
|
901 /* syntax error */ |
|
902 *result = 0; |
|
903 return false; |
|
904 |
|
905 #undef PEEK |
|
906 #undef NEED |
|
907 #undef DONE_UNLESS |
|
908 #undef NEED_NDIGITS |
|
909 } |
|
910 |
|
911 static bool |
|
912 date_parseString(JSLinearString *str, double *result, DateTimeInfo *dtInfo) |
|
913 { |
|
914 double msec; |
|
915 |
|
916 const jschar *s; |
|
917 size_t limit; |
|
918 size_t i = 0; |
|
919 int year = -1; |
|
920 int mon = -1; |
|
921 int mday = -1; |
|
922 int hour = -1; |
|
923 int min = -1; |
|
924 int sec = -1; |
|
925 int c = -1; |
|
926 int n = -1; |
|
927 int tzoffset = -1; |
|
928 int prevc = 0; |
|
929 bool seenplusminus = false; |
|
930 int temp; |
|
931 bool seenmonthname = false; |
|
932 |
|
933 if (date_parseISOString(str, result, dtInfo)) |
|
934 return true; |
|
935 |
|
936 s = str->chars(); |
|
937 limit = str->length(); |
|
938 if (limit == 0) |
|
939 goto syntax; |
|
940 while (i < limit) { |
|
941 c = s[i]; |
|
942 i++; |
|
943 if (c <= ' ' || c == ',' || c == '-') { |
|
944 if (c == '-' && '0' <= s[i] && s[i] <= '9') { |
|
945 prevc = c; |
|
946 } |
|
947 continue; |
|
948 } |
|
949 if (c == '(') { /* comments) */ |
|
950 int depth = 1; |
|
951 while (i < limit) { |
|
952 c = s[i]; |
|
953 i++; |
|
954 if (c == '(') depth++; |
|
955 else if (c == ')') |
|
956 if (--depth <= 0) |
|
957 break; |
|
958 } |
|
959 continue; |
|
960 } |
|
961 if ('0' <= c && c <= '9') { |
|
962 n = c - '0'; |
|
963 while (i < limit && '0' <= (c = s[i]) && c <= '9') { |
|
964 n = n * 10 + c - '0'; |
|
965 i++; |
|
966 } |
|
967 |
|
968 /* allow TZA before the year, so |
|
969 * 'Wed Nov 05 21:49:11 GMT-0800 1997' |
|
970 * works */ |
|
971 |
|
972 /* uses of seenplusminus allow : in TZA, so Java |
|
973 * no-timezone style of GMT+4:30 works |
|
974 */ |
|
975 |
|
976 if ((prevc == '+' || prevc == '-')/* && year>=0 */) { |
|
977 /* make ':' case below change tzoffset */ |
|
978 seenplusminus = true; |
|
979 |
|
980 /* offset */ |
|
981 if (n < 24) |
|
982 n = n * 60; /* EG. "GMT-3" */ |
|
983 else |
|
984 n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */ |
|
985 if (prevc == '+') /* plus means east of GMT */ |
|
986 n = -n; |
|
987 if (tzoffset != 0 && tzoffset != -1) |
|
988 goto syntax; |
|
989 tzoffset = n; |
|
990 } else if (prevc == '/' && mon >= 0 && mday >= 0 && year < 0) { |
|
991 if (c <= ' ' || c == ',' || c == '/' || i >= limit) |
|
992 year = n; |
|
993 else |
|
994 goto syntax; |
|
995 } else if (c == ':') { |
|
996 if (hour < 0) |
|
997 hour = /*byte*/ n; |
|
998 else if (min < 0) |
|
999 min = /*byte*/ n; |
|
1000 else |
|
1001 goto syntax; |
|
1002 } else if (c == '/') { |
|
1003 /* until it is determined that mon is the actual |
|
1004 month, keep it as 1-based rather than 0-based */ |
|
1005 if (mon < 0) |
|
1006 mon = /*byte*/ n; |
|
1007 else if (mday < 0) |
|
1008 mday = /*byte*/ n; |
|
1009 else |
|
1010 goto syntax; |
|
1011 } else if (i < limit && c != ',' && c > ' ' && c != '-' && c != '(') { |
|
1012 goto syntax; |
|
1013 } else if (seenplusminus && n < 60) { /* handle GMT-3:30 */ |
|
1014 if (tzoffset < 0) |
|
1015 tzoffset -= n; |
|
1016 else |
|
1017 tzoffset += n; |
|
1018 } else if (hour >= 0 && min < 0) { |
|
1019 min = /*byte*/ n; |
|
1020 } else if (prevc == ':' && min >= 0 && sec < 0) { |
|
1021 sec = /*byte*/ n; |
|
1022 } else if (mon < 0) { |
|
1023 mon = /*byte*/n; |
|
1024 } else if (mon >= 0 && mday < 0) { |
|
1025 mday = /*byte*/ n; |
|
1026 } else if (mon >= 0 && mday >= 0 && year < 0) { |
|
1027 year = n; |
|
1028 } else { |
|
1029 goto syntax; |
|
1030 } |
|
1031 prevc = 0; |
|
1032 } else if (c == '/' || c == ':' || c == '+' || c == '-') { |
|
1033 prevc = c; |
|
1034 } else { |
|
1035 size_t st = i - 1; |
|
1036 int k; |
|
1037 while (i < limit) { |
|
1038 c = s[i]; |
|
1039 if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'))) |
|
1040 break; |
|
1041 i++; |
|
1042 } |
|
1043 if (i <= st + 1) |
|
1044 goto syntax; |
|
1045 for (k = ArrayLength(wtb); --k >= 0;) |
|
1046 if (date_regionMatches(wtb[k], 0, s, st, i-st, 1)) { |
|
1047 int action = ttb[k]; |
|
1048 if (action != 0) { |
|
1049 if (action < 0) { |
|
1050 /* |
|
1051 * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as |
|
1052 * 12:30, instead of blindly adding 12 if PM. |
|
1053 */ |
|
1054 JS_ASSERT(action == -1 || action == -2); |
|
1055 if (hour > 12 || hour < 0) { |
|
1056 goto syntax; |
|
1057 } else { |
|
1058 if (action == -1 && hour == 12) { /* am */ |
|
1059 hour = 0; |
|
1060 } else if (action == -2 && hour != 12) { /* pm */ |
|
1061 hour += 12; |
|
1062 } |
|
1063 } |
|
1064 } else if (action <= 13) { /* month! */ |
|
1065 /* Adjust mon to be 1-based until the final values |
|
1066 for mon, mday and year are adjusted below */ |
|
1067 if (seenmonthname) { |
|
1068 goto syntax; |
|
1069 } |
|
1070 seenmonthname = true; |
|
1071 temp = /*byte*/ (action - 2) + 1; |
|
1072 |
|
1073 if (mon < 0) { |
|
1074 mon = temp; |
|
1075 } else if (mday < 0) { |
|
1076 mday = mon; |
|
1077 mon = temp; |
|
1078 } else if (year < 0) { |
|
1079 year = mon; |
|
1080 mon = temp; |
|
1081 } else { |
|
1082 goto syntax; |
|
1083 } |
|
1084 } else { |
|
1085 tzoffset = action - 10000; |
|
1086 } |
|
1087 } |
|
1088 break; |
|
1089 } |
|
1090 if (k < 0) |
|
1091 goto syntax; |
|
1092 prevc = 0; |
|
1093 } |
|
1094 } |
|
1095 if (year < 0 || mon < 0 || mday < 0) |
|
1096 goto syntax; |
|
1097 /* |
|
1098 Case 1. The input string contains an English month name. |
|
1099 The form of the string can be month f l, or f month l, or |
|
1100 f l month which each evaluate to the same date. |
|
1101 If f and l are both greater than or equal to 70, or |
|
1102 both less than 70, the date is invalid. |
|
1103 The year is taken to be the greater of the values f, l. |
|
1104 If the year is greater than or equal to 70 and less than 100, |
|
1105 it is considered to be the number of years after 1900. |
|
1106 Case 2. The input string is of the form "f/m/l" where f, m and l are |
|
1107 integers, e.g. 7/16/45. |
|
1108 Adjust the mon, mday and year values to achieve 100% MSIE |
|
1109 compatibility. |
|
1110 a. If 0 <= f < 70, f/m/l is interpreted as month/day/year. |
|
1111 i. If year < 100, it is the number of years after 1900 |
|
1112 ii. If year >= 100, it is the number of years after 0. |
|
1113 b. If 70 <= f < 100 |
|
1114 i. If m < 70, f/m/l is interpreted as |
|
1115 year/month/day where year is the number of years after |
|
1116 1900. |
|
1117 ii. If m >= 70, the date is invalid. |
|
1118 c. If f >= 100 |
|
1119 i. If m < 70, f/m/l is interpreted as |
|
1120 year/month/day where year is the number of years after 0. |
|
1121 ii. If m >= 70, the date is invalid. |
|
1122 */ |
|
1123 if (seenmonthname) { |
|
1124 if ((mday >= 70 && year >= 70) || (mday < 70 && year < 70)) { |
|
1125 goto syntax; |
|
1126 } |
|
1127 if (mday > year) { |
|
1128 temp = year; |
|
1129 year = mday; |
|
1130 mday = temp; |
|
1131 } |
|
1132 if (year >= 70 && year < 100) { |
|
1133 year += 1900; |
|
1134 } |
|
1135 } else if (mon < 70) { /* (a) month/day/year */ |
|
1136 if (year < 100) { |
|
1137 year += 1900; |
|
1138 } |
|
1139 } else if (mon < 100) { /* (b) year/month/day */ |
|
1140 if (mday < 70) { |
|
1141 temp = year; |
|
1142 year = mon + 1900; |
|
1143 mon = mday; |
|
1144 mday = temp; |
|
1145 } else { |
|
1146 goto syntax; |
|
1147 } |
|
1148 } else { /* (c) year/month/day */ |
|
1149 if (mday < 70) { |
|
1150 temp = year; |
|
1151 year = mon; |
|
1152 mon = mday; |
|
1153 mday = temp; |
|
1154 } else { |
|
1155 goto syntax; |
|
1156 } |
|
1157 } |
|
1158 mon -= 1; /* convert month to 0-based */ |
|
1159 if (sec < 0) |
|
1160 sec = 0; |
|
1161 if (min < 0) |
|
1162 min = 0; |
|
1163 if (hour < 0) |
|
1164 hour = 0; |
|
1165 |
|
1166 msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0); |
|
1167 |
|
1168 if (tzoffset == -1) { /* no time zone specified, have to use local */ |
|
1169 msec = UTC(msec, dtInfo); |
|
1170 } else { |
|
1171 msec += tzoffset * msPerMinute; |
|
1172 } |
|
1173 |
|
1174 *result = msec; |
|
1175 return true; |
|
1176 |
|
1177 syntax: |
|
1178 /* syntax error */ |
|
1179 *result = 0; |
|
1180 return false; |
|
1181 } |
|
1182 |
|
1183 static bool |
|
1184 date_parse(JSContext *cx, unsigned argc, Value *vp) |
|
1185 { |
|
1186 CallArgs args = CallArgsFromVp(argc, vp); |
|
1187 if (args.length() == 0) { |
|
1188 args.rval().setNaN(); |
|
1189 return true; |
|
1190 } |
|
1191 |
|
1192 JSString *str = ToString<CanGC>(cx, args[0]); |
|
1193 if (!str) |
|
1194 return false; |
|
1195 |
|
1196 JSLinearString *linearStr = str->ensureLinear(cx); |
|
1197 if (!linearStr) |
|
1198 return false; |
|
1199 |
|
1200 double result; |
|
1201 if (!date_parseString(linearStr, &result, &cx->runtime()->dateTimeInfo)) { |
|
1202 args.rval().setNaN(); |
|
1203 return true; |
|
1204 } |
|
1205 |
|
1206 result = TimeClip(result); |
|
1207 args.rval().setNumber(result); |
|
1208 return true; |
|
1209 } |
|
1210 |
|
1211 static inline double |
|
1212 NowAsMillis() |
|
1213 { |
|
1214 return (double) (PRMJ_Now() / PRMJ_USEC_PER_MSEC); |
|
1215 } |
|
1216 |
|
1217 static bool |
|
1218 date_now(JSContext *cx, unsigned argc, Value *vp) |
|
1219 { |
|
1220 CallArgs args = CallArgsFromVp(argc, vp); |
|
1221 args.rval().setDouble(NowAsMillis()); |
|
1222 return true; |
|
1223 } |
|
1224 |
|
1225 void |
|
1226 DateObject::setUTCTime(double t, Value *vp) |
|
1227 { |
|
1228 for (size_t ind = COMPONENTS_START_SLOT; ind < RESERVED_SLOTS; ind++) |
|
1229 setReservedSlot(ind, UndefinedValue()); |
|
1230 |
|
1231 setFixedSlot(UTC_TIME_SLOT, DoubleValue(t)); |
|
1232 if (vp) |
|
1233 vp->setDouble(t); |
|
1234 } |
|
1235 |
|
1236 void |
|
1237 DateObject::fillLocalTimeSlots(DateTimeInfo *dtInfo) |
|
1238 { |
|
1239 /* Check if the cache is already populated. */ |
|
1240 if (!getReservedSlot(LOCAL_TIME_SLOT).isUndefined() && |
|
1241 getReservedSlot(TZA_SLOT).toDouble() == dtInfo->localTZA()) |
|
1242 { |
|
1243 return; |
|
1244 } |
|
1245 |
|
1246 /* Remember timezone used to generate the local cache. */ |
|
1247 setReservedSlot(TZA_SLOT, DoubleValue(dtInfo->localTZA())); |
|
1248 |
|
1249 double utcTime = UTCTime().toNumber(); |
|
1250 |
|
1251 if (!IsFinite(utcTime)) { |
|
1252 for (size_t ind = COMPONENTS_START_SLOT; ind < RESERVED_SLOTS; ind++) |
|
1253 setReservedSlot(ind, DoubleValue(utcTime)); |
|
1254 return; |
|
1255 } |
|
1256 |
|
1257 double localTime = LocalTime(utcTime, dtInfo); |
|
1258 |
|
1259 setReservedSlot(LOCAL_TIME_SLOT, DoubleValue(localTime)); |
|
1260 |
|
1261 int year = (int) floor(localTime /(msPerDay * 365.2425)) + 1970; |
|
1262 double yearStartTime = TimeFromYear(year); |
|
1263 |
|
1264 /* Adjust the year in case the approximation was wrong, as in YearFromTime. */ |
|
1265 int yearDays; |
|
1266 if (yearStartTime > localTime) { |
|
1267 year--; |
|
1268 yearStartTime -= (msPerDay * DaysInYear(year)); |
|
1269 yearDays = DaysInYear(year); |
|
1270 } else { |
|
1271 yearDays = DaysInYear(year); |
|
1272 double nextStart = yearStartTime + (msPerDay * yearDays); |
|
1273 if (nextStart <= localTime) { |
|
1274 year++; |
|
1275 yearStartTime = nextStart; |
|
1276 yearDays = DaysInYear(year); |
|
1277 } |
|
1278 } |
|
1279 |
|
1280 setReservedSlot(LOCAL_YEAR_SLOT, Int32Value(year)); |
|
1281 |
|
1282 uint64_t yearTime = uint64_t(localTime - yearStartTime); |
|
1283 int yearSeconds = uint32_t(yearTime / 1000); |
|
1284 |
|
1285 int day = yearSeconds / int(SecondsPerDay); |
|
1286 |
|
1287 int step = -1, next = 30; |
|
1288 int month; |
|
1289 |
|
1290 do { |
|
1291 if (day <= next) { |
|
1292 month = 0; |
|
1293 break; |
|
1294 } |
|
1295 step = next; |
|
1296 next += ((yearDays == 366) ? 29 : 28); |
|
1297 if (day <= next) { |
|
1298 month = 1; |
|
1299 break; |
|
1300 } |
|
1301 step = next; |
|
1302 if (day <= (next += 31)) { |
|
1303 month = 2; |
|
1304 break; |
|
1305 } |
|
1306 step = next; |
|
1307 if (day <= (next += 30)) { |
|
1308 month = 3; |
|
1309 break; |
|
1310 } |
|
1311 step = next; |
|
1312 if (day <= (next += 31)) { |
|
1313 month = 4; |
|
1314 break; |
|
1315 } |
|
1316 step = next; |
|
1317 if (day <= (next += 30)) { |
|
1318 month = 5; |
|
1319 break; |
|
1320 } |
|
1321 step = next; |
|
1322 if (day <= (next += 31)) { |
|
1323 month = 6; |
|
1324 break; |
|
1325 } |
|
1326 step = next; |
|
1327 if (day <= (next += 31)) { |
|
1328 month = 7; |
|
1329 break; |
|
1330 } |
|
1331 step = next; |
|
1332 if (day <= (next += 30)) { |
|
1333 month = 8; |
|
1334 break; |
|
1335 } |
|
1336 step = next; |
|
1337 if (day <= (next += 31)) { |
|
1338 month = 9; |
|
1339 break; |
|
1340 } |
|
1341 step = next; |
|
1342 if (day <= (next += 30)) { |
|
1343 month = 10; |
|
1344 break; |
|
1345 } |
|
1346 step = next; |
|
1347 month = 11; |
|
1348 } while (0); |
|
1349 |
|
1350 setReservedSlot(LOCAL_MONTH_SLOT, Int32Value(month)); |
|
1351 setReservedSlot(LOCAL_DATE_SLOT, Int32Value(day - step)); |
|
1352 |
|
1353 int weekday = WeekDay(localTime); |
|
1354 setReservedSlot(LOCAL_DAY_SLOT, Int32Value(weekday)); |
|
1355 |
|
1356 int seconds = yearSeconds % 60; |
|
1357 setReservedSlot(LOCAL_SECONDS_SLOT, Int32Value(seconds)); |
|
1358 |
|
1359 int minutes = (yearSeconds / 60) % 60; |
|
1360 setReservedSlot(LOCAL_MINUTES_SLOT, Int32Value(minutes)); |
|
1361 |
|
1362 int hours = (yearSeconds / (60 * 60)) % 24; |
|
1363 setReservedSlot(LOCAL_HOURS_SLOT, Int32Value(hours)); |
|
1364 } |
|
1365 |
|
1366 inline double |
|
1367 DateObject::cachedLocalTime(DateTimeInfo *dtInfo) |
|
1368 { |
|
1369 fillLocalTimeSlots(dtInfo); |
|
1370 return getReservedSlot(LOCAL_TIME_SLOT).toDouble(); |
|
1371 } |
|
1372 |
|
1373 MOZ_ALWAYS_INLINE bool |
|
1374 IsDate(HandleValue v) |
|
1375 { |
|
1376 return v.isObject() && v.toObject().is<DateObject>(); |
|
1377 } |
|
1378 |
|
1379 /* |
|
1380 * See ECMA 15.9.5.4 thru 15.9.5.23 |
|
1381 */ |
|
1382 /* static */ MOZ_ALWAYS_INLINE bool |
|
1383 DateObject::getTime_impl(JSContext *cx, CallArgs args) |
|
1384 { |
|
1385 args.rval().set(args.thisv().toObject().as<DateObject>().UTCTime()); |
|
1386 return true; |
|
1387 } |
|
1388 |
|
1389 static bool |
|
1390 date_getTime(JSContext *cx, unsigned argc, Value *vp) |
|
1391 { |
|
1392 CallArgs args = CallArgsFromVp(argc, vp); |
|
1393 return CallNonGenericMethod<IsDate, DateObject::getTime_impl>(cx, args); |
|
1394 } |
|
1395 |
|
1396 /* static */ MOZ_ALWAYS_INLINE bool |
|
1397 DateObject::getYear_impl(JSContext *cx, CallArgs args) |
|
1398 { |
|
1399 DateObject *dateObj = &args.thisv().toObject().as<DateObject>(); |
|
1400 dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo); |
|
1401 |
|
1402 Value yearVal = dateObj->getReservedSlot(LOCAL_YEAR_SLOT); |
|
1403 if (yearVal.isInt32()) { |
|
1404 /* Follow ECMA-262 to the letter, contrary to IE JScript. */ |
|
1405 int year = yearVal.toInt32() - 1900; |
|
1406 args.rval().setInt32(year); |
|
1407 } else { |
|
1408 args.rval().set(yearVal); |
|
1409 } |
|
1410 |
|
1411 return true; |
|
1412 } |
|
1413 |
|
1414 static bool |
|
1415 date_getYear(JSContext *cx, unsigned argc, Value *vp) |
|
1416 { |
|
1417 CallArgs args = CallArgsFromVp(argc, vp); |
|
1418 return CallNonGenericMethod<IsDate, DateObject::getYear_impl>(cx, args); |
|
1419 } |
|
1420 |
|
1421 /* static */ MOZ_ALWAYS_INLINE bool |
|
1422 DateObject::getFullYear_impl(JSContext *cx, CallArgs args) |
|
1423 { |
|
1424 DateObject *dateObj = &args.thisv().toObject().as<DateObject>(); |
|
1425 dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo); |
|
1426 |
|
1427 args.rval().set(dateObj->getReservedSlot(LOCAL_YEAR_SLOT)); |
|
1428 return true; |
|
1429 } |
|
1430 |
|
1431 static bool |
|
1432 date_getFullYear(JSContext *cx, unsigned argc, Value *vp) |
|
1433 { |
|
1434 CallArgs args = CallArgsFromVp(argc, vp); |
|
1435 return CallNonGenericMethod<IsDate, DateObject::getFullYear_impl>(cx, args); |
|
1436 } |
|
1437 |
|
1438 /* static */ MOZ_ALWAYS_INLINE bool |
|
1439 DateObject::getUTCFullYear_impl(JSContext *cx, CallArgs args) |
|
1440 { |
|
1441 double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber(); |
|
1442 if (IsFinite(result)) |
|
1443 result = YearFromTime(result); |
|
1444 |
|
1445 args.rval().setNumber(result); |
|
1446 return true; |
|
1447 } |
|
1448 |
|
1449 static bool |
|
1450 date_getUTCFullYear(JSContext *cx, unsigned argc, Value *vp) |
|
1451 { |
|
1452 CallArgs args = CallArgsFromVp(argc, vp); |
|
1453 return CallNonGenericMethod<IsDate, DateObject::getUTCFullYear_impl>(cx, args); |
|
1454 } |
|
1455 |
|
1456 /* static */ MOZ_ALWAYS_INLINE bool |
|
1457 DateObject::getMonth_impl(JSContext *cx, CallArgs args) |
|
1458 { |
|
1459 DateObject *dateObj = &args.thisv().toObject().as<DateObject>(); |
|
1460 dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo); |
|
1461 |
|
1462 args.rval().set(dateObj->getReservedSlot(LOCAL_MONTH_SLOT)); |
|
1463 return true; |
|
1464 } |
|
1465 |
|
1466 static bool |
|
1467 date_getMonth(JSContext *cx, unsigned argc, Value *vp) |
|
1468 { |
|
1469 CallArgs args = CallArgsFromVp(argc, vp); |
|
1470 return CallNonGenericMethod<IsDate, DateObject::getMonth_impl>(cx, args); |
|
1471 } |
|
1472 |
|
1473 /* static */ MOZ_ALWAYS_INLINE bool |
|
1474 DateObject::getUTCMonth_impl(JSContext *cx, CallArgs args) |
|
1475 { |
|
1476 double d = args.thisv().toObject().as<DateObject>().UTCTime().toNumber(); |
|
1477 args.rval().setNumber(MonthFromTime(d)); |
|
1478 return true; |
|
1479 } |
|
1480 |
|
1481 static bool |
|
1482 date_getUTCMonth(JSContext *cx, unsigned argc, Value *vp) |
|
1483 { |
|
1484 CallArgs args = CallArgsFromVp(argc, vp); |
|
1485 return CallNonGenericMethod<IsDate, DateObject::getUTCMonth_impl>(cx, args); |
|
1486 } |
|
1487 |
|
1488 /* static */ MOZ_ALWAYS_INLINE bool |
|
1489 DateObject::getDate_impl(JSContext *cx, CallArgs args) |
|
1490 { |
|
1491 DateObject *dateObj = &args.thisv().toObject().as<DateObject>(); |
|
1492 dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo); |
|
1493 |
|
1494 args.rval().set(dateObj->getReservedSlot(LOCAL_DATE_SLOT)); |
|
1495 return true; |
|
1496 } |
|
1497 |
|
1498 static bool |
|
1499 date_getDate(JSContext *cx, unsigned argc, Value *vp) |
|
1500 { |
|
1501 CallArgs args = CallArgsFromVp(argc, vp); |
|
1502 return CallNonGenericMethod<IsDate, DateObject::getDate_impl>(cx, args); |
|
1503 } |
|
1504 |
|
1505 /* static */ MOZ_ALWAYS_INLINE bool |
|
1506 DateObject::getUTCDate_impl(JSContext *cx, CallArgs args) |
|
1507 { |
|
1508 double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber(); |
|
1509 if (IsFinite(result)) |
|
1510 result = DateFromTime(result); |
|
1511 |
|
1512 args.rval().setNumber(result); |
|
1513 return true; |
|
1514 } |
|
1515 |
|
1516 static bool |
|
1517 date_getUTCDate(JSContext *cx, unsigned argc, Value *vp) |
|
1518 { |
|
1519 CallArgs args = CallArgsFromVp(argc, vp); |
|
1520 return CallNonGenericMethod<IsDate, DateObject::getUTCDate_impl>(cx, args); |
|
1521 } |
|
1522 |
|
1523 /* static */ MOZ_ALWAYS_INLINE bool |
|
1524 DateObject::getDay_impl(JSContext *cx, CallArgs args) |
|
1525 { |
|
1526 DateObject *dateObj = &args.thisv().toObject().as<DateObject>(); |
|
1527 dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo); |
|
1528 |
|
1529 args.rval().set(dateObj->getReservedSlot(LOCAL_DAY_SLOT)); |
|
1530 return true; |
|
1531 } |
|
1532 |
|
1533 static bool |
|
1534 date_getDay(JSContext *cx, unsigned argc, Value *vp) |
|
1535 { |
|
1536 CallArgs args = CallArgsFromVp(argc, vp); |
|
1537 return CallNonGenericMethod<IsDate, DateObject::getDay_impl>(cx, args); |
|
1538 } |
|
1539 |
|
1540 /* static */ MOZ_ALWAYS_INLINE bool |
|
1541 DateObject::getUTCDay_impl(JSContext *cx, CallArgs args) |
|
1542 { |
|
1543 double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber(); |
|
1544 if (IsFinite(result)) |
|
1545 result = WeekDay(result); |
|
1546 |
|
1547 args.rval().setNumber(result); |
|
1548 return true; |
|
1549 } |
|
1550 |
|
1551 static bool |
|
1552 date_getUTCDay(JSContext *cx, unsigned argc, Value *vp) |
|
1553 { |
|
1554 CallArgs args = CallArgsFromVp(argc, vp); |
|
1555 return CallNonGenericMethod<IsDate, DateObject::getUTCDay_impl>(cx, args); |
|
1556 } |
|
1557 |
|
1558 /* static */ MOZ_ALWAYS_INLINE bool |
|
1559 DateObject::getHours_impl(JSContext *cx, CallArgs args) |
|
1560 { |
|
1561 DateObject *dateObj = &args.thisv().toObject().as<DateObject>(); |
|
1562 dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo); |
|
1563 |
|
1564 args.rval().set(dateObj->getReservedSlot(LOCAL_HOURS_SLOT)); |
|
1565 return true; |
|
1566 } |
|
1567 |
|
1568 static bool |
|
1569 date_getHours(JSContext *cx, unsigned argc, Value *vp) |
|
1570 { |
|
1571 CallArgs args = CallArgsFromVp(argc, vp); |
|
1572 return CallNonGenericMethod<IsDate, DateObject::getHours_impl>(cx, args); |
|
1573 } |
|
1574 |
|
1575 /* static */ MOZ_ALWAYS_INLINE bool |
|
1576 DateObject::getUTCHours_impl(JSContext *cx, CallArgs args) |
|
1577 { |
|
1578 double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber(); |
|
1579 if (IsFinite(result)) |
|
1580 result = HourFromTime(result); |
|
1581 |
|
1582 args.rval().setNumber(result); |
|
1583 return true; |
|
1584 } |
|
1585 |
|
1586 static bool |
|
1587 date_getUTCHours(JSContext *cx, unsigned argc, Value *vp) |
|
1588 { |
|
1589 CallArgs args = CallArgsFromVp(argc, vp); |
|
1590 return CallNonGenericMethod<IsDate, DateObject::getUTCHours_impl>(cx, args); |
|
1591 } |
|
1592 |
|
1593 /* static */ MOZ_ALWAYS_INLINE bool |
|
1594 DateObject::getMinutes_impl(JSContext *cx, CallArgs args) |
|
1595 { |
|
1596 DateObject *dateObj = &args.thisv().toObject().as<DateObject>(); |
|
1597 dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo); |
|
1598 |
|
1599 args.rval().set(dateObj->getReservedSlot(LOCAL_MINUTES_SLOT)); |
|
1600 return true; |
|
1601 } |
|
1602 |
|
1603 static bool |
|
1604 date_getMinutes(JSContext *cx, unsigned argc, Value *vp) |
|
1605 { |
|
1606 CallArgs args = CallArgsFromVp(argc, vp); |
|
1607 return CallNonGenericMethod<IsDate, DateObject::getMinutes_impl>(cx, args); |
|
1608 } |
|
1609 |
|
1610 /* static */ MOZ_ALWAYS_INLINE bool |
|
1611 DateObject::getUTCMinutes_impl(JSContext *cx, CallArgs args) |
|
1612 { |
|
1613 double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber(); |
|
1614 if (IsFinite(result)) |
|
1615 result = MinFromTime(result); |
|
1616 |
|
1617 args.rval().setNumber(result); |
|
1618 return true; |
|
1619 } |
|
1620 |
|
1621 static bool |
|
1622 date_getUTCMinutes(JSContext *cx, unsigned argc, Value *vp) |
|
1623 { |
|
1624 CallArgs args = CallArgsFromVp(argc, vp); |
|
1625 return CallNonGenericMethod<IsDate, DateObject::getUTCMinutes_impl>(cx, args); |
|
1626 } |
|
1627 |
|
1628 /* Date.getSeconds is mapped to getUTCSeconds */ |
|
1629 |
|
1630 /* static */ MOZ_ALWAYS_INLINE bool |
|
1631 DateObject::getUTCSeconds_impl(JSContext *cx, CallArgs args) |
|
1632 { |
|
1633 DateObject *dateObj = &args.thisv().toObject().as<DateObject>(); |
|
1634 dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo); |
|
1635 |
|
1636 args.rval().set(dateObj->getReservedSlot(LOCAL_SECONDS_SLOT)); |
|
1637 return true; |
|
1638 } |
|
1639 |
|
1640 static bool |
|
1641 date_getUTCSeconds(JSContext *cx, unsigned argc, Value *vp) |
|
1642 { |
|
1643 CallArgs args = CallArgsFromVp(argc, vp); |
|
1644 return CallNonGenericMethod<IsDate, DateObject::getUTCSeconds_impl>(cx, args); |
|
1645 } |
|
1646 |
|
1647 /* Date.getMilliseconds is mapped to getUTCMilliseconds */ |
|
1648 |
|
1649 /* static */ MOZ_ALWAYS_INLINE bool |
|
1650 DateObject::getUTCMilliseconds_impl(JSContext *cx, CallArgs args) |
|
1651 { |
|
1652 double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber(); |
|
1653 if (IsFinite(result)) |
|
1654 result = msFromTime(result); |
|
1655 |
|
1656 args.rval().setNumber(result); |
|
1657 return true; |
|
1658 } |
|
1659 |
|
1660 static bool |
|
1661 date_getUTCMilliseconds(JSContext *cx, unsigned argc, Value *vp) |
|
1662 { |
|
1663 CallArgs args = CallArgsFromVp(argc, vp); |
|
1664 return CallNonGenericMethod<IsDate, DateObject::getUTCMilliseconds_impl>(cx, args); |
|
1665 } |
|
1666 |
|
1667 /* static */ MOZ_ALWAYS_INLINE bool |
|
1668 DateObject::getTimezoneOffset_impl(JSContext *cx, CallArgs args) |
|
1669 { |
|
1670 DateObject *dateObj = &args.thisv().toObject().as<DateObject>(); |
|
1671 double utctime = dateObj->UTCTime().toNumber(); |
|
1672 double localtime = dateObj->cachedLocalTime(&cx->runtime()->dateTimeInfo); |
|
1673 |
|
1674 /* |
|
1675 * Return the time zone offset in minutes for the current locale that is |
|
1676 * appropriate for this time. This value would be a constant except for |
|
1677 * daylight savings time. |
|
1678 */ |
|
1679 double result = (utctime - localtime) / msPerMinute; |
|
1680 args.rval().setNumber(result); |
|
1681 return true; |
|
1682 } |
|
1683 |
|
1684 static bool |
|
1685 date_getTimezoneOffset(JSContext *cx, unsigned argc, Value *vp) |
|
1686 { |
|
1687 CallArgs args = CallArgsFromVp(argc, vp); |
|
1688 return CallNonGenericMethod<IsDate, DateObject::getTimezoneOffset_impl>(cx, args); |
|
1689 } |
|
1690 |
|
1691 MOZ_ALWAYS_INLINE bool |
|
1692 date_setTime_impl(JSContext *cx, CallArgs args) |
|
1693 { |
|
1694 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); |
|
1695 if (args.length() == 0) { |
|
1696 dateObj->setUTCTime(GenericNaN(), args.rval().address()); |
|
1697 return true; |
|
1698 } |
|
1699 |
|
1700 double result; |
|
1701 if (!ToNumber(cx, args[0], &result)) |
|
1702 return false; |
|
1703 |
|
1704 dateObj->setUTCTime(TimeClip(result), args.rval().address()); |
|
1705 return true; |
|
1706 } |
|
1707 |
|
1708 static bool |
|
1709 date_setTime(JSContext *cx, unsigned argc, Value *vp) |
|
1710 { |
|
1711 CallArgs args = CallArgsFromVp(argc, vp); |
|
1712 return CallNonGenericMethod<IsDate, date_setTime_impl>(cx, args); |
|
1713 } |
|
1714 |
|
1715 static bool |
|
1716 GetMsecsOrDefault(JSContext *cx, const CallArgs &args, unsigned i, double t, double *millis) |
|
1717 { |
|
1718 if (args.length() <= i) { |
|
1719 *millis = msFromTime(t); |
|
1720 return true; |
|
1721 } |
|
1722 return ToNumber(cx, args[i], millis); |
|
1723 } |
|
1724 |
|
1725 static bool |
|
1726 GetSecsOrDefault(JSContext *cx, const CallArgs &args, unsigned i, double t, double *sec) |
|
1727 { |
|
1728 if (args.length() <= i) { |
|
1729 *sec = SecFromTime(t); |
|
1730 return true; |
|
1731 } |
|
1732 return ToNumber(cx, args[i], sec); |
|
1733 } |
|
1734 |
|
1735 static bool |
|
1736 GetMinsOrDefault(JSContext *cx, const CallArgs &args, unsigned i, double t, double *mins) |
|
1737 { |
|
1738 if (args.length() <= i) { |
|
1739 *mins = MinFromTime(t); |
|
1740 return true; |
|
1741 } |
|
1742 return ToNumber(cx, args[i], mins); |
|
1743 } |
|
1744 |
|
1745 /* ES5 15.9.5.28. */ |
|
1746 MOZ_ALWAYS_INLINE bool |
|
1747 date_setMilliseconds_impl(JSContext *cx, CallArgs args) |
|
1748 { |
|
1749 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); |
|
1750 |
|
1751 /* Step 1. */ |
|
1752 double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo); |
|
1753 |
|
1754 /* Step 2. */ |
|
1755 double milli; |
|
1756 if (!ToNumber(cx, args.get(0), &milli)) |
|
1757 return false; |
|
1758 double time = MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), milli); |
|
1759 |
|
1760 /* Step 3. */ |
|
1761 double u = TimeClip(UTC(MakeDate(Day(t), time), &cx->runtime()->dateTimeInfo)); |
|
1762 |
|
1763 /* Steps 4-5. */ |
|
1764 dateObj->setUTCTime(u, args.rval().address()); |
|
1765 return true; |
|
1766 } |
|
1767 |
|
1768 static bool |
|
1769 date_setMilliseconds(JSContext *cx, unsigned argc, Value *vp) |
|
1770 { |
|
1771 CallArgs args = CallArgsFromVp(argc, vp); |
|
1772 return CallNonGenericMethod<IsDate, date_setMilliseconds_impl>(cx, args); |
|
1773 } |
|
1774 |
|
1775 /* ES5 15.9.5.29. */ |
|
1776 MOZ_ALWAYS_INLINE bool |
|
1777 date_setUTCMilliseconds_impl(JSContext *cx, CallArgs args) |
|
1778 { |
|
1779 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); |
|
1780 |
|
1781 /* Step 1. */ |
|
1782 double t = dateObj->UTCTime().toNumber(); |
|
1783 |
|
1784 /* Step 2. */ |
|
1785 double milli; |
|
1786 if (!ToNumber(cx, args.get(0), &milli)) |
|
1787 return false; |
|
1788 double time = MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), milli); |
|
1789 |
|
1790 /* Step 3. */ |
|
1791 double v = TimeClip(MakeDate(Day(t), time)); |
|
1792 |
|
1793 /* Steps 4-5. */ |
|
1794 dateObj->setUTCTime(v, args.rval().address()); |
|
1795 return true; |
|
1796 } |
|
1797 |
|
1798 static bool |
|
1799 date_setUTCMilliseconds(JSContext *cx, unsigned argc, Value *vp) |
|
1800 { |
|
1801 CallArgs args = CallArgsFromVp(argc, vp); |
|
1802 return CallNonGenericMethod<IsDate, date_setUTCMilliseconds_impl>(cx, args); |
|
1803 } |
|
1804 |
|
1805 /* ES5 15.9.5.30. */ |
|
1806 MOZ_ALWAYS_INLINE bool |
|
1807 date_setSeconds_impl(JSContext *cx, CallArgs args) |
|
1808 { |
|
1809 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); |
|
1810 |
|
1811 /* Step 1. */ |
|
1812 double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo); |
|
1813 |
|
1814 /* Step 2. */ |
|
1815 double s; |
|
1816 if (!ToNumber(cx, args.get(0), &s)) |
|
1817 return false; |
|
1818 |
|
1819 /* Step 3. */ |
|
1820 double milli; |
|
1821 if (!GetMsecsOrDefault(cx, args, 1, t, &milli)) |
|
1822 return false; |
|
1823 |
|
1824 /* Step 4. */ |
|
1825 double date = MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli)); |
|
1826 |
|
1827 /* Step 5. */ |
|
1828 double u = TimeClip(UTC(date, &cx->runtime()->dateTimeInfo)); |
|
1829 |
|
1830 /* Steps 6-7. */ |
|
1831 dateObj->setUTCTime(u, args.rval().address()); |
|
1832 return true; |
|
1833 } |
|
1834 |
|
1835 /* ES5 15.9.5.31. */ |
|
1836 static bool |
|
1837 date_setSeconds(JSContext *cx, unsigned argc, Value *vp) |
|
1838 { |
|
1839 CallArgs args = CallArgsFromVp(argc, vp); |
|
1840 return CallNonGenericMethod<IsDate, date_setSeconds_impl>(cx, args); |
|
1841 } |
|
1842 |
|
1843 MOZ_ALWAYS_INLINE bool |
|
1844 date_setUTCSeconds_impl(JSContext *cx, CallArgs args) |
|
1845 { |
|
1846 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); |
|
1847 |
|
1848 /* Step 1. */ |
|
1849 double t = dateObj->UTCTime().toNumber(); |
|
1850 |
|
1851 /* Step 2. */ |
|
1852 double s; |
|
1853 if (!ToNumber(cx, args.get(0), &s)) |
|
1854 return false; |
|
1855 |
|
1856 /* Step 3. */ |
|
1857 double milli; |
|
1858 if (!GetMsecsOrDefault(cx, args, 1, t, &milli)) |
|
1859 return false; |
|
1860 |
|
1861 /* Step 4. */ |
|
1862 double date = MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli)); |
|
1863 |
|
1864 /* Step 5. */ |
|
1865 double v = TimeClip(date); |
|
1866 |
|
1867 /* Steps 6-7. */ |
|
1868 dateObj->setUTCTime(v, args.rval().address()); |
|
1869 return true; |
|
1870 } |
|
1871 |
|
1872 /* ES5 15.9.5.32. */ |
|
1873 static bool |
|
1874 date_setUTCSeconds(JSContext *cx, unsigned argc, Value *vp) |
|
1875 { |
|
1876 CallArgs args = CallArgsFromVp(argc, vp); |
|
1877 return CallNonGenericMethod<IsDate, date_setUTCSeconds_impl>(cx, args); |
|
1878 } |
|
1879 |
|
1880 MOZ_ALWAYS_INLINE bool |
|
1881 date_setMinutes_impl(JSContext *cx, CallArgs args) |
|
1882 { |
|
1883 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); |
|
1884 |
|
1885 /* Step 1. */ |
|
1886 double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo); |
|
1887 |
|
1888 /* Step 2. */ |
|
1889 double m; |
|
1890 if (!ToNumber(cx, args.get(0), &m)) |
|
1891 return false; |
|
1892 |
|
1893 /* Step 3. */ |
|
1894 double s; |
|
1895 if (!GetSecsOrDefault(cx, args, 1, t, &s)) |
|
1896 return false; |
|
1897 |
|
1898 /* Step 4. */ |
|
1899 double milli; |
|
1900 if (!GetMsecsOrDefault(cx, args, 2, t, &milli)) |
|
1901 return false; |
|
1902 |
|
1903 /* Step 5. */ |
|
1904 double date = MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli)); |
|
1905 |
|
1906 /* Step 6. */ |
|
1907 double u = TimeClip(UTC(date, &cx->runtime()->dateTimeInfo)); |
|
1908 |
|
1909 /* Steps 7-8. */ |
|
1910 dateObj->setUTCTime(u, args.rval().address()); |
|
1911 return true; |
|
1912 } |
|
1913 |
|
1914 /* ES5 15.9.5.33. */ |
|
1915 static bool |
|
1916 date_setMinutes(JSContext *cx, unsigned argc, Value *vp) |
|
1917 { |
|
1918 CallArgs args = CallArgsFromVp(argc, vp); |
|
1919 return CallNonGenericMethod<IsDate, date_setMinutes_impl>(cx, args); |
|
1920 } |
|
1921 |
|
1922 MOZ_ALWAYS_INLINE bool |
|
1923 date_setUTCMinutes_impl(JSContext *cx, CallArgs args) |
|
1924 { |
|
1925 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); |
|
1926 |
|
1927 /* Step 1. */ |
|
1928 double t = dateObj->UTCTime().toNumber(); |
|
1929 |
|
1930 /* Step 2. */ |
|
1931 double m; |
|
1932 if (!ToNumber(cx, args.get(0), &m)) |
|
1933 return false; |
|
1934 |
|
1935 /* Step 3. */ |
|
1936 double s; |
|
1937 if (!GetSecsOrDefault(cx, args, 1, t, &s)) |
|
1938 return false; |
|
1939 |
|
1940 /* Step 4. */ |
|
1941 double milli; |
|
1942 if (!GetMsecsOrDefault(cx, args, 2, t, &milli)) |
|
1943 return false; |
|
1944 |
|
1945 /* Step 5. */ |
|
1946 double date = MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli)); |
|
1947 |
|
1948 /* Step 6. */ |
|
1949 double v = TimeClip(date); |
|
1950 |
|
1951 /* Steps 7-8. */ |
|
1952 dateObj->setUTCTime(v, args.rval().address()); |
|
1953 return true; |
|
1954 } |
|
1955 |
|
1956 /* ES5 15.9.5.34. */ |
|
1957 static bool |
|
1958 date_setUTCMinutes(JSContext *cx, unsigned argc, Value *vp) |
|
1959 { |
|
1960 CallArgs args = CallArgsFromVp(argc, vp); |
|
1961 return CallNonGenericMethod<IsDate, date_setUTCMinutes_impl>(cx, args); |
|
1962 } |
|
1963 |
|
1964 MOZ_ALWAYS_INLINE bool |
|
1965 date_setHours_impl(JSContext *cx, CallArgs args) |
|
1966 { |
|
1967 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); |
|
1968 |
|
1969 /* Step 1. */ |
|
1970 double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo); |
|
1971 |
|
1972 /* Step 2. */ |
|
1973 double h; |
|
1974 if (!ToNumber(cx, args.get(0), &h)) |
|
1975 return false; |
|
1976 |
|
1977 /* Step 3. */ |
|
1978 double m; |
|
1979 if (!GetMinsOrDefault(cx, args, 1, t, &m)) |
|
1980 return false; |
|
1981 |
|
1982 /* Step 4. */ |
|
1983 double s; |
|
1984 if (!GetSecsOrDefault(cx, args, 2, t, &s)) |
|
1985 return false; |
|
1986 |
|
1987 /* Step 5. */ |
|
1988 double milli; |
|
1989 if (!GetMsecsOrDefault(cx, args, 3, t, &milli)) |
|
1990 return false; |
|
1991 |
|
1992 /* Step 6. */ |
|
1993 double date = MakeDate(Day(t), MakeTime(h, m, s, milli)); |
|
1994 |
|
1995 /* Step 6. */ |
|
1996 double u = TimeClip(UTC(date, &cx->runtime()->dateTimeInfo)); |
|
1997 |
|
1998 /* Steps 7-8. */ |
|
1999 dateObj->setUTCTime(u, args.rval().address()); |
|
2000 return true; |
|
2001 } |
|
2002 |
|
2003 /* ES5 15.9.5.35. */ |
|
2004 static bool |
|
2005 date_setHours(JSContext *cx, unsigned argc, Value *vp) |
|
2006 { |
|
2007 CallArgs args = CallArgsFromVp(argc, vp); |
|
2008 return CallNonGenericMethod<IsDate, date_setHours_impl>(cx, args); |
|
2009 } |
|
2010 |
|
2011 MOZ_ALWAYS_INLINE bool |
|
2012 date_setUTCHours_impl(JSContext *cx, CallArgs args) |
|
2013 { |
|
2014 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); |
|
2015 |
|
2016 /* Step 1. */ |
|
2017 double t = dateObj->UTCTime().toNumber(); |
|
2018 |
|
2019 /* Step 2. */ |
|
2020 double h; |
|
2021 if (!ToNumber(cx, args.get(0), &h)) |
|
2022 return false; |
|
2023 |
|
2024 /* Step 3. */ |
|
2025 double m; |
|
2026 if (!GetMinsOrDefault(cx, args, 1, t, &m)) |
|
2027 return false; |
|
2028 |
|
2029 /* Step 4. */ |
|
2030 double s; |
|
2031 if (!GetSecsOrDefault(cx, args, 2, t, &s)) |
|
2032 return false; |
|
2033 |
|
2034 /* Step 5. */ |
|
2035 double milli; |
|
2036 if (!GetMsecsOrDefault(cx, args, 3, t, &milli)) |
|
2037 return false; |
|
2038 |
|
2039 /* Step 6. */ |
|
2040 double newDate = MakeDate(Day(t), MakeTime(h, m, s, milli)); |
|
2041 |
|
2042 /* Step 7. */ |
|
2043 double v = TimeClip(newDate); |
|
2044 |
|
2045 /* Steps 8-9. */ |
|
2046 dateObj->setUTCTime(v, args.rval().address()); |
|
2047 return true; |
|
2048 } |
|
2049 |
|
2050 /* ES5 15.9.5.36. */ |
|
2051 static bool |
|
2052 date_setUTCHours(JSContext *cx, unsigned argc, Value *vp) |
|
2053 { |
|
2054 CallArgs args = CallArgsFromVp(argc, vp); |
|
2055 return CallNonGenericMethod<IsDate, date_setUTCHours_impl>(cx, args); |
|
2056 } |
|
2057 |
|
2058 MOZ_ALWAYS_INLINE bool |
|
2059 date_setDate_impl(JSContext *cx, CallArgs args) |
|
2060 { |
|
2061 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); |
|
2062 |
|
2063 /* Step 1. */ |
|
2064 double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo); |
|
2065 |
|
2066 /* Step 2. */ |
|
2067 double date; |
|
2068 if (!ToNumber(cx, args.get(0), &date)) |
|
2069 return false; |
|
2070 |
|
2071 /* Step 3. */ |
|
2072 double newDate = MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)); |
|
2073 |
|
2074 /* Step 4. */ |
|
2075 double u = TimeClip(UTC(newDate, &cx->runtime()->dateTimeInfo)); |
|
2076 |
|
2077 /* Steps 5-6. */ |
|
2078 dateObj->setUTCTime(u, args.rval().address()); |
|
2079 return true; |
|
2080 } |
|
2081 |
|
2082 /* ES5 15.9.5.37. */ |
|
2083 static bool |
|
2084 date_setDate(JSContext *cx, unsigned argc, Value *vp) |
|
2085 { |
|
2086 CallArgs args = CallArgsFromVp(argc, vp); |
|
2087 return CallNonGenericMethod<IsDate, date_setDate_impl>(cx, args); |
|
2088 } |
|
2089 |
|
2090 MOZ_ALWAYS_INLINE bool |
|
2091 date_setUTCDate_impl(JSContext *cx, CallArgs args) |
|
2092 { |
|
2093 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); |
|
2094 |
|
2095 /* Step 1. */ |
|
2096 double t = dateObj->UTCTime().toNumber(); |
|
2097 |
|
2098 /* Step 2. */ |
|
2099 double date; |
|
2100 if (!ToNumber(cx, args.get(0), &date)) |
|
2101 return false; |
|
2102 |
|
2103 /* Step 3. */ |
|
2104 double newDate = MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)); |
|
2105 |
|
2106 /* Step 4. */ |
|
2107 double v = TimeClip(newDate); |
|
2108 |
|
2109 /* Steps 5-6. */ |
|
2110 dateObj->setUTCTime(v, args.rval().address()); |
|
2111 return true; |
|
2112 } |
|
2113 |
|
2114 static bool |
|
2115 date_setUTCDate(JSContext *cx, unsigned argc, Value *vp) |
|
2116 { |
|
2117 CallArgs args = CallArgsFromVp(argc, vp); |
|
2118 return CallNonGenericMethod<IsDate, date_setUTCDate_impl>(cx, args); |
|
2119 } |
|
2120 |
|
2121 static bool |
|
2122 GetDateOrDefault(JSContext *cx, const CallArgs &args, unsigned i, double t, double *date) |
|
2123 { |
|
2124 if (args.length() <= i) { |
|
2125 *date = DateFromTime(t); |
|
2126 return true; |
|
2127 } |
|
2128 return ToNumber(cx, args[i], date); |
|
2129 } |
|
2130 |
|
2131 static bool |
|
2132 GetMonthOrDefault(JSContext *cx, const CallArgs &args, unsigned i, double t, double *month) |
|
2133 { |
|
2134 if (args.length() <= i) { |
|
2135 *month = MonthFromTime(t); |
|
2136 return true; |
|
2137 } |
|
2138 return ToNumber(cx, args[i], month); |
|
2139 } |
|
2140 |
|
2141 /* ES5 15.9.5.38. */ |
|
2142 MOZ_ALWAYS_INLINE bool |
|
2143 date_setMonth_impl(JSContext *cx, CallArgs args) |
|
2144 { |
|
2145 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); |
|
2146 |
|
2147 /* Step 1. */ |
|
2148 double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo); |
|
2149 |
|
2150 /* Step 2. */ |
|
2151 double m; |
|
2152 if (!ToNumber(cx, args.get(0), &m)) |
|
2153 return false; |
|
2154 |
|
2155 /* Step 3. */ |
|
2156 double date; |
|
2157 if (!GetDateOrDefault(cx, args, 1, t, &date)) |
|
2158 return false; |
|
2159 |
|
2160 /* Step 4. */ |
|
2161 double newDate = MakeDate(MakeDay(YearFromTime(t), m, date), TimeWithinDay(t)); |
|
2162 |
|
2163 /* Step 5. */ |
|
2164 double u = TimeClip(UTC(newDate, &cx->runtime()->dateTimeInfo)); |
|
2165 |
|
2166 /* Steps 6-7. */ |
|
2167 dateObj->setUTCTime(u, args.rval().address()); |
|
2168 return true; |
|
2169 } |
|
2170 |
|
2171 static bool |
|
2172 date_setMonth(JSContext *cx, unsigned argc, Value *vp) |
|
2173 { |
|
2174 CallArgs args = CallArgsFromVp(argc, vp); |
|
2175 return CallNonGenericMethod<IsDate, date_setMonth_impl>(cx, args); |
|
2176 } |
|
2177 |
|
2178 /* ES5 15.9.5.39. */ |
|
2179 MOZ_ALWAYS_INLINE bool |
|
2180 date_setUTCMonth_impl(JSContext *cx, CallArgs args) |
|
2181 { |
|
2182 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); |
|
2183 |
|
2184 /* Step 1. */ |
|
2185 double t = dateObj->UTCTime().toNumber(); |
|
2186 |
|
2187 /* Step 2. */ |
|
2188 double m; |
|
2189 if (!ToNumber(cx, args.get(0), &m)) |
|
2190 return false; |
|
2191 |
|
2192 /* Step 3. */ |
|
2193 double date; |
|
2194 if (!GetDateOrDefault(cx, args, 1, t, &date)) |
|
2195 return false; |
|
2196 |
|
2197 /* Step 4. */ |
|
2198 double newDate = MakeDate(MakeDay(YearFromTime(t), m, date), TimeWithinDay(t)); |
|
2199 |
|
2200 /* Step 5. */ |
|
2201 double v = TimeClip(newDate); |
|
2202 |
|
2203 /* Steps 6-7. */ |
|
2204 dateObj->setUTCTime(v, args.rval().address()); |
|
2205 return true; |
|
2206 } |
|
2207 |
|
2208 static bool |
|
2209 date_setUTCMonth(JSContext *cx, unsigned argc, Value *vp) |
|
2210 { |
|
2211 CallArgs args = CallArgsFromVp(argc, vp); |
|
2212 return CallNonGenericMethod<IsDate, date_setUTCMonth_impl>(cx, args); |
|
2213 } |
|
2214 |
|
2215 static double |
|
2216 ThisLocalTimeOrZero(Handle<DateObject*> dateObj, DateTimeInfo *dtInfo) |
|
2217 { |
|
2218 double t = dateObj->UTCTime().toNumber(); |
|
2219 if (IsNaN(t)) |
|
2220 return +0; |
|
2221 return LocalTime(t, dtInfo); |
|
2222 } |
|
2223 |
|
2224 static double |
|
2225 ThisUTCTimeOrZero(Handle<DateObject*> dateObj) |
|
2226 { |
|
2227 double t = dateObj->as<DateObject>().UTCTime().toNumber(); |
|
2228 return IsNaN(t) ? +0 : t; |
|
2229 } |
|
2230 |
|
2231 /* ES5 15.9.5.40. */ |
|
2232 MOZ_ALWAYS_INLINE bool |
|
2233 date_setFullYear_impl(JSContext *cx, CallArgs args) |
|
2234 { |
|
2235 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); |
|
2236 |
|
2237 /* Step 1. */ |
|
2238 double t = ThisLocalTimeOrZero(dateObj, &cx->runtime()->dateTimeInfo); |
|
2239 |
|
2240 /* Step 2. */ |
|
2241 double y; |
|
2242 if (!ToNumber(cx, args.get(0), &y)) |
|
2243 return false; |
|
2244 |
|
2245 /* Step 3. */ |
|
2246 double m; |
|
2247 if (!GetMonthOrDefault(cx, args, 1, t, &m)) |
|
2248 return false; |
|
2249 |
|
2250 /* Step 4. */ |
|
2251 double date; |
|
2252 if (!GetDateOrDefault(cx, args, 2, t, &date)) |
|
2253 return false; |
|
2254 |
|
2255 /* Step 5. */ |
|
2256 double newDate = MakeDate(MakeDay(y, m, date), TimeWithinDay(t)); |
|
2257 |
|
2258 /* Step 6. */ |
|
2259 double u = TimeClip(UTC(newDate, &cx->runtime()->dateTimeInfo)); |
|
2260 |
|
2261 /* Steps 7-8. */ |
|
2262 dateObj->setUTCTime(u, args.rval().address()); |
|
2263 return true; |
|
2264 } |
|
2265 |
|
2266 static bool |
|
2267 date_setFullYear(JSContext *cx, unsigned argc, Value *vp) |
|
2268 { |
|
2269 CallArgs args = CallArgsFromVp(argc, vp); |
|
2270 return CallNonGenericMethod<IsDate, date_setFullYear_impl>(cx, args); |
|
2271 } |
|
2272 |
|
2273 /* ES5 15.9.5.41. */ |
|
2274 MOZ_ALWAYS_INLINE bool |
|
2275 date_setUTCFullYear_impl(JSContext *cx, CallArgs args) |
|
2276 { |
|
2277 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); |
|
2278 |
|
2279 /* Step 1. */ |
|
2280 double t = ThisUTCTimeOrZero(dateObj); |
|
2281 |
|
2282 /* Step 2. */ |
|
2283 double y; |
|
2284 if (!ToNumber(cx, args.get(0), &y)) |
|
2285 return false; |
|
2286 |
|
2287 /* Step 3. */ |
|
2288 double m; |
|
2289 if (!GetMonthOrDefault(cx, args, 1, t, &m)) |
|
2290 return false; |
|
2291 |
|
2292 /* Step 4. */ |
|
2293 double date; |
|
2294 if (!GetDateOrDefault(cx, args, 2, t, &date)) |
|
2295 return false; |
|
2296 |
|
2297 /* Step 5. */ |
|
2298 double newDate = MakeDate(MakeDay(y, m, date), TimeWithinDay(t)); |
|
2299 |
|
2300 /* Step 6. */ |
|
2301 double v = TimeClip(newDate); |
|
2302 |
|
2303 /* Steps 7-8. */ |
|
2304 dateObj->setUTCTime(v, args.rval().address()); |
|
2305 return true; |
|
2306 } |
|
2307 |
|
2308 static bool |
|
2309 date_setUTCFullYear(JSContext *cx, unsigned argc, Value *vp) |
|
2310 { |
|
2311 CallArgs args = CallArgsFromVp(argc, vp); |
|
2312 return CallNonGenericMethod<IsDate, date_setUTCFullYear_impl>(cx, args); |
|
2313 } |
|
2314 |
|
2315 /* ES5 Annex B.2.5. */ |
|
2316 MOZ_ALWAYS_INLINE bool |
|
2317 date_setYear_impl(JSContext *cx, CallArgs args) |
|
2318 { |
|
2319 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); |
|
2320 |
|
2321 /* Step 1. */ |
|
2322 double t = ThisLocalTimeOrZero(dateObj, &cx->runtime()->dateTimeInfo); |
|
2323 |
|
2324 /* Step 2. */ |
|
2325 double y; |
|
2326 if (!ToNumber(cx, args.get(0), &y)) |
|
2327 return false; |
|
2328 |
|
2329 /* Step 3. */ |
|
2330 if (IsNaN(y)) { |
|
2331 dateObj->setUTCTime(GenericNaN(), args.rval().address()); |
|
2332 return true; |
|
2333 } |
|
2334 |
|
2335 /* Step 4. */ |
|
2336 double yint = ToInteger(y); |
|
2337 if (0 <= yint && yint <= 99) |
|
2338 yint += 1900; |
|
2339 |
|
2340 /* Step 5. */ |
|
2341 double day = MakeDay(yint, MonthFromTime(t), DateFromTime(t)); |
|
2342 |
|
2343 /* Step 6. */ |
|
2344 double u = UTC(MakeDate(day, TimeWithinDay(t)), &cx->runtime()->dateTimeInfo); |
|
2345 |
|
2346 /* Steps 7-8. */ |
|
2347 dateObj->setUTCTime(TimeClip(u), args.rval().address()); |
|
2348 return true; |
|
2349 } |
|
2350 |
|
2351 static bool |
|
2352 date_setYear(JSContext *cx, unsigned argc, Value *vp) |
|
2353 { |
|
2354 CallArgs args = CallArgsFromVp(argc, vp); |
|
2355 return CallNonGenericMethod<IsDate, date_setYear_impl>(cx, args); |
|
2356 } |
|
2357 |
|
2358 /* constants for toString, toUTCString */ |
|
2359 static const char js_NaN_date_str[] = "Invalid Date"; |
|
2360 static const char * const days[] = |
|
2361 { |
|
2362 "Sun","Mon","Tue","Wed","Thu","Fri","Sat" |
|
2363 }; |
|
2364 static const char * const months[] = |
|
2365 { |
|
2366 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" |
|
2367 }; |
|
2368 |
|
2369 |
|
2370 // Avoid dependence on PRMJ_FormatTimeUSEnglish, because it |
|
2371 // requires a PRMJTime... which only has 16-bit years. Sub-ECMA. |
|
2372 static void |
|
2373 print_gmt_string(char* buf, size_t size, double utctime) |
|
2374 { |
|
2375 JS_ASSERT(TimeClip(utctime) == utctime); |
|
2376 JS_snprintf(buf, size, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT", |
|
2377 days[int(WeekDay(utctime))], |
|
2378 int(DateFromTime(utctime)), |
|
2379 months[int(MonthFromTime(utctime))], |
|
2380 int(YearFromTime(utctime)), |
|
2381 int(HourFromTime(utctime)), |
|
2382 int(MinFromTime(utctime)), |
|
2383 int(SecFromTime(utctime))); |
|
2384 } |
|
2385 |
|
2386 static void |
|
2387 print_iso_string(char* buf, size_t size, double utctime) |
|
2388 { |
|
2389 JS_ASSERT(TimeClip(utctime) == utctime); |
|
2390 JS_snprintf(buf, size, "%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.3dZ", |
|
2391 int(YearFromTime(utctime)), |
|
2392 int(MonthFromTime(utctime)) + 1, |
|
2393 int(DateFromTime(utctime)), |
|
2394 int(HourFromTime(utctime)), |
|
2395 int(MinFromTime(utctime)), |
|
2396 int(SecFromTime(utctime)), |
|
2397 int(msFromTime(utctime))); |
|
2398 } |
|
2399 |
|
2400 /* ES5 B.2.6. */ |
|
2401 MOZ_ALWAYS_INLINE bool |
|
2402 date_toGMTString_impl(JSContext *cx, CallArgs args) |
|
2403 { |
|
2404 double utctime = args.thisv().toObject().as<DateObject>().UTCTime().toNumber(); |
|
2405 |
|
2406 char buf[100]; |
|
2407 if (!IsFinite(utctime)) |
|
2408 JS_snprintf(buf, sizeof buf, js_NaN_date_str); |
|
2409 else |
|
2410 print_gmt_string(buf, sizeof buf, utctime); |
|
2411 |
|
2412 JSString *str = JS_NewStringCopyZ(cx, buf); |
|
2413 if (!str) |
|
2414 return false; |
|
2415 args.rval().setString(str); |
|
2416 return true; |
|
2417 } |
|
2418 |
|
2419 /* ES5 15.9.5.43. */ |
|
2420 static bool |
|
2421 date_toGMTString(JSContext *cx, unsigned argc, Value *vp) |
|
2422 { |
|
2423 CallArgs args = CallArgsFromVp(argc, vp); |
|
2424 return CallNonGenericMethod<IsDate, date_toGMTString_impl>(cx, args); |
|
2425 } |
|
2426 |
|
2427 MOZ_ALWAYS_INLINE bool |
|
2428 date_toISOString_impl(JSContext *cx, CallArgs args) |
|
2429 { |
|
2430 double utctime = args.thisv().toObject().as<DateObject>().UTCTime().toNumber(); |
|
2431 if (!IsFinite(utctime)) { |
|
2432 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INVALID_DATE); |
|
2433 return false; |
|
2434 } |
|
2435 |
|
2436 char buf[100]; |
|
2437 print_iso_string(buf, sizeof buf, utctime); |
|
2438 |
|
2439 JSString *str = JS_NewStringCopyZ(cx, buf); |
|
2440 if (!str) |
|
2441 return false; |
|
2442 args.rval().setString(str); |
|
2443 return true; |
|
2444 |
|
2445 } |
|
2446 |
|
2447 static bool |
|
2448 date_toISOString(JSContext *cx, unsigned argc, Value *vp) |
|
2449 { |
|
2450 CallArgs args = CallArgsFromVp(argc, vp); |
|
2451 return CallNonGenericMethod<IsDate, date_toISOString_impl>(cx, args); |
|
2452 } |
|
2453 |
|
2454 /* ES5 15.9.5.44. */ |
|
2455 static bool |
|
2456 date_toJSON(JSContext *cx, unsigned argc, Value *vp) |
|
2457 { |
|
2458 CallArgs args = CallArgsFromVp(argc, vp); |
|
2459 |
|
2460 /* Step 1. */ |
|
2461 RootedObject obj(cx, ToObject(cx, args.thisv())); |
|
2462 if (!obj) |
|
2463 return false; |
|
2464 |
|
2465 /* Step 2. */ |
|
2466 RootedValue tv(cx, ObjectValue(*obj)); |
|
2467 if (!ToPrimitive(cx, JSTYPE_NUMBER, &tv)) |
|
2468 return false; |
|
2469 |
|
2470 /* Step 3. */ |
|
2471 if (tv.isDouble() && !IsFinite(tv.toDouble())) { |
|
2472 args.rval().setNull(); |
|
2473 return true; |
|
2474 } |
|
2475 |
|
2476 /* Step 4. */ |
|
2477 RootedValue toISO(cx); |
|
2478 if (!JSObject::getProperty(cx, obj, obj, cx->names().toISOString, &toISO)) |
|
2479 return false; |
|
2480 |
|
2481 /* Step 5. */ |
|
2482 if (!js_IsCallable(toISO)) { |
|
2483 JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, nullptr, |
|
2484 JSMSG_BAD_TOISOSTRING_PROP); |
|
2485 return false; |
|
2486 } |
|
2487 |
|
2488 /* Step 6. */ |
|
2489 InvokeArgs args2(cx); |
|
2490 if (!args2.init(0)) |
|
2491 return false; |
|
2492 |
|
2493 args2.setCallee(toISO); |
|
2494 args2.setThis(ObjectValue(*obj)); |
|
2495 |
|
2496 if (!Invoke(cx, args2)) |
|
2497 return false; |
|
2498 args.rval().set(args2.rval()); |
|
2499 return true; |
|
2500 } |
|
2501 |
|
2502 /* for Date.toLocaleFormat; interface to PRMJTime date struct. |
|
2503 */ |
|
2504 static void |
|
2505 new_explode(double timeval, PRMJTime *split, DateTimeInfo *dtInfo) |
|
2506 { |
|
2507 double year = YearFromTime(timeval); |
|
2508 |
|
2509 split->tm_usec = int32_t(msFromTime(timeval)) * 1000; |
|
2510 split->tm_sec = int8_t(SecFromTime(timeval)); |
|
2511 split->tm_min = int8_t(MinFromTime(timeval)); |
|
2512 split->tm_hour = int8_t(HourFromTime(timeval)); |
|
2513 split->tm_mday = int8_t(DateFromTime(timeval)); |
|
2514 split->tm_mon = int8_t(MonthFromTime(timeval)); |
|
2515 split->tm_wday = int8_t(WeekDay(timeval)); |
|
2516 split->tm_year = year; |
|
2517 split->tm_yday = int16_t(DayWithinYear(timeval, year)); |
|
2518 |
|
2519 /* not sure how this affects things, but it doesn't seem |
|
2520 to matter. */ |
|
2521 split->tm_isdst = (DaylightSavingTA(timeval, dtInfo) != 0); |
|
2522 } |
|
2523 |
|
2524 typedef enum formatspec { |
|
2525 FORMATSPEC_FULL, FORMATSPEC_DATE, FORMATSPEC_TIME |
|
2526 } formatspec; |
|
2527 |
|
2528 /* helper function */ |
|
2529 static bool |
|
2530 date_format(JSContext *cx, double date, formatspec format, MutableHandleValue rval) |
|
2531 { |
|
2532 char buf[100]; |
|
2533 char tzbuf[100]; |
|
2534 bool usetz; |
|
2535 size_t i, tzlen; |
|
2536 PRMJTime split; |
|
2537 |
|
2538 if (!IsFinite(date)) { |
|
2539 JS_snprintf(buf, sizeof buf, js_NaN_date_str); |
|
2540 } else { |
|
2541 JS_ASSERT(TimeClip(date) == date); |
|
2542 |
|
2543 double local = LocalTime(date, &cx->runtime()->dateTimeInfo); |
|
2544 |
|
2545 /* offset from GMT in minutes. The offset includes daylight savings, |
|
2546 if it applies. */ |
|
2547 int minutes = (int) floor(AdjustTime(date, &cx->runtime()->dateTimeInfo) / msPerMinute); |
|
2548 |
|
2549 /* map 510 minutes to 0830 hours */ |
|
2550 int offset = (minutes / 60) * 100 + minutes % 60; |
|
2551 |
|
2552 /* print as "Wed Nov 05 19:38:03 GMT-0800 (PST) 1997" The TZA is |
|
2553 * printed as 'GMT-0800' rather than as 'PST' to avoid |
|
2554 * operating-system dependence on strftime (which |
|
2555 * PRMJ_FormatTimeUSEnglish calls, for %Z only.) win32 prints |
|
2556 * PST as 'Pacific Standard Time.' This way we always know |
|
2557 * what we're getting, and can parse it if we produce it. |
|
2558 * The OS TZA string is included as a comment. |
|
2559 */ |
|
2560 |
|
2561 /* get a timezone string from the OS to include as a |
|
2562 comment. */ |
|
2563 new_explode(date, &split, &cx->runtime()->dateTimeInfo); |
|
2564 if (PRMJ_FormatTime(tzbuf, sizeof tzbuf, "(%Z)", &split) != 0) { |
|
2565 |
|
2566 /* Decide whether to use the resulting timezone string. |
|
2567 * |
|
2568 * Reject it if it contains any non-ASCII, non-alphanumeric |
|
2569 * characters. It's then likely in some other character |
|
2570 * encoding, and we probably won't display it correctly. |
|
2571 */ |
|
2572 usetz = true; |
|
2573 tzlen = strlen(tzbuf); |
|
2574 if (tzlen > 100) { |
|
2575 usetz = false; |
|
2576 } else { |
|
2577 for (i = 0; i < tzlen; i++) { |
|
2578 jschar c = tzbuf[i]; |
|
2579 if (c > 127 || |
|
2580 !(isalpha(c) || isdigit(c) || |
|
2581 c == ' ' || c == '(' || c == ')')) { |
|
2582 usetz = false; |
|
2583 } |
|
2584 } |
|
2585 } |
|
2586 |
|
2587 /* Also reject it if it's not parenthesized or if it's '()'. */ |
|
2588 if (tzbuf[0] != '(' || tzbuf[1] == ')') |
|
2589 usetz = false; |
|
2590 } else |
|
2591 usetz = false; |
|
2592 |
|
2593 switch (format) { |
|
2594 case FORMATSPEC_FULL: |
|
2595 /* |
|
2596 * Avoid dependence on PRMJ_FormatTimeUSEnglish, because it |
|
2597 * requires a PRMJTime... which only has 16-bit years. Sub-ECMA. |
|
2598 */ |
|
2599 /* Tue Oct 31 2000 09:41:40 GMT-0800 (PST) */ |
|
2600 JS_snprintf(buf, sizeof buf, |
|
2601 "%s %s %.2d %.4d %.2d:%.2d:%.2d GMT%+.4d%s%s", |
|
2602 days[int(WeekDay(local))], |
|
2603 months[int(MonthFromTime(local))], |
|
2604 int(DateFromTime(local)), |
|
2605 int(YearFromTime(local)), |
|
2606 int(HourFromTime(local)), |
|
2607 int(MinFromTime(local)), |
|
2608 int(SecFromTime(local)), |
|
2609 offset, |
|
2610 usetz ? " " : "", |
|
2611 usetz ? tzbuf : ""); |
|
2612 break; |
|
2613 case FORMATSPEC_DATE: |
|
2614 /* Tue Oct 31 2000 */ |
|
2615 JS_snprintf(buf, sizeof buf, |
|
2616 "%s %s %.2d %.4d", |
|
2617 days[int(WeekDay(local))], |
|
2618 months[int(MonthFromTime(local))], |
|
2619 int(DateFromTime(local)), |
|
2620 int(YearFromTime(local))); |
|
2621 break; |
|
2622 case FORMATSPEC_TIME: |
|
2623 /* 09:41:40 GMT-0800 (PST) */ |
|
2624 JS_snprintf(buf, sizeof buf, |
|
2625 "%.2d:%.2d:%.2d GMT%+.4d%s%s", |
|
2626 int(HourFromTime(local)), |
|
2627 int(MinFromTime(local)), |
|
2628 int(SecFromTime(local)), |
|
2629 offset, |
|
2630 usetz ? " " : "", |
|
2631 usetz ? tzbuf : ""); |
|
2632 break; |
|
2633 } |
|
2634 } |
|
2635 |
|
2636 JSString *str = JS_NewStringCopyZ(cx, buf); |
|
2637 if (!str) |
|
2638 return false; |
|
2639 rval.setString(str); |
|
2640 return true; |
|
2641 } |
|
2642 |
|
2643 static bool |
|
2644 ToLocaleFormatHelper(JSContext *cx, HandleObject obj, const char *format, MutableHandleValue rval) |
|
2645 { |
|
2646 double utctime = obj->as<DateObject>().UTCTime().toNumber(); |
|
2647 |
|
2648 char buf[100]; |
|
2649 if (!IsFinite(utctime)) { |
|
2650 JS_snprintf(buf, sizeof buf, js_NaN_date_str); |
|
2651 } else { |
|
2652 int result_len; |
|
2653 double local = LocalTime(utctime, &cx->runtime()->dateTimeInfo); |
|
2654 PRMJTime split; |
|
2655 new_explode(local, &split, &cx->runtime()->dateTimeInfo); |
|
2656 |
|
2657 /* Let PRMJTime format it. */ |
|
2658 result_len = PRMJ_FormatTime(buf, sizeof buf, format, &split); |
|
2659 |
|
2660 /* If it failed, default to toString. */ |
|
2661 if (result_len == 0) |
|
2662 return date_format(cx, utctime, FORMATSPEC_FULL, rval); |
|
2663 |
|
2664 /* Hacked check against undesired 2-digit year 00/00/00 form. */ |
|
2665 if (strcmp(format, "%x") == 0 && result_len >= 6 && |
|
2666 /* Format %x means use OS settings, which may have 2-digit yr, so |
|
2667 hack end of 3/11/22 or 11.03.22 or 11Mar22 to use 4-digit yr...*/ |
|
2668 !isdigit(buf[result_len - 3]) && |
|
2669 isdigit(buf[result_len - 2]) && isdigit(buf[result_len - 1]) && |
|
2670 /* ...but not if starts with 4-digit year, like 2022/3/11. */ |
|
2671 !(isdigit(buf[0]) && isdigit(buf[1]) && |
|
2672 isdigit(buf[2]) && isdigit(buf[3]))) { |
|
2673 JS_snprintf(buf + (result_len - 2), (sizeof buf) - (result_len - 2), |
|
2674 "%d", js_DateGetYear(cx, obj)); |
|
2675 } |
|
2676 |
|
2677 } |
|
2678 |
|
2679 if (cx->runtime()->localeCallbacks && cx->runtime()->localeCallbacks->localeToUnicode) |
|
2680 return cx->runtime()->localeCallbacks->localeToUnicode(cx, buf, rval); |
|
2681 |
|
2682 JSString *str = JS_NewStringCopyZ(cx, buf); |
|
2683 if (!str) |
|
2684 return false; |
|
2685 rval.setString(str); |
|
2686 return true; |
|
2687 } |
|
2688 |
|
2689 #if !EXPOSE_INTL_API |
|
2690 static bool |
|
2691 ToLocaleStringHelper(JSContext *cx, Handle<DateObject*> dateObj, MutableHandleValue rval) |
|
2692 { |
|
2693 /* |
|
2694 * Use '%#c' for windows, because '%c' is backward-compatible and non-y2k |
|
2695 * with msvc; '%#c' requests that a full year be used in the result string. |
|
2696 */ |
|
2697 return ToLocaleFormatHelper(cx, dateObj, |
|
2698 #if defined(_WIN32) && !defined(__MWERKS__) |
|
2699 "%#c" |
|
2700 #else |
|
2701 "%c" |
|
2702 #endif |
|
2703 , rval); |
|
2704 } |
|
2705 |
|
2706 /* ES5 15.9.5.5. */ |
|
2707 MOZ_ALWAYS_INLINE bool |
|
2708 date_toLocaleString_impl(JSContext *cx, CallArgs args) |
|
2709 { |
|
2710 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); |
|
2711 return ToLocaleStringHelper(cx, dateObj, args.rval()); |
|
2712 } |
|
2713 |
|
2714 static bool |
|
2715 date_toLocaleString(JSContext *cx, unsigned argc, Value *vp) |
|
2716 { |
|
2717 CallArgs args = CallArgsFromVp(argc, vp); |
|
2718 return CallNonGenericMethod<IsDate, date_toLocaleString_impl>(cx, args); |
|
2719 } |
|
2720 |
|
2721 /* ES5 15.9.5.6. */ |
|
2722 MOZ_ALWAYS_INLINE bool |
|
2723 date_toLocaleDateString_impl(JSContext *cx, CallArgs args) |
|
2724 { |
|
2725 /* |
|
2726 * Use '%#x' for windows, because '%x' is backward-compatible and non-y2k |
|
2727 * with msvc; '%#x' requests that a full year be used in the result string. |
|
2728 */ |
|
2729 static const char format[] = |
|
2730 #if defined(_WIN32) && !defined(__MWERKS__) |
|
2731 "%#x" |
|
2732 #else |
|
2733 "%x" |
|
2734 #endif |
|
2735 ; |
|
2736 |
|
2737 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); |
|
2738 return ToLocaleFormatHelper(cx, dateObj, format, args.rval()); |
|
2739 } |
|
2740 |
|
2741 static bool |
|
2742 date_toLocaleDateString(JSContext *cx, unsigned argc, Value *vp) |
|
2743 { |
|
2744 CallArgs args = CallArgsFromVp(argc, vp); |
|
2745 return CallNonGenericMethod<IsDate, date_toLocaleDateString_impl>(cx, args); |
|
2746 } |
|
2747 |
|
2748 /* ES5 15.9.5.7. */ |
|
2749 MOZ_ALWAYS_INLINE bool |
|
2750 date_toLocaleTimeString_impl(JSContext *cx, CallArgs args) |
|
2751 { |
|
2752 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); |
|
2753 return ToLocaleFormatHelper(cx, dateObj, "%X", args.rval()); |
|
2754 } |
|
2755 |
|
2756 static bool |
|
2757 date_toLocaleTimeString(JSContext *cx, unsigned argc, Value *vp) |
|
2758 { |
|
2759 CallArgs args = CallArgsFromVp(argc, vp); |
|
2760 return CallNonGenericMethod<IsDate, date_toLocaleTimeString_impl>(cx, args); |
|
2761 } |
|
2762 #endif /* !EXPOSE_INTL_API */ |
|
2763 |
|
2764 MOZ_ALWAYS_INLINE bool |
|
2765 date_toLocaleFormat_impl(JSContext *cx, CallArgs args) |
|
2766 { |
|
2767 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); |
|
2768 |
|
2769 if (args.length() == 0) { |
|
2770 /* |
|
2771 * Use '%#c' for windows, because '%c' is backward-compatible and non-y2k |
|
2772 * with msvc; '%#c' requests that a full year be used in the result string. |
|
2773 */ |
|
2774 return ToLocaleFormatHelper(cx, dateObj, |
|
2775 #if defined(_WIN32) && !defined(__MWERKS__) |
|
2776 "%#c" |
|
2777 #else |
|
2778 "%c" |
|
2779 #endif |
|
2780 , args.rval()); |
|
2781 } |
|
2782 |
|
2783 RootedString fmt(cx, ToString<CanGC>(cx, args[0])); |
|
2784 if (!fmt) |
|
2785 return false; |
|
2786 |
|
2787 JSAutoByteString fmtbytes(cx, fmt); |
|
2788 if (!fmtbytes) |
|
2789 return false; |
|
2790 |
|
2791 return ToLocaleFormatHelper(cx, dateObj, fmtbytes.ptr(), args.rval()); |
|
2792 } |
|
2793 |
|
2794 static bool |
|
2795 date_toLocaleFormat(JSContext *cx, unsigned argc, Value *vp) |
|
2796 { |
|
2797 CallArgs args = CallArgsFromVp(argc, vp); |
|
2798 return CallNonGenericMethod<IsDate, date_toLocaleFormat_impl>(cx, args); |
|
2799 } |
|
2800 |
|
2801 /* ES5 15.9.5.4. */ |
|
2802 MOZ_ALWAYS_INLINE bool |
|
2803 date_toTimeString_impl(JSContext *cx, CallArgs args) |
|
2804 { |
|
2805 return date_format(cx, args.thisv().toObject().as<DateObject>().UTCTime().toNumber(), |
|
2806 FORMATSPEC_TIME, args.rval()); |
|
2807 } |
|
2808 |
|
2809 static bool |
|
2810 date_toTimeString(JSContext *cx, unsigned argc, Value *vp) |
|
2811 { |
|
2812 CallArgs args = CallArgsFromVp(argc, vp); |
|
2813 return CallNonGenericMethod<IsDate, date_toTimeString_impl>(cx, args); |
|
2814 } |
|
2815 |
|
2816 /* ES5 15.9.5.3. */ |
|
2817 MOZ_ALWAYS_INLINE bool |
|
2818 date_toDateString_impl(JSContext *cx, CallArgs args) |
|
2819 { |
|
2820 return date_format(cx, args.thisv().toObject().as<DateObject>().UTCTime().toNumber(), |
|
2821 FORMATSPEC_DATE, args.rval()); |
|
2822 } |
|
2823 |
|
2824 static bool |
|
2825 date_toDateString(JSContext *cx, unsigned argc, Value *vp) |
|
2826 { |
|
2827 CallArgs args = CallArgsFromVp(argc, vp); |
|
2828 return CallNonGenericMethod<IsDate, date_toDateString_impl>(cx, args); |
|
2829 } |
|
2830 |
|
2831 #if JS_HAS_TOSOURCE |
|
2832 MOZ_ALWAYS_INLINE bool |
|
2833 date_toSource_impl(JSContext *cx, CallArgs args) |
|
2834 { |
|
2835 StringBuffer sb(cx); |
|
2836 if (!sb.append("(new Date(") || |
|
2837 !NumberValueToStringBuffer(cx, args.thisv().toObject().as<DateObject>().UTCTime(), sb) || |
|
2838 !sb.append("))")) |
|
2839 { |
|
2840 return false; |
|
2841 } |
|
2842 |
|
2843 JSString *str = sb.finishString(); |
|
2844 if (!str) |
|
2845 return false; |
|
2846 args.rval().setString(str); |
|
2847 return true; |
|
2848 } |
|
2849 |
|
2850 static bool |
|
2851 date_toSource(JSContext *cx, unsigned argc, Value *vp) |
|
2852 { |
|
2853 CallArgs args = CallArgsFromVp(argc, vp); |
|
2854 return CallNonGenericMethod<IsDate, date_toSource_impl>(cx, args); |
|
2855 } |
|
2856 #endif |
|
2857 |
|
2858 MOZ_ALWAYS_INLINE bool |
|
2859 date_toString_impl(JSContext *cx, CallArgs args) |
|
2860 { |
|
2861 return date_format(cx, args.thisv().toObject().as<DateObject>().UTCTime().toNumber(), |
|
2862 FORMATSPEC_FULL, args.rval()); |
|
2863 } |
|
2864 |
|
2865 static bool |
|
2866 date_toString(JSContext *cx, unsigned argc, Value *vp) |
|
2867 { |
|
2868 CallArgs args = CallArgsFromVp(argc, vp); |
|
2869 return CallNonGenericMethod<IsDate, date_toString_impl>(cx, args); |
|
2870 } |
|
2871 |
|
2872 MOZ_ALWAYS_INLINE bool |
|
2873 date_valueOf_impl(JSContext *cx, CallArgs args) |
|
2874 { |
|
2875 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); |
|
2876 args.rval().set(dateObj->UTCTime()); |
|
2877 return true; |
|
2878 } |
|
2879 |
|
2880 static bool |
|
2881 date_valueOf(JSContext *cx, unsigned argc, Value *vp) |
|
2882 { |
|
2883 CallArgs args = CallArgsFromVp(argc, vp); |
|
2884 return CallNonGenericMethod<IsDate, date_valueOf_impl>(cx, args); |
|
2885 } |
|
2886 |
|
2887 static const JSFunctionSpec date_static_methods[] = { |
|
2888 JS_FN("UTC", date_UTC, MAXARGS,0), |
|
2889 JS_FN("parse", date_parse, 1,0), |
|
2890 JS_FN("now", date_now, 0,0), |
|
2891 JS_FS_END |
|
2892 }; |
|
2893 |
|
2894 static const JSFunctionSpec date_methods[] = { |
|
2895 JS_FN("getTime", date_getTime, 0,0), |
|
2896 JS_FN("getTimezoneOffset", date_getTimezoneOffset, 0,0), |
|
2897 JS_FN("getYear", date_getYear, 0,0), |
|
2898 JS_FN("getFullYear", date_getFullYear, 0,0), |
|
2899 JS_FN("getUTCFullYear", date_getUTCFullYear, 0,0), |
|
2900 JS_FN("getMonth", date_getMonth, 0,0), |
|
2901 JS_FN("getUTCMonth", date_getUTCMonth, 0,0), |
|
2902 JS_FN("getDate", date_getDate, 0,0), |
|
2903 JS_FN("getUTCDate", date_getUTCDate, 0,0), |
|
2904 JS_FN("getDay", date_getDay, 0,0), |
|
2905 JS_FN("getUTCDay", date_getUTCDay, 0,0), |
|
2906 JS_FN("getHours", date_getHours, 0,0), |
|
2907 JS_FN("getUTCHours", date_getUTCHours, 0,0), |
|
2908 JS_FN("getMinutes", date_getMinutes, 0,0), |
|
2909 JS_FN("getUTCMinutes", date_getUTCMinutes, 0,0), |
|
2910 JS_FN("getSeconds", date_getUTCSeconds, 0,0), |
|
2911 JS_FN("getUTCSeconds", date_getUTCSeconds, 0,0), |
|
2912 JS_FN("getMilliseconds", date_getUTCMilliseconds, 0,0), |
|
2913 JS_FN("getUTCMilliseconds", date_getUTCMilliseconds, 0,0), |
|
2914 JS_FN("setTime", date_setTime, 1,0), |
|
2915 JS_FN("setYear", date_setYear, 1,0), |
|
2916 JS_FN("setFullYear", date_setFullYear, 3,0), |
|
2917 JS_FN("setUTCFullYear", date_setUTCFullYear, 3,0), |
|
2918 JS_FN("setMonth", date_setMonth, 2,0), |
|
2919 JS_FN("setUTCMonth", date_setUTCMonth, 2,0), |
|
2920 JS_FN("setDate", date_setDate, 1,0), |
|
2921 JS_FN("setUTCDate", date_setUTCDate, 1,0), |
|
2922 JS_FN("setHours", date_setHours, 4,0), |
|
2923 JS_FN("setUTCHours", date_setUTCHours, 4,0), |
|
2924 JS_FN("setMinutes", date_setMinutes, 3,0), |
|
2925 JS_FN("setUTCMinutes", date_setUTCMinutes, 3,0), |
|
2926 JS_FN("setSeconds", date_setSeconds, 2,0), |
|
2927 JS_FN("setUTCSeconds", date_setUTCSeconds, 2,0), |
|
2928 JS_FN("setMilliseconds", date_setMilliseconds, 1,0), |
|
2929 JS_FN("setUTCMilliseconds", date_setUTCMilliseconds, 1,0), |
|
2930 JS_FN("toUTCString", date_toGMTString, 0,0), |
|
2931 JS_FN("toLocaleFormat", date_toLocaleFormat, 0,0), |
|
2932 #if EXPOSE_INTL_API |
|
2933 JS_SELF_HOSTED_FN(js_toLocaleString_str, "Date_toLocaleString", 0,0), |
|
2934 JS_SELF_HOSTED_FN("toLocaleDateString", "Date_toLocaleDateString", 0,0), |
|
2935 JS_SELF_HOSTED_FN("toLocaleTimeString", "Date_toLocaleTimeString", 0,0), |
|
2936 #else |
|
2937 JS_FN(js_toLocaleString_str, date_toLocaleString, 0,0), |
|
2938 JS_FN("toLocaleDateString", date_toLocaleDateString, 0,0), |
|
2939 JS_FN("toLocaleTimeString", date_toLocaleTimeString, 0,0), |
|
2940 #endif |
|
2941 JS_FN("toDateString", date_toDateString, 0,0), |
|
2942 JS_FN("toTimeString", date_toTimeString, 0,0), |
|
2943 JS_FN("toISOString", date_toISOString, 0,0), |
|
2944 JS_FN(js_toJSON_str, date_toJSON, 1,0), |
|
2945 #if JS_HAS_TOSOURCE |
|
2946 JS_FN(js_toSource_str, date_toSource, 0,0), |
|
2947 #endif |
|
2948 JS_FN(js_toString_str, date_toString, 0,0), |
|
2949 JS_FN(js_valueOf_str, date_valueOf, 0,0), |
|
2950 JS_FS_END |
|
2951 }; |
|
2952 |
|
2953 bool |
|
2954 js_Date(JSContext *cx, unsigned argc, Value *vp) |
|
2955 { |
|
2956 CallArgs args = CallArgsFromVp(argc, vp); |
|
2957 |
|
2958 /* Date called as function. */ |
|
2959 if (!args.isConstructing()) |
|
2960 return date_format(cx, NowAsMillis(), FORMATSPEC_FULL, args.rval()); |
|
2961 |
|
2962 /* Date called as constructor. */ |
|
2963 double d; |
|
2964 if (args.length() == 0) { |
|
2965 /* ES5 15.9.3.3. */ |
|
2966 d = NowAsMillis(); |
|
2967 } else if (args.length() == 1) { |
|
2968 /* ES5 15.9.3.2. */ |
|
2969 |
|
2970 /* Step 1. */ |
|
2971 if (!ToPrimitive(cx, args[0])) |
|
2972 return false; |
|
2973 |
|
2974 if (args[0].isString()) { |
|
2975 /* Step 2. */ |
|
2976 JSString *str = args[0].toString(); |
|
2977 if (!str) |
|
2978 return false; |
|
2979 |
|
2980 JSLinearString *linearStr = str->ensureLinear(cx); |
|
2981 if (!linearStr) |
|
2982 return false; |
|
2983 |
|
2984 if (!date_parseString(linearStr, &d, &cx->runtime()->dateTimeInfo)) |
|
2985 d = GenericNaN(); |
|
2986 else |
|
2987 d = TimeClip(d); |
|
2988 } else { |
|
2989 /* Step 3. */ |
|
2990 if (!ToNumber(cx, args[0], &d)) |
|
2991 return false; |
|
2992 d = TimeClip(d); |
|
2993 } |
|
2994 } else { |
|
2995 double msec_time; |
|
2996 if (!date_msecFromArgs(cx, args, &msec_time)) |
|
2997 return false; |
|
2998 |
|
2999 if (IsFinite(msec_time)) { |
|
3000 msec_time = UTC(msec_time, &cx->runtime()->dateTimeInfo); |
|
3001 msec_time = TimeClip(msec_time); |
|
3002 } |
|
3003 d = msec_time; |
|
3004 } |
|
3005 |
|
3006 JSObject *obj = js_NewDateObjectMsec(cx, d); |
|
3007 if (!obj) |
|
3008 return false; |
|
3009 |
|
3010 args.rval().setObject(*obj); |
|
3011 return true; |
|
3012 } |
|
3013 |
|
3014 static bool |
|
3015 FinishDateClassInit(JSContext *cx, HandleObject ctor, HandleObject proto) |
|
3016 { |
|
3017 proto->as<DateObject>().setUTCTime(GenericNaN()); |
|
3018 |
|
3019 /* |
|
3020 * Date.prototype.toGMTString has the same initial value as |
|
3021 * Date.prototype.toUTCString. |
|
3022 */ |
|
3023 RootedValue toUTCStringFun(cx); |
|
3024 RootedId toUTCStringId(cx, NameToId(cx->names().toUTCString)); |
|
3025 RootedId toGMTStringId(cx, NameToId(cx->names().toGMTString)); |
|
3026 return baseops::GetProperty(cx, proto, toUTCStringId, &toUTCStringFun) && |
|
3027 baseops::DefineGeneric(cx, proto, toGMTStringId, toUTCStringFun, |
|
3028 JS_PropertyStub, JS_StrictPropertyStub, 0); |
|
3029 } |
|
3030 |
|
3031 const Class DateObject::class_ = { |
|
3032 js_Date_str, |
|
3033 JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) | |
|
3034 JSCLASS_HAS_CACHED_PROTO(JSProto_Date), |
|
3035 JS_PropertyStub, /* addProperty */ |
|
3036 JS_DeletePropertyStub, /* delProperty */ |
|
3037 JS_PropertyStub, /* getProperty */ |
|
3038 JS_StrictPropertyStub, /* setProperty */ |
|
3039 JS_EnumerateStub, |
|
3040 JS_ResolveStub, |
|
3041 date_convert, |
|
3042 nullptr, /* finalize */ |
|
3043 nullptr, /* call */ |
|
3044 nullptr, /* hasInstance */ |
|
3045 nullptr, /* construct */ |
|
3046 nullptr, /* trace */ |
|
3047 { |
|
3048 GenericCreateConstructor<js_Date, NAME_OFFSET(Date), MAXARGS>, |
|
3049 GenericCreatePrototype<&DateObject::class_>, |
|
3050 date_static_methods, |
|
3051 date_methods, |
|
3052 FinishDateClassInit |
|
3053 } |
|
3054 }; |
|
3055 |
|
3056 JS_FRIEND_API(JSObject *) |
|
3057 js_NewDateObjectMsec(JSContext *cx, double msec_time) |
|
3058 { |
|
3059 JSObject *obj = NewBuiltinClassInstance(cx, &DateObject::class_); |
|
3060 if (!obj) |
|
3061 return nullptr; |
|
3062 obj->as<DateObject>().setUTCTime(msec_time); |
|
3063 return obj; |
|
3064 } |
|
3065 |
|
3066 JS_FRIEND_API(JSObject *) |
|
3067 js_NewDateObject(JSContext *cx, int year, int mon, int mday, |
|
3068 int hour, int min, int sec) |
|
3069 { |
|
3070 JS_ASSERT(mon < 12); |
|
3071 double msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0); |
|
3072 return js_NewDateObjectMsec(cx, UTC(msec_time, &cx->runtime()->dateTimeInfo)); |
|
3073 } |
|
3074 |
|
3075 JS_FRIEND_API(bool) |
|
3076 js_DateIsValid(JSObject *obj) |
|
3077 { |
|
3078 return obj->is<DateObject>() && !IsNaN(obj->as<DateObject>().UTCTime().toNumber()); |
|
3079 } |
|
3080 |
|
3081 JS_FRIEND_API(int) |
|
3082 js_DateGetYear(JSContext *cx, JSObject *obj) |
|
3083 { |
|
3084 /* Preserve legacy API behavior of returning 0 for invalid dates. */ |
|
3085 JS_ASSERT(obj); |
|
3086 double localtime = obj->as<DateObject>().cachedLocalTime(&cx->runtime()->dateTimeInfo); |
|
3087 if (IsNaN(localtime)) |
|
3088 return 0; |
|
3089 |
|
3090 return (int) YearFromTime(localtime); |
|
3091 } |
|
3092 |
|
3093 JS_FRIEND_API(int) |
|
3094 js_DateGetMonth(JSContext *cx, JSObject *obj) |
|
3095 { |
|
3096 JS_ASSERT(obj); |
|
3097 double localtime = obj->as<DateObject>().cachedLocalTime(&cx->runtime()->dateTimeInfo); |
|
3098 if (IsNaN(localtime)) |
|
3099 return 0; |
|
3100 |
|
3101 return (int) MonthFromTime(localtime); |
|
3102 } |
|
3103 |
|
3104 JS_FRIEND_API(int) |
|
3105 js_DateGetDate(JSContext *cx, JSObject *obj) |
|
3106 { |
|
3107 JS_ASSERT(obj); |
|
3108 double localtime = obj->as<DateObject>().cachedLocalTime(&cx->runtime()->dateTimeInfo); |
|
3109 if (IsNaN(localtime)) |
|
3110 return 0; |
|
3111 |
|
3112 return (int) DateFromTime(localtime); |
|
3113 } |
|
3114 |
|
3115 JS_FRIEND_API(int) |
|
3116 js_DateGetHours(JSContext *cx, JSObject *obj) |
|
3117 { |
|
3118 JS_ASSERT(obj); |
|
3119 double localtime = obj->as<DateObject>().cachedLocalTime(&cx->runtime()->dateTimeInfo); |
|
3120 if (IsNaN(localtime)) |
|
3121 return 0; |
|
3122 |
|
3123 return (int) HourFromTime(localtime); |
|
3124 } |
|
3125 |
|
3126 JS_FRIEND_API(int) |
|
3127 js_DateGetMinutes(JSContext *cx, JSObject *obj) |
|
3128 { |
|
3129 JS_ASSERT(obj); |
|
3130 double localtime = obj->as<DateObject>().cachedLocalTime(&cx->runtime()->dateTimeInfo); |
|
3131 if (IsNaN(localtime)) |
|
3132 return 0; |
|
3133 |
|
3134 return (int) MinFromTime(localtime); |
|
3135 } |
|
3136 |
|
3137 JS_FRIEND_API(int) |
|
3138 js_DateGetSeconds(JSObject *obj) |
|
3139 { |
|
3140 if (!obj->is<DateObject>()) |
|
3141 return 0; |
|
3142 |
|
3143 double utctime = obj->as<DateObject>().UTCTime().toNumber(); |
|
3144 if (IsNaN(utctime)) |
|
3145 return 0; |
|
3146 return (int) SecFromTime(utctime); |
|
3147 } |
|
3148 |
|
3149 JS_FRIEND_API(double) |
|
3150 js_DateGetMsecSinceEpoch(JSObject *obj) |
|
3151 { |
|
3152 return obj->is<DateObject>() ? obj->as<DateObject>().UTCTime().toNumber() : 0; |
|
3153 } |
|
3154 |
|
3155 |
|
3156 static const NativeImpl sReadOnlyDateMethods[] = { |
|
3157 DateObject::getTime_impl, |
|
3158 DateObject::getYear_impl, |
|
3159 DateObject::getFullYear_impl, |
|
3160 DateObject::getUTCFullYear_impl, |
|
3161 DateObject::getMonth_impl, |
|
3162 DateObject::getUTCMonth_impl, |
|
3163 DateObject::getDate_impl, |
|
3164 DateObject::getUTCDate_impl, |
|
3165 DateObject::getDay_impl, |
|
3166 DateObject::getUTCDay_impl, |
|
3167 DateObject::getHours_impl, |
|
3168 DateObject::getUTCHours_impl, |
|
3169 DateObject::getMinutes_impl, |
|
3170 DateObject::getUTCMinutes_impl, |
|
3171 DateObject::getUTCSeconds_impl, |
|
3172 DateObject::getUTCMilliseconds_impl, |
|
3173 DateObject::getTimezoneOffset_impl, |
|
3174 date_toGMTString_impl, |
|
3175 date_toISOString_impl, |
|
3176 date_toLocaleFormat_impl, |
|
3177 date_toTimeString_impl, |
|
3178 date_toDateString_impl, |
|
3179 date_toSource_impl, |
|
3180 date_toString_impl, |
|
3181 date_valueOf_impl |
|
3182 }; |