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 if (self instanceof NativeDate) { 848 final NativeDate nd = (NativeDate)self; 849 return (isNaN(nd.getTime())) ? null : toISOStringImpl(nd); 850 } 851 // NOTE: Date.prototype.toJSON is generic. Accepts other objects as well. 852 final Object selfObj = Global.toObject(self); 853 if (!(selfObj instanceof ScriptObject)) { 854 return null; 855 } 856 final ScriptObject sobj = (ScriptObject)selfObj; 857 final Object value = sobj.getDefaultValue(Number.class); 858 859 final double num = (value instanceof Number) ? ((Number)value).doubleValue() : NaN; 860 861 if (isInfinite(num) || isNaN(num)) { 862 return null; 863 } 864 865 try { 866 final Object func = TO_ISO_STRING.getGetter().invokeExact(sobj); 867 if (func instanceof ScriptFunction) { 868 return TO_ISO_STRING.getInvoker().invokeExact(func, sobj, key); 869 } 870 throw typeError("not.a.function", ScriptRuntime.safeToString(func)); 871 } catch (final RuntimeException | Error e) { 872 throw e; 873 } catch (final Throwable t) { 874 throw new RuntimeException(t); 875 } 876 } 877 878 // -- Internals below this point 879 880 private static double parseDateString(final String str) { 881 882 final DateParser parser = new DateParser(str); 883 if (parser.parse()) { 884 final Integer[] fields = parser.getDateFields(); 885 double d = makeDate(fields); 886 if (fields[DateParser.TIMEZONE] != null) { 887 d -= fields[DateParser.TIMEZONE] * 60000; 888 } else { 889 d = utc(d, Global.getEnv()._timezone); 890 } 891 d = timeClip(d); 892 return d; 893 } 894 895 return Double.NaN; 896 } 897 898 private static void zeroPad(final StringBuilder sb, final int n, final int length) { 899 for (int l = 1, d = 10; l < length; l++, d *= 10) { 900 if (n < d) { 901 sb.append('0'); 902 } 903 } 904 sb.append(n); 905 } 906 907 private static String toStringImpl(final Object self, final int format) { 908 final NativeDate nd = getNativeDate(self); 909 910 if (nd != null && nd.isValidDate()) { 911 final StringBuilder sb = new StringBuilder(40); 912 final double t = nd.getLocalTime(); 913 914 switch (format) { 915 916 case FORMAT_DATE_TIME: 917 case FORMAT_DATE : 918 case FORMAT_LOCAL_DATE_TIME: 919 // EEE MMM dd yyyy 920 sb.append(weekDays[(int) weekDay(t)]) 921 .append(' ') 922 .append(months[(int) monthFromTime(t)]) 923 .append(' '); 924 zeroPad(sb, (int) dayFromTime(t), 2); 925 sb.append(' '); 926 zeroPad(sb, (int) yearFromTime(t), 4); 927 if (format == FORMAT_DATE) { 928 break; 929 } 930 sb.append(' '); 931 932 //$FALL-THROUGH$ 933 case FORMAT_TIME: 934 final TimeZone tz = nd.getTimeZone(); 935 final double utcTime = nd.getTime(); 936 int offset = tz.getOffset((long) utcTime) / 60000; 937 final boolean inDaylightTime = offset != tz.getRawOffset() / 60000; 938 // Convert minutes to HHmm timezone offset 939 offset = (offset / 60) * 100 + offset % 60; 940 941 // HH:mm:ss GMT+HHmm 942 zeroPad(sb, (int) hourFromTime(t), 2); 943 sb.append(':'); 944 zeroPad(sb, (int) minFromTime(t), 2); 945 sb.append(':'); 946 zeroPad(sb, (int) secFromTime(t), 2); 947 sb.append(" GMT") 948 .append(offset < 0 ? '-' : '+'); 949 zeroPad(sb, Math.abs(offset), 4); 950 sb.append(" (") 951 .append(tz.getDisplayName(inDaylightTime, TimeZone.SHORT, Locale.US)) 952 .append(')'); 953 break; 954 955 case FORMAT_LOCAL_DATE: 956 // yyyy-MM-dd 957 zeroPad(sb, (int) yearFromTime(t), 4); 958 sb.append('-'); 959 zeroPad(sb, (int) monthFromTime(t) + 1, 2); 960 sb.append('-'); 961 zeroPad(sb, (int) dayFromTime(t), 2); 962 break; 963 964 case FORMAT_LOCAL_TIME: 965 // HH:mm:ss 966 zeroPad(sb, (int) hourFromTime(t), 2); 967 sb.append(':'); 968 zeroPad(sb, (int) minFromTime(t), 2); 969 sb.append(':'); 970 zeroPad(sb, (int) secFromTime(t), 2); 971 break; 972 973 default: 974 throw new IllegalArgumentException("format: " + format); 975 } 976 977 return sb.toString(); 978 } 979 980 return INVALID_DATE; 981 } 982 983 private static String toGMTStringImpl(final Object self) { 984 final NativeDate nd = getNativeDate(self); 985 986 if (nd != null && nd.isValidDate()) { 987 final StringBuilder sb = new StringBuilder(29); 988 final double t = nd.getTime(); 989 // EEE, dd MMM yyyy HH:mm:ss z 990 sb.append(weekDays[(int) weekDay(t)]) 991 .append(", "); 992 zeroPad(sb, (int) dayFromTime(t), 2); 993 sb.append(' ') 994 .append(months[(int) monthFromTime(t)]) 995 .append(' '); 996 zeroPad(sb, (int) yearFromTime(t), 4); 997 sb.append(' '); 998 zeroPad(sb, (int) hourFromTime(t), 2); 999 sb.append(':'); 1000 zeroPad(sb, (int) minFromTime(t), 2); 1001 sb.append(':'); 1002 zeroPad(sb, (int) secFromTime(t), 2); 1003 sb.append(" GMT"); 1004 return sb.toString(); 1005 } 1006 1007 throw rangeError("invalid.date"); 1008 } 1009 1010 private static String toISOStringImpl(final Object self) { 1011 final NativeDate nd = getNativeDate(self); 1012 1013 if (nd != null && nd.isValidDate()) { 1014 final StringBuilder sb = new StringBuilder(24); 1015 final double t = nd.getTime(); 1016 // yyyy-MM-dd'T'HH:mm:ss.SSS'Z' 1017 zeroPad(sb, (int) yearFromTime(t), 4); 1018 sb.append('-'); 1019 zeroPad(sb, (int) monthFromTime(t) + 1, 2); 1020 sb.append('-'); 1021 zeroPad(sb, (int) dayFromTime(t), 2); 1022 sb.append('T'); 1023 zeroPad(sb, (int) hourFromTime(t), 2); 1024 sb.append(':'); 1025 zeroPad(sb, (int) minFromTime(t), 2); 1026 sb.append(':'); 1027 zeroPad(sb, (int) secFromTime(t), 2); 1028 sb.append('.'); 1029 zeroPad(sb, (int) msFromTime(t), 3); 1030 sb.append("Z"); 1031 return sb.toString(); 1032 } 1033 1034 throw rangeError("invalid.date"); 1035 } 1036 1037 // ECMA 15.9.1.2 Day (t) 1038 private static double day(final double t) { 1039 return Math.floor(t / msPerDay); 1040 } 1041 1042 // ECMA 15.9.1.2 TimeWithinDay (t) 1043 private static double timeWithinDay(final double t) { 1044 return t % msPerDay; 1045 } 1046 1047 // ECMA 15.9.1.3 InLeapYear (t) 1048 private static boolean isLeapYear(final int y) { 1049 return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0); 1050 } 1051 1052 // ECMA 15.9.1.3 DaysInYear (y) 1053 private static int daysInYear(final int y) { 1054 return isLeapYear(y) ? 366 : 365; 1055 } 1056 1057 // ECMA 15.9.1.3 DayFromYear (y) 1058 private static double dayFromYear(final double y) { 1059 return 365 * (y - 1970) 1060 + Math.floor((y -1969) / 4.0) 1061 - Math.floor((y - 1901) / 100.0) 1062 + Math.floor((y - 1601) / 400.0); 1063 } 1064 1065 // ECMA 15.9.1.3 Year Number 1066 private static double timeFromYear(final double y) { 1067 return dayFromYear(y) * msPerDay; 1068 } 1069 1070 private static double yearFromTime(final double t) { 1071 double y = Math.floor(t / (msPerDay * 365.2425)) + 1970; 1072 final double t2 = timeFromYear(y); 1073 if (t2 > t) { 1074 y--; 1075 } else if (t2 + msPerDay * daysInYear((int) y) <= t) { 1076 y++; 1077 } 1078 return y; 1079 } 1080 1081 private static double dayWithinYear(final double t, final double year) { 1082 return day(t) - dayFromYear(year); 1083 } 1084 1085 private static double monthFromTime(final double t) { 1086 final double year = yearFromTime(t); 1087 final double day = dayWithinYear(t, year); 1088 final int[] firstDay = firstDayInMonth[isLeapYear((int) year) ? 1 : 0]; 1089 int month = 0; 1090 1091 while (month < 11 && firstDay[month + 1] <= day) { 1092 month++; 1093 } 1094 return month; 1095 } 1096 1097 private static double dayFromTime(final double t) { 1098 final double year = yearFromTime(t); 1099 final double day = dayWithinYear(t, year); 1100 final int[] firstDay = firstDayInMonth[isLeapYear((int) year) ? 1 : 0]; 1101 int month = 0; 1102 1103 while (month < 11 && firstDay[month + 1] <= day) { 1104 month++; 1105 } 1106 return 1 + day - firstDay[month]; 1107 } 1108 1109 private static int dayFromMonth(final int month, final int year) { 1110 assert(month >= 0 && month <= 11); 1111 final int[] firstDay = firstDayInMonth[isLeapYear(year) ? 1 : 0]; 1112 return firstDay[month]; 1113 } 1114 1115 private static double weekDay(final double time) { 1116 if (isNaN(time)) { 1117 return NaN; 1118 } 1119 final double day = (day(time) + 4) % 7; 1120 return day < 0 ? day + 7 : day; 1121 } 1122 1123 // ECMA 15.9.1.9 LocalTime 1124 private static double localTime(final double time, final TimeZone tz) { 1125 return time + tz.getOffset((long) time); 1126 } 1127 1128 // ECMA 15.9.1.9 UTC 1129 private static double utc(final double time, final TimeZone tz) { 1130 return time - tz.getOffset((long) (time - tz.getRawOffset())); 1131 } 1132 1133 // ECMA 15.9.1.10 Hours, Minutes, Second, and Milliseconds 1134 private static double hourFromTime(final double t) { 1135 final double h = Math.floor(t / msPerHour) % hoursPerDay; 1136 return h < 0 ? h + hoursPerDay: h; 1137 } 1138 private static double minFromTime(final double t) { 1139 final double m = Math.floor(t / msPerMinute) % minutesPerHour; 1140 return m < 0 ? m + minutesPerHour : m; 1141 } 1142 1143 private static double secFromTime(final double t) { 1144 final double s = Math.floor(t / msPerSecond) % secondsPerMinute; 1145 return s < 0 ? s + secondsPerMinute : s; 1146 } 1147 1148 private static double msFromTime(final double t) { 1149 final double m = t % msPerSecond; 1150 return m < 0 ? m + msPerSecond : m; 1151 } 1152 1153 private static double valueFromTime(final int unit, final double t) { 1154 switch (unit) { 1155 case YEAR: return yearFromTime(t); 1156 case MONTH: return monthFromTime(t); 1157 case DAY: return dayFromTime(t); 1158 case HOUR: return hourFromTime(t); 1159 case MINUTE: return minFromTime(t); 1160 case SECOND: return secFromTime(t); 1161 case MILLISECOND: return msFromTime(t); 1162 default: throw new IllegalArgumentException(Integer.toString(unit)); 1163 } 1164 } 1165 1166 // ECMA 15.9.1.11 MakeTime (hour, min, sec, ms) 1167 private static double makeTime(final double hour, final double min, final double sec, final double ms) { 1168 return hour * 3600000 + min * 60000 + sec * 1000 + ms; 1169 } 1170 1171 // ECMA 15.9.1.12 MakeDay (year, month, date) 1172 private static double makeDay(final double year, final double month, final double date) { 1173 final double y = year + Math.floor(month / 12); 1174 double m = month % 12; 1175 if (m < 0) { 1176 m += 12; 1177 } 1178 double d = Math.floor(dayFromYear(y)); 1179 d += dayFromMonth((int) m, (int) y); 1180 1181 return d + date - 1; 1182 } 1183 1184 // ECMA 15.9.1.13 MakeDate (day, time) 1185 private static double makeDate(final double day, final double time) { 1186 return day * msPerDay + time; 1187 } 1188 1189 1190 private static double makeDate(final Integer[] d) { 1191 final double time = makeDay(d[0], d[1], d[2]) * msPerDay; 1192 return time + makeTime(d[3], d[4], d[5], d[6]); 1193 } 1194 1195 private static double makeDate(final double[] d) { 1196 final double time = makeDay(d[0], d[1], d[2]) * msPerDay; 1197 return time + makeTime(d[3], d[4], d[5], d[6]); 1198 } 1199 1200 // Convert Date constructor args, checking for NaN, filling in defaults etc. 1201 private static double[] convertCtorArgs(final Object[] args) { 1202 final double[] d = new double[7]; 1203 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 return null; 1209 } 1210 d[i] = (long)darg; 1211 } else { 1212 d[i] = i == 2 ? 1 : 0; // day in month defaults to 1 1213 } 1214 } 1215 1216 if (0 <= d[0] && d[0] <= 99) { 1217 d[0] += 1900; 1218 } 1219 1220 return d; 1221 } 1222 1223 // This method does the hard work for all setter methods: If a value is provided 1224 // as argument it is used, otherwise the value is calculated from the existing time value. 1225 private static double[] convertArgs(final Object[] args, final double time, final int fieldId, final int start, final int length) { 1226 final double[] d = new double[length]; 1227 1228 for (int i = start; i < start + length; i++) { 1229 if (fieldId <= i && i < fieldId + args.length) { 1230 final double darg = JSType.toNumber(args[i - fieldId]); 1231 if (isNaN(darg) || isInfinite(darg)) { 1232 return null; 1233 } 1234 d[i - start] = (long) darg; 1235 } else { 1236 // Date.prototype.set* methods require first argument to be defined 1237 if (i == fieldId) { 1238 return null; 1239 } 1240 d[i - start] = valueFromTime(i, time); 1241 } 1242 } 1243 return d; 1244 1245 } 1246 1247 // ECMA 15.9.1.14 TimeClip (time) 1248 private static double timeClip(final double time) { 1249 if (isInfinite(time) || isNaN(time) || Math.abs(time) > 8.64e15) { 1250 return Double.NaN; 1251 } 1252 return (long)time; 1253 } 1254 1255 private static NativeDate ensureNativeDate(final Object self) { 1256 return getNativeDate(self); 1257 } 1258 1259 private static NativeDate getNativeDate(final Object self) { 1260 if (self instanceof NativeDate) { 1261 return (NativeDate)self; 1262 } else if (self != null && self == Global.instance().getDatePrototype()) { 1263 return Global.instance().DEFAULT_DATE; 1264 } else { 1265 throw typeError("not.a.date", ScriptRuntime.safeToString(self)); 1266 } 1267 } 1268 1269 private static Object getField(final Object self, final int field) { 1270 final NativeDate nd = getNativeDate(self); 1271 return (nd != null && nd.isValidDate()) ? valueFromTime(field, nd.getLocalTime()) : Double.NaN; 1272 } 1273 1274 private static Object getUTCField(final Object self, final int field) { 1275 final NativeDate nd = getNativeDate(self); 1276 return (nd != null && nd.isValidDate()) ? valueFromTime(field, nd.getTime()) : Double.NaN; 1277 } 1278 1279 private static void setFields(final NativeDate nd, final int fieldId, final Object[] args, final boolean local) { 1280 int start, length; 1281 if (fieldId < HOUR) { 1282 start = YEAR; 1283 length = 3; 1284 } else { 1285 start = HOUR; 1286 length = 4; 1287 } 1288 final double time = local ? nd.getLocalTime() : nd.getTime(); 1289 final double d[] = convertArgs(args, time, fieldId, start, length); 1290 1291 double newTime; 1292 if (d == null) { 1293 newTime = NaN; 1294 } else { 1295 if (start == YEAR) { 1296 newTime = makeDate(makeDay(d[0], d[1], d[2]), timeWithinDay(time)); 1297 } else { 1298 newTime = makeDate(day(time), makeTime(d[0], d[1], d[2], d[3])); 1299 } 1300 if (local) { 1301 newTime = utc(newTime, nd.getTimeZone()); 1302 } 1303 newTime = timeClip(newTime); 1304 } 1305 nd.setTime(newTime); 1306 } 1307 1308 private boolean isValidDate() { 1309 return !isNaN(time); 1310 } 1311 1312 private double getLocalTime() { 1313 return localTime(time, timezone); 1314 } 1315 1316 private double getTime() { 1317 return time; 1318 } 1319 1320 private void setTime(final double time) { 1321 this.time = time; 1322 } 1323 1324 private TimeZone getTimeZone() { 1325 return timezone; 1326 } 1327 1328 }