Have you ever noticed that in some cases, Java will not take the time zone which is configured in a server? This has serious impacts if the server handles scheduling tasks and runs them periodically.
Before diving deep down, let’s understand what is a time zone?
Time Zones are geographical world globe division of 15 degree each, starting at Greenwich, England. It’s a uniform standard time for legal, commercial and social purposes.
Issue: Timezone difference between the system and java
While executing the below code, It will print the system time along with the timezone. I’ve added the output of the date command as well.
Even though the system timezone is configured as PST, java prints the timezone as GMT.
Quick fix:
Set user.timezonein the command line as
-Duser.timezone=America/Los_Angeles
Set the environmental variable
TZ
to provide the timezone.
The above solutions will work, but to understand and fix it once for all, read further for the actual fix. (All required source code links are embedded in the method names)
Detailed explanation on why do we get this timezone difference?
In the example java code, we call System.out.println
of new Date()
. We know that java calls an object’s .toString()
method internally to provide the string representation of an object. Let’s take a look at the toString
method of the Date class in java.
The timezone on Line:16
is set by calling the date.getZone()
. The variable date
is initialized by calling the normalize() method.
The timezone in normalize
is set by calling theTimeZone.getDefaultRef() in TimeZone.java
which then calls setDefaultZone()to get the system timezone.
Now, let's break up and understand what the setDefaultZone()
does.
It checks for the zone by checking the
user.timezone
property (which we didn’t set via the command line).If the
user.timezone
property isnull
, it gets thejava.home
property and calls thegetSystemTimeZoneID()
which is a native method.Any
NULL
values will set the timezone to default GMT. (So, this is why the timezone is set to GMT!)
Ignoring the unwanted details, let’s focus on what the findJavaTZ_md() does.
Now, we can understand why the quick search solutions mentioned to set values for user.timezone
and TZ
! In our case, we didn’t set the TZ environmental variable which means TZ = NULL
.
It calls getPlatformTimeZoneID() if TZ is NULL
. (We’re about to reach a conclusion in the next step! Hang on..)
As the comment says, it will check for /etc/timezone
file which contains the system timezone information. I checked in my Ubuntu machine for the file and it's straightforward.
If the file is not present, it checks /etc/localtime
to obtain the timezone.
Here’s what happens in the above code.
Get the file status of
/etc/localtime
using lstatCheck whether it's a symlink and read the symlink to obtain the timezone.
For lstat
to work as expected, it's required to have the executable(x
) permission on the file (as stated in the manpage of lstat). In my case, the /etc/localtime file doesn’t have the executable permission set. Hence, it returns NULL taking the default timezone of GMT.
Obtaining Timezone via symlink:
If the file has the required permission set, the output will be like the below one. After resolving the symlink, the zone info will be taken using the getZoneNamemethod.
If the /etc/localtimefile
is not a symlink, the getPlatformTimeZoneID
method will try to open and read the file like /etc/timezone. As the last fallback, it will recursively iterate the /usr/share/zoneinfo
directory to find the timezone. When none of them works out, the timezone is set to GMT- the default!
Actual Fix:
For my case, I just had to create a symlink of /etc/localtime
from /usr/share/zoneinfo/America/Los_Angeles
.
After setting the symlink, running the DateExampleprogram
yields the expected result.
Thank you for reading!
Top comments (1)
Thank you for the detailed explanation! In my case, I set the timezone system-wide using
sudo timedatectl set-timezone America/Lima
, which creates a symbolic link. I confirmed this withls -l /etc/localtime
showinglrwxrwxrwx 1 root root 34 Sep 14 08:06 /etc/localtime -> ../usr/share/zoneinfo/America/Lima
I ran a similar test with Node.js, and the output reflected the correct timezone:
However, when I ran the same test with the JVM, it still used the default America/Los_Angeles timezone. While your quick fix works perfectly, I’d like to apply a permanent solution to ensure the JVM consistently picks up the correct system timezone.
Thanks again for your thorough explanation and guidance!
Best wishes!