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]

Re: TimeZone


Hi,

On Tue, 2004-08-24 at 22:49, Bryce McKinlay wrote:
> Mark Wielaard wrote:
> >Note that although both /etc/timezone and /etc/localtime are not
> >official posix standards lots of platforms (GNU, Darwin, Solaris,
> >FreeBSB at least) support one or the other.
> >  
> >
> Useful to know, thanks.  This should probably be mentioned in a comment 
> in the code? I presume there are no endian/64-bit issues with this file 
> format?

The /etc/timezone file is used differently on different systems (even
different on some GNU/Linux systems). But if it exists then the first
line always contains the timezone as ascii string. The tzdate file
format format is described pretty strictly. Andreas has tested it on a
powerpc (darwin) system. The only problem on Darwin was that the file
was missing the 4 magic bytes at the start (the code can now handle
this). I have added this as comment to TimeZone now.

> >>- In the TimeZone initializer, we should set the user.timezone system 
> >>property if the user didn't set it.  
> >
> >Why should we set the user.timezone system property?
> >And why in the initializer?
> >  
> >
> Sorry. Thinko. TimeZone.getDefault() should set user.timezone if it 
> wasn't set when called. This is for compatibility with Sun's 
> implementation. I think I remember reading about this somewhere, but 
> you're right, its not mentioned in the spec. I think we should implement 
> this for compatibility, though - and document it, of course.

Interesting. But I don't think we should add this unspecified behavior
unless there are real programs depending on it. And even then it is just
a bug in the program.

> >>- Its not good to silently ignore exceptions:
> >>
> >I have changed the comments to make clear that we don't just ignore
> >them, but that they signal specific unexpected conditions.
> >
> Thanks. readTimeZoneFile() looks good, could you add a similar comment 
> to readtzFile()?

Added.

> Also:
> 
> - readtzFile() needs a "return null" at the bottom, I think. Its 
> probably a GCJ bug that this didn't get detected.

No, there is no fall through (beyond the finally) in the code.
All code paths end in proper returns.

> - readLocaltimeFile() would be a better name for readtzFile()?

It can be used generically to read any tzdata file (even though we don't
do that currently).

> >I think that is a bit general statement that you should really back up
> >with benchmarks. But don't forget that this code is shared with other
> >runtimes which might have other tradeoffs. In this case the extra
> >security checks and disk i/o probably negate any gain you think comes
> >from so called "defensive coding" and makes the code a lot harder to
> >read/write. 
> >
> There are no extra security checks needed - if you open a file, a 
> security check will performed, if a security manager is installed, 
> regardless of what methods you use to access the file. Using a String 
> constructor rather than a File constructor does not avoid this.

There is now an extra security check. One when doing File.exists() and
another one when doing new FileInputStream(File). Also there are now two
transitions from java source code to native code. For JNI based systems
this is much less efficient.

> >I added a File.exist() check.
> >  
> >
> Thanks. "if (!file.exists()) return" might look better?

Yes, added that. It solves my deep nesting complaint.

> >@@ -1145,11 +1529,13 @@
> >    */
> >   public static TimeZone getDefault()
> >   {
> >+    // Should we return a clone?
> >     return defaultZone();
> >   }
> >  
> >
> 
> Hmm, TimeZone state can be manipulated, so yes, I think we should 
> clone() here.

Ah, good that I kept that comment in. I added the clone() call.

> >   public static void setDefault(TimeZone zone)
> >   {
> >+    // Hmmmm. No Security checks?
> >     defaultZone0 = zone;
> >   }
> >
> I don't see any java.security.Permission types relating to time zones, 
> so I guess this is not considered a security risk. Presumably, an applet 
> could only screw itself up by messing with this, not the rest of the system.

I think it is an oversight in the spec since changing the default time
zone can disrupt some programs I think. So I keep the comment here.

> Anyway, this patch is basically fine. Feel free to address the above 
> comments and then commit.

Thanks. Committed as attached.

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

       Fixes PR libgcj/17002:
       * 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.

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	26 Aug 2004 16:05:28 -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,363 @@
     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.
