OSDN Git Service

libjava/
authorjakub <jakub@138bc75d-0d04-0410-961f-82ee72b054a4>
Thu, 22 Feb 2007 16:04:55 +0000 (16:04 +0000)
committerjakub <jakub@138bc75d-0d04-0410-961f-82ee72b054a4>
Thu, 22 Feb 2007 16:04:55 +0000 (16:04 +0000)
PR libgcj/17002
PR classpath/28550
* 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.
* java/lang/System.java: Add gnu.java.util.zoneinfo.dir to comments.
* posix.cc (_Jv_platform_initProperties): Set
gnu.java.util.zoneinfo.dir.
* sources.am (gnu_java_util_source_files): Add
classpath/gnu/java/util/ZoneInfo.java.
* Makefile.in: Regenerated.
* java/util/VMTimeZone.h: Regenerated.
* java/util/TimeZone.h: Regenerated.
* gnu/java/util/ZoneInfo.h: Generated.
libjava/classpath/
* 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.
* gnu/java/util/ZoneInfo.java: New file.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@122229 138bc75d-0d04-0410-961f-82ee72b054a4

20 files changed:
libjava/ChangeLog
libjava/Makefile.in
libjava/classpath/ChangeLog
libjava/classpath/gnu/java/util/ZoneInfo.java [new file with mode: 0644]
libjava/classpath/java/util/Date.java
libjava/classpath/java/util/SimpleTimeZone.java
libjava/classpath/java/util/TimeZone.java
libjava/classpath/lib/gnu/java/util/ZoneInfo.class [new file with mode: 0644]
libjava/classpath/lib/java/util/Date.class
libjava/classpath/lib/java/util/SimpleTimeZone.class
libjava/classpath/lib/java/util/TimeZone$1.class
libjava/classpath/lib/java/util/TimeZone.class
libjava/classpath/lib/java/util/VMTimeZone.class
libjava/gnu/java/util/ZoneInfo.h [new file with mode: 0644]
libjava/java/lang/System.java
libjava/java/util/TimeZone.h
libjava/java/util/VMTimeZone.h
libjava/java/util/VMTimeZone.java
libjava/posix.cc
libjava/sources.am

index 6d337d5..eaea0ec 100644 (file)
@@ -1,3 +1,23 @@
+2007-02-22  Jakub Jelinek  <jakub@redhat.com>
+
+       PR libgcj/17002
+       PR classpath/28550
+       * 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.
+       * java/lang/System.java: Add gnu.java.util.zoneinfo.dir to comments.
+       * posix.cc (_Jv_platform_initProperties): Set
+       gnu.java.util.zoneinfo.dir.
+       * sources.am (gnu_java_util_source_files): Add
+       classpath/gnu/java/util/ZoneInfo.java.
+       * Makefile.in: Regenerated.
+       * java/util/VMTimeZone.h: Regenerated.
+       * java/util/TimeZone.h: Regenerated.
+       * gnu/java/util/ZoneInfo.h: Generated.
+
 2007-02-22  Mohan Embar  <gnustuff@thisiscool.com>
 
        * include/win32-threads.h: Added #undef OUT.
