Can't resolve TZInfo::AmbiguousTime for 'Europe/Moscow' timezone (tzinfo-0.3.41) #32

Closed
sitnikovme opened this Issue Oct 23, 2014 · 4 comments

Comments

Projects
None yet
3 participants
@sitnikovme

t=TZInfo::Timezone.get('Europe/Moscow').local_to_utc(DateTime.parse('26.10.2014 1:30:00'), true)
TZInfo::AmbiguousTime: DateTime: 2014-10-26T01:30:00+00:00 is an ambiguous local time.
from tzinfo-0.3.41/lib/tzinfo/timezone.rb:391:in `period_for_local'

t=TZInfo::Timezone.get('Europe/Moscow').local_to_utc(DateTime.parse('26.10.2014 1:30:00'), false)
TZInfo::AmbiguousTime: DateTime: 2014-10-26T01:30:00+00:00 is an ambiguous local time.
from tzinfo-0.3.41/lib/tzinfo/timezone.rb:391:in `period_for_local'

@philr

This comment has been minimized.

Show comment
Hide comment
@philr

philr Oct 23, 2014

Member
$ zdump -v -c 2013,2015 Europe/Moscow
Europe/Moscow  Sat Oct 25 21:59:59 2014 UT = Sun Oct 26 01:59:59 2014 MSK isdst=0 gmtoff=14400
Europe/Moscow  Sat Oct 25 22:00:00 2014 UT = Sun Oct 26 01:00:00 2014 MSK isdst=0 gmtoff=10800

At 02:00 local time on 26 October 2014 (22:00 on 25 October UTC), Europe/Moscow will put the clocks back by an hour. There are two instances of 01:30 local time.

Since 2011, Europe/Moscow no longer changes the clocks twice a year for daylight savings time (summer time). The IANA time zone database therefore does not consider either the period before or the period after the change in 2014 to be daylight savings time (DST).

The second parameter to local_to_utc specifies whether to treat the time parameter as DST or non-DST when there is more than one possibility. However, in this case both instances of 01:30 are non-DST, so the parameter cannot resolve them (specifying false doesn't eliminate any options and specifying true eliminates both options).

To handle this case, you need to pass the optional block to local_to_utc. The block is passed an array of all the possible TimezonePeriod instances that could apply and selects the one that should be used for conversion. For example:

tz = TZInfo::Timezone.get('Europe/Moscow')
d = DateTime.parse('26.10.2014 1:30:00')
tz.local_to_utc(d) { |periods| periods.first } # Returns 21:30
tz.local_to_utc(d) { |periods| periods.last }  # Returns 22:30
Member

philr commented Oct 23, 2014

$ zdump -v -c 2013,2015 Europe/Moscow
Europe/Moscow  Sat Oct 25 21:59:59 2014 UT = Sun Oct 26 01:59:59 2014 MSK isdst=0 gmtoff=14400
Europe/Moscow  Sat Oct 25 22:00:00 2014 UT = Sun Oct 26 01:00:00 2014 MSK isdst=0 gmtoff=10800

At 02:00 local time on 26 October 2014 (22:00 on 25 October UTC), Europe/Moscow will put the clocks back by an hour. There are two instances of 01:30 local time.

Since 2011, Europe/Moscow no longer changes the clocks twice a year for daylight savings time (summer time). The IANA time zone database therefore does not consider either the period before or the period after the change in 2014 to be daylight savings time (DST).

The second parameter to local_to_utc specifies whether to treat the time parameter as DST or non-DST when there is more than one possibility. However, in this case both instances of 01:30 are non-DST, so the parameter cannot resolve them (specifying false doesn't eliminate any options and specifying true eliminates both options).

To handle this case, you need to pass the optional block to local_to_utc. The block is passed an array of all the possible TimezonePeriod instances that could apply and selects the one that should be used for conversion. For example:

tz = TZInfo::Timezone.get('Europe/Moscow')
d = DateTime.parse('26.10.2014 1:30:00')
tz.local_to_utc(d) { |periods| periods.first } # Returns 21:30
tz.local_to_utc(d) { |periods| periods.last }  # Returns 22:30

@philr philr added the invalid label Oct 23, 2014

@philr philr closed this Oct 23, 2014

@sitnikovme

This comment has been minimized.

Show comment
Hide comment
@sitnikovme

sitnikovme Oct 27, 2014

Thank you. I think this is one more way to reproduce this problem. Could you please check this and say if this is ok? Our code base use a lot queries where expression "X.days.ago" is used. And we get unexpected errors.

tzinfo-0.3.41

1.9.3-p484 :008 > Rails.version
 => "4.0.0"
1.9.3-p484 :009 > Time.current
 => Mon, 27 Oct 2014 01:36:25 MSK +03:00
1.9.3-p484 :010 > 1.day.ago
TZInfo::AmbiguousTime: 2014-10-26 01:36:40 UTC is an ambiguous local time.
  from /gems/tzinfo-0.3.41/lib/tzinfo/timezone.rb:391:in `period_for_local'
  from /gems/activesupport-4.0.0/lib/active_support/values/time_zone.rb:339:in `period_for_local'
  from /gems/activesupport-4.0.0/lib/active_support/time_with_zone.rb:379:in `get_period_and_ensure_valid_local_time'
  from /gems/activesupport-4.0.0/lib/active_support/time_with_zone.rb:48:in `initialize'
  from /gems/activesupport-4.0.0/lib/active_support/time_with_zone.rb:397:in `new'
  from /gems/activesupport-4.0.0/lib/active_support/time_with_zone.rb:397:in `wrap_with_time_zone'
  from /gems/activesupport-4.0.0/lib/active_support/time_with_zone.rb:368:in `method_missing'
  from /gems/activesupport-4.0.0/lib/active_support/time_with_zone.rb:289:in `advance'
  from /gems/activesupport-4.0.0/lib/active_support/duration.rb:94:in `block in sum'
  from /gems/activesupport-4.0.0/lib/active_support/duration.rb:89:in `each'
  from /gems/activesupport-4.0.0/lib/active_support/duration.rb:89:in `inject'
  from /gems/activesupport-4.0.0/lib/active_support/duration.rb:89:in `sum'
  from /gems/activesupport-4.0.0/lib/active_support/duration.rb:68:in `ago'
  from (irb):10
  from /gems/railties-4.0.0/lib/rails/commands/console.rb:90:in `start'
  from /gems/railties-4.0.0/lib/rails/commands/console.rb:9:in `start'
  from /gems/railties-4.0.0/lib/rails/commands.rb:64:in `<top (required)>'
  from script/rails:6:in `require'
  from script/rails:6:in `<main>'1.9.3-p484 :011 >

Thank you. I think this is one more way to reproduce this problem. Could you please check this and say if this is ok? Our code base use a lot queries where expression "X.days.ago" is used. And we get unexpected errors.

tzinfo-0.3.41

1.9.3-p484 :008 > Rails.version
 => "4.0.0"
1.9.3-p484 :009 > Time.current
 => Mon, 27 Oct 2014 01:36:25 MSK +03:00
1.9.3-p484 :010 > 1.day.ago
TZInfo::AmbiguousTime: 2014-10-26 01:36:40 UTC is an ambiguous local time.
  from /gems/tzinfo-0.3.41/lib/tzinfo/timezone.rb:391:in `period_for_local'
  from /gems/activesupport-4.0.0/lib/active_support/values/time_zone.rb:339:in `period_for_local'
  from /gems/activesupport-4.0.0/lib/active_support/time_with_zone.rb:379:in `get_period_and_ensure_valid_local_time'
  from /gems/activesupport-4.0.0/lib/active_support/time_with_zone.rb:48:in `initialize'
  from /gems/activesupport-4.0.0/lib/active_support/time_with_zone.rb:397:in `new'
  from /gems/activesupport-4.0.0/lib/active_support/time_with_zone.rb:397:in `wrap_with_time_zone'
  from /gems/activesupport-4.0.0/lib/active_support/time_with_zone.rb:368:in `method_missing'
  from /gems/activesupport-4.0.0/lib/active_support/time_with_zone.rb:289:in `advance'
  from /gems/activesupport-4.0.0/lib/active_support/duration.rb:94:in `block in sum'
  from /gems/activesupport-4.0.0/lib/active_support/duration.rb:89:in `each'
  from /gems/activesupport-4.0.0/lib/active_support/duration.rb:89:in `inject'
  from /gems/activesupport-4.0.0/lib/active_support/duration.rb:89:in `sum'
  from /gems/activesupport-4.0.0/lib/active_support/duration.rb:68:in `ago'
  from (irb):10
  from /gems/railties-4.0.0/lib/rails/commands/console.rb:90:in `start'
  from /gems/railties-4.0.0/lib/rails/commands/console.rb:9:in `start'
  from /gems/railties-4.0.0/lib/rails/commands.rb:64:in `<top (required)>'
  from script/rails:6:in `require'
  from script/rails:6:in `<main>'1.9.3-p484 :011 >
@philr

This comment has been minimized.

Show comment
Hide comment
@philr

philr Oct 27, 2014

Member

This is the same issue.

The Rails ActiveSupport::TimeWithZone#get_period_and_ensure_valid_local_time method is calling TZInfo::Timezone#period_for_local (via ActiveSupport::TimeZone#period_for_local), but not passing a block to resolve ambiguities.

The error is then raised when period_for_local is passed the ambiguous local time for Moscow of 01:36:40 on 2014-10-26.

It looks like this issue may have been resolved in Rails 4.1 (I was able to reproduce it on Rails 4.0.10, but not on Rails 4.1.6).

Member

philr commented Oct 27, 2014

This is the same issue.

The Rails ActiveSupport::TimeWithZone#get_period_and_ensure_valid_local_time method is calling TZInfo::Timezone#period_for_local (via ActiveSupport::TimeZone#period_for_local), but not passing a block to resolve ambiguities.

The error is then raised when period_for_local is passed the ambiguous local time for Moscow of 01:36:40 on 2014-10-26.

It looks like this issue may have been resolved in Rails 4.1 (I was able to reproduce it on Rails 4.0.10, but not on Rails 4.1.6).

@conf

This comment has been minimized.

Show comment
Hide comment
@conf

conf Oct 31, 2014

For those, who like me, can live with first TimeZonePeriod (instead of raising exception), you can use my monkey patch here: https://gist.github.com/conf/f3d18db8370961c8540f.

conf commented Oct 31, 2014

For those, who like me, can live with first TimeZonePeriod (instead of raising exception), you can use my monkey patch here: https://gist.github.com/conf/f3d18db8370961c8540f.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment