summaryrefslogtreecommitdiff
path: root/vm
diff options
context:
space:
mode:
authorGary Benson <gbenson@redhat.com>2007-02-23 15:50:03 +0000
committerGary Benson <gbenson@redhat.com>2007-02-23 15:50:03 +0000
commit2f8ad2e27e4eb2c556af748828ce474aafbf0473 (patch)
treea5bf20256d0983004c4469eae6ba971fb9880048 /vm
parentde6fca4801b251c2ffe063bd710dbd1d3275aed4 (diff)
downloadclasspath-2f8ad2e27e4eb2c556af748828ce474aafbf0473.tar.gz
2007-02-23 Gary Benson <gbenson@redhat.com>
Jakub Jelinek <jakub@redhat.com> PR libgcj/17002 PR classpath/28550 * java/util/Date.java (parse): Properly parse 09:01:02 as hours/minutes/seconds, not as hours/minutes/year. * java/util/SimpleTimeZone.java (SimpleTimeZone): Simplify {start,end}TimeMode constructor by calling shorter constructor, set {start,end}TimeMode fields after it returns. (setStartRule): Don't adjust startTime into WALL_TIME. Set startTimeMode to WALL_TIME. (endStartRule): Similarly. (getOffset): Handle properly millis + dstOffset overflowing into the next day. Adjust startTime resp. endTime based on startTimeMode resp. endTimeMode. * java/util/TimeZone.java (zoneinfo_dir, availableIDs, aliases0): New static fields. (timezones): Remove synchronized keyword. Set zoneinfo_dir. If non-null, set up aliases0 and don't put anything into timezones0. (defaultZone): Call getTimeZone instead of timezones().get. (getDefaultTimeZone): Fix parsing of EST5 or EST5EDT6. Use getTimeZoneInternal instead of timezones().get. (parseTime): Parse correctly hour:minute. (getTimeZoneInternal): New private method. (getTimeZone): Do the custom ID checking first, canonicalize ID for custom IDs as required by documentation. Call getTimeZoneInternal to handle the rest. (getAvailableIDs(int)): Add locking. Handle zoneinfo_dir != null. (getAvailableIDs(File,String,ArrayList)): New private method. (getAvailableIDs()): Add locking. Handle zoneinfo_dir != null. * vm/reference/java/util/VMTimeZone.java (getDefaultTimeZoneId): To read /etc/localtime, use ZoneInfo.readTZFile instead of VMTimeZone.readtzFile. Get better timezone name for /etc/localtime, either if it is a symlink or through /etc/sysconfig/clock. (readSysconfigClockFile): New static method. (readtzFile): Removed. * gnu/java/util/ZoneInfo.java: New file. * java/lang/System.java: Add gnu.java.util.zoneinfo.dir to comments. * NEWS: Documented TimeZone interface changes.
Diffstat (limited to 'vm')
-rw-r--r--vm/reference/java/util/VMTimeZone.java560
1 files changed, 84 insertions, 476 deletions
diff --git a/vm/reference/java/util/VMTimeZone.java b/vm/reference/java/util/VMTimeZone.java
index 27bab9391..992ecaf28 100644
--- a/vm/reference/java/util/VMTimeZone.java
+++ b/vm/reference/java/util/VMTimeZone.java
@@ -40,9 +40,9 @@ exception statement from your version. */
package java.util;
import gnu.classpath.Configuration;
+import gnu.classpath.SystemProperties;
+import gnu.java.util.ZoneInfo;
import java.util.TimeZone;
-import java.util.Calendar;
-import java.util.GregorianCalendar;
import java.io.*;
@@ -78,9 +78,10 @@ final class VMTimeZone
* The reference implementation which is made for GNU/Posix like
* systems calls <code>System.getenv("TZ")</code>,
* <code>readTimeZoneFile("/etc/timezone")</code>,
- * <code>readtzFile("/etc/localtime")</code> and finally
- * <code>getSystemTimeZoneId()</code> till a supported TimeZone is
- * found through <code>TimeZone.getDefaultTimeZone(String)</code>.
+ * <code>ZoneInfo.readTZFile((String)null, "/etc/localtime")</code>
+ * and finally <code>getSystemTimeZoneId()</code> till a supported
+ * TimeZone is found through
+ * <code>TimeZone.getDefaultTimeZone(String)</code>.
* If every method fails <code>null</code> is returned (which means
* the TimeZone code will fall back on GMT as default time zone).
* <p>
@@ -111,9 +112,51 @@ final class VMTimeZone
// Try to parse /etc/localtime
if (zone == null)
{
- tzid = readtzFile("/etc/localtime");
- if (tzid != null && !tzid.equals(""))
- zone = TimeZone.getDefaultTimeZone(tzid);
+ zone = ZoneInfo.readTZFile((String) null, "/etc/localtime");
+ if (zone != null)
+ {
+ // Try to find a more suitable ID for the /etc/localtime
+ // timezone.
+ // Sometimes /etc/localtime is a symlink to some
+ // /usr/share/zoneinfo/ file.
+ String id = null;
+ try
+ {
+ id = new File("/etc/localtime").getCanonicalPath();
+ if (id != null)
+ {
+ String zoneinfo_dir
+ = SystemProperties.getProperty("gnu.java.util.zoneinfo.dir");
+ if (zoneinfo_dir != null)
+ zoneinfo_dir
+ = new File(zoneinfo_dir
+ + File.separatorChar).getCanonicalPath();
+ if (zoneinfo_dir != null && id.startsWith(zoneinfo_dir))
+ {
+ int pos = zoneinfo_dir.length();
+ while (pos < id.length()
+ && id.charAt(pos) == File.separatorChar)
+ pos++;
+ if (pos < id.length())
+ id = id.substring(pos);
+ else
+ id = null;
+ }
+ else
+ id = null;
+ }
+ }
+ catch (IOException ioe)
+ {
+ id = null;
+ }
+
+ if (id == null)
+ id = readSysconfigClockFile("/etc/sysconfig/clock");
+
+ if (id != null)
+ zone.setID(id);
+ }
}
// Try some system specific way
@@ -189,466 +232,47 @@ final class VMTimeZone
}
/**
- * 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.
+ * Tries to read the time zone name from a file.
+ * If the file cannot be read or an IOException occurs 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).
+ * The /etc/sysconfig/clock file is not standard, but a lot of systems
+ * have it. The file is included by shell scripts and the timezone
+ * name is defined in ZONE variable.
+ * This routine should grok it with or without quotes:
+ * ZONE=America/New_York
+ * or
+ * ZONE="Europe/London"
*/
- private static String readtzFile(String file)
+ private static String readSysconfigClockFile(String file)
{
- File f = new File(file);
- if (!f.exists())
- return null;
-
- DataInputStream dis = null;
+ BufferedReader br = null;
try
{
- FileInputStream fis = new FileInputStream(f);
+ FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
- dis = new DataInputStream(bis);
-
- // Make sure we are reading a tzfile.
- byte[] tzif = new byte[5];
- dis.readFully(tzif);
- int tzif2 = 4;
- if (tzif[0] == 'T' && tzif[1] == 'Z'
- && tzif[2] == 'i' && tzif[3] == 'f')
- {
- if (tzif[4] >= '2')
- tzif2 = 8;
- // Reserved bytes
- skipFully(dis, 16 - 1);
- }
- else
- // Darwin has tzdata files that don't start with the TZif marker
- skipFully(dis, 16 - 5);
-
- String id = null;
- int ttisgmtcnt = dis.readInt();
- int ttisstdcnt = dis.readInt();
- int leapcnt = dis.readInt();
- int timecnt = dis.readInt();
- int typecnt = dis.readInt();
- int charcnt = dis.readInt();
- if (tzif2 == 8)
- {
- skipFully(dis, timecnt * (4 + 1) + typecnt * (4 + 1 + 1) + charcnt
- + leapcnt * (4 + 4) + ttisgmtcnt + ttisstdcnt);
-
- dis.readFully(tzif);
- if (tzif[0] != 'T' || tzif[1] != 'Z' || tzif[2] != 'i'
- || tzif[3] != 'f' || tzif[4] < '2')
- return null;
-
- // Reserved bytes
- skipFully(dis, 16 - 1);
- ttisgmtcnt = dis.readInt();
- ttisstdcnt = dis.readInt();
- leapcnt = dis.readInt();
- timecnt = dis.readInt();
- typecnt = dis.readInt();
- charcnt = dis.readInt();
- }
- if (typecnt > 0)
- {
- int seltimecnt = timecnt;
- if (seltimecnt > 16)
- seltimecnt = 16;
-
- long[] times = new long[seltimecnt];
- int[] types = new int[seltimecnt];
-
- // Transition times
- skipFully(dis, (timecnt - seltimecnt) * tzif2);
-
- for (int i = 0; i < seltimecnt; i++)
- if (tzif2 == 8)
- times[i] = dis.readLong();
- else
- times[i] = (long) dis.readInt();
-
- // Transition types
- skipFully(dis, timecnt - seltimecnt);
- for (int i = 0; i < seltimecnt; i++)
- {
- types[i] = dis.readByte();
- if (types[i] < 0)
- types[i] += 256;
- }
-
- // Get std/dst_offset and dst/non-dst time zone names.
- int std_abbrind = -1;
- int dst_abbrind = -1;
- int std_offset = 0;
- int dst_offset = 0;
- int std_ind = -1;
- int dst_ind = -1;
-
- int alternation = 0;
- if (seltimecnt >= 4 && types[0] != types[1]
- && types[0] < typecnt && types[1] < typecnt)
- {
- // Verify only two types are involved
- // in the transitions and they alternate.
- alternation = 1;
- for (int i = 2; i < seltimecnt; i++)
- if (types[i] != types[i % 2])
- alternation = 0;
- }
-
- // If a timezone previously used DST, but no longer does
- // (or no longer will in the near future, say 5 years),
- // then always pick only the std zone type corresponding
- // to latest applicable transition.
- if (seltimecnt > 0
- && times[seltimecnt - 1]
- < System.currentTimeMillis() / 1000 + 5 * 365 * 86400)
- alternation = -1;
-
- for (int i = 0; i < typecnt; i++)
- {
- // gmtoff
- int offset = dis.readInt();
- int dst = dis.readByte();
- int abbrind = dis.readByte();
- if (dst == 0)
- {
- if (alternation == 0
- || (alternation == 1
- && (i == types[0] || i == types[1]))
- || (alternation == -1 && i == types[seltimecnt - 1]))
- {
- std_abbrind = abbrind;
- std_offset = offset * -1;
- std_ind = i;
- }
- }
- else if (alternation >= 0)
- {
- if (alternation == 0 || i == types[0] || i == types[1])
- {
- dst_abbrind = abbrind;
- dst_offset = offset * -1;
- dst_ind = i;
- }
- }
- }
-
- if (std_abbrind >= 0)
- {
- byte[] names = new byte[charcnt];
- dis.readFully(names);
- int j = std_abbrind;
- while (j < charcnt && names[j] != 0)
- j++;
-
- String zonename = new String(names, std_abbrind,
- j - std_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 = "";
+ br = new BufferedReader(new InputStreamReader(bis));
- String[] change_spec = { null, null };
- if (dst_abbrind >= 0 && alternation > 0)
- {
- // Guess rules for the std->dst and dst->std transitions
- // from the transition times since Epoch.
- // tzdata actually uses only 3 forms of rules:
- // fixed date within a month, e.g. change on April, 5th
- // 1st weekday on or after Nth: change on Sun>=15 in April
- // last weekday in a month: change on lastSun in April
- GregorianCalendar cal
- = new GregorianCalendar (TimeZone.getTimeZone("GMT"));
-
- int[] values = new int[2 * 11];
- int i;
- for (i = seltimecnt - 1; i >= 0; i--)
- {
- int base = (i % 2) * 11;
- int offset = types[i] == dst_ind ? std_offset : dst_offset;
- cal.setTimeInMillis((times[i] - offset) * 1000);
- if (i >= seltimecnt - 2)
- {
- values[base + 0] = cal.get(Calendar.YEAR);
- values[base + 1] = cal.get(Calendar.MONTH);
- values[base + 2] = cal.get(Calendar.DAY_OF_MONTH);
- values[base + 3]
- = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
- values[base + 4] = cal.get(Calendar.DAY_OF_WEEK);
- values[base + 5] = cal.get(Calendar.HOUR_OF_DAY);
- values[base + 6] = cal.get(Calendar.MINUTE);
- values[base + 7] = cal.get(Calendar.SECOND);
- values[base + 8] = values[base + 2]; // Range start
- values[base + 9] = values[base + 2]; // Range end
- values[base + 10] = 0; // Determined type
- }
- else
- {
- int year = cal.get(Calendar.YEAR);
- int month = cal.get(Calendar.MONTH);
- int day_of_month = cal.get(Calendar.DAY_OF_MONTH);
- int month_days
- = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
- int day_of_week = cal.get(Calendar.DAY_OF_WEEK);
- int hour = cal.get(Calendar.HOUR_OF_DAY);
- int minute = cal.get(Calendar.MINUTE);
- int second = cal.get(Calendar.SECOND);
- if (year != values[base + 0] - 1
- || month != values[base + 1]
- || hour != values[base + 5]
- || minute != values[base + 6]
- || second != values[base + 7])
- break;
- if (day_of_week == values[base + 4])
- {
- // Either a Sun>=8 or lastSun rule.
- if (day_of_month < values[base + 8])
- values[base + 8] = day_of_month;
- if (day_of_month > values[base + 9])
- values[base + 9] = day_of_month;
- if (values[base + 10] < 0)
- break;
- if (values[base + 10] == 0)
- {
- values[base + 10] = 1;
- // If day of month > 28, this is
- // certainly lastSun rule.
- if (values[base + 2] > 28)
- values[base + 2] = 3;
- // If day of month isn't in the last
- // week, it can't be lastSun rule.
- else if (values[base + 2]
- <= values[base + 3] - 7)
- values[base + 3] = 2;
- }
- if (values[base + 10] == 1)
- {
- // If day of month is > 28, this is
- // certainly lastSun rule.
- if (day_of_month > 28)
- values[base + 10] = 3;
- // If day of month isn't in the last
- // week, it can't be lastSun rule.
- else if (day_of_month <= month_days - 7)
- values[base + 10] = 2;
- }
- else if ((values[base + 10] == 2
- && day_of_month > 28)
- || (values[base + 10] == 3
- && day_of_month
- <= month_days - 7))
- break;
- }
- else
- {
- // Must be fixed day in month rule.
- if (day_of_month != values[base + 2]
- || values[base + 10] > 0)
- break;
- values[base + 4] = day_of_week;
- values[base + 10] = -1;
- }
- values[base + 0] -= 1;
- }
- }
- if (i < 0)
- {
- for (i = 0; i < 2; i++)
- {
- int base = 11 * i;
- if (values[base + 10] == 0)
- continue;
- if (values[base + 10] == -1)
- {
- int[] dayCount
- = { 0, 31, 59, 90, 120, 151,
- 181, 212, 243, 273, 304, 334 };
- int d = dayCount[values[base + 1]
- - Calendar.JANUARY];
- d += values[base + 2];
- change_spec[i] = ",J" + Integer.toString(d);
- }
- else if (values[base + 10] == 2)
- {
- // If we haven't seen all days of the week,
- // we can't be sure what the rule really is.
- if (values[base + 8] + 6 != values[base + 9])
- continue;
-
- // FIXME: Sun >= 5 is representable in
- // SimpleTimeZone, but not in POSIX TZ env
- // strings. Should we change readtzFile
- // to actually return a SimpleTimeZone
- // rather than POSIX TZ string?
- if ((values[base + 8] % 7) != 1)
- continue;
-
- int d;
- d = values[base + 1] - Calendar.JANUARY + 1;
- change_spec[i] = ",M" + Integer.toString(d);
- d = (values[base + 8] + 6) / 7;
- change_spec[i] += "." + Integer.toString(d);
- d = values[base + 4] - Calendar.SUNDAY;
- change_spec[i] += "." + Integer.toString(d);
- }
- else
- {
- // If we don't know whether this is lastSun or
- // Sun >= 22 rule. That can be either because
- // there was insufficient number of
- // transitions, or February, where it is quite
- // probable we haven't seen any 29th dates.
- // For February, assume lastSun rule, otherwise
- // punt.
- if (values[base + 10] == 1
- && values[base + 1] != Calendar.FEBRUARY)
- continue;
-
- int d;
- d = values[base + 1] - Calendar.JANUARY + 1;
- change_spec[i] = ",M" + Integer.toString(d);
- d = values[base + 4] - Calendar.SUNDAY;
- change_spec[i] += ".5." + Integer.toString(d);
- }
-
- // Don't add time specification if time is
- // 02:00:00.
- if (values[base + 5] != 2
- || values[base + 6] != 0
- || values[base + 7] != 0)
- {
- int d = values[base + 5];
- change_spec[i] += "/" + Integer.toString(d);
- if (values[base + 6] != 0
- || values[base + 7] != 0)
- {
- d = values[base + 6];
- if (d < 10)
- change_spec[i]
- += ":0" + Integer.toString(d);
- else
- change_spec[i]
- += ":" + Integer.toString(d);
- d = values[base + 7];
- if (d >= 10)
- change_spec[i]
- += ":" + Integer.toString(d);
- else if (d > 0)
- change_spec[i]
- += ":0" + Integer.toString(d);
- }
- }
- }
- if (types[0] == std_ind)
- {
- String tmp = change_spec[0];
- change_spec[0] = change_spec[1];
- change_spec[1] = tmp;
- }
- }
- }
-
- // Only use gmt offset when necessary.
- // Also special case GMT+/- timezones.
- String offset_string, dst_offset_string = "";
- if (dst_abbrind < 0
- && (std_offset == 0
- || zonename.startsWith("GMT+")
- || zonename.startsWith("GMT-")))
- offset_string = "";
- else
- {
- offset_string = Integer.toString(std_offset / 3600);
- int seconds = std_offset % 3600;
- if (seconds != 0)
- {
- if (seconds < 0)
- seconds *= -1;
- if (seconds < 600)
- offset_string
- += ":0" + Integer.toString(seconds / 60);
- else
- offset_string
- += ":" + Integer.toString(seconds / 60);
- seconds = seconds % 60;
- if (seconds >= 10)
- offset_string
- += ":" + Integer.toString(seconds);
- else if (seconds > 0)
- offset_string
- += ":0" + Integer.toString(seconds);
- }
- if (dst_abbrind >= 0
- && dst_offset != std_offset - 3600)
- {
- dst_offset_string
- = Integer.toString(dst_offset / 3600);
- seconds = dst_offset % 3600;
- if (seconds != 0)
- {
- if (seconds < 0)
- seconds *= -1;
- if (seconds < 600)
- dst_offset_string
- += ":0" + Integer.toString(seconds / 60);
- else
- dst_offset_string
- += ":" + Integer.toString(seconds / 60);
- seconds = seconds % 60;
- if (seconds >= 10)
- dst_offset_string
- += ":" + Integer.toString(seconds);
- else if (seconds > 0)
- dst_offset_string
- += ":0" + Integer.toString(seconds);
- }
- }
- }
-
- if (dst_abbrind < 0)
- id = zonename + offset_string;
- else if (change_spec[0] != null && change_spec[1] != null)
- id = zonename + offset_string + dst_zonename
- + dst_offset_string + change_spec[0] + change_spec[1];
- }
- else if (tzif2 == 8)
- skipFully(dis, charcnt);
- }
- else if (tzif2 == 8)
- skipFully(dis, timecnt * (8 + 1) + typecnt * (4 + 1 + 1) + charcnt);
-
- if (tzif2 == 8)
+ for (String line = br.readLine(); line != null; line = br.readLine())
{
- // Skip over the rest of 64-bit data
- skipFully(dis, leapcnt * (8 + 4) + ttisgmtcnt + ttisstdcnt);
- if (dis.readByte() == '\n')
+ line = line.trim();
+ if (line.length() < 8 || !line.startsWith("ZONE="))
+ continue;
+ int posstart = 6;
+ int posend;
+ if (line.charAt(5) == '"')
+ posend = line.indexOf('"', 6);
+ else if (line.charAt(5) == '\'')
+ posend = line.indexOf('\'', 6);
+ else
{
- String posixtz = dis.readLine();
- if (posixtz.length() > 0)
- id = posixtz;
+ posstart = 5;
+ posend = line.length();
}
+ if (posend < 0)
+ return null;
+ return line.substring(posstart, posend);
}
-
- return id;
+ return null;
}
catch (IOException ioe)
{
@@ -659,31 +283,15 @@ final class VMTimeZone
{
try
{
- if (dis != null)
- dis.close();
+ if (br != null)
+ br.close();
}
- catch(IOException ioe)
+ 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;
- }
- }
/**
* Tries to get the system time zone id through native code.