40 |
package java.util; |
package java.util; |
41 |
import gnu.classpath.Configuration; |
import gnu.classpath.Configuration; |
42 |
|
|
43 |
|
import java.io.*; |
44 |
|
import java.security.AccessController; |
45 |
|
import java.security.PrivilegedAction; |
46 |
import java.text.DateFormatSymbols; |
import java.text.DateFormatSymbols; |
47 |
|
|
48 |
/** |
/** |
86 |
* The default time zone, as returned by getDefault. |
* The default time zone, as returned by getDefault. |
87 |
*/ |
*/ |
88 |
private static TimeZone defaultZone0; |
private static TimeZone defaultZone0; |
89 |
/* initialize this static field lazily to overhead if |
|
90 |
* it is not needed: |
/** |
91 |
|
* Tries to get the default TimeZone for this system if not already |
92 |
|
* set. It will call <code>getDefaultTimeZone(String)</code> with |
93 |
|
* the result of |
94 |
|
* <code>System.getProperty("user.timezone")</code>, |
95 |
|
* <code>System.getenv("TZ")</code>, |
96 |
|
* <code>readTimeZoneFile("/etc/timezone")</code>, |
97 |
|
* <code>readtzFile("/etc/localtime")</code> and |
98 |
|
* <code>getDefaultTimeZoneId()</code> |
99 |
|
* till a supported TimeZone is found. |
100 |
|
* If every method fails GMT is returned. |
101 |
*/ |
*/ |
102 |
private static synchronized TimeZone defaultZone() { |
private static synchronized TimeZone defaultZone() |
103 |
|
{ |
104 |
/* Look up default timezone */ |
/* Look up default timezone */ |
105 |
if (defaultZone0 == null) |
if (defaultZone0 == null) |
106 |
{ |
{ |
107 |
if (Configuration.INIT_LOAD_LIBRARY) |
defaultZone0 = (TimeZone) AccessController.doPrivileged |
108 |
{ |
(new PrivilegedAction() |
109 |
System.loadLibrary("javautil"); |
{ |
110 |
} |
public Object run() |
111 |
String tzid = System.getProperty("user.timezone"); |
{ |
112 |
|
if (Configuration.INIT_LOAD_LIBRARY) |
113 |
if (tzid == null) |
{ |
114 |
tzid = getDefaultTimeZoneId(); |
System.loadLibrary("javautil"); |
115 |
|
} |
116 |
if (tzid == null) |
|
117 |
tzid = "GMT"; |
TimeZone zone = null; |
118 |
|
|
119 |
defaultZone0 = getTimeZone(tzid); |
// Prefer System property user.timezone. |
120 |
|
String tzid = System.getProperty("user.timezone"); |
121 |
|
if (tzid != null && !tzid.equals("")) |
122 |
|
zone = getDefaultTimeZone(tzid); |
123 |
|
|
124 |
|
// See if TZ environment variable is set and accessible. |
125 |
|
if (zone == null) |
126 |
|
{ |
127 |
|
tzid = System.getenv("TZ"); |
128 |
|
if (tzid != null && !tzid.equals("")) |
129 |
|
zone = getDefaultTimeZone(tzid); |
130 |
|
} |
131 |
|
|
132 |
|
// Try to parse /etc/timezone. |
133 |
|
if (zone == null) |
134 |
|
{ |
135 |
|
tzid = readTimeZoneFile("/etc/timezone"); |
136 |
|
if (tzid != null && !tzid.equals("")) |
137 |
|
zone = getDefaultTimeZone(tzid); |
138 |
|
} |
139 |
|
|
140 |
|
// Try to parse /etc/localtime |
141 |
|
if (zone == null) |
142 |
|
{ |
143 |
|
tzid = readtzFile("/etc/localtime"); |
144 |
|
if (tzid != null && !tzid.equals("")) |
145 |
|
zone = getDefaultTimeZone(tzid); |
146 |
|
} |
147 |
|
|
148 |
|
// Try some system specific way |
149 |
|
if (zone == null) |
150 |
|
{ |
151 |
|
tzid = getDefaultTimeZoneId(); |
152 |
|
if (tzid != null && !tzid.equals("")) |
153 |
|
zone = getDefaultTimeZone(tzid); |
154 |
|
} |
155 |
|
|
156 |
|
// Fall back on GMT. |
157 |
|
if (zone == null) |
158 |
|
zone = (TimeZone) timezones().get("GMT"); |
159 |
|
|
160 |
|
return zone; |
161 |
|
} |
162 |
|
}); |
163 |
} |
} |
164 |
|
|
165 |
return defaultZone0; |
return defaultZone0; |
166 |
} |
} |
167 |
|
|
|
|
|
168 |
private static final long serialVersionUID = 3581463369166924961L; |
private static final long serialVersionUID = 3581463369166924961L; |
169 |
|
|
170 |
/** |
/** |
171 |
* Hashtable for timezones by ID. |
* HashMap for timezones by ID. |
172 |
*/ |
*/ |
173 |
private static Hashtable timezones0; |
private static HashMap timezones0; |
174 |
/* initialize this static field lazily to overhead if |
/* initialize this static field lazily to overhead if |
175 |
* it is not needed: |
* it is not needed: |
176 |
*/ |
*/ |
177 |
private static synchronized Hashtable timezones() { |
private static synchronized HashMap timezones() |
178 |
if (timezones0==null) |
{ |
179 |
|
if (timezones0 == null) |
180 |
{ |
{ |
181 |
Hashtable timezones = new Hashtable(); |
HashMap timezones = new HashMap(); |
182 |
timezones0 = timezones; |
timezones0 = timezones; |
183 |
|
|
184 |
TimeZone tz; |
TimeZone tz; |
842 |
return timezones0; |
return timezones0; |
843 |
} |
} |
844 |
|
|
845 |
|
/** |
846 |
/* This method returns us a time zone id string which is in the |
* This method returns a time zone id string which is in the form |
847 |
form <standard zone name><GMT offset><daylight time zone name>. |
* (standard zone name) or (standard zone name)(GMT offset) or |
848 |
The GMT offset is in seconds, except where it is evenly divisible |
* (standard zone name)(GMT offset)(daylight time zone name). The |
849 |
by 3600, then it is in hours. If the zone does not observe |
* GMT offset can be in seconds, or where it is evenly divisible by |
850 |
daylight time, then the daylight zone name is omitted. Examples: |
* 3600, then it can be in hours. The offset must be the time to |
851 |
in Chicago, the timezone would be CST6CDT. In Indianapolis |
* add to the local time to get GMT. If a offset is given and the |
852 |
(which does not have Daylight Savings Time) the string would |
* time zone observes daylight saving then the (daylight time zone |
853 |
be EST5 |
* name) must also be given (otherwise it is assumed the time zone |
854 |
|
* does not observe any daylight savings). |
855 |
|
* <p> |
856 |
|
* The result of this method is given to getDefaultTimeZone(String) |
857 |
|
* which tries to map the time zone id to a known TimeZone. See |
858 |
|
* that method on how the returned String is mapped to a real |
859 |
|
* TimeZone object. |
860 |
*/ |
*/ |
861 |
private static native String getDefaultTimeZoneId(); |
private static native String getDefaultTimeZoneId(); |
862 |
|
|
863 |
/** |
/** |
864 |
|
* Tries to read the time zone name from a file. Only the first |
865 |
|
* consecutive letters, digits, slashes, dashes and underscores are |
866 |
|
* read from the file. If the file cannot be read or an IOException |
867 |
|
* occurs null is returned. |
868 |
|
* <p> |
869 |
|
* The /etc/timezone file is not standard, but a lot of systems have |
870 |
|
* it. If it exist the first line always contains a string |
871 |
|
* describing the timezone of the host of domain. Some systems |
872 |
|
* contain a /etc/TIMEZONE file which is used to set the TZ |
873 |
|
* environment variable (which is checked before /etc/timezone is |
874 |
|
* read). |
875 |
|
*/ |
876 |
|
private static String readTimeZoneFile(String file) |
877 |
|
{ |
878 |
|
File f = new File(file); |
879 |
|
if (!f.exists()) |
880 |
|
return null; |
881 |
|
|
882 |
|
InputStreamReader isr = null; |
883 |
|
try |
884 |
|
{ |
885 |
|
FileInputStream fis = new FileInputStream(f); |
886 |
|
BufferedInputStream bis = new BufferedInputStream(fis); |
887 |
|
isr = new InputStreamReader(bis); |
888 |
|
|
889 |
|
StringBuffer sb = new StringBuffer(); |
890 |
|
int i = isr.read(); |
891 |
|
while (i != -1) |
892 |
|
{ |
893 |
|
char c = (char) i; |
894 |
|
if (Character.isLetter(c) || Character.isDigit(c) |
895 |
|
|| c == '/' || c == '-' || c == '_') |
896 |
|
{ |
897 |
|
sb.append(c); |
898 |
|
i = isr.read(); |
899 |
|
} |
900 |
|
else |
901 |
|
break; |
902 |
|
} |
903 |
|
return sb.toString(); |
904 |
|
} |
905 |
|
catch (IOException ioe) |
906 |
|
{ |
907 |
|
// Parse error, not a proper tzfile. |
908 |
|
return null; |
909 |
|
} |
910 |
|
finally |
911 |
|
{ |
912 |
|
try |
913 |
|
{ |
914 |
|
if (isr != null) |
915 |
|
isr.close(); |
916 |
|
} |
917 |
|
catch (IOException ioe) |
918 |
|
{ |
919 |
|
// Error while close, nothing we can do. |
920 |
|
} |
921 |
|
} |
922 |
|
} |
923 |
|
|
924 |
|
/** |
925 |
|
* Tries to read a file as a "standard" tzfile and return a time |
926 |
|
* zone id string as expected by <code>getDefaultTimeZone(String)</code>. |
927 |
|
* If the file doesn't exist, an IOException occurs or it isn't a tzfile |
928 |
|
* that can be parsed null is returned. |
929 |
|
* <p> |
930 |
|
* The tzfile structure (as also used by glibc) is described in the Olson |
931 |
|
* tz database archive as can be found at |
932 |
|
* <code>ftp://elsie.nci.nih.gov/pub/</code>. |
933 |
|
* <p> |
934 |
|
* At least the following platforms support the tzdata file format |
935 |
|
* and /etc/localtime (GNU/Linux, Darwin, Solaris and FreeBSD at |
936 |
|
* least). Some systems (like Darwin) don't start the file with the |
937 |
|
* required magic bytes 'TZif', this implementation can handle |
938 |
|
* that). |
939 |
|
*/ |
940 |
|
private static String readtzFile(String file) |
941 |
|
{ |
942 |
|
File f = new File(file); |
943 |
|
if (!f.exists()) |
944 |
|
return null; |
945 |
|
|
946 |
|
DataInputStream dis = null; |
947 |
|
try |
948 |
|
{ |
949 |
|
FileInputStream fis = new FileInputStream(f); |
950 |
|
BufferedInputStream bis = new BufferedInputStream(fis); |
951 |
|
dis = new DataInputStream(bis); |
952 |
|
|
953 |
|
// Make sure we are reading a tzfile. |
954 |
|
byte[] tzif = new byte[4]; |
955 |
|
dis.readFully(tzif); |
956 |
|
if (tzif[0] == 'T' && tzif[1] == 'Z' |
957 |
|
&& tzif[2] == 'i' && tzif[3] == 'f') |
958 |
|
// Reserved bytes, ttisgmtcnt, ttisstdcnt and leapcnt |
959 |
|
skipFully(dis, 16 + 3 * 4); |
960 |
|
else |
961 |
|
// Darwin has tzdata files that don't start with the TZif marker |
962 |
|
skipFully(dis, 16 + 3 * 4 - 4); |
963 |
|
|
964 |
|
int timecnt = dis.readInt(); |
965 |
|
int typecnt = dis.readInt(); |
966 |
|
if (typecnt > 0) |
967 |
|
{ |
968 |
|
int charcnt = dis.readInt(); |
969 |
|
// Transition times plus indexed transition times. |
970 |
|
skipFully(dis, timecnt * (4 + 1)); |
971 |
|
|
972 |
|
// Get last gmt_offset and dst/non-dst time zone names. |
973 |
|
int abbrind = -1; |
974 |
|
int dst_abbrind = -1; |
975 |
|
int gmt_offset = 0; |
976 |
|
while (typecnt-- > 0) |
977 |
|
{ |
978 |
|
// gmtoff |
979 |
|
int offset = dis.readInt(); |
980 |
|
int dst = dis.readByte(); |
981 |
|
if (dst == 0) |
982 |
|
{ |
983 |
|
abbrind = dis.readByte(); |
984 |
|
gmt_offset = offset; |
985 |
|
} |
986 |
|
else |
987 |
|
dst_abbrind = dis.readByte(); |
988 |
|
} |
989 |
|
|
990 |
|
// gmt_offset is the offset you must add to UTC/GMT to |
991 |
|
// get the local time, we need the offset to add to |
992 |
|
// the local time to get UTC/GMT. |
993 |
|
gmt_offset *= -1; |
994 |
|
|
995 |
|
// Turn into hours if possible. |
996 |
|
if (gmt_offset % 3600 == 0) |
997 |
|
gmt_offset /= 3600; |
998 |
|
|
999 |
|
if (abbrind >= 0) |
1000 |
|
{ |
1001 |
|
byte[] names = new byte[charcnt]; |
1002 |
|
dis.readFully(names); |
1003 |
|
int j = abbrind; |
1004 |
|
while (j < charcnt && names[j] != 0) |
1005 |
|
j++; |
1006 |
|
|
1007 |
|
String zonename = new String(names, abbrind, j - abbrind, |
1008 |
|
"ASCII"); |
1009 |
|
|
1010 |
|
String dst_zonename; |
1011 |
|
if (dst_abbrind >= 0) |
1012 |
|
{ |
1013 |
|
j = dst_abbrind; |
1014 |
|
while (j < charcnt && names[j] != 0) |
1015 |
|
j++; |
1016 |
|
dst_zonename = new String(names, dst_abbrind, |
1017 |
|
j - dst_abbrind, "ASCII"); |
1018 |
|
} |
1019 |
|
else |
1020 |
|
dst_zonename = ""; |
1021 |
|
|
1022 |
|
// Only use gmt offset when necessary. |
1023 |
|
// Also special case GMT+/- timezones. |
1024 |
|
String offset_string; |
1025 |
|
if ("".equals(dst_zonename) |
1026 |
|
&& (gmt_offset == 0 |
1027 |
|
|| zonename.startsWith("GMT+") |
1028 |
|
|| zonename.startsWith("GMT-"))) |
1029 |
|
offset_string = ""; |
1030 |
|
else |
1031 |
|
offset_string = Integer.toString(gmt_offset); |
1032 |
|
|
1033 |
|
String id = zonename + offset_string + dst_zonename; |
1034 |
|
|
1035 |
|
return id; |
1036 |
|
} |
1037 |
|
} |
1038 |
|
|
1039 |
|
// Something didn't match while reading the file. |
1040 |
|
return null; |
1041 |
|
} |
1042 |
|
catch (IOException ioe) |
1043 |
|
{ |
1044 |
|
// Parse error, not a proper tzfile. |
1045 |
|
return null; |
1046 |
|
} |
1047 |
|
finally |
1048 |
|
{ |
1049 |
|
try |
1050 |
|
{ |
1051 |
|
if (dis != null) |
1052 |
|
dis.close(); |
1053 |
|
} |
1054 |
|
catch(IOException ioe) |
1055 |
|
{ |
1056 |
|
// Error while close, nothing we can do. |
1057 |
|
} |
1058 |
|
} |
1059 |
|
} |
1060 |
|
|
1061 |
|
/** |
1062 |
|
* Skips the requested number of bytes in the given InputStream. |
1063 |
|
* Throws EOFException if not enough bytes could be skipped. |
1064 |
|
* Negative numbers of bytes to skip are ignored. |
1065 |
|
*/ |
1066 |
|
private static void skipFully(InputStream is, long l) throws IOException |
1067 |
|
{ |
1068 |
|
while (l > 0) |
1069 |
|
{ |
1070 |
|
long k = is.skip(l); |
1071 |
|
if (k <= 0) |
1072 |
|
throw new EOFException(); |
1073 |
|
l -= k; |
1074 |
|
} |
1075 |
|
} |
1076 |
|
|
1077 |
|
/** |
1078 |
|
* Maps a time zone name (with optional GMT offset and daylight time |
1079 |
|
* zone name) to one of the known time zones. This method called |
1080 |
|
* with the result of <code>System.getProperty("user.timezone")</code> |
1081 |
|
* or <code>getDefaultTimeZoneId()</code>. Note that giving one of |
1082 |
|
* the standard tz data names from ftp://elsie.nci.nih.gov/pub/ is |
1083 |
|
* preferred. The time zone name can be given as follows: |
1084 |
|
* <code>(standard zone name)[(GMT offset)[(daylight time zone name)]]</code> |
1085 |
|
* <p> |
1086 |
|
* If only a (standard zone name) is given (no numbers in the |
1087 |
|
* String) then it gets mapped directly to the TimeZone with that |
1088 |
|
* name, if that fails null is returned. |
1089 |
|
* <p> |
1090 |
|
* A GMT offset is the offset to add to the local time to get GMT. |
1091 |
|
* If a (GMT offset) is included (either in seconds or hours) then |
1092 |
|
* an attempt is made to find a TimeZone name matching both the name |
1093 |
|
* and the offset (that doesn't observe daylight time, if the |
1094 |
|
* timezone observes daylight time then you must include a daylight |
1095 |
|
* time zone name after the offset), if that fails then a TimeZone |
1096 |
|
* with the given GMT offset is returned (whether or not the |
1097 |
|
* TimeZone observes daylight time is ignored), if that also fails |
1098 |
|
* the GMT TimeZone is returned. |
1099 |
|
* <p> |
1100 |
|
* If the String ends with (GMT offset)(daylight time zone name) |
1101 |
|
* then an attempt is made to find a TimeZone with the given name and |
1102 |
|
* GMT offset that also observes (the daylight time zone name is not |
1103 |
|
* currently used in any other way), if that fails a TimeZone with |
1104 |
|
* the given GMT offset that observes daylight time is returned, if |
1105 |
|
* that also fails the GMT TimeZone is returned. |
1106 |
|
* <p> |
1107 |
|
* Examples: In Chicago, the time zone id could be "CST6CDT", but |
1108 |
|
* the preferred name would be "America/Chicago". In Indianapolis |
1109 |
|
* (which does not have Daylight Savings Time) the string could be |
1110 |
|
* "EST5", but the preferred name would be "America/Indianapolis". |
1111 |
|
* The standard time zone name for The Netherlands is "Europe/Amsterdam", |
1112 |
|
* but can also be given as "CET-1CEST". |
1113 |
|
*/ |
1114 |
|
private static TimeZone getDefaultTimeZone(String sysTimeZoneId) |
1115 |
|
{ |
1116 |
|
// First find start of GMT offset info and any Daylight zone name. |
1117 |
|
int startGMToffset = 0; |
1118 |
|
int sysTimeZoneIdLength = sysTimeZoneId.length(); |
1119 |
|
for (int i = 0; i < sysTimeZoneIdLength && startGMToffset == 0; i++) |
1120 |
|
{ |
1121 |
|
char c = sysTimeZoneId.charAt(i); |
1122 |
|
if (c == '+' || c == '-' || Character.isDigit(c)) |
1123 |
|
startGMToffset = i; |
1124 |
|
} |
1125 |
|
|
1126 |
|
String tzBasename; |
1127 |
|
if (startGMToffset == 0) |
1128 |
|
tzBasename = sysTimeZoneId; |
1129 |
|
else |
1130 |
|
tzBasename = sysTimeZoneId.substring (0, startGMToffset); |
1131 |
|
|
1132 |
|
int startDaylightZoneName = 0; |
1133 |
|
for (int i = sysTimeZoneIdLength - 1; |
1134 |
|
i >= 0 && !Character.isDigit(sysTimeZoneId.charAt(i)); --i) |
1135 |
|
startDaylightZoneName = i; |
1136 |
|
|
1137 |
|
boolean useDaylightTime = startDaylightZoneName > 0; |
1138 |
|
|
1139 |
|
// Integer.parseInt() doesn't handle leading +. |
1140 |
|
if (sysTimeZoneId.charAt(startGMToffset) == '+') |
1141 |
|
startGMToffset++; |
1142 |
|
|
1143 |
|
int gmtOffset = 0; |
1144 |
|
if (startGMToffset > 0) |
1145 |
|
{ |
1146 |
|
gmtOffset = Integer.parseInt |
1147 |
|
(startDaylightZoneName == 0 |
1148 |
|
? sysTimeZoneId.substring(startGMToffset) |
1149 |
|
: sysTimeZoneId.substring(startGMToffset, |
1150 |
|
startDaylightZoneName)); |
1151 |
|
|
1152 |
|
// Offset could be in hours or seconds. Convert to millis. |
1153 |
|
// The offset is given as the time to add to local time to get GMT |
1154 |
|
// we need the time to add to GMT to get localtime. |
1155 |
|
if (gmtOffset < 24) |
1156 |
|
gmtOffset *= 60 * 60; |
1157 |
|
gmtOffset *= -1000; |
1158 |
|
} |
1159 |
|
|
1160 |
|
// Try to be optimistic and get the timezone that matches the base name. |
1161 |
|
// If we only have the base name then just accept this timezone. |
1162 |
|
// Otherwise check the gmtOffset and day light attributes. |
1163 |
|
TimeZone tz = (TimeZone) timezones().get(tzBasename); |
1164 |
|
if (tz != null |
1165 |
|
&& (tzBasename == sysTimeZoneId |
1166 |
|
|| (tz.getRawOffset() == gmtOffset |
1167 |
|
&& tz.useDaylightTime() == useDaylightTime))) |
1168 |
|
return tz; |
1169 |
|
|
1170 |
|
// Maybe there is one with the daylight zone name? |
1171 |
|
if (useDaylightTime) |
1172 |
|
{ |
1173 |
|
String daylightZoneName; |
1174 |
|
daylightZoneName = sysTimeZoneId.substring(startDaylightZoneName); |
1175 |
|
if (!daylightZoneName.equals(tzBasename)) |
1176 |
|
{ |
1177 |
|
tz = (TimeZone) timezones().get(tzBasename); |
1178 |
|
if (tz != null |
1179 |
|
&& tz.getRawOffset() == gmtOffset |
1180 |
|
&& tz.useDaylightTime()) |
1181 |
|
return tz; |
1182 |
|
} |
1183 |
|
} |
1184 |
|
|
1185 |
|
// If no match, see if a valid timezone has similar attributes as this |
1186 |
|
// and then use it instead. We take the first one that looks OKish. |
1187 |
|
if (startGMToffset > 0) |
1188 |
|
{ |
1189 |
|
String[] ids = getAvailableIDs(gmtOffset); |
1190 |
|
for (int i = 0; i < ids.length; i++) |
1191 |
|
{ |
1192 |
|
tz = (TimeZone) timezones().get(ids[i]); |
1193 |
|
if (tz.useDaylightTime() == useDaylightTime) |
1194 |
|
return tz; |
1195 |
|
} |
1196 |
|
} |
1197 |
|
|
1198 |
|
return null; |
1199 |
|
} |
1200 |
|
|
1201 |
|
/** |
1202 |
* Gets the time zone offset, for current date, modified in case of |
* Gets the time zone offset, for current date, modified in case of |
1203 |
* daylight savings. This is the offset to add to UTC to get the local |
* daylight savings. This is the offset to add to UTC to get the local |
1204 |
* time. |
* time. |
1542 |
/** |
/** |
1543 |
* Returns the time zone under which the host is running. This |
* Returns the time zone under which the host is running. This |
1544 |
* can be changed with setDefault. |
* can be changed with setDefault. |
1545 |
* @return the time zone for this host. |
* |
1546 |
|
* @return A clone of the current default time zone for this host. |
1547 |
* @see #setDefault |
* @see #setDefault |
1548 |
*/ |
*/ |
1549 |
public static TimeZone getDefault() |
public static TimeZone getDefault() |
1550 |
{ |
{ |
1551 |
return defaultZone(); |
return (TimeZone) defaultZone().clone(); |
1552 |
} |
} |
1553 |
|
|
1554 |
public static void setDefault(TimeZone zone) |
public static void setDefault(TimeZone zone) |
1555 |
{ |
{ |
1556 |
|
// Hmmmm. No Security checks? |
1557 |
defaultZone0 = zone; |
defaultZone0 = zone; |
1558 |
} |
} |
1559 |
|
|