re PR libgcj/17002 (java.util.TimeZone.getDefault() is broken)

libjava/
	PR libgcj/17002
	PR classpath/28550
	* java/util/VMTimeZone.java (getDefaultTimeZoneId): To read
	/etc/localtime, use ZoneInfo.readTZFile instead of
	VMTimeZone.readtzFile.  Get better timezone name for /etc/localtime,
	either if it is a symlink or through /etc/sysconfig/clock.
	(readSysconfigClockFile): New static method.
	(readtzFile): Removed.
	* java/lang/System.java: Add gnu.java.util.zoneinfo.dir to comments.
	* posix.cc (_Jv_platform_initProperties): Set
	gnu.java.util.zoneinfo.dir.
	* sources.am (gnu_java_util_source_files): Add
	classpath/gnu/java/util/ZoneInfo.java.
	* Makefile.in: Regenerated.
	* java/util/VMTimeZone.h: Regenerated.
	* java/util/TimeZone.h: Regenerated.
	* gnu/java/util/ZoneInfo.h: Generated.
libjava/classpath/
	* java/util/Date.java (parse): Properly parse 09:01:02 as
	hours/minutes/seconds, not as hours/minutes/year.
	* java/util/SimpleTimeZone.java (SimpleTimeZone): Simplify
	{start,end}TimeMode constructor by calling shorter constructor,
	set {start,end}TimeMode fields after it returns.
	(setStartRule): Don't adjust startTime into WALL_TIME.  Set
	startTimeMode to WALL_TIME.
	(endStartRule): Similarly.
	(getOffset): Handle properly millis + dstOffset overflowing into the
	next day.  Adjust startTime resp. endTime based on startTimeMode
	resp. endTimeMode.
	* java/util/TimeZone.java (zoneinfo_dir, availableIDs, aliases0): New
	static fields.
	(timezones): Remove synchronized keyword.  Set zoneinfo_dir.
	If non-null, set up aliases0 and don't put anything into
	timezones0.
	(defaultZone): Call getTimeZone instead of timezones().get.
	(getDefaultTimeZone): Fix parsing of EST5 or EST5EDT6.  Use
	getTimeZoneInternal instead of timezones().get.
	(parseTime): Parse correctly hour:minute.
	(getTimeZoneInternal): New private method.
	(getTimeZone): Do the custom ID checking first, canonicalize
	ID for custom IDs as required by documentation.  Call
	getTimeZoneInternal to handle the rest.
	(getAvailableIDs(int)): Add locking.  Handle zoneinfo_dir != null.
	(getAvailableIDs(File,String,ArrayList)): New private method.
	(getAvailableIDs()): Add locking.  Handle zoneinfo_dir != null.
	* gnu/java/util/ZoneInfo.java: New file.

From-SVN: r122229
This commit is contained in:
Jakub Jelinek 2007-02-22 17:04:55 +01:00 committed by Jakub Jelinek
parent 0c5c188f07
commit b3502aa8d4
20 changed files with 1702 additions and 607 deletions

View file

