blob: 960147072c27cca7d690fe8beba71d64dffc7ea0 [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.
Iain Merrick75681382010-08-19 15:07:18 +0100140 increment: 0,
141 initial_increment: 19 * msPerDay
Andrei Popescu31002712010-02-23 13:46:05 +0000142};
143
144
145// NOTE: The implementation relies on the fact that no time zones have
Iain Merrick75681382010-08-19 15:07:18 +0100146// more than one daylight savings offset change per 19 days.
147//
148// In Egypt in 2010 they decided to suspend DST during Ramadan. This
149// led to a short interval where DST is in effect from September 10 to
150// September 30.
151//
Andrei Popescu31002712010-02-23 13:46:05 +0000152// If this function is called with NaN it returns NaN.
153function DaylightSavingsOffset(t) {
154 // Load the cache object from the builtins object.
155 var cache = DST_offset_cache;
156
157 // Cache the start and the end in local variables for fast access.
158 var start = cache.start;
159 var end = cache.end;
160
161 if (start <= t) {
162 // If the time fits in the cached interval, return the cached offset.
163 if (t <= end) return cache.offset;
164
Andrei Popescu6599b9d2010-04-28 13:01:47 +0100165 // If the cache misses, the local_time_offset may not be initialized.
166 if (IS_UNDEFINED(local_time_offset)) {
167 local_time_offset = %DateLocalTimeOffset();
168 }
169
Andrei Popescu31002712010-02-23 13:46:05 +0000170 // Compute a possible new interval end.
171 var new_end = end + cache.increment;
172
173 if (t <= new_end) {
174 var end_offset = %DateDaylightSavingsOffset(EquivalentTime(new_end));
175 if (cache.offset == end_offset) {
176 // If the offset at the end of the new interval still matches
177 // the offset in the cache, we grow the cached time interval
178 // and return the offset.
179 cache.end = new_end;
Iain Merrick75681382010-08-19 15:07:18 +0100180 cache.increment = cache.initial_increment;
Andrei Popescu31002712010-02-23 13:46:05 +0000181 return end_offset;
182 } else {
183 var offset = %DateDaylightSavingsOffset(EquivalentTime(t));
184 if (offset == end_offset) {
185 // The offset at the given time is equal to the offset at the
186 // new end of the interval, so that means that we've just skipped
187 // the point in time where the DST offset change occurred. Updated
188 // the interval to reflect this and reset the increment.
189 cache.start = t;
190 cache.end = new_end;
Iain Merrick75681382010-08-19 15:07:18 +0100191 cache.increment = cache.initial_increment;
Andrei Popescu31002712010-02-23 13:46:05 +0000192 } else {
193 // The interval contains a DST offset change and the given time is
194 // before it. Adjust the increment to avoid a linear search for
195 // the offset change point and change the end of the interval.
196 cache.increment /= 3;
197 cache.end = t;
198 }
199 // Update the offset in the cache and return it.
200 cache.offset = offset;
201 return offset;
202 }
203 }
204 }
205
Andrei Popescu6599b9d2010-04-28 13:01:47 +0100206 // If the cache misses, the local_time_offset may not be initialized.
207 if (IS_UNDEFINED(local_time_offset)) {
208 local_time_offset = %DateLocalTimeOffset();
209 }
Andrei Popescu31002712010-02-23 13:46:05 +0000210 // Compute the DST offset for the time and shrink the cache interval
211 // to only contain the time. This allows fast repeated DST offset
212 // computations for the same time.
213 var offset = %DateDaylightSavingsOffset(EquivalentTime(t));
214 cache.offset = offset;
215 cache.start = cache.end = t;
Iain Merrick75681382010-08-19 15:07:18 +0100216 cache.increment = cache.initial_increment;
Andrei Popescu31002712010-02-23 13:46:05 +0000217 return offset;
218}
219
220
221var timezone_cache_time = $NaN;
222var timezone_cache_timezone;
223
224function LocalTimezone(t) {
225 if (NUMBER_IS_NAN(t)) return "";
226 if (t == timezone_cache_time) {
227 return timezone_cache_timezone;
228 }
229 var timezone = %DateLocalTimezone(EquivalentTime(t));
230 timezone_cache_time = t;
231 timezone_cache_timezone = timezone;
232 return timezone;
233}
234
235
236function WeekDay(time) {
237 return Modulo(DAY(time) + 4, 7);
238}
239
Andrei Popescu31002712010-02-23 13:46:05 +0000240
241function LocalTime(time) {
242 if (NUMBER_IS_NAN(time)) return time;
Andrei Popescu6599b9d2010-04-28 13:01:47 +0100243 // DaylightSavingsOffset called before local_time_offset used.
244 return time + DaylightSavingsOffset(time) + local_time_offset;
Andrei Popescu31002712010-02-23 13:46:05 +0000245}
246
Kristian Monsen25f61362010-05-21 11:50:48 +0100247
248var ltcache = {
Ben Murdochf87a2032010-10-22 12:50:53 +0100249 key: null,
Kristian Monsen25f61362010-05-21 11:50:48 +0100250 val: null
251};
252
Andrei Popescu31002712010-02-23 13:46:05 +0000253function LocalTimeNoCheck(time) {
Kristian Monsen25f61362010-05-21 11:50:48 +0100254 var ltc = ltcache;
255 if (%_ObjectEquals(time, ltc.key)) return ltc.val;
Steve Block6ded16b2010-05-10 14:33:55 +0100256 if (time < -MAX_TIME_MS || time > MAX_TIME_MS) {
257 return $NaN;
258 }
259
Andrei Popescu31002712010-02-23 13:46:05 +0000260 // Inline the DST offset cache checks for speed.
Andrei Popescu6599b9d2010-04-28 13:01:47 +0100261 // The cache is hit, or DaylightSavingsOffset is called,
262 // before local_time_offset is used.
Andrei Popescu31002712010-02-23 13:46:05 +0000263 var cache = DST_offset_cache;
264 if (cache.start <= time && time <= cache.end) {
265 var dst_offset = cache.offset;
266 } else {
267 var dst_offset = DaylightSavingsOffset(time);
268 }
Kristian Monsen25f61362010-05-21 11:50:48 +0100269 ltc.key = time;
270 return (ltc.val = time + local_time_offset + dst_offset);
Andrei Popescu31002712010-02-23 13:46:05 +0000271}
272
273
274function UTC(time) {
275 if (NUMBER_IS_NAN(time)) return time;
Andrei Popescu6599b9d2010-04-28 13:01:47 +0100276 // local_time_offset is needed before the call to DaylightSavingsOffset,
277 // so it may be uninitialized.
278 if (IS_UNDEFINED(local_time_offset)) {
279 local_time_offset = %DateLocalTimeOffset();
280 }
Andrei Popescu31002712010-02-23 13:46:05 +0000281 var tmp = time - local_time_offset;
282 return tmp - DaylightSavingsOffset(tmp);
283}
284
285
286// ECMA 262 - 15.9.1.11
287function MakeTime(hour, min, sec, ms) {
288 if (!$isFinite(hour)) return $NaN;
289 if (!$isFinite(min)) return $NaN;
290 if (!$isFinite(sec)) return $NaN;
291 if (!$isFinite(ms)) return $NaN;
292 return TO_INTEGER(hour) * msPerHour
293 + TO_INTEGER(min) * msPerMinute
294 + TO_INTEGER(sec) * msPerSecond
295 + TO_INTEGER(ms);
296}
297
298
299// ECMA 262 - 15.9.1.12
300function TimeInYear(year) {
301 return DaysInYear(year) * msPerDay;
302}
303
304
Steve Block6ded16b2010-05-10 14:33:55 +0100305var ymd_from_time_cache = [$NaN, $NaN, $NaN];
306var ymd_from_time_cached_time = $NaN;
Andrei Popescu31002712010-02-23 13:46:05 +0000307
Steve Block6ded16b2010-05-10 14:33:55 +0100308function YearFromTime(t) {
309 if (t !== ymd_from_time_cached_time) {
310 if (!$isFinite(t)) {
311 return $NaN;
Andrei Popescu31002712010-02-23 13:46:05 +0000312 }
Steve Block6ded16b2010-05-10 14:33:55 +0100313
314 %DateYMDFromTime(t, ymd_from_time_cache);
315 ymd_from_time_cached_time = t
316 }
317
318 return ymd_from_time_cache[0];
319}
320
321function MonthFromTime(t) {
322 if (t !== ymd_from_time_cached_time) {
323 if (!$isFinite(t)) {
324 return $NaN;
Andrei Popescu31002712010-02-23 13:46:05 +0000325 }
Steve Block6ded16b2010-05-10 14:33:55 +0100326 %DateYMDFromTime(t, ymd_from_time_cache);
327 ymd_from_time_cached_time = t
Andrei Popescu31002712010-02-23 13:46:05 +0000328 }
Steve Block6ded16b2010-05-10 14:33:55 +0100329
330 return ymd_from_time_cache[1];
Andrei Popescu31002712010-02-23 13:46:05 +0000331}
332
Steve Block6ded16b2010-05-10 14:33:55 +0100333function DateFromTime(t) {
334 if (t !== ymd_from_time_cached_time) {
335 if (!$isFinite(t)) {
336 return $NaN;
337 }
Andrei Popescu31002712010-02-23 13:46:05 +0000338
Steve Block6ded16b2010-05-10 14:33:55 +0100339 %DateYMDFromTime(t, ymd_from_time_cache);
340 ymd_from_time_cached_time = t
Andrei Popescu31002712010-02-23 13:46:05 +0000341 }
Steve Block6ded16b2010-05-10 14:33:55 +0100342
343 return ymd_from_time_cache[2];
Andrei Popescu31002712010-02-23 13:46:05 +0000344}
345
346
347// Compute number of days given a year, month, date.
348// Note that month and date can lie outside the normal range.
349// For example:
350// MakeDay(2007, -4, 20) --> MakeDay(2006, 8, 20)
351// MakeDay(2007, -33, 1) --> MakeDay(2004, 3, 1)
352// MakeDay(2007, 14, -50) --> MakeDay(2007, 8, 11)
353function MakeDay(year, month, date) {
354 if (!$isFinite(year) || !$isFinite(month) || !$isFinite(date)) return $NaN;
355
Steve Block8defd9f2010-07-08 12:39:36 +0100356 // Convert to integer and map -0 to 0.
357 year = TO_INTEGER_MAP_MINUS_ZERO(year);
358 month = TO_INTEGER_MAP_MINUS_ZERO(month);
359 date = TO_INTEGER_MAP_MINUS_ZERO(date);
Andrei Popescu31002712010-02-23 13:46:05 +0000360
Steve Block6ded16b2010-05-10 14:33:55 +0100361 if (year < kMinYear || year > kMaxYear ||
362 month < kMinMonth || month > kMaxMonth ||
363 date < kMinDate || date > kMaxDate) {
364 return $NaN;
Andrei Popescu31002712010-02-23 13:46:05 +0000365 }
366
Steve Block6ded16b2010-05-10 14:33:55 +0100367 // Now we rely on year, month and date being SMIs.
368 return %DateMakeDay(year, month, date);
Andrei Popescu31002712010-02-23 13:46:05 +0000369}
370
371
372// ECMA 262 - 15.9.1.13
373function MakeDate(day, time) {
374 if (!$isFinite(day)) return $NaN;
375 if (!$isFinite(time)) return $NaN;
376 return day * msPerDay + time;
377}
378
379
380// ECMA 262 - 15.9.1.14
381function TimeClip(time) {
382 if (!$isFinite(time)) return $NaN;
383 if ($abs(time) > 8.64E15) return $NaN;
384 return TO_INTEGER(time);
385}
386
387
388// The Date cache is used to limit the cost of parsing the same Date
389// strings over and over again.
390var Date_cache = {
391 // Cached time value.
392 time: $NaN,
393 // Cached year when interpreting the time as a local time. Only
394 // valid when the time matches cached time.
395 year: $NaN,
396 // String input for which the cached time is valid.
397 string: null
398};
399
400
401%SetCode($Date, function(year, month, date, hours, minutes, seconds, ms) {
402 if (!%_IsConstructCall()) {
403 // ECMA 262 - 15.9.2
404 return (new $Date()).toString();
405 }
406
407 // ECMA 262 - 15.9.3
408 var argc = %_ArgumentsLength();
409 var value;
410 if (argc == 0) {
411 value = %DateCurrentTime();
412
413 } else if (argc == 1) {
414 if (IS_NUMBER(year)) {
415 value = TimeClip(year);
416
417 } else if (IS_STRING(year)) {
418 // Probe the Date cache. If we already have a time value for the
419 // given time, we re-use that instead of parsing the string again.
420 var cache = Date_cache;
421 if (cache.string === year) {
422 value = cache.time;
423 } else {
424 value = DateParse(year);
425 if (!NUMBER_IS_NAN(value)) {
426 cache.time = value;
427 cache.year = YEAR_FROM_TIME(LocalTimeNoCheck(value));
428 cache.string = year;
429 }
430 }
431
432 } else {
433 // According to ECMA 262, no hint should be given for this
434 // conversion. However, ToPrimitive defaults to STRING_HINT for
435 // Date objects which will lose precision when the Date
436 // constructor is called with another Date object as its
437 // argument. We therefore use NUMBER_HINT for the conversion,
438 // which is the default for everything else than Date objects.
439 // This makes us behave like KJS and SpiderMonkey.
440 var time = ToPrimitive(year, NUMBER_HINT);
441 value = IS_STRING(time) ? DateParse(time) : TimeClip(ToNumber(time));
442 }
443
444 } else {
445 year = ToNumber(year);
446 month = ToNumber(month);
447 date = argc > 2 ? ToNumber(date) : 1;
448 hours = argc > 3 ? ToNumber(hours) : 0;
449 minutes = argc > 4 ? ToNumber(minutes) : 0;
450 seconds = argc > 5 ? ToNumber(seconds) : 0;
451 ms = argc > 6 ? ToNumber(ms) : 0;
452 year = (!NUMBER_IS_NAN(year) && 0 <= TO_INTEGER(year) && TO_INTEGER(year) <= 99)
453 ? 1900 + TO_INTEGER(year) : year;
454 var day = MakeDay(year, month, date);
455 var time = MakeTime(hours, minutes, seconds, ms);
456 value = TimeClip(UTC(MakeDate(day, time)));
457 }
458 %_SetValueOf(this, value);
459});
460
461
Andrei Popescu31002712010-02-23 13:46:05 +0000462%FunctionSetPrototype($Date, new $Date($NaN));
463
464
465var WeekDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
466var Months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
467
468
469function TwoDigitString(value) {
470 return value < 10 ? "0" + value : "" + value;
471}
472
473
474function DateString(time) {
Andrei Popescu31002712010-02-23 13:46:05 +0000475 return WeekDays[WeekDay(time)] + ' '
Steve Block6ded16b2010-05-10 14:33:55 +0100476 + Months[MonthFromTime(time)] + ' '
477 + TwoDigitString(DateFromTime(time)) + ' '
478 + YearFromTime(time);
Andrei Popescu31002712010-02-23 13:46:05 +0000479}
480
481
482var LongWeekDays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
483var LongMonths = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
484
485
486function LongDateString(time) {
Andrei Popescu31002712010-02-23 13:46:05 +0000487 return LongWeekDays[WeekDay(time)] + ', '
Steve Block6ded16b2010-05-10 14:33:55 +0100488 + LongMonths[MonthFromTime(time)] + ' '
489 + TwoDigitString(DateFromTime(time)) + ', '
490 + YearFromTime(time);
Andrei Popescu31002712010-02-23 13:46:05 +0000491}
492
493
494function TimeString(time) {
495 return TwoDigitString(HOUR_FROM_TIME(time)) + ':'
496 + TwoDigitString(MIN_FROM_TIME(time)) + ':'
497 + TwoDigitString(SEC_FROM_TIME(time));
498}
499
500
501function LocalTimezoneString(time) {
Ben Murdochd69d2e32010-03-30 12:55:27 +0100502 var old_timezone = timezone_cache_timezone;
503 var timezone = LocalTimezone(time);
504 if (old_timezone && timezone != old_timezone) {
505 // If the timezone string has changed from the one that we cached,
506 // the local time offset may now be wrong. So we need to update it
507 // and try again.
508 local_time_offset = %DateLocalTimeOffset();
509 // We also need to invalidate the DST cache as the new timezone may have
510 // different DST times.
511 var dst_cache = DST_offset_cache;
512 dst_cache.start = 0;
513 dst_cache.end = -1;
514 }
515
Andrei Popescu31002712010-02-23 13:46:05 +0000516 var timezoneOffset =
Andrei Popescu6599b9d2010-04-28 13:01:47 +0100517 (DaylightSavingsOffset(time) + local_time_offset) / msPerMinute;
Andrei Popescu31002712010-02-23 13:46:05 +0000518 var sign = (timezoneOffset >= 0) ? 1 : -1;
519 var hours = FLOOR((sign * timezoneOffset)/60);
520 var min = FLOOR((sign * timezoneOffset)%60);
521 var gmt = ' GMT' + ((sign == 1) ? '+' : '-') +
522 TwoDigitString(hours) + TwoDigitString(min);
Ben Murdochd69d2e32010-03-30 12:55:27 +0100523 return gmt + ' (' + timezone + ')';
Andrei Popescu31002712010-02-23 13:46:05 +0000524}
525
526
527function DatePrintString(time) {
528 return DateString(time) + ' ' + TimeString(time);
529}
530
531// -------------------------------------------------------------------
532
533// Reused output buffer. Used when parsing date strings.
Steve Block6ded16b2010-05-10 14:33:55 +0100534var parse_buffer = $Array(8);
Andrei Popescu31002712010-02-23 13:46:05 +0000535
536// ECMA 262 - 15.9.4.2
537function DateParse(string) {
538 var arr = %DateParseString(ToString(string), parse_buffer);
539 if (IS_NULL(arr)) return $NaN;
540
541 var day = MakeDay(arr[0], arr[1], arr[2]);
Steve Block6ded16b2010-05-10 14:33:55 +0100542 var time = MakeTime(arr[3], arr[4], arr[5], arr[6]);
Andrei Popescu31002712010-02-23 13:46:05 +0000543 var date = MakeDate(day, time);
544
Steve Block6ded16b2010-05-10 14:33:55 +0100545 if (IS_NULL(arr[7])) {
Andrei Popescu31002712010-02-23 13:46:05 +0000546 return TimeClip(UTC(date));
547 } else {
Steve Block6ded16b2010-05-10 14:33:55 +0100548 return TimeClip(date - arr[7] * 1000);
Andrei Popescu31002712010-02-23 13:46:05 +0000549 }
550}
551
552
553// ECMA 262 - 15.9.4.3
554function DateUTC(year, month, date, hours, minutes, seconds, ms) {
555 year = ToNumber(year);
556 month = ToNumber(month);
557 var argc = %_ArgumentsLength();
558 date = argc > 2 ? ToNumber(date) : 1;
559 hours = argc > 3 ? ToNumber(hours) : 0;
560 minutes = argc > 4 ? ToNumber(minutes) : 0;
561 seconds = argc > 5 ? ToNumber(seconds) : 0;
562 ms = argc > 6 ? ToNumber(ms) : 0;
563 year = (!NUMBER_IS_NAN(year) && 0 <= TO_INTEGER(year) && TO_INTEGER(year) <= 99)
564 ? 1900 + TO_INTEGER(year) : year;
565 var day = MakeDay(year, month, date);
566 var time = MakeTime(hours, minutes, seconds, ms);
567 return %_SetValueOf(this, TimeClip(MakeDate(day, time)));
568}
569
570
571// Mozilla-specific extension. Returns the number of milliseconds
572// elapsed since 1 January 1970 00:00:00 UTC.
573function DateNow() {
574 return %DateCurrentTime();
575}
576
577
578// ECMA 262 - 15.9.5.2
579function DateToString() {
580 var t = DATE_VALUE(this);
581 if (NUMBER_IS_NAN(t)) return kInvalidDate;
Andrei Popescu6599b9d2010-04-28 13:01:47 +0100582 var time_zone_string = LocalTimezoneString(t); // May update local offset.
583 return DatePrintString(LocalTimeNoCheck(t)) + time_zone_string;
Andrei Popescu31002712010-02-23 13:46:05 +0000584}
585
586
587// ECMA 262 - 15.9.5.3
588function DateToDateString() {
589 var t = DATE_VALUE(this);
590 if (NUMBER_IS_NAN(t)) return kInvalidDate;
591 return DateString(LocalTimeNoCheck(t));
592}
593
594
595// ECMA 262 - 15.9.5.4
596function DateToTimeString() {
597 var t = DATE_VALUE(this);
598 if (NUMBER_IS_NAN(t)) return kInvalidDate;
Andrei Popescu6599b9d2010-04-28 13:01:47 +0100599 var time_zone_string = LocalTimezoneString(t); // May update local offset.
600 return TimeString(LocalTimeNoCheck(t)) + time_zone_string;
Andrei Popescu31002712010-02-23 13:46:05 +0000601}
602
603
604// ECMA 262 - 15.9.5.5
605function DateToLocaleString() {
606 return DateToString.call(this);
607}
608
609
610// ECMA 262 - 15.9.5.6
611function DateToLocaleDateString() {
612 var t = DATE_VALUE(this);
613 if (NUMBER_IS_NAN(t)) return kInvalidDate;
614 return LongDateString(LocalTimeNoCheck(t));
615}
616
617
618// ECMA 262 - 15.9.5.7
619function DateToLocaleTimeString() {
620 var t = DATE_VALUE(this);
621 if (NUMBER_IS_NAN(t)) return kInvalidDate;
622 var lt = LocalTimeNoCheck(t);
623 return TimeString(lt);
624}
625
626
627// ECMA 262 - 15.9.5.8
628function DateValueOf() {
629 return DATE_VALUE(this);
630}
631
632
633// ECMA 262 - 15.9.5.9
Andrei Popescu402d9372010-02-26 13:31:12 +0000634function DateGetTime() {
Andrei Popescu31002712010-02-23 13:46:05 +0000635 return DATE_VALUE(this);
636}
637
638
639// ECMA 262 - 15.9.5.10
640function DateGetFullYear() {
Leon Clarkeac952652010-07-15 11:15:24 +0100641 var t = DATE_VALUE(this);
642 if (NUMBER_IS_NAN(t)) return t;
643 var cache = Date_cache;
644 if (cache.time === t) return cache.year;
645 return YEAR_FROM_TIME(LocalTimeNoCheck(t));
Andrei Popescu31002712010-02-23 13:46:05 +0000646}
647
648
649// ECMA 262 - 15.9.5.11
650function DateGetUTCFullYear() {
Leon Clarkeac952652010-07-15 11:15:24 +0100651 var t = DATE_VALUE(this);
652 if (NUMBER_IS_NAN(t)) return t;
653 return YEAR_FROM_TIME(t);
Andrei Popescu31002712010-02-23 13:46:05 +0000654}
655
656
657// ECMA 262 - 15.9.5.12
658function DateGetMonth() {
Leon Clarkeac952652010-07-15 11:15:24 +0100659 var t = DATE_VALUE(this);
660 if (NUMBER_IS_NAN(t)) return t;
661 return MONTH_FROM_TIME(LocalTimeNoCheck(t));
Andrei Popescu31002712010-02-23 13:46:05 +0000662}
663
664
665// ECMA 262 - 15.9.5.13
666function DateGetUTCMonth() {
Leon Clarkeac952652010-07-15 11:15:24 +0100667 var t = DATE_VALUE(this);
668 if (NUMBER_IS_NAN(t)) return t;
669 return MONTH_FROM_TIME(t);
Andrei Popescu31002712010-02-23 13:46:05 +0000670}
671
672
673// ECMA 262 - 15.9.5.14
674function DateGetDate() {
Leon Clarkeac952652010-07-15 11:15:24 +0100675 var t = DATE_VALUE(this);
676 if (NUMBER_IS_NAN(t)) return t;
677 return DATE_FROM_TIME(LocalTimeNoCheck(t));
Andrei Popescu31002712010-02-23 13:46:05 +0000678}
679
680
681// ECMA 262 - 15.9.5.15
682function DateGetUTCDate() {
Leon Clarkeac952652010-07-15 11:15:24 +0100683 var t = DATE_VALUE(this);
684 return NAN_OR_DATE_FROM_TIME(t);
Andrei Popescu31002712010-02-23 13:46:05 +0000685}
686
687
688// ECMA 262 - 15.9.5.16
689function DateGetDay() {
690 var t = %_ValueOf(this);
691 if (NUMBER_IS_NAN(t)) return t;
692 return WeekDay(LocalTimeNoCheck(t));
693}
694
695
696// ECMA 262 - 15.9.5.17
697function DateGetUTCDay() {
698 var t = %_ValueOf(this);
699 if (NUMBER_IS_NAN(t)) return t;
700 return WeekDay(t);
701}
702
703
704// ECMA 262 - 15.9.5.18
705function DateGetHours() {
Leon Clarkeac952652010-07-15 11:15:24 +0100706 var t = DATE_VALUE(this);
707 if (NUMBER_IS_NAN(t)) return t;
708 return HOUR_FROM_TIME(LocalTimeNoCheck(t));
Andrei Popescu31002712010-02-23 13:46:05 +0000709}
710
711
712// ECMA 262 - 15.9.5.19
713function DateGetUTCHours() {
Leon Clarkeac952652010-07-15 11:15:24 +0100714 var t = DATE_VALUE(this);
715 if (NUMBER_IS_NAN(t)) return t;
716 return HOUR_FROM_TIME(t);
Andrei Popescu31002712010-02-23 13:46:05 +0000717}
718
719
720// ECMA 262 - 15.9.5.20
721function DateGetMinutes() {
Leon Clarkeac952652010-07-15 11:15:24 +0100722 var t = DATE_VALUE(this);
723 if (NUMBER_IS_NAN(t)) return t;
724 return MIN_FROM_TIME(LocalTimeNoCheck(t));
Andrei Popescu31002712010-02-23 13:46:05 +0000725}
726
727
728// ECMA 262 - 15.9.5.21
729function DateGetUTCMinutes() {
Leon Clarkeac952652010-07-15 11:15:24 +0100730 var t = DATE_VALUE(this);
731 return NAN_OR_MIN_FROM_TIME(t);
Andrei Popescu31002712010-02-23 13:46:05 +0000732}
733
734
735// ECMA 262 - 15.9.5.22
736function DateGetSeconds() {
Leon Clarkeac952652010-07-15 11:15:24 +0100737 var t = DATE_VALUE(this);
738 if (NUMBER_IS_NAN(t)) return t;
739 return SEC_FROM_TIME(LocalTimeNoCheck(t));
Andrei Popescu31002712010-02-23 13:46:05 +0000740}
741
742
743// ECMA 262 - 15.9.5.23
744function DateGetUTCSeconds() {
Leon Clarkeac952652010-07-15 11:15:24 +0100745 var t = DATE_VALUE(this);
746 return NAN_OR_SEC_FROM_TIME(t);
Andrei Popescu31002712010-02-23 13:46:05 +0000747}
748
749
750// ECMA 262 - 15.9.5.24
751function DateGetMilliseconds() {
Leon Clarkeac952652010-07-15 11:15:24 +0100752 var t = DATE_VALUE(this);
753 if (NUMBER_IS_NAN(t)) return t;
754 return MS_FROM_TIME(LocalTimeNoCheck(t));
Andrei Popescu31002712010-02-23 13:46:05 +0000755}
756
757
758// ECMA 262 - 15.9.5.25
759function DateGetUTCMilliseconds() {
Leon Clarkeac952652010-07-15 11:15:24 +0100760 var t = DATE_VALUE(this);
761 return NAN_OR_MS_FROM_TIME(t);
Andrei Popescu31002712010-02-23 13:46:05 +0000762}
763
764
765// ECMA 262 - 15.9.5.26
766function DateGetTimezoneOffset() {
767 var t = DATE_VALUE(this);
768 if (NUMBER_IS_NAN(t)) return t;
769 return (t - LocalTimeNoCheck(t)) / msPerMinute;
770}
771
772
773// ECMA 262 - 15.9.5.27
774function DateSetTime(ms) {
775 if (!IS_DATE(this)) ThrowDateTypeError();
776 return %_SetValueOf(this, TimeClip(ToNumber(ms)));
777}
778
779
780// ECMA 262 - 15.9.5.28
781function DateSetMilliseconds(ms) {
782 var t = LocalTime(DATE_VALUE(this));
783 ms = ToNumber(ms);
784 var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), SEC_FROM_TIME(t), ms);
785 return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
786}
787
788
789// ECMA 262 - 15.9.5.29
790function DateSetUTCMilliseconds(ms) {
791 var t = DATE_VALUE(this);
792 ms = ToNumber(ms);
793 var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), SEC_FROM_TIME(t), ms);
794 return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
795}
796
797
798// ECMA 262 - 15.9.5.30
799function DateSetSeconds(sec, ms) {
800 var t = LocalTime(DATE_VALUE(this));
801 sec = ToNumber(sec);
Leon Clarkeac952652010-07-15 11:15:24 +0100802 ms = %_ArgumentsLength() < 2 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms);
Andrei Popescu31002712010-02-23 13:46:05 +0000803 var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), sec, ms);
804 return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
805}
806
807
808// ECMA 262 - 15.9.5.31
809function DateSetUTCSeconds(sec, ms) {
810 var t = DATE_VALUE(this);
811 sec = ToNumber(sec);
Leon Clarkeac952652010-07-15 11:15:24 +0100812 ms = %_ArgumentsLength() < 2 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms);
Andrei Popescu31002712010-02-23 13:46:05 +0000813 var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), sec, ms);
814 return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
815}
816
817
818// ECMA 262 - 15.9.5.33
819function DateSetMinutes(min, sec, ms) {
820 var t = LocalTime(DATE_VALUE(this));
821 min = ToNumber(min);
822 var argc = %_ArgumentsLength();
Leon Clarkeac952652010-07-15 11:15:24 +0100823 sec = argc < 2 ? NAN_OR_SEC_FROM_TIME(t) : ToNumber(sec);
824 ms = argc < 3 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms);
Andrei Popescu31002712010-02-23 13:46:05 +0000825 var time = MakeTime(HOUR_FROM_TIME(t), min, sec, ms);
826 return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
827}
828
829
830// ECMA 262 - 15.9.5.34
831function DateSetUTCMinutes(min, sec, ms) {
832 var t = DATE_VALUE(this);
833 min = ToNumber(min);
834 var argc = %_ArgumentsLength();
Leon Clarkeac952652010-07-15 11:15:24 +0100835 sec = argc < 2 ? NAN_OR_SEC_FROM_TIME(t) : ToNumber(sec);
836 ms = argc < 3 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms);
Andrei Popescu31002712010-02-23 13:46:05 +0000837 var time = MakeTime(HOUR_FROM_TIME(t), min, sec, ms);
838 return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
839}
840
841
842// ECMA 262 - 15.9.5.35
843function DateSetHours(hour, min, sec, ms) {
844 var t = LocalTime(DATE_VALUE(this));
845 hour = ToNumber(hour);
846 var argc = %_ArgumentsLength();
Leon Clarkeac952652010-07-15 11:15:24 +0100847 min = argc < 2 ? NAN_OR_MIN_FROM_TIME(t) : ToNumber(min);
848 sec = argc < 3 ? NAN_OR_SEC_FROM_TIME(t) : ToNumber(sec);
849 ms = argc < 4 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms);
Andrei Popescu31002712010-02-23 13:46:05 +0000850 var time = MakeTime(hour, min, sec, ms);
851 return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
852}
853
854
855// ECMA 262 - 15.9.5.34
856function DateSetUTCHours(hour, min, sec, ms) {
857 var t = DATE_VALUE(this);
858 hour = ToNumber(hour);
859 var argc = %_ArgumentsLength();
Leon Clarkeac952652010-07-15 11:15:24 +0100860 min = argc < 2 ? NAN_OR_MIN_FROM_TIME(t) : ToNumber(min);
861 sec = argc < 3 ? NAN_OR_SEC_FROM_TIME(t) : ToNumber(sec);
862 ms = argc < 4 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms);
Andrei Popescu31002712010-02-23 13:46:05 +0000863 var time = MakeTime(hour, min, sec, ms);
864 return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
865}
866
867
868// ECMA 262 - 15.9.5.36
869function DateSetDate(date) {
870 var t = LocalTime(DATE_VALUE(this));
871 date = ToNumber(date);
872 var day = MakeDay(YEAR_FROM_TIME(t), MONTH_FROM_TIME(t), date);
873 return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
874}
875
876
877// ECMA 262 - 15.9.5.37
878function DateSetUTCDate(date) {
879 var t = DATE_VALUE(this);
880 date = ToNumber(date);
881 var day = MakeDay(YEAR_FROM_TIME(t), MONTH_FROM_TIME(t), date);
882 return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t))));
883}
884
885
886// ECMA 262 - 15.9.5.38
887function DateSetMonth(month, date) {
888 var t = LocalTime(DATE_VALUE(this));
889 month = ToNumber(month);
Leon Clarkeac952652010-07-15 11:15:24 +0100890 date = %_ArgumentsLength() < 2 ? NAN_OR_DATE_FROM_TIME(t) : ToNumber(date);
Andrei Popescu31002712010-02-23 13:46:05 +0000891 var day = MakeDay(YEAR_FROM_TIME(t), month, date);
892 return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
893}
894
895
896// ECMA 262 - 15.9.5.39
897function DateSetUTCMonth(month, date) {
898 var t = DATE_VALUE(this);
899 month = ToNumber(month);
Leon Clarkeac952652010-07-15 11:15:24 +0100900 date = %_ArgumentsLength() < 2 ? NAN_OR_DATE_FROM_TIME(t) : ToNumber(date);
Andrei Popescu31002712010-02-23 13:46:05 +0000901 var day = MakeDay(YEAR_FROM_TIME(t), month, date);
902 return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t))));
903}
904
905
906// ECMA 262 - 15.9.5.40
907function DateSetFullYear(year, month, date) {
908 var t = DATE_VALUE(this);
909 t = NUMBER_IS_NAN(t) ? 0 : LocalTimeNoCheck(t);
910 year = ToNumber(year);
911 var argc = %_ArgumentsLength();
912 month = argc < 2 ? MONTH_FROM_TIME(t) : ToNumber(month);
913 date = argc < 3 ? DATE_FROM_TIME(t) : ToNumber(date);
914 var day = MakeDay(year, month, date);
915 return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
916}
917
918
919// ECMA 262 - 15.9.5.41
920function DateSetUTCFullYear(year, month, date) {
921 var t = DATE_VALUE(this);
922 if (NUMBER_IS_NAN(t)) t = 0;
923 var argc = %_ArgumentsLength();
924 year = ToNumber(year);
925 month = argc < 2 ? MONTH_FROM_TIME(t) : ToNumber(month);
926 date = argc < 3 ? DATE_FROM_TIME(t) : ToNumber(date);
927 var day = MakeDay(year, month, date);
928 return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t))));
929}
930
931
932// ECMA 262 - 15.9.5.42
933function DateToUTCString() {
934 var t = DATE_VALUE(this);
935 if (NUMBER_IS_NAN(t)) return kInvalidDate;
936 // Return UTC string of the form: Sat, 31 Jan 1970 23:00:00 GMT
937 return WeekDays[WeekDay(t)] + ', '
938 + TwoDigitString(DATE_FROM_TIME(t)) + ' '
939 + Months[MONTH_FROM_TIME(t)] + ' '
940 + YEAR_FROM_TIME(t) + ' '
941 + TimeString(t) + ' GMT';
942}
943
944
945// ECMA 262 - B.2.4
946function DateGetYear() {
947 var t = DATE_VALUE(this);
948 if (NUMBER_IS_NAN(t)) return $NaN;
949 return YEAR_FROM_TIME(LocalTimeNoCheck(t)) - 1900;
950}
951
952
953// ECMA 262 - B.2.5
954function DateSetYear(year) {
955 var t = LocalTime(DATE_VALUE(this));
956 if (NUMBER_IS_NAN(t)) t = 0;
957 year = ToNumber(year);
958 if (NUMBER_IS_NAN(year)) return %_SetValueOf(this, $NaN);
959 year = (0 <= TO_INTEGER(year) && TO_INTEGER(year) <= 99)
960 ? 1900 + TO_INTEGER(year) : year;
961 var day = MakeDay(year, MONTH_FROM_TIME(t), DATE_FROM_TIME(t));
962 return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
963}
964
965
966// ECMA 262 - B.2.6
967//
968// Notice that this does not follow ECMA 262 completely. ECMA 262
969// says that toGMTString should be the same Function object as
970// toUTCString. JSC does not do this, so for compatibility we do not
971// do that either. Instead, we create a new function whose name
972// property will return toGMTString.
973function DateToGMTString() {
974 return DateToUTCString.call(this);
975}
976
977
978function PadInt(n, digits) {
979 if (digits == 1) return n;
980 return n < MathPow(10, digits - 1) ? '0' + PadInt(n, digits - 1) : n;
981}
982
983
984function DateToISOString() {
985 var t = DATE_VALUE(this);
986 if (NUMBER_IS_NAN(t)) return kInvalidDate;
987 return this.getUTCFullYear() + '-' + PadInt(this.getUTCMonth() + 1, 2) +
988 '-' + PadInt(this.getUTCDate(), 2) + 'T' + PadInt(this.getUTCHours(), 2) +
989 ':' + PadInt(this.getUTCMinutes(), 2) + ':' + PadInt(this.getUTCSeconds(), 2) +
990 '.' + PadInt(this.getUTCMilliseconds(), 3) +
991 'Z';
992}
993
994
995function DateToJSON(key) {
996 return CheckJSONPrimitive(this.toISOString());
997}
998
999
1000// -------------------------------------------------------------------
1001
1002function SetupDate() {
1003 // Setup non-enumerable properties of the Date object itself.
1004 InstallFunctions($Date, DONT_ENUM, $Array(
1005 "UTC", DateUTC,
1006 "parse", DateParse,
1007 "now", DateNow
1008 ));
1009
1010 // Setup non-enumerable constructor property of the Date prototype object.
1011 %SetProperty($Date.prototype, "constructor", $Date, DONT_ENUM);
1012
1013 // Setup non-enumerable functions of the Date prototype object and
1014 // set their names.
1015 InstallFunctionsOnHiddenPrototype($Date.prototype, DONT_ENUM, $Array(
1016 "toString", DateToString,
1017 "toDateString", DateToDateString,
1018 "toTimeString", DateToTimeString,
1019 "toLocaleString", DateToLocaleString,
1020 "toLocaleDateString", DateToLocaleDateString,
1021 "toLocaleTimeString", DateToLocaleTimeString,
1022 "valueOf", DateValueOf,
1023 "getTime", DateGetTime,
1024 "getFullYear", DateGetFullYear,
1025 "getUTCFullYear", DateGetUTCFullYear,
1026 "getMonth", DateGetMonth,
1027 "getUTCMonth", DateGetUTCMonth,
1028 "getDate", DateGetDate,
1029 "getUTCDate", DateGetUTCDate,
1030 "getDay", DateGetDay,
1031 "getUTCDay", DateGetUTCDay,
1032 "getHours", DateGetHours,
1033 "getUTCHours", DateGetUTCHours,
1034 "getMinutes", DateGetMinutes,
1035 "getUTCMinutes", DateGetUTCMinutes,
1036 "getSeconds", DateGetSeconds,
1037 "getUTCSeconds", DateGetUTCSeconds,
1038 "getMilliseconds", DateGetMilliseconds,
1039 "getUTCMilliseconds", DateGetUTCMilliseconds,
1040 "getTimezoneOffset", DateGetTimezoneOffset,
1041 "setTime", DateSetTime,
1042 "setMilliseconds", DateSetMilliseconds,
1043 "setUTCMilliseconds", DateSetUTCMilliseconds,
1044 "setSeconds", DateSetSeconds,
1045 "setUTCSeconds", DateSetUTCSeconds,
1046 "setMinutes", DateSetMinutes,
1047 "setUTCMinutes", DateSetUTCMinutes,
1048 "setHours", DateSetHours,
1049 "setUTCHours", DateSetUTCHours,
1050 "setDate", DateSetDate,
1051 "setUTCDate", DateSetUTCDate,
1052 "setMonth", DateSetMonth,
1053 "setUTCMonth", DateSetUTCMonth,
1054 "setFullYear", DateSetFullYear,
1055 "setUTCFullYear", DateSetUTCFullYear,
1056 "toGMTString", DateToGMTString,
1057 "toUTCString", DateToUTCString,
1058 "getYear", DateGetYear,
1059 "setYear", DateSetYear,
1060 "toISOString", DateToISOString,
1061 "toJSON", DateToJSON
1062 ));
1063}
1064
1065SetupDate();