Thu, 24 May 2018 16:39:31 +0800
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();
1014 }
1016 throw rangeError("invalid.date");
1017 }
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();
1041 }
1043 throw rangeError("invalid.date");
1044 }
1046 // ECMA 15.9.1.2 Day (t)
1047 private static double day(final double t) {
1048 return Math.floor(t / msPerDay);
1049 }
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;
1055 }
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);
1060 }
1062 // ECMA 15.9.1.3 DaysInYear (y)
1063 private static int daysInYear(final int y) {
1064 return isLeapYear(y) ? 366 : 365;
1065 }
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);
1073 }
1075 // ECMA 15.9.1.3 Year Number
1076 private static double timeFromYear(final int y) {
1077 return dayFromYear(y) * msPerDay;
1078 }
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++;
1088 }
1089 return y;
1090 }
1092 private static int dayWithinYear(final double t, final int year) {
1093 return (int) (day(t) - dayFromYear(year));
1094 }
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++;
1104 }
1105 return month;
1106 }
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++;
1116 }
1117 return 1 + day - firstDay[month];
1118 }
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];
1124 }
1126 private static int weekDay(final double time) {
1127 final int day = (int) (day(time) + 4) % 7;
1128 return day < 0 ? day + 7 : day;
1129 }
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);
1134 }
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()));
1139 }
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;
1145 }
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;
1149 }
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;
1154 }
1156 private static int msFromTime(final double t) {
1157 final int m = (int) (t % msPerSecond);
1158 return m < 0 ? m + msPerSecond : m;
1159 }
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));
1171 }
1172 }
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;
1177 }
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;
1185 }
1186 double d = dayFromYear(y);
1187 d += dayFromMonth(m, (int) y);
1189 return d + date - 1;
1190 }
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;
1195 }
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]);
1201 }
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]);
1206 }
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;
1221 }
1223 d[i] = (long)darg;
1224 } else {
1225 d[i] = i == 2 ? 1 : 0; // day in month defaults to 1
1226 }
1227 }
1229 if (0 <= d[0] && d[0] <= 99) {
1230 d[0] += 1900;
1231 }
1233 return nullReturn? null : d;
1234 }
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;
1250 }
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;
1257 }
1259 if (!nullReturn && !isNaN(time)) {
1260 d[i - start] = valueFromTime(i, time);
1261 }
1262 }
1263 }
1265 return nullReturn ? null : d;
1266 }
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;
1272 }
1273 return (long)time;
1274 }
1276 private static NativeDate ensureNativeDate(final Object self) {
1277 return getNativeDate(self);
1278 }
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));
1287 }
1288 }
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;
1293 }
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;
1298 }
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;
1308 }
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;
1314 }
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]));
1324 }
1325 if (local) {
1326 newTime = utc(newTime, nd.getTimeZone());
1327 }
1328 newTime = timeClip(newTime);
1329 }
1330 nd.setTime(newTime);
1331 }
1333 private boolean isValidDate() {
1334 return !isNaN(time);
1335 }
1337 private double getLocalTime() {
1338 return localTime(time, timezone);
1339 }
1341 private double getTime() {
1342 return time;
1343 }
1345 private void setTime(final double time) {
1346 this.time = time;
1347 }
1349 private TimeZone getTimeZone() {
1350 return timezone;
1351 }
1352 }