38 |
|
|
39 |
|
|
40 |
package java.util; |
package java.util; |
|
import gnu.classpath.Configuration; |
|
41 |
|
|
|
import java.io.*; |
|
42 |
import java.security.AccessController; |
import java.security.AccessController; |
43 |
import java.security.PrivilegedAction; |
import java.security.PrivilegedAction; |
44 |
import java.text.DateFormatSymbols; |
import java.text.DateFormatSymbols; |
88 |
/** |
/** |
89 |
* Tries to get the default TimeZone for this system if not already |
* Tries to get the default TimeZone for this system if not already |
90 |
* set. It will call <code>getDefaultTimeZone(String)</code> with |
* set. It will call <code>getDefaultTimeZone(String)</code> with |
91 |
* the result of |
* the result of <code>System.getProperty("user.timezone")</code>. |
92 |
* <code>System.getProperty("user.timezone")</code>, |
* If that fails it calls <code>VMTimeZone.getDefaultTimeZoneId()</code>. |
93 |
* <code>System.getenv("TZ")</code>, |
* If that also fails GMT is returned. |
|
* <code>readTimeZoneFile("/etc/timezone")</code>, |
|
|
* <code>readtzFile("/etc/localtime")</code> and |
|
|
* <code>getDefaultTimeZoneId()</code> |
|
|
* till a supported TimeZone is found. |
|
|
* If every method fails GMT is returned. |
|
94 |
*/ |
*/ |
95 |
private static synchronized TimeZone defaultZone() |
private static synchronized TimeZone defaultZone() |
96 |
{ |
{ |
102 |
{ |
{ |
103 |
public Object run() |
public Object run() |
104 |
{ |
{ |
|
if (Configuration.INIT_LOAD_LIBRARY) |
|
|
{ |
|
|
System.loadLibrary("javautil"); |
|
|
} |
|
|
|
|
105 |
TimeZone zone = null; |
TimeZone zone = null; |
106 |
|
|
107 |
// Prefer System property user.timezone. |
// Prefer System property user.timezone. |
109 |
if (tzid != null && !tzid.equals("")) |
if (tzid != null && !tzid.equals("")) |
110 |
zone = getDefaultTimeZone(tzid); |
zone = getDefaultTimeZone(tzid); |
111 |
|
|
112 |
// See if TZ environment variable is set and accessible. |
// Try platfom specific way. |
|
if (zone == null) |
|
|
{ |
|
|
tzid = System.getenv("TZ"); |
|
|
if (tzid != null && !tzid.equals("")) |
|
|
zone = getDefaultTimeZone(tzid); |
|
|
} |
|
|
|
|
|
// Try to parse /etc/timezone. |
|
|
if (zone == null) |
|
|
{ |
|
|
tzid = readTimeZoneFile("/etc/timezone"); |
|
|
if (tzid != null && !tzid.equals("")) |
|
|
zone = getDefaultTimeZone(tzid); |
|
|
} |
|
|
|
|
|
// Try to parse /etc/localtime |
|
|
if (zone == null) |
|
|
{ |
|
|
tzid = readtzFile("/etc/localtime"); |
|
|
if (tzid != null && !tzid.equals("")) |
|
|
zone = getDefaultTimeZone(tzid); |
|
|
} |
|
|
|
|
|
// Try some system specific way |
|
113 |
if (zone == null) |
if (zone == null) |
114 |
{ |
zone = VMTimeZone.getDefaultTimeZoneId(); |
|
tzid = getDefaultTimeZoneId(); |
|
|
if (tzid != null && !tzid.equals("")) |
|
|
zone = getDefaultTimeZone(tzid); |
|
|
} |
|
115 |
|
|
116 |
// Fall back on GMT. |
// Fall back on GMT. |
117 |
if (zone == null) |
if (zone == null) |
803 |
} |
} |
804 |
|
|
805 |
/** |
/** |
|
* This method returns a time zone id string which is in the form |
|
|
* (standard zone name) or (standard zone name)(GMT offset) or |
|
|
* (standard zone name)(GMT offset)(daylight time zone name). The |
|
|
* GMT offset can be in seconds, or where it is evenly divisible by |
|
|
* 3600, then it can be in hours. The offset must be the time to |
|
|
* add to the local time to get GMT. If a offset is given and the |
|
|
* time zone observes daylight saving then the (daylight time zone |
|
|
* name) must also be given (otherwise it is assumed the time zone |
|
|
* does not observe any daylight savings). |
|
|
* <p> |
|
|
* The result of this method is given to getDefaultTimeZone(String) |
|
|
* which tries to map the time zone id to a known TimeZone. See |
|
|
* that method on how the returned String is mapped to a real |
|
|
* TimeZone object. |
|
|
*/ |
|
|
private static native String getDefaultTimeZoneId(); |
|
|
|
|
|
/** |
|
|
* Tries to read the time zone name from a file. Only the first |
|
|
* consecutive letters, digits, slashes, dashes and underscores are |
|
|
* read from the file. If the file cannot be read or an IOException |
|
|
* occurs null is returned. |
|
|
* <p> |
|
|
* The /etc/timezone file is not standard, but a lot of systems have |
|
|
* it. If it exist the first line always contains a string |
|
|
* describing the timezone of the host of domain. Some systems |
|
|
* contain a /etc/TIMEZONE file which is used to set the TZ |
|
|
* environment variable (which is checked before /etc/timezone is |
|
|
* read). |
|
|
*/ |
|
|
private static String readTimeZoneFile(String file) |
|
|
{ |
|
|
File f = new File(file); |
|
|
if (!f.exists()) |
|
|
return null; |
|
|
|
|
|
InputStreamReader isr = null; |
|
|
try |
|
|
{ |
|
|
FileInputStream fis = new FileInputStream(f); |
|
|
BufferedInputStream bis = new BufferedInputStream(fis); |
|
|
isr = new InputStreamReader(bis); |
|
|
|
|
|
StringBuffer sb = new StringBuffer(); |
|
|
int i = isr.read(); |
|
|
while (i != -1) |
|
|
{ |
|
|
char c = (char) i; |
|
|
if (Character.isLetter(c) || Character.isDigit(c) |
|
|
|| c == '/' || c == '-' || c == '_') |
|
|
{ |
|
|
sb.append(c); |
|
|
i = isr.read(); |
|
|
} |
|
|
else |
|
|
break; |
|
|
} |
|
|
return sb.toString(); |
|
|
} |
|
|
catch (IOException ioe) |
|
|
{ |
|
|
// Parse error, not a proper tzfile. |
|
|
return null; |
|
|
} |
|
|
finally |
|
|
{ |
|
|
try |
|
|
{ |
|
|
if (isr != null) |
|
|
isr.close(); |
|
|
} |
|
|
catch (IOException ioe) |
|
|
{ |
|
|
// Error while close, nothing we can do. |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
/** |
|
|
* Tries to read a file as a "standard" tzfile and return a time |
|
|
* zone id string as expected by <code>getDefaultTimeZone(String)</code>. |
|
|
* If the file doesn't exist, an IOException occurs or it isn't a tzfile |
|
|
* that can be parsed null is returned. |
|
|
* <p> |
|
|
* The tzfile structure (as also used by glibc) is described in the Olson |
|
|
* tz database archive as can be found at |
|
|
* <code>ftp://elsie.nci.nih.gov/pub/</code>. |
|
|
* <p> |
|
|
* At least the following platforms support the tzdata file format |
|
|
* and /etc/localtime (GNU/Linux, Darwin, Solaris and FreeBSD at |
|
|
* least). Some systems (like Darwin) don't start the file with the |
|
|
* required magic bytes 'TZif', this implementation can handle |
|
|
* that). |
|
|
*/ |
|
|
private static String readtzFile(String file) |
|
|
{ |
|
|
File f = new File(file); |
|
|
if (!f.exists()) |
|
|
return null; |
|
|
|
|
|
DataInputStream dis = null; |
|
|
try |
|
|
{ |
|
|
FileInputStream fis = new FileInputStream(f); |
|
|
BufferedInputStream bis = new BufferedInputStream(fis); |
|
|
dis = new DataInputStream(bis); |
|
|
|
|
|
// Make sure we are reading a tzfile. |
|
|
byte[] tzif = new byte[4]; |
|
|
dis.readFully(tzif); |
|
|
if (tzif[0] == 'T' && tzif[1] == 'Z' |
|
|
&& tzif[2] == 'i' && tzif[3] == 'f') |
|
|
// Reserved bytes, ttisgmtcnt, ttisstdcnt and leapcnt |
|
|
skipFully(dis, 16 + 3 * 4); |
|
|
else |
|
|
// Darwin has tzdata files that don't start with the TZif marker |
|
|
skipFully(dis, 16 + 3 * 4 - 4); |
|
|
|
|
|
int timecnt = dis.readInt(); |
|
|
int typecnt = dis.readInt(); |
|
|
if (typecnt > 0) |
|
|
{ |
|
|
int charcnt = dis.readInt(); |
|
|
// Transition times plus indexed transition times. |
|
|
skipFully(dis, timecnt * (4 + 1)); |
|
|
|
|
|
// Get last gmt_offset and dst/non-dst time zone names. |
|
|
int abbrind = -1; |
|
|
int dst_abbrind = -1; |
|
|
int gmt_offset = 0; |
|
|
while (typecnt-- > 0) |
|
|
{ |
|
|
// gmtoff |
|
|
int offset = dis.readInt(); |
|
|
int dst = dis.readByte(); |
|
|
if (dst == 0) |
|
|
{ |
|
|
abbrind = dis.readByte(); |
|
|
gmt_offset = offset; |
|
|
} |
|
|
else |
|
|
dst_abbrind = dis.readByte(); |
|
|
} |
|
|
|
|
|
// gmt_offset is the offset you must add to UTC/GMT to |
|
|
// get the local time, we need the offset to add to |
|
|
// the local time to get UTC/GMT. |
|
|
gmt_offset *= -1; |
|
|
|
|
|
// Turn into hours if possible. |
|
|
if (gmt_offset % 3600 == 0) |
|
|
gmt_offset /= 3600; |
|
|
|
|
|
if (abbrind >= 0) |
|
|
{ |
|
|
byte[] names = new byte[charcnt]; |
|
|
dis.readFully(names); |
|
|
int j = abbrind; |
|
|
while (j < charcnt && names[j] != 0) |
|
|
j++; |
|
|
|
|
|
String zonename = new String(names, abbrind, j - abbrind, |
|
|
"ASCII"); |
|
|
|
|
|
String dst_zonename; |
|
|
if (dst_abbrind >= 0) |
|
|
{ |
|
|
j = dst_abbrind; |
|
|
while (j < charcnt && names[j] != 0) |
|
|
j++; |
|
|
dst_zonename = new String(names, dst_abbrind, |
|
|
j - dst_abbrind, "ASCII"); |
|
|
} |
|
|
else |
|
|
dst_zonename = ""; |
|
|
|
|
|
// Only use gmt offset when necessary. |
|
|
// Also special case GMT+/- timezones. |
|
|
String offset_string; |
|
|
if ("".equals(dst_zonename) |
|
|
&& (gmt_offset == 0 |
|
|
|| zonename.startsWith("GMT+") |
|
|
|| zonename.startsWith("GMT-"))) |
|
|
offset_string = ""; |
|
|
else |
|
|
offset_string = Integer.toString(gmt_offset); |
|
|
|
|
|
String id = zonename + offset_string + dst_zonename; |
|
|
|
|
|
return id; |
|
|
} |
|
|
} |
|
|
|
|
|
// Something didn't match while reading the file. |
|
|
return null; |
|
|
} |
|
|
catch (IOException ioe) |
|
|
{ |
|
|
// Parse error, not a proper tzfile. |
|
|
return null; |
|
|
} |
|
|
finally |
|
|
{ |
|
|
try |
|
|
{ |
|
|
if (dis != null) |
|
|
dis.close(); |
|
|
} |
|
|
catch(IOException ioe) |
|
|
{ |
|
|
// Error while close, nothing we can do. |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
/** |
|
|
* Skips the requested number of bytes in the given InputStream. |
|
|
* Throws EOFException if not enough bytes could be skipped. |
|
|
* Negative numbers of bytes to skip are ignored. |
|
|
*/ |
|
|
private static void skipFully(InputStream is, long l) throws IOException |
|
|
{ |
|
|
while (l > 0) |
|
|
{ |
|
|
long k = is.skip(l); |
|
|
if (k <= 0) |
|
|
throw new EOFException(); |
|
|
l -= k; |
|
|
} |
|
|
} |
|
|
|
|
|
/** |
|
806 |
* Maps a time zone name (with optional GMT offset and daylight time |
* Maps a time zone name (with optional GMT offset and daylight time |
807 |
* zone name) to one of the known time zones. This method called |
* zone name) to one of the known time zones. This method called |
808 |
* with the result of <code>System.getProperty("user.timezone")</code> |
* with the result of <code>System.getProperty("user.timezone")</code> |
839 |
* The standard time zone name for The Netherlands is "Europe/Amsterdam", |
* The standard time zone name for The Netherlands is "Europe/Amsterdam", |
840 |
* but can also be given as "CET-1CEST". |
* but can also be given as "CET-1CEST". |
841 |
*/ |
*/ |
842 |
private static TimeZone getDefaultTimeZone(String sysTimeZoneId) |
static TimeZone getDefaultTimeZone(String sysTimeZoneId) |
843 |
{ |
{ |
844 |
// First find start of GMT offset info and any Daylight zone name. |
// First find start of GMT offset info and any Daylight zone name. |
845 |
int startGMToffset = 0; |
int startGMToffset = 0; |
847 |
for (int i = 0; i < sysTimeZoneIdLength && startGMToffset == 0; i++) |
for (int i = 0; i < sysTimeZoneIdLength && startGMToffset == 0; i++) |
848 |
{ |
{ |
849 |
char c = sysTimeZoneId.charAt(i); |
char c = sysTimeZoneId.charAt(i); |
850 |
if (c == '+' || c == '-' || Character.isDigit(c)) |
if (Character.isDigit(c)) |
851 |
|
startGMToffset = i; |
852 |
|
else if ((c == '+' || c == '-') |
853 |
|
&& i + 1 < sysTimeZoneIdLength |
854 |
|
&& Character.isDigit(sysTimeZoneId.charAt(i + 1))) |
855 |
startGMToffset = i; |
startGMToffset = i; |
856 |
} |
} |
857 |
|
|