NativeDate.java revision 1077:dd7bbdf81a53
1215911Sjfv/*
2215911Sjfv * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3235528Sjfv * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4215911Sjfv *
5215911Sjfv * This code is free software; you can redistribute it and/or modify it
6215911Sjfv * under the terms of the GNU General Public License version 2 only, as
7215911Sjfv * published by the Free Software Foundation.  Oracle designates this
8215911Sjfv * particular file as subject to the "Classpath" exception as provided
9215911Sjfv * by Oracle in the LICENSE file that accompanied this code.
10215911Sjfv *
11215911Sjfv * This code is distributed in the hope that it will be useful, but WITHOUT
12215911Sjfv * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13215911Sjfv * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14215911Sjfv * version 2 for more details (a copy is included in the LICENSE file that
15215911Sjfv * accompanied this code).
16215911Sjfv *
17215911Sjfv * You should have received a copy of the GNU General Public License version
18215911Sjfv * 2 along with this work; if not, write to the Free Software Foundation,
19215911Sjfv * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20215911Sjfv *
21215911Sjfv * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22215911Sjfv * or visit www.oracle.com if you need additional information or have any
23215911Sjfv * questions.
24215911Sjfv */
25215911Sjfv
26215911Sjfvpackage jdk.nashorn.internal.objects;
27215911Sjfv
28215911Sjfvimport static java.lang.Double.NaN;
29215911Sjfvimport static java.lang.Double.isInfinite;
30215911Sjfvimport static java.lang.Double.isNaN;
31215911Sjfvimport static jdk.nashorn.internal.runtime.ECMAErrors.rangeError;
32215911Sjfvimport static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
33215911Sjfvimport java.util.Locale;
34215911Sjfvimport java.util.TimeZone;
35215911Sjfvimport java.util.concurrent.Callable;
36222588Sjfvimport jdk.nashorn.internal.objects.annotations.Attribute;
37222588Sjfvimport jdk.nashorn.internal.objects.annotations.Constructor;
38215911Sjfvimport jdk.nashorn.internal.objects.annotations.Function;
39215911Sjfvimport jdk.nashorn.internal.objects.annotations.ScriptClass;
40215911Sjfvimport jdk.nashorn.internal.objects.annotations.SpecializedFunction;
41215911Sjfvimport jdk.nashorn.internal.objects.annotations.Where;
42215911Sjfvimport jdk.nashorn.internal.parser.DateParser;
43215911Sjfvimport jdk.nashorn.internal.runtime.ConsString;
44215911Sjfvimport jdk.nashorn.internal.runtime.JSType;
45238263Sjfvimport jdk.nashorn.internal.runtime.PropertyMap;
46215911Sjfvimport jdk.nashorn.internal.runtime.ScriptEnvironment;
47215911Sjfvimport jdk.nashorn.internal.runtime.ScriptObject;
48215911Sjfvimport jdk.nashorn.internal.runtime.ScriptRuntime;
49215911Sjfvimport jdk.nashorn.internal.runtime.linker.Bootstrap;
50215911Sjfvimport jdk.nashorn.internal.runtime.linker.InvokeByName;
51215911Sjfv
52215911Sjfv/**
53215911Sjfv * ECMA 15.9 Date Objects
54215911Sjfv *
55215911Sjfv */
56215911Sjfv@ScriptClass("Date")
57215911Sjfvpublic final class NativeDate extends ScriptObject {
58215911Sjfv
59215911Sjfv    private static final String INVALID_DATE = "Invalid Date";
60235528Sjfv
61215911Sjfv    private static final int YEAR        = 0;
62215911Sjfv    private static final int MONTH       = 1;
63215911Sjfv    private static final int DAY         = 2;
64215911Sjfv    private static final int HOUR        = 3;
65215911Sjfv    private static final int MINUTE      = 4;
66215911Sjfv    private static final int SECOND      = 5;
67215911Sjfv    private static final int MILLISECOND = 6;
68215911Sjfv
69215911Sjfv    private static final int FORMAT_DATE_TIME       = 0;
70215911Sjfv    private static final int FORMAT_DATE            = 1;
71215911Sjfv    private static final int FORMAT_TIME            = 2;
72215911Sjfv    private static final int FORMAT_LOCAL_DATE_TIME = 3;
73215911Sjfv    private static final int FORMAT_LOCAL_DATE      = 4;
74215911Sjfv    private static final int FORMAT_LOCAL_TIME      = 5;
75215911Sjfv
76215911Sjfv    // Constants defined in ECMA 15.9.1.10
77215911Sjfv    private static final int    hoursPerDay      = 24;
78215911Sjfv    private static final int    minutesPerHour   = 60;
79215911Sjfv    private static final int    secondsPerMinute = 60;
80215911Sjfv    private static final int    msPerSecond   = 1_000;
81215911Sjfv    private static final int    msPerMinute  = 60_000;
82215911Sjfv    private static final double msPerHour = 3_600_000;
83215911Sjfv    private static final double msPerDay = 86_400_000;
84215911Sjfv
85215911Sjfv    private static int[][] firstDayInMonth = {
86215911Sjfv            {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}, // normal year
87215911Sjfv            {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}  // leap year
88215911Sjfv    };
89215911Sjfv
90215911Sjfv    private static String[] weekDays = {
91215911Sjfv            "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
92215911Sjfv    };
93215911Sjfv
94215911Sjfv    private static String[] months = {
95215911Sjfv            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
96215911Sjfv    };
97215911Sjfv
98215911Sjfv    private static final Object TO_ISO_STRING = new Object();
99215911Sjfv
100215911Sjfv    private static InvokeByName getTO_ISO_STRING() {
101215911Sjfv        return Global.instance().getInvokeByName(TO_ISO_STRING,
102215911Sjfv                new Callable<InvokeByName>() {
103215911Sjfv                    @Override
104215911Sjfv                    public InvokeByName call() {
105215911Sjfv                        return new InvokeByName("toISOString", ScriptObject.class, Object.class, Object.class);
106215911Sjfv                    }
107215911Sjfv                });
108215911Sjfv    }
109215911Sjfv
110215911Sjfv    private double time;
111215911Sjfv    private final TimeZone timezone;
112215911Sjfv
113215911Sjfv    // initialized by nasgen
114215911Sjfv    private static PropertyMap $nasgenmap$;
115215911Sjfv
116215911Sjfv    private NativeDate(final double time, final ScriptObject proto, final PropertyMap map) {
117215911Sjfv        super(proto, map);
118215911Sjfv        final ScriptEnvironment env = Global.getEnv();
119215911Sjfv
120215911Sjfv        this.time = time;
121215911Sjfv        this.timezone = env._timezone;
122215911Sjfv    }
123215911Sjfv
124215911Sjfv    NativeDate(final double time, final Global global) {
125215911Sjfv        this(time, global.getDatePrototype(), $nasgenmap$);
126215911Sjfv    }
127215911Sjfv
128215911Sjfv    private NativeDate (final double time) {
129215911Sjfv        this(time, Global.instance());
130215911Sjfv    }
131215911Sjfv
132215911Sjfv    private NativeDate() {
133215911Sjfv        this(System.currentTimeMillis());
134215911Sjfv    }
135215911Sjfv
136215911Sjfv    @Override
137215911Sjfv    public String getClassName() {
138215911Sjfv        return "Date";
139215911Sjfv    }
140215911Sjfv
141215911Sjfv    // ECMA 8.12.8 [[DefaultValue]] (hint)
142215911Sjfv    @Override
143215911Sjfv    public Object getDefaultValue(final Class<?> hint) {
144215911Sjfv        // When the [[DefaultValue]] internal method of O is called with no hint,
145215911Sjfv        // then it behaves as if the hint were Number, unless O is a Date object
146215911Sjfv        // in which case it behaves as if the hint were String.
147215911Sjfv        return super.getDefaultValue(hint == null ? String.class : hint);
148215911Sjfv    }
149215911Sjfv
150215911Sjfv    /**
151215911Sjfv     * Constructor - ECMA 15.9.3.1 new Date
152215911Sjfv     *
153215911Sjfv     * @param isNew is this Date constructed with the new operator
154215911Sjfv     * @param self  self references
155215911Sjfv     * @return Date representing now
156215911Sjfv     */
157215911Sjfv    @SpecializedFunction(isConstructor=true)
158215911Sjfv    public static Object construct(final boolean isNew, final Object self) {
159215911Sjfv        final NativeDate result = new NativeDate();
160215911Sjfv        return isNew ? result : toStringImpl(result, FORMAT_DATE_TIME);
161215911Sjfv    }
162215911Sjfv
163215911Sjfv    /**
164215911Sjfv     * Constructor - ECMA 15.9.3.1 new Date (year, month [, date [, hours [, minutes [, seconds [, ms ] ] ] ] ] )
165215911Sjfv     *
166215911Sjfv     * @param isNew is this Date constructed with the new operator
167215911Sjfv     * @param self  self reference
168215911Sjfv     * @param args  arguments
169215911Sjfv     * @return new Date
170215911Sjfv     */
171215911Sjfv    @Constructor(arity = 7)
172215911Sjfv    public static Object construct(final boolean isNew, final Object self, final Object... args) {
173215911Sjfv        if (! isNew) {
174215911Sjfv            return toStringImpl(new NativeDate(), FORMAT_DATE_TIME);
175215911Sjfv        }
176215911Sjfv
177215911Sjfv        NativeDate result;
178215911Sjfv        switch (args.length) {
179215911Sjfv        case 0:
180215911Sjfv            result = new NativeDate();
181215911Sjfv            break;
182215911Sjfv
183215911Sjfv        case 1:
184215911Sjfv            double num;
185215911Sjfv            final Object arg = JSType.toPrimitive(args[0]);
186215911Sjfv            if (arg instanceof String || arg instanceof ConsString) {
187215911Sjfv                num = parseDateString(arg.toString());
188215911Sjfv            } else {
189215911Sjfv                num = timeClip(JSType.toNumber(args[0]));
190215911Sjfv            }
191215911Sjfv            result = new NativeDate(num);
192215911Sjfv            break;
193215911Sjfv
194215911Sjfv        default:
195215911Sjfv            result = new NativeDate(0);
196215911Sjfv            final double[] d = convertCtorArgs(args);
197215911Sjfv            if (d == null) {
198215911Sjfv                result.setTime(Double.NaN);
199215911Sjfv            } else {
200215911Sjfv                final double time = timeClip(utc(makeDate(d), result.getTimeZone()));
201215911Sjfv                result.setTime(time);
202215911Sjfv            }
203215911Sjfv            break;
204215911Sjfv         }
205215911Sjfv
206215911Sjfv         return result;
207215911Sjfv    }
208215911Sjfv
209215911Sjfv    @Override
210215911Sjfv    public String safeToString() {
211229153Smdf        final String str = isValidDate() ? toISOStringImpl(this) : INVALID_DATE;
212215911Sjfv        return "[Date " + str + "]";
213215911Sjfv    }
214215911Sjfv
215215911Sjfv    @Override
216215911Sjfv    public String toString() {
217215911Sjfv        return isValidDate() ? toString(this).toString() : INVALID_DATE;
218215911Sjfv    }
219215911Sjfv
220215911Sjfv    /**
221215911Sjfv     * ECMA 15.9.4.2 Date.parse (string)
222215911Sjfv     *
223215911Sjfv     * @param self self reference
224215911Sjfv     * @param string string to parse as date
225215911Sjfv     * @return Date interpreted from the string, or NaN for illegal values
226215911Sjfv     */
227215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
228215911Sjfv    public static double parse(final Object self, final Object string) {
229215911Sjfv        return parseDateString(JSType.toString(string));
230215911Sjfv    }
231215911Sjfv
232215911Sjfv    /**
233215911Sjfv     * ECMA 15.9.4.3 Date.UTC (year, month [, date [, hours [, minutes [, seconds [, ms ] ] ] ] ] )
234215911Sjfv     *
235215911Sjfv     * @param self self reference
236215911Sjfv     * @param args mandatory args are year, month. Optional are date, hours, minutes, seconds and milliseconds
237215911Sjfv     * @return a time clip according to the ECMA specification
238215911Sjfv     */
239222588Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 7, where = Where.CONSTRUCTOR)
240215911Sjfv    public static double UTC(final Object self, final Object... args) {
241215911Sjfv        final NativeDate nd = new NativeDate(0);
242215911Sjfv        final double[] d = convertCtorArgs(args);
243215911Sjfv        final double time = d == null ? Double.NaN : timeClip(makeDate(d));
244215911Sjfv        nd.setTime(time);
245215911Sjfv        return time;
246215911Sjfv    }
247215911Sjfv
248215911Sjfv    /**
249215911Sjfv     * ECMA 15.9.4.4 Date.now ( )
250215911Sjfv     *
251215911Sjfv     * @param self self reference
252215911Sjfv     * @return a Date that points to the current moment in time
253215911Sjfv     */
254215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
255215911Sjfv    public static long now(final Object self) {
256215911Sjfv        return System.currentTimeMillis();
257215911Sjfv    }
258215911Sjfv
259215911Sjfv    /**
260215911Sjfv     * ECMA 15.9.5.2 Date.prototype.toString ( )
261215911Sjfv     *
262215911Sjfv     * @param self self reference
263215911Sjfv     * @return string value that represents the Date in the current time zone
264215911Sjfv     */
265215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE)
266215911Sjfv    public static String toString(final Object self) {
267215911Sjfv        return toStringImpl(self, FORMAT_DATE_TIME);
268215911Sjfv    }
269215911Sjfv
270215911Sjfv    /**
271215911Sjfv     * ECMA 15.9.5.3 Date.prototype.toDateString ( )
272215911Sjfv     *
273215911Sjfv     * @param self self reference
274215911Sjfv     * @return string value with the "date" part of the Date in the current time zone
275215911Sjfv     */
276222588Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE)
277215911Sjfv    public static String toDateString(final Object self) {
278215911Sjfv        return toStringImpl(self, FORMAT_DATE);
279215911Sjfv    }
280215911Sjfv
281215911Sjfv    /**
282215911Sjfv     * ECMA 15.9.5.4 Date.prototype.toTimeString ( )
283215911Sjfv     *
284215911Sjfv     * @param self self reference
285215911Sjfv     * @return string value with "time" part of Date in the current time zone
286215911Sjfv     */
287215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE)
288215911Sjfv    public static String toTimeString(final Object self) {
289215911Sjfv        return toStringImpl(self, FORMAT_TIME);
290215911Sjfv    }
291215911Sjfv
292215911Sjfv    /**
293215911Sjfv     * ECMA 15.9.5.5 Date.prototype.toLocaleString ( )
294215911Sjfv     *
295215911Sjfv     * @param self self reference
296215911Sjfv     * @return string value that represents the Data in the current time zone and locale
297215911Sjfv     */
298215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE)
299215911Sjfv    public static String toLocaleString(final Object self) {
300215911Sjfv        return toStringImpl(self, FORMAT_LOCAL_DATE_TIME);
301215911Sjfv    }
302215911Sjfv
303215911Sjfv    /**
304215911Sjfv     * ECMA 15.9.5.6 Date.prototype.toLocaleDateString ( )
305215911Sjfv     *
306215911Sjfv     * @param self self reference
307215911Sjfv     * @return string value with the "date" part of the Date in the current time zone and locale
308215911Sjfv     */
309215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE)
310215911Sjfv    public static String toLocaleDateString(final Object self) {
311215911Sjfv        return toStringImpl(self, FORMAT_LOCAL_DATE);
312215911Sjfv    }
313215911Sjfv
314215911Sjfv    /**
315215911Sjfv     * ECMA 15.9.5.7 Date.prototype.toLocaleTimeString ( )
316215911Sjfv     *
317215911Sjfv     * @param self self reference
318215911Sjfv     * @return string value with the "time" part of Date in the current time zone and locale
319215911Sjfv     */
320215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE)
321215911Sjfv    public static String toLocaleTimeString(final Object self) {
322215911Sjfv        return toStringImpl(self, FORMAT_LOCAL_TIME);
323215911Sjfv    }
324215911Sjfv
325215911Sjfv    /**
326215911Sjfv     * ECMA 15.9.5.8 Date.prototype.valueOf ( )
327215911Sjfv     *
328215911Sjfv     * @param self self reference
329215911Sjfv     * @return valueOf - a number which is this time value
330215911Sjfv     */
331215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE)
332215911Sjfv    public static double valueOf(final Object self) {
333215911Sjfv        final NativeDate nd = getNativeDate(self);
334215911Sjfv        return (nd != null) ? nd.getTime() : Double.NaN;
335215911Sjfv    }
336215911Sjfv
337215911Sjfv    /**
338215911Sjfv     * ECMA 15.9.5.9 Date.prototype.getTime ( )
339215911Sjfv     *
340215911Sjfv     * @param self self reference
341215911Sjfv     * @return time
342215911Sjfv     */
343215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE)
344215911Sjfv    public static double getTime(final Object self) {
345215911Sjfv        final NativeDate nd = getNativeDate(self);
346215911Sjfv        return (nd != null) ? nd.getTime() : Double.NaN;
347215911Sjfv    }
348215911Sjfv
349215911Sjfv    /**
350215911Sjfv     * ECMA 15.9.5.10 Date.prototype.getFullYear ( )
351215911Sjfv     *
352215911Sjfv     * @param self self reference
353215911Sjfv     * @return full year
354215911Sjfv     */
355215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE)
356215911Sjfv    public static Object getFullYear(final Object self) {
357215911Sjfv        return getField(self, YEAR);
358215911Sjfv    }
359215911Sjfv
360215911Sjfv    /**
361215911Sjfv     * ECMA 15.9.5.11 Date.prototype.getUTCFullYear( )
362215911Sjfv     *
363215911Sjfv     * @param self self reference
364215911Sjfv     * @return UTC full year
365215911Sjfv     */
366215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE)
367215911Sjfv    public static double getUTCFullYear(final Object self) {
368215911Sjfv        return getUTCField(self, YEAR);
369215911Sjfv    }
370215911Sjfv
371215911Sjfv    /**
372215911Sjfv     * B.2.4 Date.prototype.getYear ( )
373215911Sjfv     *
374215911Sjfv     * @param self self reference
375215911Sjfv     * @return year
376215911Sjfv     */
377215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE)
378215911Sjfv    public static double getYear(final Object self) {
379215911Sjfv        final NativeDate nd = getNativeDate(self);
380215911Sjfv        return (nd != null && nd.isValidDate()) ? (yearFromTime(nd.getLocalTime()) - 1900) : Double.NaN;
381215911Sjfv    }
382215911Sjfv
383215911Sjfv    /**
384238263Sjfv     * ECMA 15.9.5.12 Date.prototype.getMonth ( )
385235528Sjfv     *
386215911Sjfv     * @param self self reference
387215911Sjfv     * @return month
388215911Sjfv     */
389215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE)
390215911Sjfv    public static double getMonth(final Object self) {
391215911Sjfv        return getField(self, MONTH);
392215911Sjfv    }
393215911Sjfv
394215911Sjfv    /**
395215911Sjfv     * ECMA 15.9.5.13 Date.prototype.getUTCMonth ( )
396215911Sjfv     *
397215911Sjfv     * @param self self reference
398215911Sjfv     * @return UTC month
399215911Sjfv     */
400215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE)
401215911Sjfv    public static double getUTCMonth(final Object self) {
402215911Sjfv        return getUTCField(self, MONTH);
403215911Sjfv    }
404215911Sjfv
405215911Sjfv    /**
406215911Sjfv     * ECMA 15.9.5.14 Date.prototype.getDate ( )
407215911Sjfv     *
408215911Sjfv     * @param self self reference
409215911Sjfv     * @return date
410215911Sjfv     */
411215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE)
412215911Sjfv    public static double getDate(final Object self) {
413215911Sjfv        return getField(self, DAY);
414215911Sjfv    }
415215911Sjfv
416215911Sjfv    /**
417215911Sjfv     * ECMA 15.9.5.15 Date.prototype.getUTCDate ( )
418215911Sjfv     *
419215911Sjfv     * @param self self reference
420215911Sjfv     * @return UTC Date
421215911Sjfv     */
422215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE)
423215911Sjfv    public static double getUTCDate(final Object self) {
424215911Sjfv        return getUTCField(self, DAY);
425215911Sjfv    }
426215911Sjfv
427215911Sjfv    /**
428215911Sjfv     * ECMA 15.9.5.16 Date.prototype.getDay ( )
429215911Sjfv     *
430215911Sjfv     * @param self self reference
431215911Sjfv     * @return day
432215911Sjfv     */
433215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE)
434215911Sjfv    public static double getDay(final Object self) {
435215911Sjfv        final NativeDate nd = getNativeDate(self);
436215911Sjfv        return (nd != null && nd.isValidDate()) ? weekDay(nd.getLocalTime()) : Double.NaN;
437215911Sjfv    }
438215911Sjfv
439215911Sjfv    /**
440215911Sjfv     * ECMA 15.9.5.17 Date.prototype.getUTCDay ( )
441215911Sjfv     *
442215911Sjfv     * @param self self reference
443215911Sjfv     * @return UTC day
444215911Sjfv     */
445215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE)
446215911Sjfv    public static double getUTCDay(final Object self) {
447215911Sjfv        final NativeDate nd = getNativeDate(self);
448215911Sjfv        return (nd != null && nd.isValidDate()) ? weekDay(nd.getTime()) : Double.NaN;
449215911Sjfv    }
450215911Sjfv
451215911Sjfv    /**
452215911Sjfv     * ECMA 15.9.5.18 Date.prototype.getHours ( )
453215911Sjfv     *
454215911Sjfv     * @param self self reference
455215911Sjfv     * @return hours
456215911Sjfv     */
457215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE)
458215911Sjfv    public static double getHours(final Object self) {
459215911Sjfv        return getField(self, HOUR);
460215911Sjfv    }
461215911Sjfv
462215911Sjfv    /**
463215911Sjfv     * ECMA 15.9.5.19 Date.prototype.getUTCHours ( )
464215911Sjfv     *
465215911Sjfv     * @param self self reference
466215911Sjfv     * @return UTC hours
467215911Sjfv     */
468215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE)
469215911Sjfv    public static double getUTCHours(final Object self) {
470215911Sjfv        return getUTCField(self, HOUR);
471215911Sjfv    }
472215911Sjfv
473215911Sjfv    /**
474215911Sjfv     * ECMA 15.9.5.20 Date.prototype.getMinutes ( )
475215911Sjfv     *
476215911Sjfv     * @param self self reference
477215911Sjfv     * @return minutes
478215911Sjfv     */
479215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE)
480215911Sjfv    public static double getMinutes(final Object self) {
481215911Sjfv        return getField(self, MINUTE);
482215911Sjfv    }
483215911Sjfv
484215911Sjfv    /**
485215911Sjfv     * ECMA 15.9.5.21 Date.prototype.getUTCMinutes ( )
486215911Sjfv     *
487215911Sjfv     * @param self self reference
488215911Sjfv     * @return UTC minutes
489215911Sjfv     */
490215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE)
491215911Sjfv    public static double getUTCMinutes(final Object self) {
492215911Sjfv        return getUTCField(self, MINUTE);
493215911Sjfv    }
494215911Sjfv
495215911Sjfv    /**
496215911Sjfv     * ECMA 15.9.5.22 Date.prototype.getSeconds ( )
497215911Sjfv     *
498215911Sjfv     * @param self self reference
499215911Sjfv     * @return seconds
500215911Sjfv     */
501215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE)
502215911Sjfv    public static double getSeconds(final Object self) {
503215911Sjfv        return getField(self, SECOND);
504215911Sjfv    }
505215911Sjfv
506215911Sjfv    /**
507215911Sjfv     * ECMA 15.9.5.23 Date.prototype.getUTCSeconds ( )
508215911Sjfv     *
509215911Sjfv     * @param self self reference
510215911Sjfv     * @return UTC seconds
511215911Sjfv     */
512215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE)
513215911Sjfv    public static double getUTCSeconds(final Object self) {
514215911Sjfv        return getUTCField(self, SECOND);
515215911Sjfv    }
516215911Sjfv
517215911Sjfv    /**
518215911Sjfv     * ECMA 15.9.5.24 Date.prototype.getMilliseconds ( )
519215911Sjfv     *
520215911Sjfv     * @param self self reference
521215911Sjfv     * @return milliseconds
522215911Sjfv     */
523215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE)
524215911Sjfv    public static double getMilliseconds(final Object self) {
525215911Sjfv        return getField(self, MILLISECOND);
526215911Sjfv    }
527215911Sjfv
528215911Sjfv    /**
529215911Sjfv     * ECMA 15.9.5.25 Date.prototype.getUTCMilliseconds ( )
530215911Sjfv     *
531215911Sjfv     * @param self self reference
532215911Sjfv     * @return UTC milliseconds
533215911Sjfv     */
534215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE)
535215911Sjfv    public static double getUTCMilliseconds(final Object self) {
536215911Sjfv        return getUTCField(self, MILLISECOND);
537215911Sjfv    }
538215911Sjfv
539215911Sjfv    /**
540215911Sjfv     * ECMA 15.9.5.26 Date.prototype.getTimezoneOffset ( )
541215911Sjfv     *
542215911Sjfv     * @param self self reference
543215911Sjfv     * @return time zone offset or NaN if N/A
544215911Sjfv     */
545215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE)
546215911Sjfv    public static double getTimezoneOffset(final Object self) {
547215911Sjfv        final NativeDate nd = getNativeDate(self);
548215911Sjfv        if (nd != null && nd.isValidDate()) {
549215911Sjfv            final long msec = (long) nd.getTime();
550215911Sjfv            return - nd.getTimeZone().getOffset(msec) / msPerMinute;
551215911Sjfv        }
552215911Sjfv        return Double.NaN;
553215911Sjfv    }
554215911Sjfv
555215911Sjfv    /**
556215911Sjfv     * ECMA 15.9.5.27 Date.prototype.setTime (time)
557215911Sjfv     *
558215911Sjfv     * @param self self reference
559215911Sjfv     * @param time time
560215911Sjfv     * @return time
561215911Sjfv     */
562215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE)
563215911Sjfv    public static double setTime(final Object self, final Object time) {
564215911Sjfv        final NativeDate nd  = getNativeDate(self);
565215911Sjfv        final double     num = timeClip(JSType.toNumber(time));
566215911Sjfv        nd.setTime(num);
567215911Sjfv        return num;
568215911Sjfv    }
569215911Sjfv
570215911Sjfv    /**
571215911Sjfv     * ECMA 15.9.5.28 Date.prototype.setMilliseconds (ms)
572215911Sjfv     *
573215911Sjfv     * @param self self reference
574215911Sjfv     * @param args milliseconds
575215911Sjfv     * @return time
576215911Sjfv     */
577215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
578215911Sjfv    public static double setMilliseconds(final Object self, final Object... args) {
579215911Sjfv        final NativeDate nd = getNativeDate(self);
580215911Sjfv        setFields(nd, MILLISECOND, args, true);
581215911Sjfv        return nd.getTime();
582215911Sjfv    }
583215911Sjfv
584215911Sjfv    /**
585215911Sjfv     * ECMA 15.9.5.29 Date.prototype.setUTCMilliseconds (ms)
586215911Sjfv     *
587215911Sjfv     * @param self self reference
588215911Sjfv     * @param args utc milliseconds
589215911Sjfv     * @return time
590215911Sjfv     */
591215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
592215911Sjfv    public static double setUTCMilliseconds(final Object self, final Object... args) {
593215911Sjfv        final NativeDate nd = getNativeDate(self);
594215911Sjfv        setFields(nd, MILLISECOND, args, false);
595215911Sjfv        return nd.getTime();
596215911Sjfv    }
597215911Sjfv
598215911Sjfv    /**
599215911Sjfv     * ECMA 15.9.5.30 Date.prototype.setSeconds (sec [, ms ] )
600215911Sjfv     *
601215911Sjfv     * @param self self reference
602215911Sjfv     * @param args seconds (milliseconds optional second argument)
603215911Sjfv     * @return time
604215911Sjfv     */
605215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2)
606215911Sjfv    public static double setSeconds(final Object self, final Object... args) {
607215911Sjfv        final NativeDate nd = getNativeDate(self);
608215911Sjfv        setFields(nd, SECOND, args, true);
609215911Sjfv        return nd.getTime();
610215911Sjfv    }
611215911Sjfv
612215911Sjfv    /**
613215911Sjfv     * ECMA 15.9.5.31 Date.prototype.setUTCSeconds (sec [, ms ] )
614215911Sjfv     *
615215911Sjfv     * @param self self reference
616215911Sjfv     * @param args UTC seconds (milliseconds optional second argument)
617215911Sjfv     * @return time
618215911Sjfv     */
619215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2)
620215911Sjfv    public static double setUTCSeconds(final Object self, final Object... args) {
621215911Sjfv        final NativeDate nd = getNativeDate(self);
622215911Sjfv        setFields(nd, SECOND, args, false);
623215911Sjfv        return nd.getTime();
624215911Sjfv    }
625215911Sjfv
626215911Sjfv    /**
627215911Sjfv     * ECMA 15.9.5.32 Date.prototype.setMinutes (min [, sec [, ms ] ] )
628215911Sjfv     *
629215911Sjfv     * @param self self reference
630215911Sjfv     * @param args minutes (seconds and milliseconds are optional second and third arguments)
631215911Sjfv     * @return time
632215911Sjfv     */
633215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 3)
634215911Sjfv    public static double setMinutes(final Object self, final Object... args) {
635215911Sjfv        final NativeDate nd = getNativeDate(self);
636215911Sjfv        setFields(nd, MINUTE, args, true);
637215911Sjfv        return nd.getTime();
638215911Sjfv    }
639243440Sglebius
640243440Sglebius    /**
641243440Sglebius     * ECMA 15.9.5.33 Date.prototype.setUTCMinutes (min [, sec [, ms ] ] )
642215911Sjfv     *
643215911Sjfv     * @param self self reference
644215911Sjfv     * @param args minutes (seconds and milliseconds are optional second and third arguments)
645215911Sjfv     * @return time
646215911Sjfv     */
647215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 3)
648215911Sjfv    public static double setUTCMinutes(final Object self, final Object... args) {
649215911Sjfv        final NativeDate nd = getNativeDate(self);
650215911Sjfv        setFields(nd, MINUTE, args, false);
651215911Sjfv        return nd.getTime();
652215911Sjfv    }
653215911Sjfv
654215911Sjfv    /**
655215911Sjfv     * ECMA 15.9.5.34 Date.prototype.setHours (hour [, min [, sec [, ms ] ] ] )
656215911Sjfv     *
657215911Sjfv     * @param self self reference
658215911Sjfv     * @param args hour (optional arguments after are minutes, seconds, milliseconds)
659215911Sjfv     * @return time
660215911Sjfv     */
661215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 4)
662215911Sjfv    public static double setHours(final Object self, final Object... args) {
663215911Sjfv        final NativeDate nd = getNativeDate(self);
664215911Sjfv        setFields(nd, HOUR, args, true);
665215911Sjfv        return nd.getTime();
666215911Sjfv    }
667215911Sjfv
668215911Sjfv    /**
669215911Sjfv     * ECMA 15.9.5.35 Date.prototype.setUTCHours (hour [, min [, sec [, ms ] ] ] )
670215911Sjfv     *
671215911Sjfv     * @param self self reference
672215911Sjfv     * @param args hour (optional arguments after are minutes, seconds, milliseconds)
673215911Sjfv     * @return time
674215911Sjfv     */
675215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 4)
676215911Sjfv    public static double setUTCHours(final Object self, final Object... args) {
677215911Sjfv        final NativeDate nd = getNativeDate(self);
678215911Sjfv        setFields(nd, HOUR, args, false);
679215911Sjfv        return nd.getTime();
680215911Sjfv    }
681215911Sjfv
682215911Sjfv    /**
683215911Sjfv     * ECMA 15.9.5.36 Date.prototype.setDate (date)
684215911Sjfv     *
685215911Sjfv     * @param self self reference
686215911Sjfv     * @param args date
687215911Sjfv     * @return time
688215911Sjfv     */
689215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
690215911Sjfv    public static double setDate(final Object self, final Object... args) {
691215911Sjfv        final NativeDate nd = getNativeDate(self);
692215911Sjfv        setFields(nd, DAY, args, true);
693215911Sjfv        return nd.getTime();
694215911Sjfv    }
695215911Sjfv
696215911Sjfv    /**
697222588Sjfv     * ECMA 15.9.5.37 Date.prototype.setUTCDate (date)
698222592Sjfv     *
699222588Sjfv     * @param self self reference
700222588Sjfv     * @param args UTC date
701215911Sjfv     * @return time
702215911Sjfv     */
703215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
704215911Sjfv    public static double setUTCDate(final Object self, final Object... args) {
705222588Sjfv        final NativeDate nd = getNativeDate(self);
706222588Sjfv        setFields(nd, DAY, args, false);
707222588Sjfv        return nd.getTime();
708222588Sjfv    }
709222588Sjfv
710222588Sjfv    /**
711222588Sjfv     * ECMA 15.9.5.38 Date.prototype.setMonth (month [, date ] )
712222588Sjfv     *
713222588Sjfv     * @param self self reference
714222588Sjfv     * @param args month (optional second argument is date)
715222588Sjfv     * @return time
716222588Sjfv     */
717222588Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2)
718222588Sjfv    public static double setMonth(final Object self, final Object... args) {
719222588Sjfv        final NativeDate nd = getNativeDate(self);
720222588Sjfv        setFields(nd, MONTH, args, true);
721222588Sjfv        return nd.getTime();
722222588Sjfv    }
723222588Sjfv
724222588Sjfv    /**
725222588Sjfv     * ECMA 15.9.5.39 Date.prototype.setUTCMonth (month [, date ] )
726222588Sjfv     *
727222588Sjfv     * @param self self reference
728222588Sjfv     * @param args UTC month (optional second argument is date)
729215911Sjfv     * @return time
730215911Sjfv     */
731215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2)
732215911Sjfv    public static double setUTCMonth(final Object self, final Object... args) {
733215911Sjfv        final NativeDate nd = ensureNativeDate(self);
734215911Sjfv        setFields(nd, MONTH, args, false);
735215911Sjfv        return nd.getTime();
736215911Sjfv    }
737215911Sjfv
738215911Sjfv    /**
739215911Sjfv     * ECMA 15.9.5.40 Date.prototype.setFullYear (year [, month [, date ] ] )
740215911Sjfv     *
741215911Sjfv     * @param self self reference
742215911Sjfv     * @param args year (optional second and third arguments are month and date)
743215911Sjfv     * @return time
744215911Sjfv     */
745215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 3)
746215911Sjfv    public static double setFullYear(final Object self, final Object... args) {
747215911Sjfv        final NativeDate nd   = ensureNativeDate(self);
748215911Sjfv        if (nd.isValidDate()) {
749215911Sjfv            setFields(nd, YEAR, args, true);
750215911Sjfv        } else {
751215911Sjfv            final double[] d = convertArgs(args, 0, YEAR, YEAR, 3);
752215911Sjfv            if (d != null) {
753215911Sjfv                nd.setTime(timeClip(utc(makeDate(makeDay(d[0], d[1], d[2]), 0), nd.getTimeZone())));
754215911Sjfv            } else {
755215911Sjfv                nd.setTime(NaN);
756215911Sjfv            }
757215911Sjfv        }
758215911Sjfv        return nd.getTime();
759215911Sjfv    }
760215911Sjfv
761215911Sjfv    /**
762215911Sjfv     * ECMA 15.9.5.41 Date.prototype.setUTCFullYear (year [, month [, date ] ] )
763215911Sjfv     *
764215911Sjfv     * @param self self reference
765215911Sjfv     * @param args UTC full year (optional second and third arguments are month and date)
766215911Sjfv     * @return time
767215911Sjfv     */
768215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 3)
769215911Sjfv    public static double setUTCFullYear(final Object self, final Object... args) {
770215911Sjfv        final NativeDate nd   = ensureNativeDate(self);
771215911Sjfv        if (nd.isValidDate()) {
772215911Sjfv            setFields(nd, YEAR, args, false);
773215911Sjfv        } else {
774215911Sjfv            final double[] d = convertArgs(args, 0, YEAR, YEAR, 3);
775215911Sjfv            nd.setTime(timeClip(makeDate(makeDay(d[0], d[1], d[2]), 0)));
776215911Sjfv        }
777215911Sjfv        return nd.getTime();
778215911Sjfv    }
779215911Sjfv
780215911Sjfv    /**
781215911Sjfv     * ECMA B.2.5 Date.prototype.setYear (year)
782215911Sjfv     *
783215911Sjfv     * @param self self reference
784215911Sjfv     * @param year year
785215911Sjfv     * @return NativeDate
786215911Sjfv     */
787215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE)
788215911Sjfv    public static double setYear(final Object self, final Object year) {
789215911Sjfv        final NativeDate nd = getNativeDate(self);
790215911Sjfv        if (isNaN(nd.getTime())) {
791215911Sjfv            nd.setTime(utc(0, nd.getTimeZone()));
792215911Sjfv        }
793215911Sjfv
794215911Sjfv        final double yearNum = JSType.toNumber(year);
795215911Sjfv        if (isNaN(yearNum)) {
796215911Sjfv            nd.setTime(NaN);
797215911Sjfv            return nd.getTime();
798215911Sjfv        }
799215911Sjfv        int yearInt = (int)yearNum;
800215911Sjfv        if (0 <= yearInt && yearInt <= 99) {
801215911Sjfv            yearInt += 1900;
802215911Sjfv        }
803215911Sjfv        setFields(nd, YEAR, new Object[] {yearInt}, true);
804215911Sjfv
805215911Sjfv        return nd.getTime();
806215911Sjfv    }
807215911Sjfv
808215911Sjfv    /**
809215911Sjfv     * ECMA 15.9.5.42 Date.prototype.toUTCString ( )
810215911Sjfv     *
811215911Sjfv     * @param self self reference
812215911Sjfv     * @return string representation of date
813215911Sjfv     */
814215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE)
815215911Sjfv    public static String toUTCString(final Object self) {
816215911Sjfv        return toGMTStringImpl(self);
817215911Sjfv    }
818215911Sjfv
819215911Sjfv    /**
820215911Sjfv     * ECMA B.2.6 Date.prototype.toGMTString ( )
821215911Sjfv     *
822215911Sjfv     * See {@link NativeDate#toUTCString(Object)}
823215911Sjfv     *
824215911Sjfv     * @param self self reference
825215911Sjfv     * @return string representation of date
826215911Sjfv     */
827215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE)
828215911Sjfv    public static String toGMTString(final Object self) {
829215911Sjfv        return toGMTStringImpl(self);
830215911Sjfv    }
831215911Sjfv
832215911Sjfv    /**
833215911Sjfv     * ECMA 15.9.5.43 Date.prototype.toISOString ( )
834215911Sjfv     *
835215911Sjfv     * @param self self reference
836215911Sjfv     * @return string representation of date
837215911Sjfv     */
838215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE)
839215911Sjfv    public static String toISOString(final Object self) {
840215911Sjfv        return toISOStringImpl(self);
841215911Sjfv    }
842215911Sjfv
843215911Sjfv    /**
844215911Sjfv     * ECMA 15.9.5.44 Date.prototype.toJSON ( key )
845215911Sjfv     *
846215911Sjfv     * Provides a string representation of this Date for use by {@link NativeJSON#stringify(Object, Object, Object, Object)}
847215911Sjfv     *
848215911Sjfv     * @param self self reference
849215911Sjfv     * @param key ignored
850215911Sjfv     * @return JSON representation of this date
851215911Sjfv     */
852215911Sjfv    @Function(attributes = Attribute.NOT_ENUMERABLE)
853215911Sjfv    public static Object toJSON(final Object self, final Object key) {
854215911Sjfv        // NOTE: Date.prototype.toJSON is generic. Accepts other objects as well.
855215911Sjfv        final Object selfObj = Global.toObject(self);
856215911Sjfv        if (!(selfObj instanceof ScriptObject)) {
857215911Sjfv            return null;
858215911Sjfv        }
859215911Sjfv        final ScriptObject sobj  = (ScriptObject)selfObj;
860215911Sjfv        final Object       value = sobj.getDefaultValue(Number.class);
861215911Sjfv        if (value instanceof Number) {
862215911Sjfv            final double num = ((Number)value).doubleValue();
863215911Sjfv            if (isInfinite(num) || isNaN(num)) {
864215911Sjfv                return null;
865215911Sjfv            }
866215911Sjfv        }
867215911Sjfv
868215911Sjfv        try {
869215911Sjfv            final InvokeByName toIsoString = getTO_ISO_STRING();
870215911Sjfv            final Object func = toIsoString.getGetter().invokeExact(sobj);
871215911Sjfv            if (Bootstrap.isCallable(func)) {
872215911Sjfv                return toIsoString.getInvoker().invokeExact(func, sobj, key);
873215911Sjfv            }
874215911Sjfv            throw typeError("not.a.function", ScriptRuntime.safeToString(func));
875215911Sjfv        } catch (final RuntimeException | Error e) {
876215911Sjfv            throw e;
877215911Sjfv        } catch (final Throwable t) {
878215911Sjfv            throw new RuntimeException(t);
879215911Sjfv        }
880215911Sjfv    }
881215911Sjfv
882215911Sjfv    // -- Internals below this point
883215911Sjfv
884215911Sjfv    private static double parseDateString(final String str) {
885215911Sjfv
886215911Sjfv        final DateParser parser = new DateParser(str);
887215911Sjfv        if (parser.parse()) {
888215911Sjfv            final Integer[] fields = parser.getDateFields();
889215911Sjfv            double d = makeDate(fields);
890215911Sjfv            if (fields[DateParser.TIMEZONE] != null) {
891215911Sjfv                d -= fields[DateParser.TIMEZONE] * 60000;
892215911Sjfv            } else {
893215911Sjfv                d = utc(d, Global.getEnv()._timezone);
894215911Sjfv            }
895215911Sjfv            d = timeClip(d);
896215911Sjfv            return d;
897215911Sjfv        }
898215911Sjfv
899215911Sjfv        return Double.NaN;
900215911Sjfv    }
901215911Sjfv
902215911Sjfv    private static void zeroPad(final StringBuilder sb, final int n, final int length) {
903215911Sjfv        for (int l = 1, d = 10; l < length; l++, d *= 10) {
904215911Sjfv            if (n < d) {
905215911Sjfv                sb.append('0');
906215911Sjfv            }
907215911Sjfv        }
908215911Sjfv        sb.append(n);
909215911Sjfv    }
910215911Sjfv
911215911Sjfv    @SuppressWarnings("fallthrough")
912215911Sjfv    private static String toStringImpl(final Object self, final int format) {
913215911Sjfv        final NativeDate nd = getNativeDate(self);
914215911Sjfv
915215911Sjfv        if (nd != null && nd.isValidDate()) {
916215911Sjfv            final StringBuilder sb = new StringBuilder(40);
917215911Sjfv            final double t = nd.getLocalTime();
918215911Sjfv
919215911Sjfv            switch (format) {
920215911Sjfv
921215911Sjfv                case FORMAT_DATE_TIME:
922215911Sjfv                case FORMAT_DATE :
923215911Sjfv                case FORMAT_LOCAL_DATE_TIME:
924215911Sjfv                    // EEE MMM dd yyyy
925215911Sjfv                    sb.append(weekDays[weekDay(t)])
926215911Sjfv                            .append(' ')
927215911Sjfv                            .append(months[monthFromTime(t)])
928215911Sjfv                            .append(' ');
929215911Sjfv                    zeroPad(sb, dayFromTime(t), 2);
930215911Sjfv                    sb.append(' ');
931215911Sjfv                    zeroPad(sb, yearFromTime(t), 4);
932215911Sjfv                    if (format == FORMAT_DATE) {
933215911Sjfv                        break;
934215911Sjfv                    }
935215911Sjfv                    sb.append(' ');
936215911Sjfv
937215911Sjfv                case FORMAT_TIME:
938215911Sjfv                    final TimeZone tz = nd.getTimeZone();
939215911Sjfv                    final double utcTime = nd.getTime();
940215911Sjfv                    int offset = tz.getOffset((long) utcTime) / 60000;
941215911Sjfv                    final boolean inDaylightTime = offset != tz.getRawOffset() / 60000;
942215911Sjfv                    // Convert minutes to HHmm timezone offset
943215911Sjfv                    offset = (offset / 60) * 100 + offset % 60;
944215911Sjfv
945215911Sjfv                    // HH:mm:ss GMT+HHmm
946215911Sjfv                    zeroPad(sb, hourFromTime(t), 2);
947215911Sjfv                    sb.append(':');
948215911Sjfv                    zeroPad(sb, minFromTime(t), 2);
949215911Sjfv                    sb.append(':');
950215911Sjfv                    zeroPad(sb, secFromTime(t), 2);
951215911Sjfv                    sb.append(" GMT")
952215911Sjfv                            .append(offset < 0 ? '-' : '+');
953215911Sjfv                    zeroPad(sb, Math.abs(offset), 4);
954215911Sjfv                    sb.append(" (")
955215911Sjfv                            .append(tz.getDisplayName(inDaylightTime, TimeZone.SHORT, Locale.US))
956215911Sjfv                            .append(')');
957215911Sjfv                    break;
958215911Sjfv
959215911Sjfv                case FORMAT_LOCAL_DATE:
960215911Sjfv                    // yyyy-MM-dd
961215911Sjfv                    zeroPad(sb, yearFromTime(t), 4);
962215911Sjfv                    sb.append('-');
963215911Sjfv                    zeroPad(sb, monthFromTime(t) + 1, 2);
964215911Sjfv                    sb.append('-');
965215911Sjfv                    zeroPad(sb, dayFromTime(t), 2);
966215911Sjfv                    break;
967215911Sjfv
968215911Sjfv                case FORMAT_LOCAL_TIME:
969215911Sjfv                    // HH:mm:ss
970215911Sjfv                    zeroPad(sb, hourFromTime(t), 2);
971215911Sjfv                    sb.append(':');
972215911Sjfv                    zeroPad(sb, minFromTime(t), 2);
973215911Sjfv                    sb.append(':');
974215911Sjfv                    zeroPad(sb, secFromTime(t), 2);
975215911Sjfv                    break;
976215911Sjfv
977215911Sjfv                default:
978215911Sjfv                    throw new IllegalArgumentException("format: " + format);
979215911Sjfv            }
980215911Sjfv
981215911Sjfv            return sb.toString();
982215911Sjfv        }
983215911Sjfv
984215911Sjfv        return INVALID_DATE;
985215911Sjfv    }
986215911Sjfv
987215911Sjfv    private static String toGMTStringImpl(final Object self) {
988215911Sjfv        final NativeDate nd = getNativeDate(self);
989215911Sjfv
990215911Sjfv        if (nd != null && nd.isValidDate()) {
991215911Sjfv            final StringBuilder sb = new StringBuilder(29);
992215911Sjfv            final double t = nd.getTime();
993215911Sjfv            // EEE, dd MMM yyyy HH:mm:ss z
994215911Sjfv            sb.append(weekDays[weekDay(t)])
995215911Sjfv                    .append(", ");
996215911Sjfv            zeroPad(sb, dayFromTime(t), 2);
997215911Sjfv            sb.append(' ')
998215911Sjfv                    .append(months[monthFromTime(t)])
999215911Sjfv                    .append(' ');
1000215911Sjfv            zeroPad(sb, yearFromTime(t), 4);
1001215911Sjfv            sb.append(' ');
1002215911Sjfv            zeroPad(sb, hourFromTime(t), 2);
1003215911Sjfv            sb.append(':');
1004215911Sjfv            zeroPad(sb, minFromTime(t), 2);
1005215911Sjfv            sb.append(':');
1006215911Sjfv            zeroPad(sb, secFromTime(t), 2);
1007215911Sjfv            sb.append(" GMT");
1008215911Sjfv            return sb.toString();
1009215911Sjfv        }
1010215911Sjfv
1011215911Sjfv        throw rangeError("invalid.date");
1012215911Sjfv    }
1013215911Sjfv
1014215911Sjfv    private static String toISOStringImpl(final Object self) {
1015215911Sjfv        final NativeDate nd = getNativeDate(self);
1016215911Sjfv
1017215911Sjfv        if (nd != null && nd.isValidDate()) {
1018215911Sjfv            final StringBuilder sb = new StringBuilder(24);
1019215911Sjfv            final double t = nd.getTime();
1020215911Sjfv            // yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
1021215911Sjfv            zeroPad(sb, yearFromTime(t), 4);
1022215911Sjfv            sb.append('-');
1023215911Sjfv            zeroPad(sb, monthFromTime(t) + 1, 2);
1024235528Sjfv            sb.append('-');
1025235528Sjfv            zeroPad(sb, dayFromTime(t), 2);
1026235528Sjfv            sb.append('T');
1027235528Sjfv            zeroPad(sb, hourFromTime(t), 2);
1028235528Sjfv            sb.append(':');
1029235528Sjfv            zeroPad(sb, minFromTime(t), 2);
1030235528Sjfv            sb.append(':');
1031235528Sjfv            zeroPad(sb, secFromTime(t), 2);
1032235528Sjfv            sb.append('.');
1033235528Sjfv            zeroPad(sb, msFromTime(t), 3);
1034235528Sjfv            sb.append("Z");
1035215911Sjfv            return sb.toString();
1036215911Sjfv        }
1037215911Sjfv
1038215911Sjfv        throw rangeError("invalid.date");
1039215911Sjfv    }
1040215911Sjfv
1041215911Sjfv    // ECMA 15.9.1.2 Day (t)
1042215911Sjfv    private static double day(final double t) {
1043215911Sjfv        return Math.floor(t / msPerDay);
1044215911Sjfv    }
1045215911Sjfv
1046215911Sjfv    // ECMA 15.9.1.2 TimeWithinDay (t)
1047215911Sjfv    private static double timeWithinDay(final double t) {
1048215911Sjfv        final double val = t % msPerDay;
1049215911Sjfv        return val < 0? val + msPerDay : val;
1050215911Sjfv    }
1051215911Sjfv
1052215911Sjfv    // ECMA 15.9.1.3 InLeapYear (t)
1053215911Sjfv    private static boolean isLeapYear(final int y) {
1054215911Sjfv        return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0);
1055215911Sjfv    }
1056215911Sjfv
1057215911Sjfv    // ECMA 15.9.1.3 DaysInYear (y)
1058215911Sjfv    private static int daysInYear(final int y) {
1059215911Sjfv        return isLeapYear(y) ? 366 : 365;
1060215911Sjfv    }
1061215911Sjfv
1062215911Sjfv    // ECMA 15.9.1.3 DayFromYear (y)
1063215911Sjfv    private static double dayFromYear(final double y) {
1064215911Sjfv        return 365 * (y - 1970)
1065215911Sjfv                + Math.floor((y -1969) / 4.0)
1066215911Sjfv                - Math.floor((y - 1901) / 100.0)
1067215911Sjfv                + Math.floor((y - 1601) / 400.0);
1068215911Sjfv    }
1069215911Sjfv
1070215911Sjfv    // ECMA 15.9.1.3 Year Number
1071215911Sjfv    private static double timeFromYear(final int y) {
1072215911Sjfv        return dayFromYear(y) * msPerDay;
1073215911Sjfv    }
1074215911Sjfv
1075215911Sjfv    // ECMA 15.9.1.3 Year Number
1076215911Sjfv    private static int yearFromTime(final double t) {
1077215911Sjfv        int y = (int) Math.floor(t / (msPerDay * 365.2425)) + 1970;
1078215911Sjfv        final double t2 = timeFromYear(y);
1079215911Sjfv        if (t2 > t) {
1080215911Sjfv            y--;
1081215911Sjfv        } else if (t2 + msPerDay * daysInYear(y) <= t) {
1082215911Sjfv            y++;
1083215911Sjfv        }
1084215911Sjfv        return y;
1085215911Sjfv    }
1086215911Sjfv
1087215911Sjfv    private static int dayWithinYear(final double t, final int year) {
1088215911Sjfv        return (int) (day(t) - dayFromYear(year));
1089215911Sjfv    }
1090215911Sjfv
1091215911Sjfv    private static int monthFromTime(final double t) {
1092215911Sjfv        final int year = yearFromTime(t);
1093215911Sjfv        final int day = dayWithinYear(t, year);
1094215911Sjfv        final int[] firstDay = firstDayInMonth[isLeapYear(year) ? 1 : 0];
1095215911Sjfv        int month = 0;
1096215911Sjfv
1097215911Sjfv        while (month < 11 && firstDay[month + 1] <= day) {
1098215911Sjfv            month++;
1099215911Sjfv        }
1100215911Sjfv        return month;
1101215911Sjfv    }
1102215911Sjfv
1103215911Sjfv    private static int dayFromTime(final double t)  {
1104215911Sjfv        final int year = yearFromTime(t);
1105215911Sjfv        final int day = dayWithinYear(t, year);
1106215911Sjfv        final int[] firstDay = firstDayInMonth[isLeapYear(year) ? 1 : 0];
1107215911Sjfv        int month = 0;
1108215911Sjfv
1109215911Sjfv        while (month < 11 && firstDay[month + 1] <= day) {
1110215911Sjfv            month++;
1111215911Sjfv        }
1112215911Sjfv        return 1 + day - firstDay[month];
1113215911Sjfv    }
1114215911Sjfv
1115215911Sjfv    private static int dayFromMonth(final int month, final int year) {
1116215911Sjfv        assert(month >= 0 && month <= 11);
1117215911Sjfv        final int[] firstDay = firstDayInMonth[isLeapYear(year) ? 1 : 0];
1118215911Sjfv        return firstDay[month];
1119215911Sjfv    }
1120215911Sjfv
1121215911Sjfv    private static int weekDay(final double time) {
1122215911Sjfv        final int day = (int) (day(time) + 4) % 7;
1123215911Sjfv        return day < 0 ? day + 7 : day;
1124215911Sjfv    }
1125215911Sjfv
1126215911Sjfv    // ECMA 15.9.1.9 LocalTime
1127215911Sjfv    private static double localTime(final double time, final TimeZone tz) {
1128215911Sjfv        return time + tz.getOffset((long) time);
1129215911Sjfv    }
1130215911Sjfv
1131215911Sjfv    // ECMA 15.9.1.9 UTC
1132215911Sjfv    private static double utc(final double time, final TimeZone tz) {
1133215911Sjfv        return time - tz.getOffset((long) (time - tz.getRawOffset()));
1134215911Sjfv    }
1135215911Sjfv
1136215911Sjfv    // ECMA 15.9.1.10 Hours, Minutes, Second, and Milliseconds
1137215911Sjfv    private static int hourFromTime(final double t) {
1138215911Sjfv        final int h = (int) (Math.floor(t / msPerHour) % hoursPerDay);
1139215911Sjfv        return h < 0 ? h + hoursPerDay: h;
1140215911Sjfv    }
1141215911Sjfv    private static int minFromTime(final double t) {
1142215911Sjfv        final int m = (int) (Math.floor(t / msPerMinute) % minutesPerHour);
1143215911Sjfv        return m < 0 ? m + minutesPerHour : m;
1144215911Sjfv    }
1145215911Sjfv
1146215911Sjfv    private static int secFromTime(final double t) {
1147215911Sjfv        final int s = (int) (Math.floor(t / msPerSecond) % secondsPerMinute);
1148215911Sjfv        return s < 0 ? s + secondsPerMinute : s;
1149215911Sjfv    }
1150215911Sjfv
1151215911Sjfv    private static int msFromTime(final double t) {
1152215911Sjfv        final int m = (int) (t % msPerSecond);
1153215911Sjfv        return m < 0 ? m + msPerSecond : m;
1154215911Sjfv    }
1155215911Sjfv
1156215911Sjfv    private static int valueFromTime(final int unit, final double t) {
1157215911Sjfv        switch (unit) {
1158215911Sjfv            case YEAR: return yearFromTime(t);
1159215911Sjfv            case MONTH: return monthFromTime(t);
1160215911Sjfv            case DAY: return dayFromTime(t);
1161215911Sjfv            case HOUR: return hourFromTime(t);
1162215911Sjfv            case MINUTE: return minFromTime(t);
1163215911Sjfv            case SECOND: return secFromTime(t);
1164215911Sjfv            case MILLISECOND: return msFromTime(t);
1165215911Sjfv            default: throw new IllegalArgumentException(Integer.toString(unit));
1166215911Sjfv        }
1167215911Sjfv    }
1168215911Sjfv
1169215911Sjfv    // ECMA 15.9.1.11 MakeTime (hour, min, sec, ms)
1170215911Sjfv    private static double makeTime(final double hour, final double min, final double sec, final double ms) {
1171215911Sjfv        return hour * 3600000 + min * 60000 + sec * 1000 + ms;
1172215911Sjfv    }
1173215911Sjfv
1174215911Sjfv    // ECMA 15.9.1.12 MakeDay (year, month, date)
1175215911Sjfv    private static double makeDay(final double year, final double month, final double date) {
1176215911Sjfv        final double y = year + Math.floor(month / 12);
1177215911Sjfv        int m = (int) (month % 12);
1178215911Sjfv        if (m < 0) {
1179215911Sjfv            m += 12;
1180215911Sjfv        }
1181215911Sjfv        double d = dayFromYear(y);
1182215911Sjfv        d += dayFromMonth(m, (int) y);
1183215911Sjfv
1184215911Sjfv        return d + date - 1;
1185215911Sjfv    }
1186215911Sjfv
1187215911Sjfv    // ECMA 15.9.1.13 MakeDate (day, time)
1188215911Sjfv    private static double makeDate(final double day, final double time) {
1189215911Sjfv        return day * msPerDay + time;
1190215911Sjfv    }
1191215911Sjfv
1192215911Sjfv
1193215911Sjfv    private static double makeDate(final Integer[] d) {
1194215911Sjfv        final double time = makeDay(d[0], d[1], d[2]) * msPerDay;
1195215911Sjfv        return time + makeTime(d[3], d[4], d[5], d[6]);
1196215911Sjfv    }
1197215911Sjfv
1198215911Sjfv    private static double makeDate(final double[] d) {
1199215911Sjfv        final double time = makeDay(d[0], d[1], d[2]) * msPerDay;
1200215911Sjfv        return time + makeTime(d[3], d[4], d[5], d[6]);
1201215911Sjfv    }
1202215911Sjfv
1203215911Sjfv    // Convert Date constructor args, checking for NaN, filling in defaults etc.
1204215911Sjfv    private static double[] convertCtorArgs(final Object[] args) {
1205215911Sjfv        final double[] d = new double[7];
1206215911Sjfv        boolean nullReturn = false;
1207235528Sjfv
1208215911Sjfv        // should not bailout on first NaN or infinite. Need to convert all
1209215911Sjfv        // subsequent args for possible side-effects via valueOf/toString overrides
1210215911Sjfv        // on argument objects.
1211215911Sjfv        for (int i = 0; i < d.length; i++) {
1212215911Sjfv            if (i < args.length) {
1213215911Sjfv                final double darg = JSType.toNumber(args[i]);
1214215911Sjfv                if (isNaN(darg) || isInfinite(darg)) {
1215215911Sjfv                    nullReturn = true;
1216215911Sjfv                }
1217215911Sjfv
1218215911Sjfv                d[i] = (long)darg;
1219215911Sjfv            } else {
1220215911Sjfv                d[i] = i == 2 ? 1 : 0; // day in month defaults to 1
1221215911Sjfv            }
1222215911Sjfv        }
1223215911Sjfv
1224215911Sjfv        if (0 <= d[0] && d[0] <= 99) {
1225215911Sjfv            d[0] += 1900;
1226235528Sjfv        }
1227215911Sjfv
1228215911Sjfv        return nullReturn? null : d;
1229215911Sjfv    }
1230215911Sjfv
1231215911Sjfv    // This method does the hard work for all setter methods: If a value is provided
1232215911Sjfv    // as argument it is used, otherwise the value is calculated from the existing time value.
1233215911Sjfv    private static double[] convertArgs(final Object[] args, final double time, final int fieldId, final int start, final int length) {
1234215911Sjfv        final double[] d = new double[length];
1235215911Sjfv        boolean nullReturn = false;
1236215911Sjfv
1237215911Sjfv        // Need to call toNumber on all args for side-effects - even if an argument
1238248078Smarius        // fails to convert to number, subsequent toNumber calls needed for possible
1239215911Sjfv        // side-effects via valueOf/toString overrides.
1240215911Sjfv        for (int i = start; i < start + length; i++) {
1241215911Sjfv            if (fieldId <= i && i < fieldId + args.length) {
1242215911Sjfv                final double darg = JSType.toNumber(args[i - fieldId]);
1243215911Sjfv                if (isNaN(darg) || isInfinite(darg)) {
1244215911Sjfv                    nullReturn = true;
1245215911Sjfv                }
1246215911Sjfv
1247215911Sjfv                d[i - start] = (long) darg;
1248215911Sjfv            } else {
1249215911Sjfv                // Date.prototype.set* methods require first argument to be defined
1250215911Sjfv                if (i == fieldId) {
1251215911Sjfv                    nullReturn = true;
1252215911Sjfv                }
1253215911Sjfv
1254215911Sjfv                if (!nullReturn && !isNaN(time)) {
1255215911Sjfv                    d[i - start] = valueFromTime(i, time);
1256215911Sjfv                }
1257215911Sjfv            }
1258215911Sjfv        }
1259215911Sjfv
1260215911Sjfv        return nullReturn ? null : d;
1261215911Sjfv    }
1262215911Sjfv
1263215911Sjfv    // ECMA 15.9.1.14 TimeClip (time)
1264215911Sjfv    private static double timeClip(final double time) {
1265215911Sjfv        if (isInfinite(time) || isNaN(time) || Math.abs(time) > 8.64e15) {
1266215911Sjfv            return Double.NaN;
1267215911Sjfv        }
1268215911Sjfv        return (long)time;
1269215911Sjfv    }
1270215911Sjfv
1271215911Sjfv    private static NativeDate ensureNativeDate(final Object self) {
1272215911Sjfv        return getNativeDate(self);
1273215911Sjfv    }
1274215911Sjfv
1275215911Sjfv    private static NativeDate getNativeDate(final Object self) {
1276215911Sjfv        if (self instanceof NativeDate) {
1277215911Sjfv            return (NativeDate)self;
1278215911Sjfv        } else if (self != null && self == Global.instance().getDatePrototype()) {
1279215911Sjfv            return Global.instance().DEFAULT_DATE;
1280215911Sjfv        } else {
1281215911Sjfv            throw typeError("not.a.date", ScriptRuntime.safeToString(self));
1282215911Sjfv        }
1283215911Sjfv    }
1284215911Sjfv
1285215911Sjfv    private static double getField(final Object self, final int field) {
1286215911Sjfv        final NativeDate nd = getNativeDate(self);
1287215911Sjfv        return (nd != null && nd.isValidDate()) ? (double)valueFromTime(field, nd.getLocalTime()) : Double.NaN;
1288215911Sjfv    }
1289215911Sjfv
1290215911Sjfv    private static double getUTCField(final Object self, final int field) {
1291215911Sjfv        final NativeDate nd = getNativeDate(self);
1292215911Sjfv        return (nd != null && nd.isValidDate()) ? (double)valueFromTime(field, nd.getTime()) : Double.NaN;
1293215911Sjfv    }
1294215911Sjfv
1295215911Sjfv    private static void setFields(final NativeDate nd, final int fieldId, final Object[] args, final boolean local) {
1296215911Sjfv        int start, length;
1297215911Sjfv        if (fieldId < HOUR) {
1298215911Sjfv            start = YEAR;
1299215911Sjfv            length = 3;
1300215911Sjfv        } else {
1301215911Sjfv            start = HOUR;
1302215911Sjfv            length = 4;
1303215911Sjfv        }
1304215911Sjfv        final double time = local ? nd.getLocalTime() : nd.getTime();
1305215911Sjfv        final double d[] = convertArgs(args, time, fieldId, start, length);
1306215911Sjfv
1307215911Sjfv        if (! nd.isValidDate()) {
1308215911Sjfv            return;
1309215911Sjfv        }
1310215911Sjfv
1311215911Sjfv        double newTime;
1312215911Sjfv        if (d == null) {
1313215911Sjfv            newTime = NaN;
1314215911Sjfv        } else {
1315215911Sjfv            if (start == YEAR) {
1316215911Sjfv                newTime = makeDate(makeDay(d[0], d[1], d[2]), timeWithinDay(time));
1317215911Sjfv            } else {
1318215911Sjfv                newTime = makeDate(day(time), makeTime(d[0], d[1], d[2], d[3]));
1319215911Sjfv            }
1320215911Sjfv            if (local) {
1321215911Sjfv                newTime = utc(newTime, nd.getTimeZone());
1322215911Sjfv            }
1323215911Sjfv            newTime = timeClip(newTime);
1324215911Sjfv        }
1325215911Sjfv        nd.setTime(newTime);
1326215911Sjfv    }
1327215911Sjfv
1328215911Sjfv    private boolean isValidDate() {
1329222588Sjfv        return !isNaN(time);
1330215911Sjfv    }
1331215911Sjfv
1332215911Sjfv    private double getLocalTime() {
1333215911Sjfv        return localTime(time, timezone);
1334215911Sjfv    }
1335215911Sjfv
1336215911Sjfv    private double getTime() {
1337215911Sjfv        return time;
1338215911Sjfv    }
1339215911Sjfv
1340215911Sjfv    private void setTime(final double time) {
1341215911Sjfv        this.time = time;
1342215911Sjfv    }
1343215911Sjfv
1344215911Sjfv    private TimeZone getTimeZone() {
1345215911Sjfv        return timezone;
1346215911Sjfv    }
1347215911Sjfv}
1348215911Sjfv