src/jdk/nashorn/internal/objects/NativeDate.java

Thu, 24 May 2018 16:39:31 +0800

author
aoqi
date
Thu, 24 May 2018 16:39:31 +0800
changeset 1959
61ffdd1b89f2
parent 1721
bfc671539e50
parent 1490
d85f981c8cf8
permissions
-rw-r--r--

Merge

     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  */
    26 package jdk.nashorn.internal.objects;
    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;
    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.SpecializedFunction;
    42 import jdk.nashorn.internal.objects.annotations.Where;
    43 import jdk.nashorn.internal.parser.DateParser;
    44 import jdk.nashorn.internal.runtime.JSType;
    45 import jdk.nashorn.internal.runtime.PropertyMap;
    46 import jdk.nashorn.internal.runtime.ScriptEnvironment;
    47 import jdk.nashorn.internal.runtime.ScriptObject;
    48 import jdk.nashorn.internal.runtime.ScriptRuntime;
    49 import jdk.nashorn.internal.runtime.linker.Bootstrap;
    50 import jdk.nashorn.internal.runtime.linker.InvokeByName;
    52 /**
    53  * ECMA 15.9 Date Objects
    54  *
    55  */
    56 @ScriptClass("Date")
    57 public final class NativeDate extends ScriptObject {
    59     private static final String INVALID_DATE = "Invalid Date";
    61     private static final int YEAR        = 0;
    62     private static final int MONTH       = 1;
    63     private static final int DAY         = 2;
    64     private static final int HOUR        = 3;
    65     private static final int MINUTE      = 4;
    66     private static final int SECOND      = 5;
    67     private static final int MILLISECOND = 6;
    69     private static final int FORMAT_DATE_TIME       = 0;
    70     private static final int FORMAT_DATE            = 1;
    71     private static final int FORMAT_TIME            = 2;
    72     private static final int FORMAT_LOCAL_DATE_TIME = 3;
    73     private static final int FORMAT_LOCAL_DATE      = 4;
    74     private static final int FORMAT_LOCAL_TIME      = 5;
    76     // Constants defined in ECMA 15.9.1.10
    77     private static final int    hoursPerDay      = 24;
    78     private static final int    minutesPerHour   = 60;
    79     private static final int    secondsPerMinute = 60;
    80     private static final int    msPerSecond   = 1_000;
    81     private static final int    msPerMinute  = 60_000;
    82     private static final double msPerHour = 3_600_000;
    83     private static final double msPerDay = 86_400_000;
    85     private static int[][] firstDayInMonth = {
    86             {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}, // normal year
    87             {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}  // leap year
    88     };
    90     private static String[] weekDays = {
    91             "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
    92     };
    94     private static String[] months = {
    95             "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
    96     };
    98     private static final Object TO_ISO_STRING = new Object();
   100     private static InvokeByName getTO_ISO_STRING() {
   101         return Global.instance().getInvokeByName(TO_ISO_STRING,
   102                 new Callable<InvokeByName>() {
   103                     @Override
   104                     public InvokeByName call() {
   105                         return new InvokeByName("toISOString", ScriptObject.class, Object.class, Object.class);
   106                     }
   107                 });
   108     }
   110     private double time;
   111     private final TimeZone timezone;
   113     // initialized by nasgen
   114     private static PropertyMap $nasgenmap$;
   116     private NativeDate(final double time, final ScriptObject proto, final PropertyMap map) {
   117         super(proto, map);
   118         final ScriptEnvironment env = Global.getEnv();
   120         this.time = time;
   121         this.timezone = env._timezone;
   122     }
   124     NativeDate(final double time, final ScriptObject proto) {
   125         this(time, proto, $nasgenmap$);
   126     }
   128     NativeDate(final double time, final Global global) {
   129         this(time, global.getDatePrototype(), $nasgenmap$);
   130     }
   132     private NativeDate (final double time) {
   133         this(time, Global.instance());
   134     }
   136     private NativeDate() {
   137         this(System.currentTimeMillis());
   138     }
   140     @Override
   141     public String getClassName() {
   142         return "Date";
   143     }
   145     // ECMA 8.12.8 [[DefaultValue]] (hint)
   146     @Override
   147     public Object getDefaultValue(final Class<?> hint) {
   148         // When the [[DefaultValue]] internal method of O is called with no hint,
   149         // then it behaves as if the hint were Number, unless O is a Date object
   150         // in which case it behaves as if the hint were String.
   151         return super.getDefaultValue(hint == null ? String.class : hint);
   152     }
   154     /**
   155      * Constructor - ECMA 15.9.3.1 new Date
   156      *
   157      * @param isNew is this Date constructed with the new operator
   158      * @param self  self references
   159      * @return Date representing now
   160      */
   161     @SpecializedFunction(isConstructor=true)
   162     public static Object construct(final boolean isNew, final Object self) {
   163         final NativeDate result = new NativeDate();
   164         return isNew ? result : toStringImpl(result, FORMAT_DATE_TIME);
   165     }
   167     /**
   168      * Constructor - ECMA 15.9.3.1 new Date (year, month [, date [, hours [, minutes [, seconds [, ms ] ] ] ] ] )
   169      *
   170      * @param isNew is this Date constructed with the new operator
   171      * @param self  self reference
   172      * @param args  arguments
   173      * @return new Date
   174      */
   175     @Constructor(arity = 7)
   176     public static Object construct(final boolean isNew, final Object self, final Object... args) {
   177         if (! isNew) {
   178             return toStringImpl(new NativeDate(), FORMAT_DATE_TIME);
   179         }
   181         NativeDate result;
   182         switch (args.length) {
   183         case 0:
   184             result = new NativeDate();
   185             break;
   187         case 1:
   188             double num;
   189             final Object arg = JSType.toPrimitive(args[0]);
   190             if (JSType.isString(arg)) {
   191                 num = parseDateString(arg.toString());
   192             } else {
   193                 num = timeClip(JSType.toNumber(args[0]));
   194             }
   195             result = new NativeDate(num);
   196             break;
   198         default:
   199             result = new NativeDate(0);
   200             final double[] d = convertCtorArgs(args);
   201             if (d == null) {
   202                 result.setTime(Double.NaN);
   203             } else {
   204                 final double time = timeClip(utc(makeDate(d), result.getTimeZone()));
   205                 result.setTime(time);
   206             }
   207             break;
   208          }
   210          return result;
   211     }
   213     @Override
   214     public String safeToString() {
   215         final String str = isValidDate() ? toISOStringImpl(this) : INVALID_DATE;
   216         return "[Date " + str + "]";
   217     }
   219     @Override
   220     public String toString() {
   221         return isValidDate() ? toString(this).toString() : INVALID_DATE;
   222     }
   224     /**
   225      * ECMA 15.9.4.2 Date.parse (string)
   226      *
   227      * @param self self reference
   228      * @param string string to parse as date
   229      * @return Date interpreted from the string, or NaN for illegal values
   230      */
   231     @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
   232     public static double parse(final Object self, final Object string) {
   233         return parseDateString(JSType.toString(string));
   234     }
   236     /**
   237      * ECMA 15.9.4.3 Date.UTC (year, month [, date [, hours [, minutes [, seconds [, ms ] ] ] ] ] )
   238      *
   239      * @param self self reference
   240      * @param args mandatory args are year, month. Optional are date, hours, minutes, seconds and milliseconds
   241      * @return a time clip according to the ECMA specification
   242      */
   243     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 7, where = Where.CONSTRUCTOR)
   244     public static double UTC(final Object self, final Object... args) {
   245         final NativeDate nd = new NativeDate(0);
   246         final double[] d = convertCtorArgs(args);
   247         final double time = d == null ? Double.NaN : timeClip(makeDate(d));
   248         nd.setTime(time);
   249         return time;
   250     }
   252     /**
   253      * ECMA 15.9.4.4 Date.now ( )
   254      *
   255      * @param self self reference
   256      * @return a Date that points to the current moment in time
   257      */
   258     @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
   259     public static double now(final Object self) {
   260         // convert to double as long does not represent the primitive JS number type
   261         return (double) System.currentTimeMillis();
   262     }
   264     /**
   265      * ECMA 15.9.5.2 Date.prototype.toString ( )
   266      *
   267      * @param self self reference
   268      * @return string value that represents the Date in the current time zone
   269      */
   270     @Function(attributes = Attribute.NOT_ENUMERABLE)
   271     public static String toString(final Object self) {
   272         return toStringImpl(self, FORMAT_DATE_TIME);
   273     }
   275     /**
   276      * ECMA 15.9.5.3 Date.prototype.toDateString ( )
   277      *
   278      * @param self self reference
   279      * @return string value with the "date" part of the Date in the current time zone
   280      */
   281     @Function(attributes = Attribute.NOT_ENUMERABLE)
   282     public static String toDateString(final Object self) {
   283         return toStringImpl(self, FORMAT_DATE);
   284     }
   286     /**
   287      * ECMA 15.9.5.4 Date.prototype.toTimeString ( )
   288      *
   289      * @param self self reference
   290      * @return string value with "time" part of Date in the current time zone
   291      */
   292     @Function(attributes = Attribute.NOT_ENUMERABLE)
   293     public static String toTimeString(final Object self) {
   294         return toStringImpl(self, FORMAT_TIME);
   295     }
   297     /**
   298      * ECMA 15.9.5.5 Date.prototype.toLocaleString ( )
   299      *
   300      * @param self self reference
   301      * @return string value that represents the Data in the current time zone and locale
   302      */
   303     @Function(attributes = Attribute.NOT_ENUMERABLE)
   304     public static String toLocaleString(final Object self) {
   305         return toStringImpl(self, FORMAT_LOCAL_DATE_TIME);
   306     }
   308     /**
   309      * ECMA 15.9.5.6 Date.prototype.toLocaleDateString ( )
   310      *
   311      * @param self self reference
   312      * @return string value with the "date" part of the Date in the current time zone and locale
   313      */
   314     @Function(attributes = Attribute.NOT_ENUMERABLE)
   315     public static String toLocaleDateString(final Object self) {
   316         return toStringImpl(self, FORMAT_LOCAL_DATE);
   317     }
   319     /**
   320      * ECMA 15.9.5.7 Date.prototype.toLocaleTimeString ( )
   321      *
   322      * @param self self reference
   323      * @return string value with the "time" part of Date in the current time zone and locale
   324      */
   325     @Function(attributes = Attribute.NOT_ENUMERABLE)
   326     public static String toLocaleTimeString(final Object self) {
   327         return toStringImpl(self, FORMAT_LOCAL_TIME);
   328     }
   330     /**
   331      * ECMA 15.9.5.8 Date.prototype.valueOf ( )
   332      *
   333      * @param self self reference
   334      * @return valueOf - a number which is this time value
   335      */
   336     @Function(attributes = Attribute.NOT_ENUMERABLE)
   337     public static double valueOf(final Object self) {
   338         final NativeDate nd = getNativeDate(self);
   339         return (nd != null) ? nd.getTime() : Double.NaN;
   340     }
   342     /**
   343      * ECMA 15.9.5.9 Date.prototype.getTime ( )
   344      *
   345      * @param self self reference
   346      * @return time
   347      */
   348     @Function(attributes = Attribute.NOT_ENUMERABLE)
   349     public static double getTime(final Object self) {
   350         final NativeDate nd = getNativeDate(self);
   351         return (nd != null) ? nd.getTime() : Double.NaN;
   352     }
   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     }
   365     /**
   366      * ECMA 15.9.5.11 Date.prototype.getUTCFullYear( )
   367      *
   368      * @param self self reference
   369      * @return UTC full year
   370      */
   371     @Function(attributes = Attribute.NOT_ENUMERABLE)
   372     public static double getUTCFullYear(final Object self) {
   373         return getUTCField(self, YEAR);
   374     }
   376     /**
   377      * B.2.4 Date.prototype.getYear ( )
   378      *
   379      * @param self self reference
   380      * @return year
   381      */
   382     @Function(attributes = Attribute.NOT_ENUMERABLE)
   383     public static double getYear(final Object self) {
   384         final NativeDate nd = getNativeDate(self);
   385         return (nd != null && nd.isValidDate()) ? (yearFromTime(nd.getLocalTime()) - 1900) : Double.NaN;
   386     }
   388     /**
   389      * ECMA 15.9.5.12 Date.prototype.getMonth ( )
   390      *
   391      * @param self self reference
   392      * @return month
   393      */
   394     @Function(attributes = Attribute.NOT_ENUMERABLE)
   395     public static double getMonth(final Object self) {
   396         return getField(self, MONTH);
   397     }
   399     /**
   400      * ECMA 15.9.5.13 Date.prototype.getUTCMonth ( )
   401      *
   402      * @param self self reference
   403      * @return UTC month
   404      */
   405     @Function(attributes = Attribute.NOT_ENUMERABLE)
   406     public static double getUTCMonth(final Object self) {
   407         return getUTCField(self, MONTH);
   408     }
   410     /**
   411      * ECMA 15.9.5.14 Date.prototype.getDate ( )
   412      *
   413      * @param self self reference
   414      * @return date
   415      */
   416     @Function(attributes = Attribute.NOT_ENUMERABLE)
   417     public static double getDate(final Object self) {
   418         return getField(self, DAY);
   419     }
   421     /**
   422      * ECMA 15.9.5.15 Date.prototype.getUTCDate ( )
   423      *
   424      * @param self self reference
   425      * @return UTC Date
   426      */
   427     @Function(attributes = Attribute.NOT_ENUMERABLE)
   428     public static double getUTCDate(final Object self) {
   429         return getUTCField(self, DAY);
   430     }
   432     /**
   433      * ECMA 15.9.5.16 Date.prototype.getDay ( )
   434      *
   435      * @param self self reference
   436      * @return day
   437      */
   438     @Function(attributes = Attribute.NOT_ENUMERABLE)
   439     public static double getDay(final Object self) {
   440         final NativeDate nd = getNativeDate(self);
   441         return (nd != null && nd.isValidDate()) ? weekDay(nd.getLocalTime()) : Double.NaN;
   442     }
   444     /**
   445      * ECMA 15.9.5.17 Date.prototype.getUTCDay ( )
   446      *
   447      * @param self self reference
   448      * @return UTC day
   449      */
   450     @Function(attributes = Attribute.NOT_ENUMERABLE)
   451     public static double getUTCDay(final Object self) {
   452         final NativeDate nd = getNativeDate(self);
   453         return (nd != null && nd.isValidDate()) ? weekDay(nd.getTime()) : Double.NaN;
   454     }
   456     /**
   457      * ECMA 15.9.5.18 Date.prototype.getHours ( )
   458      *
   459      * @param self self reference
   460      * @return hours
   461      */
   462     @Function(attributes = Attribute.NOT_ENUMERABLE)
   463     public static double getHours(final Object self) {
   464         return getField(self, HOUR);
   465     }
   467     /**
   468      * ECMA 15.9.5.19 Date.prototype.getUTCHours ( )
   469      *
   470      * @param self self reference
   471      * @return UTC hours
   472      */
   473     @Function(attributes = Attribute.NOT_ENUMERABLE)
   474     public static double getUTCHours(final Object self) {
   475         return getUTCField(self, HOUR);
   476     }
   478     /**
   479      * ECMA 15.9.5.20 Date.prototype.getMinutes ( )
   480      *
   481      * @param self self reference
   482      * @return minutes
   483      */
   484     @Function(attributes = Attribute.NOT_ENUMERABLE)
   485     public static double getMinutes(final Object self) {
   486         return getField(self, MINUTE);
   487     }
   489     /**
   490      * ECMA 15.9.5.21 Date.prototype.getUTCMinutes ( )
   491      *
   492      * @param self self reference
   493      * @return UTC minutes
   494      */
   495     @Function(attributes = Attribute.NOT_ENUMERABLE)
   496     public static double getUTCMinutes(final Object self) {
   497         return getUTCField(self, MINUTE);
   498     }
   500     /**
   501      * ECMA 15.9.5.22 Date.prototype.getSeconds ( )
   502      *
   503      * @param self self reference
   504      * @return seconds
   505      */
   506     @Function(attributes = Attribute.NOT_ENUMERABLE)
   507     public static double getSeconds(final Object self) {
   508         return getField(self, SECOND);
   509     }
   511     /**
   512      * ECMA 15.9.5.23 Date.prototype.getUTCSeconds ( )
   513      *
   514      * @param self self reference
   515      * @return UTC seconds
   516      */
   517     @Function(attributes = Attribute.NOT_ENUMERABLE)
   518     public static double getUTCSeconds(final Object self) {
   519         return getUTCField(self, SECOND);
   520     }
   522     /**
   523      * ECMA 15.9.5.24 Date.prototype.getMilliseconds ( )
   524      *
   525      * @param self self reference
   526      * @return milliseconds
   527      */
   528     @Function(attributes = Attribute.NOT_ENUMERABLE)
   529     public static double getMilliseconds(final Object self) {
   530         return getField(self, MILLISECOND);
   531     }
   533     /**
   534      * ECMA 15.9.5.25 Date.prototype.getUTCMilliseconds ( )
   535      *
   536      * @param self self reference
   537      * @return UTC milliseconds
   538      */
   539     @Function(attributes = Attribute.NOT_ENUMERABLE)
   540     public static double getUTCMilliseconds(final Object self) {
   541         return getUTCField(self, MILLISECOND);
   542     }
   544     /**
   545      * ECMA 15.9.5.26 Date.prototype.getTimezoneOffset ( )
   546      *
   547      * @param self self reference
   548      * @return time zone offset or NaN if N/A
   549      */
   550     @Function(attributes = Attribute.NOT_ENUMERABLE)
   551     public static double getTimezoneOffset(final Object self) {
   552         final NativeDate nd = getNativeDate(self);
   553         if (nd != null && nd.isValidDate()) {
   554             final long msec = (long) nd.getTime();
   555             return - nd.getTimeZone().getOffset(msec) / msPerMinute;
   556         }
   557         return Double.NaN;
   558     }
   560     /**
   561      * ECMA 15.9.5.27 Date.prototype.setTime (time)
   562      *
   563      * @param self self reference
   564      * @param time time
   565      * @return time
   566      */
   567     @Function(attributes = Attribute.NOT_ENUMERABLE)
   568     public static double setTime(final Object self, final Object time) {
   569         final NativeDate nd  = getNativeDate(self);
   570         final double     num = timeClip(JSType.toNumber(time));
   571         nd.setTime(num);
   572         return num;
   573     }
   575     /**
   576      * ECMA 15.9.5.28 Date.prototype.setMilliseconds (ms)
   577      *
   578      * @param self self reference
   579      * @param args milliseconds
   580      * @return time
   581      */
   582     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
   583     public static double setMilliseconds(final Object self, final Object... args) {
   584         final NativeDate nd = getNativeDate(self);
   585         setFields(nd, MILLISECOND, args, true);
   586         return nd.getTime();
   587     }
   589     /**
   590      * ECMA 15.9.5.29 Date.prototype.setUTCMilliseconds (ms)
   591      *
   592      * @param self self reference
   593      * @param args utc milliseconds
   594      * @return time
   595      */
   596     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
   597     public static double setUTCMilliseconds(final Object self, final Object... args) {
   598         final NativeDate nd = getNativeDate(self);
   599         setFields(nd, MILLISECOND, args, false);
   600         return nd.getTime();
   601     }
   603     /**
   604      * ECMA 15.9.5.30 Date.prototype.setSeconds (sec [, ms ] )
   605      *
   606      * @param self self reference
   607      * @param args seconds (milliseconds optional second argument)
   608      * @return time
   609      */
   610     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2)
   611     public static double setSeconds(final Object self, final Object... args) {
   612         final NativeDate nd = getNativeDate(self);
   613         setFields(nd, SECOND, args, true);
   614         return nd.getTime();
   615     }
   617     /**
   618      * ECMA 15.9.5.31 Date.prototype.setUTCSeconds (sec [, ms ] )
   619      *
   620      * @param self self reference
   621      * @param args UTC seconds (milliseconds optional second argument)
   622      * @return time
   623      */
   624     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2)
   625     public static double setUTCSeconds(final Object self, final Object... args) {
   626         final NativeDate nd = getNativeDate(self);
   627         setFields(nd, SECOND, args, false);
   628         return nd.getTime();
   629     }
   631     /**
   632      * ECMA 15.9.5.32 Date.prototype.setMinutes (min [, sec [, ms ] ] )
   633      *
   634      * @param self self reference
   635      * @param args minutes (seconds and milliseconds are optional second and third arguments)
   636      * @return time
   637      */
   638     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 3)
   639     public static double setMinutes(final Object self, final Object... args) {
   640         final NativeDate nd = getNativeDate(self);
   641         setFields(nd, MINUTE, args, true);
   642         return nd.getTime();
   643     }
   645     /**
   646      * ECMA 15.9.5.33 Date.prototype.setUTCMinutes (min [, sec [, ms ] ] )
   647      *
   648      * @param self self reference
   649      * @param args minutes (seconds and milliseconds are optional second and third arguments)
   650      * @return time
   651      */
   652     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 3)
   653     public static double setUTCMinutes(final Object self, final Object... args) {
   654         final NativeDate nd = getNativeDate(self);
   655         setFields(nd, MINUTE, args, false);
   656         return nd.getTime();
   657     }
   659     /**
   660      * ECMA 15.9.5.34 Date.prototype.setHours (hour [, min [, sec [, ms ] ] ] )
   661      *
   662      * @param self self reference
   663      * @param args hour (optional arguments after are minutes, seconds, milliseconds)
   664      * @return time
   665      */
   666     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 4)
   667     public static double setHours(final Object self, final Object... args) {
   668         final NativeDate nd = getNativeDate(self);
   669         setFields(nd, HOUR, args, true);
   670         return nd.getTime();
   671     }
   673     /**
   674      * ECMA 15.9.5.35 Date.prototype.setUTCHours (hour [, min [, sec [, ms ] ] ] )
   675      *
   676      * @param self self reference
   677      * @param args hour (optional arguments after are minutes, seconds, milliseconds)
   678      * @return time
   679      */
   680     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 4)
   681     public static double setUTCHours(final Object self, final Object... args) {
   682         final NativeDate nd = getNativeDate(self);
   683         setFields(nd, HOUR, args, false);
   684         return nd.getTime();
   685     }
   687     /**
   688      * ECMA 15.9.5.36 Date.prototype.setDate (date)
   689      *
   690      * @param self self reference
   691      * @param args date
   692      * @return time
   693      */
   694     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
   695     public static double setDate(final Object self, final Object... args) {
   696         final NativeDate nd = getNativeDate(self);
   697         setFields(nd, DAY, args, true);
   698         return nd.getTime();
   699     }
   701     /**
   702      * ECMA 15.9.5.37 Date.prototype.setUTCDate (date)
   703      *
   704      * @param self self reference
   705      * @param args UTC date
   706      * @return time
   707      */
   708     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
   709     public static double setUTCDate(final Object self, final Object... args) {
   710         final NativeDate nd = getNativeDate(self);
   711         setFields(nd, DAY, args, false);
   712         return nd.getTime();
   713     }
   715     /**
   716      * ECMA 15.9.5.38 Date.prototype.setMonth (month [, date ] )
   717      *
   718      * @param self self reference
   719      * @param args month (optional second argument is date)
   720      * @return time
   721      */
   722     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2)
   723     public static double setMonth(final Object self, final Object... args) {
   724         final NativeDate nd = getNativeDate(self);
   725         setFields(nd, MONTH, args, true);
   726         return nd.getTime();
   727     }
   729     /**
   730      * ECMA 15.9.5.39 Date.prototype.setUTCMonth (month [, date ] )
   731      *
   732      * @param self self reference
   733      * @param args UTC month (optional second argument is date)
   734      * @return time
   735      */
   736     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2)
   737     public static double setUTCMonth(final Object self, final Object... args) {
   738         final NativeDate nd = ensureNativeDate(self);
   739         setFields(nd, MONTH, args, false);
   740         return nd.getTime();
   741     }
   743     /**
   744      * ECMA 15.9.5.40 Date.prototype.setFullYear (year [, month [, date ] ] )
   745      *
   746      * @param self self reference
   747      * @param args year (optional second and third arguments are month and date)
   748      * @return time
   749      */
   750     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 3)
   751     public static double setFullYear(final Object self, final Object... args) {
   752         final NativeDate nd   = ensureNativeDate(self);
   753         if (nd.isValidDate()) {
   754             setFields(nd, YEAR, args, true);
   755         } else {
   756             final double[] d = convertArgs(args, 0, YEAR, YEAR, 3);
   757             if (d != null) {
   758                 nd.setTime(timeClip(utc(makeDate(makeDay(d[0], d[1], d[2]), 0), nd.getTimeZone())));
   759             } else {
   760                 nd.setTime(NaN);
   761             }
   762         }
   763         return nd.getTime();
   764     }
   766     /**
   767      * ECMA 15.9.5.41 Date.prototype.setUTCFullYear (year [, month [, date ] ] )
   768      *
   769      * @param self self reference
   770      * @param args UTC full year (optional second and third arguments are month and date)
   771      * @return time
   772      */
   773     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 3)
   774     public static double setUTCFullYear(final Object self, final Object... args) {
   775         final NativeDate nd   = ensureNativeDate(self);
   776         if (nd.isValidDate()) {
   777             setFields(nd, YEAR, args, false);
   778         } else {
   779             final double[] d = convertArgs(args, 0, YEAR, YEAR, 3);
   780             nd.setTime(timeClip(makeDate(makeDay(d[0], d[1], d[2]), 0)));
   781         }
   782         return nd.getTime();
   783     }
   785     /**
   786      * ECMA B.2.5 Date.prototype.setYear (year)
   787      *
   788      * @param self self reference
   789      * @param year year
   790      * @return NativeDate
   791      */
   792     @Function(attributes = Attribute.NOT_ENUMERABLE)
   793     public static double setYear(final Object self, final Object year) {
   794         final NativeDate nd = getNativeDate(self);
   795         if (isNaN(nd.getTime())) {
   796             nd.setTime(utc(0, nd.getTimeZone()));
   797         }
   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);
   810         return nd.getTime();
   811     }
   813     /**
   814      * ECMA 15.9.5.42 Date.prototype.toUTCString ( )
   815      *
   816      * @param self self reference
   817      * @return string representation of date
   818      */
   819     @Function(attributes = Attribute.NOT_ENUMERABLE)
   820     public static String toUTCString(final Object self) {
   821         return toGMTStringImpl(self);
   822     }
   824     /**
   825      * ECMA B.2.6 Date.prototype.toGMTString ( )
   826      *
   827      * See {@link NativeDate#toUTCString(Object)}
   828      *
   829      * @param self self reference
   830      * @return string representation of date
   831      */
   832     @Function(attributes = Attribute.NOT_ENUMERABLE)
   833     public static String toGMTString(final Object self) {
   834         return toGMTStringImpl(self);
   835     }
   837     /**
   838      * ECMA 15.9.5.43 Date.prototype.toISOString ( )
   839      *
   840      * @param self self reference
   841      * @return string representation of date
   842      */
   843     @Function(attributes = Attribute.NOT_ENUMERABLE)
   844     public static String toISOString(final Object self) {
   845         return toISOStringImpl(self);
   846     }
   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         }
   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     }
   887     // -- Internals below this point
   889     private static double parseDateString(final String str) {
   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         }
   904         return Double.NaN;
   905     }
   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     }
   916     @SuppressWarnings("fallthrough")
   917     private static String toStringImpl(final Object self, final int format) {
   918         final NativeDate nd = getNativeDate(self);
   920         if (nd != null && nd.isValidDate()) {
   921             final StringBuilder sb = new StringBuilder(40);
   922             final double t = nd.getLocalTime();
   924             switch (format) {
   926                 case FORMAT_DATE_TIME:
   927                 case FORMAT_DATE :
   928                 case FORMAT_LOCAL_DATE_TIME:
   929                     // EEE MMM dd yyyy
   930                     sb.append(weekDays[weekDay(t)])
   931                             .append(' ')
   932                             .append(months[monthFromTime(t)])
   933                             .append(' ');
   934                     zeroPad(sb, dayFromTime(t), 2);
   935                     sb.append(' ');
   936                     zeroPad(sb, yearFromTime(t), 4);
   937                     if (format == FORMAT_DATE) {
   938                         break;
   939                     }
   940                     sb.append(' ');
   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;
   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;
   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;
   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;
   982                 default:
   983                     throw new IllegalArgumentException("format: " + format);
   984             }
   986             return sb.toString();
   987         }
   989         return INVALID_DATE;
   990     }
   992     private static String toGMTStringImpl(final Object self) {
   993         final NativeDate nd = getNativeDate(self);
   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();
  1016         throw rangeError("invalid.date");
  1019     private static String toISOStringImpl(final Object self) {
  1020         final NativeDate nd = getNativeDate(self);
  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();
  1043         throw rangeError("invalid.date");
  1046     // ECMA 15.9.1.2 Day (t)
  1047     private static double day(final double t) {
  1048         return Math.floor(t / msPerDay);
  1051     // ECMA 15.9.1.2 TimeWithinDay (t)
  1052     private static double timeWithinDay(final double t) {
  1053         final double val = t % msPerDay;
  1054         return val < 0? val + msPerDay : val;
  1057     // ECMA 15.9.1.3 InLeapYear (t)
  1058     private static boolean isLeapYear(final int y) {
  1059         return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0);
  1062     // ECMA 15.9.1.3 DaysInYear (y)
  1063     private static int daysInYear(final int y) {
  1064         return isLeapYear(y) ? 366 : 365;
  1067     // ECMA 15.9.1.3 DayFromYear (y)
  1068     private static double dayFromYear(final double y) {
  1069         return 365 * (y - 1970)
  1070                 + Math.floor((y -1969) / 4.0)
  1071                 - Math.floor((y - 1901) / 100.0)
  1072                 + Math.floor((y - 1601) / 400.0);
  1075     // ECMA 15.9.1.3 Year Number
  1076     private static double timeFromYear(final int y) {
  1077         return dayFromYear(y) * msPerDay;
  1080     // ECMA 15.9.1.3 Year Number
  1081     private static int yearFromTime(final double t) {
  1082         int y = (int) Math.floor(t / (msPerDay * 365.2425)) + 1970;
  1083         final double t2 = timeFromYear(y);
  1084         if (t2 > t) {
  1085             y--;
  1086         } else if (t2 + msPerDay * daysInYear(y) <= t) {
  1087             y++;
  1089         return y;
  1092     private static int dayWithinYear(final double t, final int year) {
  1093         return (int) (day(t) - dayFromYear(year));
  1096     private static int monthFromTime(final double t) {
  1097         final int year = yearFromTime(t);
  1098         final int day = dayWithinYear(t, year);
  1099         final int[] firstDay = firstDayInMonth[isLeapYear(year) ? 1 : 0];
  1100         int month = 0;
  1102         while (month < 11 && firstDay[month + 1] <= day) {
  1103             month++;
  1105         return month;
  1108     private static int dayFromTime(final double t)  {
  1109         final int year = yearFromTime(t);
  1110         final int day = dayWithinYear(t, year);
  1111         final int[] firstDay = firstDayInMonth[isLeapYear(year) ? 1 : 0];
  1112         int month = 0;
  1114         while (month < 11 && firstDay[month + 1] <= day) {
  1115             month++;
  1117         return 1 + day - firstDay[month];
  1120     private static int dayFromMonth(final int month, final int year) {
  1121         assert(month >= 0 && month <= 11);
  1122         final int[] firstDay = firstDayInMonth[isLeapYear(year) ? 1 : 0];
  1123         return firstDay[month];
  1126     private static int weekDay(final double time) {
  1127         final int day = (int) (day(time) + 4) % 7;
  1128         return day < 0 ? day + 7 : day;
  1131     // ECMA 15.9.1.9 LocalTime
  1132     private static double localTime(final double time, final TimeZone tz) {
  1133         return time + tz.getOffset((long) time);
  1136     // ECMA 15.9.1.9 UTC
  1137     private static double utc(final double time, final TimeZone tz) {
  1138         return time - tz.getOffset((long) (time - tz.getRawOffset()));
  1141     // ECMA 15.9.1.10 Hours, Minutes, Second, and Milliseconds
  1142     private static int hourFromTime(final double t) {
  1143         final int h = (int) (Math.floor(t / msPerHour) % hoursPerDay);
  1144         return h < 0 ? h + hoursPerDay: h;
  1146     private static int minFromTime(final double t) {
  1147         final int m = (int) (Math.floor(t / msPerMinute) % minutesPerHour);
  1148         return m < 0 ? m + minutesPerHour : m;
  1151     private static int secFromTime(final double t) {
  1152         final int s = (int) (Math.floor(t / msPerSecond) % secondsPerMinute);
  1153         return s < 0 ? s + secondsPerMinute : s;
  1156     private static int msFromTime(final double t) {
  1157         final int m = (int) (t % msPerSecond);
  1158         return m < 0 ? m + msPerSecond : m;
  1161     private static int valueFromTime(final int unit, final double t) {
  1162         switch (unit) {
  1163             case YEAR: return yearFromTime(t);
  1164             case MONTH: return monthFromTime(t);
  1165             case DAY: return dayFromTime(t);
  1166             case HOUR: return hourFromTime(t);
  1167             case MINUTE: return minFromTime(t);
  1168             case SECOND: return secFromTime(t);
  1169             case MILLISECOND: return msFromTime(t);
  1170             default: throw new IllegalArgumentException(Integer.toString(unit));
  1174     // ECMA 15.9.1.11 MakeTime (hour, min, sec, ms)
  1175     private static double makeTime(final double hour, final double min, final double sec, final double ms) {
  1176         return hour * 3600000 + min * 60000 + sec * 1000 + ms;
  1179     // ECMA 15.9.1.12 MakeDay (year, month, date)
  1180     private static double makeDay(final double year, final double month, final double date) {
  1181         final double y = year + Math.floor(month / 12);
  1182         int m = (int) (month % 12);
  1183         if (m < 0) {
  1184             m += 12;
  1186         double d = dayFromYear(y);
  1187         d += dayFromMonth(m, (int) y);
  1189         return d + date - 1;
  1192     // ECMA 15.9.1.13 MakeDate (day, time)
  1193     private static double makeDate(final double day, final double time) {
  1194         return day * msPerDay + time;
  1198     private static double makeDate(final Integer[] d) {
  1199         final double time = makeDay(d[0], d[1], d[2]) * msPerDay;
  1200         return time + makeTime(d[3], d[4], d[5], d[6]);
  1203     private static double makeDate(final double[] d) {
  1204         final double time = makeDay(d[0], d[1], d[2]) * msPerDay;
  1205         return time + makeTime(d[3], d[4], d[5], d[6]);
  1208     // Convert Date constructor args, checking for NaN, filling in defaults etc.
  1209     private static double[] convertCtorArgs(final Object[] args) {
  1210         final double[] d = new double[7];
  1211         boolean nullReturn = false;
  1213         // should not bailout on first NaN or infinite. Need to convert all
  1214         // subsequent args for possible side-effects via valueOf/toString overrides
  1215         // on argument objects.
  1216         for (int i = 0; i < d.length; i++) {
  1217             if (i < args.length) {
  1218                 final double darg = JSType.toNumber(args[i]);
  1219                 if (isNaN(darg) || isInfinite(darg)) {
  1220                     nullReturn = true;
  1223                 d[i] = (long)darg;
  1224             } else {
  1225                 d[i] = i == 2 ? 1 : 0; // day in month defaults to 1
  1229         if (0 <= d[0] && d[0] <= 99) {
  1230             d[0] += 1900;
  1233         return nullReturn? null : d;
  1236     // This method does the hard work for all setter methods: If a value is provided
  1237     // as argument it is used, otherwise the value is calculated from the existing time value.
  1238     private static double[] convertArgs(final Object[] args, final double time, final int fieldId, final int start, final int length) {
  1239         final double[] d = new double[length];
  1240         boolean nullReturn = false;
  1242         // Need to call toNumber on all args for side-effects - even if an argument
  1243         // fails to convert to number, subsequent toNumber calls needed for possible
  1244         // side-effects via valueOf/toString overrides.
  1245         for (int i = start; i < start + length; i++) {
  1246             if (fieldId <= i && i < fieldId + args.length) {
  1247                 final double darg = JSType.toNumber(args[i - fieldId]);
  1248                 if (isNaN(darg) || isInfinite(darg)) {
  1249                     nullReturn = true;
  1252                 d[i - start] = (long) darg;
  1253             } else {
  1254                 // Date.prototype.set* methods require first argument to be defined
  1255                 if (i == fieldId) {
  1256                     nullReturn = true;
  1259                 if (!nullReturn && !isNaN(time)) {
  1260                     d[i - start] = valueFromTime(i, time);
  1265         return nullReturn ? null : d;
  1268     // ECMA 15.9.1.14 TimeClip (time)
  1269     private static double timeClip(final double time) {
  1270         if (isInfinite(time) || isNaN(time) || Math.abs(time) > 8.64e15) {
  1271             return Double.NaN;
  1273         return (long)time;
  1276     private static NativeDate ensureNativeDate(final Object self) {
  1277         return getNativeDate(self);
  1280     private static NativeDate getNativeDate(final Object self) {
  1281         if (self instanceof NativeDate) {
  1282             return (NativeDate)self;
  1283         } else if (self != null && self == Global.instance().getDatePrototype()) {
  1284             return Global.instance().getDefaultDate();
  1285         } else {
  1286             throw typeError("not.a.date", ScriptRuntime.safeToString(self));
  1290     private static double getField(final Object self, final int field) {
  1291         final NativeDate nd = getNativeDate(self);
  1292         return (nd != null && nd.isValidDate()) ? (double)valueFromTime(field, nd.getLocalTime()) : Double.NaN;
  1295     private static double getUTCField(final Object self, final int field) {
  1296         final NativeDate nd = getNativeDate(self);
  1297         return (nd != null && nd.isValidDate()) ? (double)valueFromTime(field, nd.getTime()) : Double.NaN;
  1300     private static void setFields(final NativeDate nd, final int fieldId, final Object[] args, final boolean local) {
  1301         int start, length;
  1302         if (fieldId < HOUR) {
  1303             start = YEAR;
  1304             length = 3;
  1305         } else {
  1306             start = HOUR;
  1307             length = 4;
  1309         final double time = local ? nd.getLocalTime() : nd.getTime();
  1310         final double d[] = convertArgs(args, time, fieldId, start, length);
  1312         if (! nd.isValidDate()) {
  1313             return;
  1316         double newTime;
  1317         if (d == null) {
  1318             newTime = NaN;
  1319         } else {
  1320             if (start == YEAR) {
  1321                 newTime = makeDate(makeDay(d[0], d[1], d[2]), timeWithinDay(time));
  1322             } else {
  1323                 newTime = makeDate(day(time), makeTime(d[0], d[1], d[2], d[3]));
  1325             if (local) {
  1326                 newTime = utc(newTime, nd.getTimeZone());
  1328             newTime = timeClip(newTime);
  1330         nd.setTime(newTime);
  1333     private boolean isValidDate() {
  1334         return !isNaN(time);
  1337     private double getLocalTime() {
  1338         return localTime(time, timezone);
  1341     private double getTime() {
  1342         return time;
  1345     private void setTime(final double time) {
  1346         this.time = time;
  1349     private TimeZone getTimeZone() {
  1350         return timezone;

mercurial