JDK-8223490 : Optimize search algorithm for determining default time zone
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util
  • Affected Version: 8
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • Submitted: 2019-05-07
  • Updated: 2022-06-27
  • Resolved: 2019-09-17
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 11 JDK 13 JDK 14 JDK 7 JDK 8 Other
11.0.6-oracleFixed 13.0.4Fixed 14 b15Fixed 7u251Fixed 8u231Fixed openjdk8u242Fixed
Related Reports
CSR :  
Relates :  
Relates :  
Description
This seems to be a regression from JDK-6456628 

Effectively, if /etc/localtime is a binary (not a symlink to somewhere in /usr/share/zoneinfo), java.util.TimeZone.getDefault may return any of the 
timezones that match the binary. 

This can be done easily by using the oraclelinux:6 docker image as by default this has /etc/localtime as a binary that matches UTC. 

Code to reproduce: 

import java.util.TimeZone; 
public class TZBug { 
    public static void main(String[] args) { 
        System.out.println(TimeZone.getDefault().getID()); 
    } 
} 


Comments
Fix request (13u): I'd like to port it to 13u as well. Patch applies cleanly here.
22-05-2020

Fix request (8u) to ensure parity RFR thread: https://mail.openjdk.java.net/pipermail/jdk8u-dev/2019-December/010719.html This patch has two follow ups, that should be included too: https://bugs.openjdk.java.net/browse/JDK-8231124 https://bugs.openjdk.java.net/browse/JDK-8234591
10-12-2019

Fix request (11u) - on behalf of ~goetz: Backport is requested for parity with Oracle 11.0.6. Patch needed manual resolve and was reviewed here: https://mail.openjdk.java.net/pipermail/jdk-updates-dev/2019-November/002117.html
20-11-2019

For a standard UTC timezone testcase (where the "/usr/share/zoneinfo/UTC" binary file is identical to /etc/localtime), I'm seeing far less stat calls for /usr/share/zoneinfo/* on my local linux box. (test case was "TimeZone.getDefault()") pre fix : 1,020 stat calls to /usr/share/zoneinfo/* post fix: 1
19-09-2019

URL: https://hg.openjdk.java.net/jdk/jdk/rev/8ee083465318 User: coffeys Date: 2019-09-17 11:07:50 +0000
17-09-2019

A better solution here may be to optimize the search path so that the most common time zones are queried first. In a modern linux system there are over 1800 timezone data files under /usr/share/zoneinfo. Worse case scenario means that a TimeZone.getDefault() call could end up stat'ing a large proportion of those files if the JDK has to match the "/etc/localtime" binary file with one from zoneinfo directory. Another issue with the current implementation is that a popular Timezone setting such as "UTC" may end up returning any of the following values : "UTC", "UCT", "Universal", "Zulu" -- all files are identical. I'm proposing that we make special case for common timezones. For now, I'm proposing "UTC" and "GMT" be first checked for matching pattern before full directory recursion search begins. I'll share a patch on the mailing list shortly.
10-09-2019

Had a zoom session with one of the bug submitters today to discuss this issue further. On his system in question, step 4 from above (else if /etc/localtime is a binary, find the first identical time zone binary file in /usr/share/zoneinfo/ ") is being used by JDK to determine the default TZ. This places a reliance on the order of the /usr/share/zoneinfo/ directory files returned by the readdir_r linux function. On the test system, `date` was returning a UTC timezone value even though the JDK was returning a UCT timezone value. This is because UCT was returned by file listings before UTC *and* both files have the same file size/checksum. (identical in everything but file name) there are various configuration changes that can be made to aid determination of the correct timezone but these don't seem to suit the submitter. I've developed a prototype JDK that parses the timezone value directly from the /etc/localtime binary file. Unfortunately, that file can store an abbreviated format for timezone name. Some examples here : **tz debug: returning value from localtime_r: IST -- would be new behaviour Europe/Dublin --> current behaviour == **tz debug: returning value from localtime_r: CEST -- would be new behaviour Europe/Vatican --> current behaviour As you can see - the name string contained in the binary file is not too informative. It's hard to justify such a JDK change as a result.
05-09-2019

The JDK uses the following logic to obtain the default timezone on linux: 1. If TZ environment variable is defined, use it 2. else if /etc/timezone exists, use the value contained within it 3. else if /etc/localtime exists and is a symbolic link, use the name pointed to 4. else if /etc/localtime is a binary, find the first identical time zone binary file in /usr/share/zoneinfo/
05-09-2019

As suspected the two filesystems in this docker environment are returning a different iteration ordering of the /usr/share/zoneinfo/ files. This would explain the behavioural difference seen. readdir64_r is used as system call. tail -10 devicemapper.txt **tz debug: findZoneinfoFile.1. entry: GMT-5 **tz debug: findZoneinfoFile.1. entry: GMT-6 **tz debug: findZoneinfoFile.1. entry: GMT-7 **tz debug: findZoneinfoFile.1. entry: GMT-8 **tz debug: findZoneinfoFile.1. entry: GMT-9 **tz debug: findZoneinfoFile.1. entry: GMT0 **tz debug: findZoneinfoFile.1. entry: Greenwich **tz debug: findZoneinfoFile.1. entry: UCT ** tz debug: getZoneName(char*) returning Etc/UCT **tz debug: getPlatformTimeZoneID() returned Etc/UCT $ tail -10 overlay.txt **tz debug: findZoneinfoFile.1. entry: Comoro **tz debug: findZoneinfoFile.1. entry: Christmas **tz debug: findZoneinfoFile.1. entry: Reunion **tz debug: findZoneinfoFile.1. entry: ROK **tz debug: findZoneinfoFile.1. entry: Poland **tz debug: findZoneinfoFile.1. entry: Cuba **tz debug: findZoneinfoFile.1. entry: tzdata.zi **tz debug: findZoneinfoFile.1. entry: UCT ** tz debug: getZoneName(char*) returning UCT **tz debug: getPlatformTimeZoneID() returned UCT The devicemapper output lists entries in an alphabetical form while the overlay output seems to be random. I'll have to check if there's any way to resolve that. (sorting the directory entries into a sorted list before traversing would most likely have unacceptable performance impact) The issue was reported on docker images. If the dockerfile could be modified, then simply setting the TZ environment variable to the preferred TZ would fix this issue.
28-08-2019