rubyguides

Time

The Time class provides methods for working with dates and times in Ruby. It’s part of the standard library and offers powerful date/time manipulation capabilities.

Overview

Ruby’s Time class represents dates and times:

  • Create: Generate times from strings, timestamps, or components
  • Parse: Convert strings into Time objects
  • Manipulate: Add, subtract, and modify time values
  • Format: Display times in various formats

Creating time objects

Current Time

Time.now        # => 2026-03-13 14:52:00 +0000
Time.current    # => Same as now, but respects Rails timezone

Time.now gives you the current system time, while Time.current is an ActiveSupport addition that respects the configured Rails time zone. Both return a Time object, but Time.current is what you should reach for inside a Rails application where time-zone awareness matters.

From string

The Time.parse method converts a string representation into a Time object. It handles several common date formats automatically, including ISO 8601 and natural-looking timestamps. When you pass only a date without a time component, the parser defaults to midnight UTC. Adding an explicit time portion gives you a full timestamp you can use for scheduling, logging, or comparison with other Time values.

Time.parse("2026-03-13")
# => 2026-03-13 00:00:00 +0000

Time.parse("2026-03-13 10:30:00")
# => 2026-03-13 10:30:00 +0000

The Time.parse examples above demonstrate parsing from string representations, but Ruby also lets you construct a Time from individual numeric components. This approach is useful when you already have separate year, month, and day values — for example, when processing user input from a form or reading structured data from a file. The constructor accepts up to seven arguments: year, month, day, hour, minute, second, and optional UTC offset.

From components

Time.new(2026, 3, 13, 14, 30, 0)
# => 2026-03-13 14:30:00 +0000

From timestamp

Time.at converts a Unix timestamp (seconds since the epoch) into a Time object. This is especially useful when you receive timestamps from APIs, databases, or log files that store time as an integer. Passing the :millisecond symbol as the second argument tells Ruby to interpret the number as milliseconds, which several modern systems use for sub-second precision. Without that argument, the value is treated as seconds.

Time.at(1700000000)
# => 2023-11-14 22:13:20 +0000

Time.at(1700000000, :millisecond)
# => 2023-11-14 22:13:20.000 +0000

Once you have a Time object, you rarely need the raw timestamp again — instead you pull out specific fields. Ruby provides accessor methods for every component you might need, from the year and month down to the second and time zone.

Time components

You can extract individual components from a Time object:

time = Time.now

time.year      # => 2026
time.month     # => 3
time.day       # => 13
time.hour      # => 14
time.min       # => 52
time.sec       # => 0
time.wday      # => 5 (0 = Sunday)
time.yday      # => 72 (day of year)
time.zone      # => "UTC"

The component accessors shown above are read-only — they describe the moment but don’t modify it. Once you’ve extracted the fields you need, arithmetic and comparison methods let you derive new times from the existing one.

Time calculations

Adding and subtracting

Arithmetic on Time objects uses seconds as the unit. Adding 3600 advances the time by exactly one hour, and subtracting 86400 moves it back by exactly one day. The result is always a new Time object — Ruby never mutates the original. This second-based arithmetic is straightforward but remember that “one day” in calendar terms sometimes means 23 or 25 hours due to daylight saving shifts, so using exact second counts carries that assumption with it.

time = Time.now

time + 3600        # One hour from now
time - 86400       # One day ago

Arithmetic on Time objects returns a new Time rather than mutating the original. The result preserves the same time zone as the source, so adding or subtracting seconds is safe across zone boundaries as long as both operands are in the same zone.

Comparing times

past = Time.parse("2020-01-01")
future = Time.parse("2030-01-01")

future > past      # => true
past <=> future    # => -1 (past is earlier)

Ruby’s Time objects implement Comparable, which means you can use them directly with <, >, <=, >=, and between?. Sorting a collection of timestamps is as simple as calling sort on the array — no custom block is required.

Formatting times

strftime

The strftime method formats time using directives:

time = Time.now

time.strftime("%Y-%m-%d")           # => "2026-03-13"
time.strftime("%H:%M:%S")           # => "14:52:00"
time.strftime("%B %d, %Y")          # => "March 13, 2026"
time.strftime("%d/%m/%Y")           # => "13/03/2026"
time.strftime("%A")                 # => "Friday"

Each %-prefixed code in the format string maps to a specific component of the timestamp. You can combine them with literal characters — hyphens, slashes, colons, and spaces — to produce any layout your application needs. The full set of directives is larger than the table below, but these eight cover the vast majority of real-world formatting tasks.

DirectiveMeaningExample
%YYear2026
%mMonth (01-12)03
%dDay13
%HHour (24h)14
%MMinute52
%SSecond00
%AWeekdayFriday
%BMonth nameMarch

The directives are case-sensitive: %m produces the month number while %M produces minutes. You can combine multiple directives with literal punctuation and spaces to build any date layout your application needs. For locale-specific month or weekday names, Ruby supports the %^ modifier on some platforms, though strftime behavior varies slightly across operating systems.

Predefined formats

time.iso8601     # => "2026-03-13T14:52:00Z"
time.rfc2822     # => "Fri, 13 Mar 2026 14:52:00 +0000"
time.xmlschema   # => "2026-03-13T14:52:00Z"

These convenience methods produce well-known string formats suitable for APIs, email headers, and database interchange. iso8601 and xmlschema are aliases for each other, while rfc2822 follows the older email-standard format that includes the weekday name and an abbreviated month.

Time zones

Working with time zones

require "time"

# Parse with timezone
Time.parse("2026-03-13 10:00:00 EST")
# => 2026-03-13 10:00:00 -0500

# Get time in specific zone
Time.use_zone("Pacific Time (US & Canada)") do
  Time.zone.now
end

Time.use_zone is an ActiveSupport method that temporarily overrides the application’s time zone for the duration of the block. The block’s return value is unaffected — only time-sensitive calls inside the block see the zone change. This pattern is useful for generating reports or emails in a user’s local time without permanently switching the global configuration.

Converting between zones

time = Time.now.utc

time.getlocal("-0800")    # Convert to PST
time.utc                  # Convert to UTC

getlocal and utc both mutate the receiver in place, so if you need to preserve the original timestamp, call dup first. A common pattern is to store everything in UTC and apply the local offset only at the presentation layer — this eliminates an entire class of time-zone bugs that arise from mixing offsets in the same code path.

Practical examples

Age calculation

birthday = Time.parse("1990-06-15")
age = ((Time.now - birthday) / 365.25 / 24 / 60 / 60).to_i
# => 35

The age calculation above divides the elapsed seconds by the number of seconds in a year (using 365.25 to account for leap years). This gives a rough integer age; for exact birthday logic, you would compare month and day fields rather than relying on arithmetic alone.

Day boundaries

time = Time.now

time.beginning_of_day    # => 2026-03-13 00:00:00 +0000
time.end_of_day         # => 2026-03-13 23:59:59 +0000

beginning_of_day and end_of_day are ActiveSupport methods that clamp a Time to the start or end of its calendar day. They are useful for constructing inclusive date-range queries, such as finding all records created “today” by filtering with created_at: time.beginning_of_day..time.end_of_day.

Date arithmetic

require "date"

date = Date.today
next_week = date + 7
last_month = date << 1

The examples above mix Date and Time objects, which is common in real Ruby code. Date handles calendar math natively — shifting months with << and stepping weeks with + — while Time captures the precise instant. Choosing the right type for each task keeps the code clearer and prevents subtle bugs around daylight saving transitions.

These date-arithmetic examples show how the Time and Date libraries often meet in real code. One handles a precise moment, while the other keeps the calendar math simple. Keeping them separate until the last possible moment helps you avoid mixing clock time with whole-day logic by accident.

Working with time safely

Time values are more precise than dates, so they are useful for logs, scheduling, and elapsed-time measurements. When you need a true moment in time, Time is usually the right tool because it can track hours, minutes, seconds, and zone offsets together. That makes it a better fit for events that happen at a specific instant.

For duration calculations, be clear about whether you want calendar time or elapsed seconds. The distinction matters when daylight saving changes, time zones shift, or a timestamp crosses midnight. Keeping those rules explicit in the code prevents surprising results later.

It also helps to decide early whether you want an exact timestamp or a day on the calendar. A timestamp is better for logging and scheduling, while a date is easier for human-facing planning and reports. Choosing the right layer first keeps the rest of the code much easier to reason about.

See Also

  • File — for file timestamp operations
  • ERB — templating that often uses time formatting
  • JSON — serialization often paired with time data