#[cfg(all(not(feature = "std"), feature = "alloc"))]
use alloc::string::{String, ToString};
#[cfg(feature = "alloc")]
use core::borrow::Borrow;
#[cfg(feature = "alloc")]
use core::fmt::Display;
use core::fmt::{self, Write};
#[cfg(feature = "alloc")]
use crate::offset::Offset;
#[cfg(any(feature = "alloc", feature = "serde", feature = "rustc-serialize"))]
use crate::{Datelike, FixedOffset, NaiveDateTime, Timelike};
#[cfg(feature = "alloc")]
use crate::{NaiveDate, NaiveTime, Weekday};
#[cfg(feature = "alloc")]
use super::locales;
#[cfg(all(feature = "unstable-locales", feature = "alloc"))]
use super::Locale;
#[cfg(any(feature = "alloc", feature = "serde", feature = "rustc-serialize"))]
use super::{Colons, OffsetFormat, OffsetPrecision, Pad};
#[cfg(feature = "alloc")]
use super::{Fixed, InternalFixed, InternalInternal, Item, Numeric};
#[cfg(feature = "alloc")]
use locales::*;
#[cfg(feature = "alloc")]
#[derive(Debug)]
pub struct DelayedFormat<I> {
date: Option<NaiveDate>,
time: Option<NaiveTime>,
off: Option<(String, FixedOffset)>,
items: I,
#[cfg(feature = "unstable-locales")]
locale: Option<Locale>,
}
#[cfg(feature = "alloc")]
impl<'a, I: Iterator<Item = B> + Clone, B: Borrow<Item<'a>>> DelayedFormat<I> {
#[must_use]
pub fn new(date: Option<NaiveDate>, time: Option<NaiveTime>, items: I) -> DelayedFormat<I> {
DelayedFormat {
date,
time,
off: None,
items,
#[cfg(feature = "unstable-locales")]
locale: None,
}
}
#[must_use]
pub fn new_with_offset<Off>(
date: Option<NaiveDate>,
time: Option<NaiveTime>,
offset: &Off,
items: I,
) -> DelayedFormat<I>
where
Off: Offset + Display,
{
let name_and_diff = (offset.to_string(), offset.fix());
DelayedFormat {
date,
time,
off: Some(name_and_diff),
items,
#[cfg(feature = "unstable-locales")]
locale: None,
}
}
#[cfg(feature = "unstable-locales")]
#[must_use]
pub fn new_with_locale(
date: Option<NaiveDate>,
time: Option<NaiveTime>,
items: I,
locale: Locale,
) -> DelayedFormat<I> {
DelayedFormat { date, time, off: None, items, locale: Some(locale) }
}
#[cfg(feature = "unstable-locales")]
#[must_use]
pub fn new_with_offset_and_locale<Off>(
date: Option<NaiveDate>,
time: Option<NaiveTime>,
offset: &Off,
items: I,
locale: Locale,
) -> DelayedFormat<I>
where
Off: Offset + Display,
{
let name_and_diff = (offset.to_string(), offset.fix());
DelayedFormat { date, time, off: Some(name_and_diff), items, locale: Some(locale) }
}
}
#[cfg(feature = "alloc")]
impl<'a, I: Iterator<Item = B> + Clone, B: Borrow<Item<'a>>> Display for DelayedFormat<I> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
#[cfg(feature = "unstable-locales")]
let locale = self.locale;
#[cfg(not(feature = "unstable-locales"))]
let locale = None;
let mut result = String::new();
for item in self.items.clone() {
format_inner(
&mut result,
self.date.as_ref(),
self.time.as_ref(),
self.off.as_ref(),
item.borrow(),
locale,
)?;
}
f.pad(&result)
}
}
#[cfg(feature = "alloc")]
#[deprecated(since = "0.4.32", note = "Use DelayedFormat::fmt instead")]
pub fn format<'a, I, B>(
w: &mut fmt::Formatter,
date: Option<&NaiveDate>,
time: Option<&NaiveTime>,
off: Option<&(String, FixedOffset)>,
items: I,
) -> fmt::Result
where
I: Iterator<Item = B> + Clone,
B: Borrow<Item<'a>>,
{
DelayedFormat {
date: date.copied(),
time: time.copied(),
off: off.cloned(),
items,
#[cfg(feature = "unstable-locales")]
locale: None,
}
.fmt(w)
}
#[cfg(feature = "alloc")]
#[deprecated(since = "0.4.32", note = "Use DelayedFormat::fmt instead")]
pub fn format_item(
w: &mut fmt::Formatter,
date: Option<&NaiveDate>,
time: Option<&NaiveTime>,
off: Option<&(String, FixedOffset)>,
item: &Item<'_>,
) -> fmt::Result {
DelayedFormat {
date: date.copied(),
time: time.copied(),
off: off.cloned(),
items: [item].into_iter(),
#[cfg(feature = "unstable-locales")]
locale: None,
}
.fmt(w)
}
#[cfg(feature = "alloc")]
fn format_inner(
w: &mut impl Write,
date: Option<&NaiveDate>,
time: Option<&NaiveTime>,
off: Option<&(String, FixedOffset)>,
item: &Item<'_>,
locale: Option<Locale>,
) -> fmt::Result {
let locale = locale.unwrap_or(default_locale());
match *item {
Item::Literal(s) | Item::Space(s) => w.write_str(s),
#[cfg(feature = "alloc")]
Item::OwnedLiteral(ref s) | Item::OwnedSpace(ref s) => w.write_str(s),
Item::Numeric(ref spec, ref pad) => {
use self::Numeric::*;
let week_from_sun = |d: &NaiveDate| d.weeks_from(Weekday::Sun);
let week_from_mon = |d: &NaiveDate| d.weeks_from(Weekday::Mon);
let (width, v) = match *spec {
Year => (4, date.map(|d| i64::from(d.year()))),
YearDiv100 => (2, date.map(|d| i64::from(d.year()).div_euclid(100))),
YearMod100 => (2, date.map(|d| i64::from(d.year()).rem_euclid(100))),
IsoYear => (4, date.map(|d| i64::from(d.iso_week().year()))),
IsoYearDiv100 => (2, date.map(|d| i64::from(d.iso_week().year()).div_euclid(100))),
IsoYearMod100 => (2, date.map(|d| i64::from(d.iso_week().year()).rem_euclid(100))),
Month => (2, date.map(|d| i64::from(d.month()))),
Day => (2, date.map(|d| i64::from(d.day()))),
WeekFromSun => (2, date.map(|d| i64::from(week_from_sun(d)))),
WeekFromMon => (2, date.map(|d| i64::from(week_from_mon(d)))),
IsoWeek => (2, date.map(|d| i64::from(d.iso_week().week()))),
NumDaysFromSun => (1, date.map(|d| i64::from(d.weekday().num_days_from_sunday()))),
WeekdayFromMon => (1, date.map(|d| i64::from(d.weekday().number_from_monday()))),
Ordinal => (3, date.map(|d| i64::from(d.ordinal()))),
Hour => (2, time.map(|t| i64::from(t.hour()))),
Hour12 => (2, time.map(|t| i64::from(t.hour12().1))),
Minute => (2, time.map(|t| i64::from(t.minute()))),
Second => (2, time.map(|t| i64::from(t.second() + t.nanosecond() / 1_000_000_000))),
Nanosecond => (9, time.map(|t| i64::from(t.nanosecond() % 1_000_000_000))),
Timestamp => (
1,
match (date, time, off) {
(Some(d), Some(t), None) => Some(d.and_time(*t).and_utc().timestamp()),
(Some(d), Some(t), Some(&(_, off))) => Some(
d.and_time(*t).and_utc().timestamp() - i64::from(off.local_minus_utc()),
),
(_, _, _) => None,
},
),
Internal(ref int) => match int._dummy {},
};
if let Some(v) = v {
if (spec == &Year || spec == &IsoYear) && !(0..10_000).contains(&v) {
match *pad {
Pad::None => write!(w, "{:+}", v),
Pad::Zero => write!(w, "{:+01$}", v, width + 1),
Pad::Space => write!(w, "{:+1$}", v, width + 1),
}
} else {
match *pad {
Pad::None => write!(w, "{}", v),
Pad::Zero => write!(w, "{:01$}", v, width),
Pad::Space => write!(w, "{:1$}", v, width),
}
}
} else {
Err(fmt::Error) }
}
Item::Fixed(ref spec) => {
use self::Fixed::*;
let ret = match *spec {
ShortMonthName => date.map(|d| {
w.write_str(short_months(locale)[d.month0() as usize])?;
Ok(())
}),
LongMonthName => date.map(|d| {
w.write_str(long_months(locale)[d.month0() as usize])?;
Ok(())
}),
ShortWeekdayName => date.map(|d| {
w.write_str(
short_weekdays(locale)[d.weekday().num_days_from_sunday() as usize],
)?;
Ok(())
}),
LongWeekdayName => date.map(|d| {
w.write_str(
long_weekdays(locale)[d.weekday().num_days_from_sunday() as usize],
)?;
Ok(())
}),
LowerAmPm => time.map(|t| {
let ampm = if t.hour12().0 { am_pm(locale)[1] } else { am_pm(locale)[0] };
for c in ampm.chars().flat_map(|c| c.to_lowercase()) {
w.write_char(c)?
}
Ok(())
}),
UpperAmPm => time.map(|t| {
w.write_str(if t.hour12().0 { am_pm(locale)[1] } else { am_pm(locale)[0] })?;
Ok(())
}),
Nanosecond => time.map(|t| {
let nano = t.nanosecond() % 1_000_000_000;
if nano == 0 {
Ok(())
} else {
w.write_str(decimal_point(locale))?;
if nano % 1_000_000 == 0 {
write!(w, "{:03}", nano / 1_000_000)
} else if nano % 1_000 == 0 {
write!(w, "{:06}", nano / 1_000)
} else {
write!(w, "{:09}", nano)
}
}
}),
Nanosecond3 => time.map(|t| {
let nano = t.nanosecond() % 1_000_000_000;
w.write_str(decimal_point(locale))?;
write!(w, "{:03}", nano / 1_000_000)
}),
Nanosecond6 => time.map(|t| {
let nano = t.nanosecond() % 1_000_000_000;
w.write_str(decimal_point(locale))?;
write!(w, "{:06}", nano / 1_000)
}),
Nanosecond9 => time.map(|t| {
let nano = t.nanosecond() % 1_000_000_000;
w.write_str(decimal_point(locale))?;
write!(w, "{:09}", nano)
}),
Internal(InternalFixed { val: InternalInternal::Nanosecond3NoDot }) => {
time.map(|t| {
let nano = t.nanosecond() % 1_000_000_000;
write!(w, "{:03}", nano / 1_000_000)
})
}
Internal(InternalFixed { val: InternalInternal::Nanosecond6NoDot }) => {
time.map(|t| {
let nano = t.nanosecond() % 1_000_000_000;
write!(w, "{:06}", nano / 1_000)
})
}
Internal(InternalFixed { val: InternalInternal::Nanosecond9NoDot }) => {
time.map(|t| {
let nano = t.nanosecond() % 1_000_000_000;
write!(w, "{:09}", nano)
})
}
TimezoneName => off.map(|(name, _)| {
w.write_str(name)?;
Ok(())
}),
TimezoneOffset | TimezoneOffsetZ => off.map(|&(_, off)| {
OffsetFormat {
precision: OffsetPrecision::Minutes,
colons: Colons::Maybe,
allow_zulu: *spec == TimezoneOffsetZ,
padding: Pad::Zero,
}
.format(w, off)
}),
TimezoneOffsetColon | TimezoneOffsetColonZ => off.map(|&(_, off)| {
OffsetFormat {
precision: OffsetPrecision::Minutes,
colons: Colons::Colon,
allow_zulu: *spec == TimezoneOffsetColonZ,
padding: Pad::Zero,
}
.format(w, off)
}),
TimezoneOffsetDoubleColon => off.map(|&(_, off)| {
OffsetFormat {
precision: OffsetPrecision::Seconds,
colons: Colons::Colon,
allow_zulu: false,
padding: Pad::Zero,
}
.format(w, off)
}),
TimezoneOffsetTripleColon => off.map(|&(_, off)| {
OffsetFormat {
precision: OffsetPrecision::Hours,
colons: Colons::None,
allow_zulu: false,
padding: Pad::Zero,
}
.format(w, off)
}),
Internal(InternalFixed { val: InternalInternal::TimezoneOffsetPermissive }) => {
return Err(fmt::Error);
}
RFC2822 =>
{
if let (Some(d), Some(t), Some(&(_, off))) = (date, time, off) {
Some(write_rfc2822(w, crate::NaiveDateTime::new(*d, *t), off))
} else {
None
}
}
RFC3339 =>
{
if let (Some(d), Some(t), Some(&(_, off))) = (date, time, off) {
Some(write_rfc3339(
w,
crate::NaiveDateTime::new(*d, *t),
off.fix(),
SecondsFormat::AutoSi,
false,
))
} else {
None
}
}
};
ret.unwrap_or(Err(fmt::Error)) }
Item::Error => Err(fmt::Error),
}
}
#[cfg(any(feature = "alloc", feature = "serde", feature = "rustc-serialize"))]
impl OffsetFormat {
fn format(&self, w: &mut impl Write, off: FixedOffset) -> fmt::Result {
let off = off.local_minus_utc();
if self.allow_zulu && off == 0 {
w.write_char('Z')?;
return Ok(());
}
let (sign, off) = if off < 0 { ('-', -off) } else { ('+', off) };
let hours;
let mut mins = 0;
let mut secs = 0;
let precision = match self.precision {
OffsetPrecision::Hours => {
hours = (off / 3600) as u8;
OffsetPrecision::Hours
}
OffsetPrecision::Minutes | OffsetPrecision::OptionalMinutes => {
let minutes = (off + 30) / 60;
mins = (minutes % 60) as u8;
hours = (minutes / 60) as u8;
if self.precision == OffsetPrecision::OptionalMinutes && mins == 0 {
OffsetPrecision::Hours
} else {
OffsetPrecision::Minutes
}
}
OffsetPrecision::Seconds
| OffsetPrecision::OptionalSeconds
| OffsetPrecision::OptionalMinutesAndSeconds => {
let minutes = off / 60;
secs = (off % 60) as u8;
mins = (minutes % 60) as u8;
hours = (minutes / 60) as u8;
if self.precision != OffsetPrecision::Seconds && secs == 0 {
if self.precision == OffsetPrecision::OptionalMinutesAndSeconds && mins == 0 {
OffsetPrecision::Hours
} else {
OffsetPrecision::Minutes
}
} else {
OffsetPrecision::Seconds
}
}
};
let colons = self.colons == Colons::Colon;
if hours < 10 {
if self.padding == Pad::Space {
w.write_char(' ')?;
}
w.write_char(sign)?;
if self.padding == Pad::Zero {
w.write_char('0')?;
}
w.write_char((b'0' + hours) as char)?;
} else {
w.write_char(sign)?;
write_hundreds(w, hours)?;
}
if let OffsetPrecision::Minutes | OffsetPrecision::Seconds = precision {
if colons {
w.write_char(':')?;
}
write_hundreds(w, mins)?;
}
if let OffsetPrecision::Seconds = precision {
if colons {
w.write_char(':')?;
}
write_hundreds(w, secs)?;
}
Ok(())
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
#[allow(clippy::manual_non_exhaustive)]
pub enum SecondsFormat {
Secs,
Millis,
Micros,
Nanos,
AutoSi,
#[doc(hidden)]
__NonExhaustive,
}
#[inline]
#[cfg(any(feature = "alloc", feature = "serde", feature = "rustc-serialize"))]
pub(crate) fn write_rfc3339(
w: &mut impl Write,
dt: NaiveDateTime,
off: FixedOffset,
secform: SecondsFormat,
use_z: bool,
) -> fmt::Result {
let year = dt.date().year();
if (0..=9999).contains(&year) {
write_hundreds(w, (year / 100) as u8)?;
write_hundreds(w, (year % 100) as u8)?;
} else {
write!(w, "{:+05}", year)?;
}
w.write_char('-')?;
write_hundreds(w, dt.date().month() as u8)?;
w.write_char('-')?;
write_hundreds(w, dt.date().day() as u8)?;
w.write_char('T')?;
let (hour, min, mut sec) = dt.time().hms();
let mut nano = dt.nanosecond();
if nano >= 1_000_000_000 {
sec += 1;
nano -= 1_000_000_000;
}
write_hundreds(w, hour as u8)?;
w.write_char(':')?;
write_hundreds(w, min as u8)?;
w.write_char(':')?;
let sec = sec;
write_hundreds(w, sec as u8)?;
match secform {
SecondsFormat::Secs => {}
SecondsFormat::Millis => write!(w, ".{:03}", nano / 1_000_000)?,
SecondsFormat::Micros => write!(w, ".{:06}", nano / 1000)?,
SecondsFormat::Nanos => write!(w, ".{:09}", nano)?,
SecondsFormat::AutoSi => {
if nano == 0 {
} else if nano % 1_000_000 == 0 {
write!(w, ".{:03}", nano / 1_000_000)?
} else if nano % 1_000 == 0 {
write!(w, ".{:06}", nano / 1_000)?
} else {
write!(w, ".{:09}", nano)?
}
}
SecondsFormat::__NonExhaustive => unreachable!(),
};
OffsetFormat {
precision: OffsetPrecision::Minutes,
colons: Colons::Colon,
allow_zulu: use_z,
padding: Pad::Zero,
}
.format(w, off)
}
#[cfg(feature = "alloc")]
pub(crate) fn write_rfc2822(
w: &mut impl Write,
dt: NaiveDateTime,
off: FixedOffset,
) -> fmt::Result {
let year = dt.year();
if !(0..=9999).contains(&year) {
return Err(fmt::Error);
}
let english = default_locale();
w.write_str(short_weekdays(english)[dt.weekday().num_days_from_sunday() as usize])?;
w.write_str(", ")?;
let day = dt.day();
if day < 10 {
w.write_char((b'0' + day as u8) as char)?;
} else {
write_hundreds(w, day as u8)?;
}
w.write_char(' ')?;
w.write_str(short_months(english)[dt.month0() as usize])?;
w.write_char(' ')?;
write_hundreds(w, (year / 100) as u8)?;
write_hundreds(w, (year % 100) as u8)?;
w.write_char(' ')?;
let (hour, min, sec) = dt.time().hms();
write_hundreds(w, hour as u8)?;
w.write_char(':')?;
write_hundreds(w, min as u8)?;
w.write_char(':')?;
let sec = sec + dt.nanosecond() / 1_000_000_000;
write_hundreds(w, sec as u8)?;
w.write_char(' ')?;
OffsetFormat {
precision: OffsetPrecision::Minutes,
colons: Colons::None,
allow_zulu: false,
padding: Pad::Zero,
}
.format(w, off)
}
pub(crate) fn write_hundreds(w: &mut impl Write, n: u8) -> fmt::Result {
if n >= 100 {
return Err(fmt::Error);
}
let tens = b'0' + n / 10;
let ones = b'0' + n % 10;
w.write_char(tens as char)?;
w.write_char(ones as char)
}
#[cfg(test)]
#[cfg(feature = "alloc")]
mod tests {
use super::{Colons, OffsetFormat, OffsetPrecision, Pad};
use crate::FixedOffset;
#[cfg(feature = "alloc")]
use crate::{NaiveDate, NaiveTime, TimeZone, Timelike, Utc};
#[test]
#[cfg(feature = "alloc")]
fn test_date_format() {
let d = NaiveDate::from_ymd_opt(2012, 3, 4).unwrap();
assert_eq!(d.format("%Y,%C,%y,%G,%g").to_string(), "2012,20,12,2012,12");
assert_eq!(d.format("%m,%b,%h,%B").to_string(), "03,Mar,Mar,March");
assert_eq!(d.format("%d,%e").to_string(), "04, 4");
assert_eq!(d.format("%U,%W,%V").to_string(), "10,09,09");
assert_eq!(d.format("%a,%A,%w,%u").to_string(), "Sun,Sunday,0,7");
assert_eq!(d.format("%j").to_string(), "064"); assert_eq!(d.format("%D,%x").to_string(), "03/04/12,03/04/12");
assert_eq!(d.format("%F").to_string(), "2012-03-04");
assert_eq!(d.format("%v").to_string(), " 4-Mar-2012");
assert_eq!(d.format("%t%n%%%n%t").to_string(), "\t\n%\n\t");
assert_eq!(
NaiveDate::from_ymd_opt(12345, 1, 1).unwrap().format("%Y").to_string(),
"+12345"
);
assert_eq!(NaiveDate::from_ymd_opt(1234, 1, 1).unwrap().format("%Y").to_string(), "1234");
assert_eq!(NaiveDate::from_ymd_opt(123, 1, 1).unwrap().format("%Y").to_string(), "0123");
assert_eq!(NaiveDate::from_ymd_opt(12, 1, 1).unwrap().format("%Y").to_string(), "0012");
assert_eq!(NaiveDate::from_ymd_opt(1, 1, 1).unwrap().format("%Y").to_string(), "0001");
assert_eq!(NaiveDate::from_ymd_opt(0, 1, 1).unwrap().format("%Y").to_string(), "0000");
assert_eq!(NaiveDate::from_ymd_opt(-1, 1, 1).unwrap().format("%Y").to_string(), "-0001");
assert_eq!(NaiveDate::from_ymd_opt(-12, 1, 1).unwrap().format("%Y").to_string(), "-0012");
assert_eq!(NaiveDate::from_ymd_opt(-123, 1, 1).unwrap().format("%Y").to_string(), "-0123");
assert_eq!(NaiveDate::from_ymd_opt(-1234, 1, 1).unwrap().format("%Y").to_string(), "-1234");
assert_eq!(
NaiveDate::from_ymd_opt(-12345, 1, 1).unwrap().format("%Y").to_string(),
"-12345"
);
assert_eq!(
NaiveDate::from_ymd_opt(2007, 12, 31).unwrap().format("%G,%g,%U,%W,%V").to_string(),
"2008,08,52,53,01"
);
assert_eq!(
NaiveDate::from_ymd_opt(2010, 1, 3).unwrap().format("%G,%g,%U,%W,%V").to_string(),
"2009,09,01,00,53"
);
}
#[test]
#[cfg(feature = "alloc")]
fn test_time_format() {
let t = NaiveTime::from_hms_nano_opt(3, 5, 7, 98765432).unwrap();
assert_eq!(t.format("%H,%k,%I,%l,%P,%p").to_string(), "03, 3,03, 3,am,AM");
assert_eq!(t.format("%M").to_string(), "05");
assert_eq!(t.format("%S,%f,%.f").to_string(), "07,098765432,.098765432");
assert_eq!(t.format("%.3f,%.6f,%.9f").to_string(), ".098,.098765,.098765432");
assert_eq!(t.format("%R").to_string(), "03:05");
assert_eq!(t.format("%T,%X").to_string(), "03:05:07,03:05:07");
assert_eq!(t.format("%r").to_string(), "03:05:07 AM");
assert_eq!(t.format("%t%n%%%n%t").to_string(), "\t\n%\n\t");
let t = NaiveTime::from_hms_micro_opt(3, 5, 7, 432100).unwrap();
assert_eq!(t.format("%S,%f,%.f").to_string(), "07,432100000,.432100");
assert_eq!(t.format("%.3f,%.6f,%.9f").to_string(), ".432,.432100,.432100000");
let t = NaiveTime::from_hms_milli_opt(3, 5, 7, 210).unwrap();
assert_eq!(t.format("%S,%f,%.f").to_string(), "07,210000000,.210");
assert_eq!(t.format("%.3f,%.6f,%.9f").to_string(), ".210,.210000,.210000000");
let t = NaiveTime::from_hms_opt(3, 5, 7).unwrap();
assert_eq!(t.format("%S,%f,%.f").to_string(), "07,000000000,");
assert_eq!(t.format("%.3f,%.6f,%.9f").to_string(), ".000,.000000,.000000000");
assert_eq!(
NaiveTime::from_hms_opt(13, 57, 9).unwrap().format("%r").to_string(),
"01:57:09 PM"
);
assert_eq!(
NaiveTime::from_hms_milli_opt(23, 59, 59, 1_000).unwrap().format("%X").to_string(),
"23:59:60"
);
}
#[test]
#[cfg(feature = "alloc")]
fn test_datetime_format() {
let dt =
NaiveDate::from_ymd_opt(2010, 9, 8).unwrap().and_hms_milli_opt(7, 6, 54, 321).unwrap();
assert_eq!(dt.format("%c").to_string(), "Wed Sep 8 07:06:54 2010");
assert_eq!(dt.format("%s").to_string(), "1283929614");
assert_eq!(dt.format("%t%n%%%n%t").to_string(), "\t\n%\n\t");
let dt = NaiveDate::from_ymd_opt(2012, 6, 30)
.unwrap()
.and_hms_milli_opt(23, 59, 59, 1_000)
.unwrap();
assert_eq!(dt.format("%c").to_string(), "Sat Jun 30 23:59:60 2012");
assert_eq!(dt.format("%s").to_string(), "1341100799"); }
#[test]
#[cfg(feature = "alloc")]
fn test_datetime_format_alignment() {
let datetime = Utc
.with_ymd_and_hms(2007, 1, 2, 12, 34, 56)
.unwrap()
.with_nanosecond(123456789)
.unwrap();
let percent = datetime.format("%%");
assert_eq!(" %", format!("{:>4}", percent));
assert_eq!("% ", format!("{:<4}", percent));
assert_eq!(" % ", format!("{:^4}", percent));
let year = datetime.format("%Y");
assert_eq!("——2007", format!("{:—>6}", year));
assert_eq!("2007——", format!("{:—<6}", year));
assert_eq!("—2007—", format!("{:—^6}", year));
let tz = datetime.format("%Z");
assert_eq!(" UTC", format!("{:>5}", tz));
assert_eq!("UTC ", format!("{:<5}", tz));
assert_eq!(" UTC ", format!("{:^5}", tz));
let ymd = datetime.format("%Y %B %d");
assert_eq!(" 2007 January 02", format!("{:>17}", ymd));
assert_eq!("2007 January 02 ", format!("{:<17}", ymd));
assert_eq!(" 2007 January 02 ", format!("{:^17}", ymd));
let time = datetime.format("%T%.6f");
assert_eq!("12:34:56.1234", format!("{:.13}", time));
}
#[test]
fn test_offset_formatting() {
fn check_all(precision: OffsetPrecision, expected: [[&str; 7]; 12]) {
fn check(
precision: OffsetPrecision,
colons: Colons,
padding: Pad,
allow_zulu: bool,
offsets: [FixedOffset; 7],
expected: [&str; 7],
) {
let offset_format = OffsetFormat { precision, colons, allow_zulu, padding };
for (offset, expected) in offsets.iter().zip(expected.iter()) {
let mut output = String::new();
offset_format.format(&mut output, *offset).unwrap();
assert_eq!(&output, expected);
}
}
let offsets = [
FixedOffset::east_opt(13_500).unwrap(),
FixedOffset::east_opt(-12_600).unwrap(),
FixedOffset::east_opt(39_600).unwrap(),
FixedOffset::east_opt(-39_622).unwrap(),
FixedOffset::east_opt(9266).unwrap(),
FixedOffset::east_opt(-45270).unwrap(),
FixedOffset::east_opt(0).unwrap(),
];
check(precision, Colons::Colon, Pad::Zero, false, offsets, expected[0]);
check(precision, Colons::Colon, Pad::Zero, true, offsets, expected[1]);
check(precision, Colons::Colon, Pad::Space, false, offsets, expected[2]);
check(precision, Colons::Colon, Pad::Space, true, offsets, expected[3]);
check(precision, Colons::Colon, Pad::None, false, offsets, expected[4]);
check(precision, Colons::Colon, Pad::None, true, offsets, expected[5]);
check(precision, Colons::None, Pad::Zero, false, offsets, expected[6]);
check(precision, Colons::None, Pad::Zero, true, offsets, expected[7]);
check(precision, Colons::None, Pad::Space, false, offsets, expected[8]);
check(precision, Colons::None, Pad::Space, true, offsets, expected[9]);
check(precision, Colons::None, Pad::None, false, offsets, expected[10]);
check(precision, Colons::None, Pad::None, true, offsets, expected[11]);
check(precision, Colons::Maybe, Pad::Zero, false, offsets, expected[6]);
check(precision, Colons::Maybe, Pad::Zero, true, offsets, expected[7]);
check(precision, Colons::Maybe, Pad::Space, false, offsets, expected[8]);
check(precision, Colons::Maybe, Pad::Space, true, offsets, expected[9]);
check(precision, Colons::Maybe, Pad::None, false, offsets, expected[10]);
check(precision, Colons::Maybe, Pad::None, true, offsets, expected[11]);
}
check_all(
OffsetPrecision::Hours,
[
["+03", "-03", "+11", "-11", "+02", "-12", "+00"],
["+03", "-03", "+11", "-11", "+02", "-12", "Z"],
[" +3", " -3", "+11", "-11", " +2", "-12", " +0"],
[" +3", " -3", "+11", "-11", " +2", "-12", "Z"],
["+3", "-3", "+11", "-11", "+2", "-12", "+0"],
["+3", "-3", "+11", "-11", "+2", "-12", "Z"],
["+03", "-03", "+11", "-11", "+02", "-12", "+00"],
["+03", "-03", "+11", "-11", "+02", "-12", "Z"],
[" +3", " -3", "+11", "-11", " +2", "-12", " +0"],
[" +3", " -3", "+11", "-11", " +2", "-12", "Z"],
["+3", "-3", "+11", "-11", "+2", "-12", "+0"],
["+3", "-3", "+11", "-11", "+2", "-12", "Z"],
],
);
check_all(
OffsetPrecision::Minutes,
[
["+03:45", "-03:30", "+11:00", "-11:00", "+02:34", "-12:35", "+00:00"],
["+03:45", "-03:30", "+11:00", "-11:00", "+02:34", "-12:35", "Z"],
[" +3:45", " -3:30", "+11:00", "-11:00", " +2:34", "-12:35", " +0:00"],
[" +3:45", " -3:30", "+11:00", "-11:00", " +2:34", "-12:35", "Z"],
["+3:45", "-3:30", "+11:00", "-11:00", "+2:34", "-12:35", "+0:00"],
["+3:45", "-3:30", "+11:00", "-11:00", "+2:34", "-12:35", "Z"],
["+0345", "-0330", "+1100", "-1100", "+0234", "-1235", "+0000"],
["+0345", "-0330", "+1100", "-1100", "+0234", "-1235", "Z"],
[" +345", " -330", "+1100", "-1100", " +234", "-1235", " +000"],
[" +345", " -330", "+1100", "-1100", " +234", "-1235", "Z"],
["+345", "-330", "+1100", "-1100", "+234", "-1235", "+000"],
["+345", "-330", "+1100", "-1100", "+234", "-1235", "Z"],
],
);
#[rustfmt::skip]
check_all(
OffsetPrecision::Seconds,
[
["+03:45:00", "-03:30:00", "+11:00:00", "-11:00:22", "+02:34:26", "-12:34:30", "+00:00:00"],
["+03:45:00", "-03:30:00", "+11:00:00", "-11:00:22", "+02:34:26", "-12:34:30", "Z"],
[" +3:45:00", " -3:30:00", "+11:00:00", "-11:00:22", " +2:34:26", "-12:34:30", " +0:00:00"],
[" +3:45:00", " -3:30:00", "+11:00:00", "-11:00:22", " +2:34:26", "-12:34:30", "Z"],
["+3:45:00", "-3:30:00", "+11:00:00", "-11:00:22", "+2:34:26", "-12:34:30", "+0:00:00"],
["+3:45:00", "-3:30:00", "+11:00:00", "-11:00:22", "+2:34:26", "-12:34:30", "Z"],
["+034500", "-033000", "+110000", "-110022", "+023426", "-123430", "+000000"],
["+034500", "-033000", "+110000", "-110022", "+023426", "-123430", "Z"],
[" +34500", " -33000", "+110000", "-110022", " +23426", "-123430", " +00000"],
[" +34500", " -33000", "+110000", "-110022", " +23426", "-123430", "Z"],
["+34500", "-33000", "+110000", "-110022", "+23426", "-123430", "+00000"],
["+34500", "-33000", "+110000", "-110022", "+23426", "-123430", "Z"],
],
);
check_all(
OffsetPrecision::OptionalMinutes,
[
["+03:45", "-03:30", "+11", "-11", "+02:34", "-12:35", "+00"],
["+03:45", "-03:30", "+11", "-11", "+02:34", "-12:35", "Z"],
[" +3:45", " -3:30", "+11", "-11", " +2:34", "-12:35", " +0"],
[" +3:45", " -3:30", "+11", "-11", " +2:34", "-12:35", "Z"],
["+3:45", "-3:30", "+11", "-11", "+2:34", "-12:35", "+0"],
["+3:45", "-3:30", "+11", "-11", "+2:34", "-12:35", "Z"],
["+0345", "-0330", "+11", "-11", "+0234", "-1235", "+00"],
["+0345", "-0330", "+11", "-11", "+0234", "-1235", "Z"],
[" +345", " -330", "+11", "-11", " +234", "-1235", " +0"],
[" +345", " -330", "+11", "-11", " +234", "-1235", "Z"],
["+345", "-330", "+11", "-11", "+234", "-1235", "+0"],
["+345", "-330", "+11", "-11", "+234", "-1235", "Z"],
],
);
check_all(
OffsetPrecision::OptionalSeconds,
[
["+03:45", "-03:30", "+11:00", "-11:00:22", "+02:34:26", "-12:34:30", "+00:00"],
["+03:45", "-03:30", "+11:00", "-11:00:22", "+02:34:26", "-12:34:30", "Z"],
[" +3:45", " -3:30", "+11:00", "-11:00:22", " +2:34:26", "-12:34:30", " +0:00"],
[" +3:45", " -3:30", "+11:00", "-11:00:22", " +2:34:26", "-12:34:30", "Z"],
["+3:45", "-3:30", "+11:00", "-11:00:22", "+2:34:26", "-12:34:30", "+0:00"],
["+3:45", "-3:30", "+11:00", "-11:00:22", "+2:34:26", "-12:34:30", "Z"],
["+0345", "-0330", "+1100", "-110022", "+023426", "-123430", "+0000"],
["+0345", "-0330", "+1100", "-110022", "+023426", "-123430", "Z"],
[" +345", " -330", "+1100", "-110022", " +23426", "-123430", " +000"],
[" +345", " -330", "+1100", "-110022", " +23426", "-123430", "Z"],
["+345", "-330", "+1100", "-110022", "+23426", "-123430", "+000"],
["+345", "-330", "+1100", "-110022", "+23426", "-123430", "Z"],
],
);
check_all(
OffsetPrecision::OptionalMinutesAndSeconds,
[
["+03:45", "-03:30", "+11", "-11:00:22", "+02:34:26", "-12:34:30", "+00"],
["+03:45", "-03:30", "+11", "-11:00:22", "+02:34:26", "-12:34:30", "Z"],
[" +3:45", " -3:30", "+11", "-11:00:22", " +2:34:26", "-12:34:30", " +0"],
[" +3:45", " -3:30", "+11", "-11:00:22", " +2:34:26", "-12:34:30", "Z"],
["+3:45", "-3:30", "+11", "-11:00:22", "+2:34:26", "-12:34:30", "+0"],
["+3:45", "-3:30", "+11", "-11:00:22", "+2:34:26", "-12:34:30", "Z"],
["+0345", "-0330", "+11", "-110022", "+023426", "-123430", "+00"],
["+0345", "-0330", "+11", "-110022", "+023426", "-123430", "Z"],
[" +345", " -330", "+11", "-110022", " +23426", "-123430", " +0"],
[" +345", " -330", "+11", "-110022", " +23426", "-123430", "Z"],
["+345", "-330", "+11", "-110022", "+23426", "-123430", "+0"],
["+345", "-330", "+11", "-110022", "+23426", "-123430", "Z"],
],
);
}
}