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