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:
parent
0c5c188f07
commit
b3502aa8d4
20 changed files with 1702 additions and 607 deletions
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue