From 42348101742f1257f76247b577489a7c2b8db31b Mon Sep 17 00:00:00 2001 From: Mark Wielaard Date: Thu, 26 Aug 2004 16:10:54 +0000 Subject: [PATCH] re PR libgcj/17002 (java.util.TimeZone.getDefault() is broken) 2004-08-26 Mark Wielaard 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. From-SVN: r86623 --- libjava/ChangeLog | 16 ++ libjava/java/util/TimeZone.java | 472 ++++++++++++++++++++++++++++++++++++--- libjava/java/util/natTimeZone.cc | 193 +++++++--------- 3 files changed, 538 insertions(+), 143 deletions(-) diff --git a/libjava/ChangeLog b/libjava/ChangeLog index 853d41d..fb6d555 100644 --- a/libjava/ChangeLog +++ b/libjava/ChangeLog @@ -1,3 +1,19 @@ +2004-08-26 Mark Wielaard + + 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. + 2004-08-25 David Daney * Makefile.am (AM_GCJFLAGS): Add LIBGCJ_JAVAFLAGS. diff --git a/libjava/java/util/TimeZone.java b/libjava/java/util/TimeZone.java index 504d34e..cc3c7a3 100644 --- a/libjava/java/util/TimeZone.java +++ b/libjava/java/util/TimeZone.java @@ -40,6 +40,9 @@ exception statement from your version. */ 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 @@ public abstract class TimeZone implements java.io.Serializable, Cloneable * 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 getDefaultTimeZone(String) with + * the result of + * System.getProperty("user.timezone"), + * System.getenv("TZ"), + * readTimeZoneFile("/etc/timezone"), + * readtzFile("/etc/localtime") and + * getDefaultTimeZoneId() + * 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 @@ public abstract class TimeZone implements java.io.Serializable, Cloneable return timezones0; } - - /* This method returns us a time zone id string which is in the - form . - 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). + *

+ * 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. + *

+ * 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 getDefaultTimeZone(String). + * If the file doesn't exist, an IOException occurs or it isn't a tzfile + * that can be parsed null is returned. + *

+ * The tzfile structure (as also used by glibc) is described in the Olson + * tz database archive as can be found at + * ftp://elsie.nci.nih.gov/pub/. + *

+ * 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 System.getProperty("user.timezone") + * or getDefaultTimeZoneId(). 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: + * (standard zone name)[(GMT offset)[(daylight time zone name)]] + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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 @@ public abstract class TimeZone implements java.io.Serializable, Cloneable /** * 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; } diff --git a/libjava/java/util/natTimeZone.cc b/libjava/java/util/natTimeZone.cc index cb564a9..c23d9e6 100644 --- a/libjava/java/util/natTimeZone.cc +++ b/libjava/java/util/natTimeZone.cc @@ -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 @@ details. */ #include -/* - * 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). + *

+ * 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(¤t_time); +#ifdef HAVE_LOCALTIME_R + localtime_r(¤t_time, &tim); +#else + /* Fall back on non-thread safe localtime. */ + lt_tim = localtime(¤t_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(¤t_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 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 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 @@ getSystemTimeZone (void) 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; -} -- 2.7.4