Search results

There are no results.

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 code
Hide 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 code
Hide 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 code
Hide 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 code
Hide 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:

SequenceExamplesDescription
%Y-1234, 0001, 1970, 2024The year, zero-padded to four digits, optionally starting with a - to signal a year before year zero.
%m01, 12The month of the year (01-12), zero-padded to two digits.
%d01, 31The day of the month (01-31), zero-padded to two digits.
%H01, 12, 23The hour of the day (00-23), zero-padded to two digits.
%M01, 12, 59The minute of the hour (00-59), zero-padded to two digits.
%S01, 12, 59, 60The second of the hour (00-60), zero-padded to two digits. A value of 60 is supported to account for leap seconds.
%f.123A 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, ZThe 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, GMTThe same as %z but supports UT and GMT instead of Z for UTC offsets of zero.+02)),
%bJanThe locale-aware abbreviated name of the month. For locales that don't use abbreviated names, this is an alias for %B.
%BJanuaryThe locale-aware full name of the month.
%aMonThe abbreviated name of the day of the week. Parsed values don't affect the resulting DateTime.
%AMondayThe 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 code
Hide 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 code
Hide 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 code
Hide 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 code
Hide 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 code
Hide 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 code
Hide 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 code
Hide 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 code
Hide 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 code
Hide 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 code
Hide 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 code
Hide 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 code
Hide source code
fn pub inline date -> Date {
  @date
}
fn pub inline date -> Date

Returns the Date component of self.

day

Show source code
Hide 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 code
Hide 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 code
Hide 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 code
Hide 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 code
Hide 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 code
Hide 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 produces Z if the UTC offset is zero, otherwise it produces an offset in the format [SIGN][HH]:[MM].
  • %Z always produces GMT 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 code
Hide 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 code
Hide 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 code
Hide 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 code
Hide 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 code
Hide 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 code
Hide 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 code
Hide source code
fn pub inline time -> Time {
  @time
}
fn pub inline time -> Time

Returns the Time component of self.

to_float

Show source code
Hide 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 code
Hide 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 code
Hide 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 code
Hide 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 code
Hide 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 code
Hide 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 code
Hide 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 code
Hide 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

std.clone.

Clone

impl Clone[DateTime] for DateTime
std.cmp.

Compare

impl Compare[DateTime] for DateTime
std.cmp.

Equal

impl Equal[ref DateTime] for DateTime
std.float.

ToFloat

impl ToFloat for DateTime
std.fmt.

Format

impl Format for DateTime
std.int.

ToInt

impl ToInt for DateTime
std.ops.

Add

impl Add[Duration, DateTime] for DateTime
std.ops.

Subtract

impl Subtract[Duration, DateTime] for DateTime