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 {
50 if (thisTimeZone == null) {
51 // j.u.TimeZone is synchronized, so setting class statics from it
53 thisTimeZone = java.util.TimeZone.getDefault();
54 LocalTZA = thisTimeZone.getRawOffset();
58 public String toString() { return date_format(date, FORMATSPEC_FULL); }
60 public Object callMethod(Object method, Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
64 case "toString": return date_format(date, FORMATSPEC_FULL);
65 case "toTimeString": return date_format(date, FORMATSPEC_TIME);
66 case "toDateString": return date_format(date, FORMATSPEC_DATE);
67 case "toLocaleString": return toLocaleString(date);
68 case "toLocaleTimeString": return toLocaleTimeString(date);
69 case "toLocaleDateString": return toLocaleDateString(date);
70 case "toUTCString": return toUTCString(date);
71 case "valueOf": return N(this.date);
72 case "getTime": return N(this.date);
73 case "getYear": return N(getYear(date));
74 case "getFullYear": return N(YearFromTime(LocalTime(date)));
75 case "getUTCFullYear": return N(YearFromTime(date));
76 case "getMonth": return N(MonthFromTime(LocalTime(date)));
77 case "getUTCMonth": return N(MonthFromTime(date));
78 case "getDate": return N(DateFromTime(LocalTime(date)));
79 case "getUTCDate": return N(DateFromTime(date));
80 case "getDay": return N(WeekDay(LocalTime(date)));
81 case "getUTCDay": return N(WeekDay(date));
82 case "getHours": return N(HourFromTime(LocalTime(date)));
83 case "getUTCHours": return N(HourFromTime(date));
84 case "getMinutes": return N(MinFromTime(LocalTime(date)));
85 case "getUTCMinutes": return N(MinFromTime(date));
86 case "getSeconds": return N(SecFromTime(LocalTime(date)));
87 case "getUTCSeconds": return N(SecFromTime(date));
88 case "getMilliseconds": return N(msFromTime(LocalTime(date)));
89 case "getUTCMilliseconds": return N(msFromTime(date));
90 case "getTimezoneOffset": return N(getTimezoneOffset(date));
92 return super.callMethod(method, a0, a1, a2, rest, nargs);
96 case "setTime": return N(this.setTime(toDouble(a0)));
97 case "setYear": return N(this.setYear(toDouble(a0)));
102 Object[] args = new Object[nargs];
103 for(int i=0; i<nargs; i++) args[i] = i==0 ? a0 : i==1 ? a1 : i==2 ? a2 : rest[i-3];
105 case "setMilliseconds": return N(this.makeTime(args, 1, true));
106 case "setUTCMilliseconds": return N(this.makeTime(args, 1, false));
107 case "setSeconds": return N(this.makeTime(args, 2, true));
108 case "setUTCSeconds": return N(this.makeTime(args, 2, false));
109 case "setMinutes": return N(this.makeTime(args, 3, true));
110 case "setUTCMinutes": return N(this.makeTime(args, 3, false));
111 case "setHours": return N(this.makeTime(args, 4, true));
112 case "setUTCHours": return N(this.makeTime(args, 4, false));
113 case "setDate": return N(this.makeDate(args, 1, true));
114 case "setUTCDate": return N(this.makeDate(args, 1, false));
115 case "setMonth": return N(this.makeDate(args, 2, true));
116 case "setUTCMonth": return N(this.makeDate(args, 2, false));
117 case "setFullYear": return N(this.makeDate(args, 3, true));
118 case "setUTCFullYear": return N(this.makeDate(args, 3, false));
122 return super.callMethod(method, a0, a1, a2, rest, nargs);
125 public Object get(Object key) throws JSExn {
127 case "toString": return METHOD;
128 case "toTimeString": return METHOD;
129 case "toDateString": return METHOD;
130 case "toLocaleString": return METHOD;
131 case "toLocaleTimeString": return METHOD;
132 case "toLocaleDateString": return METHOD;
133 case "toUTCString": return METHOD;
134 case "valueOf": return METHOD;
135 case "getTime": return METHOD;
136 case "getYear": return METHOD;
137 case "getFullYear": return METHOD;
138 case "getUTCFullYear": return METHOD;
139 case "getMonth": return METHOD;
140 case "getUTCMonth": return METHOD;
141 case "getDate": return METHOD;
142 case "getUTCDate": return METHOD;
143 case "getDay": return METHOD;
144 case "getUTCDay": return METHOD;
145 case "getHours": return METHOD;
146 case "getUTCHours": return METHOD;
147 case "getMinutes": return METHOD;
148 case "getUTCMinutes": return METHOD;
149 case "getSeconds": return METHOD;
150 case "getUTCSeconds": return METHOD;
151 case "getMilliseconds": return METHOD;
152 case "getUTCMilliseconds": return METHOD;
153 case "getTimezoneOffset": return METHOD;
154 case "setTime": return METHOD;
155 case "setYear": return METHOD;
156 case "setMilliseconds": return METHOD;
157 case "setUTCMilliseconds": return METHOD;
158 case "setSeconds": return METHOD;
159 case "setUTCSeconds": return METHOD;
160 case "setMinutes": return METHOD;
161 case "setUTCMinutes": return METHOD;
162 case "setHours": return METHOD;
163 case "setUTCHours": return METHOD;
164 case "setDate": return METHOD;
165 case "setUTCDate": return METHOD;
166 case "setMonth": return METHOD;
167 case "setUTCMonth": return METHOD;
168 case "setFullYear": return METHOD;
169 case "setUTCFullYear": return METHOD;
171 return super.get(key);
174 /* ECMA helper functions */
176 private static final double HalfTimeDomain = 8.64e15;
177 private static final double HoursPerDay = 24.0;
178 private static final double MinutesPerHour = 60.0;
179 private static final double SecondsPerMinute = 60.0;
180 private static final double msPerSecond = 1000.0;
181 private static final double MinutesPerDay = (HoursPerDay * MinutesPerHour);
182 private static final double SecondsPerDay = (MinutesPerDay * SecondsPerMinute);
183 private static final double SecondsPerHour = (MinutesPerHour * SecondsPerMinute);
184 private static final double msPerDay = (SecondsPerDay * msPerSecond);
185 private static final double msPerHour = (SecondsPerHour * msPerSecond);
186 private static final double msPerMinute = (SecondsPerMinute * msPerSecond);
188 private static double Day(double t) {
189 return java.lang.Math.floor(t / msPerDay);
192 private static double TimeWithinDay(double t) {
194 result = t % msPerDay;
200 private static int DaysInYear(int y) {
201 if (y % 4 == 0 && (y % 100 != 0 || y % 400 == 0))
208 /* math here has to be f.p, because we need
209 * floor((1968 - 1969) / 4) == -1
211 private static double DayFromYear(double y) {
212 return ((365 * ((y)-1970) + java.lang.Math.floor(((y)-1969)/4.0)
213 - java.lang.Math.floor(((y)-1901)/100.0) + java.lang.Math.floor(((y)-1601)/400.0)));
216 private static double TimeFromYear(double y) {
217 return DayFromYear(y) * msPerDay;
220 private static int YearFromTime(double t) {
221 int lo = (int) java.lang.Math.floor((t / msPerDay) / 366) + 1970;
222 int hi = (int) java.lang.Math.floor((t / msPerDay) / 365) + 1970;
225 /* above doesn't work for negative dates... */
232 /* Use a simple binary search algorithm to find the right
233 year. This seems like brute force... but the computation
234 of hi and lo years above lands within one year of the
235 correct answer for years within a thousand years of
236 1970; the loop below only requires six iterations
240 if (TimeFromYear(mid) > t) {
243 if (TimeFromYear(mid) <= t) {
245 if (TimeFromYear(temp) > t) {
255 private static boolean InLeapYear(double t) {
256 return DaysInYear(YearFromTime(t)) == 366;
259 private static int DayWithinYear(double t) {
260 int year = YearFromTime(t);
261 return (int) (Day(t) - DayFromYear(year));
264 * The following array contains the day of year for the first day of
265 * each month, where index 0 is January, and day 0 is January 1.
268 private static double DayFromMonth(int m, boolean leap) {
271 if (m >= 7) { day += m / 2 - 1; }
272 else if (m >= 2) { day += (m - 1) / 2 - 1; }
275 if (leap && m >= 2) { ++day; }
280 private static int MonthFromTime(double t) {
283 d = DayWithinYear(t);
288 // Originally coded as step += (InLeapYear(t) ? 29 : 28);
289 // but some jits always returned 28!
297 if (d < (step += 31))
299 if (d < (step += 30))
301 if (d < (step += 31))
303 if (d < (step += 30))
305 if (d < (step += 31))
307 if (d < (step += 31))
309 if (d < (step += 30))
311 if (d < (step += 31))
313 if (d < (step += 30))
318 private static int DateFromTime(double t) {
321 d = DayWithinYear(t);
322 if (d <= (next = 30))
326 // Originally coded as next += (InLeapYear(t) ? 29 : 28);
327 // but some jits always returned 28!
336 if (d <= (next += 31))
339 if (d <= (next += 30))
342 if (d <= (next += 31))
345 if (d <= (next += 30))
348 if (d <= (next += 31))
351 if (d <= (next += 31))
354 if (d <= (next += 30))
357 if (d <= (next += 31))
360 if (d <= (next += 30))
367 private static int WeekDay(double t) {
376 private static double Now() {
377 return (double) System.currentTimeMillis();
380 /* Should be possible to determine the need for this dynamically
381 * if we go with the workaround... I'm not using it now, because I
382 * can't think of any clean way to make toLocaleString() and the
383 * time zone (comment) in toString match the generated string
384 * values. Currently it's wrong-but-consistent in all but the
385 * most recent betas of the JRE - seems to work in 1.1.7.
387 private final static boolean TZO_WORKAROUND = false;
388 private static double DaylightSavingTA(double t) {
389 if (!TZO_WORKAROUND) {
390 java.util.Date date = new java.util.Date((long) t);
391 if (thisTimeZone.inDaylightTime(date))
396 /* Use getOffset if inDaylightTime() is broken, because it
397 * seems to work acceptably. We don't switch over to it
398 * entirely, because it requires (expensive) exploded date arguments,
399 * and the api makes it impossible to handle dst
400 * changeovers cleanly.
403 // Hardcode the assumption that the changeover always
404 // happens at 2:00 AM:
405 t += LocalTZA + (HourFromTime(t) <= 2 ? msPerHour : 0);
407 int year = YearFromTime(t);
408 double offset = thisTimeZone.getOffset(year > 0 ? 1 : 0,
413 (int)TimeWithinDay(t));
415 if ((offset - LocalTZA) != 0)
419 // return offset - LocalTZA;
423 private static double LocalTime(double t) {
424 return t + LocalTZA + DaylightSavingTA(t);
427 public static double internalUTC(double t) {
428 return t - LocalTZA - DaylightSavingTA(t - LocalTZA);
431 private static int HourFromTime(double t) {
433 result = java.lang.Math.floor(t / msPerHour) % HoursPerDay;
435 result += HoursPerDay;
439 private static int MinFromTime(double t) {
441 result = java.lang.Math.floor(t / msPerMinute) % MinutesPerHour;
443 result += MinutesPerHour;
447 private static int SecFromTime(double t) {
449 result = java.lang.Math.floor(t / msPerSecond) % SecondsPerMinute;
451 result += SecondsPerMinute;
455 private static int msFromTime(double t) {
457 result = t % msPerSecond;
459 result += msPerSecond;
463 private static double MakeTime(double hour, double min,
464 double sec, double ms)
466 return ((hour * MinutesPerHour + min) * SecondsPerMinute + sec)
470 private static double MakeDay(double year, double month, double date) {
476 year += java.lang.Math.floor(month / 12);
482 leap = (DaysInYear((int) year) == 366);
484 yearday = java.lang.Math.floor(TimeFromYear(year) / msPerDay);
485 monthday = DayFromMonth((int) month, leap);
493 private static double MakeDate(double day, double time) {
494 return day * msPerDay + time;
497 private static double TimeClip(double d) {
499 d == Double.POSITIVE_INFINITY ||
500 d == Double.NEGATIVE_INFINITY ||
501 java.lang.Math.abs(d) > HalfTimeDomain)
506 return java.lang.Math.floor(d + 0.);
508 return java.lang.Math.ceil(d + 0.);
511 /* end of ECMA helper functions */
513 /* find UTC time from given date... no 1900 correction! */
514 public static double date_msecFromDate(double year, double mon,
515 double mday, double hour,
516 double min, double sec,
523 day = MakeDay(year, mon, mday);
524 time = MakeTime(hour, min, sec, msec);
525 result = MakeDate(day, time);
530 private static final int MAXARGS = 7;
531 private static double jsStaticJSFunction_UTC(Object[] args) {
532 double array[] = new double[MAXARGS];
536 for (loop = 0; loop < MAXARGS; loop++) {
537 if (loop < args.length) {
538 d = _toNumber(args[loop]);
539 if (d != d || Double.isInfinite(d)) {
542 array[loop] = toDouble(args[loop]);
548 /* adjust 2-digit years into the 20th century */
549 if (array[0] >= 0 && array[0] <= 99)
552 /* if we got a 0 for 'date' (which is out of range)
553 * pretend it's a 1. (So Date.UTC(1972, 5) works) */
557 d = date_msecFromDate(array[0], array[1], array[2],
558 array[3], array[4], array[5], array[6]);
565 * Use ported code from jsdate.c rather than the locale-specific
566 * date-parsing code from Java, to keep js and rhino consistent.
567 * Is this the right strategy?
570 /* for use by date_parse */
572 /* replace this with byte arrays? Cheaper? */
573 private static String wtb[] = {
575 "monday", "tuesday", "wednesday", "thursday", "friday",
576 "saturday", "sunday",
577 "january", "february", "march", "april", "may", "june",
578 "july", "august", "september", "october", "november", "december",
579 "gmt", "ut", "utc", "est", "edt", "cst", "cdt",
580 "mst", "mdt", "pst", "pdt"
581 /* time zone table needs to be expanded */
584 private static int ttb[] = {
585 -1, -2, 0, 0, 0, 0, 0, 0, 0, /* AM/PM */
586 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
587 10000 + 0, 10000 + 0, 10000 + 0, /* UT/UTC */
588 10000 + 5 * 60, 10000 + 4 * 60, /* EDT */
589 10000 + 6 * 60, 10000 + 5 * 60,
590 10000 + 7 * 60, 10000 + 6 * 60,
591 10000 + 8 * 60, 10000 + 7 * 60
594 /* helper for date_parse */
595 private static boolean date_regionMatches(String s1, int s1off,
596 String s2, int s2off,
599 boolean result = false;
600 /* return true if matches, otherwise, false */
601 int s1len = s1.length();
602 int s2len = s2.length();
604 while (count > 0 && s1off < s1len && s2off < s2len) {
605 if (Character.toLowerCase(s1.charAt(s1off)) !=
606 Character.toLowerCase(s2.charAt(s2off)))
619 private static double date_parseString(String s) {
632 double tzoffset = -1;
635 boolean seenplusminus = false;
637 if (s == null) // ??? Will s be null?
643 if (c <= ' ' || c == ',' || c == '-') {
646 if (c == '-' && '0' <= si && si <= '9') {
652 if (c == '(') { /* comments) */
665 if ('0' <= c && c <= '9') {
667 while (i < limit && '0' <= (c = s.charAt(i)) && c <= '9') {
668 n = n * 10 + c - '0';
672 /* allow TZA before the year, so
673 * 'Wed Nov 05 21:49:11 GMT-0800 1997'
676 /* uses of seenplusminus allow : in TZA, so Java
677 * no-timezone style of GMT+4:30 works
679 if ((prevc == '+' || prevc == '-')/* && year>=0 */) {
680 /* make ':' case below change tzoffset */
681 seenplusminus = true;
685 n = n * 60; /* EG. "GMT-3" */
687 n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */
688 if (prevc == '+') /* plus means east of GMT */
690 if (tzoffset != 0 && tzoffset != -1)
693 } else if (n >= 70 ||
694 (prevc == '/' && mon >= 0 && mday >= 0 && year < 0)) {
697 else if (c <= ' ' || c == ',' || c == '/' || i >= limit)
698 year = n < 100 ? n + 1900 : n;
701 } else if (c == ':') {
708 } else if (c == '/') {
715 } else if (i < limit && c != ',' && c > ' ' && c != '-') {
717 } else if (seenplusminus && n < 60) { /* handle GMT-3:30 */
722 } else if (hour >= 0 && min < 0) {
724 } else if (min >= 0 && sec < 0) {
726 } else if (mday < 0) {
732 } else if (c == '/' || c == ':' || c == '+' || c == '-') {
739 if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')))
745 for (k = wtb.length; --k >= 0;)
746 if (date_regionMatches(wtb[k], 0, s, st, i-st)) {
751 * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as
752 * 12:30, instead of blindly adding 12 if PM.
754 if (hour > 12 || hour < 0) {
757 if (action == -1 && hour == 12) { // am
759 } else if (action == -2 && hour != 12) {// pm
763 } else if (action <= 13) { /* month! */
765 mon = /*byte*/ (action - 2);
770 tzoffset = action - 10000;
780 if (year < 0 || mon < 0 || mday < 0)
788 if (tzoffset == -1) { /* no time zone specified, have to use local */
790 time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
791 return internalUTC(time);
794 msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
795 msec += tzoffset * msPerMinute;
799 private static double jsStaticJSFunction_parse(String s) {
800 return date_parseString(s);
803 private static final int FORMATSPEC_FULL = 0;
804 private static final int FORMATSPEC_DATE = 1;
805 private static final int FORMATSPEC_TIME = 2;
807 private static String date_format(double t, int format) {
811 StringBuffer result = new StringBuffer(60);
812 double local = LocalTime(t);
814 /* offset from GMT in minutes. The offset includes daylight savings,
816 int minutes = (int) java.lang.Math.floor((LocalTZA + DaylightSavingTA(t))
818 /* map 510 minutes to 0830 hours */
819 int offset = (minutes / 60) * 100 + minutes % 60;
821 String dateStr = Integer.toString(DateFromTime(local));
822 String hourStr = Integer.toString(HourFromTime(local));
823 String minStr = Integer.toString(MinFromTime(local));
824 String secStr = Integer.toString(SecFromTime(local));
825 String offsetStr = Integer.toString(offset > 0 ? offset : -offset);
826 int year = YearFromTime(local);
827 String yearStr = Integer.toString(year > 0 ? year : -year);
829 /* Tue Oct 31 09:41:40 GMT-0800 (PST) 2000 */
830 /* Tue Oct 31 2000 */
831 /* 09:41:40 GMT-0800 (PST) */
833 if (format != FORMATSPEC_TIME) {
834 result.append(days[WeekDay(local)]);
836 result.append(months[MonthFromTime(local)]);
837 if (dateStr.length() == 1)
841 result.append(dateStr);
845 if (format != FORMATSPEC_DATE) {
846 if (hourStr.length() == 1)
848 result.append(hourStr);
849 if (minStr.length() == 1)
853 result.append(minStr);
854 if (secStr.length() == 1)
858 result.append(secStr);
860 result.append(" GMT+");
862 result.append(" GMT-");
863 for (int i = offsetStr.length(); i < 4; i++)
865 result.append(offsetStr);
867 if (timeZoneFormatter == null)
868 timeZoneFormatter = new java.text.SimpleDateFormat("zzz");
870 if (timeZoneFormatter != null) {
872 java.util.Date date = new java.util.Date((long) t);
873 result.append(timeZoneFormatter.format(date));
876 if (format != FORMATSPEC_TIME)
880 if (format != FORMATSPEC_TIME) {
883 for (int i = yearStr.length(); i < 4; i++)
885 result.append(yearStr);
888 return result.toString();
891 private static double _toNumber(Object o) { return JS.toDouble(o); }
892 private static double _toNumber(Object[] o, int index) { return JS.toDouble(o[index]); }
893 private static double toDouble(double d) { return d; }
895 public JSDate(Object a0, Object a1, Object a2, Object[] rest, int nargs) {
905 if (a0 instanceof JS)
906 a0 = ((JS) a0).toString();
907 if (!(a0 instanceof String)) {
908 // if it's not a string, use it as a millisecond date
909 date = _toNumber(a0);
911 // it's a string; parse it.
912 String str = (String) a0;
913 date = date_parseString(str);
915 obj.date = TimeClip(date);
919 // multiple arguments; year, month, day etc.
920 double array[] = new double[MAXARGS];
921 array[0] = toDouble(a0);
922 array[1] = toDouble(a1);
923 if (nargs >= 2) array[2] = toDouble(a2);
924 for(int i=0; i<nargs; i++) {
925 double d = _toNumber(i==0?a0:i==1?a1:i==2?a2:rest[i-3]);
926 if (d != d || Double.isInfinite(d)) {
927 obj.date = Double.NaN;
933 /* adjust 2-digit years into the 20th century */
934 if (array[0] >= 0 && array[0] <= 99)
937 /* if we got a 0 for 'date' (which is out of range)
938 * pretend it's a 1 */
942 double day = MakeDay(array[0], array[1], array[2]);
943 double time = MakeTime(array[3], array[4], array[5], array[6]);
944 time = MakeDate(day, time);
945 time = internalUTC(time);
946 obj.date = TimeClip(time);
953 /* constants for toString, toUTCString */
954 private static String NaN_date_str = "Invalid Date";
956 private static String[] days = {
957 "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
960 private static String[] months = {
961 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
962 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
965 private static String toLocale_helper(double t,
966 java.text.DateFormat formatter)
971 java.util.Date tempdate = new java.util.Date((long) t);
972 return formatter.format(tempdate);
975 private static String toLocaleString(double date) {
976 if (localeDateTimeFormatter == null)
977 localeDateTimeFormatter =
978 DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG);
980 return toLocale_helper(date, localeDateTimeFormatter);
983 private static String toLocaleTimeString(double date) {
984 if (localeTimeFormatter == null)
985 localeTimeFormatter = DateFormat.getTimeInstance(DateFormat.LONG);
987 return toLocale_helper(date, localeTimeFormatter);
990 private static String toLocaleDateString(double date) {
991 if (localeDateFormatter == null)
992 localeDateFormatter = DateFormat.getDateInstance(DateFormat.LONG);
994 return toLocale_helper(date, localeDateFormatter);
997 private static String toUTCString(double date) {
998 StringBuffer result = new StringBuffer(60);
1000 String dateStr = Integer.toString(DateFromTime(date));
1001 String hourStr = Integer.toString(HourFromTime(date));
1002 String minStr = Integer.toString(MinFromTime(date));
1003 String secStr = Integer.toString(SecFromTime(date));
1004 int year = YearFromTime(date);
1005 String yearStr = Integer.toString(year > 0 ? year : -year);
1007 result.append(days[WeekDay(date)]);
1008 result.append(", ");
1009 if (dateStr.length() == 1)
1011 result.append(dateStr);
1013 result.append(months[MonthFromTime(date)]);
1015 result.append(" -");
1019 for (i = yearStr.length(); i < 4; i++)
1021 result.append(yearStr);
1023 if (hourStr.length() == 1)
1024 result.append(" 0");
1027 result.append(hourStr);
1028 if (minStr.length() == 1)
1029 result.append(":0");
1032 result.append(minStr);
1033 if (secStr.length() == 1)
1034 result.append(":0");
1037 result.append(secStr);
1039 result.append(" GMT");
1040 return result.toString();
1043 private static double getYear(double date) {
1044 int result = YearFromTime(LocalTime(date));
1049 private static double getTimezoneOffset(double date) {
1050 return (date - LocalTime(date)) / msPerMinute;
1053 public double setTime(double time) {
1054 this.date = TimeClip(time);
1058 private double makeTime(Object[] args, int maxargs, boolean local) {
1060 double conv[] = new double[4];
1061 double hour, min, sec, msec;
1062 double lorutime; /* Local or UTC version of date */
1067 double date = this.date;
1069 /* just return NaN if the date is already NaN */
1073 /* Satisfy the ECMA rule that if a function is called with
1074 * fewer arguments than the specified formal arguments, the
1075 * remaining arguments are set to undefined. Seems like all
1076 * the Date.setWhatever functions in ECMA are only varargs
1077 * beyond the first argument; this should be set to undefined
1078 * if it's not given. This means that "d = new Date();
1079 * d.setMilliseconds()" returns NaN. Blech.
1081 if (args.length == 0)
1082 args = new Object[] { null };
1084 for (i = 0; i < args.length && i < maxargs; i++) {
1085 conv[i] = _toNumber(args[i]);
1087 // limit checks that happen in MakeTime in ECMA.
1088 if (conv[i] != conv[i] || Double.isInfinite(conv[i])) {
1089 this.date = Double.NaN;
1092 conv[i] = toDouble(conv[i]);
1096 lorutime = LocalTime(date);
1101 int stop = args.length;
1103 if (maxargs >= 4 && i < stop)
1106 hour = HourFromTime(lorutime);
1108 if (maxargs >= 3 && i < stop)
1111 min = MinFromTime(lorutime);
1113 if (maxargs >= 2 && i < stop)
1116 sec = SecFromTime(lorutime);
1118 if (maxargs >= 1 && i < stop)
1121 msec = msFromTime(lorutime);
1123 time = MakeTime(hour, min, sec, msec);
1124 result = MakeDate(Day(lorutime), time);
1127 result = internalUTC(result);
1128 date = TimeClip(result);
1134 private double setHours(Object[] args) {
1135 return makeTime(args, 4, true);
1138 private double setUTCHours(Object[] args) {
1139 return makeTime(args, 4, false);
1142 private double makeDate(Object[] args, int maxargs, boolean local) {
1144 double conv[] = new double[3];
1145 double year, month, day;
1146 double lorutime; /* local or UTC version of date */
1149 double date = this.date;
1151 /* See arg padding comment in makeTime.*/
1152 if (args.length == 0)
1153 args = new Object[] { null };
1155 for (i = 0; i < args.length && i < maxargs; i++) {
1156 conv[i] = _toNumber(args[i]);
1158 // limit checks that happen in MakeDate in ECMA.
1159 if (conv[i] != conv[i] || Double.isInfinite(conv[i])) {
1160 this.date = Double.NaN;
1163 conv[i] = toDouble(conv[i]);
1166 /* return NaN if date is NaN and we're not setting the year,
1167 * If we are, use 0 as the time. */
1169 if (args.length < 3) {
1176 lorutime = LocalTime(date);
1182 int stop = args.length;
1184 if (maxargs >= 3 && i < stop)
1187 year = YearFromTime(lorutime);
1189 if (maxargs >= 2 && i < stop)
1192 month = MonthFromTime(lorutime);
1194 if (maxargs >= 1 && i < stop)
1197 day = DateFromTime(lorutime);
1199 day = MakeDay(year, month, day); /* day within year */
1200 result = MakeDate(day, TimeWithinDay(lorutime));
1203 result = internalUTC(result);
1205 date = TimeClip(result);
1211 private double setYear(double year) {
1213 if (year != year || Double.isInfinite(year)) {
1214 this.date = Double.NaN;
1218 if (this.date != this.date) {
1221 this.date = LocalTime(this.date);
1224 if (year >= 0 && year <= 99)
1227 day = MakeDay(year, MonthFromTime(this.date), DateFromTime(this.date));
1228 result = MakeDate(day, TimeWithinDay(this.date));
1229 result = internalUTC(result);
1231 this.date = TimeClip(result);
1236 // private static final int
1237 // Id_toGMTString = Id_toUTCString; // Alias, see Ecma B.2.6
1241 private static java.util.TimeZone thisTimeZone;
1242 private static double LocalTZA;
1243 private static java.text.DateFormat timeZoneFormatter;
1244 private static java.text.DateFormat localeDateTimeFormatter;
1245 private static java.text.DateFormat localeDateFormatter;
1246 private static java.text.DateFormat localeTimeFormatter;
1248 private double date;
1250 public long getRawTime() { return (long)this.date; }