index db8813e..27aa9ae 100644 (file)
@@ -2402,7 +2402,8 @@ gnu_java_text_header_files = $(patsubst classpath/%,%,$(patsubst %.java,%.h,$(gn
 gnu_java_util_source_files = \
 classpath/gnu/java/util/DoubleEnumeration.java \
 classpath/gnu/java/util/EmptyEnumeration.java \
-classpath/gnu/java/util/WeakIdentityHashMap.java
+classpath/gnu/java/util/WeakIdentityHashMap.java \
+classpath/gnu/java/util/ZoneInfo.java
 
 gnu_java_util_header_files = $(patsubst classpath/%,%,$(patsubst %.java,%.h,$(gnu_java_util_source_files)))
 gnu_java_util_jar_source_files = \
index c959705..fa92bda 100644 (file)
@@ -1,3 +1,34 @@
+2007-02-20  Jakub Jelinek  <jakub@redhat.com>
+
+       * 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.
+       * gnu/java/util/ZoneInfo.java: New file.
+
 2007-02-20  Matthias Klose  <doko@ubuntu.com>
 
        * doc/Makefile.am: Add rules to build and install man pages
diff --git a/libjava/classpath/gnu/java/util/ZoneInfo.java b/libjava/classpath/gnu/java/util/ZoneInfo.java
new file mode 100644 (file)
index 0000000..2146a32
--- /dev/null
@@ -0,0 +1,1160 @@
+/* gnu.java.util.ZoneInfo
+   Copyright (C) 2007 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.util;
+
+import java.io.BufferedInputStream;
+import java.io.DataInputStream;
+import java.io.EOFException;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.SimpleTimeZone;
+import java.util.TimeZone;
+
+/**
+ * This class represents more advanced variant of java.util.SimpleTimeZone.
+ * It can handle zic(8) compiled transition dates plus uses a SimpleTimeZone
+ * for years beyond last precomputed transition.  Before first precomputed
+ * transition it assumes no daylight saving was in effect.
+ * Timezones that never used daylight saving time should use just
+ * SimpleTimeZone instead of this class.
+ *
+ * This object is tightly bound to the Gregorian calendar.  It assumes
+ * a regular seven days week, and the month lengths are that of the
+ * Gregorian Calendar.
+ *
+ * @see Calendar
+ * @see GregorianCalendar
+ * @see SimpleTimeZone
+ * @author Jakub Jelinek
+ */
+public class ZoneInfo extends TimeZone
+{
+  private static final int SECS_SHIFT = 22;
+  private static final long OFFSET_MASK = (1 << 21) - 1;
+  private static final int OFFSET_SHIFT = 64 - 21;
+  private static final long IS_DST = 1 << 21;
+
+  /**
+   * The raw time zone offset in milliseconds to GMT, ignoring
+   * daylight savings.
+   * @serial
+   */
+  private int rawOffset;
+
+  /**
+   * Cached DST savings for the last transition rule.
+   */
+  private int dstSavings;
+
+  /**
+   * Cached flag whether last transition rule uses DST saving.
+   */
+  private boolean useDaylight;
+
+  /**
+   * Array of encoded transitions.
+   * Transition time in UTC seconds since epoch is in the most
+   * significant 64 - SECS_SHIFT bits, then one bit flag
+   * whether DST is active and the least significant bits
+   * containing offset relative to rawOffset.  Both the DST
+   * flag and relative offset apply to time before the transition
+   * and after or equal to previous transition if any.
+   * The array must be sorted.
+   */
+  private long[] transitions;
+
+  /**
+   * SimpleTimeZone rule which applies on or after the latest
+   * transition.  If the DST changes are not expresible as a
+   * SimpleTimeZone rule, then the rule should just contain
+   * the standard time and no DST time.
+   */
+  private SimpleTimeZone lastRule;
+
+  /**
+   * Cached GMT SimpleTimeZone object for internal use in
+   * getOffset method.
+   */
+  private static SimpleTimeZone gmtZone = null;
+
+  static final long serialVersionUID = -3740626706860383657L;
+
+  /**
+   * Create a <code>ZoneInfo</code> with the given time offset
+   * from GMT and with daylight savings.
+   *
+   * @param rawOffset The time offset from GMT in milliseconds.
+   * @param id  The identifier of this time zone.
+   * @param transitions  Array of transition times in UTC seconds since
+   * Epoch in topmost 42 bits, below that 1 boolean bit whether the time
+   * before that transition used daylight saving and in bottommost 21
+   * bits relative daylight saving offset against rawOffset in seconds
+   * that applies before this transition.
+   * @param endRule SimpleTimeZone class describing the daylight saving
+   * rules after the last transition.
+   */
+  public ZoneInfo(int rawOffset, String id, long[] transitions,
+                 SimpleTimeZone lastRule)
+  {
+    if (transitions == null || transitions.length < 1)
+      throw new IllegalArgumentException("transitions must not be null");
+    if (lastRule == null)
+      throw new IllegalArgumentException("lastRule must not be null");
+    this.rawOffset = rawOffset;
+    this.transitions = transitions;
+    this.lastRule = lastRule;
+    setID(id);
+    computeDSTSavings();
+  }
+
+  /**
+   * Gets the time zone offset, for current date, modified in case of
+   * daylight savings.  This is the offset to add to UTC to get the local
+   * time.
+   *
+   * The day must be a positive number and dayOfWeek must be a positive value
+   * from Calendar.  dayOfWeek is redundant, but must match the other values
+   * or an inaccurate result may be returned.
+   *
+   * @param era the era of the given date
+   * @param year the year of the given date
+   * @param month the month of the given date, 0 for January.
+   * @param day the day of month
+   * @param dayOfWeek the day of week; this must match the other fields.
+   * @param millis the millis in the day (in local standard time)
+   * @return the time zone offset in milliseconds.
+   * @throws IllegalArgumentException if arguments are incorrect.
+   */
+  public int getOffset(int era, int year, int month, int day, int dayOfWeek,
+                      int millis)
+  {
+    if (gmtZone == null)
+      gmtZone = new SimpleTimeZone(0, "GMT");
+
+    if (dayOfWeek < Calendar.SUNDAY || dayOfWeek > Calendar.SATURDAY)
+      throw new IllegalArgumentException("dayOfWeek out of range");
+    if (month < Calendar.JANUARY || month > Calendar.DECEMBER)
+      throw new IllegalArgumentException("month out of range:" + month);
+
+    if (era != GregorianCalendar.AD)
+      return (int) (((transitions[0] << OFFSET_SHIFT) >> OFFSET_SHIFT) * 1000);
+
+    GregorianCalendar cal = new GregorianCalendar((TimeZone) gmtZone);
+    cal.set(year, month, day, 0, 0, 0);
+    if (cal.get(Calendar.DAY_OF_MONTH) != day)
+      throw new IllegalArgumentException("day out of range");
+
+    return getOffset(cal.getTimeInMillis() - rawOffset + millis);
+  }
+
+  private long findTransition(long secs)
+  {
+    if (secs < (transitions[0] >> SECS_SHIFT))
+      return transitions[0];
+
+    if (secs >= (transitions[transitions.length-1] >> SECS_SHIFT))
+      return Long.MAX_VALUE;
+
+    long val = (secs + 1) << SECS_SHIFT;
+    int lo = 1;
+    int hi = transitions.length;
+    int mid = 1;
+    while (lo < hi)
+      {
+       mid = (lo + hi) / 2;
+       // secs < (transitions[mid-1] >> SECS_SHIFT)
+       if (val <= transitions[mid-1])
+         hi = mid;
+       // secs >= (transitions[mid] >> SECS_SHIFT)
+       else if (val > transitions[mid])
+         lo = mid + 1;
+       else
+         break;
+      }
+    return transitions[mid];
+  }
+
+  /**
+   * Get the time zone offset for the specified date, modified in case of
+   * daylight savings.  This is the offset to add to UTC to get the local
+   * time.
+   * @param date the date represented in millisecends
+   * since January 1, 1970 00:00:00 GMT.
+   */
+  public int getOffset(long date)
+  {
+    long d = (date >= 0 ? date / 1000 : (date + 1) / 1000 - 1);
+    long transition = findTransition(d);
+
+    // For times on or after last transition use lastRule.
+    if (transition == Long.MAX_VALUE)
+      return lastRule.getOffset(date);
+
+    return (int) (((transition << OFFSET_SHIFT) >> OFFSET_SHIFT) * 1000);
+  }
+
+  /**
+   * Returns the time zone offset to GMT in milliseconds, ignoring
+   * day light savings.
+   * @return the time zone offset.
+   */
+  public int getRawOffset()
+  {
+    return rawOffset;
+  }
+
+  /**
+   * Sets the standard time zone offset to GMT.
+   * @param rawOffset The time offset from GMT in milliseconds.
+   */
+  public void setRawOffset(int rawOffset)
+  {
+    this.rawOffset = rawOffset;
+    lastRule.setRawOffset(rawOffset);
+  }
+
+  private void computeDSTSavings()
+  {
+    if (lastRule.useDaylightTime())
+      {
+       dstSavings = lastRule.getDSTSavings();
+       useDaylight = true;
+      }
+    else
+      {
+       dstSavings = 0;
+       useDaylight = false;
+       // lastRule might say no DST is in effect simply because
+       // the DST rules are too complex for SimpleTimeZone, say
+       // for Asia/Jerusalem.
+       // Look at the last DST offset if it is newer than current time.
+       long currentSecs = System.currentTimeMillis() / 1000;
+       int i;
+       for (i = transitions.length - 1;
+            i >= 0 && currentSecs < (transitions[i] >> SECS_SHIFT);
+            i--)
+         if ((transitions[i] & IS_DST) != 0)
+           {
+             dstSavings = (int) (((transitions[i] << OFFSET_SHIFT)
+                                  >> OFFSET_SHIFT) * 1000)
+                          - rawOffset;
+             useDaylight = true;
+             break;
+           }
+      }
+  }
+
+  /**
+   * Gets the daylight savings offset.  This is a positive offset in
+   * milliseconds with respect to standard time.  Typically this
+   * is one hour, but for some time zones this may be half an our.
+   * @return the daylight savings offset in milliseconds.
+   */
+  public int getDSTSavings()
+  {
+    return dstSavings;
+  }
+
+  /**
+   * Returns if this time zone uses daylight savings time.
+   * @return true, if we use daylight savings time, false otherwise.
+   */
+  public boolean useDaylightTime()
+  {
+    return useDaylight;
+  }
+
+  /**
+   * Determines if the given date is in daylight savings time.
+   * @return true, if it is in daylight savings time, false otherwise.
+   */
+  public boolean inDaylightTime(Date date)
+  {
+    long d = date.getTime();
+    d = (d >= 0 ? d / 1000 : (d + 1) / 1000 - 1);
+    long transition = findTransition(d);
+
+    // For times on or after last transition use lastRule.
+    if (transition == Long.MAX_VALUE)
+      return lastRule.inDaylightTime(date);
+
+    return (transition & IS_DST) != 0;
+  }
+
+  /**
+   * Generates the hashCode for the SimpleDateFormat object.  It is
+   * the rawOffset, possibly, if useDaylightSavings is true, xored
+   * with startYear, startMonth, startDayOfWeekInMonth, ..., endTime.
+   */
+  public synchronized int hashCode()
+  {
+    int hash = lastRule.hashCode();
+    // FIXME - hash transitions?
+    return hash;
+  }
+
+  public synchronized boolean equals(Object o)
+  {
+    if (! hasSameRules((TimeZone) o))
+      return false;
+
+    ZoneInfo zone = (ZoneInfo) o;
+    return getID().equals(zone.getID());
+  }
+
+  /**
+   * Test if the other time zone uses the same rule and only
+   * possibly differs in ID.  This implementation for this particular
+   * class will return true if the other object is a ZoneInfo,
+   * the raw offsets and useDaylight are identical and if useDaylight
+   * is true, also the start and end datas are identical.
+   * @return true if this zone uses the same rule.
+   */
+  public boolean hasSameRules(TimeZone o)
+  {
+    if (this == o)
+      return true;
+    if (! (o instanceof ZoneInfo))
+      return false;
+    ZoneInfo zone = (ZoneInfo) o;
+    if (zone.hashCode() != hashCode() || rawOffset != zone.rawOffset)
+      return false;
+    if (! lastRule.equals(zone.lastRule))
+      return false;
+    // FIXME - compare transitions?
+    return true;
+  }
+
+  /**
+   * Returns a string representation of this ZoneInfo object.
+   * @return a string representation of this ZoneInfo object.
+   */
+  public String toString()
+  {
+    return getClass().getName() + "[" + "id=" + getID() + ",offset="
+          + rawOffset + ",transitions=" + transitions.length
+          + ",useDaylight=" + useDaylight
+          + (useDaylight ? (",dstSavings=" + dstSavings) : "")
+          + ",lastRule=" + lastRule.toString() + "]";
+  }
+
+  /**
+   * Reads zic(8) compiled timezone data file from file
+   * and returns a TimeZone class describing it, either
+   * SimpleTimeZone or ZoneInfo depending on whether
+   * it can be described by SimpleTimeZone rule or not.
+   */
+  public static TimeZone readTZFile(String id, String file)
+  {
+    DataInputStream dis = null;
+    try
+      {
+       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);
+
+       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();
+         }
+
+       // Sanity checks
+       if (typecnt <= 0 || timecnt < 0 || charcnt < 0
+           || leapcnt < 0 || ttisgmtcnt < 0 || ttisstdcnt < 0
+           || ttisgmtcnt > typecnt || ttisstdcnt > typecnt)
+         return null;
+
+       // Transition times
+       long[] times = new long[timecnt];
+       for (int i = 0; i < timecnt; i++)
+         if (tzif2 == 8)
+           times[i] = dis.readLong();
+         else
+           times[i] = (long) dis.readInt();
+
+       // Transition types
+       int[] types = new int[timecnt];
+       for (int i = 0; i < timecnt; i++)
+         {
+           types[i] = dis.readByte();
+           if (types[i] < 0)
+             types[i] += 256;
+           if (types[i] >= typecnt)
+             return null;
+         }
+
+       // Types
+       int[] offsets = new int[typecnt];
+       int[] typeflags = new int[typecnt];
+       for (int i = 0; i < typecnt; i++)
+         {
+           offsets[i] = dis.readInt();
+           if (offsets[i] >= IS_DST / 2 || offsets[i] <= -IS_DST / 2)
+             return null;
+           int dst = dis.readByte();
+           int abbrind = dis.readByte();
+           if (abbrind < 0)
+             abbrind += 256;
+           if (abbrind >= charcnt)
+             return null;
+           typeflags[i] = (dst != 0 ? (1 << 8) : 0) + abbrind;
+         }
+
+       // Abbrev names
+       byte[] names = new byte[charcnt];
+       dis.readFully(names);
+
+       // Leap transitions, for now ignore
+       skipFully(dis, leapcnt * (tzif2 + 4) + ttisstdcnt + ttisgmtcnt);
+
+       // tzIf2 format has optional POSIX TZ env string
+       String tzstr = null;
+       if (tzif2 == 8 && dis.readByte() == '\n')
+         {
+           tzstr = dis.readLine();
+           if (tzstr.length() <= 0)
+             tzstr = null;
+         }
+
+       // Get std/dst_offset and dst/non-dst time zone names.
+       int std_ind = -1;
+       int dst_ind = -1;
+       if (timecnt == 0)
+         std_ind = 0;
+       else
+         for (int i = timecnt - 1; i >= 0; i--)
+           {
+             if (std_ind == -1 && (typeflags[types[i]] & (1 << 8)) == 0)
+               std_ind = types[i];
+             else if (dst_ind == -1 && (typeflags[types[i]] & (1 << 8)) != 0)
+               dst_ind = types[i];
+             if (dst_ind != -1 && std_ind != -1)
+               break;
+           }
+
+       if (std_ind == -1)
+         return null;
+
+       int j = typeflags[std_ind] & 255;
+       while (j < charcnt && names[j] != 0)
+         j++;
+       String std_zonename = new String(names, typeflags[std_ind] & 255,
+                                        j - (typeflags[std_ind] & 255),
+                                        "ASCII");
+
+       String dst_zonename = "";
+       if (dst_ind != -1)
+         {
+           j = typeflags[dst_ind] & 255;
+           while (j < charcnt && names[j] != 0)
+             j++;
+           dst_zonename = new String(names, typeflags[dst_ind] & 255,
+                                     j - (typeflags[dst_ind] & 255), "ASCII");
+         }
+
+       // Only use gmt offset when necessary.
+       // Also special case GMT+/- timezones.
+       String std_offset_string = "";
+       String dst_offset_string = "";
+       if (tzstr == null
+           && (dst_ind != -1
+               || (offsets[std_ind] != 0
+                   && !std_zonename.startsWith("GMT+")
+                   && !std_zonename.startsWith("GMT-"))))
+         {
+           std_offset_string = Integer.toString(-offsets[std_ind] / 3600);
+           int seconds = -offsets[std_ind] % 3600;
+           if (seconds != 0)
+             {
+               if (seconds < 0)
+                 seconds *= -1;
+               if (seconds < 600)
+                 std_offset_string += ":0" + Integer.toString(seconds / 60);
+               else
+                 std_offset_string += ":" + Integer.toString(seconds / 60);
+               seconds = seconds % 60;
+               if (seconds >= 10)
+                 std_offset_string += ":" + Integer.toString(seconds);
+               else if (seconds > 0)
+                 std_offset_string += ":0" + Integer.toString(seconds);
+             }
+
+           if (dst_ind != -1 && offsets[dst_ind] != offsets[std_ind] + 3600)
+             {
+               dst_offset_string = Integer.toString(-offsets[dst_ind] / 3600);
+               seconds = -offsets[dst_ind] % 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 no tzIf2 POSIX TZ string is available and the timezone
+        * uses DST, try to guess the last rule by trying to make
+        * sense from transitions at 5 years in the future and onwards.
+        * 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
+        */
+       String[] change_spec = { null, null };
+       if (tzstr == null && dst_ind != -1 && timecnt > 10)
+         {
+           long nowPlus5y = System.currentTimeMillis() / 1000
+                            + 5 * 365 * 86400;
+           int first;
+
+           for (first = timecnt - 1; first >= 0; first--)
+             if (times[first] < nowPlus5y
+                 || (types[first] != std_ind && types[first] != dst_ind)
+                 || types[first] != types[timecnt - 2 + ((first ^ timecnt) & 1)])
+               break;
+           first++;
+
+           if (timecnt - first >= 10 && types[timecnt - 1] != types[timecnt - 2])
+             {
+               GregorianCalendar cal
+                 = new GregorianCalendar(new SimpleTimeZone(0, "GMT"));
+
+               int[] values = new int[2 * 11];
+               int i;
+               for (i = timecnt - 1; i >= first; i--)
+                 {
+                   int base = (i % 2) * 11;
+                   int offset = offsets[types[i > first ? i - 1 : i + 1]];
+                   cal.setTimeInMillis((times[i] + offset) * 1000);
+                   if (i >= timecnt - 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 < first)
+                 {
+                   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;
+
+                           int d;
+                           d = values[base + 1] - Calendar.JANUARY + 1;
+                           // E.g. Sun >= 5 is not representable in POSIX
+                           // TZ env string, use ",Am.n.d" extension
+                           // where m is month 1 .. 12, n is the date on
+                           // or after which it happens and d is day
+                           // of the week, 0 .. 6.  So Sun >= 5 in April
+                           // is ",A4.5.0".
+                           if ((values[base + 8] % 7) == 1)
+                             {
+                               change_spec[i] = ",M" + Integer.toString(d);
+                               d = (values[base + 8] + 6) / 7;
+                             }
+                           else
+                             {
+                               change_spec[i] = ",A" + Integer.toString(d);
+                               d = values[base + 8];
+                             }
+                           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[(timecnt - 1) & -2] == std_ind)
+                     {
+                       String tmp = change_spec[0];
+                       change_spec[0] = change_spec[1];
+                       change_spec[1] = tmp;
+                     }
+                 }
+             }
+         }
+
+       if (tzstr == null)
+         {
+           tzstr = std_zonename + std_offset_string;
+           if (change_spec[0] != null && change_spec[1] != null)
+             tzstr += dst_zonename + dst_offset_string
+                      + change_spec[0] + change_spec[1];
+         }
+
+       if (timecnt == 0)
+         return new SimpleTimeZone(offsets[std_ind] * 1000,
+                                   id != null ? id : tzstr);
+
+       SimpleTimeZone endRule = createLastRule(tzstr);
+       if (endRule == null)
+         return null;
+
+       /* Finally adjust the times array into the form the constructor
+        * expects.  times[0] is special, the offset and DST flag there
+        * are for all times before that transition.  Use the first non-DST
+        * type.  For all other transitions, the data file has the type
+        * (<offset, isdst, zonename>) for the time interval starting
+        */
+       for (int i = 0; i < typecnt; i++)
+         if ((typeflags[i] & (1 << 8)) == 0)
+           {
+             times[0] = (times[0] << SECS_SHIFT) | (offsets[i] & OFFSET_MASK);
+             break;
+           }
+
+       for (int i = 1; i < timecnt; i++)
+         times[i] = (times[i] << SECS_SHIFT)
+                    | (offsets[types[i - 1]] & OFFSET_MASK)
+                    | ((typeflags[types[i - 1]] & (1 << 8)) != 0 ? IS_DST : 0);
+
+       return new ZoneInfo(offsets[std_ind] * 1000, id != null ? id : tzstr,
+                           times, endRule);
+      }
+    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;
+      }
+  }
+
+  /**
+   * Create a SimpleTimeZone from a POSIX TZ environment string,
+   * see http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html
+   * for details.
+   * It supports also an extension, where Am.n.d rule (m 1 .. 12, n 1 .. 25, d
+   * 0 .. 6) describes day of week d on or after nth day of month m.
+   * Say A4.5.0 is Sun>=5 in April.
+   */
+  private static SimpleTimeZone createLastRule(String tzstr)
+  {
+    String stdName = null;
+    int stdOffs;
+    int dstOffs;
+    try
+      {
+       int idLength = tzstr.length();
+
+       int index = 0;
+       int prevIndex;
+       char c;
+
+       // get std
+       do
+         c = tzstr.charAt(index);
+       while (c != '+' && c != '-' && c != ',' && c != ':'
+              && ! Character.isDigit(c) && c != '\0' && ++index < idLength);
+
+       if (index >= idLength)
+         return new SimpleTimeZone(0, tzstr);
+
+       stdName = tzstr.substring(0, index);
+       prevIndex = index;
+
+       // get the std offset
+       do
+         c = tzstr.charAt(index++);
+       while ((c == '-' || c == '+' || c == ':' || Character.isDigit(c))
+              && index < idLength);
+       if (index < idLength)
+         index--;
+
+       { // convert the dst string to a millis number
+           String offset = tzstr.substring(prevIndex, index);
+           prevIndex = index;
+
+           if (offset.charAt(0) == '+' || offset.charAt(0) == '-')
+             stdOffs = parseTime(offset.substring(1));
+           else
+             stdOffs = parseTime(offset);
+
+           if (offset.charAt(0) == '-')
+             stdOffs = -stdOffs;
+
+           // TZ timezone offsets are positive when WEST of the meridian.
+           stdOffs = -stdOffs;
+       }
+
+       // Done yet? (Format: std offset)
+       if (index >= idLength)
+         return new SimpleTimeZone(stdOffs, stdName);
+
+       // get dst
+       do
+         c = tzstr.charAt(index);
+       while (c != '+' && c != '-' && c != ',' && c != ':'
+              && ! Character.isDigit(c) && c != '\0' && ++index < idLength);
+
+       // Done yet? (Format: std offset dst)
+       if (index >= idLength)
+         return new SimpleTimeZone(stdOffs, stdName);
+
+       // get the dst offset
+       prevIndex = index;
+       do
+         c = tzstr.charAt(index++);
+       while ((c == '-' || c == '+' || c == ':' || Character.isDigit(c))
+              && index < idLength);
+       if (index < idLength)
+         index--;
+
+       if (index == prevIndex && (c == ',' || c == ';'))
+         {
+           // Missing dst offset defaults to one hour ahead of standard
+           // time.
+           dstOffs = stdOffs + 60 * 60 * 1000;
+         }
+       else
+         { // convert the dst string to a millis number
+           String offset = tzstr.substring(prevIndex, index);
+           prevIndex = index;
+
+           if (offset.charAt(0) == '+' || offset.charAt(0) == '-')
+             dstOffs = parseTime(offset.substring(1));
+           else
+             dstOffs = parseTime(offset);
+
+           if (offset.charAt(0) == '-')
+             dstOffs = -dstOffs;
+
+           // TZ timezone offsets are positive when WEST of the meridian.
+           dstOffs = -dstOffs;
+         }
+
+       // Done yet? (Format: std offset dst offset)
+       if (index >= idLength)
+         return new SimpleTimeZone(stdOffs, stdName);
+
+       // get the DST rule
+       if (tzstr.charAt(index) == ','
+           || tzstr.charAt(index) == ';')
+         {
+           index++;
+           int offs = index;
+           while (tzstr.charAt(index) != ','
+                  && tzstr.charAt(index) != ';')
+             index++;
+           String startTime = tzstr.substring(offs, index);
+           index++;
+           String endTime = tzstr.substring(index);
+
+           index = startTime.indexOf('/');
+           int startMillis;
+           int endMillis;
+           String startDate;
+           String endDate;
+           if (index != -1)
+             {
+               startDate = startTime.substring(0, index);
+               startMillis = parseTime(startTime.substring(index + 1));
+             }
+           else
+             {
+               startDate = startTime;
+               // if time isn't given, default to 2:00:00 AM.
+               startMillis = 2 * 60 * 60 * 1000;
+             }
+           index = endTime.indexOf('/');
+           if (index != -1)
+             {
+               endDate = endTime.substring(0, index);
+               endMillis = parseTime(endTime.substring(index + 1));
+             }
+           else
+             {
+               endDate = endTime;
+               // if time isn't given, default to 2:00:00 AM.
+               endMillis = 2 * 60 * 60 * 1000;
+             }
+
+           int[] start = getDateParams(startDate);
+           int[] end = getDateParams(endDate);
+           return new SimpleTimeZone(stdOffs, tzstr, start[0], start[1],
+                                     start[2], startMillis, end[0], end[1],
+                                     end[2], endMillis, (dstOffs - stdOffs));
+         }
+      }
+
+    catch (IndexOutOfBoundsException _)
+      {
+      }
+    catch (NumberFormatException _)
+      {
+      }
+
+    return null;
+  }
+
+  /**
+   * Parses and returns the params for a POSIX TZ date field,
+   * in the format int[]{ month, day, dayOfWeek }, following the
+   * SimpleTimeZone constructor rules.
+   */
+  private static int[] getDateParams(String date)
+  {
+    int[] dayCount = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
+    int month;
+    int type = 0;
+
+    if (date.charAt(0) == 'M' || date.charAt(0) == 'm')
+      type = 1;
+    else if (date.charAt(0) == 'A' || date.charAt(0) == 'a')
+      type = 2;
+
+    if (type > 0)
+      {
+       int day;
+
+       // Month, week of month, day of week
+       // "Mm.w.d".  d is between 0 (Sunday) and 6.  Week w is
+       // between 1 and 5; Week 1 is the first week in which day d
+       // occurs and Week 5 specifies the last d day in the month.
+       // Month m is between 1 and 12.
+
+       // Month, day of month, day of week
+       // ZoneInfo extension, not in POSIX
+       // "Am.n.d".  d is between 0 (Sunday) and 6.  Day of month n is
+       // between 1 and 25.  Month m is between 1 and 12.
+
+       month = Integer.parseInt(date.substring(1, date.indexOf('.')));
+       int week = Integer.parseInt(date.substring(date.indexOf('.') + 1,
+                                                  date.lastIndexOf('.')));
+       int dayOfWeek = Integer.parseInt(date.substring(date.lastIndexOf('.')
+                                                       + 1));
+       dayOfWeek++; // Java day of week is one-based, Sunday is first day.
+
+       if (type == 2)
+         {
+           day = week;
+           dayOfWeek = -dayOfWeek;
+         }
+       else if (week == 5)
+         day = -1; // last day of month is -1 in java, 5 in TZ
+       else
+         {
+           // First day of week starting on or after.  For example,
+           // to specify the second Sunday of April, set month to
+           // APRIL, day-of-month to 8, and day-of-week to -SUNDAY.
+           day = (week - 1) * 7 + 1;
+           dayOfWeek = -dayOfWeek;
+         }
+
+       month--; // Java month is zero-based.
+       return new int[] { month, day, dayOfWeek };
+      }
+
+    // julian day, either zero-based 0<=n<=365 (incl feb 29)
+    // or one-based 1<=n<=365 (no feb 29)
+    int julianDay; // Julian day
+
+    if (date.charAt(0) != 'J' || date.charAt(0) != 'j')
+      {
+       julianDay = Integer.parseInt(date.substring(1));
+       julianDay++; // make 1-based
+       // Adjust day count to include feb 29.
+       dayCount = new int[]
+                  {
+                    0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335
+                  };
+      }
+    else
+      // 1-based julian day
+      julianDay = Integer.parseInt(date);
+
+    int i = 11;
+    while (i > 0)
+      if (dayCount[i] < julianDay)
+       break;
+      else
+       i--;
+    julianDay -= dayCount[i];
+    month = i;
+    return new int[] { month, julianDay, 0 };
+  }
+
+  /**
+   * Parses a time field hh[:mm[:ss]], returning the result
+   * in milliseconds. No leading sign.
+   */
+  private static int parseTime(String time)
+  {
+    int millis = 0;
+    int i = 0;
+
+    while (i < time.length())
+      if (time.charAt(i) == ':')
+       break;
+      else
+       i++;
+    millis = 60 * 60 * 1000 * Integer.parseInt(time.substring(0, i));
+    if (i >= time.length())
+      return millis;
+
+    int iprev = ++i;
+    while (i < time.length())
+      if (time.charAt(i) == ':')
+       break;
+      else
+       i++;
+    millis += 60 * 1000 * Integer.parseInt(time.substring(iprev, i));
+    if (i >= time.length())
+      return millis;
+
+    millis += 1000 * Integer.parseInt(time.substring(++i));
+    return millis;
+  }
+}
index 5c43bf3..f481753 100644 (file)
@@ -754,6 +754,7 @@ public class Date
          }
        else if (firstch >= '0' && firstch <= '9')
          {
+           int lastPunct = -1;
            while (tok != null && tok.length() > 0)
              {
                int punctOffset = tok.length();
@@ -791,6 +792,13 @@ public class Date
                    else
                      minute = num;
                  }
+               else if (lastPunct == ':' && hour >= 0 && (minute < 0 || second < 0))
+                 {
+                   if (minute < 0)
+                     minute = num;
+                   else
+                     second = num;
+                 }
                else if ((num >= 70
                          && (punct == ' ' || punct == ','
                              || punct == '/' || punct < 0))
@@ -828,6 +836,7 @@ public class Date
                  tok = null;
                else
                  tok = tok.substring(punctOffset + 1);
+               lastPunct = punct;
              }
          }
        else if (firstch >= 'A' && firstch <= 'Z')
index d94f89a..14821ba 100644 (file)
@@ -1,5 +1,6 @@
 /* java.util.SimpleTimeZone
-   Copyright (C) 1998, 1999, 2000, 2003, 2004, 2005  Free Software Foundation, Inc.
+   Copyright (C) 1998, 1999, 2000, 2003, 2004, 2005, 2007
+   Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -141,8 +142,8 @@ public class SimpleTimeZone extends TimeZone
 
   /**
    * This variable specifies the time of change to daylight savings.
-   * This time is given in milliseconds after midnight local
-   * standard time.
+   * This time is given in milliseconds after midnight in startTimeMode
+   * chosen time mode.
    * @serial
    */
   private int startTime;
@@ -187,8 +188,8 @@ public class SimpleTimeZone extends TimeZone
 
   /**
    * This variable specifies the time of change back to standard time.
-   * This time is given in milliseconds after midnight local
-   * standard time.
+   * This time is given in milliseconds after midnight in endTimeMode
+   * chosen time mode.
    * @serial
    */
   private int endTime;
@@ -380,24 +381,17 @@ public class SimpleTimeZone extends TimeZone
                         int endDayOfWeekInMonth, int endDayOfWeek,
                         int endTime, int endTimeMode, int dstSavings)
   {
-    this.rawOffset = rawOffset;
-    setID(id);
-    useDaylight = true;
+    this(rawOffset, id, startMonth, startDayOfWeekInMonth, startDayOfWeek,
+        startTime, endMonth, endDayOfWeekInMonth, endDayOfWeek, endTime);
 
     if (startTimeMode < WALL_TIME || startTimeMode > UTC_TIME)
       throw new IllegalArgumentException("startTimeMode must be one of WALL_TIME, STANDARD_TIME, or UTC_TIME");
     if (endTimeMode < WALL_TIME || endTimeMode > UTC_TIME)
       throw new IllegalArgumentException("endTimeMode must be one of WALL_TIME, STANDARD_TIME, or UTC_TIME");
-    this.startTimeMode = startTimeMode;
-    this.endTimeMode = endTimeMode;
-
-    setStartRule(startMonth, startDayOfWeekInMonth, startDayOfWeek, startTime);
-    setEndRule(endMonth, endDayOfWeekInMonth, endDayOfWeek, endTime);
-    if (startMonth == endMonth)
-      throw new IllegalArgumentException("startMonth and endMonth must be different");
-    this.startYear = 0;
 
     this.dstSavings = dstSavings;
+    this.startTimeMode = startTimeMode;
+    this.endTimeMode = endTimeMode;
   }
 
   /**
@@ -477,12 +471,8 @@ public class SimpleTimeZone extends TimeZone
     this.startMonth = month;
     this.startDay = day;
     this.startDayOfWeek = Math.abs(dayOfWeek);
-    if (this.startTimeMode == WALL_TIME || this.startTimeMode == STANDARD_TIME)
-      this.startTime = time;
-    else
-      // Convert from UTC to STANDARD
-      this.startTime = time + this.rawOffset;
-    useDaylight = true;
+    this.startTime = time;
+    this.startTimeMode = WALL_TIME;
   }
 
   /**
@@ -513,24 +503,10 @@ public class SimpleTimeZone extends TimeZone
   public void setStartRule(int month, int day, int dayOfWeek, int time,
                            boolean after)
   {
-    // FIXME: XXX: Validate that checkRule and offset processing work with on
-    // or before mode.
-    this.startDay = after ? Math.abs(day) : -Math.abs(day);
-    this.startDayOfWeek = after ? Math.abs(dayOfWeek) : -Math.abs(dayOfWeek);
-    this.startMode = (dayOfWeek != 0)
-                     ? (after ? DOW_GE_DOM_MODE : DOW_LE_DOM_MODE)
-                     : checkRule(month, day, dayOfWeek);
-    this.startDay = Math.abs(this.startDay);
-    this.startDayOfWeek = Math.abs(this.startDayOfWeek);
-
-    this.startMonth = month;
-
-    if (this.startTimeMode == WALL_TIME || this.startTimeMode == STANDARD_TIME)
-      this.startTime = time;
+    if (after)
+      setStartRule(month, day, -dayOfWeek, time);
     else
-      // Convert from UTC to STANDARD
-      this.startTime = time + this.rawOffset;
-    useDaylight = true;
+      setStartRule(month, -day, -dayOfWeek, time);
   }
 
   /**
@@ -570,14 +546,8 @@ public class SimpleTimeZone extends TimeZone
     this.endMonth = month;
     this.endDay = day;
     this.endDayOfWeek = Math.abs(dayOfWeek);
-    if (this.endTimeMode == WALL_TIME)
-      this.endTime = time;
-    else if (this.endTimeMode == STANDARD_TIME)
-      // Convert from STANDARD to DST
-      this.endTime = time + this.dstSavings;
-    else
-      // Convert from UTC to DST
-      this.endTime = time + this.rawOffset + this.dstSavings;
+    this.endTime = time;
+    this.endTimeMode = WALL_TIME;
     useDaylight = true;
   }
 
@@ -607,27 +577,10 @@ public class SimpleTimeZone extends TimeZone
   public void setEndRule(int month, int day, int dayOfWeek, int time,
                          boolean after)
   {
-    // FIXME: XXX: Validate that checkRule and offset processing work with on
-    // or before mode.
-    this.endDay = after ? Math.abs(day) : -Math.abs(day);
-    this.endDayOfWeek = after ? Math.abs(dayOfWeek) : -Math.abs(dayOfWeek);
-    this.endMode = (dayOfWeek != 0)
-                   ? (after ? DOW_GE_DOM_MODE : DOW_LE_DOM_MODE)
-                   : checkRule(month, day, dayOfWeek);
-    this.endDay = Math.abs(this.endDay);
-    this.endDayOfWeek = Math.abs(endDayOfWeek);
-
-    this.endMonth = month;
-
-    if (this.endTimeMode == WALL_TIME)
-      this.endTime = time;
-    else if (this.endTimeMode == STANDARD_TIME)
-      // Convert from STANDARD to DST
-      this.endTime = time + this.dstSavings;
+    if (after)
+      setEndRule(month, day, -dayOfWeek, time);
     else
-      // Convert from UTC to DST
-      this.endTime = time + this.rawOffset + this.dstSavings;
-    useDaylight = true;
+      setEndRule(month, -day, -dayOfWeek, time);
   }
 
   /**
@@ -688,16 +641,37 @@ public class SimpleTimeZone extends TimeZone
     int daylightSavings = 0;
     if (useDaylight && era == GregorianCalendar.AD && year >= startYear)
       {
+       int orig_year = year;
+       int time = startTime + (startTimeMode == UTC_TIME ? rawOffset : 0);
        // This does only work for Gregorian calendars :-(
        // This is mainly because setStartYear doesn't take an era.
        boolean afterStart = ! isBefore(year, month, day, dayOfWeek, millis,
                                        startMode, startMonth, startDay,
-                                       startDayOfWeek, startTime);
-       boolean beforeEnd = isBefore(year, month, day, dayOfWeek,
-                                    millis + dstSavings,
-                                    endMode, endMonth, endDay, endDayOfWeek,
-                                    endTime);
-
+                                       startDayOfWeek, time);
+       millis += dstSavings;
+       if (millis >= 24 * 60 * 60 * 1000)
+         {
+           millis -= 24 * 60 * 60 * 1000;
+           dayOfWeek = (dayOfWeek % 7) + 1;
+           if (++day > daysInMonth)
+             {
+               day = 1;
+               if (month++ == Calendar.DECEMBER)
+                 {
+                   month = Calendar.JANUARY;
+                   year++;
+                 }
+             }
+         }
+       time = endTime + (endTimeMode == UTC_TIME ? rawOffset : 0);
+       if (endTimeMode != WALL_TIME)
+         time += dstSavings;
+       boolean beforeEnd = isBefore(year, month, day, dayOfWeek, millis,
+                                    endMode, endMonth, endDay, endDayOfWeek,
+                                    time);
+
+       if (year != orig_year)
+         afterStart = false;
        if (startMonth < endMonth)
          // use daylight savings, if the date is after the start of
          // savings, and before the end of savings.
index a253561..cede9fc 100644 (file)
@@ -39,6 +39,9 @@ exception statement from your version. */
 
 package java.util;
 
