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
Parsing from a TOML document is the usual path. For ad hoc values,
FromStr accepts any RFC 3339 form:
use toml_spanner::{Date, DateTime, TimeOffset};
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.offset(), Some(TimeOffset::Z));To build one from parts, use Date::new and Time::new for the
components then pick the constructor matching the TOML kind you want:
DateTime::local_date, DateTime::local_time,
DateTime::local_datetime, or DateTime::offset_datetime. The
result serializes back to its RFC 3339 form via DateTime::format:
use std::mem::MaybeUninit;
use toml_spanner::{Date, DateTime, Time, TimeOffset};
let dt = DateTime::offset_datetime(
Date::new(2026, 1, 4).unwrap(),
Time::new(12, 30, 45, 0).unwrap(),
TimeOffset::Z,
).unwrap();
let mut buf = MaybeUninit::uninit();
assert_eq!(dt.format(&mut buf), "2026-01-04T12:30:45Z");Toggle Jiff Conversions Examples
This example is kept in sync with
crates/third-party-integration-tests/src/main.rs. Edits here should be
mirrored there.
use toml_spanner::{
Arena, Date, DateTime, Error as TomlError, FromToml, Item, Key, Span as TomlSpan,
Table, Time, TimeOffset, ToToml, ToTomlError,
};
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));
};
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));
};
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(),
)),
}
}
fn from_jiff_date(date: jiff::civil::Date) -> Result<Date, ToTomlError> {
let year = date.year();
if year < 0 {
return Err(ToTomlError::from("year out of TOML range (0..=9999)"));
}
Date::new(year as u16, date.month() as u8, date.day() as u8)
.ok_or_else(|| ToTomlError::from("date out of TOML range"))
}
fn from_jiff_time(time: jiff::civil::Time) -> Result<Time, ToTomlError> {
Time::new(
time.hour() as u8,
time.minute() as u8,
time.second() as u8,
time.subsec_nanosecond() as u32,
)
.ok_or_else(|| ToTomlError::from("time out of TOML range"))
}
fn from_jiff_civil_datetime(dt: jiff::civil::DateTime) -> Result<DateTime, ToTomlError> {
Ok(DateTime::local_datetime(
from_jiff_date(dt.date())?,
from_jiff_time(dt.time())?,
))
}
fn from_jiff_timestamp(ts: jiff::Timestamp) -> Result<DateTime, ToTomlError> {
let civil = ts.to_zoned(jiff::tz::TimeZone::UTC).datetime();
Ok(DateTime::offset_datetime(
from_jiff_date(civil.date())?,
from_jiff_time(civil.time())?,
TimeOffset::Z,
)
.expect("TimeOffset::Z is always valid"))
}
#[derive(Debug, PartialEq)]
pub struct TimeConfig {
pub date: jiff::civil::Date,
pub datetime: jiff::civil::DateTime,
pub timestamp: jiff::Timestamp,
}
impl<'de> FromToml<'de> for TimeConfig {
fn from_toml(
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)
}
}
impl ToToml for TimeConfig {
fn to_toml<'a>(&'a self, arena: &'a Arena) -> Result<Item<'a>, ToTomlError> {
let Some(mut table) = Table::try_with_capacity(3, arena) else {
return Err(ToTomlError::from("table capacity exceeded"));
};
table.insert_unique(
Key::new("date"),
Item::from(DateTime::local_date(from_jiff_date(self.date)?)),
arena,
);
table.insert_unique(
Key::new("datetime"),
Item::from(from_jiff_civil_datetime(self.datetime)?),
arena,
);
table.insert_unique(
Key::new("timestamp"),
Item::from(from_jiff_timestamp(self.timestamp)?),
arena,
);
Ok(table.into_item())
}
}
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 doc = toml_spanner::parse(toml_doc, &arena).unwrap();
let config: TimeConfig = doc.to().unwrap();
let emitted = toml_spanner::to_string(&config).unwrap();
let round_trip_arena = toml_spanner::Arena::new();
let mut round_trip_doc = toml_spanner::parse(&emitted, &round_trip_arena).unwrap();
let round_trip: TimeConfig = round_trip_doc.to().unwrap();
assert_eq!(config, round_trip);
}Implementations§
Source§impl DateTime
impl DateTime
Sourcepub const MAX_FORMAT_LEN: usize = 40
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.
Sourcepub fn local_date(date: Date) -> DateTime
pub fn local_date(date: Date) -> DateTime
Builds a DateTime holding only a calendar date.
Serialized as a TOML local date.
§Examples
use toml_spanner::{Date, DateTime};
let dt = DateTime::local_date(Date::new(2026, 3, 15).unwrap());
assert_eq!(dt.date().unwrap().month, 3);
assert!(dt.time().is_none());Sourcepub fn local_time(time: Time) -> DateTime
pub fn local_time(time: Time) -> DateTime
Builds a DateTime holding only a time of day.
Serialized as a TOML local time.
§Examples
use toml_spanner::{Time, DateTime};
let dt = DateTime::local_time(Time::new(14, 30, 5, 0).unwrap());
assert_eq!(dt.time().unwrap().hour, 14);
assert!(dt.date().is_none());Sourcepub fn local_datetime(date: Date, time: Time) -> DateTime
pub fn local_datetime(date: Date, time: Time) -> DateTime
Builds a DateTime holding a date and time without a UTC offset.
Serialized as a TOML local date-time. Use this for wall-clock values whose timezone is implied by context rather than recorded in the data.
§Examples
use toml_spanner::{Date, Time, DateTime};
let dt = DateTime::local_datetime(
Date::new(2026, 3, 15).unwrap(),
Time::new(14, 30, 5, 0).unwrap(),
);
assert_eq!(dt.date().unwrap().day, 15);
assert!(dt.offset().is_none());Sourcepub fn offset_datetime(
date: Date,
time: Time,
offset: TimeOffset,
) -> Option<DateTime>
pub fn offset_datetime( date: Date, time: Time, offset: TimeOffset, ) -> Option<DateTime>
Builds a DateTime holding a date, time, and UTC offset.
Serialized as a TOML offset date-time, the form to use when the value refers to an absolute moment in time.
Returns None when offset is a TimeOffset::Custom whose
minutes fall outside ±23:59. TimeOffset::Z always succeeds.
§Examples
use toml_spanner::{Date, Time, DateTime, TimeOffset};
let dt = DateTime::offset_datetime(
Date::new(2026, 3, 15).unwrap(),
Time::new(14, 30, 5, 0).unwrap(),
TimeOffset::Z,
).unwrap();
assert_eq!(dt.offset(), Some(TimeOffset::Z));
assert!(DateTime::offset_datetime(
Date::new(2026, 3, 15).unwrap(),
Time::new(14, 30, 5, 0).unwrap(),
TimeOffset::Custom { minutes: 1440 },
).is_none());Sourcepub fn format<'a>(&self, buf: &'a mut MaybeUninit<[u8; 40]>) -> &'a str
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);Sourcepub fn offset(&self) -> Option<TimeOffset>
pub fn offset(&self) -> Option<TimeOffset>
Returns the UTC offset, or None for local date-times and local times.
Trait Implementations§
Source§impl ToToml for DateTime
Available on crate feature to-toml only.
impl ToToml for DateTime
to-toml only.