| |
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 }; |