Time Utilities Guide

Familia provides a comprehensive time utilities refinement that adds convenient time conversion and age calculation methods to Ruby's Numeric and String classes.

Overview

The Familia::Refinements::TimeLiterals module extends Ruby's built-in classes with intuitive time manipulation methods:

using Familia::Refinements::TimeLiterals

2.hours        #=> 7200.0 (seconds)
"30m".in_seconds  #=> 1800.0
timestamp.days_old  #=> 5.2

Time Constants

Familia uses Gregorian year calculations for consistent time conversions:

PER_YEAR  = 31_556_952.0  # 365.2425 days (accounts for leap years)
PER_MONTH = 2_629_746.0   # PER_YEAR / 12 (30.437 days)
PER_WEEK  = 604_800.0     # 7 days
PER_DAY   = 86_400.0      # 24 hours

Why Gregorian Year?

The key design decision ensures mathematical consistency:

12.months == 1.year  #=> true (both equal 31,556,952 seconds)

This prevents subtle bugs where 12.months and 1.year would differ by ~5.8 hours.

Basic Time Conversions

Converting Numbers to Time Units

using Familia::Refinements::TimeLiterals

# Singular and plural forms work identically
1.second   #=> 1
30.seconds #=> 30
5.minutes  #=> 300.0
2.hours    #=> 7200.0
3.days     #=> 259200.0
1.week     #=> 604800.0
2.months   #=> 5259492.0
1.year     #=> 31556952.0

Converting Time Units Back

7200.in_hours     #=> 2.0
259200.in_days    #=> 3.0
5259492.in_months #=> 2.0
31556952.in_years #=> 1.0

String Time Parsing

Parse human-readable time strings:

"30s".in_seconds    #=> 30.0
"5m".in_seconds     #=> 300.0
"2h".in_seconds     #=> 7200.0
"1d".in_seconds     #=> 86400.0
"1w".in_seconds     #=> 604800.0
"2mo".in_seconds    #=> 5259492.0
"1y".in_seconds     #=> 31556952.0

Supported String Formats

Unit Abbreviations Example
Microseconds us, μs, microsecond, microseconds "500μs"
Milliseconds ms, millisecond, milliseconds "250ms"
Seconds (no unit) "30"
Minutes m, minute, minutes "15m"
Hours h, hour, hours "2h"
Days d, day, days "7d"
Weeks w, week, weeks "2w"
Months mo, month, months "6mo"
Years y, year, years "1y"

Note: Use "mo" for months to avoid confusion with "m" (minutes). Seconds don't require a unit suffix.

Age Calculations

Calculate how old timestamps are:

# Basic age calculation
old_timestamp = 2.days.ago.to_i
old_timestamp.age_in(:days)    #=> ~2.0
old_timestamp.age_in(:hours)   #=> ~48.0
old_timestamp.age_in(:months)  #=> ~0.066

# Convenience methods
old_timestamp.days_old     #=> ~2.0
old_timestamp.hours_old    #=> ~48.0
old_timestamp.minutes_old  #=> ~2880.0
old_timestamp.weeks_old    #=> ~0.28
old_timestamp.months_old   #=> ~0.066
old_timestamp.years_old    #=> ~0.005

# Calculate age from specific reference time
past_timestamp = 1.week.ago.to_i
reference_time = 3.days.ago
past_timestamp.age_in(:days, reference_time)  #=> ~4.0

Time Comparisons

timestamp = 2.hours.ago.to_i

# Check if older than duration
timestamp.older_than?(1.hour)   #=> true
timestamp.older_than?(3.hours)  #=> false

# Check if newer than duration (future)
future_timestamp = 1.hour.from_now.to_i
future_timestamp.newer_than?(30.minutes)  #=> true

# Check if within duration of now (past or future)
timestamp.within?(3.hours)      #=> true
timestamp.within?(1.hour)       #=> false

Practical Examples

Cache Expiration

using Familia::Refinements::TimeLiterals

class CacheEntry < Familia::Horreum
  field :data
  field :created_at

  def expired?
    created_at.to_i.older_than?(1.hour)
  end

  def cache_age
    created_at.to_i.minutes_old
  end
end

Session Management

class UserSession < Familia::Horreum
  field :last_active

  def stale?
    last_active.to_i.older_than?(30.minutes)
  end

  def session_duration
    "Active for #{last_active.to_i.hours_old.round(1)} hours"
  end
end

Data Retention

def cleanup_old_logs
  Log.all.select do |log|
    log.timestamp.to_i.older_than?(30.days)
  end.each(&:destroy)
end

Important Notes

Calendar vs. Precise Time

Familia's time utilities use average durations suitable for:

  • Age calculations
  • Cache expiration
  • Time-based cleanup
  • General time arithmetic

For calendar-aware operations (exact months, leap years), use Ruby's Date/Time classes:

# For average durations (Familia)
user_age = .to_i.months_old  #=> 6.2

# For exact calendar operations (Ruby stdlib)
exact_months = Date.today.months_since()  #=> 6

Thread Safety

All time utility methods are thread-safe and work with frozen objects.

Migration from v1.x

If upgrading from earlier versions:

# Old behavior (inconsistent)
12.months != 1.year  # Different values

# New behavior (consistent)
12.months == 1.year  # Same value: 31,556,952.0 seconds

Update any code that relied on the old 365-day year constant to expect the new Gregorian year values.