/[classpath]/classpath/java/util/TimeZone.java
ViewVC logotype

Diff of /classpath/java/util/TimeZone.java

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.23 by robilad, Thu Apr 22 11:24:39 2004 UTC revision 1.24 by mark, Sat Aug 28 19:39:30 2004 UTC
# Line 40  exception statement from your version. * Line 40  exception statement from your version. *
40  package java.util;  package java.util;
41  import gnu.classpath.Configuration;  import gnu.classpath.Configuration;
42    
43    import java.io.*;
44    import java.security.AccessController;
45    import java.security.PrivilegedAction;
46  import java.text.DateFormatSymbols;  import java.text.DateFormatSymbols;
47    
48  /**  /**
# Line 83  public abstract class TimeZone implement Line 86  public abstract class TimeZone implement
86     * The default time zone, as returned by getDefault.     * The default time zone, as returned by getDefault.
87     */     */
88    private static TimeZone defaultZone0;    private static TimeZone defaultZone0;
89    /* initialize this static field lazily to overhead if  
90     * it is not needed:    /**
91       * Tries to get the default TimeZone for this system if not already
92       * set.  It will call <code>getDefaultTimeZone(String)</code> with
93       * the result of
94       * <code>System.getProperty("user.timezone")</code>,
95       * <code>System.getenv("TZ")</code>,
96       * <code>readTimeZoneFile("/etc/timezone")</code>,
97       * <code>readtzFile("/etc/localtime")</code> and
98       * <code>getDefaultTimeZoneId()</code>
99       * till a supported TimeZone is found.
100       * If every method fails GMT is returned.
101     */     */
102    private static synchronized TimeZone defaultZone() {    private static synchronized TimeZone defaultZone()
103      {
104      /* Look up default timezone */      /* Look up default timezone */
105      if (defaultZone0 == null)      if (defaultZone0 == null)
106        {        {
107          if (Configuration.INIT_LOAD_LIBRARY)          defaultZone0 = (TimeZone) AccessController.doPrivileged
108            {            (new PrivilegedAction()
109              System.loadLibrary("javautil");              {
110            }                public Object run()
111          String tzid = System.getProperty("user.timezone");                {
112                            if (Configuration.INIT_LOAD_LIBRARY)
113          if (tzid == null)                    {
114            tzid = getDefaultTimeZoneId();                      System.loadLibrary("javautil");
115                              }
116          if (tzid == null)                  
117            tzid = "GMT";                  TimeZone zone = null;
118                            
119          defaultZone0 = getTimeZone(tzid);                  // Prefer System property user.timezone.
120                    String tzid = System.getProperty("user.timezone");
121                    if (tzid != null && !tzid.equals(""))
122                      zone = getDefaultTimeZone(tzid);
123                    
124                    // See if TZ environment variable is set and accessible.
125                    if (zone == null)
126                      {
127                        tzid = System.getenv("TZ");
128                        if (tzid != null && !tzid.equals(""))
129                          zone = getDefaultTimeZone(tzid);
130                      }
131                    
132                    // Try to parse /etc/timezone.
133                    if (zone == null)
134                      {
135                        tzid = readTimeZoneFile("/etc/timezone");
136                        if (tzid != null && !tzid.equals(""))
137                          zone = getDefaultTimeZone(tzid);
138                      }
139                    
140                    // Try to parse /etc/localtime
141                    if (zone == null)
142                      {
143                        tzid = readtzFile("/etc/localtime");
144                        if (tzid != null && !tzid.equals(""))
145                          zone = getDefaultTimeZone(tzid);
146                      }
147                    
148                    // Try some system specific way
149                    if (zone == null)
150                      {
151                        tzid = getDefaultTimeZoneId();
152                        if (tzid != null && !tzid.equals(""))
153                          zone = getDefaultTimeZone(tzid);
154                      }
155                    
156                    // Fall back on GMT.
157                    if (zone == null)
158                      zone = (TimeZone) timezones().get("GMT");
159                    
160                    return zone;
161                  }
162                });
163        }        }
164        
165      return defaultZone0;      return defaultZone0;
166    }    }
167      
   
168    private static final long serialVersionUID = 3581463369166924961L;    private static final long serialVersionUID = 3581463369166924961L;
169    
170    /**    /**
171     * Hashtable for timezones by ID.       * HashMap for timezones by ID.  
172     */     */
173    private static Hashtable timezones0;    private static HashMap timezones0;
174    /* initialize this static field lazily to overhead if    /* initialize this static field lazily to overhead if
175     * it is not needed:     * it is not needed:
176     */     */
177    private static synchronized Hashtable timezones() {    private static synchronized HashMap timezones()
178      if (timezones0==null)    {
179        if (timezones0 == null)
180        {        {
181          Hashtable timezones = new Hashtable();          HashMap timezones = new HashMap();
182          timezones0 = timezones;          timezones0 = timezones;
183    
184          TimeZone tz;          TimeZone tz;
# Line 784  public abstract class TimeZone implement Line 842  public abstract class TimeZone implement
842      return timezones0;      return timezones0;
843    }    }
844    
845      /**
846    /* This method returns us a time zone id string which is in the     * This method returns a time zone id string which is in the form
847       form <standard zone name><GMT offset><daylight time zone name>.     * (standard zone name) or (standard zone name)(GMT offset) or
848       The GMT offset is in seconds, except where it is evenly divisible     * (standard zone name)(GMT offset)(daylight time zone name).  The
849       by 3600, then it is in hours.  If the zone does not observe     * GMT offset can be in seconds, or where it is evenly divisible by
850       daylight time, then the daylight zone name is omitted.  Examples:     * 3600, then it can be in hours.  The offset must be the time to
851       in Chicago, the timezone would be CST6CDT.  In Indianapolis     * add to the local time to get GMT.  If a offset is given and the
852       (which does not have Daylight Savings Time) the string would     * time zone observes daylight saving then the (daylight time zone
853       be EST5     * name) must also be given (otherwise it is assumed the time zone
854       * does not observe any daylight savings).
855       * <p>
856       * The result of this method is given to getDefaultTimeZone(String)
857       * which tries to map the time zone id to a known TimeZone.  See
858       * that method on how the returned String is mapped to a real
859       * TimeZone object.
860     */     */
861    private static native String getDefaultTimeZoneId();    private static native String getDefaultTimeZoneId();
862    
863    /**    /**
864       * Tries to read the time zone name from a file. Only the first
865       * consecutive letters, digits, slashes, dashes and underscores are
866       * read from the file. If the file cannot be read or an IOException
867       * occurs null is returned.
868       * <p>
869       * The /etc/timezone file is not standard, but a lot of systems have
870       * it. If it exist the first line always contains a string
871       * describing the timezone of the host of domain. Some systems
872       * contain a /etc/TIMEZONE file which is used to set the TZ
873       * environment variable (which is checked before /etc/timezone is
874       * read).
875       */
876      private static String readTimeZoneFile(String file)
877      {
878        File f = new File(file);
879        if (!f.exists())
880          return null;
881    
882        InputStreamReader isr = null;
883        try
884          {
885            FileInputStream fis = new FileInputStream(f);
886            BufferedInputStream bis = new BufferedInputStream(fis);
887            isr = new InputStreamReader(bis);
888            
889            StringBuffer sb = new StringBuffer();
890            int i = isr.read();
891            while (i != -1)
892              {
893                char c = (char) i;
894                if (Character.isLetter(c) || Character.isDigit(c)
895                    || c == '/' || c == '-' || c == '_')
896                  {
897                    sb.append(c);
898                    i = isr.read();
899                  }
900                else
901                  break;
902              }
903            return sb.toString();
904          }
905        catch (IOException ioe)
906          {
907            // Parse error, not a proper tzfile.
908            return null;
909          }
910        finally
911          {
912            try
913              {
914                if (isr != null)
915                  isr.close();
916              }
917            catch (IOException ioe)
918              {
919                // Error while close, nothing we can do.
920              }
921          }
922      }
923    
924      /**
925       * Tries to read a file as a "standard" tzfile and return a time
926       * zone id string as expected by <code>getDefaultTimeZone(String)</code>.
927       * If the file doesn't exist, an IOException occurs or it isn't a tzfile
928       * that can be parsed null is returned.
929       * <p>
930       * The tzfile structure (as also used by glibc) is described in the Olson
931       * tz database archive as can be found at
932       * <code>ftp://elsie.nci.nih.gov/pub/</code>.
933       * <p>
934       * At least the following platforms support the tzdata file format
935       * and /etc/localtime (GNU/Linux, Darwin, Solaris and FreeBSD at
936       * least). Some systems (like Darwin) don't start the file with the
937       * required magic bytes 'TZif', this implementation can handle
938       * that).
939       */
940      private static String readtzFile(String file)
941      {
942        File f = new File(file);
943        if (!f.exists())
944          return null;
945        
946        DataInputStream dis = null;
947        try
948          {
949            FileInputStream fis = new FileInputStream(f);
950            BufferedInputStream bis = new BufferedInputStream(fis);
951            dis = new DataInputStream(bis);
952            
953            // Make sure we are reading a tzfile.
954            byte[] tzif = new byte[4];
955            dis.readFully(tzif);
956            if (tzif[0] == 'T' && tzif[1] == 'Z'
957                && tzif[2] == 'i' && tzif[3] == 'f')
958              // Reserved bytes, ttisgmtcnt, ttisstdcnt and leapcnt
959              skipFully(dis, 16 + 3 * 4);
960            else
961              // Darwin has tzdata files that don't start with the TZif marker
962              skipFully(dis, 16 + 3 * 4 - 4);
963            
964            int timecnt = dis.readInt();
965            int typecnt = dis.readInt();
966            if (typecnt > 0)
967              {
968                int charcnt = dis.readInt();
969                // Transition times plus indexed transition times.
970                skipFully(dis, timecnt * (4 + 1));
971                
972                // Get last gmt_offset and dst/non-dst time zone names.
973                int abbrind = -1;
974                int dst_abbrind = -1;
975                int gmt_offset = 0;
976                while (typecnt-- > 0)
977                  {
978                    // gmtoff
979                    int offset = dis.readInt();
980                    int dst = dis.readByte();
981                    if (dst == 0)
982                      {
983                        abbrind = dis.readByte();
984                        gmt_offset = offset;
985                      }
986                    else
987                      dst_abbrind = dis.readByte();
988                  }
989                
990                // gmt_offset is the offset you must add to UTC/GMT to
991                // get the local time, we need the offset to add to
992                // the local time to get UTC/GMT.
993                gmt_offset *= -1;
994                
995                // Turn into hours if possible.
996                if (gmt_offset % 3600 == 0)
997                  gmt_offset /= 3600;
998                
999                if (abbrind >= 0)
1000                  {
1001                    byte[] names = new byte[charcnt];
1002                    dis.readFully(names);
1003                    int j = abbrind;
1004                    while (j < charcnt && names[j] != 0)
1005                      j++;
1006                    
1007                    String zonename = new String(names, abbrind, j - abbrind,
1008                                                 "ASCII");
1009                    
1010                    String dst_zonename;
1011                    if (dst_abbrind >= 0)
1012                      {
1013                        j = dst_abbrind;
1014                        while (j < charcnt && names[j] != 0)
1015                          j++;
1016                        dst_zonename = new String(names, dst_abbrind,
1017                                                  j - dst_abbrind, "ASCII");
1018                      }
1019                    else
1020                      dst_zonename = "";
1021                    
1022                    // Only use gmt offset when necessary.
1023                    // Also special case GMT+/- timezones.
1024                    String offset_string;
1025                    if ("".equals(dst_zonename)
1026                        && (gmt_offset == 0
1027                            || zonename.startsWith("GMT+")
1028                            || zonename.startsWith("GMT-")))
1029                      offset_string = "";
1030                    else
1031                      offset_string = Integer.toString(gmt_offset);
1032                    
1033                    String id = zonename + offset_string + dst_zonename;
1034                    
1035                    return id;
1036                  }
1037              }
1038            
1039            // Something didn't match while reading the file.
1040            return null;
1041          }
1042        catch (IOException ioe)
1043          {
1044            // Parse error, not a proper tzfile.
1045            return null;
1046          }
1047        finally
1048          {
1049            try
1050              {
1051                if (dis != null)
1052                  dis.close();
1053              }
1054            catch(IOException ioe)
1055              {
1056                // Error while close, nothing we can do.
1057              }
1058          }
1059      }
1060      
1061      /**
1062       * Skips the requested number of bytes in the given InputStream.
1063       * Throws EOFException if not enough bytes could be skipped.
1064       * Negative numbers of bytes to skip are ignored.
1065       */
1066      private static void skipFully(InputStream is, long l) throws IOException
1067      {
1068        while (l > 0)
1069          {
1070            long k = is.skip(l);
1071            if (k <= 0)
1072              throw new EOFException();
1073            l -= k;
1074          }
1075      }
1076      
1077      /**
1078       * Maps a time zone name (with optional GMT offset and daylight time
1079       * zone name) to one of the known time zones.  This method called
1080       * with the result of <code>System.getProperty("user.timezone")</code>
1081       * or <code>getDefaultTimeZoneId()</code>.  Note that giving one of
1082       * the standard tz data names from ftp://elsie.nci.nih.gov/pub/ is
1083       * preferred.  The time zone name can be given as follows:
1084       * <code>(standard zone name)[(GMT offset)[(daylight time zone name)]]</code>
1085       * <p>
1086       * If only a (standard zone name) is given (no numbers in the
1087       * String) then it gets mapped directly to the TimeZone with that
1088       * name, if that fails null is returned.
1089       * <p>
1090       * A GMT offset is the offset to add to the local time to get GMT.
1091       * If a (GMT offset) is included (either in seconds or hours) then
1092       * an attempt is made to find a TimeZone name matching both the name
1093       * and the offset (that doesn't observe daylight time, if the
1094       * timezone observes daylight time then you must include a daylight
1095       * time zone name after the offset), if that fails then a TimeZone
1096       * with the given GMT offset is returned (whether or not the
1097       * TimeZone observes daylight time is ignored), if that also fails
1098       * the GMT TimeZone is returned.
1099       * <p>
1100       * If the String ends with (GMT offset)(daylight time zone name)
1101       * then an attempt is made to find a TimeZone with the given name and
1102       * GMT offset that also observes (the daylight time zone name is not
1103       * currently used in any other way), if that fails a TimeZone with
1104       * the given GMT offset that observes daylight time is returned, if
1105       * that also fails the GMT TimeZone is returned.
1106       * <p>
1107       * Examples: In Chicago, the time zone id could be "CST6CDT", but
1108       * the preferred name would be "America/Chicago".  In Indianapolis
1109       * (which does not have Daylight Savings Time) the string could be
1110       * "EST5", but the preferred name would be "America/Indianapolis".
1111       * The standard time zone name for The Netherlands is "Europe/Amsterdam",
1112       * but can also be given as "CET-1CEST".
1113       */
1114      private static TimeZone getDefaultTimeZone(String sysTimeZoneId)
1115      {
1116        // First find start of GMT offset info and any Daylight zone name.
1117        int startGMToffset = 0;
1118        int sysTimeZoneIdLength = sysTimeZoneId.length();
1119        for (int i = 0; i < sysTimeZoneIdLength && startGMToffset == 0; i++)
1120          {
1121            char c = sysTimeZoneId.charAt(i);
1122            if (c == '+' || c == '-' || Character.isDigit(c))
1123              startGMToffset = i;
1124          }
1125        
1126        String tzBasename;
1127        if (startGMToffset == 0)
1128          tzBasename = sysTimeZoneId;
1129        else
1130          tzBasename = sysTimeZoneId.substring (0, startGMToffset);
1131        
1132        int startDaylightZoneName = 0;
1133        for (int i = sysTimeZoneIdLength - 1;
1134             i >= 0 && !Character.isDigit(sysTimeZoneId.charAt(i)); --i)
1135          startDaylightZoneName = i;
1136        
1137        boolean useDaylightTime = startDaylightZoneName > 0;
1138        
1139        // Integer.parseInt() doesn't handle leading +.
1140        if (sysTimeZoneId.charAt(startGMToffset) == '+')
1141          startGMToffset++;
1142        
1143        int gmtOffset = 0;
1144        if (startGMToffset > 0)
1145          {
1146            gmtOffset = Integer.parseInt
1147              (startDaylightZoneName == 0
1148               ? sysTimeZoneId.substring(startGMToffset)
1149               : sysTimeZoneId.substring(startGMToffset,
1150                                         startDaylightZoneName));
1151            
1152            // Offset could be in hours or seconds.  Convert to millis.
1153            // The offset is given as the time to add to local time to get GMT
1154            // we need the time to add to GMT to get localtime.
1155            if (gmtOffset < 24)
1156              gmtOffset *= 60 * 60;
1157            gmtOffset *= -1000;
1158          }
1159        
1160        // Try to be optimistic and get the timezone that matches the base name.
1161        // If we only have the base name then just accept this timezone.
1162        // Otherwise check the gmtOffset and day light attributes.
1163        TimeZone tz = (TimeZone) timezones().get(tzBasename);
1164        if (tz != null
1165            && (tzBasename == sysTimeZoneId
1166                || (tz.getRawOffset() == gmtOffset
1167                    && tz.useDaylightTime() == useDaylightTime)))
1168          return tz;
1169        
1170        // Maybe there is one with the daylight zone name?
1171        if (useDaylightTime)
1172          {
1173            String daylightZoneName;
1174            daylightZoneName = sysTimeZoneId.substring(startDaylightZoneName);
1175            if (!daylightZoneName.equals(tzBasename))
1176              {
1177                tz = (TimeZone) timezones().get(tzBasename);
1178                if (tz != null
1179                    && tz.getRawOffset() == gmtOffset
1180                    && tz.useDaylightTime())
1181                  return tz;
1182              }
1183          }
1184        
1185        // If no match, see if a valid timezone has similar attributes as this
1186        // and then use it instead. We take the first one that looks OKish.
1187        if (startGMToffset > 0)
1188          {
1189            String[] ids = getAvailableIDs(gmtOffset);
1190            for (int i = 0; i < ids.length; i++)
1191              {
1192                tz = (TimeZone) timezones().get(ids[i]);
1193                if (tz.useDaylightTime() == useDaylightTime)
1194                  return tz;
1195              }
1196          }
1197        
1198        return null;
1199      }
1200    
1201      /**
1202     * Gets the time zone offset, for current date, modified in case of     * Gets the time zone offset, for current date, modified in case of
1203     * daylight savings.  This is the offset to add to UTC to get the local     * daylight savings.  This is the offset to add to UTC to get the local
1204     * time.     * time.
# Line 1140  public abstract class TimeZone implement Line 1542  public abstract class TimeZone implement
1542    /**    /**
1543     * Returns the time zone under which the host is running.  This     * Returns the time zone under which the host is running.  This
1544     * can be changed with setDefault.     * can be changed with setDefault.
1545     * @return the time zone for this host.     *
1546       * @return A clone of the current default time zone for this host.
1547     * @see #setDefault     * @see #setDefault
1548     */     */
1549    public static TimeZone getDefault()    public static TimeZone getDefault()
1550    {    {
1551      return defaultZone();      return (TimeZone) defaultZone().clone();
1552    }    }
1553    
1554    public static void setDefault(TimeZone zone)    public static void setDefault(TimeZone zone)
1555    {    {
1556        // Hmmmm. No Security checks?
1557      defaultZone0 = zone;      defaultZone0 = zone;
1558    }    }
1559    

Legend:
Removed from v.1.23  
changed lines
  Added in v.1.24

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26