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