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