+import gnu.classpath.SystemProperties;
+import gnu.java.util.ZoneInfo;
+import java.io.File;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.text.DateFormatSymbols;
@@ -115,7 +118,7 @@ public abstract class TimeZone implements java.io.Serializable, Cloneable
                
                // Fall back on GMT.
                if (zone == null)
-                 zone = (TimeZone) timezones().get("GMT");
+                 zone = getTimeZone ("GMT");
                
                return zone;
              }
@@ -128,6 +131,22 @@ public abstract class TimeZone implements java.io.Serializable, Cloneable
   private static final long serialVersionUID = 3581463369166924961L;
 
   /**
+   * Flag whether zoneinfo data should be used,
+   * otherwise builtin timezone data will be provided.
+   */
+  private static String zoneinfo_dir;
+
+  /**
+   * Cached copy of getAvailableIDs().
+   */
+  private static String[] availableIDs = null;
+
+  /**
+   * JDK 1.1.x compatibility aliases.
+   */
+  private static HashMap aliases0;
+
+  /**
    * HashMap for timezones by ID.  
    */
   private static HashMap timezones0;
@@ -135,13 +154,55 @@ public abstract class TimeZone implements java.io.Serializable, Cloneable
    * it is not needed: 
    */
   // Package-private to avoid a trampoline.
