use core::num::NonZero; use rstest::rstest; use time::format_description::modifier::{ CalendarYearFullExtendedRange, Day, End, Ignore, MonthNumerical, OffsetMinute, OffsetSecond, Ordinal, Padding, Period, TrailingInput, UnixTimestampNanosecond, WeekNumberIso, WeekdayLong, }; use time::format_description::{BorrowedFormatItem, Component}; use time::macros::{date, format_description, time}; use time::{Date, Time}; #[rstest] fn nontrivial_string() { assert!(format_description!(r"").is_empty()); assert!(format_description!(r###""###).is_empty()); assert!(format_description!(b"").is_empty()); assert!(format_description!(br"").is_empty()); assert!(format_description!(br###""###).is_empty()); #[rustfmt::skip] assert_eq!( format_description!("foo\ bar\n\r\t\\\"\'\0\x20\x4E\x4e\u{20}\u{4E}\u{4_e}"), &[BorrowedFormatItem::StringLiteral("foobar\n\r\t\\\"'\0 NN NN")] ); #[rustfmt::skip] assert_eq!( format_description!(b"foo\ bar\n\r\t\\\"\'\0\x20\x4E\x4e"), &[BorrowedFormatItem::StringLiteral("foobar\n\r\t\\\"'\0 NN")] ); } #[rstest] fn format_description_version() { assert_eq!( format_description!(version = 1, "[["), &[BorrowedFormatItem::StringLiteral("[")] ); assert_eq!( format_description!(version = 1, r"\\"), &[BorrowedFormatItem::StringLiteral(r"\\")] ); assert_eq!( format_description!(version = 2, r"\\"), &[BorrowedFormatItem::StringLiteral(r"\")] ); } #[rstest] fn nested_v1() { assert_eq!( format_description!(version = 1, "[optional [[[]]"), &[BorrowedFormatItem::Optional( &BorrowedFormatItem::StringLiteral("[") )] ); assert_eq!( format_description!(version = 1, "[optional [ [[ ]]"), &[BorrowedFormatItem::Optional( &BorrowedFormatItem::StringLiteral(" [ ") )] ); assert_eq!( format_description!(version = 1, "[first [a][[[]]"), &[BorrowedFormatItem::First(&[ BorrowedFormatItem::StringLiteral("a"), BorrowedFormatItem::StringLiteral("[") ])] ); } #[rstest] fn optional() { assert_eq!( format_description!(version = 2, "[optional [:[year]]]"), &[BorrowedFormatItem::Optional(&BorrowedFormatItem::Compound( &[ BorrowedFormatItem::StringLiteral(":"), BorrowedFormatItem::Component(Component::CalendarYearFullExtendedRange( Default::default() )) ] ))] ); assert_eq!( format_description!(version = 2, "[optional [[year]]]"), &[BorrowedFormatItem::Optional( &BorrowedFormatItem::Component(Component::CalendarYearFullExtendedRange( Default::default() )) )] ); assert_eq!( format_description!(version = 2, r"[optional [\[]]"), &[BorrowedFormatItem::Optional( &BorrowedFormatItem::StringLiteral("[") )] ); assert_eq!( format_description!(version = 2, r"[optional [ \[ ]]"), &[BorrowedFormatItem::Optional( &BorrowedFormatItem::StringLiteral(" [ ") )] ); } #[rstest] fn first() { assert_eq!( format_description!(version = 2, "[first [a]]"), &[BorrowedFormatItem::StringLiteral("a")] ); assert_eq!( format_description!(version = 2, "[first [a] [b]]"), &[BorrowedFormatItem::First(&[ BorrowedFormatItem::StringLiteral("a"), BorrowedFormatItem::StringLiteral("b"), ])] ); assert_eq!( format_description!(version = 2, "[first [a][b]]"), &[BorrowedFormatItem::First(&[ BorrowedFormatItem::StringLiteral("a"), BorrowedFormatItem::StringLiteral("b"), ])] ); assert_eq!( format_description!(version = 2, r"[first [a][\[]]"), &[BorrowedFormatItem::First(&[ BorrowedFormatItem::StringLiteral("a"), BorrowedFormatItem::StringLiteral("["), ])] ); assert_eq!( format_description!(version = 2, r"[first [a][\[\[]]"), &[BorrowedFormatItem::First(&[ BorrowedFormatItem::StringLiteral("a"), BorrowedFormatItem::StringLiteral("[["), ])] ); assert_eq!( format_description!( version = 2, "[first [[period case:upper]] [[period case:lower]] ]" ), &[BorrowedFormatItem::First(&[ BorrowedFormatItem::Component(Component::Period( Period::default() .with_is_uppercase(true) .with_case_sensitive(true) )), BorrowedFormatItem::Component(Component::Period( Period::default() .with_is_uppercase(false) .with_case_sensitive(true) )), ])] ); } #[rstest] fn backslash_escape() { assert_eq!( format_description!(version = 2, r"[optional [\]]]"), &[BorrowedFormatItem::Optional( &BorrowedFormatItem::StringLiteral("]") )] ); assert_eq!( format_description!(version = 2, r"[optional [\[]]"), &[BorrowedFormatItem::Optional( &BorrowedFormatItem::StringLiteral("[") )] ); assert_eq!( format_description!(version = 2, r"[optional [\\]]"), &[BorrowedFormatItem::Optional( &BorrowedFormatItem::StringLiteral(r"\") )] ); assert_eq!( format_description!(version = 2, r"\\"), &[BorrowedFormatItem::StringLiteral(r"\")] ); assert_eq!( format_description!(version = 2, r"\["), &[BorrowedFormatItem::StringLiteral(r"[")] ); assert_eq!( format_description!(version = 2, r"\]"), &[BorrowedFormatItem::StringLiteral(r"]")] ); assert_eq!( format_description!(version = 2, r"foo\\"), &[BorrowedFormatItem::StringLiteral(r"foo\"),] ); assert_eq!( format_description!(version = 2, r"\\"), &[BorrowedFormatItem::StringLiteral(r"\")] ); assert_eq!( format_description!(version = 2, r"\["), &[BorrowedFormatItem::StringLiteral(r"[")] ); assert_eq!( format_description!(version = 2, r"\]"), &[BorrowedFormatItem::StringLiteral(r"]")] ); assert_eq!( format_description!(version = 2, r"foo\\"), &[BorrowedFormatItem::StringLiteral(r"foo\")] ); } #[rstest] fn format_description_coverage() { assert_eq!( format_description!("[day padding:space][day padding:zero][day padding:none]"), &[ BorrowedFormatItem::Component(Component::Day( Day::default().with_padding(Padding::Space) )), BorrowedFormatItem::Component(Component::Day( Day::default().with_padding(Padding::Zero) )), BorrowedFormatItem::Component(Component::Day( Day::default().with_padding(Padding::None) )) ] ); assert_eq!( format_description!( "[offset_minute padding:space][offset_minute padding:zero][offset_minute padding:none]" ), &[ BorrowedFormatItem::Component(Component::OffsetMinute( OffsetMinute::default().with_padding(Padding::Space) )), BorrowedFormatItem::Component(Component::OffsetMinute( OffsetMinute::default().with_padding(Padding::Zero) )), BorrowedFormatItem::Component(Component::OffsetMinute( OffsetMinute::default().with_padding(Padding::None) )) ] ); assert_eq!( format_description!( "[offset_second padding:space][offset_second padding:zero][offset_second padding:none]" ), &[ BorrowedFormatItem::Component(Component::OffsetSecond( OffsetSecond::default().with_padding(Padding::Space) )), BorrowedFormatItem::Component(Component::OffsetSecond( OffsetSecond::default().with_padding(Padding::Zero) )), BorrowedFormatItem::Component(Component::OffsetSecond( OffsetSecond::default().with_padding(Padding::None) )), ] ); assert_eq!( format_description!("[ordinal padding:space][ordinal padding:zero][ordinal padding:none]"), &[ BorrowedFormatItem::Component(Component::Ordinal( Ordinal::default().with_padding(Padding::Space) )), BorrowedFormatItem::Component(Component::Ordinal( Ordinal::default().with_padding(Padding::Zero) )), BorrowedFormatItem::Component(Component::Ordinal( Ordinal::default().with_padding(Padding::None) )), ] ); assert_eq!( format_description!("[month repr:numerical]"), &[BorrowedFormatItem::Component(Component::MonthNumerical( MonthNumerical::default().with_padding(Padding::Zero) ))] ); assert_eq!( format_description!("[week_number repr:iso ]"), &[BorrowedFormatItem::Component(Component::WeekNumberIso( WeekNumberIso::default().with_padding(Padding::Zero) ))] ); assert_eq!( format_description!("[weekday repr:long]"), &[BorrowedFormatItem::Component(Component::WeekdayLong( WeekdayLong::default() ))] ); assert_eq!( format_description!("[year repr:full base:calendar]"), &[BorrowedFormatItem::Component( Component::CalendarYearFullExtendedRange( CalendarYearFullExtendedRange::default() .with_padding(Padding::Zero) .with_sign_is_mandatory(false) ) )] ); assert_eq!( format_description!("[[ "), &[BorrowedFormatItem::StringLiteral("[ ")] ); assert_eq!( format_description!("[ignore count:2]"), &[BorrowedFormatItem::Component(Component::Ignore( Ignore::count(const { NonZero::new(2).unwrap() }) ))] ); assert_eq!( format_description!("[unix_timestamp precision:nanosecond sign:mandatory]"), &[BorrowedFormatItem::Component( Component::UnixTimestampNanosecond( UnixTimestampNanosecond::default().with_sign_is_mandatory(true) ) )] ); assert_eq!( format_description!("[end]"), &[BorrowedFormatItem::Component( Component::End(End::default()) )] ); assert_eq!( format_description!("[end trailing_input:prohibit]"), &[BorrowedFormatItem::Component(Component::End( End::default().with_trailing_input(TrailingInput::Prohibit) ))] ); assert_eq!( format_description!("[end trailing_input:discard]"), &[BorrowedFormatItem::Component(Component::End( End::default().with_trailing_input(TrailingInput::Discard) ))] ); } #[rstest] fn date_coverage() { assert_eq!(Ok(date!(2000-001)), Date::from_ordinal_date(2000, 1)); assert_eq!(Ok(date!(2019-W01-1)), Date::from_ordinal_date(2018, 365)); assert_eq!(Ok(date!(2021-W52-6)), Date::from_ordinal_date(2022, 1)); assert_eq!(Ok(date!(2021-W34-5)), Date::from_ordinal_date(2021, 239)); } #[rstest] fn time_coverage() { assert_eq!(time!(12 AM), Time::MIDNIGHT); assert_eq!(Ok(time!(12 PM)), Time::from_hms(12, 0, 0)); } mod demo { #[expect(dead_code)] type Result = core::result::Result; #[expect(dead_code)] type Option = core::option::Option<()>; #[expect(dead_code)] fn serialize() {} #[expect(dead_code)] fn deserialize() {} time::serde::format_description!( seconds, OffsetDateTime, "[year]-[month]-[day]T[hour]:[minute]:[second]Z" ); }