Reason #107 • April 17th, 2026

ActiveRecord: Duration#ago and #from_now

Our journey through ActiveSupport's time helpers culminates now with Duration#ago and Duration#from_now. These methods take a duration and apply it relative to the current time, giving us a new time in the past or future:

Ruby
# Assuming the current time is 2026-04-07 12:00:00 UTC

2.hours.ago
# => 2026-04-07 10:00:00 +0000

3.days.from_now
# => 2026-04-10 12:00:00 +0000

1.month.from_now
# => 2026-05-07 12:00:00 +0000

2.weeks.ago
# => 2026-03-24 12:00:00 +0000

71.years.from_now
# => 2097-04-07 12:00:00 +0000

(1.day + 12.hours).from_now
# => 2026-04-08 00:00:00 +0000
      
JavaScript
// Assuming the current time is 2026-04-07T12:00:00.000Z

new Date(Date.now() - 2 * 60 * 60 * 1000);
// => 2026-04-07T10:00:00.000Z

new Date(Date.now() + 3 * 24 * 60 * 60 * 1000);
// => 2026-04-10T12:00:00.000Z

new Date(Date.now() + 30 * 24 * 60 * 60 * 1000);
// => 2026-05-07T12:00:00.000Z

new Date(Date.now() - 14 * 24 * 60 * 60 * 1000);
// => 2026-03-24T12:00:00.000Z

// Not accounting for leap years here puts us in March instead of April!
new Date(Date.now() + 71 * 365 * 24 * 60 * 60 * 1000);
// => 2097-03-20T12:00:00.000Z

new Date(Date.now() + (1 * 24 + 12) * 60 * 60 * 1000);
// => 2026-04-08T00:00:00.000Z
      

Note the subtle beauty which is surfaced with the variable-length units like months (28-31 days long) and years (365 or 366 days long). ActiveSupport uses calendar-aware math there, which means 1.month.from_now behaves like a human would expect, not like a naive pile of seconds, which brought us to the wrong month in one of the naive JavaScript examples.

History

The English-like ago / from_now style actually predates ActiveSupport::Duration itself. ActiveSupport 0.10.0, released in 2005, already had Numeric#ago and #from_now in its early time DSL. Back then, though, this was much more naive: 1.month was effectively 30.days, and 1.year was 365 days, in seconds, so the syntax was lovely before the calendar math was.

Rails 2.0.0, released in 2007, is where this became the API we recognize today. That release introduced ActiveSupport::Duration, so expressions like 2.hours, 3.days, and 1.month started returning duration objects with ago / from_now defined on them. That is the shift that made month and year arithmetic calendar-aware through advance, instead of just adding a fixed number of seconds.

Rails 2.1.0, released in 2008, refined the no-argument form to use Time.current instead of Time.now, which made 5.minutes.ago and 3.days.from_now honor Rails time zones and return ActiveSupport::TimeWithZone when config.time_zone is set. Rails 2.2.0 later expanded Time#advance to handle fractional days and weeks too, so expressions like 1.7.weeks.ago became supported. Since then the public API has stayed remarkably stable; later Rails mostly polished internals, and Rails 4.1 in 2014 explicitly deprecated the old bare 5.ago / 5.from_now style in favor of the clearer duration-first form like 5.seconds.ago.

Reason #108 ?