1 /* java.util.VMTimeZone
2    Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2007
3    Free Software Foundation, Inc.
4 
5 This file is part of GNU Classpath.
6 
7 GNU Classpath is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
11 
12 GNU Classpath is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with GNU Classpath; see the file COPYING.  If not, write to the
19 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 02110-1301 USA.
21 
22 Linking this library statically or dynamically with other modules is
23 making a combined work based on this library.  Thus, the terms and
24 conditions of the GNU General Public License cover the whole
25 combination.
26 
27 As a special exception, the copyright holders of this library give you
28 permission to link this library with independent modules to produce an
29 executable, regardless of the license terms of these independent
30 modules, and to copy and distribute the resulting executable under
31 terms of your choice, provided that you also meet, for each linked
32 independent module, the terms and conditions of the license of that
33 module.  An independent module is a module which is not derived from
34 or based on this library.  If you modify this library, you may extend
35 this exception to your version of the library, but you are not
36 obligated to do so.  If you do not wish to do so, delete this
37 exception statement from your version. */
38 
39 
40 package java.util;
41 
42 import gnu.classpath.Configuration;
43 import gnu.classpath.SystemProperties;
44 import gnu.java.util.ZoneInfo;
45 import java.util.TimeZone;
46 
47 import java.io.*;
48 
49 /**
50  *
51  */
52 final class VMTimeZone
53 {
54   static
55   {
56     if (Configuration.INIT_LOAD_LIBRARY)
57       {
58 	System.loadLibrary("javautil");
59       }
60   }
61 
62   /**
63    * This method returns a time zone id string which is in the form
64    * (standard zone name) or (standard zone name)(GMT offset) or
65    * (standard zone name)(GMT offset)(daylight time zone name).  The
66    * GMT offset can be in seconds, or where it is evenly divisible by
67    * 3600, then it can be in hours.  The offset must be the time to
68    * add to the local time to get GMT.  If a offset is given and the
69    * time zone observes daylight saving then the (daylight time zone
70    * name) must also be given (otherwise it is assumed the time zone
71    * does not observe any daylight savings).
72    * <p>
73    * The result of this method is given to the method
74    * TimeZone.getDefaultTimeZone(String) which tries to map the time
75    * zone id to a known TimeZone.  See that method on how the returned
76    * String is mapped to a real TimeZone object.
77    * <p>
78    * The reference implementation which is made for GNU/Posix like
79    * systems calls <code>System.getenv("TZ")</code>,
80    * <code>readTimeZoneFile("/etc/timezone")</code>,
81    * <code>ZoneInfo.readTZFile((String)null, "/etc/localtime")</code>
82    * and finally <code>getSystemTimeZoneId()</code> till a supported
83    * TimeZone is found through
84    * <code>TimeZone.getDefaultTimeZone(String)</code>.
85    * If every method fails <code>null</code> is returned (which means
86    * the TimeZone code will fall back on GMT as default time zone).
87    * <p>
88    * Note that this method is called inside a
89    * <code>AccessController.doPrivileged()</code> block and runs with
90    * the priviliges of the java.util system classes.  It will only be
91    * called when the default time zone is not yet set, the system
92    * property user.timezone isn't set and it is requested for the
93    * first time.
94    */
getDefaultTimeZoneId()95   static TimeZone getDefaultTimeZoneId()
96   {
97     TimeZone zone = null;
98 
99     // See if TZ environment variable is set and accessible.
100     String tzid = System.getenv("TZ");
101     if (tzid != null && !tzid.equals(""))
102       zone = TimeZone.getDefaultTimeZone(tzid);
103 
104     // Try to parse /etc/timezone.
105     if (zone == null)
106       {
107 	tzid = readTimeZoneFile("/etc/timezone");
108 	if (tzid != null && !tzid.equals(""))
109 	  zone = TimeZone.getDefaultTimeZone(tzid);
110       }
111 
112     // Try to parse /etc/localtime
113     if (zone == null)
114       {
115 	zone = ZoneInfo.readTZFile((String) null, "/etc/localtime");
116 	if (zone != null)
117 	  {
118 	    // Try to find a more suitable ID for the /etc/localtime
119 	    // timezone.
120 	    // Sometimes /etc/localtime is a symlink to some
121 	    // /usr/share/zoneinfo/ file.
122 	    String id = null;
123 	    try
124 	      {
125 		id = new File("/etc/localtime").getCanonicalPath();
126 		if (id != null)
127 		  {
128 		    String zoneinfo_dir
129 		      = SystemProperties.getProperty("gnu.java.util.zoneinfo.dir");
130 		    if (zoneinfo_dir != null)
131 		      zoneinfo_dir
132 			= new File(zoneinfo_dir
133 				   + File.separatorChar).getCanonicalPath();
134 		    if (zoneinfo_dir != null && id.startsWith(zoneinfo_dir))
135 		      {
136 			int pos = zoneinfo_dir.length();
137 			while (pos < id.length()
138 			       && id.charAt(pos) == File.separatorChar)
139 			  pos++;
140 			if (pos < id.length())
141 			  id = id.substring(pos);
142 			else
143 			  id = null;
144 		      }
145 		    else
146 		      id = null;
147 		  }
148 	      }
149 	    catch (IOException ioe)
150 	      {
151 		id = null;
152 	      }
153 
154 	    if (id == null)
155 	      id = readSysconfigClockFile("/etc/sysconfig/clock");
156 
157 	    if (id != null)
158 	      zone.setID(id);
159 	  }
160       }
161 
162     // Try some system specific way
163     if (zone == null)
164       {
165 	tzid = getSystemTimeZoneId();
166 	if (tzid != null && !tzid.equals(""))
167 	  zone = TimeZone.getDefaultTimeZone(tzid);
168       }
169 
170     return zone;
171   }
172 
173   /**
174    * Tries to read the time zone name from a file. Only the first
175    * consecutive letters, digits, slashes, dashes and underscores are
176    * read from the file. If the file cannot be read or an IOException
177    * occurs null is returned.
178    * <p>
179    * The /etc/timezone file is not standard, but a lot of systems have
180    * it. If it exist the first line always contains a string
181    * describing the timezone of the host of domain. Some systems
182    * contain a /etc/TIMEZONE file which is used to set the TZ
183    * environment variable (which is checked before /etc/timezone is
184    * read).
185    */
readTimeZoneFile(String file)186   private static String readTimeZoneFile(String file)
187   {
188     File f = new File(file);
189     if (!f.exists())
190       return null;
191 
192     InputStreamReader isr = null;
193     try
194       {
195 	FileInputStream fis = new FileInputStream(f);
196 	BufferedInputStream bis = new BufferedInputStream(fis);
197 	isr = new InputStreamReader(bis);
198 
199 	StringBuffer sb = new StringBuffer();
200 	int i = isr.read();
201 	while (i != -1)
202 	  {
203 	    char c = (char) i;
204 	    if (Character.isLetter(c) || Character.isDigit(c)
205 		|| c == '/' || c == '-' || c == '_')
206 	      {
207 		sb.append(c);
208 		i = isr.read();
209 	      }
210 	    else
211 	      break;
212 	  }
213 	return sb.toString();
214       }
215     catch (IOException ioe)
216       {
217 	// Parse error, not a proper tzfile.
218 	return null;
219       }
220     finally
221       {
222 	try
223 	  {
224 	    if (isr != null)
225 	      isr.close();
226 	  }
227 	catch (IOException ioe)
228 	  {
229 	    // Error while close, nothing we can do.
230 	  }
231       }
232   }
233 
234   /**
235    * Tries to read the time zone name from a file.
236    * If the file cannot be read or an IOException occurs null is returned.
237    * <p>
238    * The /etc/sysconfig/clock file is not standard, but a lot of systems
239    * have it. The file is included by shell scripts and the timezone
240    * name is defined in ZONE variable.
241    * This routine should grok it with or without quotes:
242    * ZONE=America/New_York
243    * or
244    * ZONE="Europe/London"
245    */
readSysconfigClockFile(String file)246   private static String readSysconfigClockFile(String file)
247   {
248     BufferedReader br = null;
249     try
250       {
251 	FileInputStream fis = new FileInputStream(file);
252 	BufferedInputStream bis = new BufferedInputStream(fis);
253 	br = new BufferedReader(new InputStreamReader(bis));
254 
255 	for (String line = br.readLine(); line != null; line = br.readLine())
256 	  {
257 	    line = line.trim();
258 	    if (line.length() < 8 || !line.startsWith("ZONE="))
259 	      continue;
260 	    int posstart = 6;
261 	    int posend;
262 	    if (line.charAt(5) == '"')
263 	      posend = line.indexOf('"', 6);
264 	    else if (line.charAt(5) == '\'')
265 	      posend = line.indexOf('\'', 6);
266 	    else
267 	      {
268 		posstart = 5;
269 		posend = line.length();
270 	      }
271 	    if (posend < 0)
272 	      return null;
273 	    return line.substring(posstart, posend);
274 	  }
275 	return null;
276       }
277     catch (IOException ioe)
278       {
279 	// Parse error, not a proper tzfile.
280 	return null;
281       }
282     finally
283       {
284 	try
285 	  {
286 	    if (br != null)
287 	      br.close();
288 	  }
289 	catch (IOException ioe)
290 	  {
291 	    // Error while close, nothing we can do.
292 	  }
293       }
294   }
295 
296   /**
297    * Tries to get the system time zone id through native code.
298    */
getSystemTimeZoneId()299   private static native String getSystemTimeZoneId();
300 }
301