Ubuntu – Debugging Java Program for Changing Timezone Configuration

javatimezoneUbuntu

Here is a ruby script I wrote to change the timezone configuration on Ubuntu. I run it with jruby (a Ruby interpreter running in a JVM).

require 'java'

if ARGV.length == 0
  puts "Usage: jruby change_timezone.rb America/Toronto"
  exit
end

old_zone = File.read("../../../etc/timezone")
puts old_zone
time1 = Time.now
puts "Current Time:"+time1.localtime.to_s

new_zone = ARGV[0]
open('../../../etc/timezone','w') do |f|
  f.puts new_zone.to_s
  f.close
end

new_zone = File.read("../../../etc/timezone")
puts new_zone
time2 = Time.now
puts "Updated Time:"+time2.localtime.to_s

It does change the configuration file properly. However, the output for the script is not as expected.

Assume the default value for the timezone is America/Toronto.
When I run the command jruby change_timezone.rb Asia/Chongqing, the output is:

America/Toronto
Current Time:Thu Jul 07 14:43:23 -0400 2011
Asia/Chongqing
Updated Time:Thu Jul 07 14:43:23 -0400 2011 (My Note: +0800 expected!!!)

Continuing with the command jruby change_timezone.rb Europe/Amsterdam, I end up with the following:

Asia/Chongqing
Current Time:Fri Jul 08 03:18:25 +0800 2011 (My Note: it actually got updated from last run!!!)
Europe/Amsterdam
Updated Time:Fri Jul 08 03:18:25 +0800 2011 (My Note: +0200 expected!!!)

Going further with jruby change_timezone.rb Europe/Amsterdam again, I get the following:

Europe/Amsterdam
Current Time:Thu Jul 07 21:21:27 +0200 2011
Europe/Amsterdam
Updated Time:Thu Jul 07 21:21:27 +0200 2011

Can someone figure out why it didn't work as expected?

Best Answer

This is an issue in how Java determines the time zone under unix systems.

The POSIX specification does not specify how to determine the timezone when the TZ environment variable is not set. I can't find anything in the Linux Standard Base about this. The base system library (GNU libc) uses /etc/localtime to determine the timezone. So on non-embedded Linux, /etc/localtime is where the timezone information is stored, and ideally the story would end here.

(Looking around: FreeBSD, NetBSD and OpenBSD use /etc/localtime. Solaris and a few others use /etc/TIMEZONE. The Rosetta Stone for Unix shows what other unices use. Dietlibc (used in some embedded Linux systems) uses /etc/localtime, while uClibc uses /etc/TZ (unless patched).)

Unfortunately, Java does things differently. Debian and Ubuntu have a file called /etc/timezone which contains the name of the timezone. This extra file is intended for the packaging system, so that it remembers a geographical name like Europe/Amsterdam rather than just the description of the timezone (offsets over time, and display names CET, CEST and CEDT). This is both friendlier to humans and robust in case the geographical locale updates its time zone rules. Sun (now Oracle) Java prefers /etc/timezone (or /etc/sysconfig/clock on Red Hat-based distributions) see bug #6456628 to /etc/localtime, and OpenJDK and gcj follow suit.

See also: How do I find the current system timezone?; Java Time Zone is messed up.

The solution is simple: always update /etc/timezone and /etc/localtime together. On Debian or Ubuntu, the official method to change the timezone is dpkg-reconfigure tzdata. To change the timezone only for one application, set the TZ environment variable (this is portable across all unix systems).

Related Question