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 String coerceToString() { return date_format(date, FORMATSPEC_FULL); }
60 public JS callMethod(JS method, JS a0, JS a1, JS a2, JS[] rest, int nargs) throws JSExn {
63 //#switch(JS.toString(method))
64 case "toString": return JS.S(date_format(date, FORMATSPEC_FULL));
65 case "toTimeString": return JS.S(date_format(date, FORMATSPEC_TIME));
66 case "toDateString": return JS.S(date_format(date, FORMATSPEC_DATE));
67 case "toLocaleString": return JS.S(toLocaleString(date));
68 case "toLocaleTimeString": return JS.S(toLocaleTimeString(date));
69 case "toLocaleDateString": return JS.S(toLocaleDateString(date));
70 case "toUTCString": return JS.S(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);
95 //#switch(JS.toString(method))
96 case "setTime": return N(this.setTime(toDouble(a0)));
97 case "setYear": return N(this.setYear(toDouble(a0)));
102 JS[] args = new JS[nargs];
103 for(int i=0; i<nargs; i++) args[i] = i==0 ? a0 : i==1 ? a1 : i==2 ? a2 : rest[i-3];
104 //#switch(JS.toString(method))
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 JS get(JS key) throws JSExn {
126 //#switch(JS.toString(key))
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(JS[] args) throws JSExn {
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(JS o) throws JSExn { return JS.toDouble(o); }
892 private static double _toNumber(JS[] o, int index) throws JSExn { return JS.toDouble(o[index]); }
893 private static double toDouble(double d) { return d; }
895 public JSDate(JS a0, JS a1, JS a2, JS[] rest, int nargs) throws JSExn {
906 date = date_parseString(JS.toString(a0));
908 date = _toNumber(a0);
909 obj.date = TimeClip(date);
913 // multiple arguments; year, month, day etc.
914 double array[] = new double[MAXARGS];
915 array[0] = toDouble(a0);
916 array[1] = toDouble(a1);
917 if (nargs >= 2) array[2] = toDouble(a2);
918 for(int i=0; i<nargs; i++) {
919 double d = _toNumber(i==0?a0:i==1?a1:i==2?a2:rest[i-3]);
920 if (d != d || Double.isInfinite(d)) {
921 obj.date = Double.NaN;
927 /* adjust 2-digit years into the 20th century */
928 if (array[0] >= 0 && array[0] <= 99)
931 /* if we got a 0 for 'date' (which is out of range)
932 * pretend it's a 1 */
936 double day = MakeDay(array[0], array[1], array[2]);
937 double time = MakeTime(array[3], array[4], array[5], array[6]);
938 time = MakeDate(day, time);
939 time = internalUTC(time);
940 obj.date = TimeClip(time);
947 /* constants for toString, toUTCString */
948 private static String NaN_date_str = "Invalid Date";
950 private static String[] days = {
951 "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
954 private static String[] months = {
955 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
956 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
959 private static String toLocale_helper(double t,
960 java.text.DateFormat formatter)
965 java.util.Date tempdate = new java.util.Date((long) t);
966 return formatter.format(tempdate);
969 private static String toLocaleString(double date) {
970 if (localeDateTimeFormatter == null)
971 localeDateTimeFormatter =
972 DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG);
974 return toLocale_helper(date, localeDateTimeFormatter);
977 private static String toLocaleTimeString(double date) {
978 if (localeTimeFormatter == null)
979 localeTimeFormatter = DateFormat.getTimeInstance(DateFormat.LONG);
981 return toLocale_helper(date, localeTimeFormatter);
984 private static String toLocaleDateString(double date) {
985 if (localeDateFormatter == null)
986 localeDateFormatter = DateFormat.getDateInstance(DateFormat.LONG);
988 return toLocale_helper(date, localeDateFormatter);
991 private static String toUTCString(double date) {
992 StringBuffer result = new StringBuffer(60);
994 String dateStr = Integer.toString(DateFromTime(date));
995 String hourStr = Integer.toString(HourFromTime(date));
996 String minStr = Integer.toString(MinFromTime(date));
997 String secStr = Integer.toString(SecFromTime(date));
998 int year = YearFromTime(date);
999 String yearStr = Integer.toString(year > 0 ? year : -year);
1001 result.append(days[WeekDay(date)]);
1002 result.append(", ");
1003 if (dateStr.length() == 1)
1005 result.append(dateStr);
1007 result.append(months[MonthFromTime(date)]);
1009 result.append(" -");
1013 for (i = yearStr.length(); i < 4; i++)
1015 result.append(yearStr);
1017 if (hourStr.length() == 1)
1018 result.append(" 0");
1021 result.append(hourStr);
1022 if (minStr.length() == 1)
1023 result.append(":0");
1026 result.append(minStr);
1027 if (secStr.length() == 1)
1028 result.append(":0");
1031 result.append(secStr);
1033 result.append(" GMT");
1034 return result.toString();
1037 private static double getYear(double date) {
1038 int result = YearFromTime(LocalTime(date));
1043 private static double getTimezoneOffset(double date) {
1044 return (date - LocalTime(date)) / msPerMinute;
1047 public double setTime(double time) {
1048 this.date = TimeClip(time);
1052 private double makeTime(JS[] args, int maxargs, boolean local) throws JSExn {
1054 double conv[] = new double[4];
1055 double hour, min, sec, msec;
1056 double lorutime; /* Local or UTC version of date */
1061 double date = this.date;
1063 /* just return NaN if the date is already NaN */
1067 /* Satisfy the ECMA rule that if a function is called with
1068 * fewer arguments than the specified formal arguments, the
1069 * remaining arguments are set to undefined. Seems like all
1070 * the Date.setWhatever functions in ECMA are only varargs
1071 * beyond the first argument; this should be set to undefined
1072 * if it's not given. This means that "d = new Date();
1073 * d.setMilliseconds()" returns NaN. Blech.
1075 if (args.length == 0)
1076 args = new JS[] { null };
1078 for (i = 0; i < args.length && i < maxargs; i++) {
1079 conv[i] = _toNumber(args[i]);
1081 // limit checks that happen in MakeTime in ECMA.
1082 if (conv[i] != conv[i] || Double.isInfinite(conv[i])) {
1083 this.date = Double.NaN;
1086 conv[i] = toDouble(conv[i]);
1090 lorutime = LocalTime(date);
1095 int stop = args.length;
1097 if (maxargs >= 4 && i < stop)
1100 hour = HourFromTime(lorutime);
1102 if (maxargs >= 3 && i < stop)
1105 min = MinFromTime(lorutime);
1107 if (maxargs >= 2 && i < stop)
1110 sec = SecFromTime(lorutime);
1112 if (maxargs >= 1 && i < stop)
1115 msec = msFromTime(lorutime);
1117 time = MakeTime(hour, min, sec, msec);
1118 result = MakeDate(Day(lorutime), time);
1121 result = internalUTC(result);
1122 date = TimeClip(result);
1128 private double setHours(JS[] args) throws JSExn {
1129 return makeTime(args, 4, true);
1132 private double setUTCHours(JS[] args) throws JSExn {
1133 return makeTime(args, 4, false);
1136 private double makeDate(JS[] args, int maxargs, boolean local) throws JSExn {
1138 double conv[] = new double[3];
1139 double year, month, day;
1140 double lorutime; /* local or UTC version of date */
1143 double date = this.date;
1145 /* See arg padding comment in makeTime.*/
1146 if (args.length == 0)
1147 args = new JS[] { null };
1149 for (i = 0; i < args.length && i < maxargs; i++) {
1150 conv[i] = _toNumber(args[i]);
1152 // limit checks that happen in MakeDate in ECMA.
1153 if (conv[i] != conv[i] || Double.isInfinite(conv[i])) {
1154 this.date = Double.NaN;
1157 conv[i] = toDouble(conv[i]);
1160 /* return NaN if date is NaN and we're not setting the year,
1161 * If we are, use 0 as the time. */
1163 if (args.length < 3) {
1170 lorutime = LocalTime(date);
1176 int stop = args.length;
1178 if (maxargs >= 3 && i < stop)
1181 year = YearFromTime(lorutime);
1183 if (maxargs >= 2 && i < stop)
1186 month = MonthFromTime(lorutime);
1188 if (maxargs >= 1 && i < stop)
1191 day = DateFromTime(lorutime);
1193 day = MakeDay(year, month, day); /* day within year */
1194 result = MakeDate(day, TimeWithinDay(lorutime));
1197 result = internalUTC(result);
1199 date = TimeClip(result);
1205 private double setYear(double year) {
1207 if (year != year || Double.isInfinite(year)) {
1208 this.date = Double.NaN;
1212 if (this.date != this.date) {
1215 this.date = LocalTime(this.date);
1218 if (year >= 0 && year <= 99)
1221 day = MakeDay(year, MonthFromTime(this.date), DateFromTime(this.date));
1222 result = MakeDate(day, TimeWithinDay(this.date));
1223 result = internalUTC(result);
1225 this.date = TimeClip(result);
1230 // private static final int
1231 // Id_toGMTString = Id_toUTCString; // Alias, see Ecma B.2.6
1235 private static java.util.TimeZone thisTimeZone;
1236 private static double LocalTZA;
1237 private static java.text.DateFormat timeZoneFormatter;
1238 private static java.text.DateFormat localeDateTimeFormatter;
1239 private static java.text.DateFormat localeDateFormatter;
1240 private static java.text.DateFormat localeTimeFormatter;
1242 private double date;
1244 public long getRawTime() { return (long)this.date; }