1// 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// var $isFinite = GlobalIsFinite;
32
33// -------------------------------------------------------------------
34
35// This file contains date support implemented in JavaScript.
36
37// Keep reference to original values of some global properties.  This
38// has the added benefit that the code in this file is isolated from
39// changes to these properties.
40var $Date = global.Date;
41
42// Helper function to throw error.
43function ThrowDateTypeError() {
44  throw new $TypeError('this is not a Date object.');
45}
46
47
48var timezone_cache_time = $NaN;
49var timezone_cache_timezone;
50
51function LocalTimezone(t) {
52  if (NUMBER_IS_NAN(t)) return "";
53  if (t == timezone_cache_time) {
54    return timezone_cache_timezone;
55  }
56  var timezone = %DateLocalTimezone(t);
57  timezone_cache_time = t;
58  timezone_cache_timezone = timezone;
59  return timezone;
60}
61
62
63function UTC(time) {
64  if (NUMBER_IS_NAN(time)) return time;
65  // local_time_offset is needed before the call to DaylightSavingsOffset,
66  // so it may be uninitialized.
67  return %DateToUTC(time);
68}
69
70
71// ECMA 262 - 15.9.1.11
72function MakeTime(hour, min, sec, ms) {
73  if (!$isFinite(hour)) return $NaN;
74  if (!$isFinite(min)) return $NaN;
75  if (!$isFinite(sec)) return $NaN;
76  if (!$isFinite(ms)) return $NaN;
77  return TO_INTEGER(hour) * msPerHour
78      + TO_INTEGER(min) * msPerMinute
79      + TO_INTEGER(sec) * msPerSecond
80      + TO_INTEGER(ms);
81}
82
83
84// ECMA 262 - 15.9.1.12
85function TimeInYear(year) {
86  return DaysInYear(year) * msPerDay;
87}
88
89
90// Compute number of days given a year, month, date.
91// Note that month and date can lie outside the normal range.
92//   For example:
93//     MakeDay(2007, -4, 20) --> MakeDay(2006, 8, 20)
94//     MakeDay(2007, -33, 1) --> MakeDay(2004, 3, 1)
95//     MakeDay(2007, 14, -50) --> MakeDay(2007, 8, 11)
96function MakeDay(year, month, date) {
97  if (!$isFinite(year) || !$isFinite(month) || !$isFinite(date)) return $NaN;
98
99  // Convert to integer and map -0 to 0.
100  year = TO_INTEGER_MAP_MINUS_ZERO(year);
101  month = TO_INTEGER_MAP_MINUS_ZERO(month);
102  date = TO_INTEGER_MAP_MINUS_ZERO(date);
103
104  if (year < kMinYear || year > kMaxYear ||
105      month < kMinMonth || month > kMaxMonth) {
106    return $NaN;
107  }
108
109  // Now we rely on year and month being SMIs.
110  return %DateMakeDay(year, month) + date - 1;
111}
112
113
114// ECMA 262 - 15.9.1.13
115function MakeDate(day, time) {
116  var time = day * msPerDay + time;
117  // Some of our runtime funtions for computing UTC(time) rely on
118  // times not being significantly larger than MAX_TIME_MS. If there
119  // is no way that the time can be within range even after UTC
120  // conversion we return NaN immediately instead of relying on
121  // TimeClip to do it.
122  if ($abs(time) > MAX_TIME_BEFORE_UTC) return $NaN;
123  return time;
124}
125
126
127// ECMA 262 - 15.9.1.14
128function TimeClip(time) {
129  if (!$isFinite(time)) return $NaN;
130  if ($abs(time) > MAX_TIME_MS) return $NaN;
131  return TO_INTEGER(time);
132}
133
134
135// The Date cache is used to limit the cost of parsing the same Date
136// strings over and over again.
137var Date_cache = {
138  // Cached time value.
139  time: $NaN,
140  // String input for which the cached time is valid.
141  string: null
142};
143
144
145%SetCode($Date, function(year, month, date, hours, minutes, seconds, ms) {
146  if (!%_IsConstructCall()) {
147    // ECMA 262 - 15.9.2
148    return (new $Date()).toString();
149  }
150
151  // ECMA 262 - 15.9.3
152  var argc = %_ArgumentsLength();
153  var value;
154  if (argc == 0) {
155    value = %DateCurrentTime();
156    SET_UTC_DATE_VALUE(this, value);
157  } else if (argc == 1) {
158    if (IS_NUMBER(year)) {
159      value = year;
160    } else if (IS_STRING(year)) {
161      // Probe the Date cache. If we already have a time value for the
162      // given time, we re-use that instead of parsing the string again.
163      var cache = Date_cache;
164      if (cache.string === year) {
165        value = cache.time;
166      } else {
167        value = DateParse(year);
168        if (!NUMBER_IS_NAN(value)) {
169          cache.time = value;
170          cache.string = year;
171        }
172      }
173
174    } else {
175      // According to ECMA 262, no hint should be given for this
176      // conversion. However, ToPrimitive defaults to STRING_HINT for
177      // Date objects which will lose precision when the Date
178      // constructor is called with another Date object as its
179      // argument. We therefore use NUMBER_HINT for the conversion,
180      // which is the default for everything else than Date objects.
181      // This makes us behave like KJS and SpiderMonkey.
182      var time = ToPrimitive(year, NUMBER_HINT);
183      value = IS_STRING(time) ? DateParse(time) : ToNumber(time);
184    }
185    SET_UTC_DATE_VALUE(this, value);
186  } else {
187    year = ToNumber(year);
188    month = ToNumber(month);
189    date = argc > 2 ? ToNumber(date) : 1;
190    hours = argc > 3 ? ToNumber(hours) : 0;
191    minutes = argc > 4 ? ToNumber(minutes) : 0;
192    seconds = argc > 5 ? ToNumber(seconds) : 0;
193    ms = argc > 6 ? ToNumber(ms) : 0;
194    year = (!NUMBER_IS_NAN(year) &&
195            0 <= TO_INTEGER(year) &&
196            TO_INTEGER(year) <= 99) ? 1900 + TO_INTEGER(year) : year;
197    var day = MakeDay(year, month, date);
198    var time = MakeTime(hours, minutes, seconds, ms);
199    value = MakeDate(day, time);
200    SET_LOCAL_DATE_VALUE(this, value);
201  }
202});
203
204
205%FunctionSetPrototype($Date, new $Date($NaN));
206
207
208var WeekDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
209var Months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
210              'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
211
212
213function TwoDigitString(value) {
214  return value < 10 ? "0" + value : "" + value;
215}
216
217
218function DateString(date) {
219  return WeekDays[LOCAL_WEEKDAY(date)] + ' '
220      + Months[LOCAL_MONTH(date)] + ' '
221      + TwoDigitString(LOCAL_DAY(date)) + ' '
222      + LOCAL_YEAR(date);
223}
224
225
226var LongWeekDays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday',
227    'Thursday', 'Friday', 'Saturday'];
228var LongMonths = ['January', 'February', 'March', 'April', 'May', 'June',
229    'July', 'August', 'September', 'October', 'November', 'December'];
230
231
232function LongDateString(date) {
233  return LongWeekDays[LOCAL_WEEKDAY(date)] + ', '
234      + LongMonths[LOCAL_MONTH(date)] + ' '
235      + TwoDigitString(LOCAL_DAY(date)) + ', '
236      + LOCAL_YEAR(date);
237}
238
239
240function TimeString(date) {
241  return TwoDigitString(LOCAL_HOUR(date)) + ':'
242      + TwoDigitString(LOCAL_MIN(date)) + ':'
243      + TwoDigitString(LOCAL_SEC(date));
244}
245
246
247function TimeStringUTC(date) {
248  return TwoDigitString(UTC_HOUR(date)) + ':'
249      + TwoDigitString(UTC_MIN(date)) + ':'
250      + TwoDigitString(UTC_SEC(date));
251}
252
253
254function LocalTimezoneString(date) {
255  var timezone = LocalTimezone(UTC_DATE_VALUE(date));
256
257  var timezoneOffset = -TIMEZONE_OFFSET(date);
258  var sign = (timezoneOffset >= 0) ? 1 : -1;
259  var hours = FLOOR((sign * timezoneOffset)/60);
260  var min   = FLOOR((sign * timezoneOffset)%60);
261  var gmt = ' GMT' + ((sign == 1) ? '+' : '-') +
262      TwoDigitString(hours) + TwoDigitString(min);
263  return gmt + ' (' +  timezone + ')';
264}
265
266
267function DatePrintString(date) {
268  return DateString(date) + ' ' + TimeString(date);
269}
270
271// -------------------------------------------------------------------
272
273// Reused output buffer. Used when parsing date strings.
274var parse_buffer = $Array(8);
275
276// ECMA 262 - 15.9.4.2
277function DateParse(string) {
278  var arr = %DateParseString(ToString(string), parse_buffer);
279  if (IS_NULL(arr)) return $NaN;
280
281  var day = MakeDay(arr[0], arr[1], arr[2]);
282  var time = MakeTime(arr[3], arr[4], arr[5], arr[6]);
283  var date = MakeDate(day, time);
284
285  if (IS_NULL(arr[7])) {
286    return TimeClip(UTC(date));
287  } else {
288    return TimeClip(date - arr[7] * 1000);
289  }
290}
291
292
293// ECMA 262 - 15.9.4.3
294function DateUTC(year, month, date, hours, minutes, seconds, ms) {
295  year = ToNumber(year);
296  month = ToNumber(month);
297  var argc = %_ArgumentsLength();
298  date = argc > 2 ? ToNumber(date) : 1;
299  hours = argc > 3 ? ToNumber(hours) : 0;
300  minutes = argc > 4 ? ToNumber(minutes) : 0;
301  seconds = argc > 5 ? ToNumber(seconds) : 0;
302  ms = argc > 6 ? ToNumber(ms) : 0;
303  year = (!NUMBER_IS_NAN(year) &&
304          0 <= TO_INTEGER(year) &&
305          TO_INTEGER(year) <= 99) ? 1900 + TO_INTEGER(year) : year;
306  var day = MakeDay(year, month, date);
307  var time = MakeTime(hours, minutes, seconds, ms);
308  return TimeClip(MakeDate(day, time));
309}
310
311
312// Mozilla-specific extension. Returns the number of milliseconds
313// elapsed since 1 January 1970 00:00:00 UTC.
314function DateNow() {
315  return %DateCurrentTime();
316}
317
318
319// ECMA 262 - 15.9.5.2
320function DateToString() {
321  CHECK_DATE(this);
322  var t = UTC_DATE_VALUE(this)
323  if (NUMBER_IS_NAN(t)) return kInvalidDate;
324  var time_zone_string = LocalTimezoneString(this)
325  return DatePrintString(this) + time_zone_string;
326}
327
328
329// ECMA 262 - 15.9.5.3
330function DateToDateString() {
331  CHECK_DATE(this);
332  var t = UTC_DATE_VALUE(this);
333  if (NUMBER_IS_NAN(t)) return kInvalidDate;
334  return DateString(this);
335}
336
337
338// ECMA 262 - 15.9.5.4
339function DateToTimeString() {
340  CHECK_DATE(this);
341  var t = UTC_DATE_VALUE(this);
342  if (NUMBER_IS_NAN(t)) return kInvalidDate;
343  var time_zone_string = LocalTimezoneString(this);
344  return TimeString(this) + time_zone_string;
345}
346
347
348// ECMA 262 - 15.9.5.5
349function DateToLocaleString() {
350  return %_CallFunction(this, DateToString);
351}
352
353
354// ECMA 262 - 15.9.5.6
355function DateToLocaleDateString() {
356  CHECK_DATE(this);
357  var t = UTC_DATE_VALUE(this);
358  if (NUMBER_IS_NAN(t)) return kInvalidDate;
359  return LongDateString(this);
360}
361
362
363// ECMA 262 - 15.9.5.7
364function DateToLocaleTimeString() {
365  CHECK_DATE(this);
366  var t = UTC_DATE_VALUE(this);
367  if (NUMBER_IS_NAN(t)) return kInvalidDate;
368  return TimeString(this);
369}
370
371
372// ECMA 262 - 15.9.5.8
373function DateValueOf() {
374  CHECK_DATE(this);
375  return UTC_DATE_VALUE(this);
376}
377
378
379// ECMA 262 - 15.9.5.9
380function DateGetTime() {
381  CHECK_DATE(this);
382  return UTC_DATE_VALUE(this);
383}
384
385
386// ECMA 262 - 15.9.5.10
387function DateGetFullYear() {
388  CHECK_DATE(this);
389  return LOCAL_YEAR(this);
390}
391
392
393// ECMA 262 - 15.9.5.11
394function DateGetUTCFullYear() {
395  CHECK_DATE(this);
396  return UTC_YEAR(this);
397}
398
399
400// ECMA 262 - 15.9.5.12
401function DateGetMonth() {
402  CHECK_DATE(this);
403  return LOCAL_MONTH(this);
404}
405
406
407// ECMA 262 - 15.9.5.13
408function DateGetUTCMonth() {
409  CHECK_DATE(this);
410  return UTC_MONTH(this);
411}
412
413
414// ECMA 262 - 15.9.5.14
415function DateGetDate() {
416  CHECK_DATE(this);
417  return LOCAL_DAY(this);
418}
419
420
421// ECMA 262 - 15.9.5.15
422function DateGetUTCDate() {
423  CHECK_DATE(this);
424  return UTC_DAY(this);
425}
426
427
428// ECMA 262 - 15.9.5.16
429function DateGetDay() {
430  CHECK_DATE(this);
431  return LOCAL_WEEKDAY(this);
432}
433
434
435// ECMA 262 - 15.9.5.17
436function DateGetUTCDay() {
437  CHECK_DATE(this);
438  return UTC_WEEKDAY(this);
439}
440
441
442// ECMA 262 - 15.9.5.18
443function DateGetHours() {
444  CHECK_DATE(this);
445  return LOCAL_HOUR(this);
446}
447
448
449// ECMA 262 - 15.9.5.19
450function DateGetUTCHours() {
451  CHECK_DATE(this);
452  return UTC_HOUR(this);
453}
454
455
456// ECMA 262 - 15.9.5.20
457function DateGetMinutes() {
458  CHECK_DATE(this);
459  return LOCAL_MIN(this);
460}
461
462
463// ECMA 262 - 15.9.5.21
464function DateGetUTCMinutes() {
465  CHECK_DATE(this);
466  return UTC_MIN(this);
467}
468
469
470// ECMA 262 - 15.9.5.22
471function DateGetSeconds() {
472  CHECK_DATE(this);
473  return LOCAL_SEC(this);
474}
475
476
477// ECMA 262 - 15.9.5.23
478function DateGetUTCSeconds() {
479  CHECK_DATE(this);
480  return UTC_SEC(this)
481}
482
483
484// ECMA 262 - 15.9.5.24
485function DateGetMilliseconds() {
486  CHECK_DATE(this);
487  return LOCAL_MS(this);
488}
489
490
491// ECMA 262 - 15.9.5.25
492function DateGetUTCMilliseconds() {
493  CHECK_DATE(this);
494  return UTC_MS(this);
495}
496
497
498// ECMA 262 - 15.9.5.26
499function DateGetTimezoneOffset() {
500  CHECK_DATE(this);
501  return TIMEZONE_OFFSET(this);
502}
503
504
505// ECMA 262 - 15.9.5.27
506function DateSetTime(ms) {
507  CHECK_DATE(this);
508  SET_UTC_DATE_VALUE(this, ToNumber(ms));
509  return UTC_DATE_VALUE(this);
510}
511
512
513// ECMA 262 - 15.9.5.28
514function DateSetMilliseconds(ms) {
515  CHECK_DATE(this);
516  var t = LOCAL_DATE_VALUE(this);
517  ms = ToNumber(ms);
518  var time = MakeTime(LOCAL_HOUR(this), LOCAL_MIN(this), LOCAL_SEC(this), ms);
519  return SET_LOCAL_DATE_VALUE(this, MakeDate(LOCAL_DAYS(this), time));
520}
521
522
523// ECMA 262 - 15.9.5.29
524function DateSetUTCMilliseconds(ms) {
525  CHECK_DATE(this);
526  var t = UTC_DATE_VALUE(this);
527  ms = ToNumber(ms);
528  var time = MakeTime(UTC_HOUR(this),
529                      UTC_MIN(this),
530                      UTC_SEC(this),
531                      ms);
532  return SET_UTC_DATE_VALUE(this, MakeDate(UTC_DAYS(this), time));
533}
534
535
536// ECMA 262 - 15.9.5.30
537function DateSetSeconds(sec, ms) {
538  CHECK_DATE(this);
539  var t = LOCAL_DATE_VALUE(this);
540  sec = ToNumber(sec);
541  ms = %_ArgumentsLength() < 2 ? LOCAL_MS(this) : ToNumber(ms);
542  var time = MakeTime(LOCAL_HOUR(this), LOCAL_MIN(this), sec, ms);
543  return SET_LOCAL_DATE_VALUE(this, MakeDate(LOCAL_DAYS(this), time));
544}
545
546
547// ECMA 262 - 15.9.5.31
548function DateSetUTCSeconds(sec, ms) {
549  CHECK_DATE(this);
550  var t = UTC_DATE_VALUE(this);
551  sec = ToNumber(sec);
552  ms = %_ArgumentsLength() < 2 ? UTC_MS(this) : ToNumber(ms);
553  var time = MakeTime(UTC_HOUR(this), UTC_MIN(this), sec, ms);
554  return SET_UTC_DATE_VALUE(this, MakeDate(UTC_DAYS(this), time));
555}
556
557
558// ECMA 262 - 15.9.5.33
559function DateSetMinutes(min, sec, ms) {
560  CHECK_DATE(this);
561  var t = LOCAL_DATE_VALUE(this);
562  min = ToNumber(min);
563  var argc = %_ArgumentsLength();
564  sec = argc < 2 ? LOCAL_SEC(this) : ToNumber(sec);
565  ms = argc < 3 ? LOCAL_MS(this) : ToNumber(ms);
566  var time = MakeTime(LOCAL_HOUR(this), min, sec, ms);
567  return SET_LOCAL_DATE_VALUE(this, MakeDate(LOCAL_DAYS(this), time));
568}
569
570
571// ECMA 262 - 15.9.5.34
572function DateSetUTCMinutes(min, sec, ms) {
573  CHECK_DATE(this);
574  var t = UTC_DATE_VALUE(this);
575  min = ToNumber(min);
576  var argc = %_ArgumentsLength();
577  sec = argc < 2 ? UTC_SEC(this) : ToNumber(sec);
578  ms = argc < 3 ? UTC_MS(this) : ToNumber(ms);
579  var time = MakeTime(UTC_HOUR(this), min, sec, ms);
580  return SET_UTC_DATE_VALUE(this, MakeDate(UTC_DAYS(this), time));
581}
582
583
584// ECMA 262 - 15.9.5.35
585function DateSetHours(hour, min, sec, ms) {
586  CHECK_DATE(this);
587  var t = LOCAL_DATE_VALUE(this);
588  hour = ToNumber(hour);
589  var argc = %_ArgumentsLength();
590  min = argc < 2 ? LOCAL_MIN(this) : ToNumber(min);
591  sec = argc < 3 ? LOCAL_SEC(this) : ToNumber(sec);
592  ms = argc < 4 ? LOCAL_MS(this) : ToNumber(ms);
593  var time = MakeTime(hour, min, sec, ms);
594  return SET_LOCAL_DATE_VALUE(this, MakeDate(LOCAL_DAYS(this), time));
595}
596
597
598// ECMA 262 - 15.9.5.34
599function DateSetUTCHours(hour, min, sec, ms) {
600  CHECK_DATE(this);
601  var t = UTC_DATE_VALUE(this);
602  hour = ToNumber(hour);
603  var argc = %_ArgumentsLength();
604  min = argc < 2 ? UTC_MIN(this) : ToNumber(min);
605  sec = argc < 3 ? UTC_SEC(this) : ToNumber(sec);
606  ms = argc < 4 ? UTC_MS(this) : ToNumber(ms);
607  var time = MakeTime(hour, min, sec, ms);
608  return SET_UTC_DATE_VALUE(this, MakeDate(UTC_DAYS(this), time));
609}
610
611
612// ECMA 262 - 15.9.5.36
613function DateSetDate(date) {
614  CHECK_DATE(this);
615  var t = LOCAL_DATE_VALUE(this);
616  date = ToNumber(date);
617  var day = MakeDay(LOCAL_YEAR(this), LOCAL_MONTH(this), date);
618  return SET_LOCAL_DATE_VALUE(this, MakeDate(day, LOCAL_TIME_IN_DAY(this)));
619}
620
621
622// ECMA 262 - 15.9.5.37
623function DateSetUTCDate(date) {
624  CHECK_DATE(this);
625  var t = UTC_DATE_VALUE(this);
626  date = ToNumber(date);
627  var day = MakeDay(UTC_YEAR(this), UTC_MONTH(this), date);
628  return SET_UTC_DATE_VALUE(this, MakeDate(day, UTC_TIME_IN_DAY(this)));
629}
630
631
632// ECMA 262 - 15.9.5.38
633function DateSetMonth(month, date) {
634  CHECK_DATE(this);
635  var t = LOCAL_DATE_VALUE(this);
636  month = ToNumber(month);
637  date = %_ArgumentsLength() < 2 ? LOCAL_DAY(this) : ToNumber(date);
638  var day = MakeDay(LOCAL_YEAR(this), month, date);
639  return SET_LOCAL_DATE_VALUE(this, MakeDate(day, LOCAL_TIME_IN_DAY(this)));
640}
641
642
643// ECMA 262 - 15.9.5.39
644function DateSetUTCMonth(month, date) {
645  CHECK_DATE(this);
646  var t = UTC_DATE_VALUE(this);
647  month = ToNumber(month);
648  date = %_ArgumentsLength() < 2 ? UTC_DAY(this) : ToNumber(date);
649  var day = MakeDay(UTC_YEAR(this), month, date);
650  return SET_UTC_DATE_VALUE(this, MakeDate(day, UTC_TIME_IN_DAY(this)));
651}
652
653
654// ECMA 262 - 15.9.5.40
655function DateSetFullYear(year, month, date) {
656  CHECK_DATE(this);
657  var t = LOCAL_DATE_VALUE(this);
658  year = ToNumber(year);
659  var argc = %_ArgumentsLength();
660  var time ;
661  if (NUMBER_IS_NAN(t)) {
662    month = argc < 2 ? 0 : ToNumber(month);
663    date = argc < 3 ? 1 : ToNumber(date);
664    time = 0;
665  } else {
666    month = argc < 2 ? LOCAL_MONTH(this) : ToNumber(month);
667    date = argc < 3 ? LOCAL_DAY(this) : ToNumber(date);
668    time = LOCAL_TIME_IN_DAY(this);
669  }
670  var day = MakeDay(year, month, date);
671  return SET_LOCAL_DATE_VALUE(this, MakeDate(day, time));
672}
673
674
675// ECMA 262 - 15.9.5.41
676function DateSetUTCFullYear(year, month, date) {
677  CHECK_DATE(this);
678  var t = UTC_DATE_VALUE(this);
679  year = ToNumber(year);
680  var argc = %_ArgumentsLength();
681  var time ;
682  if (NUMBER_IS_NAN(t)) {
683    month = argc < 2 ? 0 : ToNumber(month);
684    date = argc < 3 ? 1 : ToNumber(date);
685    time = 0;
686  } else {
687    month = argc < 2 ? UTC_MONTH(this) : ToNumber(month);
688    date = argc < 3 ? UTC_DAY(this) : ToNumber(date);
689    time = UTC_TIME_IN_DAY(this);
690  }
691  var day = MakeDay(year, month, date);
692  return SET_UTC_DATE_VALUE(this, MakeDate(day, time));
693}
694
695
696// ECMA 262 - 15.9.5.42
697function DateToUTCString() {
698  CHECK_DATE(this);
699  var t = UTC_DATE_VALUE(this);
700  if (NUMBER_IS_NAN(t)) return kInvalidDate;
701  // Return UTC string of the form: Sat, 31 Jan 1970 23:00:00 GMT
702  return WeekDays[UTC_WEEKDAY(this)] + ', '
703      + TwoDigitString(UTC_DAY(this)) + ' '
704      + Months[UTC_MONTH(this)] + ' '
705      + UTC_YEAR(this) + ' '
706      + TimeStringUTC(this) + ' GMT';
707}
708
709
710// ECMA 262 - B.2.4
711function DateGetYear() {
712  CHECK_DATE(this);
713  return LOCAL_YEAR(this) - 1900;
714}
715
716
717// ECMA 262 - B.2.5
718function DateSetYear(year) {
719  CHECK_DATE(this);
720  year = ToNumber(year);
721  if (NUMBER_IS_NAN(year)) return SET_UTC_DATE_VALUE(this, $NaN);
722  year = (0 <= TO_INTEGER(year) && TO_INTEGER(year) <= 99)
723      ? 1900 + TO_INTEGER(year) : year;
724  var t = LOCAL_DATE_VALUE(this);
725  var month, date, time;
726  if (NUMBER_IS_NAN(t))  {
727    month = 0;
728    date = 1;
729    time = 0;
730  } else {
731    month = LOCAL_MONTH(this);
732    date = LOCAL_DAY(this);
733    time = LOCAL_TIME_IN_DAY(this);
734  }
735  var day = MakeDay(year, month, date);
736  return SET_LOCAL_DATE_VALUE(this, MakeDate(day, time));
737}
738
739
740// ECMA 262 - B.2.6
741//
742// Notice that this does not follow ECMA 262 completely.  ECMA 262
743// says that toGMTString should be the same Function object as
744// toUTCString.  JSC does not do this, so for compatibility we do not
745// do that either.  Instead, we create a new function whose name
746// property will return toGMTString.
747function DateToGMTString() {
748  return %_CallFunction(this, DateToUTCString);
749}
750
751
752function PadInt(n, digits) {
753  if (digits == 1) return n;
754  return n < MathPow(10, digits - 1) ? '0' + PadInt(n, digits - 1) : n;
755}
756
757
758// ECMA 262 - 15.9.5.43
759function DateToISOString() {
760  CHECK_DATE(this);
761  var t = UTC_DATE_VALUE(this);
762  if (NUMBER_IS_NAN(t)) throw MakeRangeError("invalid_time_value", []);
763  var year = this.getUTCFullYear();
764  var year_string;
765  if (year >= 0 && year <= 9999) {
766    year_string = PadInt(year, 4);
767  } else {
768    if (year < 0) {
769      year_string = "-" + PadInt(-year, 6);
770    } else {
771      year_string = "+" + PadInt(year, 6);
772    }
773  }
774  return year_string +
775      '-' + PadInt(this.getUTCMonth() + 1, 2) +
776      '-' + PadInt(this.getUTCDate(), 2) +
777      'T' + PadInt(this.getUTCHours(), 2) +
778      ':' + PadInt(this.getUTCMinutes(), 2) +
779      ':' + PadInt(this.getUTCSeconds(), 2) +
780      '.' + PadInt(this.getUTCMilliseconds(), 3) +
781      'Z';
782}
783
784
785function DateToJSON(key) {
786  var o = ToObject(this);
787  var tv = DefaultNumber(o);
788  if (IS_NUMBER(tv) && !NUMBER_IS_FINITE(tv)) {
789    return null;
790  }
791  return o.toISOString();
792}
793
794
795function ResetDateCache() {
796  // Reset the timezone cache:
797  timezone_cache_time = $NaN;
798  timezone_cache_timezone = undefined;
799
800  // Reset the date cache:
801  cache = Date_cache;
802  cache.time = $NaN;
803  cache.string = null;
804}
805
806
807// -------------------------------------------------------------------
808
809function SetUpDate() {
810  %CheckIsBootstrapping();
811  // Set up non-enumerable properties of the Date object itself.
812  InstallFunctions($Date, DONT_ENUM, $Array(
813    "UTC", DateUTC,
814    "parse", DateParse,
815    "now", DateNow
816  ));
817
818  // Set up non-enumerable constructor property of the Date prototype object.
819  %SetProperty($Date.prototype, "constructor", $Date, DONT_ENUM);
820
821  // Set up non-enumerable functions of the Date prototype object and
822  // set their names.
823  InstallFunctions($Date.prototype, DONT_ENUM, $Array(
824    "toString", DateToString,
825    "toDateString", DateToDateString,
826    "toTimeString", DateToTimeString,
827    "toLocaleString", DateToLocaleString,
828    "toLocaleDateString", DateToLocaleDateString,
829    "toLocaleTimeString", DateToLocaleTimeString,
830    "valueOf", DateValueOf,
831    "getTime", DateGetTime,
832    "getFullYear", DateGetFullYear,
833    "getUTCFullYear", DateGetUTCFullYear,
834    "getMonth", DateGetMonth,
835    "getUTCMonth", DateGetUTCMonth,
836    "getDate", DateGetDate,
837    "getUTCDate", DateGetUTCDate,
838    "getDay", DateGetDay,
839    "getUTCDay", DateGetUTCDay,
840    "getHours", DateGetHours,
841    "getUTCHours", DateGetUTCHours,
842    "getMinutes", DateGetMinutes,
843    "getUTCMinutes", DateGetUTCMinutes,
844    "getSeconds", DateGetSeconds,
845    "getUTCSeconds", DateGetUTCSeconds,
846    "getMilliseconds", DateGetMilliseconds,
847    "getUTCMilliseconds", DateGetUTCMilliseconds,
848    "getTimezoneOffset", DateGetTimezoneOffset,
849    "setTime", DateSetTime,
850    "setMilliseconds", DateSetMilliseconds,
851    "setUTCMilliseconds", DateSetUTCMilliseconds,
852    "setSeconds", DateSetSeconds,
853    "setUTCSeconds", DateSetUTCSeconds,
854    "setMinutes", DateSetMinutes,
855    "setUTCMinutes", DateSetUTCMinutes,
856    "setHours", DateSetHours,
857    "setUTCHours", DateSetUTCHours,
858    "setDate", DateSetDate,
859    "setUTCDate", DateSetUTCDate,
860    "setMonth", DateSetMonth,
861    "setUTCMonth", DateSetUTCMonth,
862    "setFullYear", DateSetFullYear,
863    "setUTCFullYear", DateSetUTCFullYear,
864    "toGMTString", DateToGMTString,
865    "toUTCString", DateToUTCString,
866    "getYear", DateGetYear,
867    "setYear", DateSetYear,
868    "toISOString", DateToISOString,
869    "toJSON", DateToJSON
870  ));
871}
872
873SetUpDate();
874