Comments (32)
Hey, All!
I was reading through this (a bit, it's quite long!) via a co-worker and wanted to present a solution we have for this instance.
Timecop.freeze(Time.parse('Tue Aug 28 06:00:00 CDT 2018').utc)
We like this because it clear about your starting point for freezing. We want to be able to say "At this time in UTC relative to our local time (CDT), freeze time".
In an ideal world we can set the timezone from within the Timecop interface without having to get Rails involved. We don't do all of our work in Rails and keeping the two (Ruby, Rails) separate seems like a preferred position.
Hopefully this helps.
from timecop.
I'm under GMT-3 and the other developer is under GMT-4. We are using VCR to record some requests with the timestamp in the querystring (HMAC).
If we use Timecop.freeze
it doesn't preserve my timezone, raising VCR errors VCR::Errors::UnhandledHTTPRequestError
.
Here is an example:
context 'using Timecop.freeze' do
before do
now = Time.new(2013, 8, 16, 10, 55, 14, '-03:00')
Timecop.freeze(now)
end
it 'requests ... ' do
now = Time.now
# WRONG, it should be -0300
# => 2013-08-16 09:55:14 -0400
end
end
context 'using Time.stub' do
before do
now = Time.new(2013, 8, 16, 10, 55, 14, '-03:00')
Time.stub(now: now)
end
it 'requests ... ' do
now = Time.now
# it is correct ;)
=> 2013-08-16 10:55:14 -0300
end
end
from timecop.
Since I did not want to patch the gem without further discussion, my workaround was external. It only works well for dates.
# Timecop does not handle UTC well
# Adjust dates to a time that will be in the same date in both UTC and local TZ
def safe_date(t)
tz_offset = Time.now.gmt_offset
if tz_offset != 0
t.beginning_of_day - (tz_offset + (tz_offset / tz_offset.abs) * 1.minute)
else
t
end
end
it 'returns all reports generated on a certain date' do
Timecop.freeze safe_date(1.day.ago) do
create_list :report, 4, name: '1.day.ago'
end
Report.generated_on(safe_date(1.day.ago)).count.should == 4
end
from timecop.
I don't really understand your issue, so can you take your solution and make an actual patch we can look at?
from timecop.
My solution doesn't actually fix Timecop's behavior. Are you suggesting I submit it as a class-level helper method on Timecop or as a patch to Date?
from timecop.
@ssimeonov I'd be interested to see the code that is used for your Report#generated_on
and your create_list
helper, to see if either of those is in some way normalizing the Time
objects involved (e.g., converting them to Date
objects).
Your workaround is not applying an offset (which would create an object that has the same unix timestamp), but rather adding/subtracting seconds from t.beginning_of_day
(aka, changing the time by the offset, which has a side effect of ensuring that the shifted timestamp's #to_date
yields the matching UTC day).
Also, for debugging purposes, could you provide your current time zone?
from timecop.
@yaauie there is no Time
normalization code in Report#generated_on
or create_list
, other than calling #to_date
at some point but that's due to core business logic and not anything to do with normalization per se. Beyond that, your description of my workaround is exactly right, which is why I don't see it as a great solution, even though it is TZ-agnostic.
My TZ is EST with DST observation.
from timecop.
Without trying to understand the purported bug, I wouldn't write a test like this:
it 'returns all reports generated on a certain date' do
Timecop.freeze 1.day.ago do
create_list :report, 4, name: '1.day.ago'
end
Report.generated_on(1.day.ago).count.should == 4
end
You call 1.day.ago
twice and expect them to match up. If the first one runs a millisecond before midnight and the last one a millisecond after midnight, the example will fail.
You could put both inside the block or use a variable, maybe something like:
it 'returns all reports generated on a certain date' do
date = Date.yesterday
Timecop.freeze(date) do
create_list :report, 4, name: 'whatever'
end
Report.generated_on(date).count.should == 4
end
from timecop.
But also, your fix seems suspect, though I don't quite understand your issue.
We would have to see your implementation of generated_on
.
Dates and time zones can be a bit tricky, since dates without time can't be converted between zones.
from timecop.
Timecop.freeze("2012-05-24".to_date) do assert_equal "2012-05-24", Date.today.to_s end
This test fails depending on your system TZ.
It doesn't seems right.
Timecop.freeze(DATE) should behave different from Timecop.freeze(TIME) since dates are not affected by tzs?
from timecop.
@irregular You're right, there seems to be a bug or unexpected behavior there.
I tried this in RSpec:
date = Date.new(2012, 5, 24)
Timecop.freeze(date) do
p ENV['TZ']
p Time.now
Date.today.to_s.should == "2012-05-24"
end
The output is:
"PST"
2012-05-23 22:00:00 +0000
Our tests set ENV['TZ'] = "PST" but the machine local time and the Rails Time.zone are CET.
The value of Time.now corresponds to midnight on the start of 2012-05-24 in CET, but the output is shown as UTC.
from timecop.
If I do
date = Date.new(2012, 5, 24)
p date.to_time
in the same test, the output is
2012-05-24 00:00:00 +0000
by the way.
from timecop.
@irregular @henrik Timecop only mocks Time methods, so rather than using Date.today use Time.now.to_date, even if Timecop's handling of timezones were perfect you would still have issues there.
from timecop.
I had a similar issue with datetime comparisons after adding Timecop, the above solution seems to have worked. Here's how we changed our global before(:each) for the suite:
now = ::Time.now.utc
::Time.stub!(:now).and_return(now)
- date_time_now = ::DateTime.now.utc
- ::DateTime.stub!(:now).and_return(date_time_now)
+ ::DateTime.stub!(:now).and_return(now.to_datetime)
from timecop.
I believe some of the issues discussed here are resolved here : #73 (use of Date.today on some passed dates to freeze/travel)
from timecop.
1.day.ago
is the equivalent of Time.now - 1.day
, which is timezone-lossy; that means this issue is not a Timecop issue and a red herring:
> Time.zone = 'Hawaii' # because it's a different date than my computer's ENV['TZ'] UTC right now
> 1.day.ago
#=> 2013-07-17 09:02:50 +0000
> Time.now - 1.day
#=> 2013-07-17 09:02:56 +0000
> Time.zone.now - 1.day
#=> Tue, 16 Jul 2013 23:05:42 HST -10:00
My best guess would be that somewhere in your Report.generated_on
method, you're going through the current Time.zone
to get to a date (e.g., Time.zone.at(timestamp).to_date
), while the report's generated_on date just gets set with Date.today
or Time.now.to_date
(or vice versa); either would cause the drift you're experiencing, but both are outside the hands of Timecop. Because #to_date
is also a lossy conversion, either every instance of it must go through Time.zone
or none of them may.
> Time.zone = 'Hawaii' # same reason as above
> Date.today
#=> Thu, 18 Jul 2013
> Time.now.to_date
#=> Thu, 18 Jul 2013
> Time.zone.now.to_date
#=> Wed, 17 Jul 2013
from timecop.
Interesting that 1.day.ago
isn't Time.zone aware. Didn't expect that.
from timecop.
+1
I just face this same issue...
from timecop.
@bcardiff if you'll follow the comments, you'll see that I believe this not to be a Timecop issue, but rather an issue in the code under test. If you could supply additional information that proves me wrong, I would be glad to dig into it some more.
from timecop.
@yaauie I just extract a little example of what I face some minutes ago. Apologize me if I'm wrong. But it was kind of weird the issue.
I put a repo at https://github.com/bcardiff/timecop_issue
Model would be https://github.com/bcardiff/timecop_issue/blob/master/app/models/foo.rb
Spec would be https://github.com/bcardiff/timecop_issue/blob/master/spec/models/foo_spec.rb
The 1 & 2 are failing, and the 3 is passing. The only difference is the Timecop.freeze initialization.
Maybe this should fall more on TimeWithZone since, if the foo.reload
are removed, all specs pass. (I've just discovered that :-$). But either way, it was kind of unexpected that the first test fail.
from timecop.
@bcardiff you aren't failing due to unexpected timezone drift, so your bug is not this one.
EDIT: Your bug has been fixed since the 'v0.5.3' Timecop in your Gemfile.lock. Please upgrade bundle update --source timecop
😄
I'm getting the following output, which is consistent with what you noted above:
Failures:
1) Foo should mark with current time 2
Failure/Error: foo.time_field.should eq(Time.zone.now)
expected: Thu, 18 Jul 2013 19:36:40 UTC +00:00
got: Thu, 18 Jul 2013 19:36:40 UTC +00:00
(compared using ==)
Diff:
# ./spec/models/foo_spec.rb:20:in `block (3 levels) in <top (required)>'
# ./spec/models/foo_spec.rb:15:in `block (2 levels) in <top (required)>'
2) Foo should mark with current time
Failure/Error: foo.time_field.should eq(Time.zone.now)
expected: Thu, 18 Jul 2013 19:36:40 UTC +00:00
got: Thu, 18 Jul 2013 19:36:40 UTC +00:00
(compared using ==)
Diff:
# ./spec/models/foo_spec.rb:10:in `block (3 levels) in <top (required)>'
# ./spec/models/foo_spec.rb:5:in `block (2 levels) in <top (required)>'
Finished in 0.02797 seconds
3 examples, 2 failures
Worth noting is that those timestamps look correct, but they're failing anyway. This has to do with a loss of precision somewhere in the underlying storage of the Time object, but is hidden because their #to_s
representation looks the same. If you compare the float values expect { foo.time_field.to_f }.to eq(Time.zone.now.to_f)
, you will see more clearly that there is precision loss, which is a different bug. Your Date
-frozen one passes (most of the time, see #100) because a Date's starting timestamp doesn't have sub-second precision.
from timecop.
@yaauie I see. Thanks for clarifying. I verified it was just a precision issue. using Time.now.change(sec: 0) or Date works nicely in the specs. I appreciate your time.
from timecop.
@bcardiff (see my edit above. you can just upgrade to the newest Timecop since the precision issue has been fixed)
from timecop.
+1
I'm trying to test something in every time zone to make sure it has the same output.
Output of the times before calling Timecop.freeze:
testing 2014-10-30 14:02:02 -1200
testing 2014-10-30 15:02:02 -1100
testing 2014-10-30 16:02:02 -1000
...
Output of the times when calling Time.now from Timecop.freeze block:
testing 2014-10-30 22:02:02 -0400
testing 2014-10-30 22:02:02 -0400
testing 2014-10-30 22:02:02 -0400
...
from timecop.
> Time.now
=> 2014-09-29 10:06:24 +0000
> Date.today
=> Mon, 29 Sep 2014
> Date.today.beginning_of_month
=> Mon, 01 Sep 2014
> Timecop.freeze Date.today.beginning_of_month
=> 2014-08-31 22:00:00 +0000
The expected date is 2014-09-01.
I'm not sure if this is the same problem, but my timezone is CEST (+2), so it's
probably related to this (even though Time.now
gives +0000
?)....
from timecop.
So, this thread seems to be on and off, but I have found that our tests are failing based on which timezone it is run on.
This works just fine EST.
> Timecop.freeze('2013-11-20 14:05:16 -0500')
=> "2013-11-20T14:05:16.000-05:00"
But in IST, it's fast forward 10.5 hours -- likely due to how TimeZone is calculated from GMT/UTC. -05:00 EST is most definitely 10.5 hours ahead in IST.
> Timecop.freeze('2013-11-20 14:05:16 -0500')
=> "2013-11-21T00:35:16.000+05:30"
I'm not convinced that we have the correct format for tests or anything (we inherited this app), but it's certainly causing issues for having developers in multiple countries across multiple timezones.
We used dotenv
and dotenv-rails
to load in a .env in the test environment that set TZ=America/New_York. This let us keep application configuration the same in the codebase, but set the "system" time zone to the zone the tests expected.
from timecop.
Our tests were failing on CI because our Rails config.time_zone
was configured to one thing, but the CI machine was configured to something different. Calling Time.zone.now
returned the Rails configured timezone, but inside the Timecop.freeze
block, the time was for the system timezone.
I was able to work around this problem by changing the system timezone of our CI machine.
from timecop.
I believe I am experiencing these issues as well, currently trying to resolve issues regarding mixed use of Date
, Time
, and ActiveSupport::TimeWithZone
first before submitting some code relating to timecop - however, I think there might be an issue between my local dev machine's system TZ and the TZ Timecop uses.
Yes, freezing Timecop creates an issue where a timezone is set that is actually not set anywhere within the application or system. ActiveSupport thinks its UTC (correct) while Timecop thinks its PDT.
from timecop.
@stratigos i'm having a similar issue with ActiveSupport::TimeWithZone where it's converts to UTC but takes into the calculation the actual system timezone. So it gives me 2 different dates :(
So sad.
from timecop.
+1
I'm having an issue with differences time zones too.
My Rails application has set timezone: Eastern Time (US & Canada)
when I create record and pass to it mocked time the time zone of record set as EDT
but time zone of mocked time is GMT+3
. GMT+3
is my time zone where I am it's Russia.
Rails 5.0.2
Ruby 2.3.1
Timecop 0.8.1
I bypassed it by mocking value of record that is expected in my test.
before do
allow(model).to receive(:last_sign_in_at).and_return(mocked_time)
end
from timecop.
@IlkhamGaysin have you seen #182 ?
from timecop.
Still tracing an issue(?) with Timecop, my own notes:
> Timecop.freeze(Date.new(2000, 1, 1)) { puts Date.today.iso8601 }
=> "1999-12-31"
> Time.now.getlocal.zone
=> "PST"
> Time.now.gmt_offset
=> -28800
Same problem with Time.now.to_date
:
Timecop.freeze(Date.new(2000, 1, 1)) { puts Time.now.to_date }
=> 1999-12-31
unsure if timezone is related here
bundle show timecop
/Users/me/.rvm/gems/ruby-2.3.4@project/gems/timecop-0.9.1
Edit: was reading through comments top to bottom while taking notes. I'm in Rails. This is almost certainly from #182. Carry on.
from timecop.
Related Issues (20)
- Is call to Timecop.travel(some random time) atomic in nature? HOT 1
- Time#travel should be side-effect free and not resume system clock HOT 10
- .
- Timecop.thread_safe = true by default HOT 1
- Timecop handles GMT/UTC Time objects incorrectly HOT 2
- Doesn't work for `Rails.cache` with `expires_in` HOT 2
- ruby 3.1 support
- Date.strptime returns wrong month for %Y HOT 6
- %W is not handled properly HOT 1
- Ruby 2.5 seems to work? HOT 4
- Using timecop with psych 4.0.5, behavior differs between the test environment and other environments HOT 3
- Timecop.scale to mock existing times HOT 2
- Timecop.freeze not working with expect().to receive HOT 3
- Test
- Need a proper test for making sure we don't change timezones inadvertently
- DateTime parse without date not working properly HOT 5
- DateTime.parse("Wednesday 12:00") format is always returning midnight HOT 1
- Time.now sometimes has greater-than-nanosecond precision when called under Timecop.travel HOT 3
- DateTime.parse does not handle CWeek correctly
- Mocking `Process.clock_gettime` causes `Concurrent::IVar` with timeout to wait indefinitely HOT 5
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from timecop.