1 /* 2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.nashorn.internal.objects; 27 28 import static java.lang.Double.NaN; 29 import static java.lang.Double.isInfinite; 30 import static java.lang.Double.isNaN; 31 import static jdk.nashorn.internal.runtime.ECMAErrors.rangeError; 32 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 33 34 import java.util.Locale; 35 import java.util.TimeZone; 36 import java.util.concurrent.Callable; 37 import jdk.nashorn.internal.objects.annotations.Attribute; 38 import jdk.nashorn.internal.objects.annotations.Constructor; 39 import jdk.nashorn.internal.objects.annotations.Function; 40 import jdk.nashorn.internal.objects.annotations.ScriptClass; 41 import jdk.nashorn.internal.objects.annotations.SpecializedConstructor; 42 import jdk.nashorn.internal.objects.annotations.Where; 43 import jdk.nashorn.internal.parser.DateParser; 44 import jdk.nashorn.internal.runtime.ConsString; 45 import jdk.nashorn.internal.runtime.JSType; 46 import jdk.nashorn.internal.runtime.PropertyMap; 47 import jdk.nashorn.internal.runtime.ScriptEnvironment; 48 import jdk.nashorn.internal.runtime.ScriptObject; 49 import jdk.nashorn.internal.runtime.ScriptRuntime; 50 import jdk.nashorn.internal.runtime.linker.Bootstrap; 51 import jdk.nashorn.internal.runtime.linker.InvokeByName; 52 53 /** 54 * ECMA 15.9 Date Objects 55 * 56 */ 57 @ScriptClass("Date") 58 public final class NativeDate extends ScriptObject { 59 60 private static final String INVALID_DATE = "Invalid Date"; 61 62 private static final int YEAR = 0; 63 private static final int MONTH = 1; 64 private static final int DAY = 2; 65 private static final int HOUR = 3; 66 private static final int MINUTE = 4; 67 private static final int SECOND = 5; 68 private static final int MILLISECOND = 6; 69 70 private static final int FORMAT_DATE_TIME = 0; 71 private static final int FORMAT_DATE = 1; 72 private static final int FORMAT_TIME = 2; 73 private static final int FORMAT_LOCAL_DATE_TIME = 3; 74 private static final int FORMAT_LOCAL_DATE = 4; 75 private static final int FORMAT_LOCAL_TIME = 5; 76 77 // Constants defined in ECMA 15.9.1.10 78 private static final int hoursPerDay = 24; 79 private static final int minutesPerHour = 60; 80 private static final int secondsPerMinute = 60; 81 private static final int msPerSecond = 1_000; 82 private static final int msPerMinute = 60_000; 83 private static final double msPerHour = 3_600_000; 84 private static final double msPerDay = 86_400_000; 85 86 private static int[][] firstDayInMonth = { 87 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}, // normal year 88 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335} // leap year 89 }; 90 91 private static String[] weekDays = { 92 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" 93 }; 94 95 private static String[] months = { 96 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 97 }; 98 99 private static final Object TO_ISO_STRING = new Object(); 100 101 private static InvokeByName getTO_ISO_STRING() { 102 return Global.instance().getInvokeByName(TO_ISO_STRING, 103 new Callable<InvokeByName>() { 104 @Override 105 public InvokeByName call() { 106 return new InvokeByName("toISOString", ScriptObject.class, Object.class, Object.class); 107 } 108 }); 109 } 110 111 private double time; 112 private final TimeZone timezone; 113 114 // initialized by nasgen 115 private static PropertyMap $nasgenmap$; 116 117 static PropertyMap getInitialMap() { 118 return $nasgenmap$; 119 } 120 121 private NativeDate(final double time, final ScriptObject proto, final PropertyMap map) { 122 super(proto, map); 123 final ScriptEnvironment env = Global.getEnv(); 124 125 this.time = time; 126 this.timezone = env._timezone; 127 } 128 129 NativeDate(final double time, final Global global) { 130 this(time, global.getDatePrototype(), getInitialMap()); 131 } 132 133 private NativeDate (final double time) { 134 this(time, Global.instance()); 135 } 136 137 private NativeDate() { 138 this(System.currentTimeMillis()); 139 } 140 141 @Override 142 public String getClassName() { 143 return "Date"; 144 } 145 146 // ECMA 8.12.8 [[DefaultValue]] (hint) 147 @Override 148 public Object getDefaultValue(final Class<?> hint) { 149 // When the [[DefaultValue]] internal method of O is called with no hint, 150 // then it behaves as if the hint were Number, unless O is a Date object 151 // in which case it behaves as if the hint were String. 152 return super.getDefaultValue(hint == null ? String.class : hint); 153 } 154 155 /** 156 * Constructor - ECMA 15.9.3.1 new Date 157 * 158 * @param isNew is this Date constructed with the new operator 159 * @param self self references 160 * @return Date representing now 161 */ 162 @SpecializedConstructor 163 public static Object construct(final boolean isNew, final Object self) { 164 final NativeDate result = new NativeDate(); 165 return isNew ? result : toStringImpl(result, FORMAT_DATE_TIME); 166 } 167 168 /** 169 * Constructor - ECMA 15.9.3.1 new Date (year, month [, date [, hours [, minutes [, seconds [, ms ] ] ] ] ] ) 170 * 171 * @param isNew is this Date constructed with the new operator 172 * @param self self reference 173 * @param args arguments 174 * @return new Date 175 */ 176 @Constructor(arity = 7) 177 public static Object construct(final boolean isNew, final Object self, final Object... args) { 178 if (! isNew) { 179 return toStringImpl(new NativeDate(), FORMAT_DATE_TIME); 180 } 181 182 NativeDate result; 183 switch (args.length) { 184 case 0: 185 result = new NativeDate(); 186 break; 187 188 case 1: 189 double num; 190 final Object arg = JSType.toPrimitive(args[0]); 191 if (arg instanceof String || arg instanceof ConsString) { 192 num = parseDateString(arg.toString()); 193 } else { 194 num = timeClip(JSType.toNumber(args[0])); 195 } 196 result = new NativeDate(num); 197 break; 198 199 default: 200 result = new NativeDate(0); 201 final double[] d = convertCtorArgs(args); 202 if (d == null) { 203 result.setTime(Double.NaN); 204 } else { 205 final double time = timeClip(utc(makeDate(d), result.getTimeZone())); 206 result.setTime(time); 207 } 208 break; 209 } 210 211 return result; 212 } 213 214 @Override 215 public String safeToString() { 216 final String str = isValidDate() ? toISOStringImpl(this) : INVALID_DATE; 217 return "[Date " + str + "]"; 218 } 219 220 @Override 221 public String toString() { 222 return isValidDate() ? toString(this).toString() : INVALID_DATE; 223 } 224 225 /** 226 * ECMA 15.9.4.2 Date.parse (string) 227 * 228 * @param self self reference 229 * @param string string to parse as date 230 * @return Date interpreted from the string, or NaN for illegal values 231 */ 232 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 233 public static Object parse(final Object self, final Object string) { 234 return parseDateString(JSType.toString(string)); 235 } 236 237 /** 238 * ECMA 15.9.4.3 Date.UTC (year, month [, date [, hours [, minutes [, seconds [, ms ] ] ] ] ] ) 239 * 240 * @param self self reference 241 * @param args mandatory args are year, month. Optional are date, hours, minutes, seconds and milliseconds 242 * @return a time clip according to the ECMA specification 243 */ 244 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 7, where = Where.CONSTRUCTOR) 245 public static Object UTC(final Object self, final Object... args) { 246 final NativeDate nd = new NativeDate(0); 247 final double[] d = convertCtorArgs(args); 248 final double time = d == null ? Double.NaN : timeClip(makeDate(d)); 249 nd.setTime(time); 250 return time; 251 } 252 253 /** 254 * ECMA 15.9.4.4 Date.now ( ) 255 * 256 * @param self self reference 257 * @return a Date that points to the current moment in time 258 */ 259 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 260 public static Object now(final Object self) { 261 return (double)System.currentTimeMillis(); 262 } 263 264 /** 265 * ECMA 15.9.5.2 Date.prototype.toString ( ) 266 * 267 * @param self self reference 268 * @return string value that represents the Date in the current time zone 269 */ 270 @Function(attributes = Attribute.NOT_ENUMERABLE) 271 public static Object toString(final Object self) { 272 return toStringImpl(self, FORMAT_DATE_TIME); 273 } 274 275 /** 276 * ECMA 15.9.5.3 Date.prototype.toDateString ( ) 277 * 278 * @param self self reference 279 * @return string value with the "date" part of the Date in the current time zone 280 */ 281 @Function(attributes = Attribute.NOT_ENUMERABLE) 282 public static Object toDateString(final Object self) { 283 return toStringImpl(self, FORMAT_DATE); 284 } 285 286 /** 287 * ECMA 15.9.5.4 Date.prototype.toTimeString ( ) 288 * 289 * @param self self reference 290 * @return string value with "time" part of Date in the current time zone 291 */ 292 @Function(attributes = Attribute.NOT_ENUMERABLE) 293 public static Object toTimeString(final Object self) { 294 return toStringImpl(self, FORMAT_TIME); 295 } 296 297 /** 298 * ECMA 15.9.5.5 Date.prototype.toLocaleString ( ) 299 * 300 * @param self self reference 301 * @return string value that represents the Data in the current time zone and locale 302 */ 303 @Function(attributes = Attribute.NOT_ENUMERABLE) 304 public static Object toLocaleString(final Object self) { 305 return toStringImpl(self, FORMAT_LOCAL_DATE_TIME); 306 } 307 308 /** 309 * ECMA 15.9.5.6 Date.prototype.toLocaleDateString ( ) 310 * 311 * @param self self reference 312 * @return string value with the "date" part of the Date in the current time zone and locale 313 */ 314 @Function(attributes = Attribute.NOT_ENUMERABLE) 315 public static Object toLocaleDateString(final Object self) { 316 return toStringImpl(self, FORMAT_LOCAL_DATE); 317 } 318 319 /** 320 * ECMA 15.9.5.7 Date.prototype.toLocaleTimeString ( ) 321 * 322 * @param self self reference 323 * @return string value with the "time" part of Date in the current time zone and locale 324 */ 325 @Function(attributes = Attribute.NOT_ENUMERABLE) 326 public static Object toLocaleTimeString(final Object self) { 327 return toStringImpl(self, FORMAT_LOCAL_TIME); 328 } 329 330 /** 331 * ECMA 15.9.5.8 Date.prototype.valueOf ( ) 332 * 333 * @param self self reference 334 * @return valueOf - a number which is this time value 335 */ 336 @Function(attributes = Attribute.NOT_ENUMERABLE) 337 public static Object valueOf(final Object self) { 338 final NativeDate nd = getNativeDate(self); 339 return (nd != null) ? nd.getTime() : Double.NaN; 340 } 341 342 /** 343 * ECMA 15.9.5.9 Date.prototype.getTime ( ) 344 * 345 * @param self self reference 346 * @return time 347 */ 348 @Function(attributes = Attribute.NOT_ENUMERABLE) 349 public static Object getTime(final Object self) { 350 final NativeDate nd = getNativeDate(self); 351 return (nd != null) ? nd.getTime() : Double.NaN; 352 } 353 354 /** 355 * ECMA 15.9.5.10 Date.prototype.getFullYear ( ) 356 * 357 * @param self self reference 358 * @return full year 359 */ 360 @Function(attributes = Attribute.NOT_ENUMERABLE) 361 public static Object getFullYear(final Object self) { 362 return getField(self, YEAR); 363 } 364 365 /** 366 * ECMA 15.9.5.11 Date.prototype.getUTCFullYear( ) 367 * 368 * @param self self reference 369 * @return UTC full year 370 */ 371 @Function(attributes = Attribute.NOT_ENUMERABLE) 372 public static Object getUTCFullYear(final Object self) { 373 return getUTCField(self, YEAR); 374 } 375 376 /** 377 * B.2.4 Date.prototype.getYear ( ) 378 * 379 * @param self self reference 380 * @return year 381 */ 382 @Function(attributes = Attribute.NOT_ENUMERABLE) 383 public static Object getYear(final Object self) { 384 final NativeDate nd = getNativeDate(self); 385 return (nd != null && nd.isValidDate()) ? (yearFromTime(nd.getLocalTime()) - 1900) : Double.NaN; 386 } 387 388 /** 389 * ECMA 15.9.5.12 Date.prototype.getMonth ( ) 390 * 391 * @param self self reference 392 * @return month 393 */ 394 @Function(attributes = Attribute.NOT_ENUMERABLE) 395 public static Object getMonth(final Object self) { 396 return getField(self, MONTH); 397 } 398 399 /** 400 * ECMA 15.9.5.13 Date.prototype.getUTCMonth ( ) 401 * 402 * @param self self reference 403 * @return UTC month 404 */ 405 @Function(attributes = Attribute.NOT_ENUMERABLE) 406 public static Object getUTCMonth(final Object self) { 407 return getUTCField(self, MONTH); 408 } 409 410 /** 411 * ECMA 15.9.5.14 Date.prototype.getDate ( ) 412 * 413 * @param self self reference 414 * @return date 415 */ 416 @Function(attributes = Attribute.NOT_ENUMERABLE) 417 public static Object getDate(final Object self) { 418 return getField(self, DAY); 419 } 420 421 /** 422 * ECMA 15.9.5.15 Date.prototype.getUTCDate ( ) 423 * 424 * @param self self reference 425 * @return UTC Date 426 */ 427 @Function(attributes = Attribute.NOT_ENUMERABLE) 428 public static Object getUTCDate(final Object self) { 429 return getUTCField(self, DAY); 430 } 431 432 /** 433 * ECMA 15.9.5.16 Date.prototype.getDay ( ) 434 * 435 * @param self self reference 436 * @return day 437 */ 438 @Function(attributes = Attribute.NOT_ENUMERABLE) 439 public static Object getDay(final Object self) { 440 final NativeDate nd = getNativeDate(self); 441 return (nd != null && nd.isValidDate()) ? weekDay(nd.getLocalTime()) : Double.NaN; 442 } 443 444 /** 445 * ECMA 15.9.5.17 Date.prototype.getUTCDay ( ) 446 * 447 * @param self self reference 448 * @return UTC day 449 */ 450 @Function(attributes = Attribute.NOT_ENUMERABLE) 451 public static Object getUTCDay(final Object self) { 452 final NativeDate nd = getNativeDate(self); 453 return (nd != null && nd.isValidDate()) ? weekDay(nd.getTime()) : Double.NaN; 454 } 455 456 /** 457 * ECMA 15.9.5.18 Date.prototype.getHours ( ) 458 * 459 * @param self self reference 460 * @return hours 461 */ 462 @Function(attributes = Attribute.NOT_ENUMERABLE) 463 public static Object getHours(final Object self) { 464 return getField(self, HOUR); 465 } 466 467 /** 468 * ECMA 15.9.5.19 Date.prototype.getUTCHours ( ) 469 * 470 * @param self self reference 471 * @return UTC hours 472 */ 473 @Function(attributes = Attribute.NOT_ENUMERABLE) 474 public static Object getUTCHours(final Object self) { 475 return getUTCField(self, HOUR); 476 } 477 478 /** 479 * ECMA 15.9.5.20 Date.prototype.getMinutes ( ) 480 * 481 * @param self self reference 482 * @return minutes 483 */ 484 @Function(attributes = Attribute.NOT_ENUMERABLE) 485 public static Object getMinutes(final Object self) { 486 return getField(self, MINUTE); 487 } 488 489 /** 490 * ECMA 15.9.5.21 Date.prototype.getUTCMinutes ( ) 491 * 492 * @param self self reference 493 * @return UTC minutes 494 */ 495 @Function(attributes = Attribute.NOT_ENUMERABLE) 496 public static Object getUTCMinutes(final Object self) { 497 return getUTCField(self, MINUTE); 498 } 499 500 /** 501 * ECMA 15.9.5.22 Date.prototype.getSeconds ( ) 502 * 503 * @param self self reference 504 * @return seconds 505 */ 506 @Function(attributes = Attribute.NOT_ENUMERABLE) 507 public static Object getSeconds(final Object self) { 508 return getField(self, SECOND); 509 } 510 511 /** 512 * ECMA 15.9.5.23 Date.prototype.getUTCSeconds ( ) 513 * 514 * @param self self reference 515 * @return UTC seconds 516 */ 517 @Function(attributes = Attribute.NOT_ENUMERABLE) 518 public static Object getUTCSeconds(final Object self) { 519 return getUTCField(self, SECOND); 520 } 521 522 /** 523 * ECMA 15.9.5.24 Date.prototype.getMilliseconds ( ) 524 * 525 * @param self self reference 526 * @return milliseconds 527 */ 528 @Function(attributes = Attribute.NOT_ENUMERABLE) 529 public static Object getMilliseconds(final Object self) { 530 return getField(self, MILLISECOND); 531 } 532 533 /** 534 * ECMA 15.9.5.25 Date.prototype.getUTCMilliseconds ( ) 535 * 536 * @param self self reference 537 * @return UTC milliseconds 538 */ 539 @Function(attributes = Attribute.NOT_ENUMERABLE) 540 public static Object getUTCMilliseconds(final Object self) { 541 return getUTCField(self, MILLISECOND); 542 } 543 544 /** 545 * ECMA 15.9.5.26 Date.prototype.getTimezoneOffset ( ) 546 * 547 * @param self self reference 548 * @return time zone offset or NaN if N/A 549 */ 550 @Function(attributes = Attribute.NOT_ENUMERABLE) 551 public static Object getTimezoneOffset(final Object self) { 552 final NativeDate nd = getNativeDate(self); 553 if (nd != null && nd.isValidDate()) { 554 final long msec = (long) nd.getTime(); 555 return - nd.getTimeZone().getOffset(msec) / msPerMinute; 556 } 557 return Double.NaN; 558 } 559 560 /** 561 * ECMA 15.9.5.27 Date.prototype.setTime (time) 562 * 563 * @param self self reference 564 * @param time time 565 * @return time 566 */ 567 @Function(attributes = Attribute.NOT_ENUMERABLE) 568 public static Object setTime(final Object self, final Object time) { 569 final NativeDate nd = getNativeDate(self); 570 final double num = timeClip(JSType.toNumber(time)); 571 nd.setTime(num); 572 return num; 573 } 574 575 /** 576 * ECMA 15.9.5.28 Date.prototype.setMilliseconds (ms) 577 * 578 * @param self self reference 579 * @param args milliseconds 580 * @return time 581 */ 582 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) 583 public static Object setMilliseconds(final Object self, final Object... args) { 584 final NativeDate nd = getNativeDate(self); 585 setFields(nd, MILLISECOND, args, true); 586 return nd.getTime(); 587 } 588 589 /** 590 * ECMA 15.9.5.29 Date.prototype.setUTCMilliseconds (ms) 591 * 592 * @param self self reference 593 * @param args utc milliseconds 594 * @return time 595 */ 596 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) 597 public static Object setUTCMilliseconds(final Object self, final Object... args) { 598 final NativeDate nd = getNativeDate(self); 599 setFields(nd, MILLISECOND, args, false); 600 return nd.getTime(); 601 } 602 603 /** 604 * ECMA 15.9.5.30 Date.prototype.setSeconds (sec [, ms ] ) 605 * 606 * @param self self reference 607 * @param args seconds (milliseconds optional second argument) 608 * @return time 609 */ 610 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2) 611 public static Object setSeconds(final Object self, final Object... args) { 612 final NativeDate nd = getNativeDate(self); 613 setFields(nd, SECOND, args, true); 614 return nd.getTime(); 615 } 616 617 /** 618 * ECMA 15.9.5.31 Date.prototype.setUTCSeconds (sec [, ms ] ) 619 * 620 * @param self self reference 621 * @param args UTC seconds (milliseconds optional second argument) 622 * @return time 623 */ 624 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2) 625 public static Object setUTCSeconds(final Object self, final Object... args) { 626 final NativeDate nd = getNativeDate(self); 627 setFields(nd, SECOND, args, false); 628 return nd.getTime(); 629 } 630 631 /** 632 * ECMA 15.9.5.32 Date.prototype.setMinutes (min [, sec [, ms ] ] ) 633 * 634 * @param self self reference 635 * @param args minutes (seconds and milliseconds are optional second and third arguments) 636 * @return time 637 */ 638 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 3) 639 public static Object setMinutes(final Object self, final Object... args) { 640 final NativeDate nd = getNativeDate(self); 641 setFields(nd, MINUTE, args, true); 642 return nd.getTime(); 643 } 644 645 /** 646 * ECMA 15.9.5.33 Date.prototype.setUTCMinutes (min [, sec [, ms ] ] ) 647 * 648 * @param self self reference 649 * @param args minutes (seconds and milliseconds are optional second and third arguments) 650 * @return time 651 */ 652 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 3) 653 public static Object setUTCMinutes(final Object self, final Object... args) { 654 final NativeDate nd = getNativeDate(self); 655 setFields(nd, MINUTE, args, false); 656 return nd.getTime(); 657 } 658 659 /** 660 * ECMA 15.9.5.34 Date.prototype.setHours (hour [, min [, sec [, ms ] ] ] ) 661 * 662 * @param self self reference 663 * @param args hour (optional arguments after are minutes, seconds, milliseconds) 664 * @return time 665 */ 666 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 4) 667 public static Object setHours(final Object self, final Object... args) { 668 final NativeDate nd = getNativeDate(self); 669 setFields(nd, HOUR, args, true); 670 return nd.getTime(); 671 } 672 673 /** 674 * ECMA 15.9.5.35 Date.prototype.setUTCHours (hour [, min [, sec [, ms ] ] ] ) 675 * 676 * @param self self reference 677 * @param args hour (optional arguments after are minutes, seconds, milliseconds) 678 * @return time 679 */ 680 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 4) 681 public static Object setUTCHours(final Object self, final Object... args) { 682 final NativeDate nd = getNativeDate(self); 683 setFields(nd, HOUR, args, false); 684 return nd.getTime(); 685 } 686 687 /** 688 * ECMA 15.9.5.36 Date.prototype.setDate (date) 689 * 690 * @param self self reference 691 * @param args date 692 * @return time 693 */ 694 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) 695 public static Object setDate(final Object self, final Object... args) { 696 final NativeDate nd = getNativeDate(self); 697 setFields(nd, DAY, args, true); 698 return nd.getTime(); 699 } 700 701 /** 702 * ECMA 15.9.5.37 Date.prototype.setUTCDate (date) 703 * 704 * @param self self reference 705 * @param args UTC date 706 * @return time 707 */ 708 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) 709 public static Object setUTCDate(final Object self, final Object... args) { 710 final NativeDate nd = getNativeDate(self); 711 setFields(nd, DAY, args, false); 712 return nd.getTime(); 713 } 714 715 /** 716 * ECMA 15.9.5.38 Date.prototype.setMonth (month [, date ] ) 717 * 718 * @param self self reference 719 * @param args month (optional second argument is date) 720 * @return time 721 */ 722 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2) 723 public static Object setMonth(final Object self, final Object... args) { 724 final NativeDate nd = getNativeDate(self); 725 setFields(nd, MONTH, args, true); 726 return nd.getTime(); 727 } 728 729 /** 730 * ECMA 15.9.5.39 Date.prototype.setUTCMonth (month [, date ] ) 731 * 732 * @param self self reference 733 * @param args UTC month (optional second argument is date) 734 * @return time 735 */ 736 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2) 737 public static Object setUTCMonth(final Object self, final Object... args) { 738 final NativeDate nd = ensureNativeDate(self); 739 setFields(nd, MONTH, args, false); 740 return nd.getTime(); 741 } 742 743 /** 744 * ECMA 15.9.5.40 Date.prototype.setFullYear (year [, month [, date ] ] ) 745 * 746 * @param self self reference 747 * @param args year (optional second and third arguments are month and date) 748 * @return time 749 */ 750 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 3) 751 public static Object setFullYear(final Object self, final Object... args) { 752 final NativeDate nd = ensureNativeDate(self); 753 if (nd.isValidDate()) { 754 setFields(nd, YEAR, args, true); 755 } else { 756 final double[] d = convertArgs(args, 0, YEAR, YEAR, 3); 757 if (d != null) { 758 nd.setTime(timeClip(utc(makeDate(makeDay(d[0], d[1], d[2]), 0), nd.getTimeZone()))); 759 } else { 760 nd.setTime(NaN); 761 } 762 } 763 return nd.getTime(); 764 } 765 766 /** 767 * ECMA 15.9.5.41 Date.prototype.setUTCFullYear (year [, month [, date ] ] ) 768 * 769 * @param self self reference 770 * @param args UTC full year (optional second and third arguments are month and date) 771 * @return time 772 */ 773 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 3) 774 public static Object setUTCFullYear(final Object self, final Object... args) { 775 final NativeDate nd = ensureNativeDate(self); 776 if (nd.isValidDate()) { 777 setFields(nd, YEAR, args, false); 778 } else { 779 final double[] d = convertArgs(args, 0, YEAR, YEAR, 3); 780 nd.setTime(timeClip(makeDate(makeDay(d[0], d[1], d[2]), 0))); 781 } 782 return nd.getTime(); 783 } 784 785 /** 786 * ECMA B.2.5 Date.prototype.setYear (year) 787 * 788 * @param self self reference 789 * @param year year 790 * @return NativeDate 791 */ 792 @Function(attributes = Attribute.NOT_ENUMERABLE) 793 public static Object setYear(final Object self, final Object year) { 794 final NativeDate nd = getNativeDate(self); 795 if (isNaN(nd.getTime())) { 796 nd.setTime(utc(0, nd.getTimeZone())); 797 } 798 799 final double yearNum = JSType.toNumber(year); 800 if (isNaN(yearNum)) { 801 nd.setTime(NaN); 802 return nd.getTime(); 803 } 804 int yearInt = (int)yearNum; 805 if (0 <= yearInt && yearInt <= 99) { 806 yearInt += 1900; 807 } 808 setFields(nd, YEAR, new Object[] {yearInt}, true); 809 810 return nd.getTime(); 811 } 812 813 /** 814 * ECMA 15.9.5.42 Date.prototype.toUTCString ( ) 815 * 816 * @param self self reference 817 * @return string representation of date 818 */ 819 @Function(attributes = Attribute.NOT_ENUMERABLE) 820 public static Object toUTCString(final Object self) { 821 return toGMTStringImpl(self); 822 } 823 824 /** 825 * ECMA B.2.6 Date.prototype.toGMTString ( ) 826 * 827 * See {@link NativeDate#toUTCString(Object)} 828 * 829 * @param self self reference 830 * @return string representation of date 831 */ 832 @Function(attributes = Attribute.NOT_ENUMERABLE) 833 public static Object toGMTString(final Object self) { 834 return toGMTStringImpl(self); 835 } 836 837 /** 838 * ECMA 15.9.5.43 Date.prototype.toISOString ( ) 839 * 840 * @param self self reference 841 * @return string representation of date 842 */ 843 @Function(attributes = Attribute.NOT_ENUMERABLE) 844 public static Object toISOString(final Object self) { 845 return toISOStringImpl(self); 846 } 847 848 /** 849 * ECMA 15.9.5.44 Date.prototype.toJSON ( key ) 850 * 851 * Provides a string representation of this Date for use by {@link NativeJSON#stringify(Object, Object, Object, Object)} 852 * 853 * @param self self reference 854 * @param key ignored 855 * @return JSON representation of this date 856 */ 857 @Function(attributes = Attribute.NOT_ENUMERABLE) 858 public static Object toJSON(final Object self, final Object key) { 859 // NOTE: Date.prototype.toJSON is generic. Accepts other objects as well. 860 final Object selfObj = Global.toObject(self); 861 if (!(selfObj instanceof ScriptObject)) { 862 return null; 863 } 864 final ScriptObject sobj = (ScriptObject)selfObj; 865 final Object value = sobj.getDefaultValue(Number.class); 866 if (value instanceof Number) { 867 final double num = ((Number)value).doubleValue(); 868 if (isInfinite(num) || isNaN(num)) { 869 return null; 870 } 871 } 872 873 try { 874 final InvokeByName toIsoString = getTO_ISO_STRING(); 875 final Object func = toIsoString.getGetter().invokeExact(sobj); 876 if (Bootstrap.isCallable(func)) { 877 return toIsoString.getInvoker().invokeExact(func, sobj, key); 878 } 879 throw typeError("not.a.function", ScriptRuntime.safeToString(func)); 880 } catch (final RuntimeException | Error e) { 881 throw e; 882 } catch (final Throwable t) { 883 throw new RuntimeException(t); 884 } 885 } 886 887 // -- Internals below this point 888 889 private static double parseDateString(final String str) { 890 891 final DateParser parser = new DateParser(str); 892 if (parser.parse()) { 893 final Integer[] fields = parser.getDateFields(); 894 double d = makeDate(fields); 895 if (fields[DateParser.TIMEZONE] != null) { 896 d -= fields[DateParser.TIMEZONE] * 60000; 897 } else { 898 d = utc(d, Global.getEnv()._timezone); 899 } 900 d = timeClip(d); 901 return d; 902 } 903 904 return Double.NaN; 905 } 906 907 private static void zeroPad(final StringBuilder sb, final int n, final int length) { 908 for (int l = 1, d = 10; l < length; l++, d *= 10) { 909 if (n < d) { 910 sb.append('0'); 911 } 912 } 913 sb.append(n); 914 } 915 916 private static String toStringImpl(final Object self, final int format) { 917 final NativeDate nd = getNativeDate(self); 918 919 if (nd != null && nd.isValidDate()) { 920 final StringBuilder sb = new StringBuilder(40); 921 final double t = nd.getLocalTime(); 922 923 switch (format) { 924 925 case FORMAT_DATE_TIME: 926 case FORMAT_DATE : 927 case FORMAT_LOCAL_DATE_TIME: 928 // EEE MMM dd yyyy 929 sb.append(weekDays[weekDay(t)]) 930 .append(' ') 931 .append(months[monthFromTime(t)]) 932 .append(' '); 933 zeroPad(sb, dayFromTime(t), 2); 934 sb.append(' '); 935 zeroPad(sb, yearFromTime(t), 4); 936 if (format == FORMAT_DATE) { 937 break; 938 } 939 sb.append(' '); 940 941 //$FALL-THROUGH$ 942 case FORMAT_TIME: 943 final TimeZone tz = nd.getTimeZone(); 944 final double utcTime = nd.getTime(); 945 int offset = tz.getOffset((long) utcTime) / 60000; 946 final boolean inDaylightTime = offset != tz.getRawOffset() / 60000; 947 // Convert minutes to HHmm timezone offset 948 offset = (offset / 60) * 100 + offset % 60; 949 950 // HH:mm:ss GMT+HHmm 951 zeroPad(sb, hourFromTime(t), 2); 952 sb.append(':'); 953 zeroPad(sb, minFromTime(t), 2); 954 sb.append(':'); 955 zeroPad(sb, secFromTime(t), 2); 956 sb.append(" GMT") 957 .append(offset < 0 ? '-' : '+'); 958 zeroPad(sb, Math.abs(offset), 4); 959 sb.append(" (") 960 .append(tz.getDisplayName(inDaylightTime, TimeZone.SHORT, Locale.US)) 961 .append(')'); 962 break; 963 964 case FORMAT_LOCAL_DATE: 965 // yyyy-MM-dd 966 zeroPad(sb, yearFromTime(t), 4); 967 sb.append('-'); 968 zeroPad(sb, monthFromTime(t) + 1, 2); 969 sb.append('-'); 970 zeroPad(sb, dayFromTime(t), 2); 971 break; 972 973 case FORMAT_LOCAL_TIME: 974 // HH:mm:ss 975 zeroPad(sb, hourFromTime(t), 2); 976 sb.append(':'); 977 zeroPad(sb, minFromTime(t), 2); 978 sb.append(':'); 979 zeroPad(sb, secFromTime(t), 2); 980 break; 981 982 default: 983 throw new IllegalArgumentException("format: " + format); 984 } 985 986 return sb.toString(); 987 } 988 989 return INVALID_DATE; 990 } 991 992 private static String toGMTStringImpl(final Object self) { 993 final NativeDate nd = getNativeDate(self); 994 995 if (nd != null && nd.isValidDate()) { 996 final StringBuilder sb = new StringBuilder(29); 997 final double t = nd.getTime(); 998 // EEE, dd MMM yyyy HH:mm:ss z 999 sb.append(weekDays[weekDay(t)]) 1000 .append(", "); 1001 zeroPad(sb, dayFromTime(t), 2); 1002 sb.append(' ') 1003 .append(months[monthFromTime(t)]) 1004 .append(' '); 1005 zeroPad(sb, yearFromTime(t), 4); 1006 sb.append(' '); 1007 zeroPad(sb, hourFromTime(t), 2); 1008 sb.append(':'); 1009 zeroPad(sb, minFromTime(t), 2); 1010 sb.append(':'); 1011 zeroPad(sb, secFromTime(t), 2); 1012 sb.append(" GMT"); 1013 return sb.toString(); 1014 } 1015 1016 throw rangeError("invalid.date"); 1017 } 1018 1019 private static String toISOStringImpl(final Object self) { 1020 final NativeDate nd = getNativeDate(self); 1021 1022 if (nd != null && nd.isValidDate()) { 1023 final StringBuilder sb = new StringBuilder(24); 1024 final double t = nd.getTime(); 1025 // yyyy-MM-dd'T'HH:mm:ss.SSS'Z' 1026 zeroPad(sb, yearFromTime(t), 4); 1027 sb.append('-'); 1028 zeroPad(sb, monthFromTime(t) + 1, 2); 1029 sb.append('-'); 1030 zeroPad(sb, dayFromTime(t), 2); 1031 sb.append('T'); 1032 zeroPad(sb, hourFromTime(t), 2); 1033 sb.append(':'); 1034 zeroPad(sb, minFromTime(t), 2); 1035 sb.append(':'); 1036 zeroPad(sb, secFromTime(t), 2); 1037 sb.append('.'); 1038 zeroPad(sb, msFromTime(t), 3); 1039 sb.append("Z"); 1040 return sb.toString(); 1041 } 1042 1043 throw rangeError("invalid.date"); 1044 } 1045 1046 // ECMA 15.9.1.2 Day (t) 1047 private static double day(final double t) { 1048 return Math.floor(t / msPerDay); 1049 } 1050 1051 // ECMA 15.9.1.2 TimeWithinDay (t) 1052 private static double timeWithinDay(final double t) { 1053 return t % msPerDay; 1054 } 1055 1056 // ECMA 15.9.1.3 InLeapYear (t) 1057 private static boolean isLeapYear(final int y) { 1058 return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0); 1059 } 1060 1061 // ECMA 15.9.1.3 DaysInYear (y) 1062 private static int daysInYear(final int y) { 1063 return isLeapYear(y) ? 366 : 365; 1064 } 1065 1066 // ECMA 15.9.1.3 DayFromYear (y) 1067 private static double dayFromYear(final double y) { 1068 return 365 * (y - 1970) 1069 + Math.floor((y -1969) / 4.0) 1070 - Math.floor((y - 1901) / 100.0) 1071 + Math.floor((y - 1601) / 400.0); 1072 } 1073 1074 // ECMA 15.9.1.3 Year Number 1075 private static double timeFromYear(final int y) { 1076 return dayFromYear(y) * msPerDay; 1077 } 1078 1079 // ECMA 15.9.1.3 Year Number 1080 private static int yearFromTime(final double t) { 1081 int y = (int) Math.floor(t / (msPerDay * 365.2425)) + 1970; 1082 final double t2 = timeFromYear(y); 1083 if (t2 > t) { 1084 y--; 1085 } else if (t2 + msPerDay * daysInYear(y) <= t) { 1086 y++; 1087 } 1088 return y; 1089 } 1090 1091 private static int dayWithinYear(final double t, final int year) { 1092 return (int) (day(t) - dayFromYear(year)); 1093 } 1094 1095 private static int monthFromTime(final double t) { 1096 final int year = yearFromTime(t); 1097 final int day = dayWithinYear(t, year); 1098 final int[] firstDay = firstDayInMonth[isLeapYear(year) ? 1 : 0]; 1099 int month = 0; 1100 1101 while (month < 11 && firstDay[month + 1] <= day) { 1102 month++; 1103 } 1104 return month; 1105 } 1106 1107 private static int dayFromTime(final double t) { 1108 final int year = yearFromTime(t); 1109 final int day = dayWithinYear(t, year); 1110 final int[] firstDay = firstDayInMonth[isLeapYear(year) ? 1 : 0]; 1111 int month = 0; 1112 1113 while (month < 11 && firstDay[month + 1] <= day) { 1114 month++; 1115 } 1116 return 1 + day - firstDay[month]; 1117 } 1118 1119 private static int dayFromMonth(final int month, final int year) { 1120 assert(month >= 0 && month <= 11); 1121 final int[] firstDay = firstDayInMonth[isLeapYear(year) ? 1 : 0]; 1122 return firstDay[month]; 1123 } 1124 1125 private static int weekDay(final double time) { 1126 final int day = (int) (day(time) + 4) % 7; 1127 return day < 0 ? day + 7 : day; 1128 } 1129 1130 // ECMA 15.9.1.9 LocalTime 1131 private static double localTime(final double time, final TimeZone tz) { 1132 return time + tz.getOffset((long) time); 1133 } 1134 1135 // ECMA 15.9.1.9 UTC 1136 private static double utc(final double time, final TimeZone tz) { 1137 return time - tz.getOffset((long) (time - tz.getRawOffset())); 1138 } 1139 1140 // ECMA 15.9.1.10 Hours, Minutes, Second, and Milliseconds 1141 private static int hourFromTime(final double t) { 1142 final int h = (int) (Math.floor(t / msPerHour) % hoursPerDay); 1143 return h < 0 ? h + hoursPerDay: h; 1144 } 1145 private static int minFromTime(final double t) { 1146 final int m = (int) (Math.floor(t / msPerMinute) % minutesPerHour); 1147 return m < 0 ? m + minutesPerHour : m; 1148 } 1149 1150 private static int secFromTime(final double t) { 1151 final int s = (int) (Math.floor(t / msPerSecond) % secondsPerMinute); 1152 return s < 0 ? s + secondsPerMinute : s; 1153 } 1154 1155 private static int msFromTime(final double t) { 1156 final int m = (int) (t % msPerSecond); 1157 return m < 0 ? m + msPerSecond : m; 1158 } 1159 1160 private static int valueFromTime(final int unit, final double t) { 1161 switch (unit) { 1162 case YEAR: return yearFromTime(t); 1163 case MONTH: return monthFromTime(t); 1164 case DAY: return dayFromTime(t); 1165 case HOUR: return hourFromTime(t); 1166 case MINUTE: return minFromTime(t); 1167 case SECOND: return secFromTime(t); 1168 case MILLISECOND: return msFromTime(t); 1169 default: throw new IllegalArgumentException(Integer.toString(unit)); 1170 } 1171 } 1172 1173 // ECMA 15.9.1.11 MakeTime (hour, min, sec, ms) 1174 private static double makeTime(final double hour, final double min, final double sec, final double ms) { 1175 return hour * 3600000 + min * 60000 + sec * 1000 + ms; 1176 } 1177 1178 // ECMA 15.9.1.12 MakeDay (year, month, date) 1179 private static double makeDay(final double year, final double month, final double date) { 1180 final double y = year + Math.floor(month / 12); 1181 int m = (int) (month % 12); 1182 if (m < 0) { 1183 m += 12; 1184 } 1185 double d = dayFromYear(y); 1186 d += dayFromMonth(m, (int) y); 1187 1188 return d + date - 1; 1189 } 1190 1191 // ECMA 15.9.1.13 MakeDate (day, time) 1192 private static double makeDate(final double day, final double time) { 1193 return day * msPerDay + time; 1194 } 1195 1196 1197 private static double makeDate(final Integer[] d) { 1198 final double time = makeDay(d[0], d[1], d[2]) * msPerDay; 1199 return time + makeTime(d[3], d[4], d[5], d[6]); 1200 } 1201 1202 private static double makeDate(final double[] d) { 1203 final double time = makeDay(d[0], d[1], d[2]) * msPerDay; 1204 return time + makeTime(d[3], d[4], d[5], d[6]); 1205 } 1206 1207 // Convert Date constructor args, checking for NaN, filling in defaults etc. 1208 private static double[] convertCtorArgs(final Object[] args) { 1209 final double[] d = new double[7]; 1210 boolean nullReturn = false; 1211 1212 // should not bailout on first NaN or infinite. Need to convert all 1213 // subsequent args for possible side-effects via valueOf/toString overrides 1214 // on argument objects. 1215 for (int i = 0; i < d.length; i++) { 1216 if (i < args.length) { 1217 final double darg = JSType.toNumber(args[i]); 1218 if (isNaN(darg) || isInfinite(darg)) { 1219 nullReturn = true; 1220 } 1221 1222 d[i] = (long)darg; 1223 } else { 1224 d[i] = i == 2 ? 1 : 0; // day in month defaults to 1 1225 } 1226 } 1227 1228 if (0 <= d[0] && d[0] <= 99) { 1229 d[0] += 1900; 1230 } 1231 1232 return nullReturn? null : d; 1233 } 1234 1235 // This method does the hard work for all setter methods: If a value is provided 1236 // as argument it is used, otherwise the value is calculated from the existing time value. 1237 private static double[] convertArgs(final Object[] args, final double time, final int fieldId, final int start, final int length) { 1238 final double[] d = new double[length]; 1239 boolean nullReturn = false; 1240 1241 // Need to call toNumber on all args for side-effects - even if an argument 1242 // fails to convert to number, subsequent toNumber calls needed for possible 1243 // side-effects via valueOf/toString overrides. 1244 for (int i = start; i < start + length; i++) { 1245 if (fieldId <= i && i < fieldId + args.length) { 1246 final double darg = JSType.toNumber(args[i - fieldId]); 1247 if (isNaN(darg) || isInfinite(darg)) { 1248 nullReturn = true; 1249 } 1250 1251 d[i - start] = (long) darg; 1252 } else { 1253 // Date.prototype.set* methods require first argument to be defined 1254 if (i == fieldId) { 1255 nullReturn = true; 1256 } 1257 1258 if (!nullReturn && !isNaN(time)) { 1259 d[i - start] = valueFromTime(i, time); 1260 } 1261 } 1262 } 1263 1264 return nullReturn ? null : d; 1265 } 1266 1267 // ECMA 15.9.1.14 TimeClip (time) 1268 private static double timeClip(final double time) { 1269 if (isInfinite(time) || isNaN(time) || Math.abs(time) > 8.64e15) { 1270 return Double.NaN; 1271 } 1272 return (long)time; 1273 } 1274 1275 private static NativeDate ensureNativeDate(final Object self) { 1276 return getNativeDate(self); 1277 } 1278 1279 private static NativeDate getNativeDate(final Object self) { 1280 if (self instanceof NativeDate) { 1281 return (NativeDate)self; 1282 } else if (self != null && self == Global.instance().getDatePrototype()) { 1283 return Global.instance().DEFAULT_DATE; 1284 } else { 1285 throw typeError("not.a.date", ScriptRuntime.safeToString(self)); 1286 } 1287 } 1288 1289 private static Object getField(final Object self, final int field) { 1290 final NativeDate nd = getNativeDate(self); 1291 return (nd != null && nd.isValidDate()) ? valueFromTime(field, nd.getLocalTime()) : Double.NaN; 1292 } 1293 1294 private static Object getUTCField(final Object self, final int field) { 1295 final NativeDate nd = getNativeDate(self); 1296 return (nd != null && nd.isValidDate()) ? valueFromTime(field, nd.getTime()) : Double.NaN; 1297 } 1298 1299 private static void setFields(final NativeDate nd, final int fieldId, final Object[] args, final boolean local) { 1300 int start, length; 1301 if (fieldId < HOUR) { 1302 start = YEAR; 1303 length = 3; 1304 } else { 1305 start = HOUR; 1306 length = 4; 1307 } 1308 final double time = local ? nd.getLocalTime() : nd.getTime(); 1309 final double d[] = convertArgs(args, time, fieldId, start, length); 1310 1311 if (! nd.isValidDate()) { 1312 return; 1313 } 1314 1315 double newTime; 1316 if (d == null) { 1317 newTime = NaN; 1318 } else { 1319 if (start == YEAR) { 1320 newTime = makeDate(makeDay(d[0], d[1], d[2]), timeWithinDay(time)); 1321 } else { 1322 newTime = makeDate(day(time), makeTime(d[0], d[1], d[2], d[3])); 1323 } 1324 if (local) { 1325 newTime = utc(newTime, nd.getTimeZone()); 1326 } 1327 newTime = timeClip(newTime); 1328 } 1329 nd.setTime(newTime); 1330 } 1331 1332 private boolean isValidDate() { 1333 return !isNaN(time); 1334 } 1335 1336 private double getLocalTime() { 1337 return localTime(time, timezone); 1338 } 1339 1340 private double getTime() { 1341 return time; 1342 } 1343 1344 private void setTime(final double time) { 1345 this.time = time; 1346 } 1347 1348 private TimeZone getTimeZone() { 1349 return timezone; 1350 } 1351 1352 }