aboutsummaryrefslogtreecommitdiff
path: root/libjava/classpath/java/util/TimeZone.java
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/classpath/java/util/TimeZone.java')
-rw-r--r--libjava/classpath/java/util/TimeZone.java323
1 files changed, 262 insertions, 61 deletions
diff --git a/libjava/classpath/java/util/TimeZone.java b/libjava/classpath/java/util/TimeZone.java
index a253561b046..5329e06c11f 100644
--- a/libjava/classpath/java/util/TimeZone.java
+++ b/libjava/classpath/java/util/TimeZone.java
@@ -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;
}
@@ -1343,14 +1402,7 @@ public abstract class TimeZone implements java.io.Serializable, Cloneable
private String getDefaultDisplayName(boolean dst)
{
- int offset = getRawOffset();
- if (dst && this instanceof SimpleTimeZone)
- {
- // ugly, but this is a design failure of the API:
- // getDisplayName takes a dst parameter even though
- // TimeZone knows nothing about daylight saving offsets.
- offset += ((SimpleTimeZone) this).getDSTSavings();
- }
+ int offset = getRawOffset() + (dst ? getDSTSavings() : 0);
StringBuffer sb = new StringBuffer(9);
sb.append("GMT");
@@ -1406,30 +1458,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 +1563,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 +1584,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 +1601,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;
+ }
}
/**