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 likeEurope/Amsterdam
rather than just the description of the timezone (offsets over time, and display namesCET
,CEST
andCEDT
). 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 isdpkg-reconfigure tzdata
. To change the timezone only for one application, set theTZ
environment variable (this is portable across all unix systems).