diff options
author | Mark Wielaard <mark@klomp.org> | 2004-08-31 21:06:48 +0000 |
---|---|---|
committer | Mark Wielaard <mark@klomp.org> | 2004-08-31 21:06:48 +0000 |
commit | 8ef734b0127d79d350319873d3f67b4df2b4f978 (patch) | |
tree | 9506d93bc4e3891b0c5212f58a11420e44a7b22c | |
parent | cd7fecc0ae8950167100846e29b1c62ce0660ba1 (diff) | |
download | classpath-8ef734b0127d79d350319873d3f67b4df2b4f978.tar.gz |
* configure.ac: Check for sys/time.h, localtime_r, tm_gmtoff in
struct tm and for for global timezone variable.
* include/Makefile.am: Generate include/java_util_VMTimeZone.h.
* include/java_util_TimeZone.h: Removed.
* include/java_util_VMTimeZone.h: Added.
* java/util/TimeZone.java (defaultZone): Use VMTimeZone.
(getDefaultTimeZone): Make package private. Check that GMToffset
contains at least one digit.
(getDefaultTimeZoneId, readTimeZoneFile, readtzFile): (Re)Moved to
VMTimeZone.
* vm/reference/java/util/VMTimeZone.java: New file with above methods.
* native/jni/java-util/Makefile.am: Compile new java_util_VMTimeZone.c.
* native/jni/java-util/java_util_TimeZone.c: Removed.
* native/jni/java-util/java_util_VMTimeZone.c: New file.
* native/target/generic/target_generic_misc.h
(TARGET_NATIVE_MISC_GET_TIMEZONE_STRING): Removed unused macro.
* NEWS: Mention TimeZone/VMTimeZone split.
-rw-r--r-- | ChangeLog | 20 | ||||
-rw-r--r-- | NEWS | 3 | ||||
-rw-r--r-- | configure.ac | 37 | ||||
-rw-r--r-- | include/Makefile.am | 6 | ||||
-rw-r--r-- | include/java_util_TimeZone.h | 19 | ||||
-rw-r--r-- | include/java_util_VMTimeZone.h | 19 | ||||
-rw-r--r-- | java/util/TimeZone.java | 290 | ||||
-rw-r--r-- | native/jni/java-util/Makefile.am | 2 | ||||
-rw-r--r-- | native/jni/java-util/java_util_TimeZone.c | 78 | ||||
-rw-r--r-- | native/jni/java-util/java_util_VMTimeZone.c | 220 | ||||
-rw-r--r-- | native/target/generic/target_generic_misc.h | 51 | ||||
-rw-r--r-- | vm/reference/java/util/VMTimeZone.java | 346 |
12 files changed, 654 insertions, 437 deletions
@@ -1,3 +1,23 @@ +2004-08-29 Mark Wielaard <mark@klomp.org> + + * configure.ac: Check for sys/time.h, localtime_r, tm_gmtoff in + struct tm and for for global timezone variable. + * include/Makefile.am: Generate include/java_util_VMTimeZone.h. + * include/java_util_TimeZone.h: Removed. + * include/java_util_VMTimeZone.h: Added. + * java/util/TimeZone.java (defaultZone): Use VMTimeZone. + (getDefaultTimeZone): Make package private. Check that GMToffset + contains at least one digit. + (getDefaultTimeZoneId, readTimeZoneFile, readtzFile): (Re)Moved to + VMTimeZone. + * vm/reference/java/util/VMTimeZone.java: New file with above methods. + * native/jni/java-util/Makefile.am: Compile new java_util_VMTimeZone.c. + * native/jni/java-util/java_util_TimeZone.c: Removed. + * native/jni/java-util/java_util_VMTimeZone.c: New file. + * native/target/generic/target_generic_misc.h + (TARGET_NATIVE_MISC_GET_TIMEZONE_STRING): Removed unused macro. + * NEWS: Mention TimeZone/VMTimeZone split. + 2004-08-31 Michael Koch <konqueror@gmx.de> * javax/swing/DefaultListSelectionModel.java @@ -20,6 +20,9 @@ Runtime interface Changes: a reference implementation that most VMs can use. * java.lang.VMSystem has a new getenv(String) method and a reference C/JNI implementation that should work on most Posix like systems. +* java.util.TimeZone has been split into a platform independent class and + a platform dependent class VMTimeZone. GNU Classpath comes with a generic + way to get at the default time zone for Posix/GNU-like platforms. New in release 0.10 (Jul 9, 2004) diff --git a/configure.ac b/configure.ac index 0a5123bf9..a5125481f 100644 --- a/configure.ac +++ b/configure.ac @@ -122,23 +122,48 @@ if test "x${COMPILE_JNI}" = xyes; then dnl We check for sys/filio.h because Solaris 2.5 defines FIONREAD there. dnl On that system, sys/ioctl.h will not include sys/filio.h unless dnl BSD_COMP is defined; just including sys/filio.h is simpler. - AC_CHECK_HEADERS(unistd.h sys/types.h sys/config.h sys/ioctl.h asm/ioctls.h) - AC_CHECK_HEADERS(inttypes.h stdint.h utime.h sys/utime.h sys/filio.h) + AC_CHECK_HEADERS([unistd.h sys/types.h sys/config.h sys/ioctl.h asm/ioctls.h]) + AC_CHECK_HEADERS([inttypes.h stdint.h utime.h sys/utime.h sys/filio.h]) + AC_CHECK_HEADERS([sys/time.h]) AC_EGREP_HEADER(uint32_t, stdint.h, AC_DEFINE(HAVE_INT32_DEFINED, 1, [Define to 1 if you have uint32_t])) AC_EGREP_HEADER(uint32_t, inttypes.h, AC_DEFINE(HAVE_INT32_DEFINED, 1, [Define to 1 if you have uint32_t])) AC_EGREP_HEADER(u_int32_t, sys/types.h, AC_DEFINE(HAVE_BSD_INT32_DEFINED, 1, [Define to 1 if you have BSD u_int32_t])) AC_EGREP_HEADER(u_int32_t, sys/config.h, AC_DEFINE(HAVE_BSD_INT32_DEFINED, 1, [Define to 1 if you have BSD u_int32_t])) - AC_CHECK_FUNCS(ftruncate fsync select) - AC_CHECK_FUNCS(gethostname socket strerror fork pipe execve open close lseek \ - fstat read write htonl memset htons connect getsockname sizeof getpeername \ - bind listen accept recvfrom send sendto setsockopt getsockopt time mktime) + AC_CHECK_FUNCS([ftruncate fsync select]) + AC_CHECK_FUNCS([gethostname socket strerror fork pipe execve open close]) + AC_CHECK_FUNCS([lseek fstat read write htonl memset htons connect]) + AC_CHECK_FUNCS([getsockname sizeof getpeername bind listen accept]) + AC_CHECK_FUNCS([recvfrom send sendto setsockopt getsockopt time mktime]) + AC_CHECK_FUNCS([localtime_r]) AC_HEADER_TIME AC_STRUCT_TM AC_STRUCT_TIMEZONE + AC_MSG_CHECKING([for tm_gmtoff in struct tm]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <time.h>]],[[struct tm tim; tim.tm_gmtoff = 0;]])], + [AC_DEFINE(STRUCT_TM_HAS_GMTOFF, 1, [Define if struct tm has tm_gmtoff field.]) + AC_MSG_RESULT(yes)], + [AC_MSG_RESULT(no) + AC_MSG_CHECKING([for global timezone variable]) + dnl FIXME: we don't want a link check here because that won't work + dnl when cross-compiling. So instead we make an assumption that + dnl the header file will mention timezone if it exists. + dnl Don't find the win32 function timezone + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <time.h>]], [[void i(){long z2 = 2*timezone;}]])], + [AC_DEFINE(HAVE_TIMEZONE, 1, [Define if global 'timezone' exists.]) + AC_MSG_RESULT(yes)], + [AC_MSG_RESULT(no) + AC_MSG_CHECKING([for global _timezone variable]) + dnl FIXME: As above, don't want link check + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <time.h>]], [[long z2 = _timezone;]])], + [AC_DEFINE(HAVE_UNDERSCORE_TIMEZONE, 1, + [Define if your platform has the global _timezone variable.]) + AC_MSG_RESULT(yes)], + [AC_MSG_RESULT(no)])])]) + AC_C_CONST dnl FIXME - does not allow cross compiling diff --git a/include/Makefile.am b/include/Makefile.am index 5a6b5a88d..942756ff2 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -64,7 +64,7 @@ $(top_srcdir)/include/java_net_NetworkInterface.h \ $(top_srcdir)/include/java_nio_DirectByteBufferImpl.h \ $(top_srcdir)/include/java_nio_MappedByteBufferImpl.h \ $(top_srcdir)/include/java_nio_channels_Channels.h \ -$(top_srcdir)/include/java_util_TimeZone.h +$(top_srcdir)/include/java_util_VMTimeZone.h if CREATE_JNI_HEADERS @@ -188,7 +188,7 @@ $(top_srcdir)/include/java_nio_channels_Channels.h: $(top_srcdir)/java/nio/chann $(JAVAH) -o $@ java.nio.channels.Channels $(top_srcdir)/include/gnu_java_nio_channels_FileChannelImpl.h: $(top_srcdir)/gnu/java/nio/channels/FileChannelImpl.java $(JAVAH) -o $@ gnu.java.nio.channels.FileChannelImpl -$(top_srcdir)/include/java_util_TimeZone.h: $(top_srcdir)/java/util/TimeZone.java - $(JAVAH) -o $@ java.util.TimeZone +$(top_srcdir)/include/java_util_VMTimeZone.h: $(top_srcdir)/vm/reference/java/util/VMTimeZone.java + $(JAVAH) -o $@ java.util.VMTimeZone endif # CREATE_JNI_HEADERS diff --git a/include/java_util_TimeZone.h b/include/java_util_TimeZone.h deleted file mode 100644 index 2854c6634..000000000 --- a/include/java_util_TimeZone.h +++ /dev/null @@ -1,19 +0,0 @@ -/* DO NOT EDIT THIS FILE - it is machine generated */ - -#ifndef __java_util_TimeZone__ -#define __java_util_TimeZone__ - -#include <jni.h> - -#ifdef __cplusplus -extern "C" -{ -#endif - -JNIEXPORT jstring JNICALL Java_java_util_TimeZone_getDefaultTimeZoneId (JNIEnv *env, jclass); - -#ifdef __cplusplus -} -#endif - -#endif /* __java_util_TimeZone__ */ diff --git a/include/java_util_VMTimeZone.h b/include/java_util_VMTimeZone.h new file mode 100644 index 000000000..8431e67b4 --- /dev/null +++ b/include/java_util_VMTimeZone.h @@ -0,0 +1,19 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ + +#ifndef __java_util_VMTimeZone__ +#define __java_util_VMTimeZone__ + +#include <jni.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + +JNIEXPORT jstring JNICALL Java_java_util_VMTimeZone_getSystemTimeZoneId (JNIEnv *env, jclass); + +#ifdef __cplusplus +} +#endif + +#endif /* __java_util_VMTimeZone__ */ diff --git a/java/util/TimeZone.java b/java/util/TimeZone.java index cc3c7a3d2..772f89305 100644 --- a/java/util/TimeZone.java +++ b/java/util/TimeZone.java @@ -38,9 +38,7 @@ exception statement from your version. */ package java.util; -import gnu.classpath.Configuration; -import java.io.*; import java.security.AccessController; import java.security.PrivilegedAction; import java.text.DateFormatSymbols; @@ -90,14 +88,9 @@ public abstract class TimeZone implements java.io.Serializable, Cloneable /** * Tries to get the default TimeZone for this system if not already * set. It will call <code>getDefaultTimeZone(String)</code> with - * the result of - * <code>System.getProperty("user.timezone")</code>, - * <code>System.getenv("TZ")</code>, - * <code>readTimeZoneFile("/etc/timezone")</code>, - * <code>readtzFile("/etc/localtime")</code> and - * <code>getDefaultTimeZoneId()</code> - * till a supported TimeZone is found. - * If every method fails GMT is returned. + * the result of <code>System.getProperty("user.timezone")</code>. + * If that fails it calls <code>VMTimeZone.getDefaultTimeZoneId()</code>. + * If that also fails GMT is returned. */ private static synchronized TimeZone defaultZone() { @@ -109,11 +102,6 @@ public abstract class TimeZone implements java.io.Serializable, Cloneable { public Object run() { - if (Configuration.INIT_LOAD_LIBRARY) - { - System.loadLibrary("javautil"); - } - TimeZone zone = null; // Prefer System property user.timezone. @@ -121,37 +109,9 @@ public abstract class TimeZone implements java.io.Serializable, Cloneable if (tzid != null && !tzid.equals("")) zone = getDefaultTimeZone(tzid); - // See if TZ environment variable is set and accessible. - if (zone == null) - { - tzid = System.getenv("TZ"); - if (tzid != null && !tzid.equals("")) - zone = getDefaultTimeZone(tzid); - } - - // Try to parse /etc/timezone. + // Try platfom specific way. if (zone == null) - { - tzid = readTimeZoneFile("/etc/timezone"); - if (tzid != null && !tzid.equals("")) - zone = getDefaultTimeZone(tzid); - } - - // Try to parse /etc/localtime - if (zone == null) - { - tzid = readtzFile("/etc/localtime"); - if (tzid != null && !tzid.equals("")) - zone = getDefaultTimeZone(tzid); - } - - // Try some system specific way - if (zone == null) - { - tzid = getDefaultTimeZoneId(); - if (tzid != null && !tzid.equals("")) - zone = getDefaultTimeZone(tzid); - } + zone = VMTimeZone.getDefaultTimeZoneId(); // Fall back on GMT. if (zone == null) @@ -843,238 +803,6 @@ public abstract class TimeZone implements java.io.Serializable, Cloneable } /** - * This method returns a time zone id string which is in the form - * (standard zone name) or (standard zone name)(GMT offset) or - * (standard zone name)(GMT offset)(daylight time zone name). The - * GMT offset can be in seconds, or where it is evenly divisible by - * 3600, then it can be in hours. The offset must be the time to - * add to the local time to get GMT. If a offset is given and the - * time zone observes daylight saving then the (daylight time zone - * name) must also be given (otherwise it is assumed the time zone - * does not observe any daylight savings). - * <p> - * The result of this method is given to getDefaultTimeZone(String) - * which tries to map the time zone id to a known TimeZone. See - * that method on how the returned String is mapped to a real - * TimeZone object. - */ - private static native String getDefaultTimeZoneId(); - - /** - * Tries to read the time zone name from a file. Only the first - * consecutive letters, digits, slashes, dashes and underscores are - * read from the file. If the file cannot be read or an IOException - * occurs null is returned. - * <p> - * The /etc/timezone file is not standard, but a lot of systems have - * it. If it exist the first line always contains a string - * describing the timezone of the host of domain. Some systems - * contain a /etc/TIMEZONE file which is used to set the TZ - * environment variable (which is checked before /etc/timezone is - * read). - */ - private static String readTimeZoneFile(String file) - { - File f = new File(file); - if (!f.exists()) - return null; - - InputStreamReader isr = null; - try - { - FileInputStream fis = new FileInputStream(f); - BufferedInputStream bis = new BufferedInputStream(fis); - isr = new InputStreamReader(bis); - - StringBuffer sb = new StringBuffer(); - int i = isr.read(); - while (i != -1) - { - char c = (char) i; - if (Character.isLetter(c) || Character.isDigit(c) - || c == '/' || c == '-' || c == '_') - { - sb.append(c); - i = isr.read(); - } - else - break; - } - return sb.toString(); - } - catch (IOException ioe) - { - // Parse error, not a proper tzfile. - return null; - } - finally - { - try - { - if (isr != null) - isr.close(); - } - catch (IOException ioe) - { - // Error while close, nothing we can do. - } - } - } - - /** - * Tries to read a file as a "standard" tzfile and return a time - * zone id string as expected by <code>getDefaultTimeZone(String)</code>. - * If the file doesn't exist, an IOException occurs or it isn't a tzfile - * that can be parsed null is returned. - * <p> - * The tzfile structure (as also used by glibc) is described in the Olson - * tz database archive as can be found at - * <code>ftp://elsie.nci.nih.gov/pub/</code>. - * <p> - * 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). - */ - private static String readtzFile(String file) - { - File f = new File(file); - if (!f.exists()) - return null; - - DataInputStream dis = null; - try - { - FileInputStream fis = new FileInputStream(f); - BufferedInputStream bis = new BufferedInputStream(fis); - dis = new DataInputStream(bis); - - // Make sure we are reading a tzfile. - byte[] tzif = new byte[4]; - dis.readFully(tzif); - if (tzif[0] == 'T' && tzif[1] == 'Z' - && tzif[2] == 'i' && tzif[3] == 'f') - // Reserved bytes, ttisgmtcnt, ttisstdcnt and leapcnt - skipFully(dis, 16 + 3 * 4); - else - // Darwin has tzdata files that don't start with the TZif marker - skipFully(dis, 16 + 3 * 4 - 4); - - int timecnt = dis.readInt(); - int typecnt = dis.readInt(); - if (typecnt > 0) - { - int charcnt = dis.readInt(); - // Transition times plus indexed transition times. - skipFully(dis, timecnt * (4 + 1)); - - // Get last gmt_offset and dst/non-dst time zone names. - int abbrind = -1; - int dst_abbrind = -1; - int gmt_offset = 0; - while (typecnt-- > 0) - { - // gmtoff - int offset = dis.readInt(); - int dst = dis.readByte(); - if (dst == 0) - { - abbrind = dis.readByte(); - gmt_offset = offset; - } - else - dst_abbrind = dis.readByte(); - } - - // gmt_offset is the offset you must add to UTC/GMT to - // get the local time, we need the offset to add to - // the local time to get UTC/GMT. - gmt_offset *= -1; - - // Turn into hours if possible. - if (gmt_offset % 3600 == 0) - gmt_offset /= 3600; - - if (abbrind >= 0) - { - byte[] names = new byte[charcnt]; - dis.readFully(names); - int j = abbrind; - while (j < charcnt && names[j] != 0) - j++; - - String zonename = new String(names, abbrind, j - 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 = ""; - - // Only use gmt offset when necessary. - // Also special case GMT+/- timezones. - String offset_string; - if ("".equals(dst_zonename) - && (gmt_offset == 0 - || zonename.startsWith("GMT+") - || zonename.startsWith("GMT-"))) - offset_string = ""; - else - offset_string = Integer.toString(gmt_offset); - - String id = zonename + offset_string + dst_zonename; - - return id; - } - } - - // Something didn't match while reading the file. - return null; - } - 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; - } - } - - /** * Maps a time zone name (with optional GMT offset and daylight time * zone name) to one of the known time zones. This method called * with the result of <code>System.getProperty("user.timezone")</code> @@ -1111,7 +839,7 @@ public abstract class TimeZone implements java.io.Serializable, Cloneable * The standard time zone name for The Netherlands is "Europe/Amsterdam", * but can also be given as "CET-1CEST". */ - private static TimeZone getDefaultTimeZone(String sysTimeZoneId) + static TimeZone getDefaultTimeZone(String sysTimeZoneId) { // First find start of GMT offset info and any Daylight zone name. int startGMToffset = 0; @@ -1119,7 +847,11 @@ public abstract class TimeZone implements java.io.Serializable, Cloneable for (int i = 0; i < sysTimeZoneIdLength && startGMToffset == 0; i++) { char c = sysTimeZoneId.charAt(i); - if (c == '+' || c == '-' || Character.isDigit(c)) + if (Character.isDigit(c)) + startGMToffset = i; + else if ((c == '+' || c == '-') + && i + 1 < sysTimeZoneIdLength + && Character.isDigit(sysTimeZoneId.charAt(i + 1))) startGMToffset = i; } diff --git a/native/jni/java-util/Makefile.am b/native/jni/java-util/Makefile.am index f7497e6d5..7a06bb709 100644 --- a/native/jni/java-util/Makefile.am +++ b/native/jni/java-util/Makefile.am @@ -1,5 +1,5 @@ pkglib_LTLIBRARIES = libjavautil.la -libjavautil_la_SOURCES = java_util_TimeZone.c +libjavautil_la_SOURCES = java_util_VMTimeZone.c libjavautil_la_LDFLAGS = @CLASSPATH_MODULE@ diff --git a/native/jni/java-util/java_util_TimeZone.c b/native/jni/java-util/java_util_TimeZone.c deleted file mode 100644 index 1c8cf5d2f..000000000 --- a/native/jni/java-util/java_util_TimeZone.c +++ /dev/null @@ -1,78 +0,0 @@ -/* TimeZone.c - Native methods for java.util.TimeZone - Copyright (C) 1999 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., 59 Temple Place, Suite 330, Boston, MA -02111-1307 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. */ - -/* do not move; needed here because of some macro definitions */ -#include "config.h" - -#include <stdio.h> -#include <stdlib.h> - -#include <jni.h> - -#include "target_native.h" -#include "target_native_misc.h" - -#include "java_util_TimeZone.h" - -/* - * This method returns a time zone string that is used by the static - * initializer in java.util.TimeZone to create the default timezone - * instance. This is a key into the timezone table used by - * that class. - * - * Class: java_util_TimeZone - * Method: getDefaultTimeZoneId - * Signature: ()Ljava/lang/String; - */ -JNIEXPORT jstring JNICALL -Java_java_util_TimeZone_getDefaultTimeZoneId(JNIEnv *env, jclass clazz) -{ -#ifdef HAVE_TZNAME - char buffer[128]; - int result; - jstring retval; - - TARGET_NATIVE_MISC_GET_TIMEZONE_STRING(buffer,sizeof(buffer),result); - - retval = (*env)->NewStringUTF(env, buffer); - - return(retval); -#else - return(0); /* added this statement (crashes without..:-) --Fridi. */ -#endif /* HAVE_TZNAME */ -} - diff --git a/native/jni/java-util/java_util_VMTimeZone.c b/native/jni/java-util/java_util_VMTimeZone.c new file mode 100644 index 000000000..536f7bfef --- /dev/null +++ b/native/jni/java-util/java_util_VMTimeZone.c @@ -0,0 +1,220 @@ +/* VMTimeZone.c - Native method for java.util.VMTimeZone + Copyright (C) 1999, 2004 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., 59 Temple Place, Suite 330, Boston, MA +02111-1307 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. */ + +#include "config.h" + +#if TIME_WITH_SYS_TIME +# include <sys/time.h> +# include <time.h> +#else +# if HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif +#endif + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <jni.h> + +#include "java_util_VMTimeZone.h" + +size_t jint_to_charbuf (char* bufend, jint num); + +/** + * This method returns a time zone id string which is in the form + * (standard zone name) or (standard zone name)(GMT offset) or + * (standard zone name)(GMT offset)(daylight time zone name). The + * GMT offset can be in seconds, or where it is evenly divisible by + * 3600, then it can be in hours. The offset must be the time to + * add to the local time to get GMT. If a offset is given and the + * time zone observes daylight saving then the (daylight time zone + * name) must also be given (otherwise it is assumed the time zone + * does not observe any daylight savings). + * <p> + * The result of this method is given to getDefaultTimeZone(String) + * which tries to map the time zone id to a known TimeZone. See + * that method on how the returned String is mapped to a real + * TimeZone object. + */ +JNIEXPORT jstring JNICALL +Java_java_util_VMTimeZone_getSystemTimeZoneId(JNIEnv *env, jclass clazz) +{ + struct tm tim; +#ifndef HAVE_LOCALTIME_R + struct tm *lt_tim; +#endif +#ifdef HAVE_TM_ZONE + int month; +#endif + time_t current_time; + long tzoffset; + const char *tz1, *tz2; + char tzoff[11]; + size_t tz1_len, tz2_len, tzoff_len; + char *tzid; + jstring retval; + + time(¤t_time); +#ifdef HAVE_LOCALTIME_R + localtime_r(¤t_time, &tim); +#else + /* Fall back on non-thread safe localtime. */ + lt_tim = localtime(¤t_time); + memcpy(&tim, lt_tim, sizeof (struct tm)); +#endif + mktime(&tim); + +#ifdef HAVE_STRUCT_TM_TM_ZONE + /* We will cycle through the months to make sure we hit dst. */ + month = tim.tm_mon; + tz1 = tz2 = NULL; + while (tz1 == NULL || tz2 == NULL) + { + if (tim.tm_isdst > 0) + tz2 = tim.tm_zone; + else if (tz1 == NULL) + { + tz1 = tim.tm_zone; + month = tim.tm_mon; + } + + if (tz1 == NULL || tz2 == NULL) + { + tim.tm_mon++; + tim.tm_mon %= 12; + } + + if (tim.tm_mon == month && tz2 == NULL) + tz2 = ""; + else + mktime(&tim); + } + /* We want to make sure the tm struct we use later on is not dst. */ + tim.tm_mon = month; + mktime(&tim); +#elif defined (HAVE_TZNAME) + /* If dst is never used, tzname[1] is the empty string. */ + tzset(); + tz1 = tzname[0]; + tz2 = tzname[1]; +#else + /* Some targets have no concept of timezones. Assume GMT without dst. */ + tz1 = "GMT"; + tz2 = ""; +#endif + +#ifdef STRUCT_TM_HAS_GMTOFF + /* tm_gmtoff is the number of seconds that you must add to GMT to get + local time, we need the number of seconds to add to the local time + to get GMT. */ + tzoffset = -1L * tim.tm_gmtoff; +#elif HAVE_UNDERSCORE_TIMEZONE + tzoffset = _timezone; +#elif HAVE_TIMEZONE + /* timezone is secs WEST of UTC. */ + tzoffset = timezone; +#else + /* FIXME: there must be another global if neither tm_gmtoff nor timezone + is available, esp. if tzname is valid. + Richard Earnshaw <rearnsha@arm.com> has suggested using difftime to + calculate between gmtime and localtime (and accounting for possible + daylight savings time) as an alternative. */ + tzoffset = 0L; +#endif + + if ((tzoffset % 3600) == 0) + tzoffset = tzoffset / 3600; + + tz1_len = strlen(tz1); + tz2_len = strlen(tz2); + tzoff_len = jint_to_charbuf (tzoff + 11, tzoffset); + tzid = (char*) malloc (tz1_len + tz2_len + tzoff_len + 1); /* FIXME alloc */ + memcpy (tzid, tz1, tz1_len); + memcpy (tzid + tz1_len, tzoff + 11 - tzoff_len, tzoff_len); + memcpy (tzid + tz1_len + tzoff_len, tz2, tz2_len); + tzid[tz1_len + tzoff_len + tz2_len] = '\0'; + + retval = (*env)->NewStringUTF (env, tzid); + free (tzid); + + return retval; +} + +/* Put printed (decimal) representation of NUM in a buffer. + BUFEND marks the end of the buffer, which must be at least 11 chars long. + Returns the COUNT of chars written. The result is in + (BUFEND - COUNT) (inclusive) upto (BUFEND) (exclusive). + + Note that libgcj has a slightly different version called _Jv_FormatInt + that works on jchar buffers. +*/ + +static size_t +jint_to_charbuf (char* bufend, jint num) +{ + register char* ptr = bufend; + jboolean isNeg; + if (num < 0) + { + isNeg = JNI_TRUE; + num = -(num); + if (num < 0) + { + /* Must be MIN_VALUE, so handle this special case. + FIXME use 'unsigned jint' for num. */ + *--ptr = '8'; + num = 214748364; + } + } + else + isNeg = JNI_FALSE; + + do + { + *--ptr = (char) ((int) '0' + (num % 10)); + num /= 10; + } + while (num > 0); + + if (isNeg) + *--ptr = '-'; + return bufend - ptr; +} diff --git a/native/target/generic/target_generic_misc.h b/native/target/generic/target_generic_misc.h index 19e3ab2d6..e3a4de6e5 100644 --- a/native/target/generic/target_generic_misc.h +++ b/native/target/generic/target_generic_misc.h @@ -187,57 +187,6 @@ Systems : all } while (0) #endif -/***********************************************************************\ -* Name : TARGET_NATIVE_MISC_GET_TIMEZONE_STRING -* Purpose : get timezone string -* Input : string - buffer for timezone string -* maxStringLength - max. string length -* Output : string - timezone string -* result - TARGET_NATIVE_OK or TARGET_NATIVE_ERROR -* Return : - -* Side-effect: unknown -* Notes : set WITH_TIMEZONE_VARIABLE to timezone variable if not -* 'timezone' (e. g. Cygwin) -\***********************************************************************/ - -#ifndef TARGET_NATIVE_MISC_GET_TIMEZONE_STRING - #if TIME_WITH_SYS_TIME - #include <sys/time.h> - #include <time.h> - #else - #if HAVE_SYS_TIME_H - #include <sys/time.h> - #else - #include <time.h> - #endif - #endif - #include <string.h> - #ifndef WITH_TIMEZONE_VARIABLE - #define WITH_TIMEZONE_VARIABLE timezone - #endif - #define TARGET_NATIVE_MISC_GET_TIMEZONE_STRING(string,maxStringLength,result) \ - do { \ - tzset(); \ - \ - if (strcmp(tzname[0],tzname[1])!=0) \ - { \ - result=((strlen(tzname[0])+6)<=maxStringLength)?TARGET_NATIVE_OK:TARGET_NATIVE_ERROR; \ - if (result==TARGET_NATIVE_OK) \ - { \ - snprintf(string,maxStringLength,"%s%ld",tzname[0],((WITH_TIMEZONE_VARIABLE%3600)==0)?WITH_TIMEZONE_VARIABLE/3600:WITH_TIMEZONE_VARIABLE); \ - } \ - } \ - else \ - { \ - result=((strlen(tzname[0])+strlen(tzname[1])+6)<=maxStringLength)?TARGET_NATIVE_OK:TARGET_NATIVE_ERROR; \ - if (result==TARGET_NATIVE_OK) \ - { \ - snprintf(string,maxStringLength,"%s%ld%s",tzname[0],((WITH_TIMEZONE_VARIABLE%3600)==0)?WITH_TIMEZONE_VARIABLE/3600:WITH_TIMEZONE_VARIABLE,tzname[1]); \ - } \ - } \ - } while (0) -#endif - /***************************** Functions *******************************/ #ifdef __cplusplus diff --git a/vm/reference/java/util/VMTimeZone.java b/vm/reference/java/util/VMTimeZone.java new file mode 100644 index 000000000..24e79f37e --- /dev/null +++ b/vm/reference/java/util/VMTimeZone.java @@ -0,0 +1,346 @@ +/* java.util.VMTimeZone + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 + 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., 59 Temple Place, Suite 330, Boston, MA +02111-1307 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 java.util; + +import gnu.classpath.Configuration; + +import java.io.*; + +/** + * + */ +final class VMTimeZone +{ + static + { + if (Configuration.INIT_LOAD_LIBRARY) + { + System.loadLibrary("javautil"); + } + } + + /** + * This method returns a time zone id string which is in the form + * (standard zone name) or (standard zone name)(GMT offset) or + * (standard zone name)(GMT offset)(daylight time zone name). The + * GMT offset can be in seconds, or where it is evenly divisible by + * 3600, then it can be in hours. The offset must be the time to + * add to the local time to get GMT. If a offset is given and the + * time zone observes daylight saving then the (daylight time zone + * name) must also be given (otherwise it is assumed the time zone + * does not observe any daylight savings). + * <p> + * The result of this method is given to the method + * TimeZone.getDefaultTimeZone(String) which tries to map the time + * zone id to a known TimeZone. See that method on how the returned + * String is mapped to a real TimeZone object. + * <p> + * The reference implementation which is made for GNU/Posix like + * systems calls <code>System.getenv("TZ")</code>, + * <code>readTimeZoneFile("/etc/timezone")</code>, + * <code>readtzFile("/etc/localtime")</code> and finally + * <code>getSystemTimeZoneId()</code> till a supported TimeZone is + * found through <code>TimeZone.getDefaultTimeZone(String)</code>. + * If every method fails <code>null</code> is returned (which means + * the TimeZone code will fall back on GMT as default time zone). + * <p> + * Note that this method is called inside a + * <code>AccessController.doPrivileged()</code> block and runs with + * the priviliges of the java.util system classes. It will only be + * called when the default time zone is not yet set, the system + * property user.timezone isn't set and it is requested for the + * first time. + */ + static TimeZone getDefaultTimeZoneId() + { + TimeZone zone = null; + + // See if TZ environment variable is set and accessible. + String tzid = System.getenv("TZ"); + if (tzid != null && !tzid.equals("")) + zone = TimeZone.getDefaultTimeZone(tzid); + + // Try to parse /etc/timezone. + if (zone == null) + { + tzid = readTimeZoneFile("/etc/timezone"); + if (tzid != null && !tzid.equals("")) + zone = TimeZone.getDefaultTimeZone(tzid); + } + + // Try to parse /etc/localtime + if (zone == null) + { + tzid = readtzFile("/etc/localtime"); + if (tzid != null && !tzid.equals("")) + zone = TimeZone.getDefaultTimeZone(tzid); + } + + // Try some system specific way + if (zone == null) + { + tzid = getSystemTimeZoneId(); + System.err.println("tzid: " + tzid); + if (tzid != null && !tzid.equals("")) + zone = TimeZone.getDefaultTimeZone(tzid); + } + + return zone; + } + + /** + * Tries to read the time zone name from a file. Only the first + * consecutive letters, digits, slashes, dashes and underscores are + * read from the file. If the file cannot be read or an IOException + * occurs null is returned. + * <p> + * The /etc/timezone file is not standard, but a lot of systems have + * it. If it exist the first line always contains a string + * describing the timezone of the host of domain. Some systems + * contain a /etc/TIMEZONE file which is used to set the TZ + * environment variable (which is checked before /etc/timezone is + * read). + */ + private static String readTimeZoneFile(String file) + { + File f = new File(file); + if (!f.exists()) + return null; + + InputStreamReader isr = null; + try + { + FileInputStream fis = new FileInputStream(f); + BufferedInputStream bis = new BufferedInputStream(fis); + isr = new InputStreamReader(bis); + + StringBuffer sb = new StringBuffer(); + int i = isr.read(); + while (i != -1) + { + char c = (char) i; + if (Character.isLetter(c) || Character.isDigit(c) + || c == '/' || c == '-' || c == '_') + { + sb.append(c); + i = isr.read(); + } + else + break; + } + return sb.toString(); + } + catch (IOException ioe) + { + // Parse error, not a proper tzfile. + return null; + } + finally + { + try + { + if (isr != null) + isr.close(); + } + catch (IOException ioe) + { + // Error while close, nothing we can do. + } + } + } + + /** + * Tries to read a file as a "standard" tzfile and return a time + * zone id string as expected by <code>getDefaultTimeZone(String)</code>. + * If the file doesn't exist, an IOException occurs or it isn't a tzfile + * that can be parsed null is returned. + * <p> + * The tzfile structure (as also used by glibc) is described in the Olson + * tz database archive as can be found at + * <code>ftp://elsie.nci.nih.gov/pub/</code>. + * <p> + * 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). + */ + private static String readtzFile(String file) + { + File f = new File(file); + if (!f.exists()) + return null; + + DataInputStream dis = null; + try + { + FileInputStream fis = new FileInputStream(f); + BufferedInputStream bis = new BufferedInputStream(fis); + dis = new DataInputStream(bis); + + // Make sure we are reading a tzfile. + byte[] tzif = new byte[4]; + dis.readFully(tzif); + if (tzif[0] == 'T' && tzif[1] == 'Z' + && tzif[2] == 'i' && tzif[3] == 'f') + // Reserved bytes, ttisgmtcnt, ttisstdcnt and leapcnt + skipFully(dis, 16 + 3 * 4); + else + // Darwin has tzdata files that don't start with the TZif marker + skipFully(dis, 16 + 3 * 4 - 4); + + int timecnt = dis.readInt(); + int typecnt = dis.readInt(); + if (typecnt > 0) + { + int charcnt = dis.readInt(); + // Transition times plus indexed transition times. + skipFully(dis, timecnt * (4 + 1)); + + // Get last gmt_offset and dst/non-dst time zone names. + int abbrind = -1; + int dst_abbrind = -1; + int gmt_offset = 0; + while (typecnt-- > 0) + { + // gmtoff + int offset = dis.readInt(); + int dst = dis.readByte(); + if (dst == 0) + { + abbrind = dis.readByte(); + gmt_offset = offset; + } + else + dst_abbrind = dis.readByte(); + } + + // gmt_offset is the offset you must add to UTC/GMT to + // get the local time, we need the offset to add to + // the local time to get UTC/GMT. + gmt_offset *= -1; + + // Turn into hours if possible. + if (gmt_offset % 3600 == 0) + gmt_offset /= 3600; + + if (abbrind >= 0) + { + byte[] names = new byte[charcnt]; + dis.readFully(names); + int j = abbrind; + while (j < charcnt && names[j] != 0) + j++; + + String zonename = new String(names, abbrind, j - 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 = ""; + + // Only use gmt offset when necessary. + // Also special case GMT+/- timezones. + String offset_string; + if ("".equals(dst_zonename) + && (gmt_offset == 0 + || zonename.startsWith("GMT+") + || zonename.startsWith("GMT-"))) + offset_string = ""; + else + offset_string = Integer.toString(gmt_offset); + + String id = zonename + offset_string + dst_zonename; + + return id; + } + } + + // Something didn't match while reading the file. + return null; + } + 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; + } + } + + /** + * Tries to get the system time zone id through native code. + */ + private static native String getSystemTimeZoneId(); +} |