timecop: Time Travel, Freezing, and Acceleration for Ruby Tests
A gem providing "time travel", "time freezing", and "time acceleration" capabilities, making it simple to test time-dependent code. It provides a unified method to mock Time.now, Date.today, and DateTime.now in a single call.
At a glance
- What is it?
- timecop is a Ruby gem that controls Time.now, Date.today, DateTime.now, and optionally Process.clock_gettime during tests. It provides three modes: freeze for static time, travel for an offset that still advances, and scale for accelerated time.
- Who is it for?
- timecop is the right choice for any Ruby project that needs to test time-sensitive business logic: mortgage due dates, billing cycles, session expiry, or scheduled jobs. Because it has no dependencies and works outside Rails, it fits plain Ruby projects as well as Rails applications.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 13 days ago.
- What is it written in?
- Mainly Ruby, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What timecop Solves and Which Projects Need It
Time-dependent code is one of the hardest categories to test reliably. A function that checks whether a mortgage is due, an invoice is overdue, or a session has expired produces different results depending on when the test runs. timecop addresses this by intercepting the Ruby system calls that return the current time and replacing them with controlled values for the duration of a test.
The README summarizes the problem: tests written today may fail tomorrow if they rely on the real current time. timecop makes those tests deterministic by letting the developer specify exactly what time the code should see. It works with any Ruby project, not just Rails, because it has no dependencies. The gem mocks Time.now, Date.today, DateTime.now, and optionally Process.clock_gettime in a single call.
Installing timecop and Writing the First Frozen Test
Add timecop to your Gemfile:
bundle add timecopThe README gives a direct example of testing a time-sensitive condition. To verify that a user's mortgage becomes due after 30 days:
joe = User.find(1)
joe.purchase_home()
assert !joe.mortgage_due?
# move ahead a month and assert that the mortgage is due
Timecop.freeze(Date.today + 30) do
assert joe.mortgage_due?
endTimecop.freeze inside a block sets the clock to the specified value for the duration of the block, then restores it automatically when the block exits. The time argument accepts a Time instance, a DateTime instance, a Date instance, individual year/month/day/hour/minute/second arguments, or a single integer as an offset in seconds from the current time.
For a set of tests sharing the same time context, use setup and teardown:
describe "some set of tests to mock" do
before do
Timecop.freeze(Time.local(1990))
end
after do
Timecop.return
end
it "should do blah blah blah" do
end
endTimecop.return turns off all time mocking and restores real time.
The Difference Between freeze and travel
The README distinguishes two behaviors carefully. Timecop.freeze sets a fixed point in time; Time.now returns the same value no matter how long the program runs. Timecop.travel computes an offset and keeps time moving forward from the specified point. The README demonstrates:
new_time = Time.local(2008, 9, 1, 12, 0, 0)
Timecop.freeze(new_time)
sleep(10)
new_time == Time.now # ==> true
Timecop.return # "turn off" Timecop
Timecop.travel(new_time)
sleep(10)
new_time == Time.now # ==> falseAfter sleeping 10 seconds with freeze, Time.now still equals new_time. After sleeping 10 seconds with travel, Time.now is new_time plus 10 seconds. Choose freeze when you need a repeatable snapshot. Choose travel when your test code needs to observe time passing, such as checking a timeout expiry that depends on elapsed wall-clock time within the test.
The scale Method: Running Days in Seconds
timecop.scale is the third mode. It does not fix time and does not simply offset it: it multiplies the speed at which time passes. The README shows:
# seconds will now seem like hours
Timecop.scale(3600)
Time.now
# => 2012-09-20 21:23:25 -0500
# seconds later, hours have passed and it's gone from 9pm at night to 6am in the morning
Time.now
# => 2012-09-21 06:22:59 -0500With a scale factor of 3600, each real second counts as one hour. This is useful for testing reports, invoices, or other logic that needs an entire 30-day billing cycle to pass, but where running the real cycle is impractical in a test suite. The README credits Ken Mayer, David Holcomb, and Pivotal Labs for this feature.
safe_mode and Preventing Unclosed Time Windows
Calling Timecop.freeze or Timecop.travel without a block leaves time mocked until an explicit Timecop.return call. If a test fails before Timecop.return runs, every subsequent test in the suite sees the wrong time. Timecop.safe_mode prevents this:
# turn on safe mode
Timecop.safe_mode = true
# check if you are in safe mode
Timecop.safe_mode?
# => true
# using method without block
Timecop.freeze
# => Timecop::SafeModeException: Safe mode is enabled, only calls passing a block are allowed.With safe_mode enabled, any call to freeze or travel that omits a block raises Timecop::SafeModeException immediately. This guarantees that time is always restored at the end of the block, regardless of whether the test passes or fails. The README recommends safe_mode for teams where test isolation is a concern.
Process.clock_gettime and Rails Compatibility
By default, timecop does not mock Process.clock_gettime. This matters for code that uses Process.clock_gettime for monotonic timing rather than Time.now. To enable mocking:
# turn on
Timecop.mock_process_clock = trueThe README explicitly warns about a compatibility issue between Rails and Ruby date methods. Ruby's Date.today and Rails' Date.tomorrow or Date.yesterday can behave differently in the same test, and this interaction may cause unexpected failures. The README does not document a fix; it flags it as a known area of caution. Teams mixing Ruby Date methods with Rails date helpers should test this interaction before relying on it in critical test scenarios.
Nested calls to Timecop.travel and Timecop.freeze are supported. Each nested block maintains its own interpretation of the current time, and time is restored correctly when each block exits.
Comparison with Rails ActiveSupport travel_to
Rails includes ActiveSupport::Testing::TimeHelpers, which provides travel_to as a built-in test helper. It works similarly to Timecop.freeze: time is set to the specified value for the duration of a block. For a standard Rails application running a single travel_to call in a test, no additional gem is needed.
timecop differs in three ways that matter for some projects. First, it has no Rails dependency and works in plain Ruby projects. Second, it provides the scale mode, which has no Rails equivalent. Third, it mocks Time.now, Date.today, and DateTime.now together in one call; Rails' travel_to may behave differently for Date.today in some configurations.
The README does not compare directly with travel_to, but notes that timecop was originally created by jtrupiano and is now maintained by travisjeffery. The last push was on 2026-09-16.
Editorial conclusion
timecop is the right choice for any Ruby project that needs to test time-sensitive business logic: mortgage due dates, billing cycles, session expiry, or scheduled jobs. Because it has no dependencies and works outside Rails, it fits plain Ruby projects as well as Rails applications. Teams that use only Rails and need a single travel_to call in a test can use ActiveSupport::Testing::TimeHelpers directly without adding a gem. Before using timecop in a Rails application, read the README warning about mixing Ruby Date.today with Rails Date.tomorrow or Date.yesterday, and enable safe_mode to prevent unclosed time windows from leaking across tests.
Frequently asked questions
What is the difference between Timecop.freeze and Timecop.travel?
Timecop.freeze stops the clock at the specified time; Time.now returns that same value no matter how long the program runs. Timecop.travel sets an offset and keeps time moving forward from that point, so Time.now advances as real time passes. Use freeze for deterministic snapshots and travel when your code needs to observe elapsed time within the test.
How do I prevent timecop from leaking across tests?
Enable safe_mode by setting Timecop.safe_mode = true. This forces all Timecop.freeze and Timecop.travel calls to use the block form, which restores time automatically when the block exits. Without safe_mode, forgetting to call Timecop.return leaves the mocked time in effect for all subsequent tests.
Does timecop work with plain Ruby projects, or only with Rails?
timecop has no dependencies and works with any Ruby project. The README states explicitly that it can be used with any Ruby project and also works with Ruby on Rails. Process.clock_gettime mocking is opt-in and disabled by default.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/travisjeffery-timecop)