Grzegorz D. wrote:
In “Rails Recipes” book there is a recipe how to deal with time zones.
However standard RoR TimeZone class doesn’t handle Daylight Saving Time.
It is rather impossible to use a such solution in a commercial app. So
the recipe suggests to install TZInfo gem with TimeZone class correctly
handling DST (you need also tzinfo_timezone plugin).
The TZInfo gem is pure ruby code and is quite slow. In fact the gem
functionality is already implemented in libc (they are based on the same
Olson time zone database) and Ruby Time class uses libc functions.
My tests show that TZInfo (version 0.3.1 on Ruby 1.8.5) is about 9 times
slower than libc converting from UTC to local and about 7 times slower
converting from local to UTC:
tz = ‘America/New_York’
t = Time.now.utc
n = 100000
Benchmark.bm do |bm|
bm.report(‘tzinfo u->l:’) {
n.times { TZInfo::Timezone.get(tz).utc_to_local(t) } }
bm.report(‘libc u->l:’) {
n.times { utc2user(tz, t) } }
bm.report(‘tzinfo l->u:’) {
n.times { TZInfo::Timezone.get(tz).local_to_utc(t) } }
bm.report(‘libc l->u:’) {
n.times { user2utc(tz, t) } }
end
user system total real
tzinfo u->l: 7.880000 0.000000 7.880000 ( 7.888051)
libc u->l: 0.870000 0.000000 0.870000 ( 0.868656)
tzinfo l->u: 10.110000 0.000000 10.110000 ( 10.116807)
libc l->u: 1.500000 0.000000 1.500000 ( 1.504355)
Above seems to work on Linux/Ubuntu and should be faster than TZInfo gem
based solution. So why people try to implement their own time zone
classes? Did I missed something?
The reason I started developing TZInfo was that I needed a solution that
would work on both Windows and Linux. Setting the TZ environment
variable doesn’t work on Windows (see
http://article.gmane.org/gmane.comp.lang.ruby.rails/75790). Even if
ENV[‘TZ’] could be made to work, Windows only stores timezone transition
information for the current year and doesn’t use compatible zone
identifiers.
For my purposes (use within Rails), the performance of TZInfo is good
enough. I probably do at most ~100 UTC to local conversions per page
(and usually far less). I have though been able to make some significant
performance gains in the year since the first release and I hope to
continue to make improvements in this area.
Another area TZInfo improves upon using the TZ environment variable is
in its handling of invalid and ambiguous local times (i.e. during the
transitions to and from daylight savings). Time.local always returns a
time regardless of whether it was invalid or ambiguous. TZInfo reports
invalid times and allows the ambiguity to be resolved by specifying
whether to use the DST or non-DST time or running a block to do the
selection.
In the example below 01:30 on 26-Mar-2006 shouldn’t exist as it occurs
during the transition from GMT to BST. 01:30 on 29-Oct-2006 refers to
both 02:30 and 01:30 UTC as it occurs during the transition from BST to
GMT:
irb(main):001:0> require_gem ‘tzinfo’
irb(main):002:0> tz = TZInfo::Timezone.get(‘Europe/London’)
irb(main):003:0> ENV[‘TZ’] = ‘Europe/London’
irb(main):004:0> tz.current_period.local_start.to_s
=> “2006-03-26T02:00:00Z”
irb(main):005:0> tz.local_to_utc(Time.utc(2006,3,26,1,30,0))
TZInfo::PeriodNotFound: TZInfo::PeriodNotFound
from ./lib/tzinfo/timezone.rb:338:in `period_for_local’
irb(main):006:0> Time.local(2006,10,29,1,30,0).utc
=> Sun Oct 29 01:30:00 UTC 2006
irb(main):007:0> tz.local_to_utc(Time.utc(2006,10,29,1,30,0))
TZInfo::AmbiguousTime: Time: Sun Oct 29 01:30:00 UTC 2006 is an
ambiguous local time.
from ./lib/tzinfo/timezone.rb:363:in `period_for_local’
irb(main):008:0> Time.local(2006,10,29,1,30,0).utc
=> Sun Oct 29 01:30:00 UTC 2006
–
Philip R.
http://tzinfo.rubyforge.org/ – DST-aware timezone library for Ruby