@ -39,6 +39,9 @@ exception statement from your version. */
package java.util;
import gnu.classpath.SystemProperties;
import gnu.java.util.ZoneInfo;
import java.io.File;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.text.DateFormatSymbols;
@ -115,7 +118,7 @@ public abstract class TimeZone implements java.io.Serializable, Cloneable
// Fall back on GMT.
if (zone == null)
zone = (TimeZone) timezones().get("GMT");
zone = getTimeZone ("GMT");
return zone;
}
@ -127,6 +130,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.
*/
@ -135,13 +154,55 @@ public abstract class TimeZone implements java.io.Serializable, Cloneable
* it is not needed:
*/
// Package-private to avoid a trampoline.
static synchronized HashMap timezones()
static HashMap timezones()
{
if (timezones0 == null)
{
HashMap timezones = new HashMap();
timezones0 = timezones;
zoneinfo_dir = SystemProperties.getProperty("gnu.java.util.zoneinfo.dir");
if (zoneinfo_dir != null && !new File(zoneinfo_dir).isDirectory())
zoneinfo_dir = null;
if (zoneinfo_dir != null)
{
aliases0 = new HashMap();
// These deprecated aliases for JDK 1.1.x compatibility
// should take precedence over data files read from
// /usr/share/zoneinfo.
aliases0.put("ACT", "Australia/Darwin");
aliases0.put("AET", "Australia/Sydney");
aliases0.put("AGT", "America/Argentina/Buenos_Aires");
aliases0.put("ART", "Africa/Cairo");
aliases0.put("AST", "America/Juneau");
aliases0.put("BST", "Asia/Colombo");
aliases0.put("CAT", "Africa/Gaborone");
aliases0.put("CNT", "America/St_Johns");
aliases0.put("CST", "CST6CDT");
aliases0.put("CTT", "Asia/Brunei");
aliases0.put("EAT", "Indian/Comoro");
aliases0.put("ECT", "CET");
aliases0.put("EST", "EST5EDT");
aliases0.put("EST5", "EST5EDT");
aliases0.put("IET", "EST5EDT");
aliases0.put("IST", "Asia/Calcutta");
aliases0.put("JST", "Asia/Seoul");
aliases0.put("MIT", "Pacific/Niue");
aliases0.put("MST", "MST7MDT");
aliases0.put("MST7", "MST7MDT");
aliases0.put("NET", "Indian/Mauritius");
aliases0.put("NST", "Pacific/Auckland");
aliases0.put("PLT", "Indian/Kerguelen");
aliases0.put("PNT", "MST7MDT");
aliases0.put("PRT", "America/Anguilla");
aliases0.put("PST", "PST8PDT");
aliases0.put("SST", "Pacific/Ponape");
aliases0.put("VST", "Asia/Bangkok");
return timezones;
}
TimeZone tz;
// Automatically generated by scripts/timezones.pl
// XXX - Should we read this data from a file?
@ -887,7 +948,6 @@ public abstract class TimeZone implements java.io.Serializable, Cloneable
static TimeZone getDefaultTimeZone(String sysTimeZoneId)
{
String stdName = null;
String dstName;
int stdOffs;
int dstOffs;
try
@ -900,14 +960,14 @@ public abstract class TimeZone implements java.io.Serializable, Cloneable
// get std
do
c = sysTimeZoneId.charAt(index++);
c = sysTimeZoneId.charAt(index);
while (c != '+' && c != '-' && c != ',' && c != ':'
&& ! Character.isDigit(c) && c != '\0' && index < idLength);
&& ! Character.isDigit(c) && c != '\0' && ++index < idLength);
if (index >= idLength)
return (TimeZone)timezones().get(sysTimeZoneId);
return getTimeZoneInternal(sysTimeZoneId);
stdName = sysTimeZoneId.substring(0, --index);
stdName = sysTimeZoneId.substring(0, index);
prevIndex = index;
// get the std offset
@ -938,7 +998,7 @@ public abstract class TimeZone implements java.io.Serializable, Cloneable
if (index >= idLength)
{
// Do we have an existing timezone with that name and offset?
TimeZone tz = (TimeZone) timezones().get(stdName);
TimeZone tz = getTimeZoneInternal(stdName);
if (tz != null)
if (tz.getRawOffset() == stdOffs)
return tz;
@ -949,16 +1009,16 @@ public abstract class TimeZone implements java.io.Serializable, Cloneable
// get dst
do
c = sysTimeZoneId.charAt(index++);
c = sysTimeZoneId.charAt(index);
while (c != '+' && c != '-' && c != ',' && c != ':'
&& ! Character.isDigit(c) && c != '\0' && index < idLength);
&& ! Character.isDigit(c) && c != '\0' && ++index < idLength);
// Done yet? (Format: std offset dst)
if (index >= idLength)
{
// Do we have an existing timezone with that name and offset
// which has DST?
TimeZone tz = (TimeZone) timezones().get(stdName);
TimeZone tz = getTimeZoneInternal(stdName);
if (tz != null)
if (tz.getRawOffset() == stdOffs && tz.useDaylightTime())
return tz;
@ -968,7 +1028,6 @@ public abstract class TimeZone implements java.io.Serializable, Cloneable
}
// get the dst offset
dstName = sysTimeZoneId.substring(prevIndex, --index);
prevIndex = index;
do
c = sysTimeZoneId.charAt(index++);
@ -1005,7 +1064,7 @@ public abstract class TimeZone implements java.io.Serializable, Cloneable
if (index >= idLength)
{
// Time Zone existing with same name, dst and offsets?
TimeZone tz = (TimeZone) timezones().get(stdName);
TimeZone tz = getTimeZoneInternal(stdName);
if (tz != null)
if (tz.getRawOffset() == stdOffs && tz.useDaylightTime()
&& tz.getDSTSavings() == (dstOffs - stdOffs))
@ -1171,10 +1230,10 @@ public abstract class TimeZone implements java.io.Serializable, Cloneable
break;
else
i++;
millis += 60 * 1000 * Integer.parseInt(time.substring(iprev, i));
if (i >= time.length())
return millis;
millis += 60 * 1000 * Integer.parseInt(time.substring(iprev, i));
millis += 1000 * Integer.parseInt(time.substring(++i));
return millis;
}
@ -1406,30 +1465,67 @@ public abstract class TimeZone implements java.io.Serializable, Cloneable
* @return The time zone for the identifier or GMT, if no such time
* zone exists.
*/
// FIXME: XXX: JCL indicates this and other methods are synchronized.
public static TimeZone getTimeZone(String ID)
private static TimeZone getTimeZoneInternal(String ID)
{
// First check timezones hash
TimeZone tz = (TimeZone) timezones().get(ID);
if (tz != null)
TimeZone tz = null;
TimeZone tznew = null;
for (int pass = 0; pass < 2; pass++)
{
if (tz.getID().equals(ID))
return tz;
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;
}
}
// 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;
if (pass == 1 || zoneinfo_dir == null)
return null;
// aliases0 is never changing after first timezones(), so should
// be safe without synchronization.
String zonename = (String) aliases0.get(ID);
if (zonename == null)
zonename = ID;
// Read the file outside of the critical section, it is expensive.
tznew = ZoneInfo.readTZFile (ID, zoneinfo_dir
+ File.separatorChar + zonename);
if (tznew == null)
return null;
}
// See if the ID is really a GMT offset form.
// Note that GMT is in the table so we know it is different.
if (ID.startsWith("GMT"))
return null;
}
/**
* Gets the TimeZone for the given ID.
* @param ID the time zone identifier.
* @return The time zone for the identifier or GMT, if no such time
* zone exists.
*/
public static TimeZone getTimeZone(String ID)
{
// Check for custom IDs first
if (ID.startsWith("GMT") && ID.length() > 3)
{
int pos = 3;
int offset_direction = 1;
@ -1474,8 +1570,20 @@ public abstract class TimeZone implements java.io.Serializable, Cloneable
}
}
return new SimpleTimeZone((hour * (60 * 60 * 1000) +
minute * (60 * 1000))
// Custom IDs have to be normalized
StringBuffer sb = new StringBuffer(9);
sb.append("GMT");
sb.append(offset_direction >= 0 ? '+' : '-');
sb.append((char) ('0' + hour / 10));
sb.append((char) ('0' + hour % 10));
sb.append(':');
sb.append((char) ('0' + minute / 10));
sb.append((char) ('0' + minute % 10));
ID = sb.toString();
return new SimpleTimeZone((hour * (60 * 60 * 1000)
+ minute * (60 * 1000))
* offset_direction, ID);
}
catch (NumberFormatException e)
@ -1483,8 +1591,11 @@ public abstract class TimeZone implements java.io.Serializable, Cloneable
}
}
// Finally, return GMT per spec
return getTimeZone("GMT");
TimeZone tz = getTimeZoneInternal(ID);
if (tz != null)
return tz;
return new SimpleTimeZone(0, "GMT");
}
/**
@ -1497,37 +1608,134 @@ public abstract class TimeZone implements java.io.Serializable, Cloneable
*/
public static String[] getAvailableIDs(int rawOffset)
{
int count = 0;
Iterator iter = timezones().entrySet().iterator();
while (iter.hasNext())
synchronized (TimeZone.class)
{
// 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++;
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;
for (int i = 0; i < s.length; i++)
{
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;
}
}
/**