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