+   * <p>
+   * The /etc/timezone file is not standard, but a lot of systems have
+   * it. If it exist the first line always contains a string
+   * describing the timezone of the host of domain. Some systems
+   * contain a /etc/TIMEZONE file which is used to set the TZ
+   * environment variable (which is checked before /etc/timezone is
+   * read).
+   */
+  private static String readTimeZoneFile(String file)
+  {
+    File f = new File(file);
+    if (!f.exists())
+      return null;
+
+    InputStreamReader isr = null;
+    try
+      {
+	FileInputStream fis = new FileInputStream(f);
+	BufferedInputStream bis = new BufferedInputStream(fis);
+	isr = new InputStreamReader(bis);
+	
+	StringBuffer sb = new StringBuffer();
+	int i = isr.read();
+	while (i != -1)
+	  {
+	    char c = (char) i;
+	    if (Character.isLetter(c) || Character.isDigit(c)
+		|| c == '/' || c == '-' || c == '_')
+	      {
+		sb.append(c);
+		i = isr.read();
+	      }
+	    else
+	      break;
+	  }
+	return sb.toString();
+      }
+    catch (IOException ioe)
+      {
+	// Parse error, not a proper tzfile.
+	return null;
+      }
+    finally
+      {
+	try
+	  {
+	    if (isr != null)
+	      isr.close();
+	  }
+	catch (IOException ioe)
+	  {
+	    // Error while close, nothing we can do.
+	  }
+      }
+  }
+
+  /**
+   * Tries to read a file as a "standard" tzfile and return a time
+   * zone id string as expected by <code>getDefaultTimeZone(String)</code>.
+   * If the file doesn't exist, an IOException occurs or it isn't a tzfile
+   * that can be parsed null is returned.
+   * <p>
+   * The tzfile structure (as also used by glibc) is described in the Olson
+   * tz database archive as can be found at
+   * <code>ftp://elsie.nci.nih.gov/pub/</code>.
+   * <p>
+   * At least the following platforms support the tzdata file format
+   * and /etc/localtime (GNU/Linux, Darwin, Solaris and FreeBSD at
+   * least). Some systems (like Darwin) don't start the file with the
+   * required magic bytes 'TZif', this implementation can handle
+   * that).
+   */
+  private static String readtzFile(String file)
+  {
+    File f = new File(file);
+    if (!f.exists())
+      return null;
+    
+    DataInputStream dis = null;
+    try
+      {
+        FileInputStream fis = new FileInputStream(f);
+        BufferedInputStream bis = new BufferedInputStream(fis);
+        dis = new DataInputStream(bis);
+	
+        // Make sure we are reading a tzfile.
+        byte[] tzif = new byte[4];
+        dis.readFully(tzif);
+        if (tzif[0] == 'T' && tzif[1] == 'Z'
+            && tzif[2] == 'i' && tzif[3] == 'f')
+	  // Reserved bytes, ttisgmtcnt, ttisstdcnt and leapcnt
+	  skipFully(dis, 16 + 3 * 4);
+	else
+	  // Darwin has tzdata files that don't start with the TZif marker
+	  skipFully(dis, 16 + 3 * 4 - 4);
+	
+	int timecnt = dis.readInt();
+	int typecnt = dis.readInt();
+	if (typecnt > 0)
+	  {
+	    int charcnt = dis.readInt();
+	    // Transition times plus indexed transition times.
+	    skipFully(dis, timecnt * (4 + 1));
+	    
+	    // Get last gmt_offset and dst/non-dst time zone names.
+	    int abbrind = -1;
+	    int dst_abbrind = -1;
+	    int gmt_offset = 0;
+	    while (typecnt-- > 0)
+	      {
+		// gmtoff
+		int offset = dis.readInt();
+		int dst = dis.readByte();
+		if (dst == 0)
+		  {
+		    abbrind = dis.readByte();
+		    gmt_offset = offset;
+		  }
+		else
+		  dst_abbrind = dis.readByte();
+	      }
+	    
+	    // gmt_offset is the offset you must add to UTC/GMT to
+	    // get the local time, we need the offset to add to
+	    // the local time to get UTC/GMT.
+	    gmt_offset *= -1;
+	    
+	    // Turn into hours if possible.
+	    if (gmt_offset % 3600 == 0)
+	      gmt_offset /= 3600;
+	    
+	    if (abbrind >= 0)
+	      {
+		byte[] names = new byte[charcnt];
+		dis.readFully(names);
+		int j = abbrind;
+		while (j < charcnt && names[j] != 0)
+		  j++;
+		
+		String zonename = new String(names, abbrind, j - abbrind,
+					     "ASCII");
+		
+		String dst_zonename;
+		if (dst_abbrind >= 0)
+		  {
+		    j = dst_abbrind;
+		    while (j < charcnt && names[j] != 0)
+		      j++;
+		    dst_zonename = new String(names, dst_abbrind,
+					      j - dst_abbrind, "ASCII");
+		  }
+		else
+		  dst_zonename = "";
+		
+		// Only use gmt offset when necessary.
+		// Also special case GMT+/- timezones.
+		String offset_string;
+		if ("".equals(dst_zonename)
+		    && (gmt_offset == 0
+			|| zonename.startsWith("GMT+")
+			|| zonename.startsWith("GMT-")))
+		  offset_string = "";
+		else
+		  offset_string = Integer.toString(gmt_offset);
+		
+		String id = zonename + offset_string + dst_zonename;
+		
+		return id;
+	      }
+	  }
+	
+	// Something didn't match while reading the file.
+	return null;
+      }
+    catch (IOException ioe)
+      {
+	// Parse error, not a proper tzfile.
+	return null;
+      }
+    finally
+      {
+	try
+	  {
+	    if (dis != null)
+	      dis.close();
+	  }
+	catch(IOException ioe)
+	  {
+	    // Error while close, nothing we can do.
+	  }
+      }
+  }
+  
+  /**
+   * Skips the requested number of bytes in the given InputStream.
+   * Throws EOFException if not enough bytes could be skipped.
+   * Negative numbers of bytes to skip are ignored.
+   */
+  private static void skipFully(InputStream is, long l) throws IOException
+  {
+    while (l > 0)
+      {
+        long k = is.skip(l);
+        if (k <= 0)
+          throw new EOFException();
+        l -= k;
+      }
+  }
+  
+  /**
+   * 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.
+	// The offset is given as the time to add to local time to get GMT
+	// we need the time to add to GMT to get localtime.
+	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.
@@ -1140,16 +1542,18 @@
   /**
    * Returns the time zone under which the host is running.  This
    * can be changed with setDefault.
-   * @return the time zone for this host.
+   *
+   * @return A clone of the current default time zone for this host.
    * @see #setDefault
    */
   public static TimeZone getDefault()
   {
-    return defaultZone();
+    return (TimeZone) defaultZone().clone();
   }
 
   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	26 Aug 2004 16:05:28 -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,105 @@
 
 #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;
+#ifndef HAVE_LOCALTIME_R
+  struct tm *lt_tim;
+#endif
+#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);
+#ifdef HAVE_LOCALTIME_R
+  localtime_r(&current_time, &tim);
+#else
+  /* Fall back on non-thread safe localtime. */
+  lt_tim = localtime(&current_time);
+  memcpy(&tim, lt_tim, sizeof (struct tm));
+#endif
+  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 +143,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]