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