Java doesn’t appear to apply DST offset when the OS uses a POSIX time zone description rather than a time zone name. Is the use of a TZ description unsupported by the JRE or is this behavior a bug?
More details…
I’m working on a Linux (Debian) based system where the TZ environment variable is set to a POSIX formatted TZ like STD+7DST+6,M3.2.0/02:00:00,M11.1.0/02:00:00
instead of a TZ name such as America/Denver
. (See TZ Variable)
While this seems to work correctly for date
and related system tools, when I try to find the time in a java application, it doesn’t appear it has been correctly adjust for DST. This results in the time being wrong for part of the year when DST is in effect.
I’ve tested this on a several different systems and seen the same results on each (test w/ results are below)
This behavior actually surfaced in an application that uses Quartz Scheduler but I’ve since been able to reproduce the issue with the following SSCCE:
import java.util.Date; import java.util.TimeZone; public class CheckTime { public static void main(String[] args) { TimeZone tz = TimeZone.getDefault(); Date now = new Date(); System.out.println("Current time " + now.toString()); System.out.println("Current time zone " + tz.getDisplayName()); System.out.println("Current time zone in DST? " + tz.inDaylightTime(now)); } }
Ubuntu 18.04.2 LTS (bionic)
$ java -version openjdk version "1.8.0_222" OpenJDK Runtime Environment (build 1.8.0_222-8u222-b10-1ubuntu1~18.04.1-b10) OpenJDK 64-Bit Server VM (build 25.222-b10, mixed mode) $ export TZ="STD+7DST+6,M3.2.0/02:00:00,M11.1.0/02:00:00" $ date Fri Aug 9 07:02:27 DST 2019 $ java CheckTime Current time Fri Aug 09 06:02:30 GMT-07:00 2019 Current timezone GMT-07:00 Current timezone in DST? false $ export TZ="America/Denver" $ date Fri Aug 9 07:03:29 MDT 2019 $ java CheckTime Current time Fri Aug 09 07:03:32 MDT 2019 Current timezone Mountain Standard Time Current timezone in DST? true
Custom ARM32hf build, based on debian (kernel 4.1.0-altera)
$ java -version openjdk version "1.8.0_162" OpenJDK Runtime Environment (Zulu Embedded 8.27.0.91-linux-aarch32hf) (build 1.8.0_162-b91) $ export TZ="STD+7DST+6,M3.2.0/02:00:00,M11.1.0/02:00:00" $ date Fri Aug 9 07:06:59 DST 2019 $ java CheckTime Current time Fri Aug 09 06:07:03 GMT-07:00 2019 Current time zone GMT-07:00 Current time zone in DST? false $ export TZ="America/Denver" $ date Fri Aug 9 07:09:45 MDT 2019 $ java CheckTime Current time Fri Aug 09 07:09:49 MDT 2019 Current time zone Mountain Standard Time Current time zone in DST? true
Raspbian 9.9 (stretch)
$ java -version java version "1.8.0_65" Java(TM) SE Runtime Environment (build 1.8.0_65-b17) Java HotSpot(TM) Client VM (build 25.65-b01, mixed mode) $ export TZ="STD+7DST+6,M3.2.0/02:00:00,M11.1.0/02:00:00" $ date Fri Aug 9 07:12:52 DST 2019 $ java CheckTime Current time Fri Aug 09 06:12:57 GMT-07:00 2019 Current time zone GMT-07:00 Current time zone in DST? false $ export TZ="America/Denver" $ date Fri Aug 9 07:13:42 MDT 2019 $ java CheckTime Current time Fri Aug 09 07:13:44 MDT 2019 Current time zone Mountain Standard Time Current time zone in DST? true
EDIT
Curiously, I see slightly different results on a mac. Java still doesn’t report that the system is in DST, but it at least reports the correct local time.
MacOS 10.14.6 (Mojave)
$ java -version openjdk version "1.8.0_212" OpenJDK Runtime Environment Corretto-8.212.04.2 (build 1.8.0_212-b04) OpenJDK 64-Bit Server VM Corretto-8.212.04.2 (build 25.212-b04, mixed mode) $ export TZ="STD+7DST+6,M3.2.0/02:00:00,M11.1.0/02:00:00" $ date Fri Aug 9 09:47:14 DST 2019 $ java CheckTime Current time Fri Aug 09 09:47:18 GMT-06:00 2019 Current timezone GMT-06:00 Current timezone in DST? false $ export TZ="America/Denver" $ date Fri Aug 9 09:49:04 MDT 2019 $ java CheckTime Current time Fri Aug 09 09:49:08 MDT 2019 Current timezone Mountain Standard Time Current timezone in DST? true $java -version java version "1.8.0_161" Java(TM) SE Runtime Environment (build 1.8.0_161-b12) Java HotSpot(TM) 64-Bit Server VM (build 25.161-b12, mixed mode) $ export TZ="STD+7DST+6,M3.2.0/02:00:00,M11.1.0/02:00:00" $ date Fri Aug 9 10:19:23 DST 2019 $java CheckTime Current time Fri Aug 09 10:19:40 GMT-06:00 2019 Current timezone GMT-06:00 Current timezone in DST? false $ export TZ="America/Denver" $ date Fri Aug 9 10:21:03 MDT 2019 $ java CheckTime Current time Fri Aug 09 10:21:07 MDT 2019 Current timezone Mountain Standard Time Current timezone in DST? true
Advertisement
Answer
It is not a bug. It is a feature.
According to the javadoc for ZoneId
, that POSIX syntax for specifying zones is not supported:
Time-zone IDs
The ID is unique within the system. There are three types of ID.
The simplest type of ID is that from ZoneOffset. This consists of ‘Z’ and IDs starting with ‘+’ or ‘-‘.
The next type of ID are offset-style IDs with some form of prefix, such as ‘GMT+2’ or ‘UTC+01:00’. The recognised prefixes are ‘UTC’, ‘GMT’ and ‘UT’. The offset is the suffix and will be normalized during creation. These IDs can be normalized to a ZoneOffset using normalized().
The third type of ID are region-based IDs. A region-based ID must be of two or more characters, and not start with ‘UTC’, ‘GMT’, ‘UT’ ‘+’ or ‘-‘. Region-based IDs are defined by configuration, see ZoneRulesProvider. The configuration focuses on providing the lookup from the ID to the underlying ZoneRules.
Time-zone rules are defined by governments and change frequently. There are a number of organizations, known here as groups, that monitor time-zone changes and collate them. The default group is the IANA Time Zone Database (TZDB). Other organizations include IATA (the airline industry body) and Microsoft.
Each group defines its own format for the region ID it provides. The TZDB group defines IDs such as ‘Europe/London’ or ‘America/New_York’. TZDB IDs take precedence over other groups.
See also:
If you wrote some code to parse that syntax, you should be able to use the data to construct a SimpleTimeZone
(javadoc). Unfortunately, this forces you to continue using the old (“mostly deprecated”) Date
class and friends.
The new (in Java 8) java.time.*
classes don’t appear to have an easy way to construct your own ZoneId
from a set of rules. (Maybe it could be done by implementing your own ZoneRuleProvider
(javadoc), but it looks complicated.)
So (IMO) you would be better off getting your OS to use the standard TZDB zone ids.
You commented:
On this particular system, neither “/etc/localtime” nor “/etc/timezone” exist.
If you are running Ubuntu Bionic, “/etc/localtime” should exist. It should be a symlink to a binary timezone file in the “/usr/share/zoneinfo” tree. See https://linuxize.com/post/how-to-set-or-change-timezone-on-ubuntu-18-04/. Or perhaps the problem is that the system has been deliberately configured to not know its local timezone.