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