1 /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3 * The contents of this file are subject to the Netscape Public
4 * License Version 1.1 (the "License"); you may not use this file
5 * except in compliance with the License. You may obtain a copy of
6 * the License at http://www.mozilla.org/NPL/
8 * Software distributed under the License is distributed on an "AS
9 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
10 * implied. See the License for the specific language governing
11 * rights and limitations under the License.
13 * The Original Code is Rhino code, released
16 * The Initial Developer of the Original Code is Netscape
17 * Communications Corporation. Portions created by Netscape are
18 * Copyright (C) 1997-1999 Netscape Communications Corporation. All
25 * Alternatively, the contents of this file may be used under the
26 * terms of the GNU Public License (the "GPL"), in which case the
27 * provisions of the GPL are applicable instead of those above.
28 * If you wish to allow use of your version of this file only
29 * under the terms of the GPL and not to allow others to use your
30 * version of this file under the NPL, indicate your decision by
31 * deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL. If you do not delete
33 * the provisions above, a recipient may use your version of this
34 * file under either the NPL or the GPL.
39 import java.text.DateFormat;
42 * This class implements the Date native object.
45 * @author Adam Megacz (many modifications
47 public class JSDate extends JS.Immutable {
48 private static final JS.Method METHOD = new JS.Method();
51 if (thisTimeZone == null) {
52 // j.u.TimeZone is synchronized, so setting class statics from it
54 thisTimeZone = java.util.TimeZone.getDefault();
55 LocalTZA = thisTimeZone.getRawOffset();
59 public String coerceToString() { return date_format(date, FORMATSPEC_FULL); }
61 public JS call(JS method, JS[] args) throws JSExn {
64 //#switch(JSU.toString(method))
65 case "toString": return JSU.S(date_format(date, FORMATSPEC_FULL));
66 case "toTimeString": return JSU.S(date_format(date, FORMATSPEC_TIME));
67 case "toDateString": return JSU.S(date_format(date, FORMATSPEC_DATE));
68 case "toLocaleString": return JSU.S(toLocaleString(date));
69 case "toLocaleTimeString": return JSU.S(toLocaleTimeString(date));
70 case "toLocaleDateString": return JSU.S(toLocaleDateString(date));
71 case "toUTCString": return JSU.S(toUTCString(date));
72 case "valueOf": return JSU.N(this.date);
73 case "getTime": return JSU.N(this.date);
74 case "getYear": return JSU.N(getYear(date));
75 case "getFullYear": return JSU.N(YearFromTime(LocalTime(date)));
76 case "getUTCFullYear": return JSU.N(YearFromTime(date));
77 case "getMonth": return JSU.N(MonthFromTime(LocalTime(date)));
78 case "getUTCMonth": return JSU.N(MonthFromTime(date));
79 case "getDate": return JSU.N(DateFromTime(LocalTime(date)));
80 case "getUTCDate": return JSU.N(DateFromTime(date));
81 case "getDay": return JSU.N(WeekDay(LocalTime(date)));
82 case "getUTCDay": return JSU.N(WeekDay(date));
83 case "getHours": return JSU.N(HourFromTime(LocalTime(date)));
84 case "getUTCHours": return JSU.N(HourFromTime(date));
85 case "getMinutes": return JSU.N(MinFromTime(LocalTime(date)));
86 case "getUTCMinutes": return JSU.N(MinFromTime(date));
87 case "getSeconds": return JSU.N(SecFromTime(LocalTime(date)));
88 case "getUTCSeconds": return JSU.N(SecFromTime(date));
89 case "getMilliseconds": return JSU.N(msFromTime(LocalTime(date)));
90 case "getUTCMilliseconds": return JSU.N(msFromTime(date));
91 case "getTimezoneOffset": return JSU.N(getTimezoneOffset(date));
93 return super.call(method, args);
96 //#switch(JSU.toString(method))
97 case "setTime": return JSU.N(this.setTime(JSU.toDouble(args[0])));
98 case "setYear": return JSU.N(this.setYear(JSU.toDouble(args[0])));
103 //#switch(JSU.toString(method))
104 case "setMilliseconds": return JSU.N(this.makeTime(args, 1, true));
105 case "setUTCMilliseconds": return JSU.N(this.makeTime(args, 1, false));
106 case "setSeconds": return JSU.N(this.makeTime(args, 2, true));
107 case "setUTCSeconds": return JSU.N(this.makeTime(args, 2, false));
108 case "setMinutes": return JSU.N(this.makeTime(args, 3, true));
109 case "setUTCMinutes": return JSU.N(this.makeTime(args, 3, false));
110 case "setHours": return JSU.N(this.makeTime(args, 4, true));
111 case "setUTCHours": return JSU.N(this.makeTime(args, 4, false));
112 case "setDate": return JSU.N(this.makeDate(args, 1, true));
113 case "setUTCDate": return JSU.N(this.makeDate(args, 1, false));
114 case "setMonth": return JSU.N(this.makeDate(args, 2, true));
115 case "setUTCMonth": return JSU.N(this.makeDate(args, 2, false));
116 case "setFullYear": return JSU.N(this.makeDate(args, 3, true));
117 case "setUTCFullYear": return JSU.N(this.makeDate(args, 3, false));
121 return super.call(method, args);
124 public JS get(JS key) throws JSExn {
125 //#switch(JSU.toString(key))
126 case "toString": return METHOD;
127 case "toTimeString": return METHOD;
128 case "toDateString": return METHOD;
129 case "toLocaleString": return METHOD;
130 case "toLocaleTimeString": return METHOD;
131 case "toLocaleDateString": return METHOD;
132 case "toUTCString": return METHOD;
133 case "valueOf": return METHOD;
134 case "getTime": return METHOD;
135 case "getYear": return METHOD;
136 case "getFullYear": return METHOD;
137 case "getUTCFullYear": return METHOD;
138 case "getMonth": return METHOD;
139 case "getUTCMonth": return METHOD;
140 case "getDate": return METHOD;
141 case "getUTCDate": return METHOD;
142 case "getDay": return METHOD;
143 case "getUTCDay": return METHOD;
144 case "getHours": return METHOD;
145 case "getUTCHours": return METHOD;
146 case "getMinutes": return METHOD;
147 case "getUTCMinutes": return METHOD;
148 case "getSeconds": return METHOD;
149 case "getUTCSeconds": return METHOD;
150 case "getMilliseconds": return METHOD;
151 case "getUTCMilliseconds": return METHOD;
152 case "getTimezoneOffset": return METHOD;
153 case "setTime": return METHOD;
154 case "setYear": return METHOD;
155 case "setMilliseconds": return METHOD;
156 case "setUTCMilliseconds": return METHOD;
157 case "setSeconds": return METHOD;
158 case "setUTCSeconds": return METHOD;
159 case "setMinutes": return METHOD;
160 case "setUTCMinutes": return METHOD;
161 case "setHours": return METHOD;
162 case "setUTCHours": return METHOD;
163 case "setDate": return METHOD;
164 case "setUTCDate": return METHOD;
165 case "setMonth": return METHOD;
166 case "setUTCMonth": return METHOD;
167 case "setFullYear": return METHOD;
168 case "setUTCFullYear": return METHOD;
170 return super.get(key);
173 /* ECMA helper functions */
175 private static final double HalfTimeDomain = 8.64e15;
176 private static final double HoursPerDay = 24.0;
177 private static final double MinutesPerHour = 60.0;
178 private static final double SecondsPerMinute = 60.0;
179 private static final double msPerSecond = 1000.0;
180 private static final double MinutesPerDay = (HoursPerDay * MinutesPerHour);
181 private static final double SecondsPerDay = (MinutesPerDay * SecondsPerMinute);
182 private static final double SecondsPerHour = (MinutesPerHour * SecondsPerMinute);
183 private static final double msPerDay = (SecondsPerDay * msPerSecond);
184 private static final double msPerHour = (SecondsPerHour * msPerSecond);
185 private static final double msPerMinute = (SecondsPerMinute * msPerSecond);
187 private static double Day(double t) {
188 return java.lang.Math.floor(t / msPerDay);
191 private static double TimeWithinDay(double t) {
193 result = t % msPerDay;
199 private static int DaysInYear(int y) {
200 if (y % 4 == 0 && (y % 100 != 0 || y % 400 == 0))
207 /* math here has to be f.p, because we need
208 * floor((1968 - 1969) / 4) == -1
210 private static double DayFromYear(double y) {
211 return ((365 * ((y)-1970) + java.lang.Math.floor(((y)-1969)/4.0)
212 - java.lang.Math.floor(((y)-1901)/100.0) + java.lang.Math.floor(((y)-1601)/400.0)));
215 private static double TimeFromYear(double y) {
216 return DayFromYear(y) * msPerDay;
219 private static int YearFromTime(double t) {
220 int lo = (int) java.lang.Math.floor((t / msPerDay) / 366) + 1970;
221 int hi = (int) java.lang.Math.floor((t / msPerDay) / 365) + 1970;
224 /* above doesn't work for negative dates... */
231 /* Use a simple binary search algorithm to find the right
232 year. This seems like brute force... but the computation
233 of hi and lo years above lands within one year of the
234 correct answer for years within a thousand years of
235 1970; the loop below only requires six iterations
239 if (TimeFromYear(mid) > t) {
242 if (TimeFromYear(mid) <= t) {
244 if (TimeFromYear(temp) > t) {
254 private static boolean InLeapYear(double t) {
255 return DaysInYear(YearFromTime(t)) == 366;
258 private static int DayWithinYear(double t) {
259 int year = YearFromTime(t);
260 return (int) (Day(t) - DayFromYear(year));
263 * The following array contains the day of year for the first day of
264 * each month, where index 0 is January, and day 0 is January 1.
267 private static double DayFromMonth(int m, boolean leap) {
270 if (m >= 7) { day += m / 2 - 1; }
271 else if (m >= 2) { day += (m - 1) / 2 - 1; }
274 if (leap && m >= 2) { ++day; }
279 private static int MonthFromTime(double t) {
282 d = DayWithinYear(t);
287 // Originally coded as step += (InLeapYear(t) ? 29 : 28);
288 // but some jits always returned 28!
296 if (d < (step += 31))
298 if (d < (step += 30))
300 if (d < (step += 31))
302 if (d < (step += 30))
304 if (d < (step += 31))
306 if (d < (step += 31))
308 if (d < (step += 30))
310 if (d < (step += 31))
312 if (d < (step += 30))
317 private static int DateFromTime(double t) {
320 d = DayWithinYear(t);
321 if (d <= (next = 30))
325 // Originally coded as next += (InLeapYear(t) ? 29 : 28);
326 // but some jits always returned 28!
335 if (d <= (next += 31))
338 if (d <= (next += 30))
341 if (d <= (next += 31))
344 if (d <= (next += 30))
347 if (d <= (next += 31))
350 if (d <= (next += 31))
353 if (d <= (next += 30))
356 if (d <= (next += 31))
359 if (d <= (next += 30))
366 private static int WeekDay(double t) {
375 private static double Now() {
376 return (double) System.currentTimeMillis();
379 /* Should be possible to determine the need for this dynamically
380 * if we go with the workaround... I'm not using it now, because I
381 * can't think of any clean way to make toLocaleString() and the
382 * time zone (comment) in toString match the generated string
383 * values. Currently it's wrong-but-consistent in all but the
384 * most recent betas of the JRE - seems to work in 1.1.7.
386 private final static boolean TZO_WORKAROUND = false;
387 private static double DaylightSavingTA(double t) {
388 if (!TZO_WORKAROUND) {
389 java.util.Date date = new java.util.Date((long) t);
390 if (thisTimeZone.inDaylightTime(date))
395 /* Use getOffset if inDaylightTime() is broken, because it
396 * seems to work acceptably. We don't switch over to it
397 * entirely, because it requires (expensive) exploded date arguments,
398 * and the api makes it impossible to handle dst
399 * changeovers cleanly.
402 // Hardcode the assumption that the changeover always
403 // happens at 2:00 AM:
404 t += LocalTZA + (HourFromTime(t) <= 2 ? msPerHour : 0);
406 int year = YearFromTime(t);
407 double offset = thisTimeZone.getOffset(year > 0 ? 1 : 0,
412 (int)TimeWithinDay(t));
414 if ((offset - LocalTZA) != 0)
418 // return offset - LocalTZA;
422 private static double LocalTime(double t) {
423 return t + LocalTZA + DaylightSavingTA(t);
426 public static double internalUTC(double t) {
427 return t - LocalTZA - DaylightSavingTA(t - LocalTZA);
430 private static int HourFromTime(double t) {
432 result = java.lang.Math.floor(t / msPerHour) % HoursPerDay;
434 result += HoursPerDay;
438 private static int MinFromTime(double t) {
440 result = java.lang.Math.floor(t / msPerMinute) % MinutesPerHour;
442 result += MinutesPerHour;
446 private static int SecFromTime(double t) {
448 result = java.lang.Math.floor(t / msPerSecond) % SecondsPerMinute;
450 result += SecondsPerMinute;
454 private static int msFromTime(double t) {
456 result = t % msPerSecond;
458 result += msPerSecond;
462 private static double MakeTime(double hour, double min,
463 double sec, double ms)
465 return ((hour * MinutesPerHour + min) * SecondsPerMinute + sec)
469 private static double MakeDay(double year, double month, double date) {
475 year += java.lang.Math.floor(month / 12);
481 leap = (DaysInYear((int) year) == 366);
483 yearday = java.lang.Math.floor(TimeFromYear(year) / msPerDay);
484 monthday = DayFromMonth((int) month, leap);
492 private static double MakeDate(double day, double time) {
493 return day * msPerDay + time;
496 private static double TimeClip(double d) {
498 d == Double.POSITIVE_INFINITY ||
499 d == Double.NEGATIVE_INFINITY ||
500 java.lang.Math.abs(d) > HalfTimeDomain)
505 return java.lang.Math.floor(d + 0.);
507 return java.lang.Math.ceil(d + 0.);
510 /* end of ECMA helper functions */
512 /* find UTC time from given date... no 1900 correction! */
513 public static double date_msecFromDate(double year, double mon,
514 double mday, double hour,
515 double min, double sec,
522 day = MakeDay(year, mon, mday);
523 time = MakeTime(hour, min, sec, msec);
524 result = MakeDate(day, time);
529 private static final int MAXARGS = 7;
530 private static double jsStaticJSFunction_UTC(JS[] args) throws JSExn {
531 double array[] = new double[MAXARGS];
535 for (loop = 0; loop < MAXARGS; loop++) {
536 if (loop < args.length) {
537 d = _toNumber(args[loop]);
538 if (d != d || Double.isInfinite(d)) {
541 array[loop] = JSU.toDouble(args[loop]);
547 /* adjust 2-digit years into the 20th century */
548 if (array[0] >= 0 && array[0] <= 99)
551 /* if we got a 0 for 'date' (which is out of range)
552 * pretend it's a 1. (So Date.UTC(1972, 5) works) */
556 d = date_msecFromDate(array[0], array[1], array[2],
557 array[3], array[4], array[5], array[6]);
564 * Use ported code from jsdate.c rather than the locale-specific
565 * date-parsing code from Java, to keep js and rhino consistent.
566 * Is this the right strategy?
569 /* for use by date_parse */
571 /* replace this with byte arrays? Cheaper? */
572 private static String wtb[] = {
574 "monday", "tuesday", "wednesday", "thursday", "friday",
575 "saturday", "sunday",
576 "january", "february", "march", "april", "may", "june",
577 "july", "august", "september", "october", "november", "december",
578 "gmt", "ut", "utc", "est", "edt", "cst", "cdt",
579 "mst", "mdt", "pst", "pdt"
580 /* time zone table needs to be expanded */
583 private static int ttb[] = {
584 -1, -2, 0, 0, 0, 0, 0, 0, 0, /* AM/PM */
585 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
586 10000 + 0, 10000 + 0, 10000 + 0, /* UT/UTC */
587 10000 + 5 * 60, 10000 + 4 * 60, /* EDT */
588 10000 + 6 * 60, 10000 + 5 * 60,
589 10000 + 7 * 60, 10000 + 6 * 60,
590 10000 + 8 * 60, 10000 + 7 * 60
593 /* helper for date_parse */
594 private static boolean date_regionMatches(String s1, int s1off,
595 String s2, int s2off,
598 boolean result = false;
599 /* return true if matches, otherwise, false */
600 int s1len = s1.length();
601 int s2len = s2.length();
603 while (count > 0 && s1off < s1len && s2off < s2len) {
604 if (Character.toLowerCase(s1.charAt(s1off)) !=
605 Character.toLowerCase(s2.charAt(s2off)))
618 private static double date_parseString(String s) {
631 double tzoffset = -1;
634 boolean seenplusminus = false;
636 if (s == null) // ??? Will s be null?
642 if (c <= ' ' || c == ',' || c == '-') {
645 if (c == '-' && '0' <= si && si <= '9') {
651 if (c == '(') { /* comments) */
664 if ('0' <= c && c <= '9') {
666 while (i < limit && '0' <= (c = s.charAt(i)) && c <= '9') {
667 n = n * 10 + c - '0';
671 /* allow TZA before the year, so
672 * 'Wed Nov 05 21:49:11 GMT-0800 1997'
675 /* uses of seenplusminus allow : in TZA, so Java
676 * no-timezone style of GMT+4:30 works
678 if ((prevc == '+' || prevc == '-')/* && year>=0 */) {
679 /* make ':' case below change tzoffset */
680 seenplusminus = true;
684 n = n * 60; /* EG. "GMT-3" */
686 n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */
687 if (prevc == '+') /* plus means east of GMT */
689 if (tzoffset != 0 && tzoffset != -1)
692 } else if (n >= 70 ||
693 (prevc == '/' && mon >= 0 && mday >= 0 && year < 0)) {
696 else if (c <= ' ' || c == ',' || c == '/' || i >= limit)
697 year = n < 100 ? n + 1900 : n;
700 } else if (c == ':') {
707 } else if (c == '/') {
714 } else if (i < limit && c != ',' && c > ' ' && c != '-') {
716 } else if (seenplusminus && n < 60) { /* handle GMT-3:30 */
721 } else if (hour >= 0 && min < 0) {
723 } else if (min >= 0 && sec < 0) {
725 } else if (mday < 0) {
731 } else if (c == '/' || c == ':' || c == '+' || c == '-') {
738 if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')))
744 for (k = wtb.length; --k >= 0;)
745 if (date_regionMatches(wtb[k], 0, s, st, i-st)) {
750 * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as
751 * 12:30, instead of blindly adding 12 if PM.
753 if (hour > 12 || hour < 0) {
756 if (action == -1 && hour == 12) { // am
758 } else if (action == -2 && hour != 12) {// pm
762 } else if (action <= 13) { /* month! */
764 mon = /*byte*/ (action - 2);
769 tzoffset = action - 10000;
779 if (year < 0 || mon < 0 || mday < 0)
787 if (tzoffset == -1) { /* no time zone specified, have to use local */
789 time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
790 return internalUTC(time);
793 msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
794 msec += tzoffset * msPerMinute;
798 private static double jsStaticJSFunction_parse(String s) {
799 return date_parseString(s);
802 private static final int FORMATSPEC_FULL = 0;
803 private static final int FORMATSPEC_DATE = 1;
804 private static final int FORMATSPEC_TIME = 2;
806 private static String date_format(double t, int format) {
810 StringBuffer result = new StringBuffer(60);
811 double local = LocalTime(t);
813 /* offset from GMT in minutes. The offset includes daylight savings,
815 int minutes = (int) java.lang.Math.floor((LocalTZA + DaylightSavingTA(t))
817 /* map 510 minutes to 0830 hours */
818 int offset = (minutes / 60) * 100 + minutes % 60;
820 String dateStr = Integer.toString(DateFromTime(local));
821 String hourStr = Integer.toString(HourFromTime(local));
822 String minStr = Integer.toString(MinFromTime(local));
823 String secStr = Integer.toString(SecFromTime(local));
824 String offsetStr = Integer.toString(offset > 0 ? offset : -offset);
825 int year = YearFromTime(local);
826 String yearStr = Integer.toString(year > 0 ? year : -year);
828 /* Tue Oct 31 09:41:40 GMT-0800 (PST) 2000 */
829 /* Tue Oct 31 2000 */
830 /* 09:41:40 GMT-0800 (PST) */
832 if (format != FORMATSPEC_TIME) {
833 result.append(days[WeekDay(local)]);
835 result.append(months[MonthFromTime(local)]);
836 if (dateStr.length() == 1)
840 result.append(dateStr);
844 if (format != FORMATSPEC_DATE) {
845 if (hourStr.length() == 1)
847 result.append(hourStr);
848 if (minStr.length() == 1)
852 result.append(minStr);
853 if (secStr.length() == 1)
857 result.append(secStr);
859 result.append(" GMT+");
861 result.append(" GMT-");
862 for (int i = offsetStr.length(); i < 4; i++)
864 result.append(offsetStr);
866 if (timeZoneFormatter == null)
867 timeZoneFormatter = new java.text.SimpleDateFormat("zzz");
869 if (timeZoneFormatter != null) {
871 java.util.Date date = new java.util.Date((long) t);
872 result.append(timeZoneFormatter.format(date));
875 if (format != FORMATSPEC_TIME)
879 if (format != FORMATSPEC_TIME) {
882 for (int i = yearStr.length(); i < 4; i++)
884 result.append(yearStr);
887 return result.toString();
890 private static double _toNumber(JS o) throws JSExn { return JSU.toDouble(o); }
891 private static double _toNumber(JS[] o, int index) throws JSExn { return JSU.toDouble(o[index]); }
893 public JSDate(long l) throws JSExn {
894 this.date = (double)l;
895 thisTimeZone = java.util.TimeZone.getDefault();
896 LocalTZA = thisTimeZone.getRawOffset();
899 public JSDate(JS[] args) throws JSExn {
902 switch (args.length) {
909 if(JSU.isString(args[0]))
910 date = date_parseString(JSU.toString(args[0]));
912 date = _toNumber(args[0]);
913 obj.date = TimeClip(date);
917 // multiple arguments; year, month, day etc.
918 double array[] = new double[MAXARGS];
919 array[0] = JSU.toDouble(args[0]);
920 array[1] = JSU.toDouble(args[1]);
921 if (args.length >= 2) array[2] = JSU.toDouble(args[2]);
922 for (int i=0; i < args.length; i++) {
923 double d = _toNumber(args[i]);
924 if (d != d || Double.isInfinite(d)) {
925 obj.date = Double.NaN;
931 /* adjust 2-digit years into the 20th century */
932 if (array[0] >= 0 && array[0] <= 99)
935 /* if we got a 0 for 'date' (which is out of range)
936 * pretend it's a 1 */
940 double day = MakeDay(array[0], array[1], array[2]);
941 double time = MakeTime(array[3], array[4], array[5], array[6]);
942 time = MakeDate(day, time);
943 time = internalUTC(time);
944 obj.date = TimeClip(time);
951 /* constants for toString, toUTCString */
952 private static String NaN_date_str = "Invalid Date";
954 private static String[] days = {
955 "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
958 private static String[] months = {
959 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
960 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
963 private static String toLocale_helper(double t,
964 java.text.DateFormat formatter)
969 java.util.Date tempdate = new java.util.Date((long) t);
970 return formatter.format(tempdate);
973 private static String toLocaleString(double date) {
974 if (localeDateTimeFormatter == null)
975 localeDateTimeFormatter =
976 DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG);
978 return toLocale_helper(date, localeDateTimeFormatter);
981 private static String toLocaleTimeString(double date) {
982 if (localeTimeFormatter == null)
983 localeTimeFormatter = DateFormat.getTimeInstance(DateFormat.LONG);
985 return toLocale_helper(date, localeTimeFormatter);
988 private static String toLocaleDateString(double date) {
989 if (localeDateFormatter == null)
990 localeDateFormatter = DateFormat.getDateInstance(DateFormat.LONG);
992 return toLocale_helper(date, localeDateFormatter);
995 private static String toUTCString(double date) {
996 StringBuffer result = new StringBuffer(60);
998 String dateStr = Integer.toString(DateFromTime(date));
999 String hourStr = Integer.toString(HourFromTime(date));
1000 String minStr = Integer.toString(MinFromTime(date));
1001 String secStr = Integer.toString(SecFromTime(date));
1002 int year = YearFromTime(date);
1003 String yearStr = Integer.toString(year > 0 ? year : -year);
1005 result.append(days[WeekDay(date)]);
1006 result.append(", ");
1007 if (dateStr.length() == 1)
1009 result.append(dateStr);
1011 result.append(months[MonthFromTime(date)]);
1013 result.append(" -");
1017 for (i = yearStr.length(); i < 4; i++)
1019 result.append(yearStr);
1021 if (hourStr.length() == 1)
1022 result.append(" 0");
1025 result.append(hourStr);
1026 if (minStr.length() == 1)
1027 result.append(":0");
1030 result.append(minStr);
1031 if (secStr.length() == 1)
1032 result.append(":0");
1035 result.append(secStr);
1037 result.append(" GMT");
1038 return result.toString();
1041 private static double getYear(double date) {
1042 int result = YearFromTime(LocalTime(date));
1047 private static double getTimezoneOffset(double date) {
1048 return (date - LocalTime(date)) / msPerMinute;
1051 public double setTime(double time) {
1052 this.date = TimeClip(time);
1056 private double makeTime(JS[] args, int maxargs, boolean local) throws JSExn {
1058 double conv[] = new double[4];
1059 double hour, min, sec, msec;
1060 double lorutime; /* Local or UTC version of date */
1065 double date = this.date;
1067 /* just return NaN if the date is already NaN */
1071 /* Satisfy the ECMA rule that if a function is called with
1072 * fewer arguments than the specified formal arguments, the
1073 * remaining arguments are set to undefined. Seems like all
1074 * the Date.setWhatever functions in ECMA are only varargs
1075 * beyond the first argument; this should be set to undefined
1076 * if it's not given. This means that "d = new Date();
1077 * d.setMilliseconds()" returns NaN. Blech.
1079 if (args.length == 0)
1080 args = new JS[] { null };
1082 for (i = 0; i < args.length && i < maxargs; i++) {
1083 conv[i] = _toNumber(args[i]);
1085 // limit checks that happen in MakeTime in ECMA.
1086 if (conv[i] != conv[i] || Double.isInfinite(conv[i])) {
1087 this.date = Double.NaN;
1093 lorutime = LocalTime(date);
1098 int stop = args.length;
1100 if (maxargs >= 4 && i < stop)
1103 hour = HourFromTime(lorutime);
1105 if (maxargs >= 3 && i < stop)
1108 min = MinFromTime(lorutime);
1110 if (maxargs >= 2 && i < stop)
1113 sec = SecFromTime(lorutime);
1115 if (maxargs >= 1 && i < stop)
1118 msec = msFromTime(lorutime);
1120 time = MakeTime(hour, min, sec, msec);
1121 result = MakeDate(Day(lorutime), time);
1124 result = internalUTC(result);
1125 date = TimeClip(result);
1131 private double setHours(JS[] args) throws JSExn {
1132 return makeTime(args, 4, true);
1135 private double setUTCHours(JS[] args) throws JSExn {
1136 return makeTime(args, 4, false);
1139 private double makeDate(JS[] args, int maxargs, boolean local) throws JSExn {
1141 double conv[] = new double[3];
1142 double year, month, day;
1143 double lorutime; /* local or UTC version of date */
1146 double date = this.date;
1148 /* See arg padding comment in makeTime.*/
1149 if (args.length == 0)
1150 args = new JS[] { null };
1152 for (i = 0; i < args.length && i < maxargs; i++) {
1153 conv[i] = _toNumber(args[i]);
1155 // limit checks that happen in MakeDate in ECMA.
1156 if (conv[i] != conv[i] || Double.isInfinite(conv[i])) {
1157 this.date = Double.NaN;
1162 /* return NaN if date is NaN and we're not setting the year,
1163 * If we are, use 0 as the time. */
1165 if (args.length < 3) {
1172 lorutime = LocalTime(date);
1178 int stop = args.length;
1180 if (maxargs >= 3 && i < stop)
1183 year = YearFromTime(lorutime);
1185 if (maxargs >= 2 && i < stop)
1188 month = MonthFromTime(lorutime);
1190 if (maxargs >= 1 && i < stop)
1193 day = DateFromTime(lorutime);
1195 day = MakeDay(year, month, day); /* day within year */
1196 result = MakeDate(day, TimeWithinDay(lorutime));
1199 result = internalUTC(result);
1201 date = TimeClip(result);
1207 private double setYear(double year) {
1209 if (year != year || Double.isInfinite(year)) {
1210 this.date = Double.NaN;
1214 if (this.date != this.date) {
1217 this.date = LocalTime(this.date);
1220 if (year >= 0 && year <= 99)
1223 day = MakeDay(year, MonthFromTime(this.date), DateFromTime(this.date));
1224 result = MakeDate(day, TimeWithinDay(this.date));
1225 result = internalUTC(result);
1227 this.date = TimeClip(result);
1232 // private static final int
1233 // Id_toGMTString = Id_toUTCString; // Alias, see Ecma B.2.6
1237 private static java.util.TimeZone thisTimeZone;
1238 private static double LocalTZA;
1239 private static java.text.DateFormat timeZoneFormatter;
1240 private static java.text.DateFormat localeDateTimeFormatter;
1241 private static java.text.DateFormat localeDateFormatter;
1242 private static java.text.DateFormat localeTimeFormatter;
1244 private double date;
1246 public long getRawTime() { return (long)this.date; }