-  static synchronized HashMap timezones()
+  static HashMap timezones()
   {
     if (timezones0 == null) 
       {
        HashMap timezones = new HashMap();
        timezones0 = timezones;
 
+       zoneinfo_dir = SystemProperties.getProperty("gnu.java.util.zoneinfo.dir");
+       if (zoneinfo_dir != null && !new File(zoneinfo_dir).isDirectory())
+         zoneinfo_dir = null;
+
+       if (zoneinfo_dir != null)
+         {
+           aliases0 = new HashMap();
+
+           // These deprecated aliases for JDK 1.1.x compatibility
+           // should take precedence over data files read from
+           // /usr/share/zoneinfo.
+           aliases0.put("ACT", "Australia/Darwin");
+           aliases0.put("AET", "Australia/Sydney");
+           aliases0.put("AGT", "America/Argentina/Buenos_Aires");
+           aliases0.put("ART", "Africa/Cairo");
+           aliases0.put("AST", "America/Juneau");
+           aliases0.put("BST", "Asia/Colombo");
+           aliases0.put("CAT", "Africa/Gaborone");
+           aliases0.put("CNT", "America/St_Johns");
+           aliases0.put("CST", "CST6CDT");
+           aliases0.put("CTT", "Asia/Brunei");
+           aliases0.put("EAT", "Indian/Comoro");
+           aliases0.put("ECT", "CET");
+           aliases0.put("EST", "EST5EDT");
+           aliases0.put("EST5", "EST5EDT");
+           aliases0.put("IET", "EST5EDT");
+           aliases0.put("IST", "Asia/Calcutta");
+           aliases0.put("JST", "Asia/Seoul");
+           aliases0.put("MIT", "Pacific/Niue");
+           aliases0.put("MST", "MST7MDT");
+           aliases0.put("MST7", "MST7MDT");
+           aliases0.put("NET", "Indian/Mauritius");
+           aliases0.put("NST", "Pacific/Auckland");
+           aliases0.put("PLT", "Indian/Kerguelen");
+           aliases0.put("PNT", "MST7MDT");
+           aliases0.put("PRT", "America/Anguilla");
+           aliases0.put("PST", "PST8PDT");
+           aliases0.put("SST", "Pacific/Ponape");
+           aliases0.put("VST", "Asia/Bangkok");
+           return timezones;
+         }
+
        TimeZone tz;
        // Automatically generated by scripts/timezones.pl
        // XXX - Should we read this data from a file?
@@ -887,7 +948,6 @@ public abstract class TimeZone implements java.io.Serializable, Cloneable
   static TimeZone getDefaultTimeZone(String sysTimeZoneId)
   {
     String stdName = null;
-    String dstName;
     int stdOffs;
     int dstOffs;
     try
@@ -900,14 +960,14 @@ public abstract class TimeZone implements java.io.Serializable, Cloneable
 
        // get std
        do
-         c = sysTimeZoneId.charAt(index++);
+         c = sysTimeZoneId.charAt(index);
        while (c != '+' && c != '-' && c != ',' && c != ':'
-              && ! Character.isDigit(c) && c != '\0' && index < idLength);
+              && ! Character.isDigit(c) && c != '\0' && ++index < idLength);
 
        if (index >= idLength)
-         return (TimeZone)timezones().get(sysTimeZoneId);
+         return getTimeZoneInternal(sysTimeZoneId);
 
-       stdName = sysTimeZoneId.substring(0, --index);
+       stdName = sysTimeZoneId.substring(0, index);
        prevIndex = index;
 
        // get the std offset
@@ -938,7 +998,7 @@ public abstract class TimeZone implements java.io.Serializable, Cloneable
        if (index >= idLength)
          {
            // Do we have an existing timezone with that name and offset?
-           TimeZone tz = (TimeZone) timezones().get(stdName);
+           TimeZone tz = getTimeZoneInternal(stdName);
            if (tz != null)
              if (tz.getRawOffset() == stdOffs)
                return tz;
@@ -949,16 +1009,16 @@ public abstract class TimeZone implements java.io.Serializable, Cloneable
 
        // get dst
        do
-         c = sysTimeZoneId.charAt(index++);
+         c = sysTimeZoneId.charAt(index);
        while (c != '+' && c != '-' && c != ',' && c != ':'
-              && ! Character.isDigit(c) && c != '\0' && index < idLength);
+              && ! Character.isDigit(c) && c != '\0' && ++index < idLength);
 
        // Done yet? (Format: std offset dst)
        if (index >= idLength)
          {
            // Do we have an existing timezone with that name and offset 
            // which has DST?
-           TimeZone tz = (TimeZone) timezones().get(stdName);
+           TimeZone tz = getTimeZoneInternal(stdName);
            if (tz != null)
              if (tz.getRawOffset() == stdOffs && tz.useDaylightTime())
                return tz;
@@ -968,7 +1028,6 @@ public abstract class TimeZone implements java.io.Serializable, Cloneable
          }
 
        // get the dst offset
-       dstName = sysTimeZoneId.substring(prevIndex, --index);
        prevIndex = index;
        do
          c = sysTimeZoneId.charAt(index++);
@@ -1005,7 +1064,7 @@ public abstract class TimeZone implements java.io.Serializable, Cloneable
        if (index >= idLength)
          {
            // Time Zone existing with same name, dst and offsets?
-           TimeZone tz = (TimeZone) timezones().get(stdName);
+           TimeZone tz = getTimeZoneInternal(stdName);
            if (tz != null)
              if (tz.getRawOffset() == stdOffs && tz.useDaylightTime()
                  && tz.getDSTSavings() == (dstOffs - stdOffs))
@@ -1171,10 +1230,10 @@ public abstract class TimeZone implements java.io.Serializable, Cloneable
        break;
       else
        i++;
+    millis += 60 * 1000 * Integer.parseInt(time.substring(iprev, i));
     if (i >= time.length())
       return millis;
 
-    millis += 60 * 1000 * Integer.parseInt(time.substring(iprev, i));
     millis += 1000 * Integer.parseInt(time.substring(++i));
     return millis;
   }
@@ -1406,30 +1465,67 @@ public abstract class TimeZone implements java.io.Serializable, Cloneable
    * @return The time zone for the identifier or GMT, if no such time
    * zone exists.
    */
-  // FIXME: XXX: JCL indicates this and other methods are synchronized.
-  public static TimeZone getTimeZone(String ID)
+  private static TimeZone getTimeZoneInternal(String ID)
   {
     // First check timezones hash
-    TimeZone tz = (TimeZone) timezones().get(ID);
-    if (tz != null)
+    TimeZone tz = null;
+    TimeZone tznew = null;
+    for (int pass = 0; pass < 2; pass++)
       {
-       if (tz.getID().equals(ID))
-         return tz;
-
-       // We always return a timezone with the requested ID.
-       // This is the same behaviour as with JDK1.2.
-       tz = (TimeZone) tz.clone();
-       tz.setID(ID);
-       // We also save the alias, so that we return the same
-       // object again if getTimeZone is called with the same
-       // alias.
-       timezones().put(ID, tz);
-       return tz;
+       synchronized (TimeZone.class)
+         {
+           tz = (TimeZone) timezones().get(ID);
+           if (tz != null)
+             {
+               if (!tz.getID().equals(ID))
+                 {
+                   // We always return a timezone with the requested ID.
+                   // This is the same behaviour as with JDK1.2.
+                   tz = (TimeZone) tz.clone();
+                   tz.setID(ID);
+                   // We also save the alias, so that we return the same
+                   // object again if getTimeZone is called with the same
+                   // alias.
+                   timezones().put(ID, tz);
+                 }
+               return tz;
+             }
+           else if (tznew != null)
+             {
+               timezones().put(ID, tznew);
+               return tznew;
+             }
+         }
+
+       if (pass == 1 || zoneinfo_dir == null)
+         return null;
+
+       // aliases0 is never changing after first timezones(), so should
+       // be safe without synchronization.
+       String zonename = (String) aliases0.get(ID);
+       if (zonename == null)
+         zonename = ID;
+
+       // Read the file outside of the critical section, it is expensive.
+       tznew = ZoneInfo.readTZFile (ID, zoneinfo_dir
+                                    + File.separatorChar + zonename);
+       if (tznew == null)
+         return null;
       }
 
-    // See if the ID is really a GMT offset form.
-    // Note that GMT is in the table so we know it is different.
-    if (ID.startsWith("GMT"))
+    return null;
+  }
+
+  /**
+   * Gets the TimeZone for the given ID.
+   * @param ID the time zone identifier.
+   * @return The time zone for the identifier or GMT, if no such time
+   * zone exists.
+   */
+  public static TimeZone getTimeZone(String ID)
+  {
+    // Check for custom IDs first
+    if (ID.startsWith("GMT") && ID.length() > 3)
       {
        int pos = 3;
        int offset_direction = 1;
@@ -1474,8 +1570,20 @@ public abstract class TimeZone implements java.io.Serializable, Cloneable
                  }
              }
 
-           return new SimpleTimeZone((hour * (60 * 60 * 1000) +
-                                      minute * (60 * 1000))
+           // Custom IDs have to be normalized
+           StringBuffer sb = new StringBuffer(9);
+           sb.append("GMT");
+
+           sb.append(offset_direction >= 0 ? '+' : '-');
+           sb.append((char) ('0' + hour / 10));
+           sb.append((char) ('0' + hour % 10));
+           sb.append(':');
+           sb.append((char) ('0' + minute / 10));
+           sb.append((char) ('0' + minute % 10));
+           ID = sb.toString();
+
+           return new SimpleTimeZone((hour * (60 * 60 * 1000)
+                                      + minute * (60 * 1000))
                                      * offset_direction, ID);
          }
        catch (NumberFormatException e)
@@ -1483,8 +1591,11 @@ public abstract class TimeZone implements java.io.Serializable, Cloneable
          }
       }
 
