Overview
Ruby's Time class represents moments in time with nanosecond precision, combining date and time information with timezone awareness. The class handles temporal calculations, formatting, parsing, and conversion operations across different time zones and formats.
Time objects are immutable value objects that wrap Unix timestamps with additional timezone and subsecond precision data. Ruby maintains timezone information separately from the timestamp, enabling accurate conversions and comparisons across different temporal contexts.
# Current time with system timezone
now = Time.now
# => 2024-08-29 14:30:45.123456789 -0500
# UTC time creation
utc_time = Time.utc(2024, 8, 29, 19, 30, 45)
# => 2024-08-29 19:30:45 UTC
# Time with specific timezone offset
offset_time = Time.new(2024, 8, 29, 14, 30, 45, "-05:00")
# => 2024-08-29 14:30:45 -0500
The Time class supports creation from various inputs including Unix timestamps, formatted strings, and component values. Ruby handles leap seconds, daylight saving time transitions, and historical timezone changes through the underlying system libraries.
Time objects support arithmetic operations for duration calculations and comparison operations for temporal ordering. The class integrates with Ruby's broader temporal ecosystem including Date, DateTime, and various parsing libraries.
Basic Usage
Time creation accepts multiple forms of input data. The most common constructors handle component-based creation, current time retrieval, and parsing from external formats.
# Component-based creation (year, month, day, hour, minute, second, timezone)
specific_time = Time.new(2024, 12, 25, 9, 30, 0, "-08:00")
# => 2024-12-25 09:30:00 -0800
# UTC creation with subseconds
utc_detailed = Time.utc(2024, 12, 25, 17, 30, 15, 500000)
# => 2024-12-25 17:30:15.5 UTC
# Local timezone creation
local_time = Time.local(2024, 12, 25, 9, 30, 15)
# => 2024-12-25 09:30:15 -0800
# From Unix timestamp
from_timestamp = Time.at(1735134615)
# => 2024-12-25 17:30:15 UTC
String parsing handles common date and time formats through the parse
method, which accepts ISO 8601 strings, RFC 2822 formats, and various informal representations.
# ISO 8601 parsing
iso_time = Time.parse("2024-12-25T17:30:15Z")
# => 2024-12-25 17:30:15 UTC
# RFC 2822 format
rfc_time = Time.parse("Wed, 25 Dec 2024 17:30:15 GMT")
# => 2024-12-25 17:30:15 UTC
# Informal formats
casual_time = Time.parse("December 25, 2024 5:30 PM")
# => 2024-12-25 17:30:00 -0800
# With explicit timezone
timezone_time = Time.parse("2024-12-25 17:30:15 +0900")
# => 2024-12-25 17:30:15 +0900
Time formatting generates string representations for display and serialization purposes. The strftime
method provides format string control over output appearance.
time = Time.new(2024, 12, 25, 17, 30, 15)
# Standard formats
time.to_s
# => "2024-12-25 17:30:15 -0800"
time.to_i
# => 1735170615
time.to_f
# => 1735170615.0
# Custom formatting
time.strftime("%Y-%m-%d %H:%M:%S %z")
# => "2024-12-25 17:30:15 -0800"
time.strftime("%A, %B %d, %Y at %I:%M %p")
# => "Wednesday, December 25, 2024 at 05:30 PM"
Timezone conversion transforms Time objects between different timezone contexts while preserving the actual moment in time being represented.
original = Time.new(2024, 12, 25, 17, 30, 15, "-08:00")
# Convert to UTC
utc_version = original.utc
# => 2024-12-26 01:30:15 UTC
# Convert to local timezone
local_version = original.getlocal
# => 2024-12-25 19:30:15 -0600
# Convert to specific offset
eastern = original.getlocal("-05:00")
# => 2024-12-25 20:30:15 -0500
Error Handling & Debugging
Time creation and parsing operations raise specific exceptions when encountering invalid input data or impossible temporal values.
ArgumentError
occurs during creation when provided values fall outside valid ranges or when timezone specifications are malformed.
begin
# Invalid month
Time.new(2024, 13, 1)
rescue ArgumentError => e
puts e.message
# => "argument out of range"
end
begin
# Invalid day for month
Time.new(2024, 2, 30)
rescue ArgumentError => e
puts e.message
# => "argument out of range"
end
begin
# Malformed timezone offset
Time.new(2024, 8, 29, 12, 0, 0, "invalid")
rescue ArgumentError => e
puts e.message
# => "'+HH:MM', '-HH:MM', 'UTC' or 'A'..'I','K'..'Z' expected for utc_offset"
end
String parsing through Time.parse
raises ArgumentError
when input strings cannot be interpreted as valid temporal representations.
begin
Time.parse("not a date")
rescue ArgumentError => e
puts e.message
# => "no time information in \"not a date\""
end
begin
Time.parse("2024-13-45")
rescue ArgumentError => e
puts e.message
# => "argument out of range"
end
# Safe parsing with rescue block
def safe_time_parse(input)
Time.parse(input)
rescue ArgumentError
nil
end
safe_time_parse("2024-12-25")
# => 2024-12-25 00:00:00 -0800
safe_time_parse("invalid")
# => nil
Timezone-related issues often manifest as unexpected time shifts or incorrect local time calculations. Ruby's timezone handling depends on system timezone data and may produce inconsistent results across different environments.
# Debugging timezone issues
time = Time.new(2024, 3, 10, 2, 30, 0, "-05:00") # During DST transition
puts "Original: #{time}"
puts "UTC: #{time.utc}"
puts "Local: #{time.getlocal}"
puts "Zone: #{time.zone}"
puts "UTC Offset: #{time.utc_offset}"
# Check if time exists during DST transition
# Some local times may not exist or be ambiguous
begin
dst_transition = Time.local(2024, 3, 10, 2, 30, 0)
puts "DST transition time exists: #{dst_transition}"
rescue ArgumentError => e
puts "DST transition error: #{e.message}"
end
Arithmetic operations on Time objects can produce unexpected results when crossing timezone boundaries or during daylight saving time transitions.
# Debugging arithmetic across DST transitions
before_dst = Time.local(2024, 3, 10, 1, 0, 0) # 1 AM before DST
after_addition = before_dst + 3600 # Add one hour
puts "Before DST: #{before_dst}"
puts "Plus 1 hour: #{after_addition}"
puts "Expected 2 AM, got: #{after_addition.hour}"
# Duration calculations may skip or repeat hours
duration = after_addition - before_dst
puts "Duration in seconds: #{duration}"
puts "Duration in hours: #{duration / 3600}"
# Checking for DST transitions
def dst_transition_debug(time)
before = time - 1
after = time + 1
puts "Before: #{before} (offset: #{before.utc_offset})"
puts "During: #{time} (offset: #{time.utc_offset})"
puts "After: #{after} (offset: #{after.utc_offset})"
if before.utc_offset != after.utc_offset
puts "DST transition detected"
end
end
Production Patterns
Production applications require consistent time handling across different environments, accurate logging timestamps, and reliable scheduling mechanisms.
Database integration patterns handle timezone storage and retrieval consistently. Most applications store timestamps in UTC and convert to local timezone for display purposes.
# Database timestamp patterns
class EventLogger
def self.log_event(message, occurred_at = Time.now.utc)
# Store UTC timestamps in database
Database.insert(
message: message,
occurred_at: occurred_at.strftime("%Y-%m-%d %H:%M:%S UTC"),
unix_timestamp: occurred_at.to_i
)
end
def self.recent_events(limit = 10)
# Retrieve and convert to local timezone for display
Database.select_recent(limit).map do |row|
utc_time = Time.parse(row[:occurred_at] + " UTC")
{
message: row[:message],
display_time: utc_time.getlocal.strftime("%Y-%m-%d %I:%M %p %Z"),
utc_time: utc_time
}
end
end
end
# Usage in production
EventLogger.log_event("User login", Time.now.utc)
EventLogger.log_event("Data backup completed")
recent = EventLogger.recent_events
puts recent.first[:display_time]
# => "2024-08-29 02:30 PM CDT"
API serialization requires consistent timestamp formats across services and client applications. ISO 8601 format provides standardized representation with timezone information.
# API serialization patterns
module TimeSerializer
def self.serialize(time_obj)
return nil if time_obj.nil?
{
iso8601: time_obj.iso8601(3), # Include milliseconds
unix: time_obj.to_i,
display: time_obj.strftime("%B %d, %Y at %I:%M %p %Z")
}
end
def self.deserialize(time_data)
return nil if time_data.nil? || time_data[:iso8601].nil?
Time.parse(time_data[:iso8601])
rescue ArgumentError
# Fallback to unix timestamp
time_data[:unix] ? Time.at(time_data[:unix]) : nil
end
end
# Production API endpoint
def api_response(events)
{
events: events.map do |event|
{
id: event.id,
message: event.message,
timestamp: TimeSerializer.serialize(event.created_at)
}
end,
generated_at: TimeSerializer.serialize(Time.now.utc)
}
end
Caching strategies often incorporate time-based expiration and timestamp validation for cache invalidation decisions.
# Time-based caching patterns
class TimestampCache
def initialize
@cache = {}
@timestamps = {}
end
def set(key, value, ttl_seconds = 3600)
expiry = Time.now + ttl_seconds
@cache[key] = value
@timestamps[key] = expiry
end
def get(key)
return nil unless @cache.key?(key)
if Time.now > @timestamps[key]
# Expired
@cache.delete(key)
@timestamps.delete(key)
return nil
end
@cache[key]
end
def cleanup_expired
expired_keys = @timestamps.select { |key, expiry| Time.now > expiry }.keys
expired_keys.each do |key|
@cache.delete(key)
@timestamps.delete(key)
end
expired_keys.length
end
end
# Background job for cache maintenance
cache = TimestampCache.new
# Production usage
cache.set("user:123:profile", user_profile_data, 1800) # 30 minutes
profile = cache.get("user:123:profile")
# Scheduled cleanup
cleanup_count = cache.cleanup_expired
puts "Cleaned up #{cleanup_count} expired entries"
Monitoring and alerting systems use Time objects for threshold checking, interval calculations, and metric collection timing.
# Production monitoring patterns
class SystemMonitor
def initialize
@metrics = {}
@alerts = []
end
def record_metric(name, value, timestamp = Time.now.utc)
@metrics[name] ||= []
@metrics[name] << { value: value, timestamp: timestamp }
# Keep only recent metrics
cutoff = timestamp - 3600 # 1 hour
@metrics[name].reject! { |metric| metric[:timestamp] < cutoff }
check_alerts(name, value, timestamp)
end
def average_over_period(name, seconds_ago)
return nil unless @metrics[name]
cutoff = Time.now.utc - seconds_ago
recent_metrics = @metrics[name].select { |m| m[:timestamp] >= cutoff }
return nil if recent_metrics.empty?
total = recent_metrics.sum { |m| m[:value] }
total.to_f / recent_metrics.length
end
private
def check_alerts(name, value, timestamp)
if name == "response_time" && value > 500 # 500ms threshold
@alerts << {
type: "response_time_high",
value: value,
threshold: 500,
timestamp: timestamp,
message: "Response time #{value}ms exceeds 500ms threshold"
}
end
end
end
# Production monitoring usage
monitor = SystemMonitor.new
# Record metrics
monitor.record_metric("response_time", 245)
monitor.record_metric("cpu_usage", 78.5)
monitor.record_metric("memory_usage", 82.1)
# Calculate averages
avg_response = monitor.average_over_period("response_time", 300) # Last 5 minutes
puts "Average response time: #{avg_response}ms"
Reference
Time Creation Methods
Method | Parameters | Returns | Description |
---|---|---|---|
Time.now |
None | Time |
Current time with system timezone |
Time.new(year, month=1, day=1, hour=0, min=0, sec=0, zone=nil) |
Component values, optional timezone | Time |
Create time from components |
Time.utc(year, month=1, day=1, hour=0, min=0, sec=0, usec=0) |
Component values in UTC | Time |
Create UTC time from components |
Time.local(year, month=1, day=1, hour=0, min=0, sec=0, usec=0) |
Component values in local timezone | Time |
Create local time from components |
Time.at(seconds, microseconds=0) |
Unix timestamp, optional microseconds | Time |
Create time from Unix timestamp |
Time.parse(string) |
String representation | Time |
Parse time from string |
Time Conversion Methods
Method | Parameters | Returns | Description |
---|---|---|---|
#utc |
None | Time |
Convert to UTC timezone |
#getlocal(zone=nil) |
Optional timezone | Time |
Convert to local or specified timezone |
#to_i |
None | Integer |
Unix timestamp in seconds |
#to_f |
None | Float |
Unix timestamp with fractional seconds |
#to_s |
None | String |
String representation |
#iso8601(fraction_digits=0) |
Optional subsecond digits | String |
ISO 8601 formatted string |
#strftime(format) |
Format string | String |
Custom formatted string |
Time Component Access
Method | Parameters | Returns | Description |
---|---|---|---|
#year |
None | Integer |
Year component |
#month |
None | Integer |
Month component (1-12) |
#day |
None | Integer |
Day of month (1-31) |
#hour |
None | Integer |
Hour component (0-23) |
#min |
None | Integer |
Minute component (0-59) |
#sec |
None | Integer |
Second component (0-60) |
#usec |
None | Integer |
Microsecond component |
#nsec |
None | Integer |
Nanosecond component |
#wday |
None | Integer |
Day of week (0=Sunday) |
#yday |
None | Integer |
Day of year (1-366) |
Timezone Methods
Method | Parameters | Returns | Description |
---|---|---|---|
#zone |
None | String |
Timezone abbreviation |
#utc_offset |
None | Integer |
Offset from UTC in seconds |
#dst? |
None | Boolean |
True if daylight saving time |
#utc? |
None | Boolean |
True if UTC timezone |
Time Arithmetic
Operation | Parameters | Returns | Description |
---|---|---|---|
time + numeric |
Duration in seconds | Time |
Add duration to time |
time - numeric |
Duration in seconds | Time |
Subtract duration from time |
time1 - time2 |
Another Time object | Float |
Difference in seconds |
Comparison Methods
Method | Parameters | Returns | Description |
---|---|---|---|
#<=> |
Another Time | Integer |
Comparison result (-1, 0, 1) |
#== |
Another Time | Boolean |
Equality check |
#< , #> , #<= , #>= |
Another Time | Boolean |
Ordering comparisons |
#between? |
Two Time objects | Boolean |
Check if time falls between range |
Common Format Strings
Format | Output | Description |
---|---|---|
%Y-%m-%d |
2024-12-25 | ISO date format |
%Y-%m-%d %H:%M:%S |
2024-12-25 17:30:15 | ISO datetime format |
%A, %B %d, %Y |
Wednesday, December 25, 2024 | Full date format |
%I:%M %p |
05:30 PM | 12-hour time format |
%H:%M:%S %z |
17:30:15 -0800 | 24-hour with timezone |
%s |
1735170615 | Unix timestamp |
Common Exceptions
Exception | Cause | Solution |
---|---|---|
ArgumentError |
Invalid date components | Validate input ranges |
ArgumentError |
Unparseable string | Use rescue blocks for parsing |
ArgumentError |
Invalid timezone format | Use standard timezone formats |
TypeError |
Wrong argument type | Convert arguments to expected types |