Giter Club home page Giter Club logo

Comments (32)

dadamschi avatar dadamschi commented on July 22, 2024 6

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.

phstc avatar phstc commented on July 22, 2024 2

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.

ssimeonov avatar ssimeonov commented on July 22, 2024

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.

travisjeffery avatar travisjeffery commented on July 22, 2024

I don't really understand your issue, so can you take your solution and make an actual patch we can look at?

from timecop.

ssimeonov avatar ssimeonov commented on July 22, 2024

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.

yaauie avatar yaauie commented on July 22, 2024

@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.

ssimeonov avatar ssimeonov commented on July 22, 2024

@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.

henrik avatar henrik commented on July 22, 2024

@ssimeonov:

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.

henrik avatar henrik commented on July 22, 2024

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.

old-grrt avatar old-grrt commented on July 22, 2024
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.

henrik avatar henrik commented on July 22, 2024

@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.

henrik avatar henrik commented on July 22, 2024

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.

travisjeffery avatar travisjeffery commented on July 22, 2024

@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.

readysetawesome avatar readysetawesome commented on July 22, 2024

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.

tommeier avatar tommeier commented on July 22, 2024

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.

yaauie avatar yaauie commented on July 22, 2024

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.

henrik avatar henrik commented on July 22, 2024

Interesting that 1.day.ago isn't Time.zone aware. Didn't expect that.

from timecop.

bcardiff avatar bcardiff commented on July 22, 2024

+1

I just face this same issue...

from timecop.

yaauie avatar yaauie commented on July 22, 2024

@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.

bcardiff avatar bcardiff commented on July 22, 2024

@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.

yaauie avatar yaauie commented on July 22, 2024

@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.

bcardiff avatar bcardiff commented on July 22, 2024

@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.

yaauie avatar yaauie commented on July 22, 2024

@bcardiff (see my edit above. you can just upgrade to the newest Timecop since the precision issue has been fixed)

from timecop.

WattsInABox avatar WattsInABox commented on July 22, 2024

+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.

arp242 avatar arp242 commented on July 22, 2024
> 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.

lauram-spindance avatar lauram-spindance commented on July 22, 2024

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.

jamesmartin avatar jamesmartin commented on July 22, 2024

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.

stratigos avatar stratigos commented on July 22, 2024

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.

maximveksler avatar maximveksler commented on July 22, 2024

@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.

IlkhamGaysin avatar IlkhamGaysin commented on July 22, 2024

+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.

ballcheck avatar ballcheck commented on July 22, 2024

@IlkhamGaysin have you seen #182 ?

from timecop.

AndrewRayCode avatar AndrewRayCode commented on July 22, 2024

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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.