1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 2 * vim: set ts=8 sts=4 et sw=4 tw=99: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /* 8 * JS date methods. 9 * 10 * "For example, OS/360 devotes 26 bytes of the permanently 11 * resident date-turnover routine to the proper handling of 12 * December 31 on leap years (when it is Day 366). That 13 * might have been left to the operator." 14 * 15 * Frederick Brooks, 'The Second-System Effect'. 16 */ 17 18 #include "jsdate.h" 19 20 #include "mozilla/ArrayUtils.h" 21 #include "mozilla/FloatingPoint.h" 22 #include "mozilla/Sprintf.h" 23 24 #include <ctype.h> 25 #include <math.h> 26 #include <string.h> 27 28 #include "jsapi.h" 29 #include "jscntxt.h" 30 #include "jsnum.h" 31 #include "jsobj.h" 32 #include "jsprf.h" 33 #include "jsstr.h" 34 #include "jstypes.h" 35 #include "jsutil.h" 36 #include "jswrapper.h" 37 38 #include "js/Conversions.h" 39 #include "js/Date.h" 40 #include "vm/DateTime.h" 41 #include "vm/GlobalObject.h" 42 #include "vm/Interpreter.h" 43 #include "vm/String.h" 44 #include "vm/StringBuffer.h" 45 #include "vm/Time.h" 46 47 #include "jsobjinlines.h" 48 49 using namespace js; 50 51 using mozilla::ArrayLength; 52 using mozilla::IsFinite; 53 using mozilla::IsNaN; 54 using mozilla::NumbersAreIdentical; 55 56 using JS::AutoCheckCannotGC; 57 using JS::ClippedTime; 58 using JS::GenericNaN; 59 using JS::TimeClip; 60 using JS::ToInteger; 61 62 /* 63 * The JS 'Date' object is patterned after the Java 'Date' object. 64 * Here is a script: 65 * 66 * today = new Date(); 67 * 68 * print(today.toLocaleString()); 69 * 70 * weekDay = today.getDay(); 71 * 72 * 73 * These Java (and ECMA-262) methods are supported: 74 * 75 * UTC 76 * getDate (getUTCDate) 77 * getDay (getUTCDay) 78 * getHours (getUTCHours) 79 * getMinutes (getUTCMinutes) 80 * getMonth (getUTCMonth) 81 * getSeconds (getUTCSeconds) 82 * getMilliseconds (getUTCMilliseconds) 83 * getTime 84 * getTimezoneOffset 85 * getYear 86 * getFullYear (getUTCFullYear) 87 * parse 88 * setDate (setUTCDate) 89 * setHours (setUTCHours) 90 * setMinutes (setUTCMinutes) 91 * setMonth (setUTCMonth) 92 * setSeconds (setUTCSeconds) 93 * setMilliseconds (setUTCMilliseconds) 94 * setTime 95 * setYear (setFullYear, setUTCFullYear) 96 * toGMTString (toUTCString) 97 * toLocaleString 98 * toString 99 * 100 * 101 * These Java methods are not supported 102 * 103 * setDay 104 * before 105 * after 106 * equals 107 * hashCode 108 */ 109 110 static inline double 111 Day(double t) 112 { 113 return floor(t / msPerDay); 114 } 115 116 static double 117 TimeWithinDay(double t) 118 { 119 double result = fmod(t, msPerDay); 120 if (result < 0) 121 result += msPerDay; 122 return result; 123 } 124 125 /* ES5 15.9.1.3. */ 126 static inline bool 127 IsLeapYear(double year) 128 { 129 MOZ_ASSERT(ToInteger(year) == year); 130 return fmod(year, 4) == 0 && (fmod(year, 100) != 0 || fmod(year, 400) == 0); 131 } 132 133 static inline double 134 DaysInYear(double year) 135 { 136 if (!IsFinite(year)) 137 return GenericNaN(); 138 return IsLeapYear(year) ? 366 : 365; 139 } 140 141 static inline double 142 DayFromYear(double y) 143 { 144 return 365 * (y - 1970) + 145 floor((y - 1969) / 4.0) - 146 floor((y - 1901) / 100.0) + 147 floor((y - 1601) / 400.0); 148 } 149 150 static inline double 151 TimeFromYear(double y) 152 { 153 return DayFromYear(y) * msPerDay; 154 } 155 156 static double 157 YearFromTime(double t) 158 { 159 if (!IsFinite(t)) 160 return GenericNaN(); 161 162 MOZ_ASSERT(ToInteger(t) == t); 163 164 double y = floor(t / (msPerDay * 365.2425)) + 1970; 165 double t2 = TimeFromYear(y); 166 167 /* 168 * Adjust the year if the approximation was wrong. Since the year was 169 * computed using the average number of ms per year, it will usually 170 * be wrong for dates within several hours of a year transition. 171 */ 172 if (t2 > t) { 173 y--; 174 } else { 175 if (t2 + msPerDay * DaysInYear(y) <= t) 176 y++; 177 } 178 return y; 179 } 180 181 static inline int 182 DaysInFebruary(double year) 183 { 184 return IsLeapYear(year) ? 29 : 28; 185 } 186 187 /* ES5 15.9.1.4. */ 188 static inline double 189 DayWithinYear(double t, double year) 190 { 191 MOZ_ASSERT_IF(IsFinite(t), YearFromTime(t) == year); 192 return Day(t) - DayFromYear(year); 193 } 194 195 static double 196 MonthFromTime(double t) 197 { 198 if (!IsFinite(t)) 199 return GenericNaN(); 200 201 double year = YearFromTime(t); 202 double d = DayWithinYear(t, year); 203 204 int step; 205 if (d < (step = 31)) 206 return 0; 207 if (d < (step += DaysInFebruary(year))) 208 return 1; 209 if (d < (step += 31)) 210 return 2; 211 if (d < (step += 30)) 212 return 3; 213 if (d < (step += 31)) 214 return 4; 215 if (d < (step += 30)) 216 return 5; 217 if (d < (step += 31)) 218 return 6; 219 if (d < (step += 31)) 220 return 7; 221 if (d < (step += 30)) 222 return 8; 223 if (d < (step += 31)) 224 return 9; 225 if (d < (step += 30)) 226 return 10; 227 return 11; 228 } 229 230 /* ES5 15.9.1.5. */ 231 static double 232 DateFromTime(double t) 233 { 234 if (!IsFinite(t)) 235 return GenericNaN(); 236 237 double year = YearFromTime(t); 238 double d = DayWithinYear(t, year); 239 240 int next; 241 if (d <= (next = 30)) 242 return d + 1; 243 int step = next; 244 if (d <= (next += DaysInFebruary(year))) 245 return d - step; 246 step = next; 247 if (d <= (next += 31)) 248 return d - step; 249 step = next; 250 if (d <= (next += 30)) 251 return d - step; 252 step = next; 253 if (d <= (next += 31)) 254 return d - step; 255 step = next; 256 if (d <= (next += 30)) 257 return d - step; 258 step = next; 259 if (d <= (next += 31)) 260 return d - step; 261 step = next; 262 if (d <= (next += 31)) 263 return d - step; 264 step = next; 265 if (d <= (next += 30)) 266 return d - step; 267 step = next; 268 if (d <= (next += 31)) 269 return d - step; 270 step = next; 271 if (d <= (next += 30)) 272 return d - step; 273 step = next; 274 return d - step; 275 } 276 277 /* ES5 15.9.1.6. */ 278 static int 279 WeekDay(double t) 280 { 281 /* 282 * We can't assert TimeClip(t) == t because we call this function with 283 * local times, which can be offset outside TimeClip's permitted range. 284 */ 285 MOZ_ASSERT(ToInteger(t) == t); 286 int result = (int(Day(t)) + 4) % 7; 287 if (result < 0) 288 result += 7; 289 return result; 290 } 291 292 static inline int 293 DayFromMonth(int month, bool isLeapYear) 294 { 295 /* 296 * The following array contains the day of year for the first day of 297 * each month, where index 0 is January, and day 0 is January 1. 298 */ 299 static const int firstDayOfMonth[2][13] = { 300 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, 301 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366} 302 }; 303 304 MOZ_ASSERT(0 <= month && month <= 12); 305 return firstDayOfMonth[isLeapYear][month]; 306 } 307 308 template<typename T> 309 static inline int 310 DayFromMonth(T month, bool isLeapYear) = delete; 311 312 /* ES5 15.9.1.12 (out of order to accommodate DaylightSavingTA). */ 313 static double 314 MakeDay(double year, double month, double date) 315 { 316 /* Step 1. */ 317 if (!IsFinite(year) || !IsFinite(month) || !IsFinite(date)) 318 return GenericNaN(); 319 320 /* Steps 2-4. */ 321 double y = ToInteger(year); 322 double m = ToInteger(month); 323 double dt = ToInteger(date); 324 325 /* Step 5. */ 326 double ym = y + floor(m / 12); 327 328 /* Step 6. */ 329 int mn = int(fmod(m, 12.0)); 330 if (mn < 0) 331 mn += 12; 332 333 /* Steps 7-8. */ 334 bool leap = IsLeapYear(ym); 335 336 double yearday = floor(TimeFromYear(ym) / msPerDay); 337 double monthday = DayFromMonth(mn, leap); 338 339 return yearday + monthday + dt - 1; 340 } 341 342 /* ES5 15.9.1.13 (out of order to accommodate DaylightSavingTA). */ 343 static inline double 344 MakeDate(double day, double time) 345 { 346 /* Step 1. */ 347 if (!IsFinite(day) || !IsFinite(time)) 348 return GenericNaN(); 349 350 /* Step 2. */ 351 return day * msPerDay + time; 352 } 353 354 JS_PUBLIC_API(double) 355 JS::MakeDate(double year, unsigned month, unsigned day) 356 { 357 return ::MakeDate(MakeDay(year, month, day), 0); 358 } 359 360 JS_PUBLIC_API(double) 361 JS::YearFromTime(double time) 362 { 363 return ::YearFromTime(time); 364 } 365 366 JS_PUBLIC_API(double) 367 JS::MonthFromTime(double time) 368 { 369 return ::MonthFromTime(time); 370 } 371 372 JS_PUBLIC_API(double) 373 JS::DayFromTime(double time) 374 { 375 return DateFromTime(time); 376 } 377 378 JS_PUBLIC_API(double) 379 JS::DayFromYear(double year) 380 { 381 return ::DayFromYear(year); 382 } 383 384 JS_PUBLIC_API(double) 385 JS::DayWithinYear(double time, double year) 386 { 387 return ::DayWithinYear(time, year); 388 } 389 390 /* 391 * Find a year for which any given date will fall on the same weekday. 392 * 393 * This function should be used with caution when used other than 394 * for determining DST; it hasn't been proven not to produce an 395 * incorrect year for times near year boundaries. 396 */ 397 static int 398 EquivalentYearForDST(int year) 399 { 400 /* 401 * Years and leap years on which Jan 1 is a Sunday, Monday, etc. 402 * 403 * yearStartingWith[0][i] is an example non-leap year where 404 * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc. 405 * 406 * yearStartingWith[1][i] is an example leap year where 407 * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc. 408 */ 409 static const int yearStartingWith[2][7] = { 410 {1978, 1973, 1974, 1975, 1981, 1971, 1977}, 411 {1984, 1996, 1980, 1992, 1976, 1988, 1972} 412 }; 413 414 int day = int(DayFromYear(year) + 4) % 7; 415 if (day < 0) 416 day += 7; 417 418 return yearStartingWith[IsLeapYear(year)][day]; 419 } 420 421 /* ES5 15.9.1.8. */ 422 static double 423 DaylightSavingTA(double t) 424 { 425 if (!IsFinite(t)) 426 return GenericNaN(); 427 428 /* 429 * If earlier than 1970 or after 2038, potentially beyond the ken of 430 * many OSes, map it to an equivalent year before asking. 431 */ 432 if (t < 0.0 || t > 2145916800000.0) { 433 int year = EquivalentYearForDST(int(YearFromTime(t))); 434 double day = MakeDay(year, MonthFromTime(t), DateFromTime(t)); 435 t = MakeDate(day, TimeWithinDay(t)); 436 } 437 438 int64_t utcMilliseconds = static_cast<int64_t>(t); 439 int64_t offsetMilliseconds = DateTimeInfo::getDSTOffsetMilliseconds(utcMilliseconds); 440 return static_cast<double>(offsetMilliseconds); 441 } 442 443 static double 444 AdjustTime(double date) 445 { 446 double localTZA = DateTimeInfo::localTZA(); 447 double t = DaylightSavingTA(date) + localTZA; 448 t = (localTZA >= 0) ? fmod(t, msPerDay) : -fmod(msPerDay - t, msPerDay); 449 return t; 450 } 451 452 /* ES5 15.9.1.9. */ 453 static double 454 LocalTime(double t) 455 { 456 return t + AdjustTime(t); 457 } 458 459 static double 460 UTC(double t) 461 { 462 return t - AdjustTime(t - DateTimeInfo::localTZA()); 463 } 464 465 /* ES5 15.9.1.10. */ 466 static double 467 HourFromTime(double t) 468 { 469 double result = fmod(floor(t/msPerHour), HoursPerDay); 470 if (result < 0) 471 result += HoursPerDay; 472 return result; 473 } 474 475 static double 476 MinFromTime(double t) 477 { 478 double result = fmod(floor(t / msPerMinute), MinutesPerHour); 479 if (result < 0) 480 result += MinutesPerHour; 481 return result; 482 } 483 484 static double 485 SecFromTime(double t) 486 { 487 double result = fmod(floor(t / msPerSecond), SecondsPerMinute); 488 if (result < 0) 489 result += SecondsPerMinute; 490 return result; 491 } 492 493 static double 494 msFromTime(double t) 495 { 496 double result = fmod(t, msPerSecond); 497 if (result < 0) 498 result += msPerSecond; 499 return result; 500 } 501 502 /* ES5 15.9.1.11. */ 503 static double 504 MakeTime(double hour, double min, double sec, double ms) 505 { 506 /* Step 1. */ 507 if (!IsFinite(hour) || 508 !IsFinite(min) || 509 !IsFinite(sec) || 510 !IsFinite(ms)) 511 { 512 return GenericNaN(); 513 } 514 515 /* Step 2. */ 516 double h = ToInteger(hour); 517 518 /* Step 3. */ 519 double m = ToInteger(min); 520 521 /* Step 4. */ 522 double s = ToInteger(sec); 523 524 /* Step 5. */ 525 double milli = ToInteger(ms); 526 527 /* Steps 6-7. */ 528 return h * msPerHour + m * msPerMinute + s * msPerSecond + milli; 529 } 530 531 /** 532 * end of ECMA 'support' functions 533 */ 534 535 /* for use by date_parse */ 536 537 static const char* const wtb[] = { 538 "am", "pm", 539 "monday", "tuesday", "wednesday", "thursday", "friday", 540 "saturday", "sunday", 541 "january", "february", "march", "april", "may", "june", 542 "july", "august", "september", "october", "november", "december", 543 "gmt", "ut", "utc", 544 "est", "edt", 545 "cst", "cdt", 546 "mst", "mdt", 547 "pst", "pdt" 548 /* time zone table needs to be expanded */ 549 }; 550 551 static const int ttb[] = { 552 -1, -2, 0, 0, 0, 0, 0, 0, 0, /* AM/PM */ 553 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 554 10000 + 0, 10000 + 0, 10000 + 0, /* GMT/UT/UTC */ 555 10000 + 5 * 60, 10000 + 4 * 60, /* EST/EDT */ 556 10000 + 6 * 60, 10000 + 5 * 60, /* CST/CDT */ 557 10000 + 7 * 60, 10000 + 6 * 60, /* MST/MDT */ 558 10000 + 8 * 60, 10000 + 7 * 60 /* PST/PDT */ 559 }; 560 561 template <typename CharT> 562 static bool 563 RegionMatches(const char* s1, int s1off, const CharT* s2, int s2off, int count) 564 { 565 while (count > 0 && s1[s1off] && s2[s2off]) { 566 if (unicode::ToLowerCase(s1[s1off]) != unicode::ToLowerCase(s2[s2off])) 567 break; 568 569 s1off++; 570 s2off++; 571 count--; 572 } 573 574 return count == 0; 575 } 576 577 /* ES6 20.3.3.4. */ 578 static bool 579 date_UTC(JSContext* cx, unsigned argc, Value* vp) 580 { 581 CallArgs args = CallArgsFromVp(argc, vp); 582 583 // Steps 1-2. 584 double y; 585 if (!ToNumber(cx, args.get(0), &y)) 586 return false; 587 588 // Steps 3-4. 589 double m; 590 if (!ToNumber(cx, args.get(1), &m)) 591 return false; 592 593 // Steps 5-6. 594 double dt; 595 if (args.length() >= 3) { 596 if (!ToNumber(cx, args[2], &dt)) 597 return false; 598 } else { 599 dt = 1; 600 } 601 602 // Steps 7-8. 603 double h; 604 if (args.length() >= 4) { 605 if (!ToNumber(cx, args[3], &h)) 606 return false; 607 } else { 608 h = 0; 609 } 610 611 // Steps 9-10. 612 double min; 613 if (args.length() >= 5) { 614 if (!ToNumber(cx, args[4], &min)) 615 return false; 616 } else { 617 min = 0; 618 } 619 620 // Steps 11-12. 621 double s; 622 if (args.length() >= 6) { 623 if (!ToNumber(cx, args[5], &s)) 624 return false; 625 } else { 626 s = 0; 627 } 628 629 // Steps 13-14. 630 double milli; 631 if (args.length() >= 7) { 632 if (!ToNumber(cx, args[6], &milli)) 633 return false; 634 } else { 635 milli = 0; 636 } 637 638 // Step 15. 639 double yr = y; 640 if (!IsNaN(y)) { 641 double yint = ToInteger(y); 642 if (0 <= yint && yint <= 99) 643 yr = 1900 + yint; 644 } 645 646 // Step 16. 647 ClippedTime time = TimeClip(MakeDate(MakeDay(yr, m, dt), MakeTime(h, min, s, milli))); 648 args.rval().set(TimeValue(time)); 649 return true; 650 } 651 652 /* 653 * Read and convert decimal digits from s[*i] into *result 654 * while *i < limit. 655 * 656 * Succeed if any digits are converted. Advance *i only 657 * as digits are consumed. 658 */ 659 template <typename CharT> 660 static bool 661 ParseDigits(size_t* result, const CharT* s, size_t* i, size_t limit) 662 { 663 size_t init = *i; 664 *result = 0; 665 while (*i < limit && ('0' <= s[*i] && s[*i] <= '9')) { 666 *result *= 10; 667 *result += (s[*i] - '0'); 668 ++(*i); 669 } 670 return *i != init; 671 } 672 673 /* 674 * Read and convert decimal digits to the right of a decimal point, 675 * representing a fractional integer, from s[*i] into *result 676 * while *i < limit. 677 * 678 * Succeed if any digits are converted. Advance *i only 679 * as digits are consumed. 680 */ 681 template <typename CharT> 682 static bool 683 ParseFractional(double* result, const CharT* s, size_t* i, size_t limit) 684 { 685 double factor = 0.1; 686 size_t init = *i; 687 *result = 0.0; 688 while (*i < limit && ('0' <= s[*i] && s[*i] <= '9')) { 689 *result += (s[*i] - '0') * factor; 690 factor *= 0.1; 691 ++(*i); 692 } 693 return *i != init; 694 } 695 696 /* 697 * Read and convert exactly n decimal digits from s[*i] 698 * to s[min(*i+n,limit)] into *result. 699 * 700 * Succeed if exactly n digits are converted. Advance *i only 701 * on success. 702 */ 703 template <typename CharT> 704 static bool 705 ParseDigitsN(size_t n, size_t* result, const CharT* s, size_t* i, size_t limit) 706 { 707 size_t init = *i; 708 709 if (ParseDigits(result, s, i, Min(limit, init + n))) 710 return (*i - init) == n; 711 712 *i = init; 713 return false; 714 } 715 716 /* 717 * Read and convert n or less decimal digits from s[*i] 718 * to s[min(*i+n,limit)] into *result. 719 * 720 * Succeed only if greater than zero but less than or equal to n digits are 721 * converted. Advance *i only on success. 722 */ 723 template <typename CharT> 724 static bool 725 ParseDigitsNOrLess(size_t n, size_t* result, const CharT* s, size_t* i, size_t limit) 726 { 727 size_t init = *i; 728 729 if (ParseDigits(result, s, i, Min(limit, init + n))) 730 return ((*i - init) > 0) && ((*i - init) <= n); 731 732 *i = init; 733 return false; 734 } 735 736 static int 737 DaysInMonth(int year, int month) 738 { 739 bool leap = IsLeapYear(year); 740 int result = int(DayFromMonth(month, leap) - DayFromMonth(month - 1, leap)); 741 return result; 742 } 743 744 /* 745 * Parse a string in one of the date-time formats given by the W3C 746 * "NOTE-datetime" specification. These formats make up a restricted 747 * profile of the ISO 8601 format. Quoted here: 748 * 749 * Any combination of the date formats with the time formats is 750 * allowed, and also either the date or the time can be missing. 751 * 752 * The specification is silent on the meaning when fields are 753 * ommitted so the interpretations are a guess, but hopefully a 754 * reasonable one. We default the month to January, the day to the 755 * 1st, and hours minutes and seconds all to 0. If the date is 756 * missing entirely then we assume 1970-01-01 so that the time can 757 * be aded to a date later. If the time is missing then we assume 758 * 00:00 UTC. If the time is present but the time zone field is 759 * missing then we use local time. 760 * 761 * For the sake of cross compatibility with other implementations we 762 * make a few exceptions to the standard: months, days, hours, minutes 763 * and seconds may be either one or two digits long, and the 'T' from 764 * the time part may be replaced with a space. Given that, a date time 765 * like "1999-1-1 1:1:1" will parse successfully. 766 * 767 * Date part: 768 * 769 * Year: 770 * YYYY (eg 1997) 771 * 772 * Year and month: 773 * YYYY-MM (eg 1997-07) 774 * 775 * Complete date: 776 * YYYY-MM-DD (eg 1997-07-16) 777 * 778 * Time part: 779 * 780 * Hours and minutes: 781 * Thh:mmTZD (eg T19:20+01:00) 782 * 783 * Hours, minutes and seconds: 784 * Thh:mm:ssTZD (eg T19:20:30+01:00) 785 * 786 * Hours, minutes, seconds and a decimal fraction of a second: 787 * Thh:mm:ss.sTZD (eg T19:20:30.45+01:00) 788 * 789 * where: 790 * 791 * YYYY = four-digit year or six digit year as +YYYYYY or -YYYYYY 792 * MM = one or two-digit month (01=January, etc.) 793 * DD = one or two-digit day of month (01 through 31) 794 * hh = one or two digits of hour (00 through 23) (am/pm NOT allowed) 795 * mm = one or two digits of minute (00 through 59) 796 * ss = one or two digits of second (00 through 59) 797 * s = one or more digits representing a decimal fraction of a second 798 * TZD = time zone designator (Z or +hh:mm or -hh:mm or missing for local) 799 */ 800 template <typename CharT> 801 static bool 802 ParseISOStyleDate(const CharT* s, size_t length, ClippedTime* result) 803 { 804 size_t i = 0; 805 int tzMul = 1; 806 int dateMul = 1; 807 size_t year = 1970; 808 size_t month = 1; 809 size_t day = 1; 810 size_t hour = 0; 811 size_t min = 0; 812 size_t sec = 0; 813 double frac = 0; 814 bool isLocalTime = false; 815 size_t tzHour = 0; 816 size_t tzMin = 0; 817 818 #define PEEK(ch) (i < length && s[i] == ch) 819 820 #define NEED(ch) \ 821 if (i >= length || s[i] != ch) { return false; } else { ++i; } 822 823 #define DONE_DATE_UNLESS(ch) \ 824 if (i >= length || s[i] != ch) { goto done_date; } else { ++i; } 825 826 #define DONE_UNLESS(ch) \ 827 if (i >= length || s[i] != ch) { goto done; } else { ++i; } 828 829 #define NEED_NDIGITS(n, field) \ 830 if (!ParseDigitsN(n, &field, s, &i, length)) { return false; } 831 832 #define NEED_NDIGITS_OR_LESS(n, field) \ 833 if (!ParseDigitsNOrLess(n, &field, s, &i, length)) { return false; } 834 835 if (PEEK('+') || PEEK('-')) { 836 if (PEEK('-')) 837 dateMul = -1; 838 ++i; 839 NEED_NDIGITS(6, year); 840 } else if (!PEEK('T')) { 841 NEED_NDIGITS(4, year); 842 } 843 DONE_DATE_UNLESS('-'); 844 NEED_NDIGITS_OR_LESS(2, month); 845 DONE_DATE_UNLESS('-'); 846 NEED_NDIGITS_OR_LESS(2, day); 847 848 done_date: 849 if (PEEK('T') || PEEK(' ')) 850 i++; 851 else 852 goto done; 853 854 NEED_NDIGITS_OR_LESS(2, hour); 855 NEED(':'); 856 NEED_NDIGITS_OR_LESS(2, min); 857 858 if (PEEK(':')) { 859 ++i; 860 NEED_NDIGITS_OR_LESS(2, sec); 861 if (PEEK('.')) { 862 ++i; 863 if (!ParseFractional(&frac, s, &i, length)) 864 return false; 865 } 866 } 867 868 if (PEEK('Z')) { 869 ++i; 870 } else if (PEEK('+') || PEEK('-')) { 871 if (PEEK('-')) 872 tzMul = -1; 873 ++i; 874 NEED_NDIGITS(2, tzHour); 875 /* 876 * Non-standard extension to the ISO date format (permitted by ES5): 877 * allow "-0700" as a time zone offset, not just "-07:00". 878 */ 879 if (PEEK(':')) 880 ++i; 881 NEED_NDIGITS(2, tzMin); 882 } else { 883 isLocalTime = true; 884 } 885 886 done: 887 if (year > 275943 // ceil(1e8/365) + 1970 888 || (month == 0 || month > 12) 889 || (day == 0 || day > size_t(DaysInMonth(year,month))) 890 || hour > 24 891 || ((hour == 24) && (min > 0 || sec > 0 || frac > 0)) 892 || min > 59 893 || sec > 59 894 || tzHour > 23 895 || tzMin > 59) 896 { 897 return false; 898 } 899 900 if (i != length) 901 return false; 902 903 month -= 1; /* convert month to 0-based */ 904 905 double msec = MakeDate(MakeDay(dateMul * double(year), month, day), 906 MakeTime(hour, min, sec, frac * 1000.0)); 907 908 if (isLocalTime) 909 msec = UTC(msec); 910 else 911 msec -= tzMul * (tzHour * msPerHour + tzMin * msPerMinute); 912 913 *result = TimeClip(msec); 914 return NumbersAreIdentical(msec, result->toDouble()); 915 916 #undef PEEK 917 #undef NEED 918 #undef DONE_UNLESS 919 #undef NEED_NDIGITS 920 } 921 922 template <typename CharT> 923 static bool 924 ParseDate(const CharT* s, size_t length, ClippedTime* result) 925 { 926 if (ParseISOStyleDate(s, length, result)) 927 return true; 928 929 if (length == 0) 930 return false; 931 932 int year = -1; 933 int mon = -1; 934 int mday = -1; 935 int hour = -1; 936 int min = -1; 937 int sec = -1; 938 int tzOffset = -1; 939 940 int prevc = 0; 941 942 bool seenPlusMinus = false; 943 bool seenMonthName = false; 944 945 size_t i = 0; 946 while (i < length) { 947 int c = s[i]; 948 i++; 949 if (c <= ' ' || c == ',' || c == '-') { 950 if (c == '-' && '0' <= s[i] && s[i] <= '9') 951 prevc = c; 952 continue; 953 } 954 if (c == '(') { /* comments) */ 955 int depth = 1; 956 while (i < length) { 957 c = s[i]; 958 i++; 959 if (c == '(') { 960 depth++; 961 } else if (c == ')') { 962 if (--depth <= 0) 963 break; 964 } 965 } 966 continue; 967 } 968 if ('0' <= c && c <= '9') { 969 int n = c - '0'; 970 while (i < length && '0' <= (c = s[i]) && c <= '9') { 971 n = n * 10 + c - '0'; 972 i++; 973 } 974 975 /* 976 * Allow TZA before the year, so 'Wed Nov 05 21:49:11 GMT-0800 1997' 977 * works. 978 * 979 * Uses of seenPlusMinus allow ':' in TZA, so Java no-timezone style 980 * of GMT+4:30 works. 981 */ 982 983 if ((prevc == '+' || prevc == '-')/* && year>=0 */) { 984 /* Make ':' case below change tzOffset. */ 985 seenPlusMinus = true; 986 987 /* offset */ 988 if (n < 24) 989 n = n * 60; /* EG. "GMT-3" */ 990 else 991 n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */ 992 993 if (prevc == '+') /* plus means east of GMT */ 994 n = -n; 995 996 if (tzOffset != 0 && tzOffset != -1) 997 return false; 998 999 tzOffset = n; 1000 } else if (prevc == '/' && mon >= 0 && mday >= 0 && year < 0) { 1001 if (c <= ' ' || c == ',' || c == '/' || i >= length) 1002 year = n; 1003 else 1004 return false; 1005 } else if (c == ':') { 1006 if (hour < 0) 1007 hour = /*byte*/ n; 1008 else if (min < 0) 1009 min = /*byte*/ n; 1010 else 1011 return false; 1012 } else if (c == '/') { 1013 /* 1014 * Until it is determined that mon is the actual month, keep 1015 * it as 1-based rather than 0-based. 1016 */ 1017 if (mon < 0) 1018 mon = /*byte*/ n; 1019 else if (mday < 0) 1020 mday = /*byte*/ n; 1021 else 1022 return false; 1023 } else if (i < length && c != ',' && c > ' ' && c != '-' && c != '(') { 1024 return false; 1025 } else if (seenPlusMinus && n < 60) { /* handle GMT-3:30 */ 1026 if (tzOffset < 0) 1027 tzOffset -= n; 1028 else 1029 tzOffset += n; 1030 } else if (hour >= 0 && min < 0) { 1031 min = /*byte*/ n; 1032 } else if (prevc == ':' && min >= 0 && sec < 0) { 1033 sec = /*byte*/ n; 1034 } else if (mon < 0) { 1035 mon = /*byte*/n; 1036 } else if (mon >= 0 && mday < 0) { 1037 mday = /*byte*/ n; 1038 } else if (mon >= 0 && mday >= 0 && year < 0) { 1039 year = n; 1040 } else { 1041 return false; 1042 } 1043 prevc = 0; 1044 } else if (c == '/' || c == ':' || c == '+' || c == '-') { 1045 prevc = c; 1046 } else { 1047 size_t st = i - 1; 1048 while (i < length) { 1049 c = s[i]; 1050 if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'))) 1051 break; 1052 i++; 1053 } 1054 1055 if (i <= st + 1) 1056 return false; 1057 1058 int k; 1059 for (k = ArrayLength(wtb); --k >= 0;) { 1060 if (RegionMatches(wtb[k], 0, s, st, i - st)) { 1061 int action = ttb[k]; 1062 if (action != 0) { 1063 if (action < 0) { 1064 /* 1065 * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as 1066 * 12:30, instead of blindly adding 12 if PM. 1067 */ 1068 MOZ_ASSERT(action == -1 || action == -2); 1069 if (hour > 12 || hour < 0) 1070 return false; 1071 1072 if (action == -1 && hour == 12) /* am */ 1073 hour = 0; 1074 else if (action == -2 && hour != 12) /* pm */ 1075 hour += 12; 1076 } else if (action <= 13) { /* month! */ 1077 /* 1078 * Adjust mon to be 1-based until the final values 1079 * for mon, mday and year are adjusted below. 1080 */ 1081 if (seenMonthName) 1082 return false; 1083 1084 seenMonthName = true; 1085 int temp = /*byte*/ (action - 2) + 1; 1086 1087 if (mon < 0) { 1088 mon = temp; 1089 } else if (mday < 0) { 1090 mday = mon; 1091 mon = temp; 1092 } else if (year < 0) { 1093 year = mon; 1094 mon = temp; 1095 } else { 1096 return false; 1097 } 1098 } else { 1099 tzOffset = action - 10000; 1100 } 1101 } 1102 break; 1103 } 1104 } 1105 1106 if (k < 0) 1107 return false; 1108 1109 prevc = 0; 1110 } 1111 } 1112 1113 if (year < 0 || mon < 0 || mday < 0) 1114 return false; 1115 1116 /* 1117 * Case 1. The input string contains an English month name. 1118 * The form of the string can be month f l, or f month l, or 1119 * f l month which each evaluate to the same date. 1120 * If f and l are both greater than or equal to 100 the date 1121 * is invalid. 1122 * 1123 * The year is taken to be either the greater of the values f, l or 1124 * whichever is set to zero. If the year is greater than or equal to 1125 * 50 and less than 100, it is considered to be the number of years 1126 * after 1900. If the year is less than 50 it is considered to be the 1127 * number of years after 2000, otherwise it is considered to be the 1128 * number of years after 0. 1129 * 1130 * Case 2. The input string is of the form "f/m/l" where f, m and l are 1131 * integers, e.g. 7/16/45. mon, mday and year values are adjusted 1132 * to achieve Chrome compatibility. 1133 * 1134 * a. If 0 < f <= 12 and 0 < l <= 31, f/m/l is interpreted as 1135 * month/day/year. 1136 * i. If year < 50, it is the number of years after 2000 1137 * ii. If year >= 50, it is the number of years after 1900. 1138 * iii. If year >= 100, it is the number of years after 0. 1139 * b. If 31 < f and 0 < m <= 12 and 0 < l <= 31 f/m/l is 1140 * interpreted as year/month/day 1141 * i. If year < 50, it is the number of years after 2000 1142 * ii. If year >= 50, it is the number of years after 1900. 1143 * iii. If year >= 100, it is the number of years after 0. 1144 */ 1145 if (seenMonthName) { 1146 if (mday >= 100 && mon >= 100) 1147 return false; 1148 1149 if (year > 0 && (mday == 0 || mday > year)) { 1150 int temp = year; 1151 year = mday; 1152 mday = temp; 1153 } 1154 1155 if (mday <= 0 || mday > 31) 1156 return false; 1157 1158 } else if (0 < mon && mon <= 12 && 0 < mday && mday <= 31) { 1159 /* (a) month/day/year */ 1160 } else { 1161 /* (b) year/month/day */ 1162 if (mon > 31 && mday <= 12 && year <= 31) { 1163 int temp = year; 1164 year = mon; 1165 mon = mday; 1166 mday = temp; 1167 } else { 1168 return false; 1169 } 1170 } 1171 1172 if (year < 50) 1173 year += 2000; 1174 else if (year >= 50 && year < 100) 1175 year += 1900; 1176 1177 mon -= 1; /* convert month to 0-based */ 1178 if (sec < 0) 1179 sec = 0; 1180 if (min < 0) 1181 min = 0; 1182 if (hour < 0) 1183 hour = 0; 1184 1185 double msec = MakeDate(MakeDay(year, mon, mday), MakeTime(hour, min, sec, 0)); 1186 1187 if (tzOffset == -1) /* no time zone specified, have to use local */ 1188 msec = UTC(msec); 1189 else 1190 msec += tzOffset * msPerMinute; 1191 1192 *result = TimeClip(msec); 1193 return true; 1194 } 1195 1196 static bool 1197 ParseDate(JSLinearString* s, ClippedTime* result) 1198 { 1199 AutoCheckCannotGC nogc; 1200 return s->hasLatin1Chars() 1201 ? ParseDate(s->latin1Chars(nogc), s->length(), result) 1202 : ParseDate(s->twoByteChars(nogc), s->length(), result); 1203 } 1204 1205 static bool 1206 date_parse(JSContext* cx, unsigned argc, Value* vp) 1207 { 1208 CallArgs args = CallArgsFromVp(argc, vp); 1209 if (args.length() == 0) { 1210 args.rval().setNaN(); 1211 return true; 1212 } 1213 1214 JSString* str = ToString<CanGC>(cx, args[0]); 1215 if (!str) 1216 return false; 1217 1218 JSLinearString* linearStr = str->ensureLinear(cx); 1219 if (!linearStr) 1220 return false; 1221 1222 ClippedTime result; 1223 if (!ParseDate(linearStr, &result)) { 1224 args.rval().setNaN(); 1225 return true; 1226 } 1227 1228 args.rval().set(TimeValue(result)); 1229 return true; 1230 } 1231 1232 static ClippedTime 1233 NowAsMillis() 1234 { 1235 const double maxResolutionMs = 2; 1236 double timestamp = static_cast<double>(PRMJ_Now()) / PRMJ_USEC_PER_MSEC; 1237 timestamp = floor(timestamp / maxResolutionMs) * maxResolutionMs; 1238 return TimeClip(timestamp); 1239 } 1240 1241 bool 1242 js::date_now(JSContext* cx, unsigned argc, Value* vp) 1243 { 1244 CallArgs args = CallArgsFromVp(argc, vp); 1245 args.rval().set(TimeValue(NowAsMillis())); 1246 return true; 1247 } 1248 1249 void 1250 DateObject::setUTCTime(ClippedTime t) 1251 { 1252 for (size_t ind = COMPONENTS_START_SLOT; ind < RESERVED_SLOTS; ind++) 1253 setReservedSlot(ind, UndefinedValue()); 1254 1255 setFixedSlot(UTC_TIME_SLOT, TimeValue(t)); 1256 } 1257 1258 void 1259 DateObject::setUTCTime(ClippedTime t, MutableHandleValue vp) 1260 { 1261 setUTCTime(t); 1262 vp.set(TimeValue(t)); 1263 } 1264 1265 void 1266 DateObject::fillLocalTimeSlots() 1267 { 1268 /* Check if the cache is already populated. */ 1269 if (!getReservedSlot(LOCAL_TIME_SLOT).isUndefined() && 1270 getReservedSlot(TZA_SLOT).toDouble() == DateTimeInfo::localTZA()) 1271 { 1272 return; 1273 } 1274 1275 /* Remember time zone used to generate the local cache. */ 1276 setReservedSlot(TZA_SLOT, DoubleValue(DateTimeInfo::localTZA())); 1277 1278 double utcTime = UTCTime().toNumber(); 1279 1280 if (!IsFinite(utcTime)) { 1281 for (size_t ind = COMPONENTS_START_SLOT; ind < RESERVED_SLOTS; ind++) 1282 setReservedSlot(ind, DoubleValue(utcTime)); 1283 return; 1284 } 1285 1286 double localTime = LocalTime(utcTime); 1287 1288 setReservedSlot(LOCAL_TIME_SLOT, DoubleValue(localTime)); 1289 1290 int year = (int) floor(localTime /(msPerDay * 365.2425)) + 1970; 1291 double yearStartTime = TimeFromYear(year); 1292 1293 /* Adjust the year in case the approximation was wrong, as in YearFromTime. */ 1294 int yearDays; 1295 if (yearStartTime > localTime) { 1296 year--; 1297 yearStartTime -= (msPerDay * DaysInYear(year)); 1298 yearDays = DaysInYear(year); 1299 } else { 1300 yearDays = DaysInYear(year); 1301 double nextStart = yearStartTime + (msPerDay * yearDays); 1302 if (nextStart <= localTime) { 1303 year++; 1304 yearStartTime = nextStart; 1305 yearDays = DaysInYear(year); 1306 } 1307 } 1308 1309 setReservedSlot(LOCAL_YEAR_SLOT, Int32Value(year)); 1310 1311 uint64_t yearTime = uint64_t(localTime - yearStartTime); 1312 int yearSeconds = uint32_t(yearTime / 1000); 1313 1314 int day = yearSeconds / int(SecondsPerDay); 1315 1316 int step = -1, next = 30; 1317 int month; 1318 1319 do { 1320 if (day <= next) { 1321 month = 0; 1322 break; 1323 } 1324 step = next; 1325 next += ((yearDays == 366) ? 29 : 28); 1326 if (day <= next) { 1327 month = 1; 1328 break; 1329 } 1330 step = next; 1331 if (day <= (next += 31)) { 1332 month = 2; 1333 break; 1334 } 1335 step = next; 1336 if (day <= (next += 30)) { 1337 month = 3; 1338 break; 1339 } 1340 step = next; 1341 if (day <= (next += 31)) { 1342 month = 4; 1343 break; 1344 } 1345 step = next; 1346 if (day <= (next += 30)) { 1347 month = 5; 1348 break; 1349 } 1350 step = next; 1351 if (day <= (next += 31)) { 1352 month = 6; 1353 break; 1354 } 1355 step = next; 1356 if (day <= (next += 31)) { 1357 month = 7; 1358 break; 1359 } 1360 step = next; 1361 if (day <= (next += 30)) { 1362 month = 8; 1363 break; 1364 } 1365 step = next; 1366 if (day <= (next += 31)) { 1367 month = 9; 1368 break; 1369 } 1370 step = next; 1371 if (day <= (next += 30)) { 1372 month = 10; 1373 break; 1374 } 1375 step = next; 1376 month = 11; 1377 } while (0); 1378 1379 setReservedSlot(LOCAL_MONTH_SLOT, Int32Value(month)); 1380 setReservedSlot(LOCAL_DATE_SLOT, Int32Value(day - step)); 1381 1382 int weekday = WeekDay(localTime); 1383 setReservedSlot(LOCAL_DAY_SLOT, Int32Value(weekday)); 1384 1385 setReservedSlot(LOCAL_SECONDS_INTO_YEAR_SLOT, Int32Value(yearSeconds)); 1386 } 1387 1388 inline double 1389 DateObject::cachedLocalTime() 1390 { 1391 fillLocalTimeSlots(); 1392 return getReservedSlot(LOCAL_TIME_SLOT).toDouble(); 1393 } 1394 1395 MOZ_ALWAYS_INLINE bool 1396 IsDate(HandleValue v) 1397 { 1398 return v.isObject() && v.toObject().is<DateObject>(); 1399 } 1400 1401 /* 1402 * See ECMA 15.9.5.4 thru 15.9.5.23 1403 */ 1404 /* static */ MOZ_ALWAYS_INLINE bool 1405 DateObject::getTime_impl(JSContext* cx, const CallArgs& args) 1406 { 1407 args.rval().set(args.thisv().toObject().as<DateObject>().UTCTime()); 1408 return true; 1409 } 1410 1411 static bool 1412 date_getTime(JSContext* cx, unsigned argc, Value* vp) 1413 { 1414 CallArgs args = CallArgsFromVp(argc, vp); 1415 return CallNonGenericMethod<IsDate, DateObject::getTime_impl>(cx, args); 1416 } 1417 1418 /* static */ MOZ_ALWAYS_INLINE bool 1419 DateObject::getYear_impl(JSContext* cx, const CallArgs& args) 1420 { 1421 DateObject* dateObj = &args.thisv().toObject().as<DateObject>(); 1422 dateObj->fillLocalTimeSlots(); 1423 1424 Value yearVal = dateObj->getReservedSlot(LOCAL_YEAR_SLOT); 1425 if (yearVal.isInt32()) { 1426 /* Follow ECMA-262 to the letter, contrary to IE JScript. */ 1427 int year = yearVal.toInt32() - 1900; 1428 args.rval().setInt32(year); 1429 } else { 1430 args.rval().set(yearVal); 1431 } 1432 1433 return true; 1434 } 1435 1436 static bool 1437 date_getYear(JSContext* cx, unsigned argc, Value* vp) 1438 { 1439 CallArgs args = CallArgsFromVp(argc, vp); 1440 return CallNonGenericMethod<IsDate, DateObject::getYear_impl>(cx, args); 1441 } 1442 1443 /* static */ MOZ_ALWAYS_INLINE bool 1444 DateObject::getFullYear_impl(JSContext* cx, const CallArgs& args) 1445 { 1446 DateObject* dateObj = &args.thisv().toObject().as<DateObject>(); 1447 dateObj->fillLocalTimeSlots(); 1448 1449 args.rval().set(dateObj->getReservedSlot(LOCAL_YEAR_SLOT)); 1450 return true; 1451 } 1452 1453 static bool 1454 date_getFullYear(JSContext* cx, unsigned argc, Value* vp) 1455 { 1456 CallArgs args = CallArgsFromVp(argc, vp); 1457 return CallNonGenericMethod<IsDate, DateObject::getFullYear_impl>(cx, args); 1458 } 1459 1460 /* static */ MOZ_ALWAYS_INLINE bool 1461 DateObject::getUTCFullYear_impl(JSContext* cx, const CallArgs& args) 1462 { 1463 double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber(); 1464 if (IsFinite(result)) 1465 result = YearFromTime(result); 1466 1467 args.rval().setNumber(result); 1468 return true; 1469 } 1470 1471 static bool 1472 date_getUTCFullYear(JSContext* cx, unsigned argc, Value* vp) 1473 { 1474 CallArgs args = CallArgsFromVp(argc, vp); 1475 return CallNonGenericMethod<IsDate, DateObject::getUTCFullYear_impl>(cx, args); 1476 } 1477 1478 /* static */ MOZ_ALWAYS_INLINE bool 1479 DateObject::getMonth_impl(JSContext* cx, const CallArgs& args) 1480 { 1481 DateObject* dateObj = &args.thisv().toObject().as<DateObject>(); 1482 dateObj->fillLocalTimeSlots(); 1483 1484 args.rval().set(dateObj->getReservedSlot(LOCAL_MONTH_SLOT)); 1485 return true; 1486 } 1487 1488 static bool 1489 date_getMonth(JSContext* cx, unsigned argc, Value* vp) 1490 { 1491 CallArgs args = CallArgsFromVp(argc, vp); 1492 return CallNonGenericMethod<IsDate, DateObject::getMonth_impl>(cx, args); 1493 } 1494 1495 /* static */ MOZ_ALWAYS_INLINE bool 1496 DateObject::getUTCMonth_impl(JSContext* cx, const CallArgs& args) 1497 { 1498 double d = args.thisv().toObject().as<DateObject>().UTCTime().toNumber(); 1499 args.rval().setNumber(MonthFromTime(d)); 1500 return true; 1501 } 1502 1503 static bool 1504 date_getUTCMonth(JSContext* cx, unsigned argc, Value* vp) 1505 { 1506 CallArgs args = CallArgsFromVp(argc, vp); 1507 return CallNonGenericMethod<IsDate, DateObject::getUTCMonth_impl>(cx, args); 1508 } 1509 1510 /* static */ MOZ_ALWAYS_INLINE bool 1511 DateObject::getDate_impl(JSContext* cx, const CallArgs& args) 1512 { 1513 DateObject* dateObj = &args.thisv().toObject().as<DateObject>(); 1514 dateObj->fillLocalTimeSlots(); 1515 1516 args.rval().set(dateObj->getReservedSlot(LOCAL_DATE_SLOT)); 1517 return true; 1518 } 1519 1520 static bool 1521 date_getDate(JSContext* cx, unsigned argc, Value* vp) 1522 { 1523 CallArgs args = CallArgsFromVp(argc, vp); 1524 return CallNonGenericMethod<IsDate, DateObject::getDate_impl>(cx, args); 1525 } 1526 1527 /* static */ MOZ_ALWAYS_INLINE bool 1528 DateObject::getUTCDate_impl(JSContext* cx, const CallArgs& args) 1529 { 1530 double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber(); 1531 if (IsFinite(result)) 1532 result = DateFromTime(result); 1533 1534 args.rval().setNumber(result); 1535 return true; 1536 } 1537 1538 static bool 1539 date_getUTCDate(JSContext* cx, unsigned argc, Value* vp) 1540 { 1541 CallArgs args = CallArgsFromVp(argc, vp); 1542 return CallNonGenericMethod<IsDate, DateObject::getUTCDate_impl>(cx, args); 1543 } 1544 1545 /* static */ MOZ_ALWAYS_INLINE bool 1546 DateObject::getDay_impl(JSContext* cx, const CallArgs& args) 1547 { 1548 DateObject* dateObj = &args.thisv().toObject().as<DateObject>(); 1549 dateObj->fillLocalTimeSlots(); 1550 1551 args.rval().set(dateObj->getReservedSlot(LOCAL_DAY_SLOT)); 1552 return true; 1553 } 1554 1555 static bool 1556 date_getDay(JSContext* cx, unsigned argc, Value* vp) 1557 { 1558 CallArgs args = CallArgsFromVp(argc, vp); 1559 return CallNonGenericMethod<IsDate, DateObject::getDay_impl>(cx, args); 1560 } 1561 1562 /* static */ MOZ_ALWAYS_INLINE bool 1563 DateObject::getUTCDay_impl(JSContext* cx, const CallArgs& args) 1564 { 1565 double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber(); 1566 if (IsFinite(result)) 1567 result = WeekDay(result); 1568 1569 args.rval().setNumber(result); 1570 return true; 1571 } 1572 1573 static bool 1574 date_getUTCDay(JSContext* cx, unsigned argc, Value* vp) 1575 { 1576 CallArgs args = CallArgsFromVp(argc, vp); 1577 return CallNonGenericMethod<IsDate, DateObject::getUTCDay_impl>(cx, args); 1578 } 1579 1580 /* static */ MOZ_ALWAYS_INLINE bool 1581 DateObject::getHours_impl(JSContext* cx, const CallArgs& args) 1582 { 1583 DateObject* dateObj = &args.thisv().toObject().as<DateObject>(); 1584 dateObj->fillLocalTimeSlots(); 1585 1586 // Note: LOCAL_SECONDS_INTO_YEAR_SLOT is guaranteed to contain an 1587 // int32 or NaN after the call to fillLocalTimeSlots. 1588 Value yearSeconds = dateObj->getReservedSlot(LOCAL_SECONDS_INTO_YEAR_SLOT); 1589 if (yearSeconds.isDouble()) { 1590 MOZ_ASSERT(IsNaN(yearSeconds.toDouble())); 1591 args.rval().set(yearSeconds); 1592 } else { 1593 args.rval().setInt32((yearSeconds.toInt32() / int(SecondsPerHour)) % int(HoursPerDay)); 1594 } 1595 return true; 1596 } 1597 1598 static bool 1599 date_getHours(JSContext* cx, unsigned argc, Value* vp) 1600 { 1601 CallArgs args = CallArgsFromVp(argc, vp); 1602 return CallNonGenericMethod<IsDate, DateObject::getHours_impl>(cx, args); 1603 } 1604 1605 /* static */ MOZ_ALWAYS_INLINE bool 1606 DateObject::getUTCHours_impl(JSContext* cx, const CallArgs& args) 1607 { 1608 double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber(); 1609 if (IsFinite(result)) 1610 result = HourFromTime(result); 1611 1612 args.rval().setNumber(result); 1613 return true; 1614 } 1615 1616 static bool 1617 date_getUTCHours(JSContext* cx, unsigned argc, Value* vp) 1618 { 1619 CallArgs args = CallArgsFromVp(argc, vp); 1620 return CallNonGenericMethod<IsDate, DateObject::getUTCHours_impl>(cx, args); 1621 } 1622 1623 /* static */ MOZ_ALWAYS_INLINE bool 1624 DateObject::getMinutes_impl(JSContext* cx, const CallArgs& args) 1625 { 1626 DateObject* dateObj = &args.thisv().toObject().as<DateObject>(); 1627 dateObj->fillLocalTimeSlots(); 1628 1629 // Note: LOCAL_SECONDS_INTO_YEAR_SLOT is guaranteed to contain an 1630 // int32 or NaN after the call to fillLocalTimeSlots. 1631 Value yearSeconds = dateObj->getReservedSlot(LOCAL_SECONDS_INTO_YEAR_SLOT); 1632 if (yearSeconds.isDouble()) { 1633 MOZ_ASSERT(IsNaN(yearSeconds.toDouble())); 1634 args.rval().set(yearSeconds); 1635 } else { 1636 args.rval().setInt32((yearSeconds.toInt32() / int(SecondsPerMinute)) % int(MinutesPerHour)); 1637 } 1638 return true; 1639 } 1640 1641 static bool 1642 date_getMinutes(JSContext* cx, unsigned argc, Value* vp) 1643 { 1644 CallArgs args = CallArgsFromVp(argc, vp); 1645 return CallNonGenericMethod<IsDate, DateObject::getMinutes_impl>(cx, args); 1646 } 1647 1648 /* static */ MOZ_ALWAYS_INLINE bool 1649 DateObject::getUTCMinutes_impl(JSContext* cx, const CallArgs& args) 1650 { 1651 double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber(); 1652 if (IsFinite(result)) 1653 result = MinFromTime(result); 1654 1655 args.rval().setNumber(result); 1656 return true; 1657 } 1658 1659 static bool 1660 date_getUTCMinutes(JSContext* cx, unsigned argc, Value* vp) 1661 { 1662 CallArgs args = CallArgsFromVp(argc, vp); 1663 return CallNonGenericMethod<IsDate, DateObject::getUTCMinutes_impl>(cx, args); 1664 } 1665 1666 /* 1667 * Date.getSeconds is mapped to getUTCSeconds. As long as no supported time 1668 * zone has a fractional-minute component, the differences in their 1669 * specifications aren't observable. 1670 * 1671 * We'll have to split the implementations if a new time zone with a 1672 * fractional-minute component is introduced or once we implement ES6's 1673 * 20.3.1.7 Local Time Zone Adjustment: time zones with adjustments like that 1674 * did historically exist, e.g. 1675 * https://en.wikipedia.org/wiki/UTC%E2%88%9200:25:21 1676 */ 1677 1678 /* static */ MOZ_ALWAYS_INLINE bool 1679 DateObject::getUTCSeconds_impl(JSContext* cx, const CallArgs& args) 1680 { 1681 DateObject* dateObj = &args.thisv().toObject().as<DateObject>(); 1682 dateObj->fillLocalTimeSlots(); 1683 1684 // Note: LOCAL_SECONDS_INTO_YEAR_SLOT is guaranteed to contain an 1685 // int32 or NaN after the call to fillLocalTimeSlots. 1686 Value yearSeconds = dateObj->getReservedSlot(LOCAL_SECONDS_INTO_YEAR_SLOT); 1687 if (yearSeconds.isDouble()) { 1688 MOZ_ASSERT(IsNaN(yearSeconds.toDouble())); 1689 args.rval().set(yearSeconds); 1690 } else { 1691 args.rval().setInt32(yearSeconds.toInt32() % int(SecondsPerMinute)); 1692 } 1693 return true; 1694 } 1695 1696 static bool 1697 date_getUTCSeconds(JSContext* cx, unsigned argc, Value* vp) 1698 { 1699 CallArgs args = CallArgsFromVp(argc, vp); 1700 return CallNonGenericMethod<IsDate, DateObject::getUTCSeconds_impl>(cx, args); 1701 } 1702 /* 1703 * Date.getMilliseconds is mapped to getUTCMilliseconds for the same reasons 1704 * that getSeconds is mapped to getUTCSeconds (see above). No known LocalTZA 1705 * has *ever* included a fractional-second component, however, so we can keep 1706 * this simplification even if we stop implementing ES5 local-time computation 1707 * semantics. 1708 */ 1709 1710 /* static */ MOZ_ALWAYS_INLINE bool 1711 DateObject::getUTCMilliseconds_impl(JSContext* cx, const CallArgs& args) 1712 { 1713 double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber(); 1714 if (IsFinite(result)) 1715 result = msFromTime(result); 1716 1717 args.rval().setNumber(result); 1718 return true; 1719 } 1720 1721 static bool 1722 date_getUTCMilliseconds(JSContext* cx, unsigned argc, Value* vp) 1723 { 1724 CallArgs args = CallArgsFromVp(argc, vp); 1725 return CallNonGenericMethod<IsDate, DateObject::getUTCMilliseconds_impl>(cx, args); 1726 } 1727 1728 /* static */ MOZ_ALWAYS_INLINE bool 1729 DateObject::getTimezoneOffset_impl(JSContext* cx, const CallArgs& args) 1730 { 1731 DateObject* dateObj = &args.thisv().toObject().as<DateObject>(); 1732 double utctime = dateObj->UTCTime().toNumber(); 1733 double localtime = dateObj->cachedLocalTime(); 1734 1735 /* 1736 * Return the time zone offset in minutes for the current locale that is 1737 * appropriate for this time. This value would be a constant except for 1738 * daylight savings time. 1739 */ 1740 double result = (utctime - localtime) / msPerMinute; 1741 args.rval().setNumber(result); 1742 return true; 1743 } 1744 1745 static bool 1746 date_getTimezoneOffset(JSContext* cx, unsigned argc, Value* vp) 1747 { 1748 CallArgs args = CallArgsFromVp(argc, vp); 1749 return CallNonGenericMethod<IsDate, DateObject::getTimezoneOffset_impl>(cx, args); 1750 } 1751 1752 MOZ_ALWAYS_INLINE bool 1753 date_setTime_impl(JSContext* cx, const CallArgs& args) 1754 { 1755 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); 1756 if (args.length() == 0) { 1757 dateObj->setUTCTime(ClippedTime::invalid(), args.rval()); 1758 return true; 1759 } 1760 1761 double result; 1762 if (!ToNumber(cx, args[0], &result)) 1763 return false; 1764 1765 dateObj->setUTCTime(TimeClip(result), args.rval()); 1766 return true; 1767 } 1768 1769 static bool 1770 date_setTime(JSContext* cx, unsigned argc, Value* vp) 1771 { 1772 CallArgs args = CallArgsFromVp(argc, vp); 1773 return CallNonGenericMethod<IsDate, date_setTime_impl>(cx, args); 1774 } 1775 1776 static bool 1777 GetMsecsOrDefault(JSContext* cx, const CallArgs& args, unsigned i, double t, double* millis) 1778 { 1779 if (args.length() <= i) { 1780 *millis = msFromTime(t); 1781 return true; 1782 } 1783 return ToNumber(cx, args[i], millis); 1784 } 1785 1786 static bool 1787 GetSecsOrDefault(JSContext* cx, const CallArgs& args, unsigned i, double t, double* sec) 1788 { 1789 if (args.length() <= i) { 1790 *sec = SecFromTime(t); 1791 return true; 1792 } 1793 return ToNumber(cx, args[i], sec); 1794 } 1795 1796 static bool 1797 GetMinsOrDefault(JSContext* cx, const CallArgs& args, unsigned i, double t, double* mins) 1798 { 1799 if (args.length() <= i) { 1800 *mins = MinFromTime(t); 1801 return true; 1802 } 1803 return ToNumber(cx, args[i], mins); 1804 } 1805 1806 /* ES6 20.3.4.23. */ 1807 MOZ_ALWAYS_INLINE bool 1808 date_setMilliseconds_impl(JSContext* cx, const CallArgs& args) 1809 { 1810 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); 1811 1812 // Steps 1-2. 1813 double t = LocalTime(dateObj->UTCTime().toNumber()); 1814 1815 // Steps 3-4. 1816 double ms; 1817 if (!ToNumber(cx, args.get(0), &ms)) 1818 return false; 1819 1820 // Step 5. 1821 double time = MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms); 1822 1823 // Step 6. 1824 ClippedTime u = TimeClip(UTC(MakeDate(Day(t), time))); 1825 1826 // Steps 7-8. 1827 dateObj->setUTCTime(u, args.rval()); 1828 return true; 1829 } 1830 1831 static bool 1832 date_setMilliseconds(JSContext* cx, unsigned argc, Value* vp) 1833 { 1834 CallArgs args = CallArgsFromVp(argc, vp); 1835 return CallNonGenericMethod<IsDate, date_setMilliseconds_impl>(cx, args); 1836 } 1837 1838 /* ES5 15.9.5.29. */ 1839 MOZ_ALWAYS_INLINE bool 1840 date_setUTCMilliseconds_impl(JSContext* cx, const CallArgs& args) 1841 { 1842 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); 1843 1844 /* Step 1. */ 1845 double t = dateObj->UTCTime().toNumber(); 1846 1847 /* Step 2. */ 1848 double milli; 1849 if (!ToNumber(cx, args.get(0), &milli)) 1850 return false; 1851 double time = MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), milli); 1852 1853 /* Step 3. */ 1854 ClippedTime v = TimeClip(MakeDate(Day(t), time)); 1855 1856 /* Steps 4-5. */ 1857 dateObj->setUTCTime(v, args.rval()); 1858 return true; 1859 } 1860 1861 static bool 1862 date_setUTCMilliseconds(JSContext* cx, unsigned argc, Value* vp) 1863 { 1864 CallArgs args = CallArgsFromVp(argc, vp); 1865 return CallNonGenericMethod<IsDate, date_setUTCMilliseconds_impl>(cx, args); 1866 } 1867 1868 /* ES5 15.9.5.30. */ 1869 MOZ_ALWAYS_INLINE bool 1870 date_setSeconds_impl(JSContext* cx, const CallArgs& args) 1871 { 1872 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); 1873 1874 // Steps 1-2. 1875 double t = LocalTime(dateObj->UTCTime().toNumber()); 1876 1877 // Steps 3-4. 1878 double s; 1879 if (!ToNumber(cx, args.get(0), &s)) 1880 return false; 1881 1882 // Steps 5-6. 1883 double milli; 1884 if (!GetMsecsOrDefault(cx, args, 1, t, &milli)) 1885 return false; 1886 1887 // Step 7. 1888 double date = MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli)); 1889 1890 // Step 8. 1891 ClippedTime u = TimeClip(UTC(date)); 1892 1893 // Step 9. 1894 dateObj->setUTCTime(u, args.rval()); 1895 return true; 1896 } 1897 1898 /* ES6 20.3.4.26. */ 1899 static bool 1900 date_setSeconds(JSContext* cx, unsigned argc, Value* vp) 1901 { 1902 CallArgs args = CallArgsFromVp(argc, vp); 1903 return CallNonGenericMethod<IsDate, date_setSeconds_impl>(cx, args); 1904 } 1905 1906 MOZ_ALWAYS_INLINE bool 1907 date_setUTCSeconds_impl(JSContext* cx, const CallArgs& args) 1908 { 1909 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); 1910 1911 /* Step 1. */ 1912 double t = dateObj->UTCTime().toNumber(); 1913 1914 /* Step 2. */ 1915 double s; 1916 if (!ToNumber(cx, args.get(0), &s)) 1917 return false; 1918 1919 /* Step 3. */ 1920 double milli; 1921 if (!GetMsecsOrDefault(cx, args, 1, t, &milli)) 1922 return false; 1923 1924 /* Step 4. */ 1925 double date = MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli)); 1926 1927 /* Step 5. */ 1928 ClippedTime v = TimeClip(date); 1929 1930 /* Steps 6-7. */ 1931 dateObj->setUTCTime(v, args.rval()); 1932 return true; 1933 } 1934 1935 /* ES5 15.9.5.32. */ 1936 static bool 1937 date_setUTCSeconds(JSContext* cx, unsigned argc, Value* vp) 1938 { 1939 CallArgs args = CallArgsFromVp(argc, vp); 1940 return CallNonGenericMethod<IsDate, date_setUTCSeconds_impl>(cx, args); 1941 } 1942 1943 MOZ_ALWAYS_INLINE bool 1944 date_setMinutes_impl(JSContext* cx, const CallArgs& args) 1945 { 1946 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); 1947 1948 // Steps 1-2. 1949 double t = LocalTime(dateObj->UTCTime().toNumber()); 1950 1951 // Steps 3-4. 1952 double m; 1953 if (!ToNumber(cx, args.get(0), &m)) 1954 return false; 1955 1956 // Steps 5-6. 1957 double s; 1958 if (!GetSecsOrDefault(cx, args, 1, t, &s)) 1959 return false; 1960 1961 // Steps 7-8. 1962 double milli; 1963 if (!GetMsecsOrDefault(cx, args, 2, t, &milli)) 1964 return false; 1965 1966 // Step 9. 1967 double date = MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli)); 1968 1969 // Step 10. 1970 ClippedTime u = TimeClip(UTC(date)); 1971 1972 // Steps 11-12. 1973 dateObj->setUTCTime(u, args.rval()); 1974 return true; 1975 } 1976 1977 /* ES6 20.3.4.24. */ 1978 static bool 1979 date_setMinutes(JSContext* cx, unsigned argc, Value* vp) 1980 { 1981 // Steps 1-2 (the effectful parts). 1982 CallArgs args = CallArgsFromVp(argc, vp); 1983 return CallNonGenericMethod<IsDate, date_setMinutes_impl>(cx, args); 1984 } 1985 1986 MOZ_ALWAYS_INLINE bool 1987 date_setUTCMinutes_impl(JSContext* cx, const CallArgs& args) 1988 { 1989 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); 1990 1991 /* Step 1. */ 1992 double t = dateObj->UTCTime().toNumber(); 1993 1994 /* Step 2. */ 1995 double m; 1996 if (!ToNumber(cx, args.get(0), &m)) 1997 return false; 1998 1999 /* Step 3. */ 2000 double s; 2001 if (!GetSecsOrDefault(cx, args, 1, t, &s)) 2002 return false; 2003 2004 /* Step 4. */ 2005 double milli; 2006 if (!GetMsecsOrDefault(cx, args, 2, t, &milli)) 2007 return false; 2008 2009 /* Step 5. */ 2010 double date = MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli)); 2011 2012 /* Step 6. */ 2013 ClippedTime v = TimeClip(date); 2014 2015 /* Steps 7-8. */ 2016 dateObj->setUTCTime(v, args.rval()); 2017 return true; 2018 } 2019 2020 /* ES5 15.9.5.34. */ 2021 static bool 2022 date_setUTCMinutes(JSContext* cx, unsigned argc, Value* vp) 2023 { 2024 CallArgs args = CallArgsFromVp(argc, vp); 2025 return CallNonGenericMethod<IsDate, date_setUTCMinutes_impl>(cx, args); 2026 } 2027 2028 MOZ_ALWAYS_INLINE bool 2029 date_setHours_impl(JSContext* cx, const CallArgs& args) 2030 { 2031 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); 2032 2033 // Steps 1-2. 2034 double t = LocalTime(dateObj->UTCTime().toNumber()); 2035 2036 // Steps 3-4. 2037 double h; 2038 if (!ToNumber(cx, args.get(0), &h)) 2039 return false; 2040 2041 // Steps 5-6. 2042 double m; 2043 if (!GetMinsOrDefault(cx, args, 1, t, &m)) 2044 return false; 2045 2046 // Steps 7-8. 2047 double s; 2048 if (!GetSecsOrDefault(cx, args, 2, t, &s)) 2049 return false; 2050 2051 // Steps 9-10. 2052 double milli; 2053 if (!GetMsecsOrDefault(cx, args, 3, t, &milli)) 2054 return false; 2055 2056 // Step 11. 2057 double date = MakeDate(Day(t), MakeTime(h, m, s, milli)); 2058 2059 // Step 12. 2060 ClippedTime u = TimeClip(UTC(date)); 2061 2062 // Steps 13-14. 2063 dateObj->setUTCTime(u, args.rval()); 2064 return true; 2065 } 2066 2067 /* ES5 15.9.5.35. */ 2068 static bool 2069 date_setHours(JSContext* cx, unsigned argc, Value* vp) 2070 { 2071 CallArgs args = CallArgsFromVp(argc, vp); 2072 return CallNonGenericMethod<IsDate, date_setHours_impl>(cx, args); 2073 } 2074 2075 MOZ_ALWAYS_INLINE bool 2076 date_setUTCHours_impl(JSContext* cx, const CallArgs& args) 2077 { 2078 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); 2079 2080 /* Step 1. */ 2081 double t = dateObj->UTCTime().toNumber(); 2082 2083 /* Step 2. */ 2084 double h; 2085 if (!ToNumber(cx, args.get(0), &h)) 2086 return false; 2087 2088 /* Step 3. */ 2089 double m; 2090 if (!GetMinsOrDefault(cx, args, 1, t, &m)) 2091 return false; 2092 2093 /* Step 4. */ 2094 double s; 2095 if (!GetSecsOrDefault(cx, args, 2, t, &s)) 2096 return false; 2097 2098 /* Step 5. */ 2099 double milli; 2100 if (!GetMsecsOrDefault(cx, args, 3, t, &milli)) 2101 return false; 2102 2103 /* Step 6. */ 2104 double newDate = MakeDate(Day(t), MakeTime(h, m, s, milli)); 2105 2106 /* Step 7. */ 2107 ClippedTime v = TimeClip(newDate); 2108 2109 /* Steps 8-9. */ 2110 dateObj->setUTCTime(v, args.rval()); 2111 return true; 2112 } 2113 2114 /* ES5 15.9.5.36. */ 2115 static bool 2116 date_setUTCHours(JSContext* cx, unsigned argc, Value* vp) 2117 { 2118 CallArgs args = CallArgsFromVp(argc, vp); 2119 return CallNonGenericMethod<IsDate, date_setUTCHours_impl>(cx, args); 2120 } 2121 2122 MOZ_ALWAYS_INLINE bool 2123 date_setDate_impl(JSContext* cx, const CallArgs& args) 2124 { 2125 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); 2126 2127 /* Step 1. */ 2128 double t = LocalTime(dateObj->UTCTime().toNumber()); 2129 2130 /* Step 2. */ 2131 double date; 2132 if (!ToNumber(cx, args.get(0), &date)) 2133 return false; 2134 2135 /* Step 3. */ 2136 double newDate = MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)); 2137 2138 /* Step 4. */ 2139 ClippedTime u = TimeClip(UTC(newDate)); 2140 2141 /* Steps 5-6. */ 2142 dateObj->setUTCTime(u, args.rval()); 2143 return true; 2144 } 2145 2146 /* ES5 15.9.5.37. */ 2147 static bool 2148 date_setDate(JSContext* cx, unsigned argc, Value* vp) 2149 { 2150 CallArgs args = CallArgsFromVp(argc, vp); 2151 return CallNonGenericMethod<IsDate, date_setDate_impl>(cx, args); 2152 } 2153 2154 MOZ_ALWAYS_INLINE bool 2155 date_setUTCDate_impl(JSContext* cx, const CallArgs& args) 2156 { 2157 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); 2158 2159 /* Step 1. */ 2160 double t = dateObj->UTCTime().toNumber(); 2161 2162 /* Step 2. */ 2163 double date; 2164 if (!ToNumber(cx, args.get(0), &date)) 2165 return false; 2166 2167 /* Step 3. */ 2168 double newDate = MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)); 2169 2170 /* Step 4. */ 2171 ClippedTime v = TimeClip(newDate); 2172 2173 /* Steps 5-6. */ 2174 dateObj->setUTCTime(v, args.rval()); 2175 return true; 2176 } 2177 2178 static bool 2179 date_setUTCDate(JSContext* cx, unsigned argc, Value* vp) 2180 { 2181 CallArgs args = CallArgsFromVp(argc, vp); 2182 return CallNonGenericMethod<IsDate, date_setUTCDate_impl>(cx, args); 2183 } 2184 2185 static bool 2186 GetDateOrDefault(JSContext* cx, const CallArgs& args, unsigned i, double t, double* date) 2187 { 2188 if (args.length() <= i) { 2189 *date = DateFromTime(t); 2190 return true; 2191 } 2192 return ToNumber(cx, args[i], date); 2193 } 2194 2195 static bool 2196 GetMonthOrDefault(JSContext* cx, const CallArgs& args, unsigned i, double t, double* month) 2197 { 2198 if (args.length() <= i) { 2199 *month = MonthFromTime(t); 2200 return true; 2201 } 2202 return ToNumber(cx, args[i], month); 2203 } 2204 2205 /* ES5 15.9.5.38. */ 2206 MOZ_ALWAYS_INLINE bool 2207 date_setMonth_impl(JSContext* cx, const CallArgs& args) 2208 { 2209 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); 2210 2211 /* Step 1. */ 2212 double t = LocalTime(dateObj->UTCTime().toNumber()); 2213 2214 /* Step 2. */ 2215 double m; 2216 if (!ToNumber(cx, args.get(0), &m)) 2217 return false; 2218 2219 /* Step 3. */ 2220 double date; 2221 if (!GetDateOrDefault(cx, args, 1, t, &date)) 2222 return false; 2223 2224 /* Step 4. */ 2225 double newDate = MakeDate(MakeDay(YearFromTime(t), m, date), TimeWithinDay(t)); 2226 2227 /* Step 5. */ 2228 ClippedTime u = TimeClip(UTC(newDate)); 2229 2230 /* Steps 6-7. */ 2231 dateObj->setUTCTime(u, args.rval()); 2232 return true; 2233 } 2234 2235 static bool 2236 date_setMonth(JSContext* cx, unsigned argc, Value* vp) 2237 { 2238 CallArgs args = CallArgsFromVp(argc, vp); 2239 return CallNonGenericMethod<IsDate, date_setMonth_impl>(cx, args); 2240 } 2241 2242 /* ES5 15.9.5.39. */ 2243 MOZ_ALWAYS_INLINE bool 2244 date_setUTCMonth_impl(JSContext* cx, const CallArgs& args) 2245 { 2246 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); 2247 2248 /* Step 1. */ 2249 double t = dateObj->UTCTime().toNumber(); 2250 2251 /* Step 2. */ 2252 double m; 2253 if (!ToNumber(cx, args.get(0), &m)) 2254 return false; 2255 2256 /* Step 3. */ 2257 double date; 2258 if (!GetDateOrDefault(cx, args, 1, t, &date)) 2259 return false; 2260 2261 /* Step 4. */ 2262 double newDate = MakeDate(MakeDay(YearFromTime(t), m, date), TimeWithinDay(t)); 2263 2264 /* Step 5. */ 2265 ClippedTime v = TimeClip(newDate); 2266 2267 /* Steps 6-7. */ 2268 dateObj->setUTCTime(v, args.rval()); 2269 return true; 2270 } 2271 2272 static bool 2273 date_setUTCMonth(JSContext* cx, unsigned argc, Value* vp) 2274 { 2275 CallArgs args = CallArgsFromVp(argc, vp); 2276 return CallNonGenericMethod<IsDate, date_setUTCMonth_impl>(cx, args); 2277 } 2278 2279 static double 2280 ThisLocalTimeOrZero(Handle<DateObject*> dateObj) 2281 { 2282 double t = dateObj->UTCTime().toNumber(); 2283 if (IsNaN(t)) 2284 return +0; 2285 return LocalTime(t); 2286 } 2287 2288 static double 2289 ThisUTCTimeOrZero(Handle<DateObject*> dateObj) 2290 { 2291 double t = dateObj->as<DateObject>().UTCTime().toNumber(); 2292 return IsNaN(t) ? +0 : t; 2293 } 2294 2295 /* ES5 15.9.5.40. */ 2296 MOZ_ALWAYS_INLINE bool 2297 date_setFullYear_impl(JSContext* cx, const CallArgs& args) 2298 { 2299 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); 2300 2301 /* Step 1. */ 2302 double t = ThisLocalTimeOrZero(dateObj); 2303 2304 /* Step 2. */ 2305 double y; 2306 if (!ToNumber(cx, args.get(0), &y)) 2307 return false; 2308 2309 /* Step 3. */ 2310 double m; 2311 if (!GetMonthOrDefault(cx, args, 1, t, &m)) 2312 return false; 2313 2314 /* Step 4. */ 2315 double date; 2316 if (!GetDateOrDefault(cx, args, 2, t, &date)) 2317 return false; 2318 2319 /* Step 5. */ 2320 double newDate = MakeDate(MakeDay(y, m, date), TimeWithinDay(t)); 2321 2322 /* Step 6. */ 2323 ClippedTime u = TimeClip(UTC(newDate)); 2324 2325 /* Steps 7-8. */ 2326 dateObj->setUTCTime(u, args.rval()); 2327 return true; 2328 } 2329 2330 static bool 2331 date_setFullYear(JSContext* cx, unsigned argc, Value* vp) 2332 { 2333 CallArgs args = CallArgsFromVp(argc, vp); 2334 return CallNonGenericMethod<IsDate, date_setFullYear_impl>(cx, args); 2335 } 2336 2337 /* ES5 15.9.5.41. */ 2338 MOZ_ALWAYS_INLINE bool 2339 date_setUTCFullYear_impl(JSContext* cx, const CallArgs& args) 2340 { 2341 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); 2342 2343 /* Step 1. */ 2344 double t = ThisUTCTimeOrZero(dateObj); 2345 2346 /* Step 2. */ 2347 double y; 2348 if (!ToNumber(cx, args.get(0), &y)) 2349 return false; 2350 2351 /* Step 3. */ 2352 double m; 2353 if (!GetMonthOrDefault(cx, args, 1, t, &m)) 2354 return false; 2355 2356 /* Step 4. */ 2357 double date; 2358 if (!GetDateOrDefault(cx, args, 2, t, &date)) 2359 return false; 2360 2361 /* Step 5. */ 2362 double newDate = MakeDate(MakeDay(y, m, date), TimeWithinDay(t)); 2363 2364 /* Step 6. */ 2365 ClippedTime v = TimeClip(newDate); 2366 2367 /* Steps 7-8. */ 2368 dateObj->setUTCTime(v, args.rval()); 2369 return true; 2370 } 2371 2372 static bool 2373 date_setUTCFullYear(JSContext* cx, unsigned argc, Value* vp) 2374 { 2375 CallArgs args = CallArgsFromVp(argc, vp); 2376 return CallNonGenericMethod<IsDate, date_setUTCFullYear_impl>(cx, args); 2377 } 2378 2379 /* ES5 Annex B.2.5. */ 2380 MOZ_ALWAYS_INLINE bool 2381 date_setYear_impl(JSContext* cx, const CallArgs& args) 2382 { 2383 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); 2384 2385 /* Step 1. */ 2386 double t = ThisLocalTimeOrZero(dateObj); 2387 2388 /* Step 2. */ 2389 double y; 2390 if (!ToNumber(cx, args.get(0), &y)) 2391 return false; 2392 2393 /* Step 3. */ 2394 if (IsNaN(y)) { 2395 dateObj->setUTCTime(ClippedTime::invalid(), args.rval()); 2396 return true; 2397 } 2398 2399 /* Step 4. */ 2400 double yint = ToInteger(y); 2401 if (0 <= yint && yint <= 99) 2402 yint += 1900; 2403 2404 /* Step 5. */ 2405 double day = MakeDay(yint, MonthFromTime(t), DateFromTime(t)); 2406 2407 /* Step 6. */ 2408 double u = UTC(MakeDate(day, TimeWithinDay(t))); 2409 2410 /* Steps 7-8. */ 2411 dateObj->setUTCTime(TimeClip(u), args.rval()); 2412 return true; 2413 } 2414 2415 static bool 2416 date_setYear(JSContext* cx, unsigned argc, Value* vp) 2417 { 2418 CallArgs args = CallArgsFromVp(argc, vp); 2419 return CallNonGenericMethod<IsDate, date_setYear_impl>(cx, args); 2420 } 2421 2422 /* constants for toString, toUTCString */ 2423 static const char js_NaN_date_str[] = "Invalid Date"; 2424 static const char * const days[] = 2425 { 2426 "Sun","Mon","Tue","Wed","Thu","Fri","Sat" 2427 }; 2428 static const char * const months[] = 2429 { 2430 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 2431 }; 2432 2433 /* ES5 B.2.6. */ 2434 MOZ_ALWAYS_INLINE bool 2435 date_toGMTString_impl(JSContext* cx, const CallArgs& args) 2436 { 2437 double utctime = args.thisv().toObject().as<DateObject>().UTCTime().toNumber(); 2438 2439 JSString* str; 2440 if (!IsFinite(utctime)) { 2441 str = NewStringCopyZ<CanGC>(cx, js_NaN_date_str); 2442 } else { 2443 char buf[100]; 2444 SprintfLiteral(buf, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT", 2445 days[int(WeekDay(utctime))], 2446 int(DateFromTime(utctime)), 2447 months[int(MonthFromTime(utctime))], 2448 int(YearFromTime(utctime)), 2449 int(HourFromTime(utctime)), 2450 int(MinFromTime(utctime)), 2451 int(SecFromTime(utctime))); 2452 2453 str = NewStringCopyZ<CanGC>(cx, buf); 2454 } 2455 2456 if (!str) 2457 return false; 2458 args.rval().setString(str); 2459 return true; 2460 } 2461 2462 static bool 2463 date_toGMTString(JSContext* cx, unsigned argc, Value* vp) 2464 { 2465 CallArgs args = CallArgsFromVp(argc, vp); 2466 return CallNonGenericMethod<IsDate, date_toGMTString_impl>(cx, args); 2467 } 2468 2469 /* ES6 draft 2015-01-15 20.3.4.36. */ 2470 MOZ_ALWAYS_INLINE bool 2471 date_toISOString_impl(JSContext* cx, const CallArgs& args) 2472 { 2473 double utctime = args.thisv().toObject().as<DateObject>().UTCTime().toNumber(); 2474 if (!IsFinite(utctime)) { 2475 JS_ReportErrorNumberASCII(cx, js::GetErrorMessage, nullptr, JSMSG_INVALID_DATE); 2476 return false; 2477 } 2478 2479 char buf[100]; 2480 int year = int(YearFromTime(utctime)); 2481 if (year < 0 || year > 9999) { 2482 SprintfLiteral(buf, "%+.6d-%.2d-%.2dT%.2d:%.2d:%.2d.%.3dZ", 2483 int(YearFromTime(utctime)), 2484 int(MonthFromTime(utctime)) + 1, 2485 int(DateFromTime(utctime)), 2486 int(HourFromTime(utctime)), 2487 int(MinFromTime(utctime)), 2488 int(SecFromTime(utctime)), 2489 int(msFromTime(utctime))); 2490 } else { 2491 SprintfLiteral(buf, "%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.3dZ", 2492 int(YearFromTime(utctime)), 2493 int(MonthFromTime(utctime)) + 1, 2494 int(DateFromTime(utctime)), 2495 int(HourFromTime(utctime)), 2496 int(MinFromTime(utctime)), 2497 int(SecFromTime(utctime)), 2498 int(msFromTime(utctime))); 2499 } 2500 2501 JSString* str = NewStringCopyZ<CanGC>(cx, buf); 2502 if (!str) 2503 return false; 2504 args.rval().setString(str); 2505 return true; 2506 2507 } 2508 2509 static bool 2510 date_toISOString(JSContext* cx, unsigned argc, Value* vp) 2511 { 2512 CallArgs args = CallArgsFromVp(argc, vp); 2513 return CallNonGenericMethod<IsDate, date_toISOString_impl>(cx, args); 2514 } 2515 2516 /* ES5 15.9.5.44. */ 2517 static bool 2518 date_toJSON(JSContext* cx, unsigned argc, Value* vp) 2519 { 2520 CallArgs args = CallArgsFromVp(argc, vp); 2521 2522 /* Step 1. */ 2523 RootedObject obj(cx, ToObject(cx, args.thisv())); 2524 if (!obj) 2525 return false; 2526 2527 /* Step 2. */ 2528 RootedValue tv(cx, ObjectValue(*obj)); 2529 if (!ToPrimitive(cx, JSTYPE_NUMBER, &tv)) 2530 return false; 2531 2532 /* Step 3. */ 2533 if (tv.isDouble() && !IsFinite(tv.toDouble())) { 2534 args.rval().setNull(); 2535 return true; 2536 } 2537 2538 /* Step 4. */ 2539 RootedValue toISO(cx); 2540 if (!GetProperty(cx, obj, obj, cx->names().toISOString, &toISO)) 2541 return false; 2542 2543 /* Step 5. */ 2544 if (!IsCallable(toISO)) { 2545 JS_ReportErrorFlagsAndNumberASCII(cx, JSREPORT_ERROR, js::GetErrorMessage, nullptr, 2546 JSMSG_BAD_TOISOSTRING_PROP); 2547 return false; 2548 } 2549 2550 /* Step 6. */ 2551 return Call(cx, toISO, obj, args.rval()); 2552 } 2553 2554 /* Interface to PRMJTime date struct. */ 2555 static PRMJTime 2556 ToPRMJTime(double localTime) 2557 { 2558 double year = YearFromTime(localTime); 2559 2560 PRMJTime prtm; 2561 prtm.tm_usec = int32_t(msFromTime(localTime)) * 1000; 2562 prtm.tm_sec = int8_t(SecFromTime(localTime)); 2563 prtm.tm_min = int8_t(MinFromTime(localTime)); 2564 prtm.tm_hour = int8_t(HourFromTime(localTime)); 2565 prtm.tm_mday = int8_t(DateFromTime(localTime)); 2566 prtm.tm_mon = int8_t(MonthFromTime(localTime)); 2567 prtm.tm_wday = int8_t(WeekDay(localTime)); 2568 prtm.tm_year = year; 2569 prtm.tm_yday = int16_t(DayWithinYear(localTime, year)); 2570 2571 // XXX: DaylightSavingTA expects utc-time argument. 2572 prtm.tm_isdst = (DaylightSavingTA(localTime) != 0); 2573 2574 return prtm; 2575 } 2576 2577 enum class FormatSpec { 2578 DateTime, 2579 Date, 2580 Time 2581 }; 2582 2583 static bool 2584 FormatDate(JSContext* cx, double utcTime, FormatSpec format, MutableHandleValue rval) 2585 { 2586 JSString* str; 2587 if (!IsFinite(utcTime)) { 2588 str = NewStringCopyZ<CanGC>(cx, js_NaN_date_str); 2589 } else { 2590 MOZ_ASSERT(NumbersAreIdentical(TimeClip(utcTime).toDouble(), utcTime)); 2591 2592 double localTime = LocalTime(utcTime); 2593 2594 int offset = 0; 2595 char tzbuf[100]; 2596 bool usetz = false; 2597 if (format == FormatSpec::DateTime || format == FormatSpec::Time) { 2598 /* 2599 * Offset from GMT in minutes. The offset includes daylight 2600 * savings, if it applies. 2601 */ 2602 int minutes = (int) floor((localTime - utcTime) / msPerMinute); 2603 2604 /* Map 510 minutes to 0830 hours. */ 2605 offset = (minutes / 60) * 100 + minutes % 60; 2606 2607 /* 2608 * Print as "Wed Nov 05 19:38:03 GMT-0800 (PST) 1997". 2609 * 2610 * The TZA is printed as 'GMT-0800' rather than as 'PST' to avoid 2611 * operating-system dependence on strftime (which PRMJ_FormatTime 2612 * calls, for %Z only.) win32 prints PST as 2613 * 'Pacific Standard Time.' This way we always know what we're 2614 * getting, and can parse it if we produce it. The OS time zone 2615 * string is included as a comment. 2616 */ 2617 2618 /* get a time zone string from the OS to include as a comment. */ 2619 PRMJTime prtm = ToPRMJTime(utcTime); 2620 size_t tzlen = PRMJ_FormatTime(tzbuf, sizeof tzbuf, "(%Z)", &prtm); 2621 if (tzlen != 0) { 2622 /* 2623 * Decide whether to use the resulting time zone string. 2624 * 2625 * Reject it if it contains any non-ASCII, non-alphanumeric 2626 * characters. It's then likely in some other character 2627 * encoding, and we probably won't display it correctly. 2628 */ 2629 usetz = true; 2630 for (size_t i = 0; i < tzlen; i++) { 2631 char16_t c = tzbuf[i]; 2632 if (c > 127 || !(isalnum(c) || c == ' ' || c == '(' || c == ')' || c == '.')) { 2633 usetz = false; 2634 break; 2635 } 2636 } 2637 2638 /* Also reject it if it's not parenthesized or if it's '()'. */ 2639 if (tzbuf[0] != '(' || tzbuf[1] == ')') 2640 usetz = false; 2641 } 2642 } 2643 2644 char buf[100]; 2645 switch (format) { 2646 case FormatSpec::DateTime: 2647 /* Tue Oct 31 2000 09:41:40 GMT-0800 (PST) */ 2648 SprintfLiteral(buf, "%s %s %.2d %.4d %.2d:%.2d:%.2d GMT%+.4d%s%s", 2649 days[int(WeekDay(localTime))], 2650 months[int(MonthFromTime(localTime))], 2651 int(DateFromTime(localTime)), 2652 int(YearFromTime(localTime)), 2653 int(HourFromTime(localTime)), 2654 int(MinFromTime(localTime)), 2655 int(SecFromTime(localTime)), 2656 offset, 2657 usetz ? " " : "", 2658 usetz ? tzbuf : ""); 2659 break; 2660 case FormatSpec::Date: 2661 /* Tue Oct 31 2000 */ 2662 SprintfLiteral(buf, "%s %s %.2d %.4d", 2663 days[int(WeekDay(localTime))], 2664 months[int(MonthFromTime(localTime))], 2665 int(DateFromTime(localTime)), 2666 int(YearFromTime(localTime))); 2667 break; 2668 case FormatSpec::Time: 2669 /* 09:41:40 GMT-0800 (PST) */ 2670 SprintfLiteral(buf, "%.2d:%.2d:%.2d GMT%+.4d%s%s", 2671 int(HourFromTime(localTime)), 2672 int(MinFromTime(localTime)), 2673 int(SecFromTime(localTime)), 2674 offset, 2675 usetz ? " " : "", 2676 usetz ? tzbuf : ""); 2677 break; 2678 } 2679 2680 str = NewStringCopyZ<CanGC>(cx, buf); 2681 } 2682 2683 if (!str) 2684 return false; 2685 rval.setString(str); 2686 return true; 2687 } 2688 2689 static bool 2690 ToLocaleFormatHelper(JSContext* cx, HandleObject obj, const char* format, MutableHandleValue rval) 2691 { 2692 double utcTime = obj->as<DateObject>().UTCTime().toNumber(); 2693 2694 char buf[100]; 2695 if (!IsFinite(utcTime)) { 2696 strcpy(buf, js_NaN_date_str); 2697 } else { 2698 double localTime = LocalTime(utcTime); 2699 PRMJTime prtm = ToPRMJTime(localTime); 2700 2701 /* Let PRMJTime format it. */ 2702 size_t result_len = PRMJ_FormatTime(buf, sizeof buf, format, &prtm); 2703 2704 /* If it failed, default to toString. */ 2705 if (result_len == 0) 2706 return FormatDate(cx, utcTime, FormatSpec::DateTime, rval); 2707 2708 /* Hacked check against undesired 2-digit year 00/00/00 form. */ 2709 if (strcmp(format, "%x") == 0 && result_len >= 6 && 2710 /* Format %x means use OS settings, which may have 2-digit yr, so 2711 hack end of 3/11/22 or 11.03.22 or 11Mar22 to use 4-digit yr...*/ 2712 !isdigit(buf[result_len - 3]) && 2713 isdigit(buf[result_len - 2]) && isdigit(buf[result_len - 1]) && 2714 /* ...but not if starts with 4-digit year, like 2022/3/11. */ 2715 !(isdigit(buf[0]) && isdigit(buf[1]) && 2716 isdigit(buf[2]) && isdigit(buf[3]))) 2717 { 2718 int year = int(YearFromTime(localTime)); 2719 snprintf(buf + (result_len - 2), (sizeof buf) - (result_len - 2), "%d", year); 2720 } 2721 2722 } 2723 2724 if (cx->runtime()->localeCallbacks && cx->runtime()->localeCallbacks->localeToUnicode) 2725 return cx->runtime()->localeCallbacks->localeToUnicode(cx, buf, rval); 2726 2727 JSString* str = NewStringCopyZ<CanGC>(cx, buf); 2728 if (!str) 2729 return false; 2730 rval.setString(str); 2731 return true; 2732 } 2733 2734 #if !EXPOSE_INTL_API 2735 /* ES5 15.9.5.5. */ 2736 MOZ_ALWAYS_INLINE bool 2737 date_toLocaleString_impl(JSContext* cx, const CallArgs& args) 2738 { 2739 /* 2740 * Use '%#c' for windows, because '%c' is backward-compatible and non-y2k 2741 * with msvc; '%#c' requests that a full year be used in the result string. 2742 */ 2743 static const char format[] = 2744 #if defined(_WIN32) && !defined(__MWERKS__) 2745 "%#c" 2746 #else 2747 "%c" 2748 #endif 2749 ; 2750 2751 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); 2752 return ToLocaleFormatHelper(cx, dateObj, format, args.rval()); 2753 } 2754 2755 static bool 2756 date_toLocaleString(JSContext* cx, unsigned argc, Value* vp) 2757 { 2758 CallArgs args = CallArgsFromVp(argc, vp); 2759 return CallNonGenericMethod<IsDate, date_toLocaleString_impl>(cx, args); 2760 } 2761 2762 /* ES5 15.9.5.6. */ 2763 MOZ_ALWAYS_INLINE bool 2764 date_toLocaleDateString_impl(JSContext* cx, const CallArgs& args) 2765 { 2766 /* 2767 * Use '%#x' for windows, because '%x' is backward-compatible and non-y2k 2768 * with msvc; '%#x' requests that a full year be used in the result string. 2769 */ 2770 static const char format[] = 2771 #if defined(_WIN32) && !defined(__MWERKS__) 2772 "%#x" 2773 #else 2774 "%x" 2775 #endif 2776 ; 2777 2778 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); 2779 return ToLocaleFormatHelper(cx, dateObj, format, args.rval()); 2780 } 2781 2782 static bool 2783 date_toLocaleDateString(JSContext* cx, unsigned argc, Value* vp) 2784 { 2785 CallArgs args = CallArgsFromVp(argc, vp); 2786 return CallNonGenericMethod<IsDate, date_toLocaleDateString_impl>(cx, args); 2787 } 2788 2789 /* ES5 15.9.5.7. */ 2790 MOZ_ALWAYS_INLINE bool 2791 date_toLocaleTimeString_impl(JSContext* cx, const CallArgs& args) 2792 { 2793 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); 2794 return ToLocaleFormatHelper(cx, dateObj, "%X", args.rval()); 2795 } 2796 2797 static bool 2798 date_toLocaleTimeString(JSContext* cx, unsigned argc, Value* vp) 2799 { 2800 CallArgs args = CallArgsFromVp(argc, vp); 2801 return CallNonGenericMethod<IsDate, date_toLocaleTimeString_impl>(cx, args); 2802 } 2803 #endif /* !EXPOSE_INTL_API */ 2804 2805 MOZ_ALWAYS_INLINE bool 2806 date_toLocaleFormat_impl(JSContext* cx, const CallArgs& args) 2807 { 2808 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); 2809 2810 if (args.length() == 0) { 2811 /* 2812 * Use '%#c' for windows, because '%c' is backward-compatible and non-y2k 2813 * with msvc; '%#c' requests that a full year be used in the result string. 2814 */ 2815 static const char format[] = 2816 #if defined(_WIN32) && !defined(__MWERKS__) 2817 "%#c" 2818 #else 2819 "%c" 2820 #endif 2821 ; 2822 2823 return ToLocaleFormatHelper(cx, dateObj, format, args.rval()); 2824 } 2825 2826 RootedString fmt(cx, ToString<CanGC>(cx, args[0])); 2827 if (!fmt) 2828 return false; 2829 2830 JSAutoByteString fmtbytes(cx, fmt); 2831 if (!fmtbytes) 2832 return false; 2833 2834 return ToLocaleFormatHelper(cx, dateObj, fmtbytes.ptr(), args.rval()); 2835 } 2836 2837 static bool 2838 date_toLocaleFormat(JSContext* cx, unsigned argc, Value* vp) 2839 { 2840 CallArgs args = CallArgsFromVp(argc, vp); 2841 return CallNonGenericMethod<IsDate, date_toLocaleFormat_impl>(cx, args); 2842 } 2843 2844 /* ES5 15.9.5.4. */ 2845 MOZ_ALWAYS_INLINE bool 2846 date_toTimeString_impl(JSContext* cx, const CallArgs& args) 2847 { 2848 return FormatDate(cx, args.thisv().toObject().as<DateObject>().UTCTime().toNumber(), 2849 FormatSpec::Time, args.rval()); 2850 } 2851 2852 static bool 2853 date_toTimeString(JSContext* cx, unsigned argc, Value* vp) 2854 { 2855 CallArgs args = CallArgsFromVp(argc, vp); 2856 return CallNonGenericMethod<IsDate, date_toTimeString_impl>(cx, args); 2857 } 2858 2859 /* ES5 15.9.5.3. */ 2860 MOZ_ALWAYS_INLINE bool 2861 date_toDateString_impl(JSContext* cx, const CallArgs& args) 2862 { 2863 return FormatDate(cx, args.thisv().toObject().as<DateObject>().UTCTime().toNumber(), 2864 FormatSpec::Date, args.rval()); 2865 } 2866 2867 static bool 2868 date_toDateString(JSContext* cx, unsigned argc, Value* vp) 2869 { 2870 CallArgs args = CallArgsFromVp(argc, vp); 2871 return CallNonGenericMethod<IsDate, date_toDateString_impl>(cx, args); 2872 } 2873 2874 #if JS_HAS_TOSOURCE 2875 MOZ_ALWAYS_INLINE bool 2876 date_toSource_impl(JSContext* cx, const CallArgs& args) 2877 { 2878 StringBuffer sb(cx); 2879 if (!sb.append("(new Date(") || 2880 !NumberValueToStringBuffer(cx, args.thisv().toObject().as<DateObject>().UTCTime(), sb) || 2881 !sb.append("))")) 2882 { 2883 return false; 2884 } 2885 2886 JSString* str = sb.finishString(); 2887 if (!str) 2888 return false; 2889 args.rval().setString(str); 2890 return true; 2891 } 2892 2893 static bool 2894 date_toSource(JSContext* cx, unsigned argc, Value* vp) 2895 { 2896 CallArgs args = CallArgsFromVp(argc, vp); 2897 return CallNonGenericMethod<IsDate, date_toSource_impl>(cx, args); 2898 } 2899 #endif 2900 2901 MOZ_ALWAYS_INLINE bool 2902 IsObject(HandleValue v) 2903 { 2904 return v.isObject(); 2905 } 2906 2907 // ES6 20.3.4.41. 2908 MOZ_ALWAYS_INLINE bool 2909 date_toString_impl(JSContext* cx, const CallArgs& args) 2910 { 2911 // Step 1. 2912 RootedObject obj(cx, &args.thisv().toObject()); 2913 2914 // Step 2. 2915 ESClass cls; 2916 if (!GetBuiltinClass(cx, obj, &cls)) 2917 return false; 2918 2919 double tv; 2920 if (cls != ESClass::Date) { 2921 // Step 2. 2922 tv = GenericNaN(); 2923 } else { 2924 // Step 3. 2925 RootedValue unboxed(cx); 2926 if (!Unbox(cx, obj, &unboxed)) 2927 return false; 2928 2929 tv = unboxed.toNumber(); 2930 } 2931 2932 // Step 4. 2933 return FormatDate(cx, tv, FormatSpec::DateTime, args.rval()); 2934 } 2935 2936 bool 2937 date_toString(JSContext* cx, unsigned argc, Value* vp) 2938 { 2939 // Step 1. 2940 CallArgs args = CallArgsFromVp(argc, vp); 2941 return CallNonGenericMethod<IsObject, date_toString_impl>(cx, args); 2942 } 2943 2944 MOZ_ALWAYS_INLINE bool 2945 date_valueOf_impl(JSContext* cx, const CallArgs& args) 2946 { 2947 Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); 2948 args.rval().set(dateObj->UTCTime()); 2949 return true; 2950 } 2951 2952 bool 2953 js::date_valueOf(JSContext* cx, unsigned argc, Value* vp) 2954 { 2955 CallArgs args = CallArgsFromVp(argc, vp); 2956 return CallNonGenericMethod<IsDate, date_valueOf_impl>(cx, args); 2957 } 2958 2959 // ES6 20.3.4.45 Date.prototype[@@toPrimitive] 2960 static bool 2961 date_toPrimitive(JSContext* cx, unsigned argc, Value* vp) 2962 { 2963 CallArgs args = CallArgsFromVp(argc, vp); 2964 2965 // Steps 1-2. 2966 if (!args.thisv().isObject()) { 2967 ReportIncompatible(cx, args); 2968 return false; 2969 } 2970 2971 // Steps 3-5. 2972 JSType hint; 2973 if (!GetFirstArgumentAsTypeHint(cx, args, &hint)) 2974 return false; 2975 if (hint == JSTYPE_VOID) 2976 hint = JSTYPE_STRING; 2977 2978 args.rval().set(args.thisv()); 2979 RootedObject obj(cx, &args.thisv().toObject()); 2980 return OrdinaryToPrimitive(cx, obj, hint, args.rval()); 2981 } 2982 2983 static const JSFunctionSpec date_static_methods[] = { 2984 JS_FN("UTC", date_UTC, 7,0), 2985 JS_FN("parse", date_parse, 1,0), 2986 JS_FN("now", date_now, 0,0), 2987 JS_FS_END 2988 }; 2989 2990 static const JSFunctionSpec date_methods[] = { 2991 JS_FN("getTime", date_getTime, 0,0), 2992 JS_FN("getTimezoneOffset", date_getTimezoneOffset, 0,0), 2993 JS_FN("getYear", date_getYear, 0,0), 2994 JS_FN("getFullYear", date_getFullYear, 0,0), 2995 JS_FN("getUTCFullYear", date_getUTCFullYear, 0,0), 2996 JS_FN("getMonth", date_getMonth, 0,0), 2997 JS_FN("getUTCMonth", date_getUTCMonth, 0,0), 2998 JS_FN("getDate", date_getDate, 0,0), 2999 JS_FN("getUTCDate", date_getUTCDate, 0,0), 3000 JS_FN("getDay", date_getDay, 0,0), 3001 JS_FN("getUTCDay", date_getUTCDay, 0,0), 3002 JS_FN("getHours", date_getHours, 0,0), 3003 JS_FN("getUTCHours", date_getUTCHours, 0,0), 3004 JS_FN("getMinutes", date_getMinutes, 0,0), 3005 JS_FN("getUTCMinutes", date_getUTCMinutes, 0,0), 3006 JS_FN("getSeconds", date_getUTCSeconds, 0,0), 3007 JS_FN("getUTCSeconds", date_getUTCSeconds, 0,0), 3008 JS_FN("getMilliseconds", date_getUTCMilliseconds, 0,0), 3009 JS_FN("getUTCMilliseconds", date_getUTCMilliseconds, 0,0), 3010 JS_FN("setTime", date_setTime, 1,0), 3011 JS_FN("setYear", date_setYear, 1,0), 3012 JS_FN("setFullYear", date_setFullYear, 3,0), 3013 JS_FN("setUTCFullYear", date_setUTCFullYear, 3,0), 3014 JS_FN("setMonth", date_setMonth, 2,0), 3015 JS_FN("setUTCMonth", date_setUTCMonth, 2,0), 3016 JS_FN("setDate", date_setDate, 1,0), 3017 JS_FN("setUTCDate", date_setUTCDate, 1,0), 3018 JS_FN("setHours", date_setHours, 4,0), 3019 JS_FN("setUTCHours", date_setUTCHours, 4,0), 3020 JS_FN("setMinutes", date_setMinutes, 3,0), 3021 JS_FN("setUTCMinutes", date_setUTCMinutes, 3,0), 3022 JS_FN("setSeconds", date_setSeconds, 2,0), 3023 JS_FN("setUTCSeconds", date_setUTCSeconds, 2,0), 3024 JS_FN("setMilliseconds", date_setMilliseconds, 1,0), 3025 JS_FN("setUTCMilliseconds", date_setUTCMilliseconds, 1,0), 3026 JS_FN("toUTCString", date_toGMTString, 0,0), 3027 JS_FN("toLocaleFormat", date_toLocaleFormat, 0,0), 3028 #if EXPOSE_INTL_API 3029 JS_SELF_HOSTED_FN(js_toLocaleString_str, "Date_toLocaleString", 0,0), 3030 JS_SELF_HOSTED_FN("toLocaleDateString", "Date_toLocaleDateString", 0,0), 3031 JS_SELF_HOSTED_FN("toLocaleTimeString", "Date_toLocaleTimeString", 0,0), 3032 #else 3033 JS_FN(js_toLocaleString_str, date_toLocaleString, 0,0), 3034 JS_FN("toLocaleDateString", date_toLocaleDateString, 0,0), 3035 JS_FN("toLocaleTimeString", date_toLocaleTimeString, 0,0), 3036 #endif 3037 JS_FN("toDateString", date_toDateString, 0,0), 3038 JS_FN("toTimeString", date_toTimeString, 0,0), 3039 JS_FN("toISOString", date_toISOString, 0,0), 3040 JS_FN(js_toJSON_str, date_toJSON, 1,0), 3041 #if JS_HAS_TOSOURCE 3042 JS_FN(js_toSource_str, date_toSource, 0,0), 3043 #endif 3044 JS_FN(js_toString_str, date_toString, 0,0), 3045 JS_FN(js_valueOf_str, date_valueOf, 0,0), 3046 JS_SYM_FN(toPrimitive, date_toPrimitive, 1,JSPROP_READONLY), 3047 JS_FS_END 3048 }; 3049 3050 static bool 3051 NewDateObject(JSContext* cx, const CallArgs& args, ClippedTime t) 3052 { 3053 MOZ_ASSERT(args.isConstructing()); 3054 3055 RootedObject proto(cx); 3056 RootedObject newTarget(cx, &args.newTarget().toObject()); 3057 if (!GetPrototypeFromConstructor(cx, newTarget, &proto)) 3058 return false; 3059 3060 JSObject* obj = NewDateObjectMsec(cx, t, proto); 3061 if (!obj) 3062 return false; 3063 3064 args.rval().setObject(*obj); 3065 return true; 3066 } 3067 3068 static bool 3069 ToDateString(JSContext* cx, const CallArgs& args, ClippedTime t) 3070 { 3071 return FormatDate(cx, t.toDouble(), FormatSpec::DateTime, args.rval()); 3072 } 3073 3074 static bool 3075 DateNoArguments(JSContext* cx, const CallArgs& args) 3076 { 3077 MOZ_ASSERT(args.length() == 0); 3078 3079 ClippedTime now = NowAsMillis(); 3080 3081 if (args.isConstructing()) 3082 return NewDateObject(cx, args, now); 3083 3084 return ToDateString(cx, args, now); 3085 } 3086 3087 static bool 3088 DateOneArgument(JSContext* cx, const CallArgs& args) 3089 { 3090 MOZ_ASSERT(args.length() == 1); 3091 3092 if (args.isConstructing()) { 3093 if (args[0].isObject()) { 3094 RootedObject obj(cx, &args[0].toObject()); 3095 3096 ESClass cls; 3097 if (!GetBuiltinClass(cx, obj, &cls)) 3098 return false; 3099 3100 if (cls == ESClass::Date) { 3101 RootedValue unboxed(cx); 3102 if (!Unbox(cx, obj, &unboxed)) 3103 return false; 3104 3105 return NewDateObject(cx, args, TimeClip(unboxed.toNumber())); 3106 } 3107 } 3108 3109 if (!ToPrimitive(cx, args[0])) 3110 return false; 3111 3112 ClippedTime t; 3113 if (args[0].isString()) { 3114 JSLinearString* linearStr = args[0].toString()->ensureLinear(cx); 3115 if (!linearStr) 3116 return false; 3117 3118 if (!ParseDate(linearStr, &t)) 3119 t = ClippedTime::invalid(); 3120 } else { 3121 double d; 3122 if (!ToNumber(cx, args[0], &d)) 3123 return false; 3124 t = TimeClip(d); 3125 } 3126 3127 return NewDateObject(cx, args, t); 3128 } 3129 3130 return ToDateString(cx, args, NowAsMillis()); 3131 } 3132 3133 static bool 3134 DateMultipleArguments(JSContext* cx, const CallArgs& args) 3135 { 3136 MOZ_ASSERT(args.length() >= 2); 3137 3138 // Step 3. 3139 if (args.isConstructing()) { 3140 // Steps 3a-b. 3141 double y; 3142 if (!ToNumber(cx, args[0], &y)) 3143 return false; 3144 3145 // Steps 3c-d. 3146 double m; 3147 if (!ToNumber(cx, args[1], &m)) 3148 return false; 3149 3150 // Steps 3e-f. 3151 double dt; 3152 if (args.length() >= 3) { 3153 if (!ToNumber(cx, args[2], &dt)) 3154 return false; 3155 } else { 3156 dt = 1; 3157 } 3158 3159 // Steps 3g-h. 3160 double h; 3161 if (args.length() >= 4) { 3162 if (!ToNumber(cx, args[3], &h)) 3163 return false; 3164 } else { 3165 h = 0; 3166 } 3167 3168 // Steps 3i-j. 3169 double min; 3170 if (args.length() >= 5) { 3171 if (!ToNumber(cx, args[4], &min)) 3172 return false; 3173 } else { 3174 min = 0; 3175 } 3176 3177 // Steps 3k-l. 3178 double s; 3179 if (args.length() >= 6) { 3180 if (!ToNumber(cx, args[5], &s)) 3181 return false; 3182 } else { 3183 s = 0; 3184 } 3185 3186 // Steps 3m-n. 3187 double milli; 3188 if (args.length() >= 7) { 3189 if (!ToNumber(cx, args[6], &milli)) 3190 return false; 3191 } else { 3192 milli = 0; 3193 } 3194 3195 // Step 3o. 3196 double yr = y; 3197 if (!IsNaN(y)) { 3198 double yint = ToInteger(y); 3199 if (0 <= yint && yint <= 99) 3200 yr = 1900 + yint; 3201 } 3202 3203 // Step 3p. 3204 double finalDate = MakeDate(MakeDay(yr, m, dt), MakeTime(h, min, s, milli)); 3205 3206 // Steps 3q-t. 3207 return NewDateObject(cx, args, TimeClip(UTC(finalDate))); 3208 } 3209 3210 return ToDateString(cx, args, NowAsMillis()); 3211 } 3212 3213 bool 3214 js::DateConstructor(JSContext* cx, unsigned argc, Value* vp) 3215 { 3216 CallArgs args = CallArgsFromVp(argc, vp); 3217 3218 if (args.length() == 0) 3219 return DateNoArguments(cx, args); 3220 3221 if (args.length() == 1) 3222 return DateOneArgument(cx, args); 3223 3224 return DateMultipleArguments(cx, args); 3225 } 3226 3227 // ES6 final draft 20.3.4. 3228 static JSObject* 3229 CreateDatePrototype(JSContext* cx, JSProtoKey key) 3230 { 3231 return cx->global()->createBlankPrototype(cx, &DateObject::protoClass_); 3232 } 3233 3234 static bool 3235 FinishDateClassInit(JSContext* cx, HandleObject ctor, HandleObject proto) 3236 { 3237 /* 3238 * Date.prototype.toGMTString has the same initial value as 3239 * Date.prototype.toUTCString. 3240 */ 3241 RootedValue toUTCStringFun(cx); 3242 RootedId toUTCStringId(cx, NameToId(cx->names().toUTCString)); 3243 RootedId toGMTStringId(cx, NameToId(cx->names().toGMTString)); 3244 return NativeGetProperty(cx, proto.as<NativeObject>(), toUTCStringId, &toUTCStringFun) && 3245 NativeDefineProperty(cx, proto.as<NativeObject>(), toGMTStringId, toUTCStringFun, 3246 nullptr, nullptr, 0); 3247 } 3248 3249 static const ClassSpec DateObjectClassSpec = { 3250 GenericCreateConstructor<DateConstructor, 7, gc::AllocKind::FUNCTION>, 3251 CreateDatePrototype, 3252 date_static_methods, 3253 nullptr, 3254 date_methods, 3255 nullptr, 3256 FinishDateClassInit 3257 }; 3258 3259 const Class DateObject::class_ = { 3260 js_Date_str, 3261 JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) | 3262 JSCLASS_HAS_CACHED_PROTO(JSProto_Date), 3263 JS_NULL_CLASS_OPS, 3264 &DateObjectClassSpec 3265 }; 3266 3267 static const ClassSpec DateObjectProtoClassSpec = { 3268 DELEGATED_CLASSSPEC(DateObject::class_.spec), 3269 nullptr, 3270 nullptr, 3271 nullptr, 3272 nullptr, 3273 nullptr, 3274 nullptr, 3275 ClassSpec::IsDelegated 3276 }; 3277 3278 const Class DateObject::protoClass_ = { 3279 js_Object_str, 3280 JSCLASS_HAS_CACHED_PROTO(JSProto_Date), 3281 JS_NULL_CLASS_OPS, 3282 &DateObjectProtoClassSpec 3283 }; 3284 3285 JSObject* 3286 js::NewDateObjectMsec(JSContext* cx, ClippedTime t, HandleObject proto /* = nullptr */) 3287 { 3288 JSObject* obj = NewObjectWithClassProto(cx, &DateObject::class_, proto); 3289 if (!obj) 3290 return nullptr; 3291 obj->as<DateObject>().setUTCTime(t); 3292 return obj; 3293 } 3294 3295 JS_FRIEND_API(JSObject*) 3296 js::NewDateObject(JSContext* cx, int year, int mon, int mday, 3297 int hour, int min, int sec) 3298 { 3299 MOZ_ASSERT(mon < 12); 3300 double msec_time = MakeDate(MakeDay(year, mon, mday), MakeTime(hour, min, sec, 0.0)); 3301 return NewDateObjectMsec(cx, TimeClip(UTC(msec_time))); 3302 } 3303 3304 JS_FRIEND_API(bool) 3305 js::DateIsValid(JSContext* cx, HandleObject obj, bool* isValid) 3306 { 3307 ESClass cls; 3308 if (!GetBuiltinClass(cx, obj, &cls)) 3309 return false; 3310 3311 if (cls != ESClass::Date) { 3312 *isValid = false; 3313 return true; 3314 } 3315 3316 RootedValue unboxed(cx); 3317 if (!Unbox(cx, obj, &unboxed)) 3318 return false; 3319 3320 *isValid = !IsNaN(unboxed.toNumber()); 3321 return true; 3322 } 3323 3324 JS_FRIEND_API(bool) 3325 js::DateGetMsecSinceEpoch(JSContext* cx, HandleObject obj, double* msecsSinceEpoch) 3326 { 3327 ESClass cls; 3328 if (!GetBuiltinClass(cx, obj, &cls)) 3329 return false; 3330 3331 if (cls != ESClass::Date) { 3332 *msecsSinceEpoch = 0; 3333 return true; 3334 } 3335 3336 RootedValue unboxed(cx); 3337 if (!Unbox(cx, obj, &unboxed)) 3338 return false; 3339 3340 *msecsSinceEpoch = unboxed.toNumber(); 3341 return true; 3342 } 3343