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         final String str = isValidDate() ? toISOStringImpl(this) : INVALID_DATE;
 186         return "[Date " + str + "]";
 187     }
 188 
 189     @Override
 190     public String toString() {
 191         return isValidDate() ? toString(this).toString() : INVALID_DATE;
 192     }
 193 
 194     /**
 195      * ECMA 15.9.4.2 Date.parse (string)
 196      *
 197      * @param self self reference
 198      * @param string string to parse as date
 199      * @return Date interpreted from the string, or NaN for illegal values
 200      */
 201     @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
 202     public static Object parse(final Object self, final Object string) {
 203         return parseDateString(JSType.toString(string));
 204     }
 205 
 206     /**
 207      * ECMA 15.9.4.3 Date.UTC (year, month [, date [, hours [, minutes [, seconds [, ms ] ] ] ] ] )
 208      *
 209      * @param self self reference
 210      * @param args mandatory args are year, month. Optional are date, hours, minutes, seconds and milliseconds
 211      * @return a time clip according to the ECMA specification
 212      */
 213     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 7, where = Where.CONSTRUCTOR)
 214     public static Object UTC(final Object self, final Object... args) {
 215         final NativeDate nd = new NativeDate(0);
 216         final double[] d = convertCtorArgs(args);
 217         final double time = d == null ? Double.NaN : timeClip(makeDate(d));
 218         nd.setTime(time);
 219         return time;
 220     }
 221 
 222     /**
 223      * ECMA 15.9.4.4 Date.now ( )
 224      *
 225      * @param self self reference
 226      * @return a Date that points to the current moment in time
 227      */
 228     @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
 229     public static Object now(final Object self) {
 230         return (double)System.currentTimeMillis();
 231     }
 232 
 233     /**
 234      * ECMA 15.9.5.2 Date.prototype.toString ( )
 235      *
 236      * @param self self reference
 237      * @return string value that represents the Date in the current time zone
 238      */
 239     @Function(attributes = Attribute.NOT_ENUMERABLE)
 240     public static Object toString(final Object self) {
 241         return toStringImpl(self, FORMAT_DATE_TIME);
 242     }
 243 
 244     /**
 245      * ECMA 15.9.5.3 Date.prototype.toDateString ( )
 246      *
 247      * @param self self reference
 248      * @return string value with the "date" part of the Date in the current time zone
 249      */
 250     @Function(attributes = Attribute.NOT_ENUMERABLE)
 251     public static Object toDateString(final Object self) {
 252         return toStringImpl(self, FORMAT_DATE);
 253     }
 254 
 255     /**
 256      * ECMA 15.9.5.4 Date.prototype.toTimeString ( )
 257      *
 258      * @param self self reference
 259      * @return string value with "time" part of Date in the current time zone
 260      */
 261     @Function(attributes = Attribute.NOT_ENUMERABLE)
 262     public static Object toTimeString(final Object self) {
 263         return toStringImpl(self, FORMAT_TIME);
 264     }
 265 
 266     /**
 267      * ECMA 15.9.5.5 Date.prototype.toLocaleString ( )
 268      *
 269      * @param self self reference
 270      * @return string value that represents the Data in the current time zone and locale
 271      */
 272     @Function(attributes = Attribute.NOT_ENUMERABLE)
 273     public static Object toLocaleString(final Object self) {
 274         return toStringImpl(self, FORMAT_LOCAL_DATE_TIME);
 275     }
 276 
 277     /**
 278      * ECMA 15.9.5.6 Date.prototype.toLocaleDateString ( )
 279      *
 280      * @param self self reference
 281      * @return string value with the "date" part of the Date in the current time zone and locale
 282      */
 283     @Function(attributes = Attribute.NOT_ENUMERABLE)
 284     public static Object toLocaleDateString(final Object self) {
 285         return toStringImpl(self, FORMAT_LOCAL_DATE);
 286     }
 287 
 288     /**
 289      * ECMA 15.9.5.7 Date.prototype.toLocaleTimeString ( )
 290      *
 291      * @param self self reference
 292      * @return string value with the "time" part of Date in the current time zone and locale
 293      */
 294     @Function(attributes = Attribute.NOT_ENUMERABLE)
 295     public static Object toLocaleTimeString(final Object self) {
 296         return toStringImpl(self, FORMAT_LOCAL_TIME);
 297     }
 298 
 299     /**
 300      * ECMA 15.9.5.8 Date.prototype.valueOf ( )
 301      *
 302      * @param self self reference
 303      * @return valueOf - a number which is this time value
 304      */
 305     @Function(attributes = Attribute.NOT_ENUMERABLE)
 306     public static Object valueOf(final Object self) {
 307         final NativeDate nd = getNativeDate(self);
 308         return (nd != null) ? nd.getTime() : Double.NaN;
 309     }
 310 
 311     /**
 312      * ECMA 15.9.5.9 Date.prototype.getTime ( )
 313      *
 314      * @param self self reference
 315      * @return time
 316      */
 317     @Function(attributes = Attribute.NOT_ENUMERABLE)
 318     public static Object getTime(final Object self) {
 319         final NativeDate nd = getNativeDate(self);
 320         return (nd != null) ? nd.getTime() : Double.NaN;
 321     }
 322 
 323     /**
 324      * ECMA 15.9.5.10 Date.prototype.getFullYear ( )
 325      *
 326      * @param self self reference
 327      * @return full year
 328      */
 329     @Function(attributes = Attribute.NOT_ENUMERABLE)
 330     public static Object getFullYear(final Object self) {
 331         return getField(self, YEAR);
 332     }
 333 
 334     /**
 335      * ECMA 15.9.5.11 Date.prototype.getUTCFullYear( )
 336      *
 337      * @param self self reference
 338      * @return UTC full year
 339      */
 340     @Function(attributes = Attribute.NOT_ENUMERABLE)
 341     public static Object getUTCFullYear(final Object self) {
 342         return getUTCField(self, YEAR);
 343     }
 344 
 345     /**
 346      * B.2.4 Date.prototype.getYear ( )
 347      *
 348      * @param self self reference
 349      * @return year
 350      */
 351     @Function(attributes = Attribute.NOT_ENUMERABLE)
 352     public static Object getYear(final Object self) {
 353         final NativeDate nd = getNativeDate(self);
 354         return (nd != null && nd.isValidDate()) ? (yearFromTime(nd.getLocalTime()) - 1900) : Double.NaN;
 355     }
 356 
 357     /**
 358      * ECMA 15.9.5.12 Date.prototype.getMonth ( )
 359      *
 360      * @param self self reference
 361      * @return month
 362      */
 363     @Function(attributes = Attribute.NOT_ENUMERABLE)
 364     public static Object getMonth(final Object self) {
 365         return getField(self, MONTH);
 366     }
 367 
 368     /**
 369      * ECMA 15.9.5.13 Date.prototype.getUTCMonth ( )
 370      *
 371      * @param self self reference
 372      * @return UTC month
 373      */
 374     @Function(attributes = Attribute.NOT_ENUMERABLE)
 375     public static Object getUTCMonth(final Object self) {
 376         return getUTCField(self, MONTH);
 377     }
 378 
 379     /**
 380      * ECMA 15.9.5.14 Date.prototype.getDate ( )
 381      *
 382      * @param self self reference
 383      * @return date
 384      */
 385     @Function(attributes = Attribute.NOT_ENUMERABLE)
 386     public static Object getDate(final Object self) {
 387         return getField(self, DAY);
 388     }
 389 
 390     /**
 391      * ECMA 15.9.5.15 Date.prototype.getUTCDate ( )
 392      *
 393      * @param self self reference
 394      * @return UTC Date
 395      */
 396     @Function(attributes = Attribute.NOT_ENUMERABLE)
 397     public static Object getUTCDate(final Object self) {
 398         return getUTCField(self, DAY);
 399     }
 400 
 401     /**
 402      * ECMA 15.9.5.16 Date.prototype.getDay ( )
 403      *
 404      * @param self self reference
 405      * @return day
 406      */
 407     @Function(attributes = Attribute.NOT_ENUMERABLE)
 408     public static Object getDay(final Object self) {
 409         final NativeDate nd = getNativeDate(self);
 410         return (nd != null && nd.isValidDate()) ? weekDay(nd.getLocalTime()) : Double.NaN;
 411     }
 412 
 413     /**
 414      * ECMA 15.9.5.17 Date.prototype.getUTCDay ( )
 415      *
 416      * @param self self reference
 417      * @return UTC day
 418      */
 419     @Function(attributes = Attribute.NOT_ENUMERABLE)
 420     public static Object getUTCDay(final Object self) {
 421         final NativeDate nd = getNativeDate(self);
 422         return (nd != null && nd.isValidDate()) ? weekDay(nd.getTime()) : Double.NaN;
 423     }
 424 
 425     /**
 426      * ECMA 15.9.5.18 Date.prototype.getHours ( )
 427      *
 428      * @param self self reference
 429      * @return hours
 430      */
 431     @Function(attributes = Attribute.NOT_ENUMERABLE)
 432     public static Object getHours(final Object self) {
 433         return getField(self, HOUR);
 434     }
 435 
 436     /**
 437      * ECMA 15.9.5.19 Date.prototype.getUTCHours ( )
 438      *
 439      * @param self self reference
 440      * @return UTC hours
 441      */
 442     @Function(attributes = Attribute.NOT_ENUMERABLE)
 443     public static Object getUTCHours(final Object self) {
 444         return getUTCField(self, HOUR);
 445     }
 446 
 447     /**
 448      * ECMA 15.9.5.20 Date.prototype.getMinutes ( )
 449      *
 450      * @param self self reference
 451      * @return minutes
 452      */
 453     @Function(attributes = Attribute.NOT_ENUMERABLE)
 454     public static Object getMinutes(final Object self) {
 455         return getField(self, MINUTE);
 456     }
 457 
 458     /**
 459      * ECMA 15.9.5.21 Date.prototype.getUTCMinutes ( )
 460      *
 461      * @param self self reference
 462      * @return UTC minutes
 463      */
 464     @Function(attributes = Attribute.NOT_ENUMERABLE)
 465     public static Object getUTCMinutes(final Object self) {
 466         return getUTCField(self, MINUTE);
 467     }
 468 
 469     /**
 470      * ECMA 15.9.5.22 Date.prototype.getSeconds ( )
 471      *
 472      * @param self self reference
 473      * @return seconds
 474      */
 475     @Function(attributes = Attribute.NOT_ENUMERABLE)
 476     public static Object getSeconds(final Object self) {
 477         return getField(self, SECOND);
 478     }
 479 
 480     /**
 481      * ECMA 15.9.5.23 Date.prototype.getUTCSeconds ( )
 482      *
 483      * @param self self reference
 484      * @return UTC seconds
 485      */
 486     @Function(attributes = Attribute.NOT_ENUMERABLE)
 487     public static Object getUTCSeconds(final Object self) {
 488         return getUTCField(self, SECOND);
 489     }
 490 
 491     /**
 492      * ECMA 15.9.5.24 Date.prototype.getMilliseconds ( )
 493      *
 494      * @param self self reference
 495      * @return milliseconds
 496      */
 497     @Function(attributes = Attribute.NOT_ENUMERABLE)
 498     public static Object getMilliseconds(final Object self) {
 499         return getField(self, MILLISECOND);
 500     }
 501 
 502     /**
 503      * ECMA 15.9.5.25 Date.prototype.getUTCMilliseconds ( )
 504      *
 505      * @param self self reference
 506      * @return UTC milliseconds
 507      */
 508     @Function(attributes = Attribute.NOT_ENUMERABLE)
 509     public static Object getUTCMilliseconds(final Object self) {
 510         return getUTCField(self, MILLISECOND);
 511     }
 512 
 513     /**
 514      * ECMA 15.9.5.26 Date.prototype.getTimezoneOffset ( )
 515      *
 516      * @param self self reference
 517      * @return time zone offset or NaN if N/A
 518      */
 519     @Function(attributes = Attribute.NOT_ENUMERABLE)
 520     public static Object getTimezoneOffset(final Object self) {
 521         final NativeDate nd = getNativeDate(self);
 522         if (nd != null) {
 523             final long msec = (long) nd.getTime();
 524             return - nd.getTimeZone().getOffset(msec) / msPerMinute;
 525         }
 526         return Double.NaN;
 527     }
 528 
 529     /**
 530      * ECMA 15.9.5.27 Date.prototype.setTime (time)
 531      *
 532      * @param self self reference
 533      * @param time time
 534      * @return time
 535      */
 536     @Function(attributes = Attribute.NOT_ENUMERABLE)
 537     public static Object setTime(final Object self, final Object time) {
 538         final double     num = timeClip(JSType.toNumber(time));
 539         final NativeDate nd  = getNativeDate(self);

 540         nd.setTime(num);
 541         return num;
 542     }
 543 
 544     /**
 545      * ECMA 15.9.5.28 Date.prototype.setMilliseconds (ms)
 546      *
 547      * @param self self reference
 548      * @param args milliseconds
 549      * @return time
 550      */
 551     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
 552     public static Object setMilliseconds(final Object self, final Object... args) {
 553         final NativeDate nd = getNativeDate(self);
 554         if (nd.isValidDate()) {
 555             setFields(nd, MILLISECOND, args, true);
 556         }
 557         return nd.getTime();
 558     }
 559 
 560     /**
 561      * ECMA 15.9.5.29 Date.prototype.setUTCMilliseconds (ms)
 562      *
 563      * @param self self reference
 564      * @param args utc milliseconds
 565      * @return time
 566      */
 567     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
 568     public static Object setUTCMilliseconds(final Object self, final Object... args) {
 569         final NativeDate nd = getNativeDate(self);
 570         if (nd.isValidDate()) {
 571             setFields(nd, MILLISECOND, args, false);
 572         }
 573         return nd.getTime();
 574     }
 575 
 576     /**
 577      * ECMA 15.9.5.30 Date.prototype.setSeconds (sec [, ms ] )
 578      *
 579      * @param self self reference
 580      * @param args seconds (milliseconds optional second argument)
 581      * @return time
 582      */
 583     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2)
 584     public static Object setSeconds(final Object self, final Object... args) {
 585         final NativeDate nd = getNativeDate(self);
 586         if (nd.isValidDate()) {
 587             setFields(nd, SECOND, args, true);
 588         }
 589         return nd.getTime();
 590     }
 591 
 592     /**
 593      * ECMA 15.9.5.31 Date.prototype.setUTCSeconds (sec [, ms ] )
 594      *
 595      * @param self self reference
 596      * @param args UTC seconds (milliseconds optional second argument)
 597      * @return time
 598      */
 599     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2)
 600     public static Object setUTCSeconds(final Object self, final Object... args) {
 601         final NativeDate nd = getNativeDate(self);
 602         if (nd.isValidDate()) {
 603             setFields(nd, SECOND, args, false);
 604         }
 605         return nd.getTime();
 606     }
 607 
 608     /**
 609      * ECMA 15.9.5.32 Date.prototype.setMinutes (min [, sec [, ms ] ] )
 610      *
 611      * @param self self reference
 612      * @param args minutes (seconds and milliseconds are optional second and third arguments)
 613      * @return time
 614      */
 615     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 3)
 616     public static Object setMinutes(final Object self, final Object... args) {
 617         final NativeDate nd = getNativeDate(self);
 618         if (nd.isValidDate()) {
 619             setFields(nd, MINUTE, args, true);
 620         }
 621         return nd.getTime();
 622     }
 623 
 624     /**
 625      * ECMA 15.9.5.33 Date.prototype.setUTCMinutes (min [, sec [, ms ] ] )
 626      *
 627      * @param self self reference
 628      * @param args minutes (seconds and milliseconds are optional second and third arguments)
 629      * @return time
 630      */
 631     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 3)
 632     public static Object setUTCMinutes(final Object self, final Object... args) {
 633         final NativeDate nd = getNativeDate(self);
 634         if (nd.isValidDate()) {
 635             setFields(nd, MINUTE, args, false);
 636         }
 637         return nd.getTime();
 638     }
 639 
 640     /**
 641      * ECMA 15.9.5.34 Date.prototype.setHours (hour [, min [, sec [, ms ] ] ] )
 642      *
 643      * @param self self reference
 644      * @param args hour (optional arguments after are minutes, seconds, milliseconds)
 645      * @return time
 646      */
 647     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 4)
 648     public static Object setHours(final Object self, final Object... args) {
 649         final NativeDate nd = getNativeDate(self);
 650         if (nd.isValidDate()) {
 651             setFields(nd, HOUR, args, true);
 652         }
 653         return nd.getTime();
 654     }
 655 
 656     /**
 657      * ECMA 15.9.5.35 Date.prototype.setUTCHours (hour [, min [, sec [, ms ] ] ] )
 658      *
 659      * @param self self reference
 660      * @param args hour (optional arguments after are minutes, seconds, milliseconds)
 661      * @return time
 662      */
 663     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 4)
 664     public static Object setUTCHours(final Object self, final Object... args) {
 665         final NativeDate nd = getNativeDate(self);
 666         if (nd.isValidDate()) {
 667             setFields(nd, HOUR, args, false);
 668         }
 669         return nd.getTime();
 670     }
 671 
 672     /**
 673      * ECMA 15.9.5.36 Date.prototype.setDate (date)
 674      *
 675      * @param self self reference
 676      * @param args date
 677      * @return time
 678      */
 679     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
 680     public static Object setDate(final Object self, final Object... args) {
 681         final NativeDate nd = getNativeDate(self);
 682         if (nd.isValidDate()) {
 683             setFields(nd, DAY, args, true);
 684         }
 685         return nd.getTime();
 686     }
 687 
 688     /**
 689      * ECMA 15.9.5.37 Date.prototype.setUTCDate (date)
 690      *
 691      * @param self self reference
 692      * @param args UTC date
 693      * @return time
 694      */
 695     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
 696     public static Object setUTCDate(final Object self, final Object... args) {
 697         final NativeDate nd = getNativeDate(self);
 698         if (nd.isValidDate()) {
 699             setFields(nd, DAY, args, false);
 700         }
 701         return nd.getTime();
 702     }
 703 
 704     /**
 705      * ECMA 15.9.5.38 Date.prototype.setMonth (month [, date ] )
 706      *
 707      * @param self self reference
 708      * @param args month (optional second argument is date)
 709      * @return time
 710      */
 711     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2)
 712     public static Object setMonth(final Object self, final Object... args) {
 713         final NativeDate nd = getNativeDate(self);
 714         if (nd.isValidDate()) {
 715             setFields(nd, MONTH, args, true);
 716         }
 717         return nd.getTime();
 718     }
 719 
 720     /**
 721      * ECMA 15.9.5.39 Date.prototype.setUTCMonth (month [, date ] )
 722      *
 723      * @param self self reference
 724      * @param args UTC month (optional second argument is date)
 725      * @return time
 726      */
 727     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2)
 728     public static Object setUTCMonth(final Object self, final Object... args) {
 729         final NativeDate nd = ensureNativeDate(self);
 730         if (nd.isValidDate()) {
 731             setFields(nd, MONTH, args, false);
 732         }
 733         return nd.getTime();
 734     }
 735 
 736     /**
 737      * ECMA 15.9.5.40 Date.prototype.setFullYear (year [, month [, date ] ] )
 738      *
 739      * @param self self reference
 740      * @param args year (optional second and third arguments are month and date)
 741      * @return time
 742      */
 743     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 3)
 744     public static Object setFullYear(final Object self, final Object... args) {
 745         final NativeDate nd   = ensureNativeDate(self);
 746         if (nd.isValidDate()) {
 747             setFields(nd, YEAR, args, true);
 748         } else {
 749             final double[] d = convertArgs(args, 0, YEAR, YEAR, 3);

 750             nd.setTime(timeClip(utc(makeDate(makeDay(d[0], d[1], d[2]), 0), nd.getTimeZone())));



 751         }
 752         return nd.getTime();
 753     }
 754 
 755     /**
 756      * ECMA 15.9.5.41 Date.prototype.setUTCFullYear (year [, month [, date ] ] )
 757      *
 758      * @param self self reference
 759      * @param args UTC full year (optional second and third arguments are month and date)
 760      * @return time
 761      */
 762     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 3)
 763     public static Object setUTCFullYear(final Object self, final Object... args) {
 764         final NativeDate nd   = ensureNativeDate(self);
 765         if (nd.isValidDate()) {
 766             setFields(nd, YEAR, args, false);
 767         } else {
 768             final double[] d = convertArgs(args, 0, YEAR, YEAR, 3);
 769             nd.setTime(timeClip(makeDate(makeDay(d[0], d[1], d[2]), 0)));
 770         }
 771         return nd.getTime();
 772     }
 773 
 774     /**
 775      * ECMA B.2.5 Date.prototype.setYear (year)
 776      *
 777      * @param self self reference
 778      * @param year year
 779      * @return NativeDate
 780      */
 781     @Function(attributes = Attribute.NOT_ENUMERABLE)
 782     public static Object setYear(final Object self, final Object year) {
 783         final NativeDate nd = getNativeDate(self);
 784         if (isNaN(nd.getTime())) {
 785             return null;
 786         }
 787 
 788         final double yearNum = JSType.toNumber(year);
 789         if (isNaN(yearNum)) {
 790             nd.setTime(NaN);
 791             return nd;
 792         }
 793         int yearInt = JSType.toInteger(yearNum);
 794         if (0 <= yearInt && yearInt <= 99) {
 795             yearInt += 1900;
 796         }
 797         setFields(nd, YEAR, new Object[] {yearInt}, true);
 798 
 799         return nd;
 800     }
 801 
 802     /**
 803      * ECMA 15.9.5.42 Date.prototype.toUTCString ( )
 804      *
 805      * @param self self reference
 806      * @return string representation of date
 807      */
 808     @Function(attributes = Attribute.NOT_ENUMERABLE)
 809     public static Object toUTCString(final Object self) {
 810         return toGMTStringImpl(self);
 811     }
 812 
 813     /**
 814      * ECMA B.2.6 Date.prototype.toGMTString ( )
 815      *
 816      * See {@link NativeDate#toUTCString(Object)}
 817      *
 818      * @param self self reference
 819      * @return string representation of date
 820      */
 821     @Function(attributes = Attribute.NOT_ENUMERABLE)
 822     public static Object toGMTString(final Object self) {
 823         return toGMTStringImpl(self);
 824     }
 825 
 826     /**
 827      * ECMA 15.9.5.43 Date.prototype.toISOString ( )
 828      *
 829      * @param self self reference
 830      * @return string representation of date
 831      */
 832     @Function(attributes = Attribute.NOT_ENUMERABLE)
 833     public static Object toISOString(final Object self) {
 834         return toISOStringImpl(self);
 835     }
 836 
 837     /**
 838      * ECMA 15.9.5.44 Date.prototype.toJSON ( key )
 839      *
 840      * Provides a string representation of this Date for use by {@link NativeJSON#stringify(Object, Object, Object, Object)}
 841      *
 842      * @param self self reference
 843      * @param key ignored
 844      * @return JSON representation of this date
 845      */
 846     @Function(attributes = Attribute.NOT_ENUMERABLE)
 847     public static Object toJSON(final Object self, final Object key) {
 848         // NOTE: Date.prototype.toJSON is generic. Accepts other objects as well.
 849         final Object selfObj = Global.toObject(self);
 850         if (!(selfObj instanceof ScriptObject)) {
 851             return null;
 852         }
 853         final ScriptObject sobj  = (ScriptObject)selfObj;
 854         final Object       value = sobj.getDefaultValue(Number.class);
 855         if (value instanceof Number) {
 856             final double num = ((Number)value).doubleValue();
 857             if (isInfinite(num) || isNaN(num)) {
 858                 return null;
 859             }
 860         }
 861 
 862         try {
 863             final Object func = TO_ISO_STRING.getGetter().invokeExact(sobj);
 864             if (func instanceof ScriptFunction) {
 865                 return TO_ISO_STRING.getInvoker().invokeExact(func, sobj, key);
 866             }
 867             throw typeError("not.a.function", ScriptRuntime.safeToString(func));
 868         } catch (final RuntimeException | Error e) {
 869             throw e;
 870         } catch (final Throwable t) {
 871             throw new RuntimeException(t);
 872         }
 873     }
 874 
 875     // -- Internals below this point
 876 
 877     private static double parseDateString(final String str) {
 878 
 879         final DateParser parser = new DateParser(str);
 880         if (parser.parse()) {
 881             final Integer[] fields = parser.getDateFields();
 882             double d = makeDate(fields);
 883             if (fields[DateParser.TIMEZONE] != null) {
 884                 d -= fields[DateParser.TIMEZONE] * 60000;
 885             } else {
 886                 d = utc(d, Global.getEnv()._timezone);
 887             }
 888             d = timeClip(d);
 889             return d;
 890         }
 891 
 892         return Double.NaN;
 893     }
 894 
 895     private static void zeroPad(final StringBuilder sb, final int n, final int length) {
 896         for (int l = 1, d = 10; l < length; l++, d *= 10) {
 897             if (n < d) {
 898                 sb.append('0');
 899             }
 900         }
 901         sb.append(n);
 902     }
 903 
 904     private static String toStringImpl(final Object self, final int format) {
 905         final NativeDate nd = getNativeDate(self);
 906 
 907         if (nd != null && nd.isValidDate()) {
 908             final StringBuilder sb = new StringBuilder(40);
 909             final double t = nd.getLocalTime();
 910 
 911             switch (format) {
 912 
 913                 case FORMAT_DATE_TIME:
 914                 case FORMAT_DATE :
 915                 case FORMAT_LOCAL_DATE_TIME:
 916                     // EEE MMM dd yyyy
 917                     sb.append(weekDays[(int) weekDay(t)])
 918                             .append(' ')
 919                             .append(months[(int) monthFromTime(t)])
 920                             .append(' ');
 921                     zeroPad(sb, (int) dayFromTime(t), 2);
 922                     sb.append(' ');
 923                     zeroPad(sb, (int) yearFromTime(t), 4);
 924                     if (format == FORMAT_DATE) {
 925                         break;
 926                     }
 927                     sb.append(' ');
 928 
 929                     //$FALL-THROUGH$
 930                 case FORMAT_TIME:
 931                     final TimeZone tz = nd.getTimeZone();
 932                     final double utcTime = nd.getTime();
 933                     int offset = tz.getOffset((long) utcTime) / 60000;
 934                     final boolean inDaylightTime = offset != tz.getRawOffset() / 60000;
 935                     // Convert minutes to HHmm timezone offset
 936                     offset = (offset / 60) * 100 + offset % 60;
 937 
 938                     // HH:mm:ss GMT+HHmm
 939                     zeroPad(sb, (int) hourFromTime(t), 2);
 940                     sb.append(':');
 941                     zeroPad(sb, (int) minFromTime(t), 2);
 942                     sb.append(':');
 943                     zeroPad(sb, (int) secFromTime(t), 2);
 944                     sb.append(" GMT")
 945                             .append(offset < 0 ? '-' : '+');
 946                     zeroPad(sb, Math.abs(offset), 4);
 947                     sb.append(" (")
 948                             .append(tz.getDisplayName(inDaylightTime, TimeZone.SHORT, Locale.US))
 949                             .append(')');
 950                     break;
 951 
 952                 case FORMAT_LOCAL_DATE:
 953                     // yyyy-MM-dd
 954                     zeroPad(sb, (int) yearFromTime(t), 4);
 955                     sb.append('-');
 956                     zeroPad(sb, (int) monthFromTime(t) + 1, 2);
 957                     sb.append('-');
 958                     zeroPad(sb, (int) dayFromTime(t), 2);
 959                     break;
 960 
 961                 case FORMAT_LOCAL_TIME:
 962                     // HH:mm:ss
 963                     zeroPad(sb, (int) hourFromTime(t), 2);
 964                     sb.append(':');
 965                     zeroPad(sb, (int) minFromTime(t), 2);
 966                     sb.append(':');
 967                     zeroPad(sb, (int) secFromTime(t), 2);
 968                     break;
 969 
 970                 default:
 971                     throw new IllegalArgumentException("format: " + format);
 972             }
 973 
 974             return sb.toString();
 975         }
 976 
 977         return INVALID_DATE;
 978     }
 979 
 980     private static String toGMTStringImpl(final Object self) {
 981         final NativeDate nd = getNativeDate(self);
 982 
 983         if (nd != null && nd.isValidDate()) {
 984             final StringBuilder sb = new StringBuilder(29);
 985             final double t = nd.getTime();
 986             // EEE, dd MMM yyyy HH:mm:ss z
 987             sb.append(weekDays[(int) weekDay(t)])
 988                     .append(", ");
 989             zeroPad(sb, (int) dayFromTime(t), 2);
 990             sb.append(' ')
 991                     .append(months[(int) monthFromTime(t)])
 992                     .append(' ');
 993             zeroPad(sb, (int) yearFromTime(t), 4);
 994             sb.append(' ');
 995             zeroPad(sb, (int) hourFromTime(t), 2);
 996             sb.append(':');
 997             zeroPad(sb, (int) minFromTime(t), 2);
 998             sb.append(':');
 999             zeroPad(sb, (int) secFromTime(t), 2);
1000             sb.append(" GMT");
1001             return sb.toString();
1002         }
1003 
1004         throw rangeError("invalid.date");
1005     }
1006 
1007     private static String toISOStringImpl(final Object self) {
1008         final NativeDate nd = getNativeDate(self);
1009 
1010         if (nd != null && nd.isValidDate()) {
1011             final StringBuilder sb = new StringBuilder(24);
1012             final double t = nd.getTime();
1013             // yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
1014             zeroPad(sb, (int) yearFromTime(t), 4);
1015             sb.append('-');
1016             zeroPad(sb, (int) monthFromTime(t) + 1, 2);
1017             sb.append('-');
1018             zeroPad(sb, (int) dayFromTime(t), 2);
1019             sb.append('T');
1020             zeroPad(sb, (int) hourFromTime(t), 2);
1021             sb.append(':');
1022             zeroPad(sb, (int) minFromTime(t), 2);
1023             sb.append(':');
1024             zeroPad(sb, (int) secFromTime(t), 2);
1025             sb.append('.');
1026             zeroPad(sb, (int) msFromTime(t), 3);
1027             sb.append("Z");
1028             return sb.toString();
1029         }
1030 
1031         throw rangeError("invalid.date");
1032     }
1033 
1034     // ECMA 15.9.1.2 Day (t)
1035     private static double day(final double t) {
1036         return Math.floor(t / msPerDay);
1037     }
1038 
1039     // ECMA 15.9.1.2 TimeWithinDay (t)
1040     private static double timeWithinDay(final double t) {
1041         return t % msPerDay;
1042     }
1043 
1044     // ECMA 15.9.1.3 InLeapYear (t)
1045     private static boolean isLeapYear(final int y) {
1046         return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0);
1047     }
1048 
1049     // ECMA 15.9.1.3 DaysInYear (y)
1050     private static int daysInYear(final int y) {
1051         return isLeapYear(y) ? 366 : 365;
1052     }
1053 
1054     // ECMA 15.9.1.3 DayFromYear (y)
1055     private static double dayFromYear(final double y) {
1056         return 365 * (y - 1970)
1057                 + Math.floor((y -1969) / 4.0)
1058                 - Math.floor((y - 1901) / 100.0)
1059                 + Math.floor((y - 1601) / 400.0);
1060     }
1061 
1062     // ECMA 15.9.1.3 Year Number
1063     private static double timeFromYear(final double y) {
1064         return dayFromYear(y) * msPerDay;
1065     }
1066 
1067     private static double yearFromTime(final double t) {
1068         double y = Math.floor(t / (msPerDay * 365.2425)) + 1970;
1069         final double t2 = timeFromYear(y);
1070         if (t2 > t) {
1071             y--;
1072         } else if (t2 + msPerDay * daysInYear((int) y) <= t) {
1073             y++;
1074         }
1075         return y;
1076     }
1077 
1078     private static double dayWithinYear(final double t, final double year) {
1079         return day(t) - dayFromYear(year);
1080     }
1081 
1082     private static double monthFromTime(final double t) {
1083         final double year = yearFromTime(t);
1084         final double day = dayWithinYear(t, year);
1085         final int[] firstDay = firstDayInMonth[isLeapYear((int) year) ? 1 : 0];
1086         int month = 0;
1087 
1088         while (month < 11 && firstDay[month + 1] <= day) {
1089             month++;
1090         }
1091         return month;
1092     }
1093 
1094     private static double dayFromTime(final double t)  {
1095         final double year = yearFromTime(t);
1096         final double day = dayWithinYear(t, year);
1097         final int[] firstDay = firstDayInMonth[isLeapYear((int) year) ? 1 : 0];
1098         int month = 0;
1099 
1100         while (month < 11 && firstDay[month + 1] <= day) {
1101             month++;
1102         }
1103         return 1 + day - firstDay[month];
1104     }
1105 
1106     private static int dayFromMonth(final int month, final int year) {
1107         assert(month >= 0 && month <= 11);
1108         final int[] firstDay = firstDayInMonth[isLeapYear(year) ? 1 : 0];
1109         return firstDay[month];
1110     }
1111 
1112     private static double weekDay(final double time) {
1113         if (isNaN(time)) {
1114             return NaN;
1115         }
1116         final double day = (day(time) + 4) % 7;
1117         return day < 0 ? day + 7 : day;
1118     }
1119 
1120     // ECMA 15.9.1.9 LocalTime
1121     private static double localTime(final double time, final TimeZone tz) {
1122         return time + tz.getOffset((long) time);
1123     }
1124 
1125     // ECMA 15.9.1.9 UTC
1126     private static double utc(final double time, final TimeZone tz) {
1127         return time - tz.getOffset((long) (time - tz.getRawOffset()));
1128     }
1129 
1130     // ECMA 15.9.1.10 Hours, Minutes, Second, and Milliseconds
1131     private static double hourFromTime(final double t) {
1132         final double h = Math.floor(t / msPerHour) % hoursPerDay;
1133         return h < 0 ? h + hoursPerDay: h;
1134     }
1135     private static double minFromTime(final double t) {
1136         final double m = Math.floor(t / msPerMinute) % minutesPerHour;
1137         return m < 0 ? m + minutesPerHour : m;
1138     }
1139 
1140     private static double secFromTime(final double t) {
1141         final double s = Math.floor(t / msPerSecond) % secondsPerMinute;
1142         return s < 0 ? s + secondsPerMinute : s;
1143     }
1144 
1145     private static double msFromTime(final double t) {
1146         final double m = t % msPerSecond;
1147         return m < 0 ? m + msPerSecond : m;
1148     }
1149 
1150     private static double valueFromTime(final int unit, final double t) {
1151         switch (unit) {
1152             case YEAR: return yearFromTime(t);
1153             case MONTH: return monthFromTime(t);
1154             case DAY: return dayFromTime(t);
1155             case HOUR: return hourFromTime(t);
1156             case MINUTE: return minFromTime(t);
1157             case SECOND: return secFromTime(t);
1158             case MILLISECOND: return msFromTime(t);
1159             default: throw new IllegalArgumentException(Integer.toString(unit));
1160         }
1161     }
1162 
1163     // ECMA 15.9.1.11 MakeTime (hour, min, sec, ms)
1164     private static double makeTime(final double hour, final double min, final double sec, final double ms) {
1165         return hour * 3600000 + min * 60000 + sec * 1000 + ms;
1166     }
1167 
1168     // ECMA 15.9.1.12 MakeDay (year, month, date)
1169     private static double makeDay(final double year, final double month, final double date) {
1170         final double y = year + Math.floor(month / 12);
1171         double m = month % 12;
1172         if (m < 0) {
1173             m += 12;
1174         }
1175         double d = Math.floor(dayFromYear(y));
1176         d += dayFromMonth((int) m, (int) y);
1177 
1178         return d + date - 1;
1179     }
1180 
1181     // ECMA 15.9.1.13 MakeDate (day, time)
1182     private static double makeDate(final double day, final double time) {
1183         return day * msPerDay + time;
1184     }
1185 
1186 
1187     private static double makeDate(final Integer[] d) {
1188         final double time = makeDay(d[0], d[1], d[2]) * msPerDay;
1189         return time + makeTime(d[3], d[4], d[5], d[6]);
1190     }
1191 
1192     private static double makeDate(final double[] d) {
1193         final double time = makeDay(d[0], d[1], d[2]) * msPerDay;
1194         return time + makeTime(d[3], d[4], d[5], d[6]);
1195     }
1196 
1197     // Convert Date constructor args, checking for NaN, filling in defaults etc.
1198     private static double[] convertCtorArgs(final Object[] args) {
1199         final double[] d = new double[7];
1200         boolean nullReturn = false;
1201 
1202         // should not bailout on first NaN or infinite. Need to convert all
1203         // subsequent args for possible side-effects via valueOf/toString overrides
1204         // on argument objects.
1205         for (int i = 0; i < d.length; i++) {
1206             if (i < args.length) {
1207                 final double darg = JSType.toNumber(args[i]);
1208                 if (isNaN(darg) || isInfinite(darg)) {
1209                     nullReturn = true;
1210                 }
1211 
1212                 d[i] = (long)darg;
1213             } else {
1214                 d[i] = i == 2 ? 1 : 0; // day in month defaults to 1
1215             }
1216         }
1217 
1218         if (0 <= d[0] && d[0] <= 99) {
1219             d[0] += 1900;
1220         }
1221 
1222         return nullReturn? null : d;
1223     }
1224 
1225     // This method does the hard work for all setter methods: If a value is provided
1226     // as argument it is used, otherwise the value is calculated from the existing time value.
1227     private static double[] convertArgs(final Object[] args, final double time, final int fieldId, final int start, final int length) {
1228         final double[] d = new double[length];
1229         boolean nullReturn = false;
1230 
1231         // Need to call toNumber on all args for side-effects - even if an argument
1232         // fails to convert to number, subsequent toNumber calls needed for possible
1233         // side-effects via valueOf/toString overrides.
1234         for (int i = start; i < start + length; i++) {
1235             if (fieldId <= i && i < fieldId + args.length) {
1236                 final double darg = JSType.toNumber(args[i - fieldId]);
1237                 if (isNaN(darg) || isInfinite(darg)) {
1238                     nullReturn = true;
1239                 }
1240 
1241                 d[i - start] = (long) darg;
1242             } else {
1243                 // Date.prototype.set* methods require first argument to be defined
1244                 if (i == fieldId) {
1245                     nullReturn = true;
1246                 }
1247 
1248                 if (! nullReturn) {
1249                     d[i - start] = valueFromTime(i, time);
1250                 }
1251             }
1252         }
1253 
1254         return nullReturn? null : d;
1255     }
1256 
1257     // ECMA 15.9.1.14 TimeClip (time)
1258     private static double timeClip(final double time) {
1259         if (isInfinite(time) || isNaN(time) || Math.abs(time) > 8.64e15) {
1260             return Double.NaN;
1261         }
1262         return (long)time;
1263     }
1264 
1265     private static NativeDate ensureNativeDate(final Object self) {
1266         return getNativeDate(self);
1267     }
1268 
1269     private static NativeDate getNativeDate(final Object self) {
1270         if (self instanceof NativeDate) {
1271             return (NativeDate)self;
1272         } else if (self != null && self == Global.instance().getDatePrototype()) {
1273             return Global.instance().DEFAULT_DATE;
1274         } else {
1275             throw typeError("not.a.date", ScriptRuntime.safeToString(self));
1276         }
1277     }
1278 
1279     private static Object getField(final Object self, final int field) {
1280         final NativeDate nd = getNativeDate(self);
1281         return (nd != null && nd.isValidDate()) ? valueFromTime(field, nd.getLocalTime()) : Double.NaN;
1282     }
1283 
1284     private static Object getUTCField(final Object self, final int field) {
1285         final NativeDate nd = getNativeDate(self);
1286         return (nd != null && nd.isValidDate()) ? valueFromTime(field, nd.getTime()) : Double.NaN;
1287     }
1288 
1289     private static void setFields(final NativeDate nd, final int fieldId, final Object[] args, final boolean local) {
1290         int start, length;
1291         if (fieldId < HOUR) {
1292             start = YEAR;
1293             length = 3;
1294         } else {
1295             start = HOUR;
1296             length = 4;
1297         }
1298         final double time = local ? nd.getLocalTime() : nd.getTime();
1299         final double d[] = convertArgs(args, time, fieldId, start, length);




1300 
1301         double newTime;
1302         if (d == null) {
1303             newTime = NaN;
1304         } else {
1305             if (start == YEAR) {
1306                 newTime = makeDate(makeDay(d[0], d[1], d[2]), timeWithinDay(time));
1307             } else {
1308                 newTime = makeDate(day(time), makeTime(d[0], d[1], d[2], d[3]));
1309             }
1310             if (local) {
1311                 newTime = utc(newTime, nd.getTimeZone());
1312             }
1313             newTime = timeClip(newTime);
1314         }
1315         nd.setTime(newTime);
1316     }
1317 
1318     private boolean isValidDate() {
1319         return !isNaN(time);
1320     }
1321 
1322     private double getLocalTime() {
1323         return localTime(time, timezone);
1324     }
1325 
1326     private double getTime() {
1327         return time;
1328     }
1329 
1330     private void setTime(final double time) {
1331         this.time = time;
1332     }
1333 
1334     private TimeZone getTimeZone() {
1335         return timezone;
1336     }
1337 
1338 }
--- EOF ---