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 // 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. 41 const $Date = global.Date; 42 43 // Helper function to throw error. 44 function ThrowDateTypeError() { 45 throw new $TypeError('this is not a Date object.'); 46 } 47 48 // ECMA 262 - 5.2 49 function 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 57 function TimeWithinDay(time) { 58 return Modulo(time, msPerDay); 59 } 60 61 62 // ECMA 262 - 15.9.1.3 63 function 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 70 function 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 78 function TimeFromYear(year) { 79 return msPerDay * DayFromYear(year); 80 } 81 82 83 function InLeapYear(time) { 84 return DaysInYear(YearFromTime(time)) - 365; // Returns 1 or 0. 85 } 86 87 88 // ECMA 262 - 15.9.1.9 89 function EquivalentYear(year) { 90 // Returns an equivalent year in the range [2008-2035] matching 91 // - leap year. 92 // - week day of first day. 93 var time = TimeFromYear(year); 94 var recent_year = (InLeapYear(time) == 0 ? 1967 : 1956) + 95 (WeekDay(time) * 12) % 28; 96 // Find the year in the range 2008..2037 that is equivalent mod 28. 97 // Add 3*28 to give a positive argument to the modulus operator. 98 return 2008 + (recent_year + 3*28 - 2008) % 28; 99 } 100 101 102 function EquivalentTime(t) { 103 // The issue here is that some library calls don't work right for dates 104 // that cannot be represented using a non-negative signed 32 bit integer 105 // (measured in whole seconds based on the 1970 epoch). 106 // We solve this by mapping the time to a year with same leap-year-ness 107 // and same starting day for the year. The ECMAscript specification says 108 // we must do this, but for compatibility with other browsers, we use 109 // the actual year if it is in the range 1970..2037 110 if (t >= 0 && t <= 2.1e12) return t; 111 112 var day = MakeDay(EquivalentYear(YearFromTime(t)), 113 MonthFromTime(t), 114 DateFromTime(t)); 115 return MakeDate(day, TimeWithinDay(t)); 116 } 117 118 119 // local_time_offset is initialized when the DST_offset_cache is missed. 120 // It must not be used until after a call to DaylightSavingsOffset(). 121 // In this way, only one check, for a DST cache miss, is needed. 122 var local_time_offset; 123 124 125 // Because computing the DST offset is an expensive operation, 126 // we keep a cache of the last computed DST offset along with a time interval 127 // where we know the cache is valid. 128 // When the cache is valid, local_time_offset is also valid. 129 var DST_offset_cache = { 130 // Cached DST offset. 131 offset: 0, 132 // Time interval where the cached offset is valid. 133 start: 0, end: -1, 134 // Size of next interval expansion. 135 increment: 0, 136 initial_increment: 19 * msPerDay 137 }; 138 139 140 // NOTE: The implementation relies on the fact that no time zones have 141 // more than one daylight savings offset change per 19 days. 142 // 143 // In Egypt in 2010 they decided to suspend DST during Ramadan. This 144 // led to a short interval where DST is in effect from September 10 to 145 // September 30. 146 // 147 // If this function is called with NaN it returns NaN. 148 function DaylightSavingsOffset(t) { 149 // Load the cache object from the builtins object. 150 var cache = DST_offset_cache; 151 152 // Cache the start and the end in local variables for fast access. 153 var start = cache.start; 154 var end = cache.end; 155 156 if (start <= t) { 157 // If the time fits in the cached interval, return the cached offset. 158 if (t <= end) return cache.offset; 159 160 // If the cache misses, the local_time_offset may not be initialized. 161 if (IS_UNDEFINED(local_time_offset)) { 162 local_time_offset = %DateLocalTimeOffset(); 163 } 164 165 // Compute a possible new interval end. 166 var new_end = end + cache.increment; 167 168 if (t <= new_end) { 169 var end_offset = %DateDaylightSavingsOffset(EquivalentTime(new_end)); 170 if (cache.offset == end_offset) { 171 // If the offset at the end of the new interval still matches 172 // the offset in the cache, we grow the cached time interval 173 // and return the offset. 174 cache.end = new_end; 175 cache.increment = cache.initial_increment; 176 return end_offset; 177 } else { 178 var offset = %DateDaylightSavingsOffset(EquivalentTime(t)); 179 if (offset == end_offset) { 180 // The offset at the given time is equal to the offset at the 181 // new end of the interval, so that means that we've just skipped 182 // the point in time where the DST offset change occurred. Updated 183 // the interval to reflect this and reset the increment. 184 cache.start = t; 185 cache.end = new_end; 186 cache.increment = cache.initial_increment; 187 } else { 188 // The interval contains a DST offset change and the given time is 189 // before it. Adjust the increment to avoid a linear search for 190 // the offset change point and change the end of the interval. 191 cache.increment /= 3; 192 cache.end = t; 193 } 194 // Update the offset in the cache and return it. 195 cache.offset = offset; 196 return offset; 197 } 198 } 199 } 200 201 // If the cache misses, the local_time_offset may not be initialized. 202 if (IS_UNDEFINED(local_time_offset)) { 203 local_time_offset = %DateLocalTimeOffset(); 204 } 205 // Compute the DST offset for the time and shrink the cache interval 206 // to only contain the time. This allows fast repeated DST offset 207 // computations for the same time. 208 var offset = %DateDaylightSavingsOffset(EquivalentTime(t)); 209 cache.offset = offset; 210 cache.start = cache.end = t; 211 cache.increment = cache.initial_increment; 212 return offset; 213 } 214 215 216 var timezone_cache_time = $NaN; 217 var timezone_cache_timezone; 218 219 function LocalTimezone(t) { 220 if (NUMBER_IS_NAN(t)) return ""; 221 if (t == timezone_cache_time) { 222 return timezone_cache_timezone; 223 } 224 var timezone = %DateLocalTimezone(EquivalentTime(t)); 225 timezone_cache_time = t; 226 timezone_cache_timezone = timezone; 227 return timezone; 228 } 229 230 231 function WeekDay(time) { 232 return Modulo(DAY(time) + 4, 7); 233 } 234 235 236 function LocalTime(time) { 237 if (NUMBER_IS_NAN(time)) return time; 238 // DaylightSavingsOffset called before local_time_offset used. 239 return time + DaylightSavingsOffset(time) + local_time_offset; 240 } 241 242 243 var ltcache = { 244 key: null, 245 val: null 246 }; 247 248 function LocalTimeNoCheck(time) { 249 var ltc = ltcache; 250 if (%_ObjectEquals(time, ltc.key)) return ltc.val; 251 252 // Inline the DST offset cache checks for speed. 253 // The cache is hit, or DaylightSavingsOffset is called, 254 // before local_time_offset is used. 255 var cache = DST_offset_cache; 256 if (cache.start <= time && time <= cache.end) { 257 var dst_offset = cache.offset; 258 } else { 259 var dst_offset = DaylightSavingsOffset(time); 260 } 261 ltc.key = time; 262 return (ltc.val = time + local_time_offset + dst_offset); 263 } 264 265 266 function UTC(time) { 267 if (NUMBER_IS_NAN(time)) return time; 268 // local_time_offset is needed before the call to DaylightSavingsOffset, 269 // so it may be uninitialized. 270 if (IS_UNDEFINED(local_time_offset)) { 271 local_time_offset = %DateLocalTimeOffset(); 272 } 273 var tmp = time - local_time_offset; 274 return tmp - DaylightSavingsOffset(tmp); 275 } 276 277 278 // ECMA 262 - 15.9.1.11 279 function MakeTime(hour, min, sec, ms) { 280 if (!$isFinite(hour)) return $NaN; 281 if (!$isFinite(min)) return $NaN; 282 if (!$isFinite(sec)) return $NaN; 283 if (!$isFinite(ms)) return $NaN; 284 return TO_INTEGER(hour) * msPerHour 285 + TO_INTEGER(min) * msPerMinute 286 + TO_INTEGER(sec) * msPerSecond 287 + TO_INTEGER(ms); 288 } 289 290 291 // ECMA 262 - 15.9.1.12 292 function TimeInYear(year) { 293 return DaysInYear(year) * msPerDay; 294 } 295 296 297 var ymd_from_time_cache = [$NaN, $NaN, $NaN]; 298 var ymd_from_time_cached_time = $NaN; 299 300 function YearFromTime(t) { 301 if (t !== ymd_from_time_cached_time) { 302 if (!$isFinite(t)) { 303 return $NaN; 304 } 305 306 %DateYMDFromTime(t, ymd_from_time_cache); 307 ymd_from_time_cached_time = t 308 } 309 310 return ymd_from_time_cache[0]; 311 } 312 313 function MonthFromTime(t) { 314 if (t !== ymd_from_time_cached_time) { 315 if (!$isFinite(t)) { 316 return $NaN; 317 } 318 %DateYMDFromTime(t, ymd_from_time_cache); 319 ymd_from_time_cached_time = t 320 } 321 322 return ymd_from_time_cache[1]; 323 } 324 325 function DateFromTime(t) { 326 if (t !== ymd_from_time_cached_time) { 327 if (!$isFinite(t)) { 328 return $NaN; 329 } 330 331 %DateYMDFromTime(t, ymd_from_time_cache); 332 ymd_from_time_cached_time = t 333 } 334 335 return ymd_from_time_cache[2]; 336 } 337 338 339 // Compute number of days given a year, month, date. 340 // Note that month and date can lie outside the normal range. 341 // For example: 342 // MakeDay(2007, -4, 20) --> MakeDay(2006, 8, 20) 343 // MakeDay(2007, -33, 1) --> MakeDay(2004, 3, 1) 344 // MakeDay(2007, 14, -50) --> MakeDay(2007, 8, 11) 345 function MakeDay(year, month, date) { 346 if (!$isFinite(year) || !$isFinite(month) || !$isFinite(date)) return $NaN; 347 348 // Convert to integer and map -0 to 0. 349 year = TO_INTEGER_MAP_MINUS_ZERO(year); 350 month = TO_INTEGER_MAP_MINUS_ZERO(month); 351 date = TO_INTEGER_MAP_MINUS_ZERO(date); 352 353 if (year < kMinYear || year > kMaxYear || 354 month < kMinMonth || month > kMaxMonth || 355 date < kMinDate || date > kMaxDate) { 356 return $NaN; 357 } 358 359 // Now we rely on year, month and date being SMIs. 360 return %DateMakeDay(year, month, date); 361 } 362 363 364 // ECMA 262 - 15.9.1.13 365 function MakeDate(day, time) { 366 var time = day * msPerDay + time; 367 // Some of our runtime funtions for computing UTC(time) rely on 368 // times not being significantly larger than MAX_TIME_MS. If there 369 // is no way that the time can be within range even after UTC 370 // conversion we return NaN immediately instead of relying on 371 // TimeClip to do it. 372 if ($abs(time) > MAX_TIME_BEFORE_UTC) return $NaN; 373 return time; 374 } 375 376 377 // ECMA 262 - 15.9.1.14 378 function TimeClip(time) { 379 if (!$isFinite(time)) return $NaN; 380 if ($abs(time) > MAX_TIME_MS) return $NaN; 381 return TO_INTEGER(time); 382 } 383 384 385 // The Date cache is used to limit the cost of parsing the same Date 386 // strings over and over again. 387 var Date_cache = { 388 // Cached time value. 389 time: $NaN, 390 // Cached year when interpreting the time as a local time. Only 391 // valid when the time matches cached time. 392 year: $NaN, 393 // String input for which the cached time is valid. 394 string: null 395 }; 396 397 398 %SetCode($Date, function(year, month, date, hours, minutes, seconds, ms) { 399 if (!%_IsConstructCall()) { 400 // ECMA 262 - 15.9.2 401 return (new $Date()).toString(); 402 } 403 404 // ECMA 262 - 15.9.3 405 var argc = %_ArgumentsLength(); 406 var value; 407 if (argc == 0) { 408 value = %DateCurrentTime(); 409 410 } else if (argc == 1) { 411 if (IS_NUMBER(year)) { 412 value = TimeClip(year); 413 414 } else if (IS_STRING(year)) { 415 // Probe the Date cache. If we already have a time value for the 416 // given time, we re-use that instead of parsing the string again. 417 var cache = Date_cache; 418 if (cache.string === year) { 419 value = cache.time; 420 } else { 421 value = DateParse(year); 422 if (!NUMBER_IS_NAN(value)) { 423 cache.time = value; 424 cache.year = YearFromTime(LocalTimeNoCheck(value)); 425 cache.string = year; 426 } 427 } 428 429 } else { 430 // According to ECMA 262, no hint should be given for this 431 // conversion. However, ToPrimitive defaults to STRING_HINT for 432 // Date objects which will lose precision when the Date 433 // constructor is called with another Date object as its 434 // argument. We therefore use NUMBER_HINT for the conversion, 435 // which is the default for everything else than Date objects. 436 // This makes us behave like KJS and SpiderMonkey. 437 var time = ToPrimitive(year, NUMBER_HINT); 438 value = IS_STRING(time) ? DateParse(time) : TimeClip(ToNumber(time)); 439 } 440 441 } else { 442 year = ToNumber(year); 443 month = ToNumber(month); 444 date = argc > 2 ? ToNumber(date) : 1; 445 hours = argc > 3 ? ToNumber(hours) : 0; 446 minutes = argc > 4 ? ToNumber(minutes) : 0; 447 seconds = argc > 5 ? ToNumber(seconds) : 0; 448 ms = argc > 6 ? ToNumber(ms) : 0; 449 year = (!NUMBER_IS_NAN(year) && 0 <= TO_INTEGER(year) && TO_INTEGER(year) <= 99) 450 ? 1900 + TO_INTEGER(year) : year; 451 var day = MakeDay(year, month, date); 452 var time = MakeTime(hours, minutes, seconds, ms); 453 value = TimeClip(UTC(MakeDate(day, time))); 454 } 455 %_SetValueOf(this, value); 456 }); 457 458 459 %FunctionSetPrototype($Date, new $Date($NaN)); 460 461 462 var WeekDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; 463 var Months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; 464 465 466 function TwoDigitString(value) { 467 return value < 10 ? "0" + value : "" + value; 468 } 469 470 471 function DateString(time) { 472 return WeekDays[WeekDay(time)] + ' ' 473 + Months[MonthFromTime(time)] + ' ' 474 + TwoDigitString(DateFromTime(time)) + ' ' 475 + YearFromTime(time); 476 } 477 478 479 var LongWeekDays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; 480 var LongMonths = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; 481 482 483 function LongDateString(time) { 484 return LongWeekDays[WeekDay(time)] + ', ' 485 + LongMonths[MonthFromTime(time)] + ' ' 486 + TwoDigitString(DateFromTime(time)) + ', ' 487 + YearFromTime(time); 488 } 489 490 491 function TimeString(time) { 492 return TwoDigitString(HOUR_FROM_TIME(time)) + ':' 493 + TwoDigitString(MIN_FROM_TIME(time)) + ':' 494 + TwoDigitString(SEC_FROM_TIME(time)); 495 } 496 497 498 function LocalTimezoneString(time) { 499 var old_timezone = timezone_cache_timezone; 500 var timezone = LocalTimezone(time); 501 if (old_timezone && timezone != old_timezone) { 502 // If the timezone string has changed from the one that we cached, 503 // the local time offset may now be wrong. So we need to update it 504 // and try again. 505 local_time_offset = %DateLocalTimeOffset(); 506 // We also need to invalidate the DST cache as the new timezone may have 507 // different DST times. 508 var dst_cache = DST_offset_cache; 509 dst_cache.start = 0; 510 dst_cache.end = -1; 511 } 512 513 var timezoneOffset = 514 (DaylightSavingsOffset(time) + local_time_offset) / msPerMinute; 515 var sign = (timezoneOffset >= 0) ? 1 : -1; 516 var hours = FLOOR((sign * timezoneOffset)/60); 517 var min = FLOOR((sign * timezoneOffset)%60); 518 var gmt = ' GMT' + ((sign == 1) ? '+' : '-') + 519 TwoDigitString(hours) + TwoDigitString(min); 520 return gmt + ' (' + timezone + ')'; 521 } 522 523 524 function DatePrintString(time) { 525 return DateString(time) + ' ' + TimeString(time); 526 } 527 528 // ------------------------------------------------------------------- 529 530 // Reused output buffer. Used when parsing date strings. 531 var parse_buffer = $Array(8); 532 533 // ECMA 262 - 15.9.4.2 534 function DateParse(string) { 535 var arr = %DateParseString(ToString(string), parse_buffer); 536 if (IS_NULL(arr)) return $NaN; 537 538 var day = MakeDay(arr[0], arr[1], arr[2]); 539 var time = MakeTime(arr[3], arr[4], arr[5], arr[6]); 540 var date = MakeDate(day, time); 541 542 if (IS_NULL(arr[7])) { 543 return TimeClip(UTC(date)); 544 } else { 545 return TimeClip(date - arr[7] * 1000); 546 } 547 } 548 549 550 // ECMA 262 - 15.9.4.3 551 function DateUTC(year, month, date, hours, minutes, seconds, ms) { 552 year = ToNumber(year); 553 month = ToNumber(month); 554 var argc = %_ArgumentsLength(); 555 date = argc > 2 ? ToNumber(date) : 1; 556 hours = argc > 3 ? ToNumber(hours) : 0; 557 minutes = argc > 4 ? ToNumber(minutes) : 0; 558 seconds = argc > 5 ? ToNumber(seconds) : 0; 559 ms = argc > 6 ? ToNumber(ms) : 0; 560 year = (!NUMBER_IS_NAN(year) && 0 <= TO_INTEGER(year) && TO_INTEGER(year) <= 99) 561 ? 1900 + TO_INTEGER(year) : year; 562 var day = MakeDay(year, month, date); 563 var time = MakeTime(hours, minutes, seconds, ms); 564 return %_SetValueOf(this, TimeClip(MakeDate(day, time))); 565 } 566 567 568 // Mozilla-specific extension. Returns the number of milliseconds 569 // elapsed since 1 January 1970 00:00:00 UTC. 570 function DateNow() { 571 return %DateCurrentTime(); 572 } 573 574 575 // ECMA 262 - 15.9.5.2 576 function DateToString() { 577 var t = DATE_VALUE(this); 578 if (NUMBER_IS_NAN(t)) return kInvalidDate; 579 var time_zone_string = LocalTimezoneString(t); // May update local offset. 580 return DatePrintString(LocalTimeNoCheck(t)) + time_zone_string; 581 } 582 583 584 // ECMA 262 - 15.9.5.3 585 function DateToDateString() { 586 var t = DATE_VALUE(this); 587 if (NUMBER_IS_NAN(t)) return kInvalidDate; 588 return DateString(LocalTimeNoCheck(t)); 589 } 590 591 592 // ECMA 262 - 15.9.5.4 593 function DateToTimeString() { 594 var t = DATE_VALUE(this); 595 if (NUMBER_IS_NAN(t)) return kInvalidDate; 596 var time_zone_string = LocalTimezoneString(t); // May update local offset. 597 return TimeString(LocalTimeNoCheck(t)) + time_zone_string; 598 } 599 600 601 // ECMA 262 - 15.9.5.5 602 function DateToLocaleString() { 603 return %_CallFunction(this, DateToString); 604 } 605 606 607 // ECMA 262 - 15.9.5.6 608 function DateToLocaleDateString() { 609 var t = DATE_VALUE(this); 610 if (NUMBER_IS_NAN(t)) return kInvalidDate; 611 return LongDateString(LocalTimeNoCheck(t)); 612 } 613 614 615 // ECMA 262 - 15.9.5.7 616 function DateToLocaleTimeString() { 617 var t = DATE_VALUE(this); 618 if (NUMBER_IS_NAN(t)) return kInvalidDate; 619 var lt = LocalTimeNoCheck(t); 620 return TimeString(lt); 621 } 622 623 624 // ECMA 262 - 15.9.5.8 625 function DateValueOf() { 626 return DATE_VALUE(this); 627 } 628 629 630 // ECMA 262 - 15.9.5.9 631 function DateGetTime() { 632 return DATE_VALUE(this); 633 } 634 635 636 // ECMA 262 - 15.9.5.10 637 function DateGetFullYear() { 638 var t = DATE_VALUE(this); 639 if (NUMBER_IS_NAN(t)) return t; 640 var cache = Date_cache; 641 if (cache.time === t) return cache.year; 642 return YearFromTime(LocalTimeNoCheck(t)); 643 } 644 645 646 // ECMA 262 - 15.9.5.11 647 function DateGetUTCFullYear() { 648 var t = DATE_VALUE(this); 649 if (NUMBER_IS_NAN(t)) return t; 650 return YearFromTime(t); 651 } 652 653 654 // ECMA 262 - 15.9.5.12 655 function DateGetMonth() { 656 var t = DATE_VALUE(this); 657 if (NUMBER_IS_NAN(t)) return t; 658 return MonthFromTime(LocalTimeNoCheck(t)); 659 } 660 661 662 // ECMA 262 - 15.9.5.13 663 function DateGetUTCMonth() { 664 var t = DATE_VALUE(this); 665 if (NUMBER_IS_NAN(t)) return t; 666 return MonthFromTime(t); 667 } 668 669 670 // ECMA 262 - 15.9.5.14 671 function DateGetDate() { 672 var t = DATE_VALUE(this); 673 if (NUMBER_IS_NAN(t)) return t; 674 return DateFromTime(LocalTimeNoCheck(t)); 675 } 676 677 678 // ECMA 262 - 15.9.5.15 679 function DateGetUTCDate() { 680 var t = DATE_VALUE(this); 681 return NAN_OR_DATE_FROM_TIME(t); 682 } 683 684 685 // ECMA 262 - 15.9.5.16 686 function DateGetDay() { 687 var t = %_ValueOf(this); 688 if (NUMBER_IS_NAN(t)) return t; 689 return WeekDay(LocalTimeNoCheck(t)); 690 } 691 692 693 // ECMA 262 - 15.9.5.17 694 function DateGetUTCDay() { 695 var t = %_ValueOf(this); 696 if (NUMBER_IS_NAN(t)) return t; 697 return WeekDay(t); 698 } 699 700 701 // ECMA 262 - 15.9.5.18 702 function DateGetHours() { 703 var t = DATE_VALUE(this); 704 if (NUMBER_IS_NAN(t)) return t; 705 return HOUR_FROM_TIME(LocalTimeNoCheck(t)); 706 } 707 708 709 // ECMA 262 - 15.9.5.19 710 function DateGetUTCHours() { 711 var t = DATE_VALUE(this); 712 if (NUMBER_IS_NAN(t)) return t; 713 return HOUR_FROM_TIME(t); 714 } 715 716 717 // ECMA 262 - 15.9.5.20 718 function DateGetMinutes() { 719 var t = DATE_VALUE(this); 720 if (NUMBER_IS_NAN(t)) return t; 721 return MIN_FROM_TIME(LocalTimeNoCheck(t)); 722 } 723 724 725 // ECMA 262 - 15.9.5.21 726 function DateGetUTCMinutes() { 727 var t = DATE_VALUE(this); 728 return NAN_OR_MIN_FROM_TIME(t); 729 } 730 731 732 // ECMA 262 - 15.9.5.22 733 function DateGetSeconds() { 734 var t = DATE_VALUE(this); 735 if (NUMBER_IS_NAN(t)) return t; 736 return SEC_FROM_TIME(LocalTimeNoCheck(t)); 737 } 738 739 740 // ECMA 262 - 15.9.5.23 741 function DateGetUTCSeconds() { 742 var t = DATE_VALUE(this); 743 return NAN_OR_SEC_FROM_TIME(t); 744 } 745 746 747 // ECMA 262 - 15.9.5.24 748 function DateGetMilliseconds() { 749 var t = DATE_VALUE(this); 750 if (NUMBER_IS_NAN(t)) return t; 751 return MS_FROM_TIME(LocalTimeNoCheck(t)); 752 } 753 754 755 // ECMA 262 - 15.9.5.25 756 function DateGetUTCMilliseconds() { 757 var t = DATE_VALUE(this); 758 return NAN_OR_MS_FROM_TIME(t); 759 } 760 761 762 // ECMA 262 - 15.9.5.26 763 function DateGetTimezoneOffset() { 764 var t = DATE_VALUE(this); 765 if (NUMBER_IS_NAN(t)) return t; 766 return (t - LocalTimeNoCheck(t)) / msPerMinute; 767 } 768 769 770 // ECMA 262 - 15.9.5.27 771 function DateSetTime(ms) { 772 if (!IS_DATE(this)) ThrowDateTypeError(); 773 return %_SetValueOf(this, TimeClip(ToNumber(ms))); 774 } 775 776 777 // ECMA 262 - 15.9.5.28 778 function DateSetMilliseconds(ms) { 779 var t = LocalTime(DATE_VALUE(this)); 780 ms = ToNumber(ms); 781 var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), SEC_FROM_TIME(t), ms); 782 return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time)))); 783 } 784 785 786 // ECMA 262 - 15.9.5.29 787 function DateSetUTCMilliseconds(ms) { 788 var t = DATE_VALUE(this); 789 ms = ToNumber(ms); 790 var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), SEC_FROM_TIME(t), ms); 791 return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time))); 792 } 793 794 795 // ECMA 262 - 15.9.5.30 796 function DateSetSeconds(sec, ms) { 797 var t = LocalTime(DATE_VALUE(this)); 798 sec = ToNumber(sec); 799 ms = %_ArgumentsLength() < 2 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms); 800 var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), sec, ms); 801 return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time)))); 802 } 803 804 805 // ECMA 262 - 15.9.5.31 806 function DateSetUTCSeconds(sec, ms) { 807 var t = DATE_VALUE(this); 808 sec = ToNumber(sec); 809 ms = %_ArgumentsLength() < 2 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms); 810 var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), sec, ms); 811 return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time))); 812 } 813 814 815 // ECMA 262 - 15.9.5.33 816 function DateSetMinutes(min, sec, ms) { 817 var t = LocalTime(DATE_VALUE(this)); 818 min = ToNumber(min); 819 var argc = %_ArgumentsLength(); 820 sec = argc < 2 ? NAN_OR_SEC_FROM_TIME(t) : ToNumber(sec); 821 ms = argc < 3 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms); 822 var time = MakeTime(HOUR_FROM_TIME(t), min, sec, ms); 823 return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time)))); 824 } 825 826 827 // ECMA 262 - 15.9.5.34 828 function DateSetUTCMinutes(min, sec, ms) { 829 var t = DATE_VALUE(this); 830 min = ToNumber(min); 831 var argc = %_ArgumentsLength(); 832 sec = argc < 2 ? NAN_OR_SEC_FROM_TIME(t) : ToNumber(sec); 833 ms = argc < 3 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms); 834 var time = MakeTime(HOUR_FROM_TIME(t), min, sec, ms); 835 return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time))); 836 } 837 838 839 // ECMA 262 - 15.9.5.35 840 function DateSetHours(hour, min, sec, ms) { 841 var t = LocalTime(DATE_VALUE(this)); 842 hour = ToNumber(hour); 843 var argc = %_ArgumentsLength(); 844 min = argc < 2 ? NAN_OR_MIN_FROM_TIME(t) : ToNumber(min); 845 sec = argc < 3 ? NAN_OR_SEC_FROM_TIME(t) : ToNumber(sec); 846 ms = argc < 4 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms); 847 var time = MakeTime(hour, min, sec, ms); 848 return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time)))); 849 } 850 851 852 // ECMA 262 - 15.9.5.34 853 function DateSetUTCHours(hour, min, sec, ms) { 854 var t = DATE_VALUE(this); 855 hour = ToNumber(hour); 856 var argc = %_ArgumentsLength(); 857 min = argc < 2 ? NAN_OR_MIN_FROM_TIME(t) : ToNumber(min); 858 sec = argc < 3 ? NAN_OR_SEC_FROM_TIME(t) : ToNumber(sec); 859 ms = argc < 4 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms); 860 var time = MakeTime(hour, min, sec, ms); 861 return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time))); 862 } 863 864 865 // ECMA 262 - 15.9.5.36 866 function DateSetDate(date) { 867 var t = LocalTime(DATE_VALUE(this)); 868 date = ToNumber(date); 869 var day = MakeDay(YearFromTime(t), MonthFromTime(t), date); 870 return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t))))); 871 } 872 873 874 // ECMA 262 - 15.9.5.37 875 function DateSetUTCDate(date) { 876 var t = DATE_VALUE(this); 877 date = ToNumber(date); 878 var day = MakeDay(YearFromTime(t), MonthFromTime(t), date); 879 return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t)))); 880 } 881 882 883 // ECMA 262 - 15.9.5.38 884 function DateSetMonth(month, date) { 885 var t = LocalTime(DATE_VALUE(this)); 886 month = ToNumber(month); 887 date = %_ArgumentsLength() < 2 ? NAN_OR_DATE_FROM_TIME(t) : ToNumber(date); 888 var day = MakeDay(YearFromTime(t), month, date); 889 return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t))))); 890 } 891 892 893 // ECMA 262 - 15.9.5.39 894 function DateSetUTCMonth(month, date) { 895 var t = DATE_VALUE(this); 896 month = ToNumber(month); 897 date = %_ArgumentsLength() < 2 ? NAN_OR_DATE_FROM_TIME(t) : ToNumber(date); 898 var day = MakeDay(YearFromTime(t), month, date); 899 return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t)))); 900 } 901 902 903 // ECMA 262 - 15.9.5.40 904 function DateSetFullYear(year, month, date) { 905 var t = DATE_VALUE(this); 906 t = NUMBER_IS_NAN(t) ? 0 : LocalTimeNoCheck(t); 907 year = ToNumber(year); 908 var argc = %_ArgumentsLength(); 909 month = argc < 2 ? MonthFromTime(t) : ToNumber(month); 910 date = argc < 3 ? DateFromTime(t) : ToNumber(date); 911 var day = MakeDay(year, month, date); 912 return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t))))); 913 } 914 915 916 // ECMA 262 - 15.9.5.41 917 function DateSetUTCFullYear(year, month, date) { 918 var t = DATE_VALUE(this); 919 if (NUMBER_IS_NAN(t)) t = 0; 920 var argc = %_ArgumentsLength(); 921 year = ToNumber(year); 922 month = argc < 2 ? MonthFromTime(t) : ToNumber(month); 923 date = argc < 3 ? DateFromTime(t) : ToNumber(date); 924 var day = MakeDay(year, month, date); 925 return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t)))); 926 } 927 928 929 // ECMA 262 - 15.9.5.42 930 function DateToUTCString() { 931 var t = DATE_VALUE(this); 932 if (NUMBER_IS_NAN(t)) return kInvalidDate; 933 // Return UTC string of the form: Sat, 31 Jan 1970 23:00:00 GMT 934 return WeekDays[WeekDay(t)] + ', ' 935 + TwoDigitString(DateFromTime(t)) + ' ' 936 + Months[MonthFromTime(t)] + ' ' 937 + YearFromTime(t) + ' ' 938 + TimeString(t) + ' GMT'; 939 } 940 941 942 // ECMA 262 - B.2.4 943 function DateGetYear() { 944 var t = DATE_VALUE(this); 945 if (NUMBER_IS_NAN(t)) return $NaN; 946 return YearFromTime(LocalTimeNoCheck(t)) - 1900; 947 } 948 949 950 // ECMA 262 - B.2.5 951 function DateSetYear(year) { 952 var t = LocalTime(DATE_VALUE(this)); 953 if (NUMBER_IS_NAN(t)) t = 0; 954 year = ToNumber(year); 955 if (NUMBER_IS_NAN(year)) return %_SetValueOf(this, $NaN); 956 year = (0 <= TO_INTEGER(year) && TO_INTEGER(year) <= 99) 957 ? 1900 + TO_INTEGER(year) : year; 958 var day = MakeDay(year, MonthFromTime(t), DateFromTime(t)); 959 return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t))))); 960 } 961 962 963 // ECMA 262 - B.2.6 964 // 965 // Notice that this does not follow ECMA 262 completely. ECMA 262 966 // says that toGMTString should be the same Function object as 967 // toUTCString. JSC does not do this, so for compatibility we do not 968 // do that either. Instead, we create a new function whose name 969 // property will return toGMTString. 970 function DateToGMTString() { 971 return %_CallFunction(this, DateToUTCString); 972 } 973 974 975 function PadInt(n, digits) { 976 if (digits == 1) return n; 977 return n < MathPow(10, digits - 1) ? '0' + PadInt(n, digits - 1) : n; 978 } 979 980 981 function DateToISOString() { 982 var t = DATE_VALUE(this); 983 if (NUMBER_IS_NAN(t)) return kInvalidDate; 984 return this.getUTCFullYear() + 985 '-' + PadInt(this.getUTCMonth() + 1, 2) + 986 '-' + PadInt(this.getUTCDate(), 2) + 987 'T' + PadInt(this.getUTCHours(), 2) + 988 ':' + PadInt(this.getUTCMinutes(), 2) + 989 ':' + PadInt(this.getUTCSeconds(), 2) + 990 '.' + PadInt(this.getUTCMilliseconds(), 3) + 991 'Z'; 992 } 993 994 995 function DateToJSON(key) { 996 var o = ToObject(this); 997 var tv = DefaultNumber(o); 998 if (IS_NUMBER(tv) && !NUMBER_IS_FINITE(tv)) { 999 return null; 1000 } 1001 return o.toISOString(); 1002 } 1003 1004 1005 function ResetDateCache() { 1006 1007 // Reset the local_time_offset: 1008 local_time_offset = %DateLocalTimeOffset(); 1009 1010 // Reset the DST offset cache: 1011 var cache = DST_offset_cache; 1012 cache.offset = 0; 1013 cache.start = 0; 1014 cache.end = -1; 1015 cache.increment = 0; 1016 cache.initial_increment = 19 * msPerDay; 1017 1018 // Reset the timezone cache: 1019 timezone_cache_time = $NaN; 1020 timezone_cache_timezone = undefined; 1021 1022 // Reset the ltcache: 1023 ltcache.key = null; 1024 ltcache.val = null; 1025 1026 // Reset the ymd_from_time_cache: 1027 ymd_from_time_cache = [$NaN, $NaN, $NaN]; 1028 ymd_from_time_cached_time = $NaN; 1029 1030 // Reset the date cache: 1031 cache = Date_cache; 1032 cache.time = $NaN; 1033 cache.year = $NaN; 1034 cache.string = null; 1035 } 1036 1037 1038 // ------------------------------------------------------------------- 1039 1040 function SetupDate() { 1041 // Setup non-enumerable properties of the Date object itself. 1042 InstallFunctions($Date, DONT_ENUM, $Array( 1043 "UTC", DateUTC, 1044 "parse", DateParse, 1045 "now", DateNow 1046 )); 1047 1048 // Setup non-enumerable constructor property of the Date prototype object. 1049 %SetProperty($Date.prototype, "constructor", $Date, DONT_ENUM); 1050 1051 // Setup non-enumerable functions of the Date prototype object and 1052 // set their names. 1053 InstallFunctionsOnHiddenPrototype($Date.prototype, DONT_ENUM, $Array( 1054 "toString", DateToString, 1055 "toDateString", DateToDateString, 1056 "toTimeString", DateToTimeString, 1057 "toLocaleString", DateToLocaleString, 1058 "toLocaleDateString", DateToLocaleDateString, 1059 "toLocaleTimeString", DateToLocaleTimeString, 1060 "valueOf", DateValueOf, 1061 "getTime", DateGetTime, 1062 "getFullYear", DateGetFullYear, 1063 "getUTCFullYear", DateGetUTCFullYear, 1064 "getMonth", DateGetMonth, 1065 "getUTCMonth", DateGetUTCMonth, 1066 "getDate", DateGetDate, 1067 "getUTCDate", DateGetUTCDate, 1068 "getDay", DateGetDay, 1069 "getUTCDay", DateGetUTCDay, 1070 "getHours", DateGetHours, 1071 "getUTCHours", DateGetUTCHours, 1072 "getMinutes", DateGetMinutes, 1073 "getUTCMinutes", DateGetUTCMinutes, 1074 "getSeconds", DateGetSeconds, 1075 "getUTCSeconds", DateGetUTCSeconds, 1076 "getMilliseconds", DateGetMilliseconds, 1077 "getUTCMilliseconds", DateGetUTCMilliseconds, 1078 "getTimezoneOffset", DateGetTimezoneOffset, 1079 "setTime", DateSetTime, 1080 "setMilliseconds", DateSetMilliseconds, 1081 "setUTCMilliseconds", DateSetUTCMilliseconds, 1082 "setSeconds", DateSetSeconds, 1083 "setUTCSeconds", DateSetUTCSeconds, 1084 "setMinutes", DateSetMinutes, 1085 "setUTCMinutes", DateSetUTCMinutes, 1086 "setHours", DateSetHours, 1087 "setUTCHours", DateSetUTCHours, 1088 "setDate", DateSetDate, 1089 "setUTCDate", DateSetUTCDate, 1090 "setMonth", DateSetMonth, 1091 "setUTCMonth", DateSetUTCMonth, 1092 "setFullYear", DateSetFullYear, 1093 "setUTCFullYear", DateSetUTCFullYear, 1094 "toGMTString", DateToGMTString, 1095 "toUTCString", DateToUTCString, 1096 "getYear", DateGetYear, 1097 "setYear", DateSetYear, 1098 "toISOString", DateToISOString, 1099 "toJSON", DateToJSON 1100 )); 1101 } 1102 1103 SetupDate(); 1104