std/time
import std/time to use it. Source: src/std/time.iyi at v0.18.0.
A language with no clock cannot log, measure, or expire anything. This module ports Crystal's core time types while adhering to the dependency floor (SPEC.md III.9, III.10):
* On Darwin, libSystem's `clock_gettime_nsec_np` provides wall clock and
monotonic readings with zero new symbols beyond what concurrency already links.
* On Linux, raw `clock_gettime` syscalls (228 on x86_64, 113 on aarch64)
issue direct kernel requests with zero libc symbols.
* On Windows, kernel32's `GetSystemTimePreciseAsFileTime` and `QueryPerformanceCounter`
provide nanosecond-scale wall and monotonic time.
* On wasm32-wasi, WASI import `__wasi_clock_time_get` provides both clocks.A Time is an instant and the Location it is read in, UTC unless it was made in another: Time.utc and Time.unix are UTC, Time.local the machine's zone, time.in(location) the same instant elsewhere. Its date, clock, to_s, to_rfc3339 and %Z are read in its location; ==, <=> and hash are the instant's. A Location is read from the system's own IANA database (TZif files, RFC 8536), so no zone data ships in the binary; see Location for where it looks. An offset passed by hand (to_s(format, offset), Format.new(pattern, offset)) still writes and reads at that offset, as before locations existed.
What was left out from Crystal's 1,626-line src/time.cr:
* `Time::MonthSpan`: `shift(years:, months:)` is the month arithmetic, with
Crystal's rule for a day the month does not have.
* Localised names: month and day names are English, as Crystal's are.
* A zone read from a `%Z` name: an abbreviation names no single zone,
so `%Z` reads UTC and offsets, and a text in a known zone is parsed
at its `Location`.
* Windows' own zone names: the database is not there to read, so a
Windows machine's local zone is its rule from the system, its zones
named by their offsets (`+03`), and `Location.load` reads `$ZONEINFO`.DayOfWeekstruct
pub struct DayOfWeek
def ==(other : DayOfWeek) : Bool
def ===(other : DayOfWeek) : Bool
def friday? : Bool
# Hashing follows `==`. Without it a day hashed to its type, `Object#hash`'s
# default: `Monday.hash == Sunday.hash`, and every day a table held went to
# one slot.
def hash : Int32
def initialize(value : Int32)
def monday? : Bool
def saturday? : Bool
def sunday? : Bool
def thursday? : Bool
def to_s : String
def tuesday? : Bool
def value : Int32
def wednesday? : Bool
endLocationclass
A time zone: the offsets from UTC a place has kept, and when it changed from one to the next. Location.load("Europe/Istanbul") reads the IANA database the machine has; Location.local is the machine's own zone; Location.fixed(19800) is an offset and nothing else; Location::UTC is UTC. A Time carries one, and its date and clock are read in it.
The database is the system's TZif files, RFC 8536: $ZONEINFO when it names a directory, then /usr/share/zoneinfo, /usr/share/lib/zoneinfo and /etc/zoneinfo. Versions 1 to 4 are read. A time before the first change has the file's first offset, and one after the last has its POSIX footer's rule, so a slim file answers for the year 2100 as a fat one does. Leap seconds are not counted, as no clock this module reads counts them.
Location.local is the TZ variable when it is set — the empty string is UTC, a leading : is dropped, an absolute path is a TZif file, a name is looked up in the database, and anything else is read as a POSIX TZ string such as EST5EDT,M3.2.0,M11.1.0 — and /etc/localtime when it is not. Windows has no database to read: there local is the system's zone, its rule from GetTimeZoneInformation, its zones named by their offsets, and load reads $ZONEINFO alone. Where nothing answers, local is UTC. The local zone is read once, the first time it is asked for.
A location is built once and never changed, so a Time that holds one can cross to another task.
pub class Location
lib LibKernel32
end
# One offset a location keeps: its abbreviation (`CEST`, `+03`), its
# seconds east of UTC, and whether it is summer time.
struct Zone
def ==(other : Zone) : Bool
def dst? : Bool
def initialize(name : String, offset : Int32, dst : Bool)
def name : String
def offset : Int32
def to_s : String
end
def ==(other : Location) : Bool
def self.fixed(name : String, offset : Int32) : Location
# An offset of *offset* seconds east of UTC and nothing else, named as
# the offset is written: `+05:30`. Less than a day either way.
def self.fixed(offset : Int32) : Location
# One offset, never changing.
def fixed? : Bool
def hash : Int32
def initialize(name : String, zones : Array(Zone), transitions : Array(Int64), transition_zones : Array(Int32), rule : TzRule | ::Nil)
def inspect : String
# The zone the IANA database names *name*: `Europe/Istanbul`,
# `America/New_York`. `UTC` and the empty name are UTC and `Local` is
# `Location.local`, without a file. A name that climbs out of the database
# (`..`, a leading `/`, a `\`) is refused, and so is one no directory has.
def self.load(name : String) : Location | LocationError
# The machine's zone; see the type's comment for where it comes from.
def self.local : Location
# The machine's own zone, the one `Location.local` answers.
def local? : Bool
# The zone *time* is in here.
def lookup(time : Time) : Zone
def lookup(unix_seconds : Int64) : Zone
def name : String
# A location from the bytes of a TZif file, RFC 8536, named *name*.
# Anything that is not one is refused with what was wrong and where.
def self.read_zoneinfo(name : String, data : String) : Location | LocationError
# The instant a wall clock here reads *wall* (seconds since 1970 as a
# UTC clock would count them). A wall time a change repeats is the
# earlier of the two instants; one a change skips is read on the clock
# from before the change, which lands it past the gap: 02:30 on the
# morning summer starts in Berlin is 03:30 CEST. Python's `fold=0` and
# Go read them the same way. `Time.local` and a parse at a location ask
# this.
def resolve(wall : Int64) : Int64
def to_s : String
def utc? : Bool
endLocationErrorstruct
What Location.load and Location.read_zoneinfo answer when there is no zone by that name, or the bytes are not a zone: a name the program was handed or a file on the machine is not a bug in the program, so it is a value and not a panic.
pub struct LocationError
def initialize(message : String)
def to_s : String
endSpanstruct
pub struct Span
def +(other : Span) : Span
def -(other : Span) : Span
def - : Span
def <(other : Span) : Bool
def <=(other : Span) : Bool
def <=>(other : Span) : Int32
def ==(other : Span) : Bool
def >(other : Span) : Bool
def >=(other : Span) : Bool
# The seconds are an Int64, and a span that leaves it is refused here,
# by name: `Span.seconds(Int64::MAX) + Span.seconds(1)` panicked with the
# prelude's "arithmetic overflow", a sentence that named no span. The
# wrapping sum is compared with an addend to see whether it wrapped; the
# product is bounded before it is taken.
def self.add_seconds(a : Int64, b : Int64) : Int64
def days : Int64
def self.days(n : Int64) : Span
# Hashing follows `==`, the two parts mixed as a tuple's members are. Without
# it every span hashed to its type: `Span.seconds(1).hash ==
# Span.days(9).hash`, and a table of 16,000 spans took 0.95 s to fill where
# the same pairs as tuples took 1 ms.
def hash : Int32
def hours : Int32
def self.hours(n : Int64) : Span
def initialize(seconds : Int64 = 0_i64, nanoseconds : Int32 = 0)
def millisecond : Int32
def milliseconds : Int32
def self.milliseconds(n : Int64) : Span
def minutes : Int32
def self.minutes(n : Int64) : Span
def self.mul_seconds(n : Int64, unit : Int64) : Int64
def nanosecond : Int32
def nanoseconds : Int32
def self.nanoseconds(n : Int64) : Span
def self.new(*, days : Int64 = 0_i64, hours : Int64 = 0_i64, minutes : Int64 = 0_i64, seconds : Int64 = 0_i64, milliseconds : Int64 = 0_i64, nanoseconds : Int64 = 0_i64) : Span
def seconds : Int32
def self.seconds(n : Int64) : Span
def self.sub_seconds(a : Int64, b : Int64) : Int64
def to_i : Int64
def to_s : String
def total_days : Float64
def total_hours : Float64
def total_milliseconds : Float64
def total_minutes : Float64
def total_nanoseconds : Float64
def total_seconds : Float64
def total_seconds_i64 : Int64
def self.zero : Span
endTimestruct
pub struct Time
alias DayOfWeek = Std::Time::DayOfWeek
# A pattern of Crystal's directives, for writing a `Time` and reading one
# back. Text that is not a directive is written as it is and must be
# there, byte for byte, when reading; ASCII whitespace in the pattern is
# written as it is and reads any run of whitespace, none included.
#
# %Y year, four digits or more, year -1 as -0001 %C year // 100
# %y year % 100 %G %g %V the ISO 8601 week year, its last two digits,
# and the week 01..53 %m month 01..12 %B January %b %h Jan
# %d day 01..31 %e day " 1".."31" %j day of the year 001..366
# %H hour 00..23 %k " 0".."23" %I 01..12 %l " 1".."12" %p am %P AM
# %M minute %S second %L milliseconds %3N %6N %9N the fraction to
# 3, 6 and 9 digits %N nanoseconds, any number of digits when read
# %A Sunday %a Sun %u day of the week, Monday 1..7 %w Sunday 0..6
# %s seconds since the epoch %z +0530 %:z +05:30 %::z +05:30:00
# %Z and %^Z: UTC at offset 0 and the offset, +05:30, at any other
# %F %Y-%m-%d %T %X %H:%M:%S %R %H:%M %D %x %m/%d/%y
# %r %I:%M:%S %P %c %a %b %e %H:%M:%S %Y %n newline %t tab %% %
#
# A number takes a flag between the `%` and its letter: `-` for no
# padding (`%-d` is 5), `_` for spaces (`%_m` is " 5") and `0` for zeros
# (`%0e` is 05); `^` upcases a name (`%^a` is SUN). The other library knows
# `%-d`, `%-m`, `%_m` and the `^` names. The same flags on its other numbers
# are this module's, where that library wrote the `%-` back out as text; a `%`
# that starts no directive is text here as it is there.
#
# Reading takes up to a number's width in digits, fewer when the next byte
# is not a digit, and a name in any case. `%Y` takes four digits when a
# directive follows it (`%Y%m%d`), and when text or the end of the
# pattern does (`%F`), an optional `-` and up to ten, so the years `to_s`
# writes are the years it reads. A field outside its range, a day the
# month does not have, a name that is not one and text after the pattern
# are each a `TimeParseError`, never a panic. A day name is checked to be
# one and not checked against the date, as Crystal, Go and Python read it.
struct Format
# The parts of an instant as they read at an offset, worked out once
# per `format` rather than once per directive. *zone* is what `%Z`
# writes, the location's abbreviation; with none it writes `UTC` at
# offset 0 and the offset at any other.
struct Fields
def day : Int32
def day_of_week : Int32
def day_of_year : Int32
def hour : Int32
def initialize(time : Time, offset : Int32, zone : String | ::Nil)
def minute : Int32
def month : Int32
def nanosecond : Int32
def offset : Int32
def second : Int32
def unix : Int64
def week : ::Tuple(Int64, Int32)
def year : Int32
def zone : String | ::Nil
end
# The HTTP date of RFC 9110 section 5.6.7. It is written as the
# IMF-fixdate, always in GMT, `Sun, 06 Nov 1994 08:49:37 GMT`; it is
# read in all three of the forms a recipient must accept: that one, the
# obsolete RFC 850 `Sunday, 06-Nov-94 08:49:37 GMT`, and asctime's
# `Sun Nov 6 08:49:37 1994`.
module HTTP_DATE
def self.format(time : Time) : String
def self.parse(string : String) : Time | TimeParseError
end
# ISO 8601's calendar date, written `2016-02-15` as the date reads at
# *offset* or in the time's own location; read extended or basic, and
# as an ordinal or a week date, at *offset* since a date names no zone.
module ISO_8601_DATE
def self.format(time : Time, offset : Int32 | ::Nil = nil) : String
def self.parse(string : String, offset : Int32 = 0) : Time | TimeParseError
end
# ISO 8601's date and time, written as RFC 3339 writes it and read with
# every form of `ISO_8601_DATE` and `ISO_8601_TIME` joined by an
# optional `T`. The zone is required, as Crystal requires it.
module ISO_8601_DATE_TIME
def self.format(time : Time, offset : Int32 | ::Nil = nil) : String
def self.parse(string : String) : Time | TimeParseError
end
# ISO 8601's time of day and its zone, `04:35:50Z`; read as on
# 0001-01-01, the day Crystal reads it on.
module ISO_8601_TIME
def self.format(time : Time, offset : Int32 | ::Nil = nil) : String
def self.parse(string : String) : Time | TimeParseError
end
# Reads a text by a pattern or by one of the named grammars. Every step
# answers whether it matched; the first that did not leaves its sentence
# in `@error`, and `finish` answers that, so no text a program is handed
# ends it in a panic.
class Parser
def accept(char : Char) : Bool
def am_pm : Bool
def at_end : Bool
# RFC 5322's CFWS: whitespace and parenthesised comments, which nest
# and in which `\` quotes the next byte. Answers whether there was any;
# a comment left open is the error, and the parse fails on it.
def cfws : Bool
def day_name : Bool
def self.digit?(b : UInt8) : Bool
# Up to *max* digits and at least one. Nil, with the sentence, when
# there are none, or when they are past what an Int64 holds.
def digits(max : Int32, what : String) : Int64 | ::Nil
def directive(letter : Char, flag : Char, digits : Int32, wide : Bool) : Bool
# Exactly *count* digits, or nil, reading nothing and saying nothing:
# the caller names what was missing.
def exact(count : Int32) : Int32 | ::Nil
def expect(char : Char) : Bool
def fail(what : String) : Bool
# A numeric directive's field, read as its flag and its padding say.
def field(width : Int32, pad : Char, flag : Char, what : String) : Int32 | ::Nil
# The instant the fields name, or the first sentence a step left. The
# fields are checked the way `Time.utc` checks them, with RFC 3339's
# allowance: `:60` is a leap second, read as the second after `:59`,
# the instant it names on every clock that has none. A text that
# names no zone is read at *default_offset*, or failing that on
# *location*'s wall clock; with a location the time answered is in it.
def finish(default_offset : Int32 | ::Nil, location : Location | ::Nil = nil) : Time | TimeParseError
# A fraction of a second: up to *max* digits, scaled to nanoseconds,
# and for `%N` any digits past the ninth read and dropped.
def fraction(max : Int32, rest : Bool) : Bool
# RFC 9110 section 5.6.7's three forms of an HTTP date: the
# IMF-fixdate `Sun, 06 Nov 1994 08:49:37 GMT`, the obsolete RFC 850
# `Sunday, 06-Nov-94 08:49:37 GMT`, and asctime's `Sun Nov 6 08:49:37
# 1994`, which has no zone and is GMT. Crystal's reading: a day name,
# short or long, in any of the three; the four-digit year IIS writes
# in the RFC 850 shape; and the zones RFC 2822 names where RFC 9110
# writes GMT.
def http_date : Bool
def initialize(input : String)
# ISO 8601's dates, extended or basic: 2016-02-15 and 20160215, the
# ordinal 2016-046 and 2016046, and the week date 2016-W07-1 and
# 2016W071.
def iso_date : Bool
def iso_date_time : Bool
# ISO 8601's time of day, extended or basic: 04:35:50 and 043550, a
# fraction of the second after `.` or `,`, or the minutes or the hour
# alone with a fraction of their own (04:35.5 is 04:35:30).
def iso_time : Bool
def self.letter?(b : UInt8) : Bool
def literal(b : UInt8) : Bool
def month_name(short : Bool) : Bool
# A run of ASCII letters and the index in *names* of the name it
# spells, in any case: for *short*, the first three letters of a name
# and no more; otherwise the start of a name, three letters or longer,
# as Crystal reads `%B` and `%A`.
def name(names : Array(String), short : Bool, what : String) : Int32 | ::Nil
def need_cfws : Bool
def number(max : Int32, what : String) : Int32 | ::Nil
def offset_number(allow_colon : Bool, force_colon : Bool, allow_seconds : Bool) : Bool
# A number blank padded, as `%e` writes " 5": one space stands for a
# digit.
def padded_number(max : Int32, what : String) : Int32 | ::Nil
def pattern(pattern : String) : Bool
# The byte *ahead* of the current one, or 0 past the end, as Crystal's
# reader answers '\0' there.
def peek(ahead : Int32 = 0) : UInt8
# RFC 9110's rule for a two-digit year (section 5.6.7): a year that
# appears to be more than 50 years in the future is the most recent
# past year with those last two digits. The candidate is taken in this
# century and moved a century to land in the 100 years ending 50 years
# from now, so the rule holds in 2090 too. The other library read 00..49 as 20xx
# and the rest as 19xx, whatever the year it was read in.
def self.recent_year(two_digits : Int64) : Int64
# A sentence about fields that were all read, where no one byte is to
# blame.
def refuse(what : String) : TimeParseError
# RFC 5322 section 3.3's date-time, with section 4.3's obsolete
# two- and three-digit years and zone names, as Crystal's `RFC_2822`
# reads it: `Mon, 15 Feb 2016 04:35:50 +0000`, the day name and the
# seconds optional, comments anywhere whitespace goes.
def rfc_2822 : Bool
def same_letters?(start : Int32, name : String, size : Int32) : Bool
def skip_spaces : Bool
def self.space?(b : UInt8) : Bool
def text(start : Int32, stop : Int32) : String
# HH:MM:SS, as both HTTP and RFC 850 write the time of day.
def time_of_day : Bool
# One whitespace byte, as the HTTP grammars put between their parts.
def whitespace : Bool
# `%Y`: four digits when a directive follows it, and an optional `-`
# and up to ten when text does, which is every year an Int32 holds.
def year(wide : Bool) : Int64 | ::Nil
# Exactly four digits, as the HTTP and ISO 8601 grammars write a year.
def year4 : Bool
# `%z`, `%:z` and `%::z` alike, as Crystal reads them: `Z`, `UTC`, or
# an offset with or without its colons.
def zone : Bool
# `%Z`: an offset, or a name read up to the next whitespace. Only the
# names of UTC itself are read: an abbreviation names no single zone
# (CST is three of them), so any other is refused by name rather than
# read as UTC or guessed at. A text whose zone is known is read at
# that `Location` instead.
def zone_name : Bool
# RFC 5322's zone (section 3.3, and 4.3 for the obsolete names): an
# offset of four digits, or a name. A military letter is read as
# `-0000`, the offset of a time whose zone is not known, as section
# 4.3 says to; a name the RFC does not give is refused, where Crystal
# read it as UTC in silence. `UTC` is not the RFC's, and is read as
# Crystal reads it.
def zone_rfc2822 : Bool
# ISO 8601's zone: `Z` or an offset.
def zone_z_or_offset : Bool
end
# RFC 2822's date and time, `Mon, 15 Feb 2016 04:35:50 +0000`, as
# Crystal writes it: the day of the month unpadded, the offset at the
# end, the time's own unless another is given. RFC 2822 writes an offset
# in whole minutes, so an offset with seconds is refused rather than
# written as some other instant.
module RFC_2822
def self.format(time : Time, offset : Int32 | ::Nil = nil) : String
def self.parse(string : String) : Time | TimeParseError
end
# RFC 3339, the module's own serialisation: `to_rfc3339` and
# `parse_rfc3339` are this module's `format` and `parse`, and
# `parse_rfc3339` panics with the sentence `parse` answers. Written at
# *offset*, or at the time's own when it is whole minutes and in UTC
# when it is not.
module RFC_3339
def self.format(time : Time, fraction_digits : Int32 = 0, offset : Int32 | ::Nil = nil) : String
def self.parse(str : String) : Time | TimeParseError
end
# An offset is less than a day either side of UTC: the widest RFC 3339
# and RFC 2822 write is 23:59, and a day or more is a date, not a zone.
# An offset is the program's own, so a wrong one is a panic.
def self.check_offset(offset : Int32) : Nil
def format(time : Time, offset : Int32) : String
def format(time : Time) : String
# *offset*, seconds east of UTC, is the one a parse reads a text at when
# the text names no zone, and the one `format` writes at. Nil, as for
# Crystal's `Format.new(pattern)`, makes a text without a zone an error,
# and `format` writes a time in its own location.
def initialize(pattern : String, offset : Int32 | ::Nil = nil)
# A text that names no zone is read on *location*'s wall clock, and
# every time is read and written in *location*.
def initialize(pattern : String, location : Location)
# The directive at the `%` at byte *i* of *pattern*: the byte after it,
# its letter, its flag, the colons of `%:z` and `%::z`, and the digits of
# `%3N`. A `%` that starts no directive answers the letter '\0' and the
# byte after the `%`: it is text, and so is what follows it, as Crystal
# writes `%Q` back out as `%Q`.
def self.lex(pattern : String, i : Int32) : ::Tuple(Int32, Char, Char, Int32, Int32)
def location : Location | ::Nil
def self.name(io : String::Builder, text : String, flag : Char) : Nil
# A number padded to *width* with *pad*, '\0' for none, unless a flag
# says otherwise. The sign of a negative year goes in front of the
# padding, as `-0001`, the way `to_rfc3339` writes it.
def self.number(io : String::Builder, value : Int64, width : Int32, pad : Char, flag : Char) : Nil
def self.numeric?(letter : Char) : Bool
def offset : Int32 | ::Nil
# `+hh:mm` or `+hhmm`, and the seconds never (0), when there are any (1)
# or always (2), the way Crystal's `Zone#format` writes an offset.
def self.offset_text(offset : Int32, colon : Bool, seconds : Int32) : String
def parse(string : String, offset : Int32 | ::Nil) : Time | TimeParseError
def parse(string : String) : Time | TimeParseError
def pattern : String
def self.plain?(letter : Char) : Bool
def self.textual?(letter : Char) : Bool
def self.twelve(hour : Int32) : Int64
def self.write(io : String::Builder, pattern : String, f : Fields) : Nil
def self.write_directive(io : String::Builder, letter : Char, flag : Char, colons : Int32, digits : Int32, f : Fields) : Nil
end
lib LibKernel32
end
alias Location = Std::Time::Location
alias Span = Std::Time::Span
# The sum is checked before it is taken, so a span too long for the
# seconds is refused as one; a sum that fits but leaves the years an
# Int32 names is refused by the constructor. A span is exact: a day
# added across the night summer begins is 24 hours, and the clock reads
# an hour later than it did.
def +(span : Span) : Time
def -(span : Span) : Time
def -(other : Time) : Span
def <(other : Time) : Bool
def <=(other : Time) : Bool
def <=>(other : Time) : Int32
def ==(other : Time) : Bool
def >(other : Time) : Bool
def >=(other : Time) : Bool
# The ISO 8601 week-numbering year and week this day falls in, as
# Crystal's `calendar_week` answers: 2021-01-01 is in week 53 of 2020.
def calendar_week : ::Tuple(Int32, Int32)
# Converts days since 1970-01-01 into {year, month, day}.
def self.civil_from_days(z : Int64) : ::Tuple(Int32, Int32, Int32)
def day : Int32
def day_of_week : DayOfWeek
def day_of_year : Int32
def self.days_from_civil(y : Int64, m : Int32, d : Int32) : Int64
def self.days_in_month(year : Int32, month : Int32) : Int32
def self.days_in_year(year : Int32) : Int32
# Number of days between 1970-01-01 and the given civil date.
# Follows Howard Hinnant's Gregorian calendar algorithm.
def self.floor_div(a : Int64, b : Int64) : Int64
def self.floor_mod(a : Int64, b : Int64) : Int64
def self.from_unix(seconds : Int64, nanoseconds : Int32 = 0) : Time
def self.from_unix_ms(milliseconds : Int64) : Time
# Hashing follows `==`, as `Span`'s does. Without it every time hashed to its
# type: `Time.unix(1).hash == Time.unix(1000000, 5).hash`, and a table of
# 16,000 times took 1.2 s to fill where the same pairs as tuples took 1 ms.
def hash : Int32
def hour : Int32
# The same instant read in *location*: `Time.utc(2024, 7, 1, 12).in(berlin)`
# is 14:00 CEST.
def in(location : Location) : Time
def initialize(seconds : Int64, nanoseconds : Int32 = 0, location : Location = Location::UTC)
# As the other library writes it: the date and time, the nanoseconds
# when there are any, and `Z`. There was no `inspect`, so it was `to_s`,
# and `Time.utc(2024, 1, 1, 0, 0, 0, nanosecond: 5)` inspected as
# `2024-01-01 00:00:00 UTC`, the same as `Time.utc(2024, 1, 1)`, which
# `==` tells apart from it. Out of UTC the offset follows, and the
# location's name when it is more than an offset:
# `2016-02-15 11:20:30 +01:00 Europe/Berlin`.
def inspect : String
# The ISO 8601 week-numbering year and week of a date, from its year, day
# of the month, day of the year and day of the week (Monday is 1). This is
# Crystal's computation: the week is the count of Mondays up to the day,
# and the first days of January or the last of December can belong to a
# week of the year next to it. The year is an Int64 because the first days
# of the smallest Int32 year belong to the year before it.
def self.iso_week(year : Int64, day : Int32, day_of_year : Int32, day_of_week : Int32) : ::Tuple(Int64, Int32)
def self.leap_year?(year : Int32) : Bool
# The time a clock in *location* shows as the date and time given, the
# machine's own zone when none is. Its fields are checked as `utc` checks
# them. A wall time a change of offset repeats, the hour after summer
# ends, is the earlier of the two; one a change skips, the hour summer
# begins with, is read past the gap (see `Location#resolve`).
def self.local(year : Int32, month : Int32, day : Int32, hour : Int32 = 0, minute : Int32 = 0, second : Int32 = 0, millisecond : Int32 = 0, nanosecond : Int32 = 0, location : Location = Location.local) : Time
# Now, read in *location*.
def self.local(location : Location) : Time
# Now, in the machine's own zone (`Location.local`).
def self.local : Time
def local? : Bool
# The zone the date and clock are read in, and its offset from UTC at
# this instant in seconds east. `==`, `<=>` and `hash` are the instant's
# and do not look at either: noon in London and 13:00 in Paris are the
# same time.
def location : Location
def millisecond : Int32
def milliseconds : Int32
def minute : Int32
def self.monotonic : Span
def self.monotonic_nanoseconds : UInt64
def month : Int32
def nanosecond : Int32
def nanoseconds : Int32
def self.new(year : Int32, month : Int32, day : Int32, hour : Int32 = 0, minute : Int32 = 0, second : Int32 = 0, millisecond : Int32 = 0, nanosecond : Int32 = 0) : Time
def offset : Int32
def self.pad2(n : Int32) : String
def self.pad3(n : Int32) : String
# Four digits, and a negative year keeps its sign in front of them rather
# than inside: written as `n < 10 ? "000" + n.to_s`, year -1 printed
# `000-1-01-01`, which is neither a year nor a date. ISO 8601 spells it
# `-0001`, and the conversions themselves were right all along - the epoch
# round-trips through it.
def self.pad4(n : Int32) : String
def self.pad6(n : Int32) : String
def self.pad9(n : Int32) : String
# Reads *string* by *pattern*'s directives (see `Time::Format`). A text
# that names no zone is read at *offset* seconds east of UTC, as Crystal's
# `Time.parse` reads one at the location it is given.
def self.parse(string : String, pattern : String, offset : Int32) : Time | TimeParseError
# Reads *string* by *pattern* in *location*: a text that names no zone is
# the wall clock there, its offset the one the location kept at that date
# (a wall time a change repeats or skips is read as `Time.local` reads
# it), and the time answered is in *location* whether the text named a
# zone or not.
def self.parse(string : String, pattern : String, location : Location) : Time | TimeParseError
def self.parse_iso8601(string : String) : Time | TimeParseError
# `parse` in the machine's own zone.
def self.parse_local(string : String, pattern : String) : Time | TimeParseError
# Any of RFC 9110's three HTTP date forms; see `Time::Format::HTTP_DATE`.
def self.parse_rfc1123(string : String) : Time | TimeParseError
def self.parse_rfc2822(string : String) : Time | TimeParseError
# Parses an RFC 3339 / ISO 8601 string into a UTC Time, and panics with the
# parser's sentence when it is not one. Handles forms like:
# 2024-02-29T12:30:45Z
# 2024-02-29T12:30:45.123Z
# 2024-02-29T12:30:45.123456789Z
# 2024-02-29 12:30:45Z
# 2024-02-29T12:30:45+05:00
# 2024-02-29T12:30:45-08:00
# -0001-01-01T00:00:00Z and 12345-06-07T08:09:10Z, as `to_rfc3339` writes them
# `Time::Format::RFC_3339.parse` reads the same grammar and answers a
# `TimeParseError` instead, for text the program did not write itself.
def self.parse_rfc3339(str : String) : Time
# `parse` at UTC when the text names no zone.
def self.parse_utc(string : String, pattern : String) : Time | TimeParseError
def second : Int32
def seconds : Int64
# Moves the date by calendar years and months, then by weeks and days,
# then by the smaller units, in that order, as Crystal's `shift` does. A
# day the month does not have becomes its last: `Time.utc(2024, 1,
# 31).shift(months: 1)` is 2024-02-29. The date moves on the wall clock
# of the time's location, so the time of day is kept across a change of
# offset - a day after 12:00 on the Saturday summer begins is 12:00 on
# Sunday, 23 hours later - and the hours, minutes, seconds and
# nanoseconds are exact. A year past an Int32 is refused with the
# constructor's sentence, and a span the Int64 seconds do not hold with
# `Span`'s.
def shift(*, years : Int32 = 0, months : Int32 = 0, weeks : Int64 = 0_i64, days : Int64 = 0_i64, hours : Int64 = 0_i64, minutes : Int64 = 0_i64, seconds : Int64 = 0_i64, nanoseconds : Int64 = 0_i64) : Time
def to_local : Time
# RFC 1123's date as HTTP writes it, RFC 9110's IMF-fixdate, always in GMT:
# `Sun, 06 Nov 1994 08:49:37 GMT`.
def to_rfc1123 : String
# RFC 2822's date, `Mon, 15 Feb 2016 04:35:50 +0000`, at *offset*, or at
# the time's own offset when none is given.
def to_rfc2822(offset : Int32 | ::Nil = nil) : String
# RFC 3339 at the time's own offset: `Z` at UTC, `+02:00` in CEST. An
# offset RFC 3339 cannot write, one with seconds in it as local mean time
# before standard time had, is written as the same instant in UTC.
def to_rfc3339(fraction_digits : Int32 = 0) : String
# The instant written as it reads at *offset* seconds east of UTC:
# `to_s("%F %T %:z", 19800)` is the wall clock in India.
def to_s(format : String, offset : Int32) : String
# The time written by *format*'s directives (see `Time::Format`) as its
# own location reads it; `%Z` is the zone's abbreviation, `CEST`.
def to_s(format : String) : String
# `2016-02-15 10:20:30 UTC`, and in any other location the offset in
# place of `UTC`: `2016-02-15 11:20:30 +01:00`.
def to_s : String
def to_unix : Int64
def to_unix_f : Float64
# Refused with the module's sentence past the milliseconds an Int64
# holds, some 292 million years either side of 1970, where the multiply
# was the prelude's bare "arithmetic overflow" for a year `Time.utc`
# accepts, 300,000,000. Both edges answer to the last millisecond that
# fits: the lowest 808 an Int64 holds were refused, so
# `Time.unix_ms(Int64::MIN).to_unix_ms` panicked. A negative second is
# taken one up and its milliseconds from 1000 down, so the product fits
# too.
def to_unix_ms : Int64
def to_utc : Time
def self.unix(seconds : Int64, nanoseconds : Int32 = 0) : Time
def self.unix_ms(milliseconds : Int64) : Time
def self.utc(year : Int32, month : Int32, day : Int32, hour : Int32 = 0, minute : Int32 = 0, second : Int32 = 0, millisecond : Int32 = 0, nanosecond : Int32 = 0) : Time
def self.utc : Time
def utc? : Bool
def self.wall_clock_nanoseconds : UInt64
# The instant of a day of an ISO 8601 week, as Crystal's `week_date` makes
# it: `Time.week_date(2016, 1, 1)` is Monday 2016-01-04. A week the year
# does not have and a day past Sunday are refused, where the other library answered
# a day of some other week.
def self.week_date(year : Int32, week : Int32, day_of_week : Int32, hour : Int32 = 0, minute : Int32 = 0, second : Int32 = 0, nanosecond : Int32 = 0) : Time
# Days since 1970-01-01 of a day of an ISO 8601 week. Week 1 is the week
# holding January 4th, so its Monday is the Monday on or before that day.
def self.week_date_days(year : Int32, week : Int32, day_of_week : Int32) : Int64
# A year has 53 ISO 8601 weeks when it starts on a Thursday, or on a
# Wednesday in a leap year: either way it has 53 Thursdays, and a week
# belongs to the year that holds its Thursday.
def self.weeks_in_year(year : Int32) : Int32
def year : Int32
# The zone this instant is in at its location: its abbreviation, offset
# and whether it is summer time.
def zone : Location::Zone
endTimeParseErrorstruct
What Time::Format#parse and the named parsers answer when the text is not a time the pattern describes. Bad input is the caller's to handle, not a panic: a header or a log line from somewhere else is not a bug in the program reading it. message is the sentence, naming the byte the parser stood at and the text; position is that byte, counted from 0.
pub struct TimeParseError
def initialize(message : String, input : String, position : Int32)
def input : String
def position : Int32
def to_s : String
endError for TimeParseErrorimpl
impl Error for TimeParseError
def message : String
endStd::Traits::Comparable for Spanimpl
impl Std::Traits::Comparable for Span
def <=>(other : self) : Int32
endError for LocationErrorimpl
impl Error for LocationError
def message : String
endStd::Traits::Comparable for Timeimpl
impl Std::Traits::Comparable for Time
def <=>(other : self) : Int32
end
Time,Time::SpanandTime::Location: the wall clock, the monotonic clock, time zones, civil-calendar arithmetic, strftime-style formatting and parsing (Time::Format), and the named formats: RFC 3339, ISO 8601, RFC 2822 and the HTTP date of RFC 9110.