Skip to content

Instantly share code, notes, and snippets.

@conf
Created October 31, 2014 12:32
Show Gist options
  • Save conf/f3d18db8370961c8540f to your computer and use it in GitHub Desktop.
Save conf/f3d18db8370961c8540f to your computer and use it in GitHub Desktop.
Monkey patch ActiveSupport::TimeZone to fix TZInfo::AmbiguousTime errors
# put this file in config/initializers/monkey_patches.rb
require 'monkey_patches/active_support/time_zone'
# put this in lib/monkey_patches/active_support/time_zone.rb
module MonkeyPatches
module ActiveSupport
module TimeZone
extend ::ActiveSupport::Concern
included do
alias_method_chain :period_for_local, :ambiguous_handling
end
# This monkey patch fixes exceptions like this one:
# TZInfo::AmbiguousTime: 2014-10-26 01:00:12 UTC is an ambiguous local time.
# They happen to appear when code like `3.days.ago` or `Time.zone.parse('some ambiguous date here')` is used,
# i.e. when some local time point can be translated into more than 1 points of time in UTC time, like it's
# happened in Europe/Moscow on October 26, 2014 when time from 01:00:00 till 01:59:59 went twice:
# 01:00:00 MSK +04:00 -> 01:59:59 MSK +04:00
# 01:59:59 MSK +04:00 -> 01:00:00 MSK +03:00
# 01:00:00 MSK +03:00 -> 02:00:00 MSK +04:00
# This change is permanent, so no daylight saving time (DST) is involved here
# See https://github.com/tzinfo/tzinfo/issues/32 for discussion
#
# Most of the time TZInfo resolves ambiguous dates by itself, but when it can't we have to choose it manually
# via supplied block, so take the earlier date by default
def period_for_local_with_ambiguous_handling(time, dst=true)
tzinfo.period_for_local(time, dst) { |results| results.first }
end
end
end
end
ActiveSupport::TimeZone.send :include, MonkeyPatches::ActiveSupport::TimeZone
# put this file in spec/lib/monkey_patches/active_support/time_zone_spec.rb
require 'spec_helper'
require 'timecop'
describe MonkeyPatches::ActiveSupport::TimeZone do
describe 'ambigious time bug' do
# I could reproduce this bug only on Rails 3.x
it 'n.days.ago should not throw exception' do
Timecop.travel(Time.zone.parse('27.10.2014 1:30:00 MSK +03:00')) do
expect { 1.day.ago }.not_to raise_error
end
end
it 'Time.zone.parse should not throw exception' do
expect { Time.zone.parse('26.10.2014 1:30:00') }.not_to raise_error
end
end
end
@almays
Copy link

almays commented Mar 15, 2017

Hi. In rails 5 there is this thing:

DEPRECATION WARNING: alias_method_chain is deprecated. Please, use Module#prepend instead. From module, you can access the original method using super. (called from block in <module:TimeZone> at lib/monkey_patches/active_support/time_zone.rb:8)

@Startouf
Copy link

Startouf commented Apr 1, 2018

I am using this patch only while running specs (since only my specs are affected) so I'll put it in spec/support and require it in my rails_helper.rb. I hope it will help me get rid of those flaky tests, thanks

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