blob: 9c42a04f67b4fe2292e3b30c05d778d92d4d87d7 [file] [log] [blame]
Andrei Popescu31002712010-02-23 13:46:05 +00001// Copyright 2006-2008 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28
29// This file relies on the fact that the following declarations have been made
30// in v8natives.js:
31// const $isFinite = GlobalIsFinite;
32
33// -------------------------------------------------------------------
34
35// This file contains date support implemented in JavaScript.
36
37
38// Keep reference to original values of some global properties. This
39// has the added benefit that the code in this file is isolated from
40// changes to these properties.
41const $Date = global.Date;
42
43// Helper function to throw error.
44function ThrowDateTypeError() {
45 throw new $TypeError('this is not a Date object.');
46}
47
48// ECMA 262 - 5.2
49function Modulo(value, remainder) {
50 var mod = value % remainder;
51 // Guard against returning -0.
52 if (mod == 0) return 0;
53 return mod >= 0 ? mod : mod + remainder;
54}
55
56
57function TimeWithinDay(time) {
58 return Modulo(time, msPerDay);
59}
60
61
62// ECMA 262 - 15.9.1.3
63function DaysInYear(year) {
64 if (year % 4 != 0) return 365;
65 if ((year % 100 == 0) && (year % 400 != 0)) return 365;
66 return 366;
67}
68
69
70function DayFromYear(year) {
71 return 365 * (year-1970)
72 + FLOOR((year-1969)/4)
73 - FLOOR((year-1901)/100)
74 + FLOOR((year-1601)/400);
75}
76
77
78function TimeFromYear(year) {
79 return msPerDay * DayFromYear(year);
80}
81
82
83function InLeapYear(time) {
84 return DaysInYear(YEAR_FROM_TIME(time)) == 366 ? 1 : 0;
85}
86
87
88function DayWithinYear(time) {
89 return DAY(time) - DayFromYear(YEAR_FROM_TIME(time));
90}
91
92
93// ECMA 262 - 15.9.1.9
94function EquivalentYear(year) {
95 // Returns an equivalent year in the range [2008-2035] matching
96 // - leap year.
97 // - week day of first day.
98 var time = TimeFromYear(year);
99 var recent_year = (InLeapYear(time) == 0 ? 1967 : 1956) +
100 (WeekDay(time) * 12) % 28;
101 // Find the year in the range 2008..2037 that is equivalent mod 28.
102 // Add 3*28 to give a positive argument to the modulus operator.
103 return 2008 + (recent_year + 3*28 - 2008) % 28;
104}
105
106
107function EquivalentTime(t) {
108 // The issue here is that some library calls don't work right for dates
109 // that cannot be represented using a non-negative signed 32 bit integer
110 // (measured in whole seconds based on the 1970 epoch).
111 // We solve this by mapping the time to a year with same leap-year-ness
112 // and same starting day for the year. The ECMAscript specification says
113 // we must do this, but for compatibility with other browsers, we use
114 // the actual year if it is in the range 1970..2037
115 if (t >= 0 && t <= 2.1e12) return t;
Steve Block6ded16b2010-05-10 14:33:55 +0100116
117 var day = MakeDay(EquivalentYear(YEAR_FROM_TIME(t)),
118 MONTH_FROM_TIME(t),
119 DATE_FROM_TIME(t));
120 return MakeDate(day, TimeWithinDay(t));
Andrei Popescu31002712010-02-23 13:46:05 +0000121}
122
123
Andrei Popescu6599b9d2010-04-28 13:01:47 +0100124// local_time_offset is initialized when the DST_offset_cache is missed.
125// It must not be used until after a call to DaylightSavingsOffset().
126// In this way, only one check, for a DST cache miss, is needed.
127var local_time_offset;
128
129
130// Because computing the DST offset is an expensive operation,
131// we keep a cache of the last computed DST offset along with a time interval
Andrei Popescu31002712010-02-23 13:46:05 +0000132// where we know the cache is valid.
Andrei Popescu6599b9d2010-04-28 13:01:47 +0100133// When the cache is valid, local_time_offset is also valid.
Andrei Popescu31002712010-02-23 13:46:05 +0000134var DST_offset_cache = {
135 // Cached DST offset.
136 offset: 0,
137 // Time interval where the cached offset is valid.
138 start: 0, end: -1,
139 // Size of next interval expansion.
140 increment: 0
141};
142
143
144// NOTE: The implementation relies on the fact that no time zones have
145// more than one daylight savings offset change per month.
146// If this function is called with NaN it returns NaN.
147function DaylightSavingsOffset(t) {
148 // Load the cache object from the builtins object.
149 var cache = DST_offset_cache;
150
151 // Cache the start and the end in local variables for fast access.
152 var start = cache.start;
153 var end = cache.end;
154
155 if (start <= t) {
156 // If the time fits in the cached interval, return the cached offset.
157 if (t <= end) return cache.offset;
158
Andrei Popescu6599b9d2010-04-28 13:01:47 +0100159 // If the cache misses, the local_time_offset may not be initialized.
160 if (IS_UNDEFINED(local_time_offset)) {
161 local_time_offset = %DateLocalTimeOffset();
162 }
163
Andrei Popescu31002712010-02-23 13:46:05 +0000164 // Compute a possible new interval end.
165 var new_end = end + cache.increment;
166
167 if (t <= new_end) {
168 var end_offset = %DateDaylightSavingsOffset(EquivalentTime(new_end));
169 if (cache.offset == end_offset) {
170 // If the offset at the end of the new interval still matches
171 // the offset in the cache, we grow the cached time interval
172 // and return the offset.
173 cache.end = new_end;
174 cache.increment = msPerMonth;
175 return end_offset;
176 } else {
177 var offset = %DateDaylightSavingsOffset(EquivalentTime(t));
178 if (offset == end_offset) {
179 // The offset at the given time is equal to the offset at the
180 // new end of the interval, so that means that we've just skipped
181 // the point in time where the DST offset change occurred. Updated
182 // the interval to reflect this and reset the increment.
183 cache.start = t;
184 cache.end = new_end;
185 cache.increment = msPerMonth;
186 } else {
187 // The interval contains a DST offset change and the given time is
188 // before it. Adjust the increment to avoid a linear search for
189 // the offset change point and change the end of the interval.
190 cache.increment /= 3;
191 cache.end = t;
192 }
193 // Update the offset in the cache and return it.
194 cache.offset = offset;
195 return offset;
196 }
197 }
198 }
199
Andrei Popescu6599b9d2010-04-28 13:01:47 +0100200 // If the cache misses, the local_time_offset may not be initialized.
201 if (IS_UNDEFINED(local_time_offset)) {
202 local_time_offset = %DateLocalTimeOffset();
203 }
Andrei Popescu31002712010-02-23 13:46:05 +0000204 // Compute the DST offset for the time and shrink the cache interval
205 // to only contain the time. This allows fast repeated DST offset
206 // computations for the same time.
207 var offset = %DateDaylightSavingsOffset(EquivalentTime(t));
208 cache.offset = offset;
209 cache.start = cache.end = t;
210 cache.increment = msPerMonth;
211 return offset;
212}
213
214
215var timezone_cache_time = $NaN;
216var timezone_cache_timezone;
217
218function LocalTimezone(t) {
219 if (NUMBER_IS_NAN(t)) return "";
220 if (t == timezone_cache_time) {
221 return timezone_cache_timezone;
222 }
223 var timezone = %DateLocalTimezone(EquivalentTime(t));
224 timezone_cache_time = t;
225 timezone_cache_timezone = timezone;
226 return timezone;
227}
228
229
230function WeekDay(time) {
231 return Modulo(DAY(time) + 4, 7);
232}
233
Andrei Popescu31002712010-02-23 13:46:05 +0000234
235function LocalTime(time) {
236 if (NUMBER_IS_NAN(time)) return time;
Andrei Popescu6599b9d2010-04-28 13:01:47 +0100237 // DaylightSavingsOffset called before local_time_offset used.
238 return time + DaylightSavingsOffset(time) + local_time_offset;
Andrei Popescu31002712010-02-23 13:46:05 +0000239}
240
Kristian Monsen25f61362010-05-21 11:50:48 +0100241
242var ltcache = {
243 key: null,
244 val: null
245};
246
Andrei Popescu31002712010-02-23 13:46:05 +0000247function LocalTimeNoCheck(time) {
Kristian Monsen25f61362010-05-21 11:50:48 +0100248 var ltc = ltcache;
249 if (%_ObjectEquals(time, ltc.key)) return ltc.val;
Steve Block6ded16b2010-05-10 14:33:55 +0100250 if (time < -MAX_TIME_MS || time > MAX_TIME_MS) {
251 return $NaN;
252 }
253
Andrei Popescu31002712010-02-23 13:46:05 +0000254 // Inline the DST offset cache checks for speed.
Andrei Popescu6599b9d2010-04-28 13:01:47 +0100255 // The cache is hit, or DaylightSavingsOffset is called,
256 // before local_time_offset is used.
Andrei Popescu31002712010-02-23 13:46:05 +0000257 var cache = DST_offset_cache;
258 if (cache.start <= time && time <= cache.end) {
259 var dst_offset = cache.offset;
260 } else {
261 var dst_offset = DaylightSavingsOffset(time);
262 }
Kristian Monsen25f61362010-05-21 11:50:48 +0100263 ltc.key = time;
264 return (ltc.val = time + local_time_offset + dst_offset);
Andrei Popescu31002712010-02-23 13:46:05 +0000265}
266
267
268function UTC(time) {
269 if (NUMBER_IS_NAN(time)) return time;
Andrei Popescu6599b9d2010-04-28 13:01:47 +0100270 // local_time_offset is needed before the call to DaylightSavingsOffset,
271 // so it may be uninitialized.
272 if (IS_UNDEFINED(local_time_offset)) {
273 local_time_offset = %DateLocalTimeOffset();
274 }
Andrei Popescu31002712010-02-23 13:46:05 +0000275 var tmp = time - local_time_offset;
276 return tmp - DaylightSavingsOffset(tmp);
277}
278
279
280// ECMA 262 - 15.9.1.11
281function MakeTime(hour, min, sec, ms) {
282 if (!$isFinite(hour)) return $NaN;
283 if (!$isFinite(min)) return $NaN;
284 if (!$isFinite(sec)) return $NaN;
285 if (!$isFinite(ms)) return $NaN;
286 return TO_INTEGER(hour) * msPerHour
287 + TO_INTEGER(min) * msPerMinute
288 + TO_INTEGER(sec) * msPerSecond
289 + TO_INTEGER(ms);
290}
291
292
293// ECMA 262 - 15.9.1.12
294function TimeInYear(year) {
295 return DaysInYear(year) * msPerDay;
296}
297
298
Steve Block6ded16b2010-05-10 14:33:55 +0100299var ymd_from_time_cache = [$NaN, $NaN, $NaN];
300var ymd_from_time_cached_time = $NaN;
Andrei Popescu31002712010-02-23 13:46:05 +0000301
Steve Block6ded16b2010-05-10 14:33:55 +0100302function YearFromTime(t) {
303 if (t !== ymd_from_time_cached_time) {
304 if (!$isFinite(t)) {
305 return $NaN;
Andrei Popescu31002712010-02-23 13:46:05 +0000306 }
Steve Block6ded16b2010-05-10 14:33:55 +0100307
308 %DateYMDFromTime(t, ymd_from_time_cache);
309 ymd_from_time_cached_time = t
310 }
311
312 return ymd_from_time_cache[0];
313}
314
315function MonthFromTime(t) {
316 if (t !== ymd_from_time_cached_time) {
317 if (!$isFinite(t)) {
318 return $NaN;
Andrei Popescu31002712010-02-23 13:46:05 +0000319 }
Steve Block6ded16b2010-05-10 14:33:55 +0100320 %DateYMDFromTime(t, ymd_from_time_cache);
321 ymd_from_time_cached_time = t
Andrei Popescu31002712010-02-23 13:46:05 +0000322 }
Steve Block6ded16b2010-05-10 14:33:55 +0100323
324 return ymd_from_time_cache[1];
Andrei Popescu31002712010-02-23 13:46:05 +0000325}
326
Steve Block6ded16b2010-05-10 14:33:55 +0100327function DateFromTime(t) {
328 if (t !== ymd_from_time_cached_time) {
329 if (!$isFinite(t)) {
330 return $NaN;
331 }
Andrei Popescu31002712010-02-23 13:46:05 +0000332
Steve Block6ded16b2010-05-10 14:33:55 +0100333 %DateYMDFromTime(t, ymd_from_time_cache);
334 ymd_from_time_cached_time = t
Andrei Popescu31002712010-02-23 13:46:05 +0000335 }
Steve Block6ded16b2010-05-10 14:33:55 +0100336
337 return ymd_from_time_cache[2];
Andrei Popescu31002712010-02-23 13:46:05 +0000338}
339
340
341// Compute number of days given a year, month, date.
342// Note that month and date can lie outside the normal range.
343// For example:
344// MakeDay(2007, -4, 20) --> MakeDay(2006, 8, 20)
345// MakeDay(2007, -33, 1) --> MakeDay(2004, 3, 1)
346// MakeDay(2007, 14, -50) --> MakeDay(2007, 8, 11)
347function MakeDay(year, month, date) {
348 if (!$isFinite(year) || !$isFinite(month) || !$isFinite(date)) return $NaN;
349
Steve Block8defd9f2010-07-08 12:39:36 +0100350 // Convert to integer and map -0 to 0.
351 year = TO_INTEGER_MAP_MINUS_ZERO(year);
352 month = TO_INTEGER_MAP_MINUS_ZERO(month);
353 date = TO_INTEGER_MAP_MINUS_ZERO(date);
Andrei Popescu31002712010-02-23 13:46:05 +0000354
Steve Block6ded16b2010-05-10 14:33:55 +0100355 if (year < kMinYear || year > kMaxYear ||
356 month < kMinMonth || month > kMaxMonth ||
357 date < kMinDate || date > kMaxDate) {
358 return $NaN;
Andrei Popescu31002712010-02-23 13:46:05 +0000359 }
360
Steve Block6ded16b2010-05-10 14:33:55 +0100361 // Now we rely on year, month and date being SMIs.
362 return %DateMakeDay(year, month, date);
Andrei Popescu31002712010-02-23 13:46:05 +0000363}
364
365
366// ECMA 262 - 15.9.1.13
367function MakeDate(day, time) {
368 if (!$isFinite(day)) return $NaN;
369 if (!$isFinite(time)) return $NaN;
370 return day * msPerDay + time;
371}
372
373
374// ECMA 262 - 15.9.1.14
375function TimeClip(time) {
376 if (!$isFinite(time)) return $NaN;
377 if ($abs(time) > 8.64E15) return $NaN;
378 return TO_INTEGER(time);
379}
380
381
382// The Date cache is used to limit the cost of parsing the same Date
383// strings over and over again.
384var Date_cache = {
385 // Cached time value.
386 time: $NaN,
387 // Cached year when interpreting the time as a local time. Only
388 // valid when the time matches cached time.
389 year: $NaN,
390 // String input for which the cached time is valid.
391 string: null
392};
393
394
395%SetCode($Date, function(year, month, date, hours, minutes, seconds, ms) {
396 if (!%_IsConstructCall()) {
397 // ECMA 262 - 15.9.2
398 return (new $Date()).toString();
399 }
400
401 // ECMA 262 - 15.9.3
402 var argc = %_ArgumentsLength();
403 var value;
404 if (argc == 0) {
405 value = %DateCurrentTime();
406
407 } else if (argc == 1) {
408 if (IS_NUMBER(year)) {
409 value = TimeClip(year);
410
411 } else if (IS_STRING(year)) {
412 // Probe the Date cache. If we already have a time value for the
413 // given time, we re-use that instead of parsing the string again.
414 var cache = Date_cache;
415 if (cache.string === year) {
416 value = cache.time;
417 } else {
418 value = DateParse(year);
419 if (!NUMBER_IS_NAN(value)) {
420 cache.time = value;
421 cache.year = YEAR_FROM_TIME(LocalTimeNoCheck(value));
422 cache.string = year;
423 }
424 }
425
426 } else {
427 // According to ECMA 262, no hint should be given for this
428 // conversion. However, ToPrimitive defaults to STRING_HINT for
429 // Date objects which will lose precision when the Date
430 // constructor is called with another Date object as its
431 // argument. We therefore use NUMBER_HINT for the conversion,
432 // which is the default for everything else than Date objects.
433 // This makes us behave like KJS and SpiderMonkey.
434 var time = ToPrimitive(year, NUMBER_HINT);
435 value = IS_STRING(time) ? DateParse(time) : TimeClip(ToNumber(time));
436 }
437
438 } else {
439 year = ToNumber(year);
440 month = ToNumber(month);
441 date = argc > 2 ? ToNumber(date) : 1;
442 hours = argc > 3 ? ToNumber(hours) : 0;
443 minutes = argc > 4 ? ToNumber(minutes) : 0;
444 seconds = argc > 5 ? ToNumber(seconds) : 0;
445 ms = argc > 6 ? ToNumber(ms) : 0;
446 year = (!NUMBER_IS_NAN(year) && 0 <= TO_INTEGER(year) && TO_INTEGER(year) <= 99)
447 ? 1900 + TO_INTEGER(year) : year;
448 var day = MakeDay(year, month, date);
449 var time = MakeTime(hours, minutes, seconds, ms);
450 value = TimeClip(UTC(MakeDate(day, time)));
451 }
452 %_SetValueOf(this, value);
453});
454
455
Andrei Popescu31002712010-02-23 13:46:05 +0000456%FunctionSetPrototype($Date, new $Date($NaN));
457
458
459var WeekDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
460var Months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
461
462
463function TwoDigitString(value) {
464 return value < 10 ? "0" + value : "" + value;
465}
466
467
468function DateString(time) {
Andrei Popescu31002712010-02-23 13:46:05 +0000469 return WeekDays[WeekDay(time)] + ' '
Steve Block6ded16b2010-05-10 14:33:55 +0100470 + Months[MonthFromTime(time)] + ' '
471 + TwoDigitString(DateFromTime(time)) + ' '
472 + YearFromTime(time);
Andrei Popescu31002712010-02-23 13:46:05 +0000473}
474
475
476var LongWeekDays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
477var LongMonths = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
478
479
480function LongDateString(time) {
Andrei Popescu31002712010-02-23 13:46:05 +0000481 return LongWeekDays[WeekDay(time)] + ', '
Steve Block6ded16b2010-05-10 14:33:55 +0100482 + LongMonths[MonthFromTime(time)] + ' '
483 + TwoDigitString(DateFromTime(time)) + ', '
484 + YearFromTime(time);
Andrei Popescu31002712010-02-23 13:46:05 +0000485}
486
487
488function TimeString(time) {
489 return TwoDigitString(HOUR_FROM_TIME(time)) + ':'
490 + TwoDigitString(MIN_FROM_TIME(time)) + ':'
491 + TwoDigitString(SEC_FROM_TIME(time));
492}
493
494
495function LocalTimezoneString(time) {
Ben Murdochd69d2e32010-03-30 12:55:27 +0100496 var old_timezone = timezone_cache_timezone;
497 var timezone = LocalTimezone(time);
498 if (old_timezone && timezone != old_timezone) {
499 // If the timezone string has changed from the one that we cached,
500 // the local time offset may now be wrong. So we need to update it
501 // and try again.
502 local_time_offset = %DateLocalTimeOffset();
503 // We also need to invalidate the DST cache as the new timezone may have
504 // different DST times.
505 var dst_cache = DST_offset_cache;
506 dst_cache.start = 0;
507 dst_cache.end = -1;
508 }
509
Andrei Popescu31002712010-02-23 13:46:05 +0000510 var timezoneOffset =
Andrei Popescu6599b9d2010-04-28 13:01:47 +0100511 (DaylightSavingsOffset(time) + local_time_offset) / msPerMinute;
Andrei Popescu31002712010-02-23 13:46:05 +0000512 var sign = (timezoneOffset >= 0) ? 1 : -1;
513 var hours = FLOOR((sign * timezoneOffset)/60);
514 var min = FLOOR((sign * timezoneOffset)%60);
515 var gmt = ' GMT' + ((sign == 1) ? '+' : '-') +
516 TwoDigitString(hours) + TwoDigitString(min);
Ben Murdochd69d2e32010-03-30 12:55:27 +0100517 return gmt + ' (' + timezone + ')';
Andrei Popescu31002712010-02-23 13:46:05 +0000518}
519
520
521function DatePrintString(time) {
522 return DateString(time) + ' ' + TimeString(time);
523}
524
525// -------------------------------------------------------------------
526
527// Reused output buffer. Used when parsing date strings.
Steve Block6ded16b2010-05-10 14:33:55 +0100528var parse_buffer = $Array(8);
Andrei Popescu31002712010-02-23 13:46:05 +0000529
530// ECMA 262 - 15.9.4.2
531function DateParse(string) {
532 var arr = %DateParseString(ToString(string), parse_buffer);
533 if (IS_NULL(arr)) return $NaN;
534
535 var day = MakeDay(arr[0], arr[1], arr[2]);
Steve Block6ded16b2010-05-10 14:33:55 +0100536 var time = MakeTime(arr[3], arr[4], arr[5], arr[6]);
Andrei Popescu31002712010-02-23 13:46:05 +0000537 var date = MakeDate(day, time);
538
Steve Block6ded16b2010-05-10 14:33:55 +0100539 if (IS_NULL(arr[7])) {
Andrei Popescu31002712010-02-23 13:46:05 +0000540 return TimeClip(UTC(date));
541 } else {
Steve Block6ded16b2010-05-10 14:33:55 +0100542 return TimeClip(date - arr[7] * 1000);
Andrei Popescu31002712010-02-23 13:46:05 +0000543 }
544}
545
546
547// ECMA 262 - 15.9.4.3
548function DateUTC(year, month, date, hours, minutes, seconds, ms) {
549 year = ToNumber(year);
550 month = ToNumber(month);
551 var argc = %_ArgumentsLength();
552 date = argc > 2 ? ToNumber(date) : 1;
553 hours = argc > 3 ? ToNumber(hours) : 0;
554 minutes = argc > 4 ? ToNumber(minutes) : 0;
555 seconds = argc > 5 ? ToNumber(seconds) : 0;
556 ms = argc > 6 ? ToNumber(ms) : 0;
557 year = (!NUMBER_IS_NAN(year) && 0 <= TO_INTEGER(year) && TO_INTEGER(year) <= 99)
558 ? 1900 + TO_INTEGER(year) : year;
559 var day = MakeDay(year, month, date);
560 var time = MakeTime(hours, minutes, seconds, ms);
561 return %_SetValueOf(this, TimeClip(MakeDate(day, time)));
562}
563
564
565// Mozilla-specific extension. Returns the number of milliseconds
566// elapsed since 1 January 1970 00:00:00 UTC.
567function DateNow() {
568 return %DateCurrentTime();
569}
570
571
572// ECMA 262 - 15.9.5.2
573function DateToString() {
574 var t = DATE_VALUE(this);
575 if (NUMBER_IS_NAN(t)) return kInvalidDate;
Andrei Popescu6599b9d2010-04-28 13:01:47 +0100576 var time_zone_string = LocalTimezoneString(t); // May update local offset.
577 return DatePrintString(LocalTimeNoCheck(t)) + time_zone_string;
Andrei Popescu31002712010-02-23 13:46:05 +0000578}
579
580
581// ECMA 262 - 15.9.5.3
582function DateToDateString() {
583 var t = DATE_VALUE(this);
584 if (NUMBER_IS_NAN(t)) return kInvalidDate;
585 return DateString(LocalTimeNoCheck(t));
586}
587
588
589// ECMA 262 - 15.9.5.4
590function DateToTimeString() {
591 var t = DATE_VALUE(this);
592 if (NUMBER_IS_NAN(t)) return kInvalidDate;
Andrei Popescu6599b9d2010-04-28 13:01:47 +0100593 var time_zone_string = LocalTimezoneString(t); // May update local offset.
594 return TimeString(LocalTimeNoCheck(t)) + time_zone_string;
Andrei Popescu31002712010-02-23 13:46:05 +0000595}
596
597
598// ECMA 262 - 15.9.5.5
599function DateToLocaleString() {
600 return DateToString.call(this);
601}
602
603
604// ECMA 262 - 15.9.5.6
605function DateToLocaleDateString() {
606 var t = DATE_VALUE(this);
607 if (NUMBER_IS_NAN(t)) return kInvalidDate;
608 return LongDateString(LocalTimeNoCheck(t));
609}
610
611
612// ECMA 262 - 15.9.5.7
613function DateToLocaleTimeString() {
614 var t = DATE_VALUE(this);
615 if (NUMBER_IS_NAN(t)) return kInvalidDate;
616 var lt = LocalTimeNoCheck(t);
617 return TimeString(lt);
618}
619
620
621// ECMA 262 - 15.9.5.8
622function DateValueOf() {
623 return DATE_VALUE(this);
624}
625
626
627// ECMA 262 - 15.9.5.9
Andrei Popescu402d9372010-02-26 13:31:12 +0000628function DateGetTime() {
Andrei Popescu31002712010-02-23 13:46:05 +0000629 return DATE_VALUE(this);
630}
631
632
633// ECMA 262 - 15.9.5.10
634function DateGetFullYear() {
Leon Clarkeac952652010-07-15 11:15:24 +0100635 var t = DATE_VALUE(this);
636 if (NUMBER_IS_NAN(t)) return t;
637 var cache = Date_cache;
638 if (cache.time === t) return cache.year;
639 return YEAR_FROM_TIME(LocalTimeNoCheck(t));
Andrei Popescu31002712010-02-23 13:46:05 +0000640}
641
642
643// ECMA 262 - 15.9.5.11
644function DateGetUTCFullYear() {
Leon Clarkeac952652010-07-15 11:15:24 +0100645 var t = DATE_VALUE(this);
646 if (NUMBER_IS_NAN(t)) return t;
647 return YEAR_FROM_TIME(t);
Andrei Popescu31002712010-02-23 13:46:05 +0000648}
649
650
651// ECMA 262 - 15.9.5.12
652function DateGetMonth() {
Leon Clarkeac952652010-07-15 11:15:24 +0100653 var t = DATE_VALUE(this);
654 if (NUMBER_IS_NAN(t)) return t;
655 return MONTH_FROM_TIME(LocalTimeNoCheck(t));
Andrei Popescu31002712010-02-23 13:46:05 +0000656}
657
658
659// ECMA 262 - 15.9.5.13
660function DateGetUTCMonth() {
Leon Clarkeac952652010-07-15 11:15:24 +0100661 var t = DATE_VALUE(this);
662 if (NUMBER_IS_NAN(t)) return t;
663 return MONTH_FROM_TIME(t);
Andrei Popescu31002712010-02-23 13:46:05 +0000664}
665
666
667// ECMA 262 - 15.9.5.14
668function DateGetDate() {
Leon Clarkeac952652010-07-15 11:15:24 +0100669 var t = DATE_VALUE(this);
670 if (NUMBER_IS_NAN(t)) return t;
671 return DATE_FROM_TIME(LocalTimeNoCheck(t));
Andrei Popescu31002712010-02-23 13:46:05 +0000672}
673
674
675// ECMA 262 - 15.9.5.15
676function DateGetUTCDate() {
Leon Clarkeac952652010-07-15 11:15:24 +0100677 var t = DATE_VALUE(this);
678 return NAN_OR_DATE_FROM_TIME(t);
Andrei Popescu31002712010-02-23 13:46:05 +0000679}
680
681
682// ECMA 262 - 15.9.5.16
683function DateGetDay() {
684 var t = %_ValueOf(this);
685 if (NUMBER_IS_NAN(t)) return t;
686 return WeekDay(LocalTimeNoCheck(t));
687}
688
689
690// ECMA 262 - 15.9.5.17
691function DateGetUTCDay() {
692 var t = %_ValueOf(this);
693 if (NUMBER_IS_NAN(t)) return t;
694 return WeekDay(t);
695}
696
697
698// ECMA 262 - 15.9.5.18
699function DateGetHours() {
Leon Clarkeac952652010-07-15 11:15:24 +0100700 var t = DATE_VALUE(this);
701 if (NUMBER_IS_NAN(t)) return t;
702 return HOUR_FROM_TIME(LocalTimeNoCheck(t));
Andrei Popescu31002712010-02-23 13:46:05 +0000703}
704
705
706// ECMA 262 - 15.9.5.19
707function DateGetUTCHours() {
Leon Clarkeac952652010-07-15 11:15:24 +0100708 var t = DATE_VALUE(this);
709 if (NUMBER_IS_NAN(t)) return t;
710 return HOUR_FROM_TIME(t);
Andrei Popescu31002712010-02-23 13:46:05 +0000711}
712
713
714// ECMA 262 - 15.9.5.20
715function DateGetMinutes() {
Leon Clarkeac952652010-07-15 11:15:24 +0100716 var t = DATE_VALUE(this);
717 if (NUMBER_IS_NAN(t)) return t;
718 return MIN_FROM_TIME(LocalTimeNoCheck(t));
Andrei Popescu31002712010-02-23 13:46:05 +0000719}
720
721
722// ECMA 262 - 15.9.5.21
723function DateGetUTCMinutes() {
Leon Clarkeac952652010-07-15 11:15:24 +0100724 var t = DATE_VALUE(this);
725 return NAN_OR_MIN_FROM_TIME(t);
Andrei Popescu31002712010-02-23 13:46:05 +0000726}
727
728
729// ECMA 262 - 15.9.5.22
730function DateGetSeconds() {
Leon Clarkeac952652010-07-15 11:15:24 +0100731 var t = DATE_VALUE(this);
732 if (NUMBER_IS_NAN(t)) return t;
733 return SEC_FROM_TIME(LocalTimeNoCheck(t));
Andrei Popescu31002712010-02-23 13:46:05 +0000734}
735
736
737// ECMA 262 - 15.9.5.23
738function DateGetUTCSeconds() {
Leon Clarkeac952652010-07-15 11:15:24 +0100739 var t = DATE_VALUE(this);
740 return NAN_OR_SEC_FROM_TIME(t);
Andrei Popescu31002712010-02-23 13:46:05 +0000741}
742
743
744// ECMA 262 - 15.9.5.24
745function DateGetMilliseconds() {
Leon Clarkeac952652010-07-15 11:15:24 +0100746 var t = DATE_VALUE(this);
747 if (NUMBER_IS_NAN(t)) return t;
748 return MS_FROM_TIME(LocalTimeNoCheck(t));
Andrei Popescu31002712010-02-23 13:46:05 +0000749}
750
751
752// ECMA 262 - 15.9.5.25
753function DateGetUTCMilliseconds() {
Leon Clarkeac952652010-07-15 11:15:24 +0100754 var t = DATE_VALUE(this);
755 return NAN_OR_MS_FROM_TIME(t);
Andrei Popescu31002712010-02-23 13:46:05 +0000756}
757
758
759// ECMA 262 - 15.9.5.26
760function DateGetTimezoneOffset() {
761 var t = DATE_VALUE(this);
762 if (NUMBER_IS_NAN(t)) return t;
763 return (t - LocalTimeNoCheck(t)) / msPerMinute;
764}
765
766
767// ECMA 262 - 15.9.5.27
768function DateSetTime(ms) {
769 if (!IS_DATE(this)) ThrowDateTypeError();
770 return %_SetValueOf(this, TimeClip(ToNumber(ms)));
771}
772
773
774// ECMA 262 - 15.9.5.28
775function DateSetMilliseconds(ms) {
776 var t = LocalTime(DATE_VALUE(this));
777 ms = ToNumber(ms);
778 var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), SEC_FROM_TIME(t), ms);
779 return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
780}
781
782
783// ECMA 262 - 15.9.5.29
784function DateSetUTCMilliseconds(ms) {
785 var t = DATE_VALUE(this);
786 ms = ToNumber(ms);
787 var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), SEC_FROM_TIME(t), ms);
788 return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
789}
790
791
792// ECMA 262 - 15.9.5.30
793function DateSetSeconds(sec, ms) {
794 var t = LocalTime(DATE_VALUE(this));
795 sec = ToNumber(sec);
Leon Clarkeac952652010-07-15 11:15:24 +0100796 ms = %_ArgumentsLength() < 2 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms);
Andrei Popescu31002712010-02-23 13:46:05 +0000797 var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), sec, ms);
798 return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
799}
800
801
802// ECMA 262 - 15.9.5.31
803function DateSetUTCSeconds(sec, ms) {
804 var t = DATE_VALUE(this);
805 sec = ToNumber(sec);
Leon Clarkeac952652010-07-15 11:15:24 +0100806 ms = %_ArgumentsLength() < 2 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms);
Andrei Popescu31002712010-02-23 13:46:05 +0000807 var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), sec, ms);
808 return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
809}
810
811
812// ECMA 262 - 15.9.5.33
813function DateSetMinutes(min, sec, ms) {
814 var t = LocalTime(DATE_VALUE(this));
815 min = ToNumber(min);
816 var argc = %_ArgumentsLength();
Leon Clarkeac952652010-07-15 11:15:24 +0100817 sec = argc < 2 ? NAN_OR_SEC_FROM_TIME(t) : ToNumber(sec);
818 ms = argc < 3 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms);
Andrei Popescu31002712010-02-23 13:46:05 +0000819 var time = MakeTime(HOUR_FROM_TIME(t), min, sec, ms);
820 return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
821}
822
823
824// ECMA 262 - 15.9.5.34
825function DateSetUTCMinutes(min, sec, ms) {
826 var t = DATE_VALUE(this);
827 min = ToNumber(min);
828 var argc = %_ArgumentsLength();
Leon Clarkeac952652010-07-15 11:15:24 +0100829 sec = argc < 2 ? NAN_OR_SEC_FROM_TIME(t) : ToNumber(sec);
830 ms = argc < 3 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms);
Andrei Popescu31002712010-02-23 13:46:05 +0000831 var time = MakeTime(HOUR_FROM_TIME(t), min, sec, ms);
832 return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
833}
834
835
836// ECMA 262 - 15.9.5.35
837function DateSetHours(hour, min, sec, ms) {
838 var t = LocalTime(DATE_VALUE(this));
839 hour = ToNumber(hour);
840 var argc = %_ArgumentsLength();
Leon Clarkeac952652010-07-15 11:15:24 +0100841 min = argc < 2 ? NAN_OR_MIN_FROM_TIME(t) : ToNumber(min);
842 sec = argc < 3 ? NAN_OR_SEC_FROM_TIME(t) : ToNumber(sec);
843 ms = argc < 4 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms);
Andrei Popescu31002712010-02-23 13:46:05 +0000844 var time = MakeTime(hour, min, sec, ms);
845 return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
846}
847
848
849// ECMA 262 - 15.9.5.34
850function DateSetUTCHours(hour, min, sec, ms) {
851 var t = DATE_VALUE(this);
852 hour = ToNumber(hour);
853 var argc = %_ArgumentsLength();
Leon Clarkeac952652010-07-15 11:15:24 +0100854 min = argc < 2 ? NAN_OR_MIN_FROM_TIME(t) : ToNumber(min);
855 sec = argc < 3 ? NAN_OR_SEC_FROM_TIME(t) : ToNumber(sec);
856 ms = argc < 4 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms);
Andrei Popescu31002712010-02-23 13:46:05 +0000857 var time = MakeTime(hour, min, sec, ms);
858 return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
859}
860
861
862// ECMA 262 - 15.9.5.36
863function DateSetDate(date) {
864 var t = LocalTime(DATE_VALUE(this));
865 date = ToNumber(date);
866 var day = MakeDay(YEAR_FROM_TIME(t), MONTH_FROM_TIME(t), date);
867 return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
868}
869
870
871// ECMA 262 - 15.9.5.37
872function DateSetUTCDate(date) {
873 var t = DATE_VALUE(this);
874 date = ToNumber(date);
875 var day = MakeDay(YEAR_FROM_TIME(t), MONTH_FROM_TIME(t), date);
876 return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t))));
877}
878
879
880// ECMA 262 - 15.9.5.38
881function DateSetMonth(month, date) {
882 var t = LocalTime(DATE_VALUE(this));
883 month = ToNumber(month);
Leon Clarkeac952652010-07-15 11:15:24 +0100884 date = %_ArgumentsLength() < 2 ? NAN_OR_DATE_FROM_TIME(t) : ToNumber(date);
Andrei Popescu31002712010-02-23 13:46:05 +0000885 var day = MakeDay(YEAR_FROM_TIME(t), month, date);
886 return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
887}
888
889
890// ECMA 262 - 15.9.5.39
891function DateSetUTCMonth(month, date) {
892 var t = DATE_VALUE(this);
893 month = ToNumber(month);
Leon Clarkeac952652010-07-15 11:15:24 +0100894 date = %_ArgumentsLength() < 2 ? NAN_OR_DATE_FROM_TIME(t) : ToNumber(date);
Andrei Popescu31002712010-02-23 13:46:05 +0000895 var day = MakeDay(YEAR_FROM_TIME(t), month, date);
896 return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t))));
897}
898
899
900// ECMA 262 - 15.9.5.40
901function DateSetFullYear(year, month, date) {
902 var t = DATE_VALUE(this);
903 t = NUMBER_IS_NAN(t) ? 0 : LocalTimeNoCheck(t);
904 year = ToNumber(year);
905 var argc = %_ArgumentsLength();
906 month = argc < 2 ? MONTH_FROM_TIME(t) : ToNumber(month);
907 date = argc < 3 ? DATE_FROM_TIME(t) : ToNumber(date);
908 var day = MakeDay(year, month, date);
909 return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
910}
911
912
913// ECMA 262 - 15.9.5.41
914function DateSetUTCFullYear(year, month, date) {
915 var t = DATE_VALUE(this);
916 if (NUMBER_IS_NAN(t)) t = 0;
917 var argc = %_ArgumentsLength();
918 year = ToNumber(year);
919 month = argc < 2 ? MONTH_FROM_TIME(t) : ToNumber(month);
920 date = argc < 3 ? DATE_FROM_TIME(t) : ToNumber(date);
921 var day = MakeDay(year, month, date);
922 return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t))));
923}
924
925
926// ECMA 262 - 15.9.5.42
927function DateToUTCString() {
928 var t = DATE_VALUE(this);
929 if (NUMBER_IS_NAN(t)) return kInvalidDate;
930 // Return UTC string of the form: Sat, 31 Jan 1970 23:00:00 GMT
931 return WeekDays[WeekDay(t)] + ', '
932 + TwoDigitString(DATE_FROM_TIME(t)) + ' '
933 + Months[MONTH_FROM_TIME(t)] + ' '
934 + YEAR_FROM_TIME(t) + ' '
935 + TimeString(t) + ' GMT';
936}
937
938
939// ECMA 262 - B.2.4
940function DateGetYear() {
941 var t = DATE_VALUE(this);
942 if (NUMBER_IS_NAN(t)) return $NaN;
943 return YEAR_FROM_TIME(LocalTimeNoCheck(t)) - 1900;
944}
945
946
947// ECMA 262 - B.2.5
948function DateSetYear(year) {
949 var t = LocalTime(DATE_VALUE(this));
950 if (NUMBER_IS_NAN(t)) t = 0;
951 year = ToNumber(year);
952 if (NUMBER_IS_NAN(year)) return %_SetValueOf(this, $NaN);
953 year = (0 <= TO_INTEGER(year) && TO_INTEGER(year) <= 99)
954 ? 1900 + TO_INTEGER(year) : year;
955 var day = MakeDay(year, MONTH_FROM_TIME(t), DATE_FROM_TIME(t));
956 return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
957}
958
959
960// ECMA 262 - B.2.6
961//
962// Notice that this does not follow ECMA 262 completely. ECMA 262
963// says that toGMTString should be the same Function object as
964// toUTCString. JSC does not do this, so for compatibility we do not
965// do that either. Instead, we create a new function whose name
966// property will return toGMTString.
967function DateToGMTString() {
968 return DateToUTCString.call(this);
969}
970
971
972function PadInt(n, digits) {
973 if (digits == 1) return n;
974 return n < MathPow(10, digits - 1) ? '0' + PadInt(n, digits - 1) : n;
975}
976
977
978function DateToISOString() {
979 var t = DATE_VALUE(this);
980 if (NUMBER_IS_NAN(t)) return kInvalidDate;
981 return this.getUTCFullYear() + '-' + PadInt(this.getUTCMonth() + 1, 2) +
982 '-' + PadInt(this.getUTCDate(), 2) + 'T' + PadInt(this.getUTCHours(), 2) +
983 ':' + PadInt(this.getUTCMinutes(), 2) + ':' + PadInt(this.getUTCSeconds(), 2) +
984 '.' + PadInt(this.getUTCMilliseconds(), 3) +
985 'Z';
986}
987
988
989function DateToJSON(key) {
990 return CheckJSONPrimitive(this.toISOString());
991}
992
993
994// -------------------------------------------------------------------
995
996function SetupDate() {
997 // Setup non-enumerable properties of the Date object itself.
998 InstallFunctions($Date, DONT_ENUM, $Array(
999 "UTC", DateUTC,
1000 "parse", DateParse,
1001 "now", DateNow
1002 ));
1003
1004 // Setup non-enumerable constructor property of the Date prototype object.
1005 %SetProperty($Date.prototype, "constructor", $Date, DONT_ENUM);
1006
1007 // Setup non-enumerable functions of the Date prototype object and
1008 // set their names.
1009 InstallFunctionsOnHiddenPrototype($Date.prototype, DONT_ENUM, $Array(
1010 "toString", DateToString,
1011 "toDateString", DateToDateString,
1012 "toTimeString", DateToTimeString,
1013 "toLocaleString", DateToLocaleString,
1014 "toLocaleDateString", DateToLocaleDateString,
1015 "toLocaleTimeString", DateToLocaleTimeString,
1016 "valueOf", DateValueOf,
1017 "getTime", DateGetTime,
1018 "getFullYear", DateGetFullYear,
1019 "getUTCFullYear", DateGetUTCFullYear,
1020 "getMonth", DateGetMonth,
1021 "getUTCMonth", DateGetUTCMonth,
1022 "getDate", DateGetDate,
1023 "getUTCDate", DateGetUTCDate,
1024 "getDay", DateGetDay,
1025 "getUTCDay", DateGetUTCDay,
1026 "getHours", DateGetHours,
1027 "getUTCHours", DateGetUTCHours,
1028 "getMinutes", DateGetMinutes,
1029 "getUTCMinutes", DateGetUTCMinutes,
1030 "getSeconds", DateGetSeconds,
1031 "getUTCSeconds", DateGetUTCSeconds,
1032 "getMilliseconds", DateGetMilliseconds,
1033 "getUTCMilliseconds", DateGetUTCMilliseconds,
1034 "getTimezoneOffset", DateGetTimezoneOffset,
1035 "setTime", DateSetTime,
1036 "setMilliseconds", DateSetMilliseconds,
1037 "setUTCMilliseconds", DateSetUTCMilliseconds,
1038 "setSeconds", DateSetSeconds,
1039 "setUTCSeconds", DateSetUTCSeconds,
1040 "setMinutes", DateSetMinutes,
1041 "setUTCMinutes", DateSetUTCMinutes,
1042 "setHours", DateSetHours,
1043 "setUTCHours", DateSetUTCHours,
1044 "setDate", DateSetDate,
1045 "setUTCDate", DateSetUTCDate,
1046 "setMonth", DateSetMonth,
1047 "setUTCMonth", DateSetUTCMonth,
1048 "setFullYear", DateSetFullYear,
1049 "setUTCFullYear", DateSetUTCFullYear,
1050 "toGMTString", DateToGMTString,
1051 "toUTCString", DateToUTCString,
1052 "getYear", DateGetYear,
1053 "setYear", DateSetYear,
1054 "toISOString", DateToISOString,
1055 "toJSON", DateToJSON
1056 ));
1057}
1058
1059SetupDate();