use core::time::Duration;
use crate::{
civil::{Date, DateTime, ISOWeekDate, Time},
error::{fmt::temporal::Error as E, Error},
fmt::{
buffer::{ArrayBuffer, BorrowedBuffer, BorrowedWriter},
temporal::{Pieces, PiecesOffset, TimeZoneAnnotationKind},
Write,
},
span::{Span, UnitSet},
tz::{Offset, TimeZone},
SignedDuration, Timestamp, Unit, Zoned,
};
const REASONABLE_ZONED_LEN: usize = 72;
const REASONABLE_PIECES_LEN: usize = 73;
const MAX_TIMESTAMP_ZULU_LEN: usize = 33;
const MAX_TIMESTAMP_OFFSET_LEN: usize = 38;
const MAX_DATETIME_LEN: usize = 32;
const MAX_DATE_LEN: usize = 13;
const MAX_TIME_LEN: usize = 18;
const MAX_OFFSET_LEN: usize = 9;
const MAX_ISO_WEEK_DATE_LEN: usize = 13;
const MAX_SPAN_LEN: usize = 78;
const MAX_DURATION_LEN: usize = 35;
#[derive(Clone, Debug)]
pub(super) struct DateTimePrinter {
lowercase: bool,
separator: u8,
precision: Option<u8>,
}
impl DateTimePrinter {
pub(super) const fn new() -> DateTimePrinter {
DateTimePrinter { lowercase: false, separator: b'T', precision: None }
}
pub(super) const fn lowercase(self, yes: bool) -> DateTimePrinter {
DateTimePrinter { lowercase: yes, ..self }
}
pub(super) const fn separator(self, ascii_char: u8) -> DateTimePrinter {
assert!(ascii_char.is_ascii(), "RFC3339 separator must be ASCII");
DateTimePrinter { separator: ascii_char, ..self }
}
pub(super) const fn precision(
self,
precision: Option<u8>,
) -> DateTimePrinter {
DateTimePrinter { precision, ..self }
}
pub(super) fn print_zoned(
&self,
zdt: &Zoned,
wtr: &mut dyn Write,
) -> Result<(), Error> {
const BASE: usize = 19 + 6 + 2;
let mut runtime_allocation = BASE
+ zdt.time_zone().iana_name().map(|name| name.len()).unwrap_or(11);
if zdt.year() < 0 {
runtime_allocation += 3;
}
if zdt.subsec_nanosecond() != 0 || self.precision.is_some() {
runtime_allocation += 10;
}
if runtime_allocation <= REASONABLE_ZONED_LEN {
return BorrowedBuffer::with_writer::<REASONABLE_ZONED_LEN>(
wtr,
runtime_allocation,
|bbuf| Ok(self.print_zoned_buf(zdt, bbuf)),
);
}
let mut buf = ArrayBuffer::<REASONABLE_ZONED_LEN>::default();
let mut bbuf = buf.as_borrowed();
let mut wtr = BorrowedWriter::new(&mut bbuf, wtr);
self.print_zoned_wtr(zdt, &mut wtr)?;
wtr.finish()
}
fn print_zoned_buf(&self, zdt: &Zoned, bbuf: &mut BorrowedBuffer<'_>) {
self.print_datetime_buf(&zdt.datetime(), bbuf);
let tz = zdt.time_zone();
if tz.is_unknown() {
bbuf.write_str("Z[Etc/Unknown]");
} else {
self.print_offset_rounded_buf(&zdt.offset(), bbuf);
self.print_time_zone_annotation_buf(&tz, &zdt.offset(), bbuf);
}
}
fn print_zoned_wtr(
&self,
zdt: &Zoned,
wtr: &mut BorrowedWriter<'_, '_, '_>,
) -> Result<(), Error> {
self.print_datetime_wtr(&zdt.datetime(), wtr)?;
let tz = zdt.time_zone();
if tz.is_unknown() {
wtr.write_str("Z[Etc/Unknown]")?;
} else {
self.print_offset_rounded_wtr(&zdt.offset(), wtr)?;
self.print_time_zone_annotation_wtr(&tz, &zdt.offset(), wtr)?;
}
Ok(())
}
pub(super) fn print_timestamp(
&self,
timestamp: &Timestamp,
wtr: &mut dyn Write,
) -> Result<(), Error> {
let mut runtime_allocation = MAX_TIMESTAMP_ZULU_LEN;
if timestamp.subsec_nanosecond() == 0 && self.precision.is_none() {
runtime_allocation -= 10;
}
BorrowedBuffer::with_writer::<MAX_TIMESTAMP_ZULU_LEN>(
wtr,
runtime_allocation,
|bbuf| Ok(self.print_timestamp_buf(timestamp, bbuf)),
)
}
fn print_timestamp_buf(
&self,
timestamp: &Timestamp,
bbuf: &mut BorrowedBuffer<'_>,
) {
let dt = Offset::UTC.to_datetime(*timestamp);
self.print_datetime_buf(&dt, bbuf);
self.print_zulu_buf(bbuf);
}
pub(super) fn print_timestamp_with_offset(
&self,
timestamp: &Timestamp,
offset: Offset,
wtr: &mut dyn Write,
) -> Result<(), Error> {
let mut runtime_allocation = MAX_TIMESTAMP_OFFSET_LEN;
if timestamp.subsec_nanosecond() == 0 && self.precision.is_none() {
runtime_allocation -= 10;
}
BorrowedBuffer::with_writer::<MAX_TIMESTAMP_OFFSET_LEN>(
wtr,
runtime_allocation,
|bbuf| {
Ok(self
.print_timestamp_with_offset_buf(timestamp, offset, bbuf))
},
)
}
fn print_timestamp_with_offset_buf(
&self,
timestamp: &Timestamp,
offset: Offset,
bbuf: &mut BorrowedBuffer<'_>,
) {
let dt = offset.to_datetime(*timestamp);
self.print_datetime_buf(&dt, bbuf);
self.print_offset_rounded_buf(&offset, bbuf);
}
pub(super) fn print_datetime(
&self,
dt: &DateTime,
wtr: &mut dyn Write,
) -> Result<(), Error> {
let mut runtime_allocation = MAX_DATETIME_LEN;
if dt.subsec_nanosecond() == 0 && self.precision.is_none() {
runtime_allocation -= 10;
}
BorrowedBuffer::with_writer::<MAX_DATETIME_LEN>(
wtr,
runtime_allocation,
|bbuf| Ok(self.print_datetime_buf(dt, bbuf)),
)
}
fn print_datetime_buf(
&self,
dt: &DateTime,
bbuf: &mut BorrowedBuffer<'_>,
) {
self.print_date_buf(&dt.date(), bbuf);
bbuf.write_ascii_char(if self.lowercase {
self.separator.to_ascii_lowercase()
} else {
self.separator
});
self.print_time_buf(&dt.time(), bbuf);
}
fn print_datetime_wtr(
&self,
dt: &DateTime,
wtr: &mut BorrowedWriter<'_, '_, '_>,
) -> Result<(), Error> {
self.print_date_wtr(&dt.date(), wtr)?;
wtr.write_ascii_char(if self.lowercase {
self.separator.to_ascii_lowercase()
} else {
self.separator
})?;
self.print_time_wtr(&dt.time(), wtr)?;
Ok(())
}
pub(super) fn print_date(
&self,
date: &Date,
wtr: &mut dyn Write,
) -> Result<(), Error> {
BorrowedBuffer::with_writer::<MAX_DATE_LEN>(
wtr,
MAX_DATE_LEN,
|bbuf| Ok(self.print_date_buf(date, bbuf)),
)
}
fn print_date_buf(&self, date: &Date, bbuf: &mut BorrowedBuffer<'_>) {
let year = date.year();
if year < 0 {
bbuf.write_str("-00");
}
bbuf.write_int_pad4(year.unsigned_abs());
bbuf.write_ascii_char(b'-');
bbuf.write_int_pad2(date.month().unsigned_abs());
bbuf.write_ascii_char(b'-');
bbuf.write_int_pad2(date.day().unsigned_abs());
}
fn print_date_wtr(
&self,
date: &Date,
wtr: &mut BorrowedWriter<'_, '_, '_>,
) -> Result<(), Error> {
let year = date.year();
if year < 0 {
wtr.write_str("-00")?;
}
wtr.write_int_pad4(year.unsigned_abs())?;
wtr.write_ascii_char(b'-')?;
wtr.write_int_pad2(date.month().unsigned_abs())?;
wtr.write_ascii_char(b'-')?;
wtr.write_int_pad2(date.day().unsigned_abs())?;
Ok(())
}
pub(super) fn print_time(
&self,
time: &Time,
wtr: &mut dyn Write,
) -> Result<(), Error> {
let mut runtime_allocation = MAX_TIME_LEN;
if time.subsec_nanosecond() == 0 && self.precision.is_none() {
runtime_allocation -= 10;
}
BorrowedBuffer::with_writer::<MAX_TIME_LEN>(
wtr,
runtime_allocation,
|bbuf| Ok(self.print_time_buf(time, bbuf)),
)
}
fn print_time_buf(&self, time: &Time, bbuf: &mut BorrowedBuffer<'_>) {
bbuf.write_int_pad2(time.hour().unsigned_abs());
bbuf.write_ascii_char(b':');
bbuf.write_int_pad2(time.minute().unsigned_abs());
bbuf.write_ascii_char(b':');
bbuf.write_int_pad2(time.second().unsigned_abs());
let fractional_nanosecond = time.subsec_nanosecond();
if self.precision.map_or(fractional_nanosecond != 0, |p| p > 0) {
bbuf.write_ascii_char(b'.');
bbuf.write_fraction(
self.precision,
fractional_nanosecond.unsigned_abs(),
);
}
}
fn print_time_wtr(
&self,
time: &Time,
wtr: &mut BorrowedWriter<'_, '_, '_>,
) -> Result<(), Error> {
wtr.write_int_pad2(time.hour().unsigned_abs())?;
wtr.write_ascii_char(b':')?;
wtr.write_int_pad2(time.minute().unsigned_abs())?;
wtr.write_ascii_char(b':')?;
wtr.write_int_pad2(time.second().unsigned_abs())?;
let fractional_nanosecond = time.subsec_nanosecond();
if self.precision.map_or(fractional_nanosecond != 0, |p| p > 0) {
wtr.write_ascii_char(b'.')?;
wtr.write_fraction(
self.precision,
fractional_nanosecond.unsigned_abs(),
)?;
}
Ok(())
}
pub(super) fn print_time_zone<W: Write>(
&self,
tz: &TimeZone,
mut wtr: W,
) -> Result<(), Error> {
self.print_time_zone_wtr(tz, &mut wtr)
}
fn print_time_zone_wtr(
&self,
tz: &TimeZone,
wtr: &mut dyn Write,
) -> Result<(), Error> {
if let Some(iana_name) = tz.iana_name() {
return wtr.write_str(iana_name);
}
if tz.is_unknown() {
return wtr.write_str("Etc/Unknown");
}
if let Ok(offset) = tz.to_fixed_offset() {
let mut buf = ArrayBuffer::<MAX_OFFSET_LEN>::default();
let mut bbuf = buf.as_borrowed();
self.print_offset_full_precision(&offset, &mut bbuf);
return wtr.write_str(bbuf.filled());
}
if let Some(posix_tz) = tz.posix_tz() {
use core::fmt::Write as _;
return write!(crate::fmt::StdFmtWrite(wtr), "{posix_tz}")
.map_err(|_| {
Error::from(crate::error::fmt::Error::StdFmtWriteAdapter)
});
}
Err(Error::from(E::PrintTimeZoneFailure))
}
pub(super) fn print_pieces<W: Write>(
&self,
pieces: &Pieces,
mut wtr: W,
) -> Result<(), Error> {
let mut buf = ArrayBuffer::<REASONABLE_PIECES_LEN>::default();
let mut bbuf = buf.as_borrowed();
let mut wtr = BorrowedWriter::new(&mut bbuf, &mut wtr);
self.print_pieces_wtr(pieces, &mut wtr)?;
wtr.finish()
}
fn print_pieces_wtr(
&self,
pieces: &Pieces,
wtr: &mut BorrowedWriter<'_, '_, '_>,
) -> Result<(), Error> {
if let Some(time) = pieces.time() {
let dt = DateTime::from_parts(pieces.date(), time);
self.print_datetime_wtr(&dt, wtr)?;
if let Some(poffset) = pieces.offset() {
self.print_pieces_offset(&poffset, wtr)?;
}
} else if let Some(poffset) = pieces.offset() {
let dt = DateTime::from_parts(pieces.date(), Time::midnight());
self.print_datetime_wtr(&dt, wtr)?;
self.print_pieces_offset(&poffset, wtr)?;
} else {
self.print_date_wtr(&pieces.date(), wtr)?;
}
if let Some(ann) = pieces.time_zone_annotation() {
wtr.write_ascii_char(b'[')?;
if ann.is_critical() {
wtr.write_ascii_char(b'!')?;
}
match *ann.kind() {
TimeZoneAnnotationKind::Named(ref name) => {
wtr.write_str(name.as_str())?;
}
TimeZoneAnnotationKind::Offset(offset) => {
self.print_offset_rounded_wtr(&offset, wtr)?;
}
}
wtr.write_ascii_char(b']')?;
}
Ok(())
}
pub(super) fn print_iso_week_date(
&self,
iso_week_date: &ISOWeekDate,
wtr: &mut dyn Write,
) -> Result<(), Error> {
BorrowedBuffer::with_writer::<MAX_ISO_WEEK_DATE_LEN>(
wtr,
MAX_ISO_WEEK_DATE_LEN,
|bbuf| Ok(self.print_iso_week_date_buf(iso_week_date, bbuf)),
)
}
fn print_iso_week_date_buf(
&self,
iso_week_date: &ISOWeekDate,
bbuf: &mut BorrowedBuffer<'_>,
) {
let year = iso_week_date.year();
if year < 0 {
bbuf.write_str("-00");
}
bbuf.write_int_pad4(year.unsigned_abs());
bbuf.write_ascii_char(b'-');
bbuf.write_ascii_char(if self.lowercase { b'w' } else { b'W' });
bbuf.write_int_pad2(iso_week_date.week().unsigned_abs());
bbuf.write_ascii_char(b'-');
bbuf.write_int1(
iso_week_date.weekday().to_monday_one_offset().unsigned_abs(),
);
}
fn print_pieces_offset(
&self,
poffset: &PiecesOffset,
wtr: &mut BorrowedWriter<'_, '_, '_>,
) -> Result<(), Error> {
match *poffset {
PiecesOffset::Zulu => self.print_zulu_wtr(wtr),
PiecesOffset::Numeric(ref noffset) => {
if noffset.offset().is_zero() && noffset.is_negative() {
wtr.write_str("-00:00")
} else {
self.print_offset_rounded_wtr(&noffset.offset(), wtr)
}
}
}
}
fn print_offset_rounded_buf(
&self,
offset: &Offset,
bbuf: &mut BorrowedBuffer<'_>,
) {
bbuf.write_ascii_char(if offset.is_negative() { b'-' } else { b'+' });
let (offset_hours, offset_minutes) = offset.round_to_nearest_minute();
bbuf.write_int_pad2(offset_hours);
bbuf.write_ascii_char(b':');
bbuf.write_int_pad2(offset_minutes);
}
fn print_offset_rounded_wtr(
&self,
offset: &Offset,
wtr: &mut BorrowedWriter<'_, '_, '_>,
) -> Result<(), Error> {
wtr.write_ascii_char(if offset.is_negative() { b'-' } else { b'+' })?;
let (offset_hours, offset_minutes) = offset.round_to_nearest_minute();
wtr.write_int_pad2(offset_hours)?;
wtr.write_ascii_char(b':')?;
wtr.write_int_pad2(offset_minutes)?;
Ok(())
}
fn print_offset_full_precision(
&self,
offset: &Offset,
bbuf: &mut BorrowedBuffer<'_>,
) {
bbuf.write_ascii_char(if offset.is_negative() { b'-' } else { b'+' });
let hours = offset.part_hours().unsigned_abs();
let minutes = offset.part_minutes().unsigned_abs();
let seconds = offset.part_seconds().unsigned_abs();
bbuf.write_int_pad2(hours);
bbuf.write_ascii_char(b':');
bbuf.write_int_pad2(minutes);
if seconds > 0 {
bbuf.write_ascii_char(b':');
bbuf.write_int_pad2(seconds);
}
}
fn print_zulu_buf(&self, bbuf: &mut BorrowedBuffer<'_>) {
bbuf.write_ascii_char(if self.lowercase { b'z' } else { b'Z' });
}
fn print_zulu_wtr(
&self,
wtr: &mut BorrowedWriter<'_, '_, '_>,
) -> Result<(), Error> {
wtr.write_ascii_char(if self.lowercase { b'z' } else { b'Z' })
}
fn print_time_zone_annotation_buf(
&self,
time_zone: &TimeZone,
offset: &Offset,
bbuf: &mut BorrowedBuffer<'_>,
) {
bbuf.write_ascii_char(b'[');
if let Some(iana_name) = time_zone.iana_name() {
bbuf.write_str(iana_name);
} else {
self.print_offset_rounded_buf(offset, bbuf);
}
bbuf.write_ascii_char(b']');
}
fn print_time_zone_annotation_wtr(
&self,
time_zone: &TimeZone,
offset: &Offset,
wtr: &mut BorrowedWriter<'_, '_, '_>,
) -> Result<(), Error> {
wtr.write_ascii_char(b'[')?;
if let Some(iana_name) = time_zone.iana_name() {
wtr.write_str(iana_name)?;
} else {
self.print_offset_rounded_wtr(offset, wtr)?;
}
wtr.write_ascii_char(b']')?;
Ok(())
}
}
impl Default for DateTimePrinter {
fn default() -> DateTimePrinter {
DateTimePrinter::new()
}
}
#[derive(Debug)]
pub(super) struct SpanPrinter {
designators: &'static Designators,
}
impl SpanPrinter {
pub(super) const fn new() -> SpanPrinter {
SpanPrinter { designators: DESIGNATORS_UPPERCASE }
}
pub(super) const fn lowercase(self, yes: bool) -> SpanPrinter {
SpanPrinter {
designators: if yes {
DESIGNATORS_LOWERCASE
} else {
DESIGNATORS_UPPERCASE
},
}
}
pub(super) fn print_span<W: Write>(
&self,
span: &Span,
mut wtr: W,
) -> Result<(), Error> {
let mut buf = ArrayBuffer::<MAX_SPAN_LEN>::default();
let mut bbuf = buf.as_borrowed();
self.print_span_impl(span, &mut bbuf);
wtr.write_str(bbuf.filled())
}
fn print_span_impl(&self, span: &Span, bbuf: &mut BorrowedBuffer<'_>) {
static SUBSECOND: UnitSet = UnitSet::from_slice(&[
Unit::Millisecond,
Unit::Microsecond,
Unit::Nanosecond,
]);
if span.is_negative() {
bbuf.write_ascii_char(b'-');
}
bbuf.write_ascii_char(b'P');
let units = span.units();
if units.contains(Unit::Year) {
bbuf.write_int(span.get_years_unsigned());
bbuf.write_ascii_char(self.label(Unit::Year));
}
if units.contains(Unit::Month) {
bbuf.write_int(span.get_months_unsigned());
bbuf.write_ascii_char(self.label(Unit::Month));
}
if units.contains(Unit::Week) {
bbuf.write_int(span.get_weeks_unsigned());
bbuf.write_ascii_char(self.label(Unit::Week));
}
if units.contains(Unit::Day) {
bbuf.write_int(span.get_days_unsigned());
bbuf.write_ascii_char(self.label(Unit::Day));
}
if units.only_time().is_empty() {
if units.only_calendar().is_empty() {
bbuf.write_ascii_char(b'T');
bbuf.write_ascii_char(b'0');
bbuf.write_ascii_char(self.label(Unit::Second));
}
return;
}
bbuf.write_ascii_char(b'T');
if units.contains(Unit::Hour) {
bbuf.write_int(span.get_hours_unsigned());
bbuf.write_ascii_char(self.label(Unit::Hour));
}
if units.contains(Unit::Minute) {
bbuf.write_int(span.get_minutes_unsigned());
bbuf.write_ascii_char(self.label(Unit::Minute));
}
let has_subsecond = !units.intersection(SUBSECOND).is_empty();
if units.contains(Unit::Second) && !has_subsecond {
bbuf.write_int(span.get_seconds_unsigned());
bbuf.write_ascii_char(self.label(Unit::Second));
} else if has_subsecond {
let (seconds, millis, micros, nanos) = (
Duration::from_secs(span.get_seconds_unsigned()),
Duration::from_millis(span.get_milliseconds_unsigned()),
Duration::from_micros(span.get_microseconds_unsigned()),
Duration::from_nanos(span.get_nanoseconds_unsigned()),
);
let total = seconds + millis + micros + nanos;
let (secs, subsecs) = (total.as_secs(), total.subsec_nanos());
bbuf.write_int(secs);
if subsecs != 0 {
bbuf.write_ascii_char(b'.');
bbuf.write_fraction(None, subsecs);
}
bbuf.write_ascii_char(self.label(Unit::Second));
}
}
pub(super) fn print_signed_duration<W: Write>(
&self,
dur: &SignedDuration,
mut wtr: W,
) -> Result<(), Error> {
let mut buf = ArrayBuffer::<MAX_DURATION_LEN>::default();
let mut bbuf = buf.as_borrowed();
if dur.is_negative() {
bbuf.write_ascii_char(b'-');
}
self.print_unsigned_duration_impl(&dur.unsigned_abs(), &mut bbuf);
wtr.write_str(bbuf.filled())
}
pub(super) fn print_unsigned_duration<W: Write>(
&self,
dur: &Duration,
mut wtr: W,
) -> Result<(), Error> {
let mut buf = ArrayBuffer::<MAX_DURATION_LEN>::default();
let mut bbuf = buf.as_borrowed();
self.print_unsigned_duration_impl(dur, &mut bbuf);
wtr.write_str(bbuf.filled())
}
fn print_unsigned_duration_impl(
&self,
dur: &Duration,
bbuf: &mut BorrowedBuffer<'_>,
) {
bbuf.write_ascii_char(b'P');
bbuf.write_ascii_char(b'T');
let (mut secs, nanos) = (dur.as_secs(), dur.subsec_nanos());
let non_zero_greater_than_second = secs >= 60;
if non_zero_greater_than_second {
let hours = secs / (60 * 60);
secs %= 60 * 60;
let minutes = secs / 60;
secs = secs % 60;
if hours != 0 {
bbuf.write_int(hours);
bbuf.write_ascii_char(self.label(Unit::Hour));
}
if minutes != 0 {
bbuf.write_int(minutes);
bbuf.write_ascii_char(self.label(Unit::Minute));
}
}
if !non_zero_greater_than_second || secs != 0 || nanos != 0 {
bbuf.write_int(secs);
if nanos != 0 {
bbuf.write_ascii_char(b'.');
bbuf.write_fraction(None, nanos);
}
bbuf.write_ascii_char(self.label(Unit::Second));
}
}
fn label(&self, unit: Unit) -> u8 {
self.designators.designator(unit)
}
}
#[derive(Clone, Debug)]
struct Designators {
map: [u8; 10],
}
const DESIGNATORS_UPPERCASE: &'static Designators = &Designators {
map: [0, 0, 0, b'S', b'M', b'H', b'D', b'W', b'M', b'Y'],
};
const DESIGNATORS_LOWERCASE: &'static Designators = &Designators {
map: [0, 0, 0, b's', b'm', b'h', b'd', b'w', b'm', b'y'],
};
impl Designators {
fn designator(&self, unit: Unit) -> u8 {
self.map[unit as usize]
}
}
#[cfg(feature = "alloc")]
#[cfg(test)]
mod tests {
use alloc::string::{String, ToString};
use crate::{
civil::{date, time, Weekday},
fmt::StdFmtWrite,
span::ToSpan,
util::b,
};
use super::*;
#[test]
fn print_zoned() {
if crate::tz::db().is_definitively_empty() {
return;
}
let p = || DateTimePrinter::new();
let via_io = |dtp: DateTimePrinter, zdt| {
let mut buf = String::new();
dtp.print_zoned(&zdt, &mut StdFmtWrite(&mut buf)).unwrap();
buf
};
let to_string = |dtp: DateTimePrinter, zdt| {
let mut buf = String::new();
dtp.print_zoned(&zdt, &mut buf).unwrap();
let got_via_io = via_io(dtp, zdt);
assert_eq!(
buf, got_via_io,
"expected writes to `&mut String` to match `&mut StdFmtWrite`"
);
buf
};
let dt = date(2024, 3, 10).at(5, 34, 45, 0);
let zdt = dt.in_tz("America/New_York").unwrap();
let got = to_string(p(), zdt);
assert_eq!(got, "2024-03-10T05:34:45-04:00[America/New_York]");
let dt = date(2024, 3, 10).at(5, 34, 45, 0);
let zdt = dt.in_tz("America/New_York").unwrap();
let zdt = zdt.with_time_zone(TimeZone::UTC);
let got = to_string(p(), zdt);
assert_eq!(got, "2024-03-10T09:34:45+00:00[UTC]");
let dt = date(2024, 3, 10).at(5, 34, 45, 0);
let zdt = dt.to_zoned(TimeZone::fixed(Offset::MIN)).unwrap();
let got = to_string(p(), zdt);
assert_eq!(got, "2024-03-10T05:34:45-25:59[-25:59]");
let dt = date(2024, 3, 10).at(5, 34, 45, 0);
let zdt = dt.to_zoned(TimeZone::fixed(Offset::MAX)).unwrap();
let got = to_string(p(), zdt);
assert_eq!(got, "2024-03-10T05:34:45+25:59[+25:59]");
let dt = date(2024, 3, 10).at(5, 34, 45, 123_456_789);
let zdt = dt.in_tz("America/New_York").unwrap();
let got = to_string(p(), zdt);
assert_eq!(
got,
"2024-03-10T05:34:45.123456789-04:00[America/New_York]"
);
let dt = date(2024, 3, 10).at(5, 34, 45, 0);
let zdt = dt.in_tz("America/New_York").unwrap();
let got = to_string(p().precision(Some(9)), zdt);
assert_eq!(
got,
"2024-03-10T05:34:45.000000000-04:00[America/New_York]"
);
let dt = date(-9999, 3, 1).at(23, 59, 59, 999_999_999);
let zdt = dt.in_tz("America/Argentina/ComodRivadavia").unwrap();
let got = to_string(p().precision(Some(9)), zdt);
assert_eq!(
got,
"-009999-03-01T23:59:59.999999999-04:23[America/Argentina/ComodRivadavia]",
);
let tz = TimeZone::tzif(
"Abc/Def/Ghi/Jkl/Mno/Pqr/Stu/Vwx/Yzz/Abc/Def/Ghi/Jkl/Mno/Pqr/Stu/Vwx/Yzz/Abc/Def/Ghi/Jkl/Mno/Pqr/Stu/Vwx/Yzz",
crate::tz::testdata::TzifTestFile::get("America/New_York").data,
).unwrap();
let dt = date(-9999, 3, 1).at(23, 59, 59, 999_999_999);
let zdt = dt.to_zoned(tz).unwrap();
let got = to_string(p().precision(Some(9)), zdt);
assert_eq!(
got,
"-009999-03-01T23:59:59.999999999-04:56[Abc/Def/Ghi/Jkl/Mno/Pqr/Stu/Vwx/Yzz/Abc/Def/Ghi/Jkl/Mno/Pqr/Stu/Vwx/Yzz/Abc/Def/Ghi/Jkl/Mno/Pqr/Stu/Vwx/Yzz]",
);
}
#[test]
fn print_timestamp_zulu() {
let p = || DateTimePrinter::new();
let via_io = |dtp: DateTimePrinter, ts| {
let mut buf = String::new();
dtp.print_timestamp(&ts, &mut StdFmtWrite(&mut buf)).unwrap();
buf
};
let to_string = |dtp: DateTimePrinter, ts| {
let mut buf = String::new();
dtp.print_timestamp(&ts, &mut buf).unwrap();
let got_via_io = via_io(dtp, ts);
assert_eq!(
buf, got_via_io,
"expected writes to `&mut String` to match `&mut StdFmtWrite`"
);
buf
};
let tz = TimeZone::fixed(-Offset::constant(4));
let dt = date(2024, 3, 10).at(5, 34, 45, 0);
let zoned: Zoned = dt.to_zoned(tz.clone()).unwrap();
let got = to_string(p(), zoned.timestamp());
assert_eq!(got, "2024-03-10T09:34:45Z");
let dt = date(-2024, 3, 10).at(5, 34, 45, 0);
let zoned: Zoned = dt.to_zoned(tz.clone()).unwrap();
let got = to_string(p(), zoned.timestamp());
assert_eq!(got, "-002024-03-10T09:34:45Z");
let dt = date(2024, 3, 10).at(5, 34, 45, 123_456_789);
let zoned: Zoned = dt.to_zoned(tz.clone()).unwrap();
let got = to_string(p(), zoned.timestamp());
assert_eq!(got, "2024-03-10T09:34:45.123456789Z");
let dt = date(2024, 3, 10).at(5, 34, 45, 0);
let zoned: Zoned = dt.to_zoned(tz.clone()).unwrap();
let got = to_string(p().precision(Some(9)), zoned.timestamp());
assert_eq!(got, "2024-03-10T09:34:45.000000000Z");
}
#[test]
fn print_timestamp_with_offset() {
let p = || DateTimePrinter::new();
let via_io = |dtp: DateTimePrinter, ts, offset| {
let mut buf = String::new();
dtp.print_timestamp_with_offset(
&ts,
offset,
&mut StdFmtWrite(&mut buf),
)
.unwrap();
buf
};
let to_string = |dtp: DateTimePrinter, ts, offset| {
let mut buf = String::new();
dtp.print_timestamp_with_offset(&ts, offset, &mut buf).unwrap();
let got_via_io = via_io(dtp, ts, offset);
assert_eq!(
buf, got_via_io,
"expected writes to `&mut String` to match `&mut StdFmtWrite`"
);
buf
};
let tz = TimeZone::fixed(-Offset::constant(4));
let dt = date(2024, 3, 10).at(5, 34, 45, 0);
let zoned: Zoned = dt.to_zoned(tz.clone()).unwrap();
let got = to_string(p(), zoned.timestamp(), zoned.offset());
assert_eq!(got, "2024-03-10T05:34:45-04:00");
let dt = date(-2024, 3, 10).at(5, 34, 45, 0);
let zoned: Zoned = dt.to_zoned(tz.clone()).unwrap();
let got = to_string(p(), zoned.timestamp(), zoned.offset());
assert_eq!(got, "-002024-03-10T05:34:45-04:00");
let dt = date(2024, 3, 10).at(5, 34, 45, 123_456_789);
let zoned: Zoned = dt.to_zoned(tz.clone()).unwrap();
let got = to_string(p(), zoned.timestamp(), zoned.offset());
assert_eq!(got, "2024-03-10T05:34:45.123456789-04:00");
let dt = date(2024, 3, 10).at(5, 34, 45, 0);
let zoned: Zoned = dt.to_zoned(tz.clone()).unwrap();
let got = to_string(
p().precision(Some(9)),
zoned.timestamp(),
zoned.offset(),
);
assert_eq!(got, "2024-03-10T05:34:45.000000000-04:00");
let dt = DateTime::MIN;
let zoned: Zoned = dt.to_zoned(TimeZone::fixed(Offset::MIN)).unwrap();
let got = to_string(
p().precision(Some(9)),
zoned.timestamp(),
zoned.offset(),
);
assert_eq!(got, "-009999-01-01T00:00:00.000000000-25:59");
}
#[test]
fn print_datetime() {
let p = || DateTimePrinter::new();
let via_io = |dtp: DateTimePrinter, dt| {
let mut buf = String::new();
dtp.print_datetime(&dt, &mut StdFmtWrite(&mut buf)).unwrap();
buf
};
let to_string = |dtp: DateTimePrinter, dt| {
let mut buf = String::new();
dtp.print_datetime(&dt, &mut buf).unwrap();
let got_via_io = via_io(dtp, dt);
assert_eq!(
buf, got_via_io,
"expected writes to `&mut String` to match `&mut StdFmtWrite`"
);
buf
};
let dt = date(2024, 3, 10).at(5, 34, 45, 0);
let got = to_string(p(), dt);
assert_eq!(got, "2024-03-10T05:34:45");
let dt = date(-2024, 3, 10).at(5, 34, 45, 0);
let got = to_string(p(), dt);
assert_eq!(got, "-002024-03-10T05:34:45");
let dt = date(2024, 3, 10).at(5, 34, 45, 123_456_789);
let got = to_string(p(), dt);
assert_eq!(got, "2024-03-10T05:34:45.123456789");
let dt = date(2024, 3, 10).at(5, 34, 45, 0);
let got = to_string(p().precision(Some(9)), dt);
assert_eq!(got, "2024-03-10T05:34:45.000000000");
let dt = DateTime::MIN;
let got = to_string(p().precision(Some(9)), dt);
assert_eq!(got, "-009999-01-01T00:00:00.000000000");
}
#[test]
fn print_date() {
let p = || DateTimePrinter::new();
let via_io = |dtp: DateTimePrinter, date| {
let mut buf = String::new();
dtp.print_date(&date, &mut StdFmtWrite(&mut buf)).unwrap();
buf
};
let to_string = |dtp: DateTimePrinter, date| {
let mut buf = String::new();
dtp.print_date(&date, &mut buf).unwrap();
let got_via_io = via_io(dtp, date);
assert_eq!(
buf, got_via_io,
"expected writes to `&mut String` to match `&mut StdFmtWrite`"
);
buf
};
let d = date(2024, 3, 10);
let got = to_string(p(), d);
assert_eq!(got, "2024-03-10");
let d = date(-2024, 3, 10);
let got = to_string(p(), d);
assert_eq!(got, "-002024-03-10");
let d = date(2024, 3, 10);
let got = to_string(p(), d);
assert_eq!(got, "2024-03-10");
let d = Date::MIN;
let got = to_string(p().precision(Some(9)), d);
assert_eq!(got, "-009999-01-01");
}
#[test]
fn print_time() {
let p = || DateTimePrinter::new();
let via_io = |dtp: DateTimePrinter, time| {
let mut buf = String::new();
dtp.print_time(&time, &mut StdFmtWrite(&mut buf)).unwrap();
buf
};
let to_string = |dtp: DateTimePrinter, time| {
let mut buf = String::new();
dtp.print_time(&time, &mut buf).unwrap();
let got_via_io = via_io(dtp, time);
assert_eq!(
buf, got_via_io,
"expected writes to `&mut String` to match `&mut StdFmtWrite`"
);
buf
};
let t = time(5, 34, 45, 0);
let got = to_string(p(), t);
assert_eq!(got, "05:34:45");
let t = time(5, 34, 45, 0);
let got = to_string(p(), t);
assert_eq!(got, "05:34:45");
let t = time(5, 34, 45, 123_456_789);
let got = to_string(p(), t);
assert_eq!(got, "05:34:45.123456789");
let t = time(5, 34, 45, 0);
let got = to_string(p().precision(Some(9)), t);
assert_eq!(got, "05:34:45.000000000");
let t = Time::MIN;
let got = to_string(p().precision(Some(9)), t);
assert_eq!(got, "00:00:00.000000000");
let t = Time::MAX;
let got = to_string(p().precision(Some(9)), t);
assert_eq!(got, "23:59:59.999999999");
}
#[test]
fn print_iso_week_date() {
let p = || DateTimePrinter::new();
let via_io = |dtp: DateTimePrinter, date| {
let mut buf = String::new();
dtp.print_iso_week_date(&date, &mut StdFmtWrite(&mut buf))
.unwrap();
buf
};
let to_string = |dtp: DateTimePrinter, date| {
let mut buf = String::new();
dtp.print_iso_week_date(&date, &mut buf).unwrap();
let got_via_io = via_io(dtp, date);
assert_eq!(
buf, got_via_io,
"expected writes to `&mut String` to match `&mut StdFmtWrite`"
);
buf
};
let d = ISOWeekDate::new(2024, 52, Weekday::Monday).unwrap();
let got = to_string(p(), d);
assert_eq!(got, "2024-W52-1");
let d = ISOWeekDate::new(2004, 1, Weekday::Sunday).unwrap();
let got = to_string(p(), d);
assert_eq!(got, "2004-W01-7");
let d = ISOWeekDate::MIN;
let got = to_string(p(), d);
assert_eq!(got, "-009999-W01-1");
let d = ISOWeekDate::MAX;
let got = to_string(p(), d);
assert_eq!(got, "9999-W52-5");
}
#[test]
fn print_pieces() {
let p = || DateTimePrinter::new();
let via_io = |dtp: DateTimePrinter, pieces| {
let mut buf = String::new();
dtp.print_pieces(&pieces, &mut StdFmtWrite(&mut buf)).unwrap();
buf
};
let to_string = |dtp: DateTimePrinter, pieces| {
let mut buf = String::new();
dtp.print_pieces(&pieces, &mut buf).unwrap();
let got_via_io = via_io(dtp, pieces);
assert_eq!(
buf, got_via_io,
"expected writes to `&mut String` to match `&mut StdFmtWrite`"
);
buf
};
let pieces = Pieces::from(date(2024, 3, 10).at(5, 34, 45, 0))
.with_offset(Offset::constant(-4))
.with_time_zone_name("America/New_York");
let got = to_string(p(), pieces);
assert_eq!(got, "2024-03-10T05:34:45-04:00[America/New_York]");
let pieces = Pieces::from(date(2024, 3, 10).at(5, 34, 45, 0))
.with_offset(Offset::UTC)
.with_time_zone_name("UTC");
let got = to_string(p(), pieces);
assert_eq!(got, "2024-03-10T05:34:45+00:00[UTC]");
let pieces = Pieces::from(date(2024, 3, 10).at(5, 34, 45, 0))
.with_offset(Offset::MIN)
.with_time_zone_offset(Offset::MIN);
let got = to_string(p(), pieces);
assert_eq!(got, "2024-03-10T05:34:45-25:59[-25:59]");
let pieces = Pieces::from(date(2024, 3, 10).at(5, 34, 45, 0))
.with_offset(Offset::MAX)
.with_time_zone_offset(Offset::MAX);
let got = to_string(p(), pieces);
assert_eq!(got, "2024-03-10T05:34:45+25:59[+25:59]");
let pieces =
Pieces::from(date(2024, 3, 10).at(5, 34, 45, 123_456_789))
.with_offset(Offset::constant(-4))
.with_time_zone_name("America/New_York");
let got = to_string(p(), pieces);
assert_eq!(
got,
"2024-03-10T05:34:45.123456789-04:00[America/New_York]"
);
let pieces = Pieces::from(date(2024, 3, 10).at(5, 34, 45, 0))
.with_offset(Offset::constant(-4))
.with_time_zone_name("America/New_York");
let got = to_string(p().precision(Some(9)), pieces);
assert_eq!(
got,
"2024-03-10T05:34:45.000000000-04:00[America/New_York]"
);
let pieces =
Pieces::from(date(-9999, 3, 1).at(23, 59, 59, 999_999_999))
.with_offset(Offset::constant(-4))
.with_time_zone_name("America/Argentina/ComodRivadavia");
let got = to_string(p().precision(Some(9)), pieces);
assert_eq!(
got,
"-009999-03-01T23:59:59.999999999-04:00[America/Argentina/ComodRivadavia]",
);
let pieces =
Pieces::parse(
"-009999-03-01T23:59:59.999999999-04:00[!America/Argentina/ComodRivadavia]",
).unwrap();
let got = to_string(p().precision(Some(9)), pieces);
assert_eq!(
got,
"-009999-03-01T23:59:59.999999999-04:00[!America/Argentina/ComodRivadavia]",
);
let name = "Abc/Def/Ghi/Jkl/Mno/Pqr/Stu/Vwx/Yzz/Abc/Def/Ghi/Jkl/Mno/Pqr/Stu/Vwx/Yzz/Abc/Def/Ghi/Jkl/Mno/Pqr/Stu/Vwx/Yzz";
let pieces =
Pieces::from(date(-9999, 3, 1).at(23, 59, 59, 999_999_999))
.with_offset(Offset::constant(-4))
.with_time_zone_name(name);
let got = to_string(p().precision(Some(9)), pieces);
assert_eq!(
got,
"-009999-03-01T23:59:59.999999999-04:00[Abc/Def/Ghi/Jkl/Mno/Pqr/Stu/Vwx/Yzz/Abc/Def/Ghi/Jkl/Mno/Pqr/Stu/Vwx/Yzz/Abc/Def/Ghi/Jkl/Mno/Pqr/Stu/Vwx/Yzz]",
);
}
#[test]
fn print_time_zone() {
let p = || DateTimePrinter::new();
let via_io = |dtp: DateTimePrinter, tz| {
let mut buf = String::new();
dtp.print_time_zone(&tz, &mut StdFmtWrite(&mut buf)).unwrap();
buf
};
let to_string = |dtp: DateTimePrinter, tz| {
let mut buf = String::new();
dtp.print_time_zone(&tz, &mut buf).unwrap();
let got_via_io = via_io(dtp, tz);
assert_eq!(
buf, got_via_io,
"expected writes to `&mut String` to match `&mut StdFmtWrite`"
);
buf
};
let tztest =
crate::tz::testdata::TzifTestFile::get("America/New_York");
let tz = TimeZone::tzif(tztest.name, tztest.data).unwrap();
let got = to_string(p(), tz);
assert_eq!(got, "America/New_York");
let tz = TimeZone::tzif(
"Abc/Def/Ghi/Jkl/Mno/Pqr/Stu/Vwx/Yzz/Abc/Def/Ghi/Jkl/Mno/Pqr/Stu/Vwx/Yzz/Abc/Def/Ghi/Jkl/Mno/Pqr/Stu/Vwx/Yzz",
tztest.data,
).unwrap();
let got = to_string(p(), tz);
assert_eq!(
got,
"Abc/Def/Ghi/Jkl/Mno/Pqr/Stu/Vwx/Yzz/Abc/Def/Ghi/Jkl/Mno/Pqr/Stu/Vwx/Yzz/Abc/Def/Ghi/Jkl/Mno/Pqr/Stu/Vwx/Yzz",
);
let tz = TimeZone::UTC;
let got = to_string(p(), tz);
assert_eq!(got, "UTC");
let tz = TimeZone::unknown();
let got = to_string(p(), tz);
assert_eq!(got, "Etc/Unknown");
let tz = TimeZone::fixed(Offset::MIN);
let got = to_string(p(), tz);
assert_eq!(got, "-25:59:59");
let tz = TimeZone::fixed(Offset::MAX);
let got = to_string(p(), tz);
assert_eq!(got, "+25:59:59");
let tz = TimeZone::posix("EST5EDT,M3.2.0,M11.1.0").unwrap();
let got = to_string(p(), tz);
assert_eq!(got, "EST5EDT,M3.2.0,M11.1.0");
let tz = TimeZone::posix(
"ABCDEFGHIJKLMNOPQRSTUVWXY5ABCDEFGHIJKLMNOPQRSTUVWXYT,M3.2.0,M11.1.0",
).unwrap();
let got = to_string(p(), tz);
assert_eq!(
got,
"ABCDEFGHIJKLMNOPQRSTUVWXY5ABCDEFGHIJKLMNOPQRSTUVWXYT,M3.2.0,M11.1.0",
);
#[cfg(feature = "tz-system")]
{
let tz = TimeZone::tzif_system(tztest.data).unwrap();
let mut buf = String::new();
let err = p().print_time_zone(&tz, &mut buf).unwrap_err();
assert_eq!(
err.to_string(),
"time zones without IANA identifiers that aren't \
either fixed offsets or a POSIX time zone can't be \
serialized (this typically occurs when this is a \
system time zone derived from `/etc/localtime` on \
Unix systems that isn't symlinked to an entry in \
`/usr/share/zoneinfo`)",
);
}
}
#[cfg(not(miri))]
#[test]
fn print_span_basic() {
let p = |span: Span| -> String {
let mut buf = String::new();
SpanPrinter::new().print_span(&span, &mut buf).unwrap();
buf
};
insta::assert_snapshot!(p(Span::new()), @"PT0S");
insta::assert_snapshot!(p(1.second()), @"PT1S");
insta::assert_snapshot!(p(-1.second()), @"-PT1S");
insta::assert_snapshot!(p(
1.second().milliseconds(1).microseconds(1).nanoseconds(1),
), @"PT1.001001001S");
insta::assert_snapshot!(p(
0.second().milliseconds(999).microseconds(999).nanoseconds(999),
), @"PT0.999999999S");
insta::assert_snapshot!(p(
1.year().months(1).weeks(1).days(1)
.hours(1).minutes(1).seconds(1)
.milliseconds(1).microseconds(1).nanoseconds(1),
), @"P1Y1M1W1DT1H1M1.001001001S");
insta::assert_snapshot!(p(
-1.year().months(1).weeks(1).days(1)
.hours(1).minutes(1).seconds(1)
.milliseconds(1).microseconds(1).nanoseconds(1),
), @"-P1Y1M1W1DT1H1M1.001001001S");
}
#[cfg(not(miri))]
#[test]
fn print_span_subsecond_positive() {
let p = |span: Span| -> String {
let mut buf = String::new();
SpanPrinter::new().print_span(&span, &mut buf).unwrap();
buf
};
insta::assert_snapshot!(p(
0.second().milliseconds(1000).microseconds(1000).nanoseconds(1000),
), @"PT1.001001S");
insta::assert_snapshot!(p(
1.second().milliseconds(1000).microseconds(1000).nanoseconds(1000),
), @"PT2.001001S");
insta::assert_snapshot!(p(
0.second()
.milliseconds(b::SpanMilliseconds::MAX),
), @"PT631107417600S");
insta::assert_snapshot!(p(
0.second()
.microseconds(b::SpanMicroseconds::MAX),
), @"PT631107417600S");
insta::assert_snapshot!(p(
0.second()
.nanoseconds(b::SpanNanoseconds::MAX),
), @"PT9223372036.854775807S");
insta::assert_snapshot!(p(
0.second()
.milliseconds(b::SpanMilliseconds::MAX)
.microseconds(999_999),
), @"PT631107417600.999999S");
insta::assert_snapshot!(p(
0.second()
.milliseconds(b::SpanMilliseconds::MAX)
.microseconds(1_000_000),
), @"PT631107417601S");
insta::assert_snapshot!(p(
0.second()
.milliseconds(b::SpanMilliseconds::MAX)
.microseconds(1_000_001),
), @"PT631107417601.000001S");
insta::assert_snapshot!(p(
0.second()
.milliseconds(b::SpanMilliseconds::MAX)
.nanoseconds(1_000_000_000),
), @"PT631107417601S");
insta::assert_snapshot!(p(
0.second()
.milliseconds(b::SpanMilliseconds::MAX)
.nanoseconds(1_000_000_001),
), @"PT631107417601.000000001S");
insta::assert_snapshot!(p(
0.second()
.milliseconds(b::SpanMilliseconds::MAX)
.microseconds(b::SpanMicroseconds::MAX)
.nanoseconds(b::SpanNanoseconds::MAX),
), @"PT1271438207236.854775807S");
insta::assert_snapshot!(p(
Span::new()
.seconds(b::SpanSeconds::MAX)
.milliseconds(b::SpanMilliseconds::MAX)
.microseconds(b::SpanMicroseconds::MAX)
.nanoseconds(b::SpanNanoseconds::MAX),
), @"PT1902545624836.854775807S");
}
#[cfg(not(miri))]
#[test]
fn print_span_subsecond_negative() {
let p = |span: Span| -> String {
let mut buf = String::new();
SpanPrinter::new().print_span(&span, &mut buf).unwrap();
buf
};
insta::assert_snapshot!(p(
-0.second().milliseconds(1000).microseconds(1000).nanoseconds(1000),
), @"-PT1.001001S");
insta::assert_snapshot!(p(
-1.second().milliseconds(1000).microseconds(1000).nanoseconds(1000),
), @"-PT2.001001S");
insta::assert_snapshot!(p(
0.second()
.milliseconds(b::SpanMilliseconds::MIN),
), @"-PT631107417600S");
insta::assert_snapshot!(p(
0.second()
.microseconds(b::SpanMicroseconds::MIN),
), @"-PT631107417600S");
insta::assert_snapshot!(p(
0.second()
.nanoseconds(b::SpanNanoseconds::MIN),
), @"-PT9223372036.854775807S");
insta::assert_snapshot!(p(
0.second()
.milliseconds(b::SpanMilliseconds::MIN)
.microseconds(999_999),
), @"-PT631107417600.999999S");
insta::assert_snapshot!(p(
0.second()
.milliseconds(b::SpanMilliseconds::MIN)
.microseconds(1_000_000),
), @"-PT631107417601S");
insta::assert_snapshot!(p(
0.second()
.milliseconds(b::SpanMilliseconds::MIN)
.microseconds(1_000_001),
), @"-PT631107417601.000001S");
insta::assert_snapshot!(p(
0.second()
.milliseconds(b::SpanMilliseconds::MIN)
.nanoseconds(1_000_000_000),
), @"-PT631107417601S");
insta::assert_snapshot!(p(
0.second()
.milliseconds(b::SpanMilliseconds::MIN)
.nanoseconds(1_000_000_001),
), @"-PT631107417601.000000001S");
insta::assert_snapshot!(p(
0.second()
.milliseconds(b::SpanMilliseconds::MIN)
.microseconds(b::SpanMicroseconds::MIN)
.nanoseconds(b::SpanNanoseconds::MIN),
), @"-PT1271438207236.854775807S");
insta::assert_snapshot!(p(
Span::new()
.seconds(b::SpanSeconds::MIN)
.milliseconds(b::SpanMilliseconds::MIN)
.microseconds(b::SpanMicroseconds::MIN)
.nanoseconds(b::SpanNanoseconds::MIN),
), @"-PT1902545624836.854775807S");
}
#[cfg(not(miri))]
#[test]
fn print_signed_duration() {
let p = |secs, nanos| -> String {
let dur = SignedDuration::new(secs, nanos);
let mut buf = String::new();
SpanPrinter::new().print_signed_duration(&dur, &mut buf).unwrap();
buf
};
insta::assert_snapshot!(p(0, 0), @"PT0S");
insta::assert_snapshot!(p(0, 1), @"PT0.000000001S");
insta::assert_snapshot!(p(1, 0), @"PT1S");
insta::assert_snapshot!(p(59, 0), @"PT59S");
insta::assert_snapshot!(p(60, 0), @"PT1M");
insta::assert_snapshot!(p(60, 1), @"PT1M0.000000001S");
insta::assert_snapshot!(p(61, 1), @"PT1M1.000000001S");
insta::assert_snapshot!(p(3_600, 0), @"PT1H");
insta::assert_snapshot!(p(3_600, 1), @"PT1H0.000000001S");
insta::assert_snapshot!(p(3_660, 0), @"PT1H1M");
insta::assert_snapshot!(p(3_660, 1), @"PT1H1M0.000000001S");
insta::assert_snapshot!(p(3_661, 0), @"PT1H1M1S");
insta::assert_snapshot!(p(3_661, 1), @"PT1H1M1.000000001S");
insta::assert_snapshot!(p(0, -1), @"-PT0.000000001S");
insta::assert_snapshot!(p(-1, 0), @"-PT1S");
insta::assert_snapshot!(p(-59, 0), @"-PT59S");
insta::assert_snapshot!(p(-60, 0), @"-PT1M");
insta::assert_snapshot!(p(-60, -1), @"-PT1M0.000000001S");
insta::assert_snapshot!(p(-61, -1), @"-PT1M1.000000001S");
insta::assert_snapshot!(p(-3_600, 0), @"-PT1H");
insta::assert_snapshot!(p(-3_600, -1), @"-PT1H0.000000001S");
insta::assert_snapshot!(p(-3_660, 0), @"-PT1H1M");
insta::assert_snapshot!(p(-3_660, -1), @"-PT1H1M0.000000001S");
insta::assert_snapshot!(p(-3_661, 0), @"-PT1H1M1S");
insta::assert_snapshot!(p(-3_661, -1), @"-PT1H1M1.000000001S");
insta::assert_snapshot!(
p(i64::MIN, -999_999_999),
@"-PT2562047788015215H30M8.999999999S",
);
insta::assert_snapshot!(
p(i64::MAX, 999_999_999),
@"PT2562047788015215H30M7.999999999S",
);
}
#[cfg(not(miri))]
#[test]
fn print_unsigned_duration() {
let p = |secs, nanos| -> String {
let dur = Duration::new(secs, nanos);
let mut buf = String::new();
SpanPrinter::new()
.print_unsigned_duration(&dur, &mut buf)
.unwrap();
buf
};
insta::assert_snapshot!(p(0, 0), @"PT0S");
insta::assert_snapshot!(p(0, 1), @"PT0.000000001S");
insta::assert_snapshot!(p(1, 0), @"PT1S");
insta::assert_snapshot!(p(59, 0), @"PT59S");
insta::assert_snapshot!(p(60, 0), @"PT1M");
insta::assert_snapshot!(p(60, 1), @"PT1M0.000000001S");
insta::assert_snapshot!(p(61, 1), @"PT1M1.000000001S");
insta::assert_snapshot!(p(3_600, 0), @"PT1H");
insta::assert_snapshot!(p(3_600, 1), @"PT1H0.000000001S");
insta::assert_snapshot!(p(3_660, 0), @"PT1H1M");
insta::assert_snapshot!(p(3_660, 1), @"PT1H1M0.000000001S");
insta::assert_snapshot!(p(3_661, 0), @"PT1H1M1S");
insta::assert_snapshot!(p(3_661, 1), @"PT1H1M1.000000001S");
insta::assert_snapshot!(
p(u64::MAX, 999_999_999),
@"PT5124095576030431H15.999999999S",
);
}
}