-    // Finally, return GMT per spec
-    return getTimeZone("GMT");
+    TimeZone tz = getTimeZoneInternal(ID);
+    if (tz != null)
+      return tz;
+
+    return new SimpleTimeZone(0, "GMT");
   }
 
   /**
@@ -1497,37 +1608,134 @@ public abstract class TimeZone implements java.io.Serializable, Cloneable
    */
   public static String[] getAvailableIDs(int rawOffset)
   {
+    synchronized (TimeZone.class)
+      {
+       HashMap h = timezones();
+       int count = 0;
+       if (zoneinfo_dir == null)
+         {
+           Iterator iter = h.entrySet().iterator();
+           while (iter.hasNext())
+             {
+               // Don't iterate the values, since we want to count
+               // doubled values (aliases)
+               Map.Entry entry = (Map.Entry) iter.next();
+               if (((TimeZone) entry.getValue()).getRawOffset() == rawOffset)
+                 count++;
+             }
+
+           String[] ids = new String[count];
+           count = 0;
+           iter = h.entrySet().iterator();
+           while (iter.hasNext())
+             {
+               Map.Entry entry = (Map.Entry) iter.next();
+               if (((TimeZone) entry.getValue()).getRawOffset() == rawOffset)
+                 ids[count++] = (String) entry.getKey();
+             }
+           return ids;
+         }
+      }
+
+    String[] s = getAvailableIDs();
     int count = 0;
-    Iterator iter = timezones().entrySet().iterator();
-    while (iter.hasNext())
+    for (int i = 0; i < s.length; i++)
       {
-       // Don't iterate the values, since we want to count 
-       // doubled values (aliases)
-       Map.Entry entry = (Map.Entry) iter.next();
-       if (((TimeZone) entry.getValue()).getRawOffset() == rawOffset)
+       TimeZone t = getTimeZoneInternal(s[i]);
+       if (t == null || t.getRawOffset() != rawOffset)
+         s[i] = null;
+       else
          count++;
       }
-
     String[] ids = new String[count];
     count = 0;
-    iter = timezones().entrySet().iterator();
-    while (iter.hasNext())
-      {
-       Map.Entry entry = (Map.Entry) iter.next();
-       if (((TimeZone) entry.getValue()).getRawOffset() == rawOffset)
-         ids[count++] = (String) entry.getKey();
-      }
+    for (int i = 0; i < s.length; i++)
+    if (s[i] != null)
+      ids[count++] = s[i];
+
     return ids;
   }
 
+  private static int getAvailableIDs(File d, String prefix, ArrayList list)
+    {
+      String[] files = d.list();
+      int count = files.length;
+      boolean top = prefix.length() == 0;
+      list.add (files);
+      for (int i = 0; i < files.length; i++)
+       {
+         if (top
+             && (files[i].equals("posix")
+                 || files[i].equals("right")
+                 || files[i].endsWith(".tab")
+                 || aliases0.get(files[i]) != null))
+           {
+             files[i] = null;
+             count--;
+             continue;
+           }
+
+         File f = new File(d, files[i]);
+         if (f.isDirectory())
+           {
+             count += getAvailableIDs(f, prefix + files[i]
+                                      + File.separatorChar, list) - 1;
+             files[i] = null;
+           }
+         else
+           files[i] = prefix + files[i];
+       }
+      return count;
+    }
+
   /**
    * Gets all available IDs.
    * @return An array of all supported IDs.
    */
   public static String[] getAvailableIDs()
   {
-    return (String[])
-      timezones().keySet().toArray(new String[timezones().size()]);
+    synchronized (TimeZone.class)
+      {
+       HashMap h = timezones();
+       if (zoneinfo_dir == null)
+         return (String[]) h.keySet().toArray(new String[h.size()]);
+
+       if (availableIDs != null)
+         {
+           String[] ids = new String[availableIDs.length];
+           for (int i = 0; i < availableIDs.length; i++)
+             ids[i] = availableIDs[i];
+           return ids;
+         }
+
+       File d = new File(zoneinfo_dir);
+       ArrayList list = new ArrayList(30);
+       int count = getAvailableIDs(d, "", list) + aliases0.size();
+       availableIDs = new String[count];
+       String[] ids = new String[count];
+
+       count = 0;
+       for (int i = 0; i < list.size(); i++)
+         {
+           String[] s = (String[]) list.get(i);
+           for (int j = 0; j < s.length; j++)
+             if (s[j] != null)
+               {
+                 availableIDs[count] = s[j];
+                 ids[count++] = s[j];
+               }
+         }
+
+       Iterator iter = aliases0.entrySet().iterator();
+       while (iter.hasNext())
+         {
+           Map.Entry entry = (Map.Entry) iter.next();
+           availableIDs[count] = (String) entry.getKey();
+           ids[count++] = (String) entry.getKey();
+         }
+
+       return ids;
+      }
   }
 
   /**
diff --git a/libjava/classpath/lib/gnu/java/util/ZoneInfo.class b/libjava/classpath/lib/gnu/java/util/ZoneInfo.class
new file mode 100644 (file)
index 0000000..3ff3706
Binary files /dev/null and b/libjava/classpath/lib/gnu/java/util/ZoneInfo.class differ
index 2ff812b..c6aebaa 100644 (file)
Binary files a/libjava/classpath/lib/java/util/Date.class and b/libjava/classpath/lib/java/util/Date.class differ
index 7009b48..1506330 100644 (file)
Binary files a/libjava/classpath/lib/java/util/SimpleTimeZone.class and b/libjava/classpath/lib/java/util/SimpleTimeZone.class differ
index fdc1c61..08d8bd2 100644 (file)
Binary files a/libjava/classpath/lib/java/util/TimeZone$1.class and b/libjava/classpath/lib/java/util/TimeZone$1.class differ
index ca7db2b..556c26d 100644 (file)
Binary files a/libjava/classpath/lib/java/util/TimeZone.class and b/libjava/classpath/lib/java/util/TimeZone.class differ
index a175a44..f2f9fa9 100644 (file)
Binary files a/libjava/classpath/lib/java/util/VMTimeZone.class and b/libjava/classpath/lib/java/util/VMTimeZone.class differ
diff --git a/libjava/gnu/java/util/ZoneInfo.h b/libjava/gnu/java/util/ZoneInfo.h
new file mode 100644 (file)
index 0000000..83a0bf8
--- /dev/null
@@ -0,0 +1,70 @@
+
+// DO NOT EDIT THIS FILE - it is machine generated -*- c++ -*-
+
+#ifndef __gnu_java_util_ZoneInfo__
+#define __gnu_java_util_ZoneInfo__
+
+#pragma interface
+
+#include <java/util/TimeZone.h>
+#include <gcj/array.h>
+
+extern "Java"
+{
+  namespace gnu
+  {
+    namespace java
+    {
+      namespace util
+      {
+          class ZoneInfo;
+      }
+    }
+  }
+}
+
+class gnu::java::util::ZoneInfo : public ::java::util::TimeZone
+{
+
+public:
+  ZoneInfo(jint, ::java::lang::String *, JArray< jlong > *, ::java::util::SimpleTimeZone *);
+  virtual jint getOffset(jint, jint, jint, jint, jint, jint);
+private:
+  jlong findTransition(jlong);
+public:
+  virtual jint getOffset(jlong);
+  virtual jint getRawOffset();
+  virtual void setRawOffset(jint);
+private:
+  void computeDSTSavings();
+public:
+  virtual jint getDSTSavings();
+  virtual jboolean useDaylightTime();
+  virtual jboolean inDaylightTime(::java::util::Date *);
+  virtual jint hashCode();
+  virtual jboolean equals(::java::lang::Object *);
+  virtual jboolean hasSameRules(::java::util::TimeZone *);
+  virtual ::java::lang::String * toString();
+  static ::java::util::TimeZone * readTZFile(::java::lang::String *, ::java::lang::String *);
+private:
+  static void skipFully(::java::io::InputStream *, jlong);
+  static ::java::util::SimpleTimeZone * createLastRule(::java::lang::String *);
+  static JArray< jint > * getDateParams(::java::lang::String *);
+  static jint parseTime(::java::lang::String *);
+  static const jint SECS_SHIFT = 22;
+  static const jlong OFFSET_MASK = 2097151LL;
+  static const jint OFFSET_SHIFT = 43;
+  static const jlong IS_DST = 2097152LL;
+  jint __attribute__((aligned(__alignof__( ::java::util::TimeZone)))) rawOffset;
+  jint dstSavings;
+  jboolean useDaylight;
+  JArray< jlong > * transitions;
+  ::java::util::SimpleTimeZone * lastRule;
+  static ::java::util::SimpleTimeZone * gmtZone;
+public: // actually package-private
+  static const jlong serialVersionUID = -3740626706860383657LL;
+public:
+  static ::java::lang::Class class$;
+};
+
+#endif // __gnu_java_util_ZoneInfo__
index 587e637..76a39f0 100644 (file)
@@ -1,5 +1,5 @@
 /* System.java -- useful methods to interface with the system
-   Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+   Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
    Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
@@ -318,6 +318,7 @@ public final class System
    * <dt>gnu.java.io.encoding_scheme_alias.latin?</dt>       <dd>8859_?</dd>
    * <dt>gnu.java.io.encoding_scheme_alias.UTF-8</dt>        <dd>UTF8</dd>
    * <dt>gnu.java.io.encoding_scheme_alias.utf-8</dt>        <dd>UTF8</dd>
+   * <dt>gnu.java.util.zoneinfo.dir</dt>       <dd>Root of zoneinfo tree</dd>
    * </dl>
    *
    * @return the system properties, will never be null
index 3eb30ad..9ae0ebc 100644 (file)
@@ -40,8 +40,14 @@ public:
   virtual jboolean useDaylightTime() = 0;
   virtual jboolean inDaylightTime(::java::util::Date *) = 0;
   virtual jint getDSTSavings();
+private:
+  static ::java::util::TimeZone * getTimeZoneInternal(::java::lang::String *);
+public:
   static ::java::util::TimeZone * getTimeZone(::java::lang::String *);
   static JArray< ::java::lang::String * > * getAvailableIDs(jint);
+private:
+  static jint getAvailableIDs(::java::io::File *, ::java::lang::String *, ::java::util::ArrayList *);
+public:
   static JArray< ::java::lang::String * > * getAvailableIDs();
   static ::java::util::TimeZone * getDefault();
   static void setDefault(::java::util::TimeZone *);
@@ -53,6 +59,9 @@ private:
   ::java::lang::String * __attribute__((aligned(__alignof__( ::java::lang::Object)))) ID;
   static ::java::util::TimeZone * defaultZone0;
   static const jlong serialVersionUID = 3581463369166924961LL;
+  static ::java::lang::String * zoneinfo_dir;
+  static JArray< ::java::lang::String * > * availableIDs;
+  static ::java::util::HashMap * aliases0;
   static ::java::util::HashMap * timezones0;
 public:
   static ::java::lang::Class class$;
index 6e57114..26ca5e2 100644 (file)
@@ -16,8 +16,7 @@ public: // actually package-private
   static ::java::util::TimeZone * getDefaultTimeZoneId();
 private:
   static ::java::lang::String * readTimeZoneFile(::java::lang::String *);
-  static ::java::lang::String * readtzFile(::java::lang::String *);
-  static void skipFully(::java::io::InputStream *, jlong);
+  static ::java::lang::String * readSysconfigClockFile(::java::lang::String *);
   static ::java::lang::String * getSystemTimeZoneId();
 public:
   static ::java::lang::Class class$;
index 27bab93..992ecaf 100644 (file)
@@ -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.
index df798b8..5d64094 100644 (file)
@@ -139,6 +139,10 @@ _Jv_platform_initProperties (java::util::Properties* newprops)
   if (! tmpdir)
     tmpdir = "/tmp";
   SET ("java.io.tmpdir", tmpdir);
+  const char *zoneinfodir = ::getenv("TZDATA");
+  if (! zoneinfodir)
+    zoneinfodir = "/usr/share/zoneinfo";
+  SET ("gnu.java.util.zoneinfo.dir", zoneinfodir);
 }
 
 static inline void
index 01618ce..77e7796 100644 (file)
@@ -2110,7 +2110,8 @@ gnu/java/text.list: $(gnu_java_text_source_files)
 gnu_java_util_source_files = \
 classpath/gnu/java/util/DoubleEnumeration.java \
 classpath/gnu/java/util/EmptyEnumeration.java \
-classpath/gnu/java/util/WeakIdentityHashMap.java
+classpath/gnu/java/util/WeakIdentityHashMap.java \
+classpath/gnu/java/util/ZoneInfo.java
 
 gnu_java_util_header_files = $(patsubst classpath/%,%,$(patsubst %.java,%.h,$(gnu_java_util_source_files)))