From 94d43e7046ee9257b0e6684f41783b9f947c59d9 Mon Sep 17 00:00:00 2001 From: jakub Date: Thu, 22 Feb 2007 16:04:55 +0000 Subject: [PATCH] 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. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@122229 138bc75d-0d04-0410-961f-82ee72b054a4 --- libjava/ChangeLog | 20 + libjava/Makefile.in | 3 +- libjava/classpath/ChangeLog | 31 + libjava/classpath/gnu/java/util/ZoneInfo.java | 1160 +++++++++++++++++ libjava/classpath/java/util/Date.java | 9 + .../classpath/java/util/SimpleTimeZone.java | 120 +- libjava/classpath/java/util/TimeZone.java | 314 ++++- .../lib/gnu/java/util/ZoneInfo.class | Bin 0 -> 12350 bytes libjava/classpath/lib/java/util/Date.class | Bin 10943 -> 8740 bytes .../lib/java/util/SimpleTimeZone.class | Bin 10254 -> 7861 bytes .../classpath/lib/java/util/TimeZone$1.class | Bin 1134 -> 859 bytes .../classpath/lib/java/util/TimeZone.class | Bin 27946 -> 29024 bytes .../classpath/lib/java/util/VMTimeZone.class | Bin 9024 -> 3256 bytes libjava/gnu/java/util/ZoneInfo.h | 70 + libjava/java/lang/System.java | 3 +- libjava/java/util/TimeZone.h | 9 + libjava/java/util/VMTimeZone.h | 3 +- libjava/java/util/VMTimeZone.java | 560 ++------ libjava/posix.cc | 4 + libjava/sources.am | 3 +- 20 files changed, 1702 insertions(+), 607 deletions(-) create mode 100644 libjava/classpath/gnu/java/util/ZoneInfo.java create mode 100644 libjava/classpath/lib/gnu/java/util/ZoneInfo.class create mode 100644 libjava/gnu/java/util/ZoneInfo.h diff --git a/libjava/ChangeLog b/libjava/ChangeLog index 6d337d522e67..eaea0ecaef3b 100644 --- a/libjava/ChangeLog +++ b/libjava/ChangeLog @@ -1,3 +1,23 @@ +2007-02-22 Jakub Jelinek + + 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. + 2007-02-22 Mohan Embar * include/win32-threads.h: Added #undef OUT. diff --git a/libjava/Makefile.in b/libjava/Makefile.in index db8813ed19b1..27aa9ae9e696 100644 --- a/libjava/Makefile.in +++ b/libjava/Makefile.in @@ -2402,7 +2402,8 @@ gnu_java_text_header_files = $(patsubst classpath/%,%,$(patsubst %.java,%.h,$(gn gnu_java_util_source_files = \ classpath/gnu/java/util/DoubleEnumeration.java \ classpath/gnu/java/util/EmptyEnumeration.java \ -classpath/gnu/java/util/WeakIdentityHashMap.java +classpath/gnu/java/util/WeakIdentityHashMap.java \ +classpath/gnu/java/util/ZoneInfo.java gnu_java_util_header_files = $(patsubst classpath/%,%,$(patsubst %.java,%.h,$(gnu_java_util_source_files))) gnu_java_util_jar_source_files = \ diff --git a/libjava/classpath/ChangeLog b/libjava/classpath/ChangeLog index c95970572e62..fa92bda1bb07 100644 --- a/libjava/classpath/ChangeLog +++ b/libjava/classpath/ChangeLog @@ -1,3 +1,34 @@ +2007-02-20 Jakub Jelinek + + * 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. + 2007-02-20 Matthias Klose * doc/Makefile.am: Add rules to build and install man pages diff --git a/libjava/classpath/gnu/java/util/ZoneInfo.java b/libjava/classpath/gnu/java/util/ZoneInfo.java new file mode 100644 index 000000000000..2146a321f404 --- /dev/null +++ b/libjava/classpath/gnu/java/util/ZoneInfo.java @@ -0,0 +1,1160 @@ +/* gnu.java.util.ZoneInfo + Copyright (C) 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.util; + +import java.io.BufferedInputStream; +import java.io.DataInputStream; +import java.io.EOFException; +import java.io.FileInputStream; +import java.io.InputStream; +import java.io.IOException; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.SimpleTimeZone; +import java.util.TimeZone; + +/** + * This class represents more advanced variant of java.util.SimpleTimeZone. + * It can handle zic(8) compiled transition dates plus uses a SimpleTimeZone + * for years beyond last precomputed transition. Before first precomputed + * transition it assumes no daylight saving was in effect. + * Timezones that never used daylight saving time should use just + * SimpleTimeZone instead of this class. + * + * This object is tightly bound to the Gregorian calendar. It assumes + * a regular seven days week, and the month lengths are that of the + * Gregorian Calendar. + * + * @see Calendar + * @see GregorianCalendar + * @see SimpleTimeZone + * @author Jakub Jelinek + */ +public class ZoneInfo extends TimeZone +{ + private static final int SECS_SHIFT = 22; + private static final long OFFSET_MASK = (1 << 21) - 1; + private static final int OFFSET_SHIFT = 64 - 21; + private static final long IS_DST = 1 << 21; + + /** + * The raw time zone offset in milliseconds to GMT, ignoring + * daylight savings. + * @serial + */ + private int rawOffset; + + /** + * Cached DST savings for the last transition rule. + */ + private int dstSavings; + + /** + * Cached flag whether last transition rule uses DST saving. + */ + private boolean useDaylight; + + /** + * Array of encoded transitions. + * Transition time in UTC seconds since epoch is in the most + * significant 64 - SECS_SHIFT bits, then one bit flag + * whether DST is active and the least significant bits + * containing offset relative to rawOffset. Both the DST + * flag and relative offset apply to time before the transition + * and after or equal to previous transition if any. + * The array must be sorted. + */ + private long[] transitions; + + /** + * SimpleTimeZone rule which applies on or after the latest + * transition. If the DST changes are not expresible as a + * SimpleTimeZone rule, then the rule should just contain + * the standard time and no DST time. + */ + private SimpleTimeZone lastRule; + + /** + * Cached GMT SimpleTimeZone object for internal use in + * getOffset method. + */ + private static SimpleTimeZone gmtZone = null; + + static final long serialVersionUID = -3740626706860383657L; + + /** + * Create a ZoneInfo with the given time offset + * from GMT and with daylight savings. + * + * @param rawOffset The time offset from GMT in milliseconds. + * @param id The identifier of this time zone. + * @param transitions Array of transition times in UTC seconds since + * Epoch in topmost 42 bits, below that 1 boolean bit whether the time + * before that transition used daylight saving and in bottommost 21 + * bits relative daylight saving offset against rawOffset in seconds + * that applies before this transition. + * @param endRule SimpleTimeZone class describing the daylight saving + * rules after the last transition. + */ + public ZoneInfo(int rawOffset, String id, long[] transitions, + SimpleTimeZone lastRule) + { + if (transitions == null || transitions.length < 1) + throw new IllegalArgumentException("transitions must not be null"); + if (lastRule == null) + throw new IllegalArgumentException("lastRule must not be null"); + this.rawOffset = rawOffset; + this.transitions = transitions; + this.lastRule = lastRule; + setID(id); + computeDSTSavings(); + } + + /** + * 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. + * + * The day must be a positive number and dayOfWeek must be a positive value + * from Calendar. dayOfWeek is redundant, but must match the other values + * or an inaccurate result may be returned. + * + * @param era the era of the given date + * @param year the year of the given date + * @param month the month of the given date, 0 for January. + * @param day the day of month + * @param dayOfWeek the day of week; this must match the other fields. + * @param millis the millis in the day (in local standard time) + * @return the time zone offset in milliseconds. + * @throws IllegalArgumentException if arguments are incorrect. + */ + public int getOffset(int era, int year, int month, int day, int dayOfWeek, + int millis) + { + if (gmtZone == null) + gmtZone = new SimpleTimeZone(0, "GMT"); + + if (dayOfWeek < Calendar.SUNDAY || dayOfWeek > Calendar.SATURDAY) + throw new IllegalArgumentException("dayOfWeek out of range"); + if (month < Calendar.JANUARY || month > Calendar.DECEMBER) + throw new IllegalArgumentException("month out of range:" + month); + + if (era != GregorianCalendar.AD) + return (int) (((transitions[0] << OFFSET_SHIFT) >> OFFSET_SHIFT) * 1000); + + GregorianCalendar cal = new GregorianCalendar((TimeZone) gmtZone); + cal.set(year, month, day, 0, 0, 0); + if (cal.get(Calendar.DAY_OF_MONTH) != day) + throw new IllegalArgumentException("day out of range"); + + return getOffset(cal.getTimeInMillis() - rawOffset + millis); + } + + private long findTransition(long secs) + { + if (secs < (transitions[0] >> SECS_SHIFT)) + return transitions[0]; + + if (secs >= (transitions[transitions.length-1] >> SECS_SHIFT)) + return Long.MAX_VALUE; + + long val = (secs + 1) << SECS_SHIFT; + int lo = 1; + int hi = transitions.length; + int mid = 1; + while (lo < hi) + { + mid = (lo + hi) / 2; + // secs < (transitions[mid-1] >> SECS_SHIFT) + if (val <= transitions[mid-1]) + hi = mid; + // secs >= (transitions[mid] >> SECS_SHIFT) + else if (val > transitions[mid]) + lo = mid + 1; + else + break; + } + return transitions[mid]; + } + + /** + * Get the time zone offset for the specified date, modified in case of + * daylight savings. This is the offset to add to UTC to get the local + * time. + * @param date the date represented in millisecends + * since January 1, 1970 00:00:00 GMT. + */ + public int getOffset(long date) + { + long d = (date >= 0 ? date / 1000 : (date + 1) / 1000 - 1); + long transition = findTransition(d); + + // For times on or after last transition use lastRule. + if (transition == Long.MAX_VALUE) + return lastRule.getOffset(date); + + return (int) (((transition << OFFSET_SHIFT) >> OFFSET_SHIFT) * 1000); + } + + /** + * Returns the time zone offset to GMT in milliseconds, ignoring + * day light savings. + * @return the time zone offset. + */ + public int getRawOffset() + { + return rawOffset; + } + + /** + * Sets the standard time zone offset to GMT. + * @param rawOffset The time offset from GMT in milliseconds. + */ + public void setRawOffset(int rawOffset) + { + this.rawOffset = rawOffset; + lastRule.setRawOffset(rawOffset); + } + + private void computeDSTSavings() + { + if (lastRule.useDaylightTime()) + { + dstSavings = lastRule.getDSTSavings(); + useDaylight = true; + } + else + { + dstSavings = 0; + useDaylight = false; + // lastRule might say no DST is in effect simply because + // the DST rules are too complex for SimpleTimeZone, say + // for Asia/Jerusalem. + // Look at the last DST offset if it is newer than current time. + long currentSecs = System.currentTimeMillis() / 1000; + int i; + for (i = transitions.length - 1; + i >= 0 && currentSecs < (transitions[i] >> SECS_SHIFT); + i--) + if ((transitions[i] & IS_DST) != 0) + { + dstSavings = (int) (((transitions[i] << OFFSET_SHIFT) + >> OFFSET_SHIFT) * 1000) + - rawOffset; + useDaylight = true; + break; + } + } + } + + /** + * Gets the daylight savings offset. This is a positive offset in + * milliseconds with respect to standard time. Typically this + * is one hour, but for some time zones this may be half an our. + * @return the daylight savings offset in milliseconds. + */ + public int getDSTSavings() + { + return dstSavings; + } + + /** + * Returns if this time zone uses daylight savings time. + * @return true, if we use daylight savings time, false otherwise. + */ + public boolean useDaylightTime() + { + return useDaylight; + } + + /** + * Determines if the given date is in daylight savings time. + * @return true, if it is in daylight savings time, false otherwise. + */ + public boolean inDaylightTime(Date date) + { + long d = date.getTime(); + d = (d >= 0 ? d / 1000 : (d + 1) / 1000 - 1); + long transition = findTransition(d); + + // For times on or after last transition use lastRule. + if (transition == Long.MAX_VALUE) + return lastRule.inDaylightTime(date); + + return (transition & IS_DST) != 0; + } + + /** + * Generates the hashCode for the SimpleDateFormat object. It is + * the rawOffset, possibly, if useDaylightSavings is true, xored + * with startYear, startMonth, startDayOfWeekInMonth, ..., endTime. + */ + public synchronized int hashCode() + { + int hash = lastRule.hashCode(); + // FIXME - hash transitions? + return hash; + } + + public synchronized boolean equals(Object o) + { + if (! hasSameRules((TimeZone) o)) + return false; + + ZoneInfo zone = (ZoneInfo) o; + return getID().equals(zone.getID()); + } + + /** + * Test if the other time zone uses the same rule and only + * possibly differs in ID. This implementation for this particular + * class will return true if the other object is a ZoneInfo, + * the raw offsets and useDaylight are identical and if useDaylight + * is true, also the start and end datas are identical. + * @return true if this zone uses the same rule. + */ + public boolean hasSameRules(TimeZone o) + { + if (this == o) + return true; + if (! (o instanceof ZoneInfo)) + return false; + ZoneInfo zone = (ZoneInfo) o; + if (zone.hashCode() != hashCode() || rawOffset != zone.rawOffset) + return false; + if (! lastRule.equals(zone.lastRule)) + return false; + // FIXME - compare transitions? + return true; + } + + /** + * Returns a string representation of this ZoneInfo object. + * @return a string representation of this ZoneInfo object. + */ + public String toString() + { + return getClass().getName() + "[" + "id=" + getID() + ",offset=" + + rawOffset + ",transitions=" + transitions.length + + ",useDaylight=" + useDaylight + + (useDaylight ? (",dstSavings=" + dstSavings) : "") + + ",lastRule=" + lastRule.toString() + "]"; + } + + /** + * Reads zic(8) compiled timezone data file from file + * and returns a TimeZone class describing it, either + * SimpleTimeZone or ZoneInfo depending on whether + * it can be described by SimpleTimeZone rule or not. + */ + public static TimeZone readTZFile(String id, String file) + { + DataInputStream dis = null; + try + { + FileInputStream fis = new FileInputStream(file); + BufferedInputStream bis = new BufferedInputStream(fis); + dis = new DataInputStream(bis); + + // Make sure we are reading a tzfile. + byte[] tzif = new byte[5]; + dis.readFully(tzif); + int tzif2 = 4; + if (tzif[0] == 'T' && tzif[1] == 'Z' + && tzif[2] == 'i' && tzif[3] == 'f') + { + if (tzif[4] >= '2') + tzif2 = 8; + // Reserved bytes + skipFully(dis, 16 - 1); + } + else + // Darwin has tzdata files that don't start with the TZif marker + skipFully(dis, 16 - 5); + + int ttisgmtcnt = dis.readInt(); + int ttisstdcnt = dis.readInt(); + int leapcnt = dis.readInt(); + int timecnt = dis.readInt(); + int typecnt = dis.readInt(); + int charcnt = dis.readInt(); + if (tzif2 == 8) + { + skipFully(dis, timecnt * (4 + 1) + typecnt * (4 + 1 + 1) + charcnt + + leapcnt * (4 + 4) + ttisgmtcnt + ttisstdcnt); + + dis.readFully(tzif); + if (tzif[0] != 'T' || tzif[1] != 'Z' || tzif[2] != 'i' + || tzif[3] != 'f' || tzif[4] < '2') + return null; + + // Reserved bytes + skipFully(dis, 16 - 1); + ttisgmtcnt = dis.readInt(); + ttisstdcnt = dis.readInt(); + leapcnt = dis.readInt(); + timecnt = dis.readInt(); + typecnt = dis.readInt(); + charcnt = dis.readInt(); + } + + // Sanity checks + if (typecnt <= 0 || timecnt < 0 || charcnt < 0 + || leapcnt < 0 || ttisgmtcnt < 0 || ttisstdcnt < 0 + || ttisgmtcnt > typecnt || ttisstdcnt > typecnt) + return null; + + // Transition times + long[] times = new long[timecnt]; + for (int i = 0; i < timecnt; i++) + if (tzif2 == 8) + times[i] = dis.readLong(); + else + times[i] = (long) dis.readInt(); + + // Transition types + int[] types = new int[timecnt]; + for (int i = 0; i < timecnt; i++) + { + types[i] = dis.readByte(); + if (types[i] < 0) + types[i] += 256; + if (types[i] >= typecnt) + return null; + } + + // Types + int[] offsets = new int[typecnt]; + int[] typeflags = new int[typecnt]; + for (int i = 0; i < typecnt; i++) + { + offsets[i] = dis.readInt(); + if (offsets[i] >= IS_DST / 2 || offsets[i] <= -IS_DST / 2) + return null; + int dst = dis.readByte(); + int abbrind = dis.readByte(); + if (abbrind < 0) + abbrind += 256; + if (abbrind >= charcnt) + return null; + typeflags[i] = (dst != 0 ? (1 << 8) : 0) + abbrind; + } + + // Abbrev names + byte[] names = new byte[charcnt]; + dis.readFully(names); + + // Leap transitions, for now ignore + skipFully(dis, leapcnt * (tzif2 + 4) + ttisstdcnt + ttisgmtcnt); + + // tzIf2 format has optional POSIX TZ env string + String tzstr = null; + if (tzif2 == 8 && dis.readByte() == '\n') + { + tzstr = dis.readLine(); + if (tzstr.length() <= 0) + tzstr = null; + } + + // Get std/dst_offset and dst/non-dst time zone names. + int std_ind = -1; + int dst_ind = -1; + if (timecnt == 0) + std_ind = 0; + else + for (int i = timecnt - 1; i >= 0; i--) + { + if (std_ind == -1 && (typeflags[types[i]] & (1 << 8)) == 0) + std_ind = types[i]; + else if (dst_ind == -1 && (typeflags[types[i]] & (1 << 8)) != 0) + dst_ind = types[i]; + if (dst_ind != -1 && std_ind != -1) + break; + } + + if (std_ind == -1) + return null; + + int j = typeflags[std_ind] & 255; + while (j < charcnt && names[j] != 0) + j++; + String std_zonename = new String(names, typeflags[std_ind] & 255, + j - (typeflags[std_ind] & 255), + "ASCII"); + + String dst_zonename = ""; + if (dst_ind != -1) + { + j = typeflags[dst_ind] & 255; + while (j < charcnt && names[j] != 0) + j++; + dst_zonename = new String(names, typeflags[dst_ind] & 255, + j - (typeflags[dst_ind] & 255), "ASCII"); + } + + // Only use gmt offset when necessary. + // Also special case GMT+/- timezones. + String std_offset_string = ""; + String dst_offset_string = ""; + if (tzstr == null + && (dst_ind != -1 + || (offsets[std_ind] != 0 + && !std_zonename.startsWith("GMT+") + && !std_zonename.startsWith("GMT-")))) + { + std_offset_string = Integer.toString(-offsets[std_ind] / 3600); + int seconds = -offsets[std_ind] % 3600; + if (seconds != 0) + { + if (seconds < 0) + seconds *= -1; + if (seconds < 600) + std_offset_string += ":0" + Integer.toString(seconds / 60); + else + std_offset_string += ":" + Integer.toString(seconds / 60); + seconds = seconds % 60; + if (seconds >= 10) + std_offset_string += ":" + Integer.toString(seconds); + else if (seconds > 0) + std_offset_string += ":0" + Integer.toString(seconds); + } + + if (dst_ind != -1 && offsets[dst_ind] != offsets[std_ind] + 3600) + { + dst_offset_string = Integer.toString(-offsets[dst_ind] / 3600); + seconds = -offsets[dst_ind] % 3600; + if (seconds != 0) + { + if (seconds < 0) + seconds *= -1; + if (seconds < 600) + dst_offset_string + += ":0" + Integer.toString(seconds / 60); + else + dst_offset_string + += ":" + Integer.toString(seconds / 60); + seconds = seconds % 60; + if (seconds >= 10) + dst_offset_string += ":" + Integer.toString(seconds); + else if (seconds > 0) + dst_offset_string += ":0" + Integer.toString(seconds); + } + } + } + + /* + * If no tzIf2 POSIX TZ string is available and the timezone + * uses DST, try to guess the last rule by trying to make + * sense from transitions at 5 years in the future and onwards. + * tzdata actually uses only 3 forms of rules: + * fixed date within a month, e.g. change on April, 5th + * 1st weekday on or after Nth: change on Sun>=15 in April + * last weekday in a month: change on lastSun in April + */ + String[] change_spec = { null, null }; + if (tzstr == null && dst_ind != -1 && timecnt > 10) + { + long nowPlus5y = System.currentTimeMillis() / 1000 + + 5 * 365 * 86400; + int first; + + for (first = timecnt - 1; first >= 0; first--) + if (times[first] < nowPlus5y + || (types[first] != std_ind && types[first] != dst_ind) + || types[first] != types[timecnt - 2 + ((first ^ timecnt) & 1)]) + break; + first++; + + if (timecnt - first >= 10 && types[timecnt - 1] != types[timecnt - 2]) + { + GregorianCalendar cal + = new GregorianCalendar(new SimpleTimeZone(0, "GMT")); + + int[] values = new int[2 * 11]; + int i; + for (i = timecnt - 1; i >= first; i--) + { + int base = (i % 2) * 11; + int offset = offsets[types[i > first ? i - 1 : i + 1]]; + cal.setTimeInMillis((times[i] + offset) * 1000); + if (i >= timecnt - 2) + { + values[base + 0] = cal.get(Calendar.YEAR); + values[base + 1] = cal.get(Calendar.MONTH); + values[base + 2] = cal.get(Calendar.DAY_OF_MONTH); + values[base + 3] + = cal.getActualMaximum(Calendar.DAY_OF_MONTH); + values[base + 4] = cal.get(Calendar.DAY_OF_WEEK); + values[base + 5] = cal.get(Calendar.HOUR_OF_DAY); + values[base + 6] = cal.get(Calendar.MINUTE); + values[base + 7] = cal.get(Calendar.SECOND); + values[base + 8] = values[base + 2]; // Range start + values[base + 9] = values[base + 2]; // Range end + values[base + 10] = 0; // Determined type + } + else + { + int year = cal.get(Calendar.YEAR); + int month = cal.get(Calendar.MONTH); + int day_of_month = cal.get(Calendar.DAY_OF_MONTH); + int month_days + = cal.getActualMaximum(Calendar.DAY_OF_MONTH); + int day_of_week = cal.get(Calendar.DAY_OF_WEEK); + int hour = cal.get(Calendar.HOUR_OF_DAY); + int minute = cal.get(Calendar.MINUTE); + int second = cal.get(Calendar.SECOND); + if (year != values[base + 0] - 1 + || month != values[base + 1] + || hour != values[base + 5] + || minute != values[base + 6] + || second != values[base + 7]) + break; + if (day_of_week == values[base + 4]) + { + // Either a Sun>=8 or lastSun rule. + if (day_of_month < values[base + 8]) + values[base + 8] = day_of_month; + if (day_of_month > values[base + 9]) + values[base + 9] = day_of_month; + if (values[base + 10] < 0) + break; + if (values[base + 10] == 0) + { + values[base + 10] = 1; + // If day of month > 28, this is + // certainly lastSun rule. + if (values[base + 2] > 28) + values[base + 2] = 3; + // If day of month isn't in the last + // week, it can't be lastSun rule. + else if (values[base + 2] + <= values[base + 3] - 7) + values[base + 3] = 2; + } + if (values[base + 10] == 1) + { + // If day of month is > 28, this is + // certainly lastSun rule. + if (day_of_month > 28) + values[base + 10] = 3; + // If day of month isn't in the last + // week, it can't be lastSun rule. + else if (day_of_month <= month_days - 7) + values[base + 10] = 2; + } + else if ((values[base + 10] == 2 + && day_of_month > 28) + || (values[base + 10] == 3 + && day_of_month <= month_days - 7)) + break; + } + else + { + // Must be fixed day in month rule. + if (day_of_month != values[base + 2] + || values[base + 10] > 0) + break; + values[base + 4] = day_of_week; + values[base + 10] = -1; + } + values[base + 0] -= 1; + } + } + + if (i < first) + { + for (i = 0; i < 2; i++) + { + int base = 11 * i; + if (values[base + 10] == 0) + continue; + if (values[base + 10] == -1) + { + int[] dayCount + = { 0, 31, 59, 90, 120, 151, + 181, 212, 243, 273, 304, 334 }; + int d = dayCount[values[base + 1] + - Calendar.JANUARY]; + d += values[base + 2]; + change_spec[i] = ",J" + Integer.toString(d); + } + else if (values[base + 10] == 2) + { + // If we haven't seen all days of the week, + // we can't be sure what the rule really is. + if (values[base + 8] + 6 != values[base + 9]) + continue; + + int d; + d = values[base + 1] - Calendar.JANUARY + 1; + // E.g. Sun >= 5 is not representable in POSIX + // TZ env string, use ",Am.n.d" extension + // where m is month 1 .. 12, n is the date on + // or after which it happens and d is day + // of the week, 0 .. 6. So Sun >= 5 in April + // is ",A4.5.0". + if ((values[base + 8] % 7) == 1) + { + change_spec[i] = ",M" + Integer.toString(d); + d = (values[base + 8] + 6) / 7; + } + else + { + change_spec[i] = ",A" + Integer.toString(d); + d = values[base + 8]; + } + change_spec[i] += "." + Integer.toString(d); + d = values[base + 4] - Calendar.SUNDAY; + change_spec[i] += "." + Integer.toString(d); + } + else + { + // If we don't know whether this is lastSun or + // Sun >= 22 rule. That can be either because + // there was insufficient number of + // transitions, or February, where it is quite + // probable we haven't seen any 29th dates. + // For February, assume lastSun rule, otherwise + // punt. + if (values[base + 10] == 1 + && values[base + 1] != Calendar.FEBRUARY) + continue; + + int d; + d = values[base + 1] - Calendar.JANUARY + 1; + change_spec[i] = ",M" + Integer.toString(d); + d = values[base + 4] - Calendar.SUNDAY; + change_spec[i] += ".5." + Integer.toString(d); + } + + // Don't add time specification if time is + // 02:00:00. + if (values[base + 5] != 2 + || values[base + 6] != 0 + || values[base + 7] != 0) + { + int d = values[base + 5]; + change_spec[i] += "/" + Integer.toString(d); + if (values[base + 6] != 0 || values[base + 7] != 0) + { + d = values[base + 6]; + if (d < 10) + change_spec[i] + += ":0" + Integer.toString(d); + else + change_spec[i] += ":" + Integer.toString(d); + d = values[base + 7]; + if (d >= 10) + change_spec[i] + += ":" + Integer.toString(d); + else if (d > 0) + change_spec[i] + += ":0" + Integer.toString(d); + } + } + } + if (types[(timecnt - 1) & -2] == std_ind) + { + String tmp = change_spec[0]; + change_spec[0] = change_spec[1]; + change_spec[1] = tmp; + } + } + } + } + + if (tzstr == null) + { + tzstr = std_zonename + std_offset_string; + if (change_spec[0] != null && change_spec[1] != null) + tzstr += dst_zonename + dst_offset_string + + change_spec[0] + change_spec[1]; + } + + if (timecnt == 0) + return new SimpleTimeZone(offsets[std_ind] * 1000, + id != null ? id : tzstr); + + SimpleTimeZone endRule = createLastRule(tzstr); + if (endRule == null) + return null; + + /* Finally adjust the times array into the form the constructor + * expects. times[0] is special, the offset and DST flag there + * are for all times before that transition. Use the first non-DST + * type. For all other transitions, the data file has the type + * () for the time interval starting + */ + for (int i = 0; i < typecnt; i++) + if ((typeflags[i] & (1 << 8)) == 0) + { + times[0] = (times[0] << SECS_SHIFT) | (offsets[i] & OFFSET_MASK); + break; + } + + for (int i = 1; i < timecnt; i++) + times[i] = (times[i] << SECS_SHIFT) + | (offsets[types[i - 1]] & OFFSET_MASK) + | ((typeflags[types[i - 1]] & (1 << 8)) != 0 ? IS_DST : 0); + + return new ZoneInfo(offsets[std_ind] * 1000, id != null ? id : tzstr, + times, endRule); + } + 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; + } + } + + /** + * Create a SimpleTimeZone from a POSIX TZ environment string, + * see http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html + * for details. + * It supports also an extension, where Am.n.d rule (m 1 .. 12, n 1 .. 25, d + * 0 .. 6) describes day of week d on or after nth day of month m. + * Say A4.5.0 is Sun>=5 in April. + */ + private static SimpleTimeZone createLastRule(String tzstr) + { + String stdName = null; + int stdOffs; + int dstOffs; + try + { + int idLength = tzstr.length(); + + int index = 0; + int prevIndex; + char c; + + // get std + do + c = tzstr.charAt(index); + while (c != '+' && c != '-' && c != ',' && c != ':' + && ! Character.isDigit(c) && c != '\0' && ++index < idLength); + + if (index >= idLength) + return new SimpleTimeZone(0, tzstr); + + stdName = tzstr.substring(0, index); + prevIndex = index; + + // get the std offset + do + c = tzstr.charAt(index++); + while ((c == '-' || c == '+' || c == ':' || Character.isDigit(c)) + && index < idLength); + if (index < idLength) + index--; + + { // convert the dst string to a millis number + String offset = tzstr.substring(prevIndex, index); + prevIndex = index; + + if (offset.charAt(0) == '+' || offset.charAt(0) == '-') + stdOffs = parseTime(offset.substring(1)); + else + stdOffs = parseTime(offset); + + if (offset.charAt(0) == '-') + stdOffs = -stdOffs; + + // TZ timezone offsets are positive when WEST of the meridian. + stdOffs = -stdOffs; + } + + // Done yet? (Format: std offset) + if (index >= idLength) + return new SimpleTimeZone(stdOffs, stdName); + + // get dst + do + c = tzstr.charAt(index); + while (c != '+' && c != '-' && c != ',' && c != ':' + && ! Character.isDigit(c) && c != '\0' && ++index < idLength); + + // Done yet? (Format: std offset dst) + if (index >= idLength) + return new SimpleTimeZone(stdOffs, stdName); + + // get the dst offset + prevIndex = index; + do + c = tzstr.charAt(index++); + while ((c == '-' || c == '+' || c == ':' || Character.isDigit(c)) + && index < idLength); + if (index < idLength) + index--; + + if (index == prevIndex && (c == ',' || c == ';')) + { + // Missing dst offset defaults to one hour ahead of standard + // time. + dstOffs = stdOffs + 60 * 60 * 1000; + } + else + { // convert the dst string to a millis number + String offset = tzstr.substring(prevIndex, index); + prevIndex = index; + + if (offset.charAt(0) == '+' || offset.charAt(0) == '-') + dstOffs = parseTime(offset.substring(1)); + else + dstOffs = parseTime(offset); + + if (offset.charAt(0) == '-') + dstOffs = -dstOffs; + + // TZ timezone offsets are positive when WEST of the meridian. + dstOffs = -dstOffs; + } + + // Done yet? (Format: std offset dst offset) + if (index >= idLength) + return new SimpleTimeZone(stdOffs, stdName); + + // get the DST rule + if (tzstr.charAt(index) == ',' + || tzstr.charAt(index) == ';') + { + index++; + int offs = index; + while (tzstr.charAt(index) != ',' + && tzstr.charAt(index) != ';') + index++; + String startTime = tzstr.substring(offs, index); + index++; + String endTime = tzstr.substring(index); + + index = startTime.indexOf('/'); + int startMillis; + int endMillis; + String startDate; + String endDate; + if (index != -1) + { + startDate = startTime.substring(0, index); + startMillis = parseTime(startTime.substring(index + 1)); + } + else + { + startDate = startTime; + // if time isn't given, default to 2:00:00 AM. + startMillis = 2 * 60 * 60 * 1000; + } + index = endTime.indexOf('/'); + if (index != -1) + { + endDate = endTime.substring(0, index); + endMillis = parseTime(endTime.substring(index + 1)); + } + else + { + endDate = endTime; + // if time isn't given, default to 2:00:00 AM. + endMillis = 2 * 60 * 60 * 1000; + } + + int[] start = getDateParams(startDate); + int[] end = getDateParams(endDate); + return new SimpleTimeZone(stdOffs, tzstr, start[0], start[1], + start[2], startMillis, end[0], end[1], + end[2], endMillis, (dstOffs - stdOffs)); + } + } + + catch (IndexOutOfBoundsException _) + { + } + catch (NumberFormatException _) + { + } + + return null; + } + + /** + * Parses and returns the params for a POSIX TZ date field, + * in the format int[]{ month, day, dayOfWeek }, following the + * SimpleTimeZone constructor rules. + */ + private static int[] getDateParams(String date) + { + int[] dayCount = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; + int month; + int type = 0; + + if (date.charAt(0) == 'M' || date.charAt(0) == 'm') + type = 1; + else if (date.charAt(0) == 'A' || date.charAt(0) == 'a') + type = 2; + + if (type > 0) + { + int day; + + // Month, week of month, day of week + // "Mm.w.d". d is between 0 (Sunday) and 6. Week w is + // between 1 and 5; Week 1 is the first week in which day d + // occurs and Week 5 specifies the last d day in the month. + // Month m is between 1 and 12. + + // Month, day of month, day of week + // ZoneInfo extension, not in POSIX + // "Am.n.d". d is between 0 (Sunday) and 6. Day of month n is + // between 1 and 25. Month m is between 1 and 12. + + month = Integer.parseInt(date.substring(1, date.indexOf('.'))); + int week = Integer.parseInt(date.substring(date.indexOf('.') + 1, + date.lastIndexOf('.'))); + int dayOfWeek = Integer.parseInt(date.substring(date.lastIndexOf('.') + + 1)); + dayOfWeek++; // Java day of week is one-based, Sunday is first day. + + if (type == 2) + { + day = week; + dayOfWeek = -dayOfWeek; + } + else if (week == 5) + day = -1; // last day of month is -1 in java, 5 in TZ + else + { + // First day of week starting on or after. For example, + // to specify the second Sunday of April, set month to + // APRIL, day-of-month to 8, and day-of-week to -SUNDAY. + day = (week - 1) * 7 + 1; + dayOfWeek = -dayOfWeek; + } + + month--; // Java month is zero-based. + return new int[] { month, day, dayOfWeek }; + } + + // julian day, either zero-based 0<=n<=365 (incl feb 29) + // or one-based 1<=n<=365 (no feb 29) + int julianDay; // Julian day + + if (date.charAt(0) != 'J' || date.charAt(0) != 'j') + { + julianDay = Integer.parseInt(date.substring(1)); + julianDay++; // make 1-based + // Adjust day count to include feb 29. + dayCount = new int[] + { + 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 + }; + } + else + // 1-based julian day + julianDay = Integer.parseInt(date); + + int i = 11; + while (i > 0) + if (dayCount[i] < julianDay) + break; + else + i--; + julianDay -= dayCount[i]; + month = i; + return new int[] { month, julianDay, 0 }; + } + + /** + * Parses a time field hh[:mm[:ss]], returning the result + * in milliseconds. No leading sign. + */ + private static int parseTime(String time) + { + int millis = 0; + int i = 0; + + while (i < time.length()) + if (time.charAt(i) == ':') + break; + else + i++; + millis = 60 * 60 * 1000 * Integer.parseInt(time.substring(0, i)); + if (i >= time.length()) + return millis; + + int iprev = ++i; + while (i < time.length()) + if (time.charAt(i) == ':') + break; + else + i++; + millis += 60 * 1000 * Integer.parseInt(time.substring(iprev, i)); + if (i >= time.length()) + return millis; + + millis += 1000 * Integer.parseInt(time.substring(++i)); + return millis; + } +} diff --git a/libjava/classpath/java/util/Date.java b/libjava/classpath/java/util/Date.java index 5c43bf3c1542..f481753db8dd 100644 --- a/libjava/classpath/java/util/Date.java +++ b/libjava/classpath/java/util/Date.java @@ -754,6 +754,7 @@ public class Date } else if (firstch >= '0' && firstch <= '9') { + int lastPunct = -1; while (tok != null && tok.length() > 0) { int punctOffset = tok.length(); @@ -791,6 +792,13 @@ public class Date else minute = num; } + else if (lastPunct == ':' && hour >= 0 && (minute < 0 || second < 0)) + { + if (minute < 0) + minute = num; + else + second = num; + } else if ((num >= 70 && (punct == ' ' || punct == ',' || punct == '/' || punct < 0)) @@ -828,6 +836,7 @@ public class Date tok = null; else tok = tok.substring(punctOffset + 1); + lastPunct = punct; } } else if (firstch >= 'A' && firstch <= 'Z') diff --git a/libjava/classpath/java/util/SimpleTimeZone.java b/libjava/classpath/java/util/SimpleTimeZone.java index d94f89ad3f9d..14821ba0274a 100644 --- a/libjava/classpath/java/util/SimpleTimeZone.java +++ b/libjava/classpath/java/util/SimpleTimeZone.java @@ -1,5 +1,6 @@ /* java.util.SimpleTimeZone - Copyright (C) 1998, 1999, 2000, 2003, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 1998, 1999, 2000, 2003, 2004, 2005, 2007 + Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -141,8 +142,8 @@ public class SimpleTimeZone extends TimeZone /** * This variable specifies the time of change to daylight savings. - * This time is given in milliseconds after midnight local - * standard time. + * This time is given in milliseconds after midnight in startTimeMode + * chosen time mode. * @serial */ private int startTime; @@ -187,8 +188,8 @@ public class SimpleTimeZone extends TimeZone /** * This variable specifies the time of change back to standard time. - * This time is given in milliseconds after midnight local - * standard time. + * This time is given in milliseconds after midnight in endTimeMode + * chosen time mode. * @serial */ private int endTime; @@ -380,24 +381,17 @@ public class SimpleTimeZone extends TimeZone int endDayOfWeekInMonth, int endDayOfWeek, int endTime, int endTimeMode, int dstSavings) { - this.rawOffset = rawOffset; - setID(id); - useDaylight = true; + this(rawOffset, id, startMonth, startDayOfWeekInMonth, startDayOfWeek, + startTime, endMonth, endDayOfWeekInMonth, endDayOfWeek, endTime); if (startTimeMode < WALL_TIME || startTimeMode > UTC_TIME) throw new IllegalArgumentException("startTimeMode must be one of WALL_TIME, STANDARD_TIME, or UTC_TIME"); if (endTimeMode < WALL_TIME || endTimeMode > UTC_TIME) throw new IllegalArgumentException("endTimeMode must be one of WALL_TIME, STANDARD_TIME, or UTC_TIME"); - this.startTimeMode = startTimeMode; - this.endTimeMode = endTimeMode; - - setStartRule(startMonth, startDayOfWeekInMonth, startDayOfWeek, startTime); - setEndRule(endMonth, endDayOfWeekInMonth, endDayOfWeek, endTime); - if (startMonth == endMonth) - throw new IllegalArgumentException("startMonth and endMonth must be different"); - this.startYear = 0; this.dstSavings = dstSavings; + this.startTimeMode = startTimeMode; + this.endTimeMode = endTimeMode; } /** @@ -477,12 +471,8 @@ public class SimpleTimeZone extends TimeZone this.startMonth = month; this.startDay = day; this.startDayOfWeek = Math.abs(dayOfWeek); - if (this.startTimeMode == WALL_TIME || this.startTimeMode == STANDARD_TIME) - this.startTime = time; - else - // Convert from UTC to STANDARD - this.startTime = time + this.rawOffset; - useDaylight = true; + this.startTime = time; + this.startTimeMode = WALL_TIME; } /** @@ -513,24 +503,10 @@ public class SimpleTimeZone extends TimeZone public void setStartRule(int month, int day, int dayOfWeek, int time, boolean after) { - // FIXME: XXX: Validate that checkRule and offset processing work with on - // or before mode. - this.startDay = after ? Math.abs(day) : -Math.abs(day); - this.startDayOfWeek = after ? Math.abs(dayOfWeek) : -Math.abs(dayOfWeek); - this.startMode = (dayOfWeek != 0) - ? (after ? DOW_GE_DOM_MODE : DOW_LE_DOM_MODE) - : checkRule(month, day, dayOfWeek); - this.startDay = Math.abs(this.startDay); - this.startDayOfWeek = Math.abs(this.startDayOfWeek); - - this.startMonth = month; - - if (this.startTimeMode == WALL_TIME || this.startTimeMode == STANDARD_TIME) - this.startTime = time; + if (after) + setStartRule(month, day, -dayOfWeek, time); else - // Convert from UTC to STANDARD - this.startTime = time + this.rawOffset; - useDaylight = true; + setStartRule(month, -day, -dayOfWeek, time); } /** @@ -570,14 +546,8 @@ public class SimpleTimeZone extends TimeZone this.endMonth = month; this.endDay = day; this.endDayOfWeek = Math.abs(dayOfWeek); - if (this.endTimeMode == WALL_TIME) - this.endTime = time; - else if (this.endTimeMode == STANDARD_TIME) - // Convert from STANDARD to DST - this.endTime = time + this.dstSavings; - else - // Convert from UTC to DST - this.endTime = time + this.rawOffset + this.dstSavings; + this.endTime = time; + this.endTimeMode = WALL_TIME; useDaylight = true; } @@ -607,27 +577,10 @@ public class SimpleTimeZone extends TimeZone public void setEndRule(int month, int day, int dayOfWeek, int time, boolean after) { - // FIXME: XXX: Validate that checkRule and offset processing work with on - // or before mode. - this.endDay = after ? Math.abs(day) : -Math.abs(day); - this.endDayOfWeek = after ? Math.abs(dayOfWeek) : -Math.abs(dayOfWeek); - this.endMode = (dayOfWeek != 0) - ? (after ? DOW_GE_DOM_MODE : DOW_LE_DOM_MODE) - : checkRule(month, day, dayOfWeek); - this.endDay = Math.abs(this.endDay); - this.endDayOfWeek = Math.abs(endDayOfWeek); - - this.endMonth = month; - - if (this.endTimeMode == WALL_TIME) - this.endTime = time; - else if (this.endTimeMode == STANDARD_TIME) - // Convert from STANDARD to DST - this.endTime = time + this.dstSavings; + if (after) + setEndRule(month, day, -dayOfWeek, time); else - // Convert from UTC to DST - this.endTime = time + this.rawOffset + this.dstSavings; - useDaylight = true; + setEndRule(month, -day, -dayOfWeek, time); } /** @@ -688,16 +641,37 @@ public class SimpleTimeZone extends TimeZone int daylightSavings = 0; if (useDaylight && era == GregorianCalendar.AD && year >= startYear) { + int orig_year = year; + int time = startTime + (startTimeMode == UTC_TIME ? rawOffset : 0); // This does only work for Gregorian calendars :-( // This is mainly because setStartYear doesn't take an era. boolean afterStart = ! isBefore(year, month, day, dayOfWeek, millis, startMode, startMonth, startDay, - startDayOfWeek, startTime); - boolean beforeEnd = isBefore(year, month, day, dayOfWeek, - millis + dstSavings, - endMode, endMonth, endDay, endDayOfWeek, - endTime); - + startDayOfWeek, time); + millis += dstSavings; + if (millis >= 24 * 60 * 60 * 1000) + { + millis -= 24 * 60 * 60 * 1000; + dayOfWeek = (dayOfWeek % 7) + 1; + if (++day > daysInMonth) + { + day = 1; + if (month++ == Calendar.DECEMBER) + { + month = Calendar.JANUARY; + year++; + } + } + } + time = endTime + (endTimeMode == UTC_TIME ? rawOffset : 0); + if (endTimeMode != WALL_TIME) + time += dstSavings; + boolean beforeEnd = isBefore(year, month, day, dayOfWeek, millis, + endMode, endMonth, endDay, endDayOfWeek, + time); + + if (year != orig_year) + afterStart = false; if (startMonth < endMonth) // use daylight savings, if the date is after the start of // savings, and before the end of savings. diff --git a/libjava/classpath/java/util/TimeZone.java b/libjava/classpath/java/util/TimeZone.java index a253561b0467..cede9fc789ff 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; } @@ -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; - - // 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 +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) { + 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; + } } /** diff --git a/libjava/classpath/lib/gnu/java/util/ZoneInfo.class b/libjava/classpath/lib/gnu/java/util/ZoneInfo.class new file mode 100644 index 0000000000000000000000000000000000000000..3ff3706ba849a9da3bd4dc1f9f2dc08a6a02d9bb GIT binary patch literal 12350 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�C^0o`3^ztt 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 CWptka literal 10943 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 he&figl0O5E@m(1|rQvVv7)CT|n diff --git a/libjava/classpath/lib/java/util/SimpleTimeZone.class b/libjava/classpath/lib/java/util/SimpleTimeZone.class index 7009b48ce9b5e2d576359506ddec7d1618a4ab1b..1506330b6cf23565fde5ee5749ee4c4009c2eefd 100644 GIT binary patch literal 7861 zc-qxjdwf*Yo&TPD=9S4M3<(#6%s>FaFqwc#4PpcZ0yvluBoPRxWFSL`=0TH*f`|x$ z7$ZKb-Jn813YDgHbJ7ow zLT>+R8n2Mmnz*TQ{ra|KNMp>Yk!)0O7 zf;H*$g4Qm&d@$ORW?9ivM`?pD+??7#{`Q2zD&(ZW%JnOf$s5v#L`@2pfgK`&WOJQM z!f%6u^bxX2nGTR9Bs&~3*)HVl&u$V5<;muHp<}F`N4hp@ZYxb=wIy2{5{=7~t!)i0 z&6Uknsn%qoNg*@{e|5Z!CSL6Qxc=JO*RT7-VTxK+J$Fgj++}5Js^b;3Yu1Xn<>fXk z#sGO-T|M7j3tYOcv7xykHAg`gPF=3xnNP{GxaoWo6ET4XEpKQ}E@^LCn{2I4tZfu6 zFLh6hDvX!g;n0|9uAffv=-$$)%Y)Fk$i#&x)+l-ZHX(Mgi7fa8y2Qj72fEb6cn6we zq5u;Fy4=Jh2RF}z7rr2&G7}51P{GUa5?!srGclDjSD5f4AkbnHLHHELok_yD`zMkr zRG3(TO1g~XtHj64+C@o!VgHt^Oe~WvL2g;lTqhg+s8*QRPdeV%n5<7U&TXx4Z%Q_& z7HnFV+?b+w0-#N&_6J-+qPeah9XbU~?QN-owaJ3IhV|={tvnY*6;_JrsPFjCliihU zwd}o8VZ!I`mE8ecN23l3;tYG<;ewX+1(`636%2}?SV2o`K{{jtsHK|*MDhQL$^dga zX~0t$m*~q{w;{Ri2FC+F@xau00PC4`>|0UL(w-6)tyD58>TfX7fa`g4>yvVC+v3gc zmGl-mM;m!t>JpoW9Bjr$KU#RK-((eu6p4Ki6h$Z2wmAZk0Ml2bE`5<*@`I=aa3e25 zX3J?JZZfeBbrO>|n;7ZD_AMqX2fEe7Cc4WYIL~)SOl5Mnl|>2NXsRjhK1u4UKil)&RbwkaxPp zFA3s)td!T6P5c$QC|6?RMuvhynAR#k3%OD`@ra2>@fej#wK$6kBMVPIQYt3fZ=xFq zxVNEgUUGd)t7PB80kOPFd#7B(eI`{~)?av-zv50@4L}Z)$f&B$l%8R21LpxeLD?Ie zyIXLhl$`=HEErgu`H2FmQGz2#5eJjA5RXdoAVQm zG+Lt7#M5|&MSvp4?ZUT?RH6f^q$_Oc)bi7hmkRM-6MZkph$9{KiCEn`k&NaBL)P z@qPTjkJst1AxkEHC<6O=ehCE;k$+_3$9R)C+vAnYFH_E>`svXbz}u{~t0kb{kt^`7 zLh+fZ(OIuT06(P%8tP^T@N?=EYq7(WvtQ6uvHq|l=ARj?u>r9+Te$v=JO8C?&QSP;-c4tA~K{(jC{5 zIvb-p$8A#c9m_PdxSotRZ){IF%~Ppl(_p4LxA|3&@s!>kugt9Ym8noLB$Sr4q!zZc zH`ir$1yzm;`PFcRF=u6Isu3t=A6u7PpJ;DPEz3wMhx1G|5{DRxqCmXa#*H%7Xmt)N zkl>^dSv^luHVuDMYeOoTR&)GNH7naw1N985uo~xA5rwg5-)E}vB3!O3+=_k%a(sfR zCSom}Wiz(L3#)TYHCatz2C$JC`NXbOE$yxAk_#K8dgl%Pc9JdmM{wjto*8H;nU~0rTOUhX%|?aGpD0p2ugTOV4u0QSLa}9nW#cG443l z9rN8W?2Zw49OsVX-H{!uE$2jgG^B5}=jUQ_Mvf_@UN}gPF_=1F9_74fkX~V0BZgvP zrV-N$k96!<95SB|)acOo>KacdTwUW8$QK${UE>!hAW+CBV8}*<988uC2W>c;@H3eH zl>Gs40pFwO0pLw?e+x75HWo4`#xaYl7h;CXznwCPzb=X%!xy5tCB2v#)xDS{uhQt# znBI%oQEx9Ui{|%YZZy`1`LeV~Uh#fb)96enlgd=my^m4NSLmJ}(bONHK;>Yv8jh$6 zVLGqM7gVk-!j-NFJrt9tan1ii1doUiu{@U7^7WxSYWb~zm1PC{ury-%yD{tps%mnt zK86)94v};YWrJFH7D=z9q~(;ff|6F^619|V`c)`b%h=>sVYRBZC9iQMznpT5Tf@=( zm@7`s6vuRn`q-OaaL6+tB z9&&ZNR&=vWL7Exl8e3s&n3&M=iw!N4M(md%N}Zu@qYT?A!|l{~C$-&$i_{&sOx=kE zs)HXJci}44$rA~zb!97Jm>3+h#RqZQ{QX38B!bjK@T!LqRA1&s_QUSMkGLjL_@#VG znjgweOPaJL&G#HbeTgTs7h}TvkQKH(>Gd0A-P041-cFCldPO!GRmYkHYKk@%3ahSjG^5I!x)0V5E8iVRe+I>7i-9&T#z( zO4O4KhNlr%&!9>@iyGC7b*c|ZB@NdJ+@hYx4)p@=Rwr?vdXcU7%cRVYPS*-Gw1HS* zYBZW3jvT{Hz1S?4+!77s++l7#@N?9IWk0Z$8B<0SAM&!qI+$YFYpOWrpHocuLy~(5} z=_!k+b!5=dpoD`FLckb7%(9H8Yh6vJ^=rCIG~F$lcDR~$+M4chF8JVBnkcoxn1F0! zB0|O_SjM@C7?Ux{n1Vv%JQN#+How21^3p*nFCC(?Q3kKE0J+8@(!Ro`cQ%!m4pn&t zRbENVy|~XgLP4{c_MvW$M#2)#e#_%IfFN(Me^3-!UK{Nl615RtjEG!}e8HW-{WZA{ z9>YWA{cs=l$_`x;h(3!&T`Di%)2(vCoWu_(U$`WT&~`qvyxj=iXl3jdu^XcHzX-iUKhOMmq@ad9lSIXT7G1kiN z!f?y%vI)-=!X<$t7;Tw*5s2R7q1)l;T_`v{M2|5 zzcL=euZ@TC8)L81#v>|dJgV}H$5hzZr((u_HI48LNjy=ED2!(*I6j`1$?jGu7jD86MvgKfo z3~5dtiSR3B$k3W?1(*gyzPge?#90W0{1VWXr`F1Xqo#H8`eM;oEvOzN;l}dhm7EnKNByj*cG3H!5O9eRwt& z3iaW5*AU_C-ay|#*&7kmP3)vkWTJHw*Tno6%SEs=9E#J~YKNpNd2N^1Zh3Xct5aTk zc8Ilh^bBr4^-V->Lbl$FN&04-$FY!OjAJp!3pifHv4s03$WIq;kQBPAycR7wEW#JD zbng{g7x8XYu-iNt<HHO!f*L$@;{cnOwv53&DZUSa5MltS z)OTT{?!*>-H}24T@Tk5APwRW}D%TXgjqkWt8|zx_=fE=s{wD`s)Q4X>x8$hZ zCHyOii1B30zd2hr3;Y`g-Xie(4!l+1-#YL%fj@BI?E?SafoBQ)-wwP>;Ex=5x4<7e zaEHKua^OyZKXc$cg!2@fNvYGhFfZmQ*0WQ2iY?eFVxG)cdmg{-p^x#79;ZdW#RzR&K9vs$V#a<6QS7&R)(*5`e~a?=j_4A z#GOx+N6vK0nU3_C4q7a~D0Z?>6%@s^jFb8;a*2G%>Q8u^{)CABl==8$oa<4z!s9`O mCkv}R*|^1%gYBL?-0K;Q2R&oZ9e`Rs50nS5Wp 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 YB9Q_SJrwu4Yv;!$DB%r4FMVzE8&K6qI{*Lx literal 1134 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
    no;@H>4S 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 literal 27946 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 diff --git a/libjava/classpath/lib/java/util/VMTimeZone.class b/libjava/classpath/lib/java/util/VMTimeZone.class index a175a44d286d5d69c034fec81d61539d74eaf2ee..f2f9fa959310fd8a00c1b1eae3d4b95fb026f35d 100644 GIT binary patch literal 3256 zc-oCteQ;FO7609^yKi@SkZfN*v%?c+OY({lcwR+8tbjsA=Q3$!7q><{W zZ|&S*CL9IdVk>1iw^8DVr<6S2r7*p+s(EzblBdy+D-_Dsb_5YZSVP4nMXcFuJ7%wrshGw}nAc2gQn
    Xo!=)bXwh0VcS4Oc6K z$Ct0;8qA|-e9H=z|GUh~A-Gndyxw#Y^$u-YXbXaE>X!?G$LeNdtA!!t16IA$r)~sQng6=x3biJet z;sz`dQQUMHlBG23B%(pb*YI^VMZ}tnR61p`fz^hyp1P@=ki?k6Otw@ft~x2AP5`pl zQeBso31TsB(QvcE9O*leG;F(%>59@>ok{nZ8OJhh9k&XYleuouxLLyzg)1*#a%t8| zc9|K`O_PpgSgxRLGLl)dwfi#Zl_WC0>L6~zq5#`np(BoOl3m;EGctyg&NQtzG773m zLYj57U=aq)pWjIroKof*9c%Gza%($A#`#s zWjC7+zlGW+QNyEnTE{cu(=2gllw(9cv#2FV)U?zUO!&piXE-1 zmftMCS9SatgVIE*%j}np>aC2&Mb&$;M*@zCfPX5NP<$fQa__83XEO=2a4t?6y%ybL zhgza|AU|IB&&O|#Z?icnu`C#Z{|&w{5uZNScoY1X480JlKHxN&{H0nMF3_is=P`Tbg4384{z@L#P3$|S!j*X}Sh-*rhg8c++|VAb9Kww`e6^)!7@Nc2 z$fI#p?J(wv>m+V#ugzoW5N>}HcjT}#ht@){gecjnP0HTV}WxQC@i^Vl#^6DJ79 z9rzHxMsE?S8@fe=N<9Gp2VjssN+zH3=6o%;eC7#7FcC(u2 zagIv)4F>Q#yoAqi0DqWyeQJlj^9%HKf5S9W;hRMeZp?pT0COs4a> zL3rAtffH?GztxGD{}R9d6xI3+;n_!^r}6Co$AfJ6Xz}|sHh6%R*Icr4c>5xC+cB5V zpCf>G`M=wH2;&!=@jf@juL#lyr0z6n`;fQR88i{(6$JS%e8l;`MHU~Ez~7UiGdzPn z;X3C@#06IIIZ610D}Kr~&y%hTtmJb7|3_Bv7glf{Z_@TQZSUZ3tfTuP9jCaz0<7py zs#(A%AuqHD_%#HdEbMI;{K>?FL)a%w?T=CTd!m%}p_oU8bs_)D*c$bOk8pDg2(W!2 zUxPR5@pC(b<{d^w)DxN)^5yU&0si`;V}8Lg|DvPZFOy_v`69mk+yn%i>;0raK-3fQ zMi_cD<{#^b`cD*Uih53u^bU@E)HXPBCZ=5yqMc%cyYM14+Dlm8z*`*4Xc=}%JL-;k zK8K6;vxGzJGUoY|@f`j@a7!dUfq8uXi_u^3u=+Q_{F3`_1hq=>7B0gIH3=(~2d&D7 z4y9qe^21gE-cLGqsB+#MQ?N_T!5(!rPxNcBUxjc`Rp2OXuhaGxZ9n68o;lxFQ!%U} hIIU*jteS;$jQN-`pEBk%b)5>zJ?|c(BDONT{$Dk#M;8D9 literal 9024 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 F@qfOBhy?%u diff --git a/libjava/gnu/java/util/ZoneInfo.h b/libjava/gnu/java/util/ZoneInfo.h new file mode 100644 index 000000000000..83a0bf896a35 --- /dev/null +++ b/libjava/gnu/java/util/ZoneInfo.h @@ -0,0 +1,70 @@ + +// DO NOT EDIT THIS FILE - it is machine generated -*- c++ -*- + +#ifndef __gnu_java_util_ZoneInfo__ +#define __gnu_java_util_ZoneInfo__ + +#pragma interface + +#include +#include + +extern "Java" +{ + namespace gnu + { + namespace java + { + namespace util + { + class ZoneInfo; + } + } + } +} + +class gnu::java::util::ZoneInfo : public ::java::util::TimeZone +{ + +public: + ZoneInfo(jint, ::java::lang::String *, JArray< jlong > *, ::java::util::SimpleTimeZone *); + virtual jint getOffset(jint, jint, jint, jint, jint, jint); +private: + jlong findTransition(jlong); +public: + virtual jint getOffset(jlong); + virtual jint getRawOffset(); + virtual void setRawOffset(jint); +private: + void computeDSTSavings(); +public: + virtual jint getDSTSavings(); + virtual jboolean useDaylightTime(); + virtual jboolean inDaylightTime(::java::util::Date *); + virtual jint hashCode(); + virtual jboolean equals(::java::lang::Object *); + virtual jboolean hasSameRules(::java::util::TimeZone *); + virtual ::java::lang::String * toString(); + static ::java::util::TimeZone * readTZFile(::java::lang::String *, ::java::lang::String *); +private: + static void skipFully(::java::io::InputStream *, jlong); + static ::java::util::SimpleTimeZone * createLastRule(::java::lang::String *); + static JArray< jint > * getDateParams(::java::lang::String *); + static jint parseTime(::java::lang::String *); + static const jint SECS_SHIFT = 22; + static const jlong OFFSET_MASK = 2097151LL; + static const jint OFFSET_SHIFT = 43; + static const jlong IS_DST = 2097152LL; + jint __attribute__((aligned(__alignof__( ::java::util::TimeZone)))) rawOffset; + jint dstSavings; + jboolean useDaylight; + JArray< jlong > * transitions; + ::java::util::SimpleTimeZone * lastRule; + static ::java::util::SimpleTimeZone * gmtZone; +public: // actually package-private + static const jlong serialVersionUID = -3740626706860383657LL; +public: + static ::java::lang::Class class$; +}; + +#endif // __gnu_java_util_ZoneInfo__ diff --git a/libjava/java/lang/System.java b/libjava/java/lang/System.java index 587e637e9742..76a39f0d3f28 100644 --- a/libjava/java/lang/System.java +++ b/libjava/java/lang/System.java @@ -1,5 +1,5 @@ /* System.java -- useful methods to interface with the system - Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -318,6 +318,7 @@ public final class System *
    gnu.java.io.encoding_scheme_alias.latin?
    8859_?
    *
    gnu.java.io.encoding_scheme_alias.UTF-8
    UTF8
    *
    gnu.java.io.encoding_scheme_alias.utf-8
    UTF8
    + *
    gnu.java.util.zoneinfo.dir
    Root of zoneinfo tree
    * * * @return the system properties, will never be null diff --git a/libjava/java/util/TimeZone.h b/libjava/java/util/TimeZone.h index 3eb30ad5ff5c..9ae0ebc3f16a 100644 --- a/libjava/java/util/TimeZone.h +++ b/libjava/java/util/TimeZone.h @@ -40,8 +40,14 @@ public: virtual jboolean useDaylightTime() = 0; virtual jboolean inDaylightTime(::java::util::Date *) = 0; virtual jint getDSTSavings(); +private: + static ::java::util::TimeZone * getTimeZoneInternal(::java::lang::String *); +public: static ::java::util::TimeZone * getTimeZone(::java::lang::String *); static JArray< ::java::lang::String * > * getAvailableIDs(jint); +private: + static jint getAvailableIDs(::java::io::File *, ::java::lang::String *, ::java::util::ArrayList *); +public: static JArray< ::java::lang::String * > * getAvailableIDs(); static ::java::util::TimeZone * getDefault(); static void setDefault(::java::util::TimeZone *); @@ -53,6 +59,9 @@ private: ::java::lang::String * __attribute__((aligned(__alignof__( ::java::lang::Object)))) ID; static ::java::util::TimeZone * defaultZone0; static const jlong serialVersionUID = 3581463369166924961LL; + static ::java::lang::String * zoneinfo_dir; + static JArray< ::java::lang::String * > * availableIDs; + static ::java::util::HashMap * aliases0; static ::java::util::HashMap * timezones0; public: static ::java::lang::Class class$; diff --git a/libjava/java/util/VMTimeZone.h b/libjava/java/util/VMTimeZone.h index 6e571143dd01..26ca5e224cb9 100644 --- a/libjava/java/util/VMTimeZone.h +++ b/libjava/java/util/VMTimeZone.h @@ -16,8 +16,7 @@ public: // actually package-private static ::java::util::TimeZone * getDefaultTimeZoneId(); private: static ::java::lang::String * readTimeZoneFile(::java::lang::String *); - static ::java::lang::String * readtzFile(::java::lang::String *); - static void skipFully(::java::io::InputStream *, jlong); + static ::java::lang::String * readSysconfigClockFile(::java::lang::String *); static ::java::lang::String * getSystemTimeZoneId(); public: static ::java::lang::Class class$; diff --git a/libjava/java/util/VMTimeZone.java b/libjava/java/util/VMTimeZone.java index 27bab939166a..992ecaf28a8c 100644 --- a/libjava/java/util/VMTimeZone.java +++ b/libjava/java/util/VMTimeZone.java @@ -40,9 +40,9 @@ exception statement from your version. */ package java.util; import gnu.classpath.Configuration; +import gnu.classpath.SystemProperties; +import gnu.java.util.ZoneInfo; import java.util.TimeZone; -import java.util.Calendar; -import java.util.GregorianCalendar; import java.io.*; @@ -78,9 +78,10 @@ final class VMTimeZone * The reference implementation which is made for GNU/Posix like * systems calls System.getenv("TZ"), * readTimeZoneFile("/etc/timezone"), - * readtzFile("/etc/localtime") and finally - * getSystemTimeZoneId() till a supported TimeZone is - * found through TimeZone.getDefaultTimeZone(String). + * ZoneInfo.readTZFile((String)null, "/etc/localtime") + * and finally getSystemTimeZoneId() till a supported + * TimeZone is found through + * TimeZone.getDefaultTimeZone(String). * If every method fails null is returned (which means * the TimeZone code will fall back on GMT as default time zone). *

    @@ -111,9 +112,51 @@ final class VMTimeZone // Try to parse /etc/localtime if (zone == null) { - tzid = readtzFile("/etc/localtime"); - if (tzid != null && !tzid.equals("")) - zone = TimeZone.getDefaultTimeZone(tzid); + zone = ZoneInfo.readTZFile((String) null, "/etc/localtime"); + if (zone != null) + { + // Try to find a more suitable ID for the /etc/localtime + // timezone. + // Sometimes /etc/localtime is a symlink to some + // /usr/share/zoneinfo/ file. + String id = null; + try + { + id = new File("/etc/localtime").getCanonicalPath(); + if (id != null) + { + String zoneinfo_dir + = SystemProperties.getProperty("gnu.java.util.zoneinfo.dir"); + if (zoneinfo_dir != null) + zoneinfo_dir + = new File(zoneinfo_dir + + File.separatorChar).getCanonicalPath(); + if (zoneinfo_dir != null && id.startsWith(zoneinfo_dir)) + { + int pos = zoneinfo_dir.length(); + while (pos < id.length() + && id.charAt(pos) == File.separatorChar) + pos++; + if (pos < id.length()) + id = id.substring(pos); + else + id = null; + } + else + id = null; + } + } + catch (IOException ioe) + { + id = null; + } + + if (id == null) + id = readSysconfigClockFile("/etc/sysconfig/clock"); + + if (id != null) + zone.setID(id); + } } // Try some system specific way @@ -189,466 +232,47 @@ final class VMTimeZone } /** - * 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. + * Tries to read the time zone name from a file. + * If the file cannot be read or an IOException occurs 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). + * The /etc/sysconfig/clock file is not standard, but a lot of systems + * have it. The file is included by shell scripts and the timezone + * name is defined in ZONE variable. + * This routine should grok it with or without quotes: + * ZONE=America/New_York + * or + * ZONE="Europe/London" */ - private static String readtzFile(String file) + private static String readSysconfigClockFile(String file) { - File f = new File(file); - if (!f.exists()) - return null; - - DataInputStream dis = null; + BufferedReader br = null; try { - FileInputStream fis = new FileInputStream(f); + FileInputStream fis = new FileInputStream(file); BufferedInputStream bis = new BufferedInputStream(fis); - dis = new DataInputStream(bis); - - // Make sure we are reading a tzfile. - byte[] tzif = new byte[5]; - dis.readFully(tzif); - int tzif2 = 4; - if (tzif[0] == 'T' && tzif[1] == 'Z' - && tzif[2] == 'i' && tzif[3] == 'f') - { - if (tzif[4] >= '2') - tzif2 = 8; - // Reserved bytes - skipFully(dis, 16 - 1); - } - else - // Darwin has tzdata files that don't start with the TZif marker - skipFully(dis, 16 - 5); - - String id = null; - int ttisgmtcnt = dis.readInt(); - int ttisstdcnt = dis.readInt(); - int leapcnt = dis.readInt(); - int timecnt = dis.readInt(); - int typecnt = dis.readInt(); - int charcnt = dis.readInt(); - if (tzif2 == 8) - { - skipFully(dis, timecnt * (4 + 1) + typecnt * (4 + 1 + 1) + charcnt - + leapcnt * (4 + 4) + ttisgmtcnt + ttisstdcnt); - - dis.readFully(tzif); - if (tzif[0] != 'T' || tzif[1] != 'Z' || tzif[2] != 'i' - || tzif[3] != 'f' || tzif[4] < '2') - return null; - - // Reserved bytes - skipFully(dis, 16 - 1); - ttisgmtcnt = dis.readInt(); - ttisstdcnt = dis.readInt(); - leapcnt = dis.readInt(); - timecnt = dis.readInt(); - typecnt = dis.readInt(); - charcnt = dis.readInt(); - } - if (typecnt > 0) - { - int seltimecnt = timecnt; - if (seltimecnt > 16) - seltimecnt = 16; - - long[] times = new long[seltimecnt]; - int[] types = new int[seltimecnt]; - - // Transition times - skipFully(dis, (timecnt - seltimecnt) * tzif2); - - for (int i = 0; i < seltimecnt; i++) - if (tzif2 == 8) - times[i] = dis.readLong(); - else - times[i] = (long) dis.readInt(); - - // Transition types - skipFully(dis, timecnt - seltimecnt); - for (int i = 0; i < seltimecnt; i++) - { - types[i] = dis.readByte(); - if (types[i] < 0) - types[i] += 256; - } - - // Get std/dst_offset and dst/non-dst time zone names. - int std_abbrind = -1; - int dst_abbrind = -1; - int std_offset = 0; - int dst_offset = 0; - int std_ind = -1; - int dst_ind = -1; - - int alternation = 0; - if (seltimecnt >= 4 && types[0] != types[1] - && types[0] < typecnt && types[1] < typecnt) - { - // Verify only two types are involved - // in the transitions and they alternate. - alternation = 1; - for (int i = 2; i < seltimecnt; i++) - if (types[i] != types[i % 2]) - alternation = 0; - } - - // If a timezone previously used DST, but no longer does - // (or no longer will in the near future, say 5 years), - // then always pick only the std zone type corresponding - // to latest applicable transition. - if (seltimecnt > 0 - && times[seltimecnt - 1] - < System.currentTimeMillis() / 1000 + 5 * 365 * 86400) - alternation = -1; - - for (int i = 0; i < typecnt; i++) - { - // gmtoff - int offset = dis.readInt(); - int dst = dis.readByte(); - int abbrind = dis.readByte(); - if (dst == 0) - { - if (alternation == 0 - || (alternation == 1 - && (i == types[0] || i == types[1])) - || (alternation == -1 && i == types[seltimecnt - 1])) - { - std_abbrind = abbrind; - std_offset = offset * -1; - std_ind = i; - } - } - else if (alternation >= 0) - { - if (alternation == 0 || i == types[0] || i == types[1]) - { - dst_abbrind = abbrind; - dst_offset = offset * -1; - dst_ind = i; - } - } - } - - if (std_abbrind >= 0) - { - byte[] names = new byte[charcnt]; - dis.readFully(names); - int j = std_abbrind; - while (j < charcnt && names[j] != 0) - j++; - - String zonename = new String(names, std_abbrind, - j - std_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 = ""; + br = new BufferedReader(new InputStreamReader(bis)); - String[] change_spec = { null, null }; - if (dst_abbrind >= 0 && alternation > 0) - { - // Guess rules for the std->dst and dst->std transitions - // from the transition times since Epoch. - // tzdata actually uses only 3 forms of rules: - // fixed date within a month, e.g. change on April, 5th - // 1st weekday on or after Nth: change on Sun>=15 in April - // last weekday in a month: change on lastSun in April - GregorianCalendar cal - = new GregorianCalendar (TimeZone.getTimeZone("GMT")); - - int[] values = new int[2 * 11]; - int i; - for (i = seltimecnt - 1; i >= 0; i--) - { - int base = (i % 2) * 11; - int offset = types[i] == dst_ind ? std_offset : dst_offset; - cal.setTimeInMillis((times[i] - offset) * 1000); - if (i >= seltimecnt - 2) - { - values[base + 0] = cal.get(Calendar.YEAR); - values[base + 1] = cal.get(Calendar.MONTH); - values[base + 2] = cal.get(Calendar.DAY_OF_MONTH); - values[base + 3] - = cal.getActualMaximum(Calendar.DAY_OF_MONTH); - values[base + 4] = cal.get(Calendar.DAY_OF_WEEK); - values[base + 5] = cal.get(Calendar.HOUR_OF_DAY); - values[base + 6] = cal.get(Calendar.MINUTE); - values[base + 7] = cal.get(Calendar.SECOND); - values[base + 8] = values[base + 2]; // Range start - values[base + 9] = values[base + 2]; // Range end - values[base + 10] = 0; // Determined type - } - else - { - int year = cal.get(Calendar.YEAR); - int month = cal.get(Calendar.MONTH); - int day_of_month = cal.get(Calendar.DAY_OF_MONTH); - int month_days - = cal.getActualMaximum(Calendar.DAY_OF_MONTH); - int day_of_week = cal.get(Calendar.DAY_OF_WEEK); - int hour = cal.get(Calendar.HOUR_OF_DAY); - int minute = cal.get(Calendar.MINUTE); - int second = cal.get(Calendar.SECOND); - if (year != values[base + 0] - 1 - || month != values[base + 1] - || hour != values[base + 5] - || minute != values[base + 6] - || second != values[base + 7]) - break; - if (day_of_week == values[base + 4]) - { - // Either a Sun>=8 or lastSun rule. - if (day_of_month < values[base + 8]) - values[base + 8] = day_of_month; - if (day_of_month > values[base + 9]) - values[base + 9] = day_of_month; - if (values[base + 10] < 0) - break; - if (values[base + 10] == 0) - { - values[base + 10] = 1; - // If day of month > 28, this is - // certainly lastSun rule. - if (values[base + 2] > 28) - values[base + 2] = 3; - // If day of month isn't in the last - // week, it can't be lastSun rule. - else if (values[base + 2] - <= values[base + 3] - 7) - values[base + 3] = 2; - } - if (values[base + 10] == 1) - { - // If day of month is > 28, this is - // certainly lastSun rule. - if (day_of_month > 28) - values[base + 10] = 3; - // If day of month isn't in the last - // week, it can't be lastSun rule. - else if (day_of_month <= month_days - 7) - values[base + 10] = 2; - } - else if ((values[base + 10] == 2 - && day_of_month > 28) - || (values[base + 10] == 3 - && day_of_month - <= month_days - 7)) - break; - } - else - { - // Must be fixed day in month rule. - if (day_of_month != values[base + 2] - || values[base + 10] > 0) - break; - values[base + 4] = day_of_week; - values[base + 10] = -1; - } - values[base + 0] -= 1; - } - } - if (i < 0) - { - for (i = 0; i < 2; i++) - { - int base = 11 * i; - if (values[base + 10] == 0) - continue; - if (values[base + 10] == -1) - { - int[] dayCount - = { 0, 31, 59, 90, 120, 151, - 181, 212, 243, 273, 304, 334 }; - int d = dayCount[values[base + 1] - - Calendar.JANUARY]; - d += values[base + 2]; - change_spec[i] = ",J" + Integer.toString(d); - } - else if (values[base + 10] == 2) - { - // If we haven't seen all days of the week, - // we can't be sure what the rule really is. - if (values[base + 8] + 6 != values[base + 9]) - continue; - - // FIXME: Sun >= 5 is representable in - // SimpleTimeZone, but not in POSIX TZ env - // strings. Should we change readtzFile - // to actually return a SimpleTimeZone - // rather than POSIX TZ string? - if ((values[base + 8] % 7) != 1) - continue; - - int d; - d = values[base + 1] - Calendar.JANUARY + 1; - change_spec[i] = ",M" + Integer.toString(d); - d = (values[base + 8] + 6) / 7; - change_spec[i] += "." + Integer.toString(d); - d = values[base + 4] - Calendar.SUNDAY; - change_spec[i] += "." + Integer.toString(d); - } - else - { - // If we don't know whether this is lastSun or - // Sun >= 22 rule. That can be either because - // there was insufficient number of - // transitions, or February, where it is quite - // probable we haven't seen any 29th dates. - // For February, assume lastSun rule, otherwise - // punt. - if (values[base + 10] == 1 - && values[base + 1] != Calendar.FEBRUARY) - continue; - - int d; - d = values[base + 1] - Calendar.JANUARY + 1; - change_spec[i] = ",M" + Integer.toString(d); - d = values[base + 4] - Calendar.SUNDAY; - change_spec[i] += ".5." + Integer.toString(d); - } - - // Don't add time specification if time is - // 02:00:00. - if (values[base + 5] != 2 - || values[base + 6] != 0 - || values[base + 7] != 0) - { - int d = values[base + 5]; - change_spec[i] += "/" + Integer.toString(d); - if (values[base + 6] != 0 - || values[base + 7] != 0) - { - d = values[base + 6]; - if (d < 10) - change_spec[i] - += ":0" + Integer.toString(d); - else - change_spec[i] - += ":" + Integer.toString(d); - d = values[base + 7]; - if (d >= 10) - change_spec[i] - += ":" + Integer.toString(d); - else if (d > 0) - change_spec[i] - += ":0" + Integer.toString(d); - } - } - } - if (types[0] == std_ind) - { - String tmp = change_spec[0]; - change_spec[0] = change_spec[1]; - change_spec[1] = tmp; - } - } - } - - // Only use gmt offset when necessary. - // Also special case GMT+/- timezones. - String offset_string, dst_offset_string = ""; - if (dst_abbrind < 0 - && (std_offset == 0 - || zonename.startsWith("GMT+") - || zonename.startsWith("GMT-"))) - offset_string = ""; - else - { - offset_string = Integer.toString(std_offset / 3600); - int seconds = std_offset % 3600; - if (seconds != 0) - { - if (seconds < 0) - seconds *= -1; - if (seconds < 600) - offset_string - += ":0" + Integer.toString(seconds / 60); - else - offset_string - += ":" + Integer.toString(seconds / 60); - seconds = seconds % 60; - if (seconds >= 10) - offset_string - += ":" + Integer.toString(seconds); - else if (seconds > 0) - offset_string - += ":0" + Integer.toString(seconds); - } - if (dst_abbrind >= 0 - && dst_offset != std_offset - 3600) - { - dst_offset_string - = Integer.toString(dst_offset / 3600); - seconds = dst_offset % 3600; - if (seconds != 0) - { - if (seconds < 0) - seconds *= -1; - if (seconds < 600) - dst_offset_string - += ":0" + Integer.toString(seconds / 60); - else - dst_offset_string - += ":" + Integer.toString(seconds / 60); - seconds = seconds % 60; - if (seconds >= 10) - dst_offset_string - += ":" + Integer.toString(seconds); - else if (seconds > 0) - dst_offset_string - += ":0" + Integer.toString(seconds); - } - } - } - - if (dst_abbrind < 0) - id = zonename + offset_string; - else if (change_spec[0] != null && change_spec[1] != null) - id = zonename + offset_string + dst_zonename - + dst_offset_string + change_spec[0] + change_spec[1]; - } - else if (tzif2 == 8) - skipFully(dis, charcnt); - } - else if (tzif2 == 8) - skipFully(dis, timecnt * (8 + 1) + typecnt * (4 + 1 + 1) + charcnt); - - if (tzif2 == 8) + for (String line = br.readLine(); line != null; line = br.readLine()) { - // Skip over the rest of 64-bit data - skipFully(dis, leapcnt * (8 + 4) + ttisgmtcnt + ttisstdcnt); - if (dis.readByte() == '\n') + line = line.trim(); + if (line.length() < 8 || !line.startsWith("ZONE=")) + continue; + int posstart = 6; + int posend; + if (line.charAt(5) == '"') + posend = line.indexOf('"', 6); + else if (line.charAt(5) == '\'') + posend = line.indexOf('\'', 6); + else { - String posixtz = dis.readLine(); - if (posixtz.length() > 0) - id = posixtz; + posstart = 5; + posend = line.length(); } + if (posend < 0) + return null; + return line.substring(posstart, posend); } - - return id; + return null; } catch (IOException ioe) { @@ -659,31 +283,15 @@ final class VMTimeZone { try { - if (dis != null) - dis.close(); + if (br != null) + br.close(); } - catch(IOException ioe) + 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; - } - } /** * Tries to get the system time zone id through native code. diff --git a/libjava/posix.cc b/libjava/posix.cc index df798b88a2b4..5d64094c815a 100644 --- a/libjava/posix.cc +++ b/libjava/posix.cc @@ -139,6 +139,10 @@ _Jv_platform_initProperties (java::util::Properties* newprops) if (! tmpdir) tmpdir = "/tmp"; SET ("java.io.tmpdir", tmpdir); + const char *zoneinfodir = ::getenv("TZDATA"); + if (! zoneinfodir) + zoneinfodir = "/usr/share/zoneinfo"; + SET ("gnu.java.util.zoneinfo.dir", zoneinfodir); } static inline void diff --git a/libjava/sources.am b/libjava/sources.am index 01618ce0c205..77e7796ff339 100644 --- a/libjava/sources.am +++ b/libjava/sources.am @@ -2110,7 +2110,8 @@ gnu/java/text.list: $(gnu_java_text_source_files) gnu_java_util_source_files = \ classpath/gnu/java/util/DoubleEnumeration.java \ classpath/gnu/java/util/EmptyEnumeration.java \ -classpath/gnu/java/util/WeakIdentityHashMap.java +classpath/gnu/java/util/WeakIdentityHashMap.java \ +classpath/gnu/java/util/ZoneInfo.java gnu_java_util_header_files = $(patsubst classpath/%,%,$(patsubst %.java,%.h,$(gnu_java_util_source_files))) -- 2.47.3