Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
max5it opened this issue Oct 23, 2014 · 4 comments
Closed
Labels

Comments

@max5it
Copy link

@max5it max5it commented Oct 23, 2014

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
Copy link
Member

@philr 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
@max5it
Copy link
Author

@max5it max5it commented 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 >
@philr
Copy link
Member

@philr 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
Copy link

@conf 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
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
3 participants
You can’t perform that action at this time.