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