module ActiveSupport::Testing::TimeHelpers

def after_teardown

def after_teardown
  travel_back
  super
end

def freeze_time(&block)

Time.current # => Sun, 09 Jul 2017 15:34:50 EST -05:00
end
User.create.created_at # => Sun, 09 Jul 2017 15:34:49 EST -05:00
sleep(1)
freeze_time do
Time.current # => Sun, 09 Jul 2017 15:34:49 EST -05:00

state at the end of the block:
This method also accepts a block, which will return the current time back to its original

Time.current # => Sun, 09 Jul 2017 15:34:49 EST -05:00
sleep(1)
freeze_time
Time.current # => Sun, 09 Jul 2017 15:34:49 EST -05:00

Calls +travel_to+ with +Time.now+.
def freeze_time(&block)
  travel_to Time.now, &block
end

def simple_stubs

def simple_stubs
  @simple_stubs ||= SimpleStubs.new
end

def travel(duration, &block)

Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
end
User.create.created_at # => Sun, 10 Nov 2013 15:34:49 EST -05:00
travel 1.day do
Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00

state at the end of the block:
This method also accepts a block, which will return the current time back to its original

DateTime.current # => Sun, 10 Nov 2013 15:34:49 -0500
Date.current # => Sun, 10 Nov 2013
Time.current # => Sun, 10 Nov 2013 15:34:49 EST -05:00
travel 1.day
Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00

at the end of the test.
stubbing +Time.now+, +Date.today+, and +DateTime.now+. The stubs are automatically removed
Changes current time to the time in the future or in the past by a given time difference by
def travel(duration, &block)
  travel_to Time.now + duration, &block
end

def travel_back

Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00

end
Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
travel_back do

Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
travel_to Time.zone.local(2004, 11, 24, 1, 4, 44)

Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00

This method also accepts a block, which brings the stubs back at the end of the block:

Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
travel_back

Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
travel_to Time.zone.local(2004, 11, 24, 1, 4, 44)

Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00

+travel+, +travel_to+, and +freeze_time+.
Returns the current time back to its original state, by removing the stubs added by
def travel_back
  stubbed_time = Time.current if block_given? && simple_stubs.stubbed?
  simple_stubs.unstub_all!
  yield if block_given?
ensure
  travel_to stubbed_time if stubbed_time
end

def travel_to(date_or_time)

Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
end
Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
travel_to Time.zone.local(2004, 11, 24, 1, 4, 44) do
Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00

state at the end of the block:
This method also accepts a block, which will return the current time back to its original

leading to off-by-one-second errors).
errors with external services, like MySQL (which will round instead of floor,
Note that the usec for the time passed will be set to 0 to prevent rounding

please always use Time.current and Date.current.)
or Date.today, in order to honor the application time zone
be different. (Note that you rarely want to deal with Time.now,
Date.today the date according to Time.now, which may
Date.current returns a date equal to the argument, and
and Time.now its equivalent in the system time zone. Similarly,
application time zone. Time.current returns said timestamp,
Dates are taken as their timestamp at the beginning of the day in the

DateTime.current # => Wed, 24 Nov 2004 01:04:44 -0500
Date.current # => Wed, 24 Nov 2004
Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
travel_to Time.zone.local(2004, 11, 24, 1, 4, 44)
Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00

The stubs are automatically removed at the end of the test.
+Date.today+, and +DateTime.now+ to return the time or date passed into this method.
Changes current time to the given time by stubbing +Time.now+,
def travel_to(date_or_time)
  if block_given? && in_block
    travel_to_nested_block_call = <<~MSG
Calling `travel_to` with a block, when we have previously already made a call to `travel_to`, can lead to confusing time stubbing.
Instead of:
   travel_to 2.days.from_now do
     # 2 days from today
     travel_to 3.days.from_now do
       # 5 days from today
     end
   end
preferred way to achieve above is:
   travel 2.days do
     # 2 days from today
   end
   travel 5.days do
     # 5 days from today
   end
    MSG
    raise travel_to_nested_block_call
  end
  if date_or_time.is_a?(Date) && !date_or_time.is_a?(DateTime)
    now = date_or_time.midnight.to_time
  elsif date_or_time.is_a?(String)
    now = Time.zone.parse(date_or_time)
  else
    now = date_or_time.to_time.change(usec: 0)
  end
  stubbed_time = Time.now if simple_stubs.stubbing(Time, :now)
  simple_stubs.stub_object(Time, :now) { at(now.to_i) }
  simple_stubs.stub_object(Date, :today) { jd(now.to_date.jd) }
  simple_stubs.stub_object(DateTime, :now) { jd(now.to_date.jd, now.hour, now.min, now.sec, Rational(now.utc_offset, 86400)) }
  if block_given?
    begin
      self.in_block = true
      yield
    ensure
      if stubbed_time
        travel_to stubbed_time
      else
        travel_back
      end
      self.in_block = false
    end
  end
end