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