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