timecop: Timecop.freeze with Dates is lossy and users should be warned
Realistically, ActiveSupport’s Time.zone
being set and Timecop.freeze
-ing with Date
objects are not compatible. Date
is a lossy representation of Time
in Ruby anyway since it doesn’t store offset, and the assumptions we make to handle that are surprising.
I think that freezing to Date
objects should be deprecated.
Consider the following:
require 'active_support/all'
# The following timezones were chosen because they will always
# exemplify this bug; other timezone combinations may only show
# it during certain parts of the day.
ENV['TZ'] = 'Pacific/Kiritimati' # UTC+14:00
Time.zone = 'Midway Island' # UTC-11:00
Timecop.freeze do # prevent drift
puts Date.today #=> 2013-07-19
# Date.today is generated using ENV['TZ'] or the computer's clock,
# independent of Time.zone.
Timecop.freeze(Date.today) do
# But in freezing, we used Time.zone and *assumed* that the date given was in
# the timezone set for ActiveSupport, then froze time to that day's beginning
# timestamp.
# Here we encounter drift.
puts Date.today #=> 2013-07-20
Timecop.freeze(Date.today) do
# And we can drift again.
puts Date.today #=> 2013-07-21
end
end
end
About this issue
- Original URL
- State: closed
- Created 11 years ago
- Comments: 18 (6 by maintainers)
@brokenladder because of the interplay of
ENV['TZ']
andTime.zone
, more setup was required to exemplify the bug:My point though, was not just that there is a bug, but that the assumptions inherent in making the conversion from
Date
to aTime
or anActiveSupport::TimeWithZone
object make supportingDate
as an input faulty at best.A date is defined as the period between a midnight and the following midnight, so moment-in-time data (such as the timestamp at the beginning of that day) cannot be extracted without additional information or assumptions about the UTC-offset; it is, therefore a lossy representation of time. Timecop is built to freeze the time, not the date, and using
Date
as an input forces Timecop to make assumptions about the UTC-offset, in this case using the one provided by Time.zone (where Date.today does not).The first example you gave does not go through
Time.parse
, since it is supplying aDate
object toTimecop.freeze
, and is not representative of the context in which it was quoted. ActiveSupport’sString#to_date
makes no assumptions about timezone becauseDate
objects don’t need this, but it forces Timecop to make an assumption when converting the providedDate
object to aTime
.In the second example (which does go through
Time.parse
), the same assumptions are being made about utc-offset by bothTime.parse
andDate.today
, the two will always agree, regardless of timezone settings inTime.zone
orENV['TZ']
.