This is the mail archive of the java-patches@gcc.gnu.org mailing list for the Java project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

TimeZone


Hi,

Our TimeZone support is not optimal and a little fragile. See the
TimeZone meta-bug http://gcc.gnu.org/bugzilla/show_bug.cgi?id=16990

This patch tries to solve issues PR17002
"java.util.TimeZone.getDefault() is broken".

TimeZone.getDefault() has two bugs mapping short timezone names (plus
GMT-offset) to long/display timezone information.

- The interface between getDefault() and the native getDefaultTimeZoneId() is
unclear. Which means that the result of getDefaultTimeZoneId() is not always
mapped to the correct TimeZone.

- getDefaultTimeZoneId() should always return the standard (std) short timezone
name, never the alternative (dst) Daylight Savings Time name. When it returns a
dst name the getDefault() mapping from short name to long/display timezone name
also breaks.

Since using "standard" methods to get at the time zone and gmt offset is
fragile the patch also introduces some other ways to get at the timezone
data/name. First (as also done in the past) it tries to use the System
property "user.timezone", then the environment variable "TZ", then it
tries to read the /etc/timezone, then it tries to parse the
/etc/localtime (tz)file and finally, if all else fails, it uses the
(rewritten) native platform specific getDefaultTimeZoneId() function.

In the end we might just want to adopt the Olson tz data timezone
package completely as glibc does. Unfortunately neither the tzcode nor
glibc has any public interface to access all the available data if it is
available on the system. But the new readtzFile() method might be a
start to get our own parser for this data.

2004-08-22  Mark Wielaard  <mark@klomp.org>

       * java/util/TimeZone.java (defaultZone): Try a couple of ways to get
       a TimeZoneId string and then try to convert that to a TimeZone with
       getDefaultSystemTimeZone(String).
       (timezones0): Changed type from Hashtable to HashMap.
       (timezones): Create HashMap, not Hashtable.
       (getDefaultTimeZone): New method, rewritten from CNI version.
       (readTimeZoneFile): New method.
       (readtzFile): Likewise.
       (skipFully): Likewise.
       * java/util/natTimeZone.cc (getSystemTimeZone): Renamed to
       getDefaultTimeZoneId and rewritten.
       (getDefaultTimeZoneId): Rewritten in java.

OK to commit?

If this works out on all platforms I like to restructure it to refactor
out the pieces that should go into a VMTimeZone class so it can be
adopted by GNU Classpath upstream.

Cheers,

Mark
Index: java/util/TimeZone.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/util/TimeZone.java,v
retrieving revision 1.18
diff -u -r1.18 TimeZone.java
--- java/util/TimeZone.java	23 Apr 2004 06:36:04 -0000	1.18
+++ java/util/TimeZone.java	22 Aug 2004 13:42:40 -0000
@@ -40,6 +40,9 @@
 package java.util;
 import gnu.classpath.Configuration;
 
+import java.io.*;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 import java.text.DateFormatSymbols;
 
 /**
@@ -83,44 +86,99 @@
    * The default time zone, as returned by getDefault.
    */
   private static TimeZone defaultZone0;
-  /* initialize this static field lazily to overhead if
-   * it is not needed: 
+
+  /**
+   * Tries to get the default TimeZone for this system if not already
+   * set.  It will call <code>getDefaultTimeZone(String)</code> with
+   * the result of
+   * <code>System.getProperty("user.timezone")</code>,
+   * <code>System.getenv("TZ")</code>,
+   * <code>readTimeZoneFile("/etc/timezone")</code>,
+   * <code>readtzFile("/etc/localtime")</code> and
+   * <code>getDefaultTimeZoneId()</code>
+   * till a supported TimeZone is found.
+   * If every method fails GMT is returned.
    */
-  private static synchronized TimeZone defaultZone() {
+  private static synchronized TimeZone defaultZone()
+  {
     /* Look up default timezone */
     if (defaultZone0 == null) 
       {
-	if (Configuration.INIT_LOAD_LIBRARY)
-	  {
-	    System.loadLibrary("javautil");
-	  }
-	String tzid = System.getProperty("user.timezone");
-	
-	if (tzid == null)
-	  tzid = getDefaultTimeZoneId();
-	
-	if (tzid == null)
-	  tzid = "GMT";
-	
-	defaultZone0 = getTimeZone(tzid);
+	defaultZone0 = (TimeZone) AccessController.doPrivileged
+	  (new PrivilegedAction()
+	    {
+	      public Object run()
+	      {
+		if (Configuration.INIT_LOAD_LIBRARY)
+		  {
+		    System.loadLibrary("javautil");
+		  }
+		
+		TimeZone zone = null;
+		
+		// Prefer System property user.timezone.
+		String tzid = System.getProperty("user.timezone");
+		if (tzid != null && !tzid.equals(""))
+		  zone = getDefaultTimeZone(tzid);
+		
+		// See if TZ environment variable is set and accessible.
+		if (zone == null)
+		  {
+		    tzid = System.getenv("TZ");
+		    if (tzid != null && !tzid.equals(""))
+		      zone = getDefaultTimeZone(tzid);
+		  }
+		
+		// Try to parse /etc/timezone.
+		if (zone == null)
+		  {
+		    tzid = readTimeZoneFile("/etc/timezone");
+		    if (tzid != null && !tzid.equals(""))
+		      zone = getDefaultTimeZone(tzid);
+		  }
+		
+		// Try to parse /etc/localtime
+		if (zone == null)
+		  {
+		    tzid = readtzFile("/etc/localtime");
+		    if (tzid != null && !tzid.equals(""))
+		      zone = getDefaultTimeZone(tzid);
+		  }
+		
+		// Try some system specific way
+		if (zone == null)
+		  {
+		    tzid = getDefaultTimeZoneId();
+		    if (tzid != null && !tzid.equals(""))
+		      zone = getDefaultTimeZone(tzid);
+		  }
+		
+		// Fall back on GMT.
+		if (zone == null)
+		  zone = (TimeZone) timezones().get("GMT");
+		
+		return zone;
+	      }
+	    });
       }
+    
     return defaultZone0; 
   }
-
-
+  
   private static final long serialVersionUID = 3581463369166924961L;
 
   /**
-   * Hashtable for timezones by ID.  
+   * HashMap for timezones by ID.  
    */
-  private static Hashtable timezones0;
+  private static HashMap timezones0;
   /* initialize this static field lazily to overhead if
    * it is not needed: 
    */
-  private static synchronized Hashtable timezones() {
-    if (timezones0==null) 
+  private static synchronized HashMap timezones()
+  {
+    if (timezones0 == null) 
       {
-	Hashtable timezones = new Hashtable();
+	HashMap timezones = new HashMap();
 	timezones0 = timezones;
 
 	TimeZone tz;
@@ -784,19 +842,337 @@
     return timezones0;
   }
 
-
-  /* This method returns us a time zone id string which is in the
-     form <standard zone name><GMT offset><daylight time zone name>.
-     The GMT offset is in seconds, except where it is evenly divisible
-     by 3600, then it is in hours.  If the zone does not observe
-     daylight time, then the daylight zone name is omitted.  Examples:
-     in Chicago, the timezone would be CST6CDT.  In Indianapolis 
-     (which does not have Daylight Savings Time) the string would
-     be EST5
+  /**
+   * This method returns a time zone id string which is in the form
+   * (standard zone name) or (standard zone name)(GMT offset) or
+   * (standard zone name)(GMT offset)(daylight time zone name).  The
+   * GMT offset can be in seconds, or where it is evenly divisible by
+   * 3600, then it can be in hours.  The offset must be the time to
+   * add to the local time to get GMT.  If a offset is given and the
+   * time zone observes daylight saving then the (daylight time zone
+   * name) must also be given (otherwise it is assumed the time zone
+   * does not observe any daylight savings).
+   * <p>
+   * The result of this method is given to getDefaultTimeZone(String)
+   * which tries to map the time zone id to a known TimeZone.  See
+   * that method on how the returned String is mapped to a real
+   * TimeZone object.
    */
   private static native String getDefaultTimeZoneId();
 
   /**
+   * Tries to read the time zone name from a file. Only the first
+   * consecutive letters, digits, slashes, dashes and underscores are
+   * read from the file. If the file cannot be read or an IOException
+   * occurs null is returned.
+   */
+  private static String readTimeZoneFile(String file)
+  {
+    InputStreamReader isr = null;
+    try
+      {
+        FileInputStream fis = new FileInputStream(file);
+        BufferedInputStream bis = new BufferedInputStream(fis);
+        isr = new InputStreamReader(bis);
+	
+        StringBuffer sb = new StringBuffer();
+        int i = isr.read();
+        while (i != -1)
+          {
+            char c = (char) i;
+            if (Character.isLetter(c) || Character.isDigit(c)
+                || c == '/' || c == '-' || c == '_')
+              {
+                sb.append(c);
+                i = isr.read();
+              }
+            else
+              break;
+          }
+        return sb.toString();
+      }
+    catch (IOException ioe)
+      {
+        return null;
+      }
+    finally
+      {
+        try
+          {
+            if (isr != null)
+              isr.close();
+          }
+        catch (IOException ioe)
+          {
+            //ignored
+          }
+      }
+  }
+
+  /**
+   * Tries to read a file as a "standard" tzfile and return a time
+   * zone id string as expected by <code>getDefaultTimeZone(String)</code>.
+   * If the file doesn't exist, an IOException occurs or it isn't a tzfile
+   * that can be parsed null is returned.
+   * <p>
+   * The tzfile structure (as also used by glibc) is described in the Olson
+   * tz database archive as can be found at
+   * <code>ftp://elsie.nci.nih.gov/pub/</code>.
+   */
+  private static String readtzFile(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[4];
+        dis.readFully(tzif);
+        if (tzif[0] == 'T' && tzif[1] == 'Z'
+            && tzif[2] == 'i' && tzif[3] == 'f')
+          {
+            // Reserved bytes, ttisgmtcnt, ttisstdcnt and leapcnt
+            skipFully(dis, 16 + 3 * 4);
+	    
+            int timecnt = dis.readInt();
+            int typecnt = dis.readInt();
+            if (typecnt > 0)
+              {
+                int charcnt = dis.readInt();
+                // Transition times plus indexed transition times.
+                skipFully(dis, timecnt * (4 + 1));
+		
+                // Get last gmt_offset and dst/non-dst time zone names.
+                int abbrind = -1;
+                int dst_abbrind = -1;
+                int gmt_offset = 0;
+                while (typecnt-- > 0)
+                  {
+                    // gmtoff
+                    int offset = dis.readInt();
+                    int dst = dis.readByte();
+                    if (dst == 0)
+                      {
+                        abbrind = dis.readByte();
+                        gmt_offset = offset;
+                      }
+                    else
+                      dst_abbrind = dis.readByte();
+                  }
+		
+                // gmt_offset is the offset you must add to UTC/GMT to
+                // get the local time, we need the offset to add to
+                // the local time to get UTC/GMT.
+                gmt_offset *= -1;
+		
+                // Turn into hours if possible.
+                if (gmt_offset % 3600 == 0)
+                  gmt_offset /= 3600;
+		
+                if (abbrind >= 0)
+                  {
+                    byte[] names = new byte[charcnt];
+                    dis.readFully(names);
+                    int j = abbrind;
+                    while (j < charcnt && names[j] != 0)
+                      j++;
+		    
+                    String zonename = new String(names, abbrind, j - abbrind,
+                                                 "ASCII");
+		    
+                    String dst_zonename;
+                    if (dst_abbrind >= 0)
+                      {
+                        j = dst_abbrind;
+                        while (j < charcnt && names[j] != 0)
+                          j++;
+                        dst_zonename = new String(names, dst_abbrind,
+                                                  j - dst_abbrind, "ASCII");
+                      }
+                    else
+                      dst_zonename = "";
+		    
+                    // Only use gmt offset when necessary.
+                    // Also special case GMT+/- timezones.
+                    String offset_string;
+                    if ("".equals(dst_zonename)
+                        && (gmt_offset == 0
+                            || zonename.startsWith("GMT+")
+                            || zonename.startsWith("GMT-")))
+                      offset_string = "";
+                    else
+                      offset_string = Integer.toString(gmt_offset);
+		    
+                    String id = zonename + offset_string + dst_zonename;
+		    
+                    return id;
+                  }
+              }
+          }
+	
+        // Something didn't match while reading the file.
+        return null;
+      }
+    catch (IOException ioe)
+      {
+        return null;
+      }
+    finally
+      {
+        try
+          {
+            if (dis != null)
+              dis.close();
+          }
+        catch(IOException ioe)
+          {
+            // Ignored
+          }
+      }
+  }
+  
+  /**
+   * 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;
+      }
+  }
+  
+  /**
+   * Maps a time zone name (with optional GMT offset and daylight time
+   * zone name) to one of the known time zones.  This method called
+   * with the result of <code>System.getProperty("user.timezone")</code>
+   * or <code>getDefaultTimeZoneId()</code>.  Note that giving one of
+   * the standard tz data names from ftp://elsie.nci.nih.gov/pub/ is
+   * preferred.  The time zone name can be given as follows:
+   * <code>(standard zone name)[(GMT offset)[(daylight time zone name)]]</code>
+   * <p>
+   * If only a (standard zone name) is given (no numbers in the
+   * String) then it gets mapped directly to the TimeZone with that
+   * name, if that fails null is returned.
+   * <p>
+   * A GMT offset is the offset to add to the local time to get GMT.
+   * If a (GMT offset) is included (either in seconds or hours) then
+   * an attempt is made to find a TimeZone name matching both the name
+   * and the offset (that doesn't observe daylight time, if the
+   * timezone observes daylight time then you must include a daylight
+   * time zone name after the offset), if that fails then a TimeZone
+   * with the given GMT offset is returned (whether or not the
+   * TimeZone observes daylight time is ignored), if that also fails
+   * the GMT TimeZone is returned.
+   * <p>
+   * If the String ends with (GMT offset)(daylight time zone name)
+   * then an attempt is made to find a TimeZone with the given name and
+   * GMT offset that also observes (the daylight time zone name is not
+   * currently used in any other way), if that fails a TimeZone with
+   * the given GMT offset that observes daylight time is returned, if
+   * that also fails the GMT TimeZone is returned.
+   * <p>
+   * Examples: In Chicago, the time zone id could be "CST6CDT", but
+   * the preferred name would be "America/Chicago".  In Indianapolis
+   * (which does not have Daylight Savings Time) the string could be
+   * "EST5", but the preferred name would be "America/Indianapolis".
+   * The standard time zone name for The Netherlands is "Europe/Amsterdam",
+   * but can also be given as "CET-1CEST".
+   */
+  private static TimeZone getDefaultTimeZone(String sysTimeZoneId)
+  {
+    // First find start of GMT offset info and any Daylight zone name.
+    int startGMToffset = 0;
+    int sysTimeZoneIdLength = sysTimeZoneId.length();
+    for (int i = 0; i < sysTimeZoneIdLength && startGMToffset == 0; i++)
+      {
+	char c = sysTimeZoneId.charAt(i);
+	if (c == '+' || c == '-' || Character.isDigit(c))
+	  startGMToffset = i;
+      }
+    
+    String tzBasename;
+    if (startGMToffset == 0)
+      tzBasename = sysTimeZoneId;
+    else
+      tzBasename = sysTimeZoneId.substring (0, startGMToffset);
+    
+    int startDaylightZoneName = 0;
+    for (int i = sysTimeZoneIdLength - 1;
+	 i >= 0 && !Character.isDigit(sysTimeZoneId.charAt(i)); --i)
+      startDaylightZoneName = i;
+    
+    boolean useDaylightTime = startDaylightZoneName > 0;
+    
+    // Integer.parseInt() doesn't handle leading +.
+    if (sysTimeZoneId.charAt(startGMToffset) == '+')
+      startGMToffset++;
+    
+    int gmtOffset = 0;
+    if (startGMToffset > 0)
+      {
+	gmtOffset = Integer.parseInt
+	  (startDaylightZoneName == 0
+	   ? sysTimeZoneId.substring(startGMToffset)
+	   : sysTimeZoneId.substring(startGMToffset,
+				     startDaylightZoneName));
+	
+	// Offset could be in hours or seconds.  Convert to millis.
+	if (gmtOffset < 24)
+	  gmtOffset *= 60 * 60;
+	gmtOffset *= 1000;
+      }
+    
+    // Try to be optimistic and get the timezone that matches the base name.
+    // If we only have the base name then just accept this timezone.
+    // Otherwise check the gmtOffset and day light attributes.
+    TimeZone tz = (TimeZone) timezones().get(tzBasename);
+    if (tz != null
+	&& (tzBasename == sysTimeZoneId
+	    || (tz.getRawOffset() == gmtOffset
+		&& tz.useDaylightTime() == useDaylightTime)))
+      return tz;
+    
+    // Maybe there is one with the daylight zone name?
+    if (useDaylightTime)
+      {
+	String daylightZoneName;
+	daylightZoneName = sysTimeZoneId.substring(startDaylightZoneName);
+	if (!daylightZoneName.equals(tzBasename))
+	  {
+	    tz = (TimeZone) timezones().get(tzBasename);
+	    if (tz != null
+		&& tz.getRawOffset() == gmtOffset
+		&& tz.useDaylightTime())
+	      return tz;
+	  }
+      }
+    
+    // If no match, see if a valid timezone has similar attributes as this
+    // and then use it instead. We take the first one that looks OKish.
+    if (startGMToffset > 0)
+      {
+	String[] ids = getAvailableIDs(gmtOffset);
+	for (int i = 0; i < ids.length; i++)
+	  {
+	    tz = (TimeZone) timezones().get(ids[i]);
+	    if (tz.useDaylightTime() == useDaylightTime)
+	      return tz;
+	  }
+      }
+    
+    return null;
+  }
+
+  /**
    * 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.
@@ -1145,11 +1521,13 @@
    */
   public static TimeZone getDefault()
   {
+    // Should we return a clone?
     return defaultZone();
   }
 
   public static void setDefault(TimeZone zone)
   {
+    // Hmmmm. No Security checks?
     defaultZone0 = zone;
   }
 
Index: java/util/natTimeZone.cc
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/util/natTimeZone.cc,v
retrieving revision 1.6
diff -u -r1.6 natTimeZone.cc
--- java/util/natTimeZone.cc	19 Feb 2003 16:28:37 -0000	1.6
+++ java/util/natTimeZone.cc	22 Aug 2004 13:42:40 -0000
@@ -1,6 +1,7 @@
 // natTimeZone.cc -- Native side of TimeZone class.
 
-/* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003  Free Software Foundation
+/* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004
+   Free Software Foundation
 
    This file is part of libgcj.
 
@@ -33,53 +34,96 @@
 
 #include <string.h>
 
-/*
- * This method returns a time zone string that is used by init_properties
- * to set the default timezone property 'user.timezone'.  That value is
- * used by default as a key into the timezone table used by the
- * java::util::TimeZone class.
+/**
+ * This method returns a time zone id string which is in the form
+ * (standard zone name) or (standard zone name)(GMT offset) or
+ * (standard zone name)(GMT offset)(daylight time zone name).  The
+ * GMT offset can be in seconds, or where it is evenly divisible by
+ * 3600, then it can be in hours.  The offset must be the time to
+ * add to the local time to get GMT.  If a offset is given and the
+ * time zone observes daylight saving then the (daylight time zone
+ * name) must also be given (otherwise it is assumed the time zone
+ * does not observe any daylight savings).
+ * <p>
+ * The result of this method is given to getDefaultTimeZone(String)
+ * which tries to map the time zone id to a known TimeZone.  See
+ * that method on how the returned String is mapped to a real
+ * TimeZone object.
  */
-static jstring
-getSystemTimeZone (void)
+jstring
+java::util::TimeZone::getDefaultTimeZoneId ()
 {
-  struct tm *tim;
+  struct tm tim;
+#ifdef HAVE_TM_ZONE
+  int month;
+#endif
   time_t current_time;
   long tzoffset;
   const char *tz1, *tz2;
   char *tzid;
 
-  current_time = time(0);
+  time(&current_time);
+  localtime_r(&current_time, &tim);
+  mktime(&tim);
+
+#ifdef HAVE_TM_ZONE
+  /* We will cycle through the months to make sure we hit dst. */
+  month = tim.tm_mon;
+  tz1 = tz2 = NULL;
+  while (tz1 == NULL || tz2 == NULL)
+    {
+      if (tim.tm_isdst > 0)
+        tz2 = tim.tm_zone;
+      else if (tz1 == NULL)
+        {
+          tz1 = tim.tm_zone;
+          month = tim.tm_mon;
+        }
+
+      if (tz1 == NULL || tz2 == NULL)
+        {
+          tim.tm_mon++;
+          tim.tm_mon %= 12;
+        }
+
+      if (tim.tm_mon == month && tz2 == NULL)
+        tz2 = "";
+      else
+        mktime(&tim);
+    }
+  /* We want to make sure the tm struct we use later on is not dst. */
+  tim.tm_mon = month;
+  mktime(&tim);
+#elif defined (HAVE_TZNAME)
+  /* If dst is never used, tzname[1] is the empty string. */
+  tzset();
+  tz1 = tzname[0];
+  tz2 = tzname[1];
+#else
+  /* Some targets have no concept of timezones. Assume GMT without dst. */
+  tz1 = "GMT";
+  tz2 = "";
+#endif
 
-  mktime(tim = localtime(&current_time));
 #ifdef STRUCT_TM_HAS_GMTOFF
-  // tm_gmtoff is secs EAST of UTC.
-  tzoffset = -(tim->tm_gmtoff) + tim->tm_isdst * 3600L;
+  /* tm_gmtoff is the number of seconds that you must add to GMT to get
+     local time, we need the number of seconds to add to the local time
+     to get GMT. */
+  tzoffset = -1L * tim.tm_gmtoff;
 #elif HAVE_UNDERSCORE_TIMEZONE
   tzoffset = _timezone;
 #elif HAVE_TIMEZONE
-  // timezone is secs WEST of UTC.
+  /* timezone is secs WEST of UTC. */
   tzoffset = timezone;	
 #else
-  // FIXME: there must be another global if neither tm_gmtoff nor timezone
-  // is available, esp. if tzname is valid.
-  // Richard Earnshaw <rearnsha@arm.com> has suggested using difftime to
-  // calculate between gmtime and localtime (and accounting for possible
-  // daylight savings time) as an alternative.
+  /* FIXME: there must be another global if neither tm_gmtoff nor timezone
+     is available, esp. if tzname is valid.
+     Richard Earnshaw <rearnsha@arm.com> has suggested using difftime to
+     calculate between gmtime and localtime (and accounting for possible
+     daylight savings time) as an alternative. */
   tzoffset = 0L;
 #endif
 
-#ifdef HAVE_TM_ZONE
-  tz1 = tim->tm_zone;
-  tz2 = "";
-#elif defined (HAVE_TZNAME)
-  tz1 = tzname[0];
-  tz2 = strcmp (tzname[0], tzname[1]) ? tzname[1] : "";
-#else
-  // Some targets have no concept of timezones.
-  tz1 = "???";
-  tz2 = tz1;
-#endif
-
   if ((tzoffset % 3600) == 0)
     tzoffset = tzoffset / 3600;
 
@@ -90,81 +134,3 @@
 
   return retval;
 }
-
-// Get the System Timezone as reported by the OS.  It should be in
-// the form PST8PDT so we'll need to parse it and check that it's valid.
-// FIXME: Using the code from Classpath for generating the System
-// Timezone IMO is suboptimal because it ignores whether the rules for
-// DST match up.
-jstring
-java::util::TimeZone::getDefaultTimeZoneId ()
-{
-  jstring sysTimeZoneId = getSystemTimeZone ();
-
-  using namespace java::lang;
-
-  // Check if this is a valid timezone.  Make sure the IDs match
-  // since getTimeZone returns GMT if no match is found.
-  TimeZone *tz = TimeZone::getTimeZone (sysTimeZoneId);
-  if (tz->getID ()->equals (sysTimeZoneId))
-    return sysTimeZoneId;
-
-  // Check if the base part of sysTimeZoneId is a valid timezone that
-  // matches with daylight usage and rawOffset.  Make sure the IDs match
-  // since getTimeZone returns GMT if no match is found.
-  // First find start of GMT offset info and any Daylight zone name.
-  int startGMToffset = 0;
-  int sysTimeZoneIdLength = sysTimeZoneId->length();
-  for (int i = 0; i < sysTimeZoneIdLength && startGMToffset == 0; i++)
-    {
-      if (Character::isDigit (sysTimeZoneId->charAt (i)))
-	startGMToffset = i;
-    }
-
-  int startDaylightZoneName = 0;
-  jboolean usesDaylight = false;
-  for (int i = sysTimeZoneIdLength - 1;
-       i >= 0 && !Character::isDigit (sysTimeZoneId->charAt (i)); --i)
-    {
-      startDaylightZoneName = i;
-    }
-  if (startDaylightZoneName > 0)
-    usesDaylight = true;
-
-  int GMToffset
-    = Integer::parseInt (startDaylightZoneName == 0 ?
-			 sysTimeZoneId->substring (startGMToffset) :
-			 sysTimeZoneId->substring (startGMToffset,
-						   startDaylightZoneName));
-
-  // Offset could be in hours or seconds.  Convert to millis.
-  if (GMToffset < 24)
-    GMToffset *= 60 * 60;
-  GMToffset *= -1000;
-
-  jstring tzBasename = sysTimeZoneId->substring (0, startGMToffset);
-  tz = TimeZone::getTimeZone (tzBasename);
-  if (tz->getID ()->equals (tzBasename) && tz->getRawOffset () == GMToffset)
-    {
-      jboolean tzUsesDaylight = tz->useDaylightTime ();
-      if (usesDaylight && tzUsesDaylight || !usesDaylight && !tzUsesDaylight)
-	return tzBasename;
-    }
-
-  // If no match, see if a valid timezone has the same attributes as this
-  // and then use it instead.
-  jstringArray IDs = TimeZone::getAvailableIDs (GMToffset);
-  jstring *elts = elements (IDs);
-  for (int i = 0; i < IDs->length; ++i)
-    {
-      // FIXME: The daylight savings rules may not match the rules
-      // for the desired zone.
-      jboolean IDusesDaylight =
-	TimeZone::getTimeZone (elts[i])->useDaylightTime ();
-      if (usesDaylight && IDusesDaylight || !usesDaylight && !IDusesDaylight)
-	return elts[i];
-    }
-
-  // If all else fails, return null.
-  return NULL;
-}

Attachment: signature.asc
Description: This is a digitally signed message part


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]