Skip to main content

DateTime

Struct DateTime 

Source
pub struct DateTime { /* private fields */ }
Expand description

Container for temporal values for TOML format, based on RFC 3339.

General bounds are in forced during parsing but leniently, so things like exact leap second rules are not enforced, you should generally being converting these time values, to a more complete time library like jiff before use.

The DateTime type is essentially more compact version of:

use toml_spanner::{Date, Time, TimeOffset};
struct DateTime {
    date: Option<Date>,
    time: Option<Time>,
    offset: Option<TimeOffset>,
}

For more details on support formats inside TOML documents please reference the TOML v1.1.0 Specification.

Mapping DateTime to the TOML time kinds works like the following:

#[rustfmt::skip]
fn datetime_to_toml_kind(value: &toml_spanner::DateTime) -> &'static str {
    match (value.date(),value.time(),value.offset()) {
          (Some(_date), Some(_time), Some(_offset)) => "Offset Date-Time",
          (Some(_date), Some(_time), None         ) => "Local Date-Time",
          (Some(_date), None       , None         ) => "Local Date",
          (None       , Some(_time), None         ) => "Local Time",
        _ => unreachable!("for a DateTime produced from the toml-spanner::parse"),
    }
}

§Constructing a DateTime

Generally, you should be parsing DateTime values from a TOML document, but for testing purposes, FromStr is also implemented allowing for "2026-01-04".parse::<DateTime>().

use toml_spanner::{Date, Time, TimeOffset, DateTime};
let value: DateTime = "2026-01-04T12:30:45Z".parse().unwrap();
assert_eq!(value.date(), Some(Date { year: 2026, month: 1, day: 4 }));
assert_eq!(value.time().unwrap().minute, 30);
assert_eq!(value.offset(), Some(TimeOffset::Z));
Toggle Jiff Conversions Examples
use toml_spanner::{Deserialize, Error as TomlError, Span as TomlSpan};

fn extract_date(
    datetime: &toml_spanner::DateTime,
    span: TomlSpan,
) -> Result<jiff::civil::Date, TomlError> {
    let Some(date) = datetime.date() else {
        return Err(TomlError::custom("Missing date component", span));
    };
    // toml_spanner guartees the following inclusive ranges
    // year: 0-9999, month: 1-12, day: 1-31
    // making the as casts safe.
    match jiff::civil::Date::new(date.year as i16, date.month as i8, date.day as i8) {
        Ok(value) => Ok(value),
        Err(err) => Err(TomlError::custom(format!("Invalid date: {err}"), span)),
    }
}

fn extract_time(
    datetime: &toml_spanner::DateTime,
    span: TomlSpan,
) -> Result<jiff::civil::Time, TomlError> {
    let Some(time) = datetime.time() else {
        return Err(TomlError::custom("Missing time component", span));
    };
    // toml_spanner guartees the following inclusive ranges
    // hour: 0-23, minute: 0-59, second: 0-60, nanosecond: 0-999999999
    // making the as casts safe.
    match jiff::civil::Time::new(
        time.hour as i8,
        time.minute as i8,
        time.second as i8,
        time.nanosecond as i32,
    ) {
        Ok(value) => Ok(value),
        Err(err) => Err(TomlError::custom(format!("Invalid time: {err}"), span)),
    }
}

fn extract_timezone(
    datetime: &toml_spanner::DateTime,
    span: TomlSpan,
) -> Result<jiff::tz::TimeZone, TomlError> {
    let Some(offset) = datetime.offset() else {
        return Err(TomlError::custom("Missing offset component", span));
    };
    match offset {
        toml_spanner::TimeOffset::Z => Ok(jiff::tz::TimeZone::UTC),
        toml_spanner::TimeOffset::Custom { minutes } => {
            match jiff::tz::Offset::from_seconds(minutes as i32 * 60) {
                Ok(jiff_offset) => Ok(jiff::tz::TimeZone::fixed(jiff_offset)),
                Err(err) => Err(TomlError::custom(format!("Invalid offset: {err}"), span)),
            }
        }
    }
}

fn to_jiff_date(item: &toml_spanner::Item<'_>) -> Result<jiff::civil::Date, TomlError> {
    let Some(datetime) = item.as_datetime() else {
        return Err(item.expected("date"));
    };

    if datetime.time().is_some() {
        return Err(TomlError::custom(
            "Expected lone date but found time",
            item.span(),
        ));
    };

    extract_date(datetime, item.span())
}

fn to_jiff_datetime(item: &toml_spanner::Item<'_>) -> Result<jiff::civil::DateTime, TomlError> {
    let Some(datetime) = item.as_datetime() else {
        return Err(item.expected("civil datetime"));
    };

    if datetime.offset().is_some() {
        return Err(TomlError::custom(
            "Expected naive timestamp but found offset",
            item.span(),
        ));
    };

    Ok(jiff::civil::DateTime::from_parts(
        extract_date(datetime, item.span())?,
        extract_time(datetime, item.span())?,
    ))
}

fn to_jiff_timestamp(item: &toml_spanner::Item<'_>) -> Result<jiff::Timestamp, TomlError> {
    let Some(datetime) = item.as_datetime() else {
        return Err(item.expected("timestamp"));
    };
    let civil = jiff::civil::DateTime::from_parts(
        extract_date(datetime, item.span())?,
        extract_time(datetime, item.span())?,
    );
    let timezone = extract_timezone(datetime, item.span())?;
    match timezone.to_timestamp(civil) {
        Ok(value) => Ok(value),
        Err(err) => Err(TomlError::custom(
            format!("Invalid timestamp: {err}"),
            item.span(),
        )),
    }
}

#[derive(Debug)]
pub struct TimeConfig {
    pub date: jiff::civil::Date,
    pub datetime: jiff::civil::DateTime,
    pub timestamp: jiff::Timestamp,
}

impl<'de> Deserialize<'de> for TimeConfig {
    fn deserialize(
        ctx: &mut toml_spanner::Context<'de>,
        value: &toml_spanner::Item<'de>,
    ) -> Result<Self, toml_spanner::Failed> {
        let mut th = value.table_helper(ctx)?;
        let config = TimeConfig {
            date: th.required_mapped("date", to_jiff_date)?,
            datetime: th.required_mapped("datetime", to_jiff_datetime)?,
            timestamp: th.required_mapped("timestamp", to_jiff_timestamp)?,
        };
        Ok(config)
    }
}

fn main() {
    let arena = toml_spanner::Arena::new();

    let toml_doc = r#"
        date = 1997-02-28
        datetime = 2066-01-30T14:45:00
        timestamp = 3291-12-01T00:45:00Z
    "#;
    let mut root = toml_spanner::parse(toml_doc, &arena).unwrap();
    let config: TimeConfig = root.deserialize().unwrap();
    println!("{:#?}", config);
}

Implementations§

Source§

impl DateTime

Source

pub const MAX_FORMAT_LEN: usize = 40

Maximum number of bytes produced by DateTime::format.

Use this to size the MaybeUninit buffer passed to DateTime::format.

Source

pub fn time(&self) -> Option<Time>

Returns the time component, or None for a local-date value.

Source

pub fn format<'a>(&self, buf: &'a mut MaybeUninit<[u8; 40]>) -> &'a str

Formats this datetime into the provided buffer and returns the result as a &str.

The output follows RFC 3339 formatting and matches the TOML serialization of the value. The caller must supply an uninitializebuffer of MAX_FORMAT_LEN bytes; the returned &str borrows from that buffer, starting from the beginning.

§Examples
use std::mem::MaybeUninit;
use toml_spanner::DateTime;

let dt: DateTime = "2026-01-04T12:30:45Z".parse().unwrap();
let mut buf = MaybeUninit::uninit();
assert_eq!(dt.format(&mut buf), "2026-01-04T12:30:45Z");
assert_eq!(size_of_val(&buf), DateTime::MAX_FORMAT_LEN);
Source

pub fn date(&self) -> Option<Date>

Returns the date component, or None for a local-time value.

Source

pub fn offset(&self) -> Option<TimeOffset>

Returns the UTC offset, or None for local date-times and local times.

Trait Implementations§

Source§

impl Clone for DateTime

Source§

fn clone(&self) -> DateTime

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for DateTime

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl FromStr for DateTime

Source§

type Err = DateTimeError

The associated error which can be returned from parsing.
Source§

fn from_str(s: &str) -> Result<Self, Self::Err>

Parses a string s to return a value of this type. Read more
Source§

impl Copy for DateTime

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.