Relative time formatting for past and future timestamps
gem install philiprehberger-time_agoRelative time formatting for past and future timestamps
Add to your Gemfile:
gem "philiprehberger-time_ago"
Or install directly:
gem install philiprehberger-time_ago
require "philiprehberger/time_ago"
Philiprehberger::TimeAgo.format(Time.now - 180) # => "3 minutes ago"
Philiprehberger::TimeAgo.format(Time.now - 7200) # => "2 hours ago"
Philiprehberger::TimeAgo.format(Time.now - 180, style: :short) # => "3m ago"
Philiprehberger::TimeAgo.format(Time.now - 7200, style: :short) # => "2h ago"
Philiprehberger::TimeAgo.format(Time.now + 300) # => "in 5 minutes"
Philiprehberger::TimeAgo.format(Time.now + 3600) # => "in 1 hour"
reference = Time.new(2026, 3, 15, 12, 0, 0)
target = Time.new(2026, 3, 15, 10, 0, 0)
Philiprehberger::TimeAgo.format(target, relative_to: reference) # => "2 hours ago"
old_time = Time.now - (60 * 86_400)
Philiprehberger::TimeAgo.format(old_time, max_days: 30) # => "Jan 20, 2026"
# Show only the largest unit at hour precision
Philiprehberger::TimeAgo.format(Time.now - 5400, precision: :hour)
# => "1 hour ago"
Philiprehberger::TimeAgo.format(Time.now - 3720, max_units: 2)
# => "1 hour 2 minutes ago"
Philiprehberger::TimeAgo.format(Time.now - 3720, style: :short, max_units: 2)
# => "1h 2m ago"
Philiprehberger::TimeAgo.format(Time.now - 5400, compound: true)
# => "1 hour and 30 minutes ago"
Philiprehberger::TimeAgo.format(Time.now + 5400, compound: true)
# => "in 1 hour and 30 minutes"
Philiprehberger::TimeAgo.format(Time.now - 7200, approximate: true)
# => "about 2 hours ago"
Philiprehberger::TimeAgo.format(Time.now + 300, approximate: true)
# => "in about 5 minutes"
Philiprehberger::TimeAgo.until(Time.now + 180) # => "in 3 minutes"
Philiprehberger::TimeAgo.until(Time.now + 7200) # => "in 2 hours"
Philiprehberger::TimeAgo.until(Time.now + 86_400) # => "tomorrow"
Philiprehberger::TimeAgo.in_words(5400) # => "1 hour and 30 minutes"
Philiprehberger::TimeAgo.in_words(300) # => "5 minutes"
Philiprehberger::TimeAgo.in_words(45) # => "45 seconds"
Philiprehberger::TimeAgo.auto(Time.now - 3600)
# => "1 hour ago"
Philiprehberger::TimeAgo.auto(Time.now - (5 * 86_400), threshold: 86_400)
# => "Mar 16, 2026"
Philiprehberger::TimeAgo.auto(Time.now - (5 * 86_400), threshold: 86_400, format: "%Y-%m-%d")
# => "2026-03-16"
Philiprehberger::TimeAgo.configure(just_now: 10)
Philiprehberger::TimeAgo.format(Time.now - 15) # => "15 seconds ago"
Philiprehberger::TimeAgo.format(Time.now - 5) # => "just now"
Philiprehberger::TimeAgo.reset_config! # restore defaults
Philiprehberger::TimeAgo.format_duration(5400)
# => "1 hour and 30 minutes"
Philiprehberger::TimeAgo.format_duration(5400, style: :short)
# => "1h 30m"
Philiprehberger::TimeAgo.format_duration(90_061, max_units: 3)
# => "1 day, 1 hour, and 1 minute"
Philiprehberger::TimeAgo.format_duration(5400, approximate: true)
# => "about 1 hour and 30 minutes"
Philiprehberger::TimeAgo.future?(deadline) # => true
Philiprehberger::TimeAgo.past?(deadline) # => false
t1 = Time.new(2026, 3, 21, 10, 0, 0)
t2 = Time.new(2026, 3, 22, 12, 30, 45)
Philiprehberger::TimeAgo.duration_between(t1, t2)
# => { days: 1, hours: 2, minutes: 30, seconds: 45 }
| Method | Description |
|---|---|
TimeAgo.format(time, **opts) | Format a timestamp as a relative time string |
TimeAgo.until(time, **opts) | Format a future time as a relative string (e.g., "in 3 minutes") |
TimeAgo.in_words(seconds) | Format raw seconds as duration words (e.g., "5 minutes") |
TimeAgo.auto(time, **opts) | Relative time if within threshold, otherwise absolute date |
TimeAgo.configure(**opts) | Set module-level configuration thresholds |
TimeAgo.config | Return the current configuration hash |
TimeAgo.reset_config! | Reset configuration to defaults |
TimeAgo.format_duration(seconds, **opts) | Format raw seconds as a human-readable duration string |
TimeAgo.duration_between(time1, time2) | Return structured hash of time components between two times |
.future?(time, relative_to: Time.now) | True when time is after relative_to |
.past?(time, relative_to: Time.now) | True when time is before relative_to |
Format Options:
| Option | Type | Default | Description |
|---|---|---|---|
style | Symbol | :long | :long for full words, :short for abbreviated |
relative_to | Time | Time.now | Reference time for comparison |
max_days | Integer, nil | nil | Fallback to absolute date after this many days |
precision | Symbol, nil | nil | Smallest unit to show (:hour, :minute, etc.) |
max_units | Integer, nil | nil | Maximum number of time components to show |
compound | Boolean | false | Show two units joined with "and" |
approximate | Boolean | false | Prefix output with "about" |
Format Duration Options:
| Option | Type | Default | Description |
|---|---|---|---|
style | Symbol | :long | :long for full words, :short for abbreviated |
precision | Symbol, nil | nil | Smallest unit to show (:hour, :minute, etc.) |
max_units | Integer | 2 | Maximum number of time components to show |
compound | Boolean | true | Join units with "and" |
approximate | Boolean | false | Prefix with "about" |
Auto Options:
| Option | Type | Default | Description |
|---|---|---|---|
threshold | Integer | 86400 | Seconds threshold for relative display |
format | String | '%b %d, %Y' | strftime format for absolute fallback |
relative_to | Time | Time.now | Reference time for comparison |
Configure Options:
| Option | Type | Default | Description |
|---|---|---|---|
just_now | Integer | 30 | Seconds threshold for "just now" |
bundle install
bundle exec rspec
bundle exec rubocop
If you find this project useful: