Skip to content

Instantly share code, notes, and snippets.

@benmmurphy
Last active October 4, 2017 22:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save benmmurphy/e3d6ef6f4545683132ae2eb23c3840cb to your computer and use it in GitHub Desktop.
Save benmmurphy/e3d6ef6f4545683132ae2eb23c3840cb to your computer and use it in GitHub Desktop.
time ambiguity
# frozen_string_literal: true
begin
require "bundler/inline"
rescue LoadError => e
$stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler"
raise e
end
gemfile(true) do
source "https://rubygems.org"
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
gem "rails", "5.1.4"
gem "activerecord", "5.1.4"
gem "sqlite3"
end
require "active_support/railtie"
require "action_controller/railtie"
require "active_record"
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Base.logger = Logger.new(STDOUT)
ActiveRecord::Schema.define do
create_table :events, force: true do |t|
t.timestamp :start_datetime
end
end
class Event < ActiveRecord::Base
end
class TestApp < Rails::Application
config.root = __dir__
secrets.secret_key_base = "secret_key_base"
config.logger = Logger.new($stdout)
Rails.logger = config.logger
config.time_zone = "Europe/London"
routes.draw do
put "/events/:id" => "events#update"
end
end
class EventsController < ActionController::Base
include Rails.application.routes.url_helpers
def update
@event = Event.find(params[:id])
if @event.update(params.require(:event).permit(:start_datetime))
render plain: "Event was successfully updated."
else
render plain: "Event was failed to update."
end
end
end
require "minitest/autorun"
require "rack/test"
Time.zone_default = Time.find_zone!("Europe/London")
ActiveRecord::Base.time_zone_aware_attributes = true
class BugTest < Minitest::Test
include Rack::Test::Methods
def test_update_at_ambiguous_time
# this date is 2017/10/29 1:00 Europe/London
# however, 2017/10/29 1:00 Europe/London happens at two times
# 2017/10/29 1:00 UTC
# 2017/10/29 0:00 UTC
# When rails converts back from local->UTC it always chooses
# the first one
time = ActiveSupport::TimeZone["UTC"].parse("2017/10/29 1:00")
event = Event.new(start_datetime: time)
event.save(validate: false)
event.reload
year = event.start_datetime.year
assert_equal(2017, year)
month = event.start_datetime.month
assert_equal(10, month)
day = event.start_datetime.day
assert_equal(29, day)
hour = event.start_datetime.hour
assert_equal(1, hour)
minute = event.start_datetime.min
assert_equal(0, minute)
put "/events/#{event.id}",
"event"=>{"start_datetime(1i)"=> year.to_s,
"start_datetime(2i)"=> month.to_s,
"start_datetime(3i)"=>day.to_s,
"start_datetime(4i)"=>hour.to_s,
"start_datetime(5i)"=>minute.to_s}
assert last_response.ok?
event.reload
assert_equal(time, event.start_datetime)
end
def test_update_in_ambiguous_time_zone
time = ActiveSupport::TimeZone["UTC"].parse("2017/10/29 3:00")
event = Event.new(start_datetime: time)
event.save(validate: false)
year = event.start_datetime.year
assert_equal(2017, year)
month = event.start_datetime.month
assert_equal(10, month)
day = event.start_datetime.day
assert_equal(29, day)
hour = event.start_datetime.hour
assert_equal(3, hour)
minute = event.start_datetime.min
assert_equal(0, minute)
put "/events/#{event.id}",
"event"=>{"start_datetime(1i)"=> year.to_s,
"start_datetime(2i)"=> month.to_s,
"start_datetime(3i)"=>day.to_s,
"start_datetime(4i)"=>hour.to_s,
"start_datetime(5i)"=>minute.to_s}
assert last_response.ok?
event.reload
assert_equal(time, event.start_datetime)
end
private
def app
Rails.application
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment