struct

Time

Inherits Steppable < Comparable < Struct < Value < Object

Time represents a date-time instant in incremental time observed in a specific time zone.

The calendaric calculations are based on the rules of the proleptic Gregorian calendar as specified in ISO 8601. Leap seconds are ignored.

Internally, the time is stored as an Int64 representing seconds from epoch (0001-01-01 00:00:00Z) and an Int32 representing nanosecond-of-second with value range 0..999_999_999.

The supported date range is 0001-01-01 00:00:00.0 to 9999-12-31 23:59:59.999_999_999 in any local time zone.

Telling the Time

There are several methods to retrieve a Time instance representing the current time:

Time.utc                                        # returns the current time in UTC
Time.local Time::Location.load("Europe/Berlin") # returns the current time in time zone Europe/Berlin
Time.local                                      # returns the current time in current time zone

It is generally recommended to keep instances in UTC and only apply a local time zone when formatting for user display, unless the domain logic requires having a specific time zone (for example for calendaric operations).

Creating a Specific Instant

Time instances representing a specific instant can be created by .utc or .new with the date-time specified as individual arguments:

time = Time.utc(2016, 2, 15, 10, 20, 30)
time.to_s # => "2016-02-15 10:20:30 UTC"
time = Time.local(2016, 2, 15, 10, 20, 30, location: Time::Location.load("Europe/Berlin"))
time.to_s # => "2016-02-15 10:20:30 +01:00"
# The time-of-day can be omitted and defaults to midnight (start of day):
time = Time.utc(2016, 2, 15)
time.to_s # => "2016-02-15 00:00:00 UTC"

Retrieving Time Information

Each Time instance allows querying calendar data:

time = Time.utc(2016, 2, 15, 10, 20, 30)
time.year        # => 2016
time.month       # => 2
time.day         # => 15
time.hour        # => 10
time.minute      # => 20
time.second      # => 30
time.millisecond # => 0
time.nanosecond  # => 0
time.day_of_week # => Time::DayOfWeek::Monday
time.day_of_year # => 46
time.monday?     # => true
time.time_of_day # => 10:20:30

For querying if a time is at a specific day of week, Time offers named predicate methods, delegating to #day_of_week:

time.monday? # => true
# ...
time.sunday? # => false

Time Zones

Each time is attached to a specific time zone, represented by a Location (see #location). #zone returns the time zone observed in this location at the current time (i.e. the instant represented by this Time). #offset returns the offset of the current zone in seconds.

time = Time.local(2018, 3, 8, 22, 5, 13, location: Time::Location.load("Europe/Berlin"))
time          # => 2018-03-08 22:05:13+01:00[Europe/Berlin]
time.location # => #<Time::Location Europe/Berlin>
time.zone     # => #<Time::Location::Zone CET +01:00 (3600s) STD>
time.offset   # => 3600

Using .utc, the location is Time::Location::UTC:

time = Time.utc(2018, 3, 8, 22, 5, 13)
time          # => 2018-03-08 22:05:13Z
time.location # => #<Time::Location UTC>
time.zone     # => #<Time::Location::Zone UTC +00:00 (0s) STD>
time.offset   # => 0

A Time instance can be transformed to a different time zone while retaining the same instant using #in:

time_de = Time.local(2018, 3, 8, 22, 5, 13, location: Time::Location.load("Europe/Berlin"))
time_ar = time_de.in Time::Location.load("America/Argentina/Buenos_Aires")
time_de # => 2018-03-08 22:05:13+01:00[Europe/Berlin]
time_ar # => 2018-03-08 18:05:13-03:00[America/Argentina/Buenos_Aires]

Both Time instances show a different local date-time, but they represent the same date-time in the instant time-line, therefore they are considered equal:

time_de.to_utc     # => 2018-03-08 21:05:13Z
time_ar.to_utc     # => 2018-03-08 21:05:13Z
time_de == time_ar # => true

There are also two special methods for converting to UTC and local time zone:

time.to_utc   # equals time.in(Location::UTC)
time.to_local # equals time.in(Location.local)

#to_local_in allows changing the time zone while keeping the same local date-time (wall clock) which results in a different instant on the time line.

Formatting and Parsing Time

To make date-time instances exchangeable between different computer systems or readable to humans, they are usually converted to and from a string representation.

The method #to_s formats the date-time according to a specified pattern.

time = Time.utc(2015, 10, 12, 10, 30, 0)
time.to_s("%Y-%m-%d %H:%M:%S %:z") # => "2015-10-12 10:30:00 +00:00"

Similarly, Time.parse and Time.parse! are used to construct a Time instance from date-time information in a string, according to a specified pattern:

Time.parse("2015-10-12 10:30:00 +00:00", "%Y-%m-%d %H:%M:%S %z", Time::Location::UTC)
Time.parse!("2015-10-12 10:30:00 +00:00", "%Y-%m-%d %H:%M:%S %z")

See Time::Format for all directives.

Calculations

Time.utc(2015, 10, 10) - 5.days # => 2015-10-05 00:00:00 +00:00

span = Time.utc(2015, 10, 10) - Time.utc(2015, 9, 10)
span.days          # => 30
span.total_hours   # => 720
span.total_minutes # => 43200

Measuring Time

The typical time representation provided by the operating system is based on a "wall clock" which is subject to changes for clock synchronization. This can result in discontinuous jumps in the time-line making it not suitable for accurately measuring elapsed time.

Instances of Time are focused on telling time – using a "wall clock". When Time.local is called multiple times, the difference between the returned instances is not guaranteed to equal to the time elapsed between making the calls; even the order of the returned Time instances might not reflect invocation order.

t1 = Time.utc
# operation that takes 1 minute
t2 = Time.utc
t2 - t1 # => ?

The resulting Time::Span could be anything, even negative, if the computer's wall clock has changed between both calls.

As an alternative, the operating system also provides a monotonic clock. Its time-line has no specified starting point but is strictly linearly increasing.

This monotonic clock should always be used for measuring elapsed time.

A reading from this clock can be taken using .instant:

t1 = Time.instant
# operation that takes 1 minute
t2 = Time.instant
t2.duration_since(t1) # => 1.minute (approximately)

The execution time of a block can be measured using .measure:

elapsed_time = Time.measure do
  # operation that takes 20 milliseconds
end
elapsed_time # => 20.milliseconds (approximately)

Constants

ABBR_DAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"] of ::String
ABBR_MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] of ::String
DAY_NAMES = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"] of ::String
MONTH_NAMES = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"] of ::String

Class methods

current

Time.zone ? Time.zone.now : Time.local (Rails Time.current).

Source
use_zone(zone : String | ActiveSupport::TimeZone, &)

Time.use_zone("Europe/Berlin") { ... } — set the zone for the current fiber only, restore the previous zone afterwards (even on raise).

Source
zone
Source
zone=(zone : Nil) : Nil
Source

Instance methods

advance(years : Int = 0, months : Int = 0, weeks : Int = 0, days : Int = 0, hours : Int = 0, minutes : Int = 0, seconds : Int = 0) : Time

advance(years:, months:, weeks:, days:, hours:, minutes:, seconds:)

Source
ago(seconds : Int | Float) : Time
Source
all_day
Source
all_month
Source
all_quarter
Source
all_week(week_start = :monday) : Range(Time, Time)
Source
all_year
Source
beginning_of_day
Source
beginning_of_month
Source
beginning_of_quarter
Source
beginning_of_week(week_start = :monday) : Time
Source
beginning_of_year
Source
change(year = nil, month = nil, day = nil, hour = nil, min = nil, sec = nil, usec = nil, nsec = nil) : Time
Source
end_of_day
Source
end_of_month
Source
end_of_quarter
Source
end_of_week(week_start = :monday) : Time
Source
end_of_year
Source
friday?

Returns true if the day of week is Friday.

See #day_of_week for details.

Source
future?
Source
in(seconds : Int | Float) : Time
Source
mid_day
Source
middle_of_day
Source
monday?

Returns true if the day of week is Monday.

See #day_of_week for details.

Source
months_ago(months : Int) : Time
Source
months_since(months : Int) : Time
Source
next_day
Source
next_month
Source
next_occurring(day_of_week) : Time
Source
next_week(given_day = :monday) : Time
Source
next_year
Source
noon
Source
past?
Source
prev_day
Source
prev_month
Source
prev_occurring(day_of_week) : Time
Source
prev_week(given_day = :monday) : Time
Source
prev_year
Source
saturday?

Returns true if the day of week is Saturday.

See #day_of_week for details.

Source
shift_calendar_months(months : Int, days : Int = 0) : Time

Calendar-aware month shift with day clamping (2026-03-31 - 1 month → 2026-02-28), preserves time-of-day and location.

Source
since(seconds : Int | Float) : Time
Source
sunday?

Returns true if the day of week is Sunday.

See #day_of_week for details.

Source
thursday?

Returns true if the day of week is Thursday.

See #day_of_week for details.

Source
to_formatted_s(format : Symbol = :default) : String
Source
to_fs(format : Symbol = :default) : String

to_fs: :db/:short/:long/:iso8601/:number or strftime-ish String.

Source
to_fs(format : String) : String
Source
today?
Source
tomorrow
Source
tuesday?

Returns true if the day of week is Tuesday.

See #day_of_week for details.

Source
wday

Ruby-style wday: 0 = Sunday ... 6 = Saturday.

Source
wednesday?

Returns true if the day of week is Wednesday.

See #day_of_week for details.

Source
years_ago(years : Int) : Time
Source
years_since(years : Int) : Time
Source
yesterday
Source

Nested types