std.time.DateTime
type pub inline DateTime
A date and time based on the Gregorian calendar.
Internally the time is represented as the number of seconds since the Unix epoch, excluding leap seconds.
DateTime
is based on the Gregorian calendar, and doesn't support additional
calendars such as the Julian calendar.
If you want to measure the duration between two events, it's best to use the
Instant
type, as it's not affected by external factors such as clock
adjustments and leap seconds.
Static methods
from_timestamp
Show source codeHide source code
fn pub static from_timestamp[T: ToFloat](
time: ref T,
utc_offset: Int,
) -> Option[DateTime] {
# This implementation is based on the algorithms as described on
# http://howardhinnant.github.io/date_algorithms.html, specifically the
# `civil_from_days()` algorithm.
#
# To be truly honest, at the time of writing I didn't fully understand the
# algorithm, and reading through the linked article made my head spin. But
# it works, so ship it!
#
# Important note: this algorithm works because Inko implements integer
# divisions as floored divisions (like e.g. Python and Ruby), instead of
# rounding towards zero (like Rust and C).
let time = time.to_float
let epoch_secs = time.to_int + utc_offset
let epoch_days = (epoch_secs / SECS_PER_DAY) + 719_468
let era = if epoch_days >= 0 { epoch_days } else { epoch_days - 146_096 }
/ 146_097
let doe = epoch_days - (era * 146_097)
let yoe = (doe - (doe / 1460) + (doe / 36_524) - (doe / 146_096)) / 365
let doy = doe - ((365 * yoe) + (yoe / 4) - (yoe / 100))
let mp = ((5 * doy) + 2) / 153
let day = doy - ((153 * mp) + 2 / 5) + 1
let month = if mp < 10 { mp + 3 } else { mp - 9 }
let mut year = yoe + (era * 400)
if month <= 2 { year += 1 }
# The number of seconds since the start of the day.
let mut day_secs = epoch_secs - ((epoch_secs / SECS_PER_DAY) * SECS_PER_DAY)
let second = day_secs % 60
let minute = (day_secs % SECS_PER_HOUR) / 60
let hour = day_secs / SECS_PER_HOUR
let nsec = (time.fractional * NANOS_PER_SEC).to_int
Option.Some(
DateTime(
date: try Date.new(year, month, day),
time: try Time.new(hour, minute, second, nsec),
utc_offset: utc_offset,
),
)
}
fn pub static from_timestamp[T: ToFloat](time: ref T, utc_offset: Int) -> Option[DateTime]
Returns a new DateTime
from a Unix timestamp and UTC offset.
The time
argument is the number of seconds since or before the Unix epoch.
If the timestamp is invalid, an Option.None
is returned.
Example
import std.time (DateTime)
DateTime.from_timestamp(time: 1661546853, utc_offset: 0).year # => 2022
local
Show source codeHide source code
fn pub static local -> DateTime {
let raw = sys.local_time
DateTime(
date: Date.new_unchecked(raw.year, raw.month, raw.day),
time: Time.new_unchecked(raw.hour, raw.minute, raw.second, raw.nanos),
utc_offset: raw.offset,
)
}
fn pub static local -> DateTime
Returns a new DateTime
representing the current time using the system's
local time.
Examples
import std.time (DateTime)
DateTime.local
new
Show source codeHide source code
fn pub inline static new(
date: Date,
time: Time,
utc_offset: Int,
) -> DateTime {
DateTime(date: date, time: time, utc_offset: utc_offset)
}
fn pub inline static new(date: Date, time: Time, utc_offset: Int) -> DateTime
Returns a new DateTime
using the given components.
Examples
import std.time (Date, DateTime, Time)
let date = Date.new(year: 1992, month: 8, day: 21).get
let time = Time.new(hour: 12, minute: 15, second: 30, nanosecond: 0).get
DateTime.new(date, time, utc_offset: 3600)
parse
Show source codeHide source code
fn pub static parse[T: Bytes, L: Locale](
input: ref T,
format: String,
locale: ref L,
) -> Option[DateTime] {
if input.size == 0 or format.size == 0 { return Option.None }
let mut inp_idx = 0
let mut fmt_idx = 0
let mut year = 0
let mut mon = 1
let mut day = 1
let mut hour = 0
let mut min = 0
let mut sec = 0
let mut nsec = 0
let mut off = 0
loop {
match format.opt(fmt_idx) {
case Some(PERCENT) -> {
fmt_idx += 1
match format.opt(fmt_idx) {
case Some(PERCENT) if input.opt(inp_idx).or(-1) == PERCENT -> {
fmt_idx += 1
inp_idx += 1
}
case Some(PERCENT) -> return Option.None
case Some(what) -> {
fmt_idx += 1
inp_idx += match what {
case UPPER_Y -> {
let neg = match input.opt(inp_idx) {
case Some(MINUS) -> {
inp_idx += 1
true
}
case _ -> false
}
match try four_digits(input, inp_idx) {
case v if neg -> year = v.opposite
case v -> year = v
}
4
}
case LOWER_M -> {
mon = try two_digits(input, inp_idx)
2
}
case LOWER_D -> {
day = try two_digits(input, inp_idx)
2
}
case UPPER_H -> {
hour = try two_digits(input, inp_idx)
2
}
case UPPER_M -> {
min = try two_digits(input, inp_idx)
2
}
case UPPER_S -> {
sec = try two_digits(input, inp_idx)
2
}
case LOWER_B -> {
let idx_len = try locale.parse_short_month(input, inp_idx)
mon = idx_len.0 + 1
idx_len.1
}
case UPPER_B -> {
let idx_len = try locale.parse_full_month(input, inp_idx)
mon = idx_len.0 + 1
idx_len.1
}
case LOWER_A -> {
match try locale.parse_short_day_of_week(input, inp_idx) {
case (_, len) -> len
}
}
case UPPER_A -> {
match try locale.parse_full_day_of_week(input, inp_idx) {
case (_, len) -> len
}
}
case LOWER_F -> {
match input.opt(inp_idx := inp_idx + 1) {
case Some(DOT) -> {}
case _ -> return Option.None
}
match try digits(input, start: inp_idx, limit: 3) {
case (num, len) -> {
nsec = num * MICROS_PER_SEC
len
}
}
}
case LOWER_Z -> {
match input.opt(inp_idx) {
case Some(UPPER_Z) -> {
inp_idx += 1
next
}
case _ -> {}
}
let off_idx = try parse_offset(input, inp_idx)
off = off_idx.0
inp_idx = off_idx.1
2
}
case UPPER_Z -> {
match name_index_at(input, inp_idx, RFC_2822_ZONES) {
case Some((_, len)) -> {
inp_idx += len
next
}
case _ -> {}
}
let off_idx = try parse_offset(input, inp_idx)
off = off_idx.0
inp_idx = off_idx.1
2
}
case _ -> return Option.None
}
}
case _ -> return Option.None
}
}
case Some(f) -> {
match input.opt(inp_idx) {
case Some(i) if f == i -> {
fmt_idx += 1
inp_idx += 1
}
case _ -> return Option.None
}
}
case _ -> break
}
}
Option.Some(
DateTime.new(
date: try Date.new(year, mon, day),
time: try Time.new(hour, min, sec, nsec),
utc_offset: off,
),
)
}
fn pub static parse[T: Bytes, L: Locale](input: ref T, format: String, locale: ref L) -> Option[DateTime]
Parses a DateTime
according to a format string loosely based on the
strftime(2)
format strings.
The input is any type that implements std.string.Bytes
, such as the
String
and ByteArray
types.
The locale
argument is a type that implements std.locale.Locale
and is
used for locale-aware parsing, such as when parsing the names of the months.
Format sequences
The format
argument contains a "format string" that specifies how the
input is to be parsed. This String
can contain both regular characters to
parse as-is, and special format sequences. These sequences start with %
followed by one or more characters and determine how the input is to be
parsed. The following sequences are supported:
Sequence | Examples | Description |
---|---|---|
%Y | -1234, 0001, 1970, 2024 | The year, zero-padded to four digits, optionally starting with a - to
signal a year before year zero. |
%m | 01, 12 | The month of the year (01-12), zero-padded to two digits. |
%d | 01, 31 | The day of the month (01-31), zero-padded to two digits. |
%H | 01, 12, 23 | The hour of the day (00-23), zero-padded to two digits. |
%M | 01, 12, 59 | The minute of the hour (00-59), zero-padded to two digits. |
%S | 01, 12, 59, 60 | The second of the hour (00-60), zero-padded to two digits. A value of 60 is supported to account for leap seconds. |
%f | .123 | A dot followed by up to three digits representing the number of milliseconds past the second. This value is not padded. |
%z | +0200, +02:30, -0200, -02:00, Z | The UTC offset in the format [SIGN][HH]:[MM] , [SIGN][HH][MM] or a
literal Z where [SIGN] is either + or - , [HH] the amount of
hours and [MM] the amount of minutes. Both the hours and minutes are
zero-padded to two digits. |
+0200, +02:30, -0200, -02:00, UT, GMT | The same as %z but supports UT and GMT instead of Z for UTC
offsets of zero. | +02)), |
%b | Jan | The locale-aware abbreviated name of the month. For locales that don't use
abbreviated names, this is an alias for %B . |
%B | January | The locale-aware full name of the month. |
%a | Mon | The abbreviated name of the day of the week. Parsed values don't affect
the resulting DateTime . |
%A | Monday | The full name of the day of the week. Parsed values don't affect the
resulting DateTime . |
%% | % | A literal % . |
Certain sequences such as %a
don't influence/affect the resulting date, as
it's not possible to derive a meaningful value from them (e.g. what date is
"Monday, December 2024"?). Instead, these sequences are meant for skipping
over values in the input stream when those values aren't statically known
(e.g. the day of the week).
Sequences that match locale-aware data such as %b
and %A
are all case
sensitive.
Examples
Parsing an ISO 8601 date:
import std.time (DateTime)
import std.locale.en (Locale)
let dt = DateTime
.parse(
input: '2024-11-04 12:45:12 -02:45',
format: '%Y-%m-%d %H:%M:%S %z',
locale: Locale.new,
)
.or_panic('the input is invalid')
dt.year # => 2024
dt.month # => 11
dt.hour # => 12
dt.second # => 45
Parsing a date in Japanese:
import std.time (DateTime)
import std.locale.ja (Locale)
let dt = DateTime
.parse(
input: '2023年12月31日',
format: '%Y年%B%d日',
locale: Locale.new,
)
.or_panic('the input is invalid')
dt.year # => 2023
dt.month # => 12
dt.day # => 31
utc
Show source codeHide source code
fn pub static utc -> DateTime {
let raw = sys.utc_time
DateTime(
date: Date.new_unchecked(raw.year, raw.month, raw.day),
time: Time.new_unchecked(raw.hour, raw.minute, raw.second, raw.nanos),
utc_offset: 0,
)
}
fn pub static utc -> DateTime
Returns a new DateTime
representing the current system time using UTC as
the timezone.
Examples
import std.time (DateTime)
DateTime.utc
Instance methods
!=
Show source codeHide source code
fn pub !=(other: T) -> Bool {
(self == other).false?
}
fn pub !=(other: T) -> Bool
Returns true
if self
and the given object are not equal to each other.
+
Show source codeHide source code
fn pub +(other: Duration) -> DateTime {
let timestamp = to_float + other.to_secs
DateTime.from_timestamp(timestamp, utc_offset: @utc_offset).get
}
fn pub +(other: Duration) -> DateTime
Adds the given Duration
to self
, returning the result as a new
DateTime
.
Panics
This method may panic if the result can't be expressed as a DateTime
(e.g.
the year is too great).
-
Show source codeHide source code
fn pub -(other: Duration) -> DateTime {
let timestamp = to_float - other.to_secs
DateTime.from_timestamp(timestamp, utc_offset: @utc_offset).get
}
fn pub -(other: Duration) -> DateTime
Subtracts the given Duration
from self
, returning the result as a new
DateTime
.
Panics
This method may panic if the result can't be expressed as a DateTime
(e.g.
the year is too great).
<
Show source codeHide source code
fn pub <(other: ref T) -> Bool {
match cmp(other) {
case Less -> true
case _ -> false
}
}
fn pub <(other: ref T) -> Bool
Returns true
if self
is lower than the given argument.
<=
Show source codeHide source code
fn pub <=(other: ref T) -> Bool {
match cmp(other) {
case Less or Equal -> true
case _ -> false
}
}
fn pub <=(other: ref T) -> Bool
Returns true
if self
is lower than or equal to the given argument.
==
Show source codeHide source code
fn pub inline ==(other: ref DateTime) -> Bool {
@date == other.date
and @time == other.time
and @utc_offset == other.utc_offset
}
fn pub inline ==(other: ref DateTime) -> Bool
Returns true
if self
and the given object are equal to each other.
This operator is used to perform structural equality. This means two objects residing in different memory locations may be considered equal, provided their structure is equal. For example, two different arrays may be considered to have structural equality if they contain the exact same values.
>
Show source codeHide source code
fn pub >(other: ref T) -> Bool {
match cmp(other) {
case Greater -> true
case _ -> false
}
}
fn pub >(other: ref T) -> Bool
Returns true
if self
is greater than the given argument.
>=
Show source codeHide source code
fn pub >=(other: ref T) -> Bool {
match cmp(other) {
case Greater or Equal -> true
case _ -> false
}
}
fn pub >=(other: ref T) -> Bool
Returns true
if self
is equal to or greater than the given argument.
clone
Show source codeHide source code
fn pub inline clone -> DateTime {
DateTime(date: @date, time: @time, utc_offset: @utc_offset)
}
fn pub inline clone -> DateTime
Creates a clone of self
.
cmp
Show source codeHide source code
fn pub cmp(other: ref DateTime) -> Ordering {
to_float.cmp(other.to_float)
}
fn pub cmp(other: ref DateTime) -> Ordering
Returns the ordering between self
and the given argument.
The returned value should be as follows:
a == b
:Ordering.Equal
a > b
:Ordering.Greater
a < b
:Ordering.Less
date
Show source codeHide source code
fn pub inline date -> Date {
@date
}
fn pub inline date -> Date
Returns the Date
component of self
.
day
Show source codeHide source code
fn pub inline day -> Int {
@date.day
}
fn pub inline day -> Int
Returns the day.
Refer to the documentation of Date.day
for more details.
day_of_week
Show source codeHide source code
fn pub day_of_week -> Int {
@date.day_of_week
}
fn pub day_of_week -> Int
Returns the day of the week from 1 to 7.
Refer to the documentation of Date.day_of_week
for more details.
day_of_year
Show source codeHide source code
fn pub day_of_year -> Int {
@date.day_of_year
}
fn pub day_of_year -> Int
Returns the day of the year.
Refer to the documentation of Date.day_of_year
for more details.
days_since_unix_epoch
Show source codeHide source code
fn pub days_since_unix_epoch -> Int {
@date.days_since_unix_epoch
}
fn pub days_since_unix_epoch -> Int
Returns the number of days between self
and the Unix epoch.
Refer to the documentation of Date.days_since_unix_epoch
for more details.
fmt
Show source codeHide source code
fn pub fmt(formatter: mut Formatter) {
let offset = if @utc_offset == 0 {
' UTC'
} else {
let sign = if @utc_offset > 0 { '+' } else { '-' }
let off = @utc_offset.absolute
let hh = (off / SECS_PER_HOUR).to_string.pad_start(with: '0', chars: 2)
let mm = (off % SECS_PER_HOUR / SECS_PER_MIN).to_string.pad_start(
with: '0',
chars: 2,
)
' ${sign}${hh}${mm}'
}
@date.fmt(formatter)
formatter.write(' ')
@time.fmt(formatter)
formatter.write(offset)
}
fn pub fmt(formatter: mut Formatter)
Formats self
in a human-readable format for debugging purposes.
format
Show source codeHide source code
fn pub format[L: Locale](how: String, locale: ref L) -> String {
let buf = ByteArray.new
let mut i = 0
let max = how.size
while i < max {
match how.byte(i := i + 1) {
case PERCENT -> {
match how.opt(i := i + 1) {
case Some(PERCENT) -> buf.push(PERCENT)
case Some(UPPER_Y) -> format_digits(buf, year, amount: 4)
case Some(LOWER_M) -> format_digits(buf, month, amount: 2)
case Some(LOWER_D) -> format_digits(buf, day, amount: 2)
case Some(UPPER_H) -> format_digits(buf, hour, amount: 2)
case Some(UPPER_M) -> format_digits(buf, minute, amount: 2)
case Some(UPPER_S) -> format_digits(buf, second, amount: 2)
case Some(LOWER_B) -> buf.append(locale.short_month(month - 1))
case Some(UPPER_B) -> buf.append(locale.full_month(month - 1))
case Some(LOWER_A) -> {
buf.append(locale.short_day_of_week(day_of_week - 1))
}
case Some(UPPER_A) -> {
buf.append(locale.full_day_of_week(day_of_week - 1))
}
case Some(LOWER_F) -> {
buf.push(DOT)
format_digits(buf, nanosecond / MICROS_PER_SEC, amount: 1)
}
case Some(LOWER_Z) -> {
if utc_offset == 0 {
buf.push(UPPER_Z)
} else {
format_offset(buf, utc_offset)
}
}
case Some(UPPER_Z) -> {
if utc_offset == 0 {
buf.append('GMT')
} else {
format_offset(buf, utc_offset)
}
}
case Some(byte) -> {
buf.push(PERCENT)
buf.push(byte)
}
case _ -> buf.push(PERCENT)
}
}
case byte -> buf.push(byte)
}
}
buf.into_string
}
fn pub format[L: Locale](how: String, locale: ref L) -> String
Formats self
as a String
according to a formatting string.
The how
argument specifies the format sequences to use to format self
.
The locale
argument is the locale to use for locale-aware formatting, such
as the names of the months.
Format sequences
The supported sequences are the same as those supported by DateTime.parse
,
with the following notes:
%z
producesZ
if the UTC offset is zero, otherwise it produces an offset in the format[SIGN][HH]:[MM]
.%Z
always producesGMT
if the UTC offset is zero.
If an unsupported sequence is found, it's treated as a literal string.
Examples
import std.locale.en (Locale)
import std.time (DateTime)
let en = Locale.new
let dt = DateTime.new(
date: Date.new(2024, 12, 21).get,
time: Time.new(21, 47, 30, 123_000_000).get,
utc_offset: 3600,
)
dt.format('%Y-%m-%d %H:%M:%S%f %z', en) # => '2024-12-21 21:47:30.123 +0100'
hour
Show source codeHide source code
fn pub inline hour -> Int {
@time.hour
}
fn pub inline hour -> Int
Returns the hour of the day.
Refer to the documentation of Time.hour
for more details.
leap_year?
Show source codeHide source code
fn pub leap_year? -> Bool {
@date.leap_year?
}
fn pub leap_year? -> Bool
Returns true
if the current year is a leap year.
Refer to the documentation of Date.leap_year?
for more details.
minute
Show source codeHide source code
fn pub inline minute -> Int {
@time.minute
}
fn pub inline minute -> Int
Returns the minute of the hour.
Refer to the documentation of Time.minute
for more details.
month
Show source codeHide source code
fn pub inline month -> Int {
@date.month
}
fn pub inline month -> Int
Returns the month.
Refer to the documentation of Date.month
for more details.
nanosecond
Show source codeHide source code
fn pub inline nanosecond -> Int {
@time.nanosecond
}
fn pub inline nanosecond -> Int
Returns the number nanoseconds past the second.
Refer to the documentation of Time.sub_second
for more details.
second
Show source codeHide source code
fn pub inline second -> Int {
@time.second
}
fn pub inline second -> Int
Returns the number of seconds.
Refer to the documentation of Time.second
for more details.
time
Show source codeHide source code
fn pub inline time -> Time {
@time
}
fn pub inline time -> Time
Returns the Time
component of self
.
to_float
Show source codeHide source code
fn pub to_float -> Float {
to_int.to_float + (nanosecond.to_float / NANOS_PER_SEC)
}
fn pub to_float -> Float
Returns the timestamp since the Unix epoch, the including fractional seconds.
to_int
Show source codeHide source code
fn pub to_int -> Int {
let days = days_since_unix_epoch
let days_sec = days.absolute * SECS_PER_DAY
let time_sec = (@time.hour * SECS_PER_HOUR)
+ (@time.minute * SECS_PER_MIN)
+ @time.second
let timestamp = if days < 0 {
0 - (days_sec - time_sec)
} else {
days_sec + time_sec
}
# Timestamps are always in UTC, not in the local time.
timestamp - @utc_offset
}
fn pub to_int -> Int
Returns the number of seconds since the Unix epoch in UTC.
This value will be negative if self
is a DateTime
before the Unix
epoch.
to_iso8601
Show source codeHide source code
fn pub to_iso8601 -> String {
format('%Y-%m-%dT%H:%M:%S%f%z', English.new)
}
fn pub to_iso8601 -> String
Formats self
as an ISO 8601 date and time String
.
This method always uses the English locale.
Examples
import std.time (DateTime)
let dt = DateTime.new(
date: Date.new(2024, 12, 21).get,
time: Time.new(21, 47, 30, 123_000_000).get,
utc_offset: 3600,
)
dt.to_iso8601 # => '2024-12-21T21:47:30.123+01:00'
to_rfc2822
Show source codeHide source code
fn pub to_rfc2822 -> String {
format('%a, %d %b %Y %H:%M:%S %Z', English.new)
}
fn pub to_rfc2822 -> String
Formats self
as an RFC 2822 date and time String
.
This method always uses the English locale.
Examples
import std.time (DateTime)
let dt = DateTime.new(
date: Date.new(2024, 12, 21).get,
time: Time.new(21, 47, 30, 123_000_000).get,
utc_offset: 3600,
)
dt.to_rfc2822 # => 'Sat, 21 Dec 2024 21:47:30 +01:00'
to_utc
Show source codeHide source code
fn pub to_utc -> DateTime {
# Since our input is already valid at this point, this can never fail.
DateTime.from_timestamp(time: to_float, utc_offset: 0).get
}
fn pub to_utc -> DateTime
Converts the DateTime
to another DateTime
that uses UTC as the
timezone.
utc?
Show source codeHide source code
fn pub inline utc? -> Bool {
@utc_offset == 0
}
fn pub inline utc? -> Bool
Returns true
if UTC is used as the timezone.
utc_offset
Show source codeHide source code
fn pub inline utc_offset -> Int {
@utc_offset
}
fn pub inline utc_offset -> Int
Returns the UTC offset in seconds.
year
Show source codeHide source code
fn pub inline year -> Int {
@date.year
}
fn pub inline year -> Int
Returns the year.
Refer to the documentation of Date.year
for more details.
Implemented traits
Clone
impl Clone[DateTime] for DateTime
Compare
impl Compare[DateTime] for DateTime
Equal
impl Equal[ref DateTime] for DateTime
ToFloat
impl ToFloat for DateTime
Format
impl Format for DateTime
ToInt
impl ToInt for DateTime
Add
impl Add[Duration, DateTime] for DateTime
Subtract
impl Subtract[Duration, DateTime] for DateTime