#![allow(clippy::all, clippy::pedantic, clippy::restriction, warnings)]
#[cfg(feature = "parse")]
mod tests {
use deep_time::{Dt, DtErrKind, Mode, Order, ParseCfg, Scale};
fn def() -> ParseCfg {
ParseCfg::DEFAULT
}
fn ref_cfg() -> ParseCfg {
ParseCfg {
ref_time: Some(Dt::from_ymd(2025, 1, 15, Scale::UTC, 12, 0, 0, 0)),
..Default::default()
}
}
fn cfg_order(order: Order) -> ParseCfg {
ParseCfg {
order,
..Default::default()
}
}
fn cfg_mode(mode: Mode) -> ParseCfg {
ParseCfg {
mode,
..Default::default()
}
}
fn parse(input: &str, cfg: &ParseCfg) -> Dt {
Dt::from_str_parse(input, cfg).unwrap_or_else(|e| {
panic!("expected Ok for {input:?}, got Err({e})");
})
}
fn assert_rfc(input: &str, expected: &str, cfg: &ParseCfg) {
let dt = parse(input, cfg);
let actual = dt.to_str_rfc3339_nf(9);
assert_eq!(actual, expected, "input={input:?}");
}
fn assert_err(input: &str, cfg: &ParseCfg) {
assert!(
Dt::from_str_parse(input, cfg).is_err(),
"expected Err for {input:?}"
);
}
fn assert_err_kind(input: &str, cfg: &ParseCfg, kind: DtErrKind) {
match Dt::from_str_parse(input, cfg) {
Ok(dt) => panic!(
"expected Err({kind:?}) for {input:?}, got Ok({})",
dt.to_str_rfc3339_nf(9)
),
Err(e) => assert_eq!(e.kind(), kind, "input={input:?}, err={e}"),
}
}
#[test]
fn guardrails_empty_and_oversize() {
let cfg = def();
assert_err_kind("", &cfg, DtErrKind::Empty);
assert_err_kind(&"x".repeat(513), &cfg, DtErrKind::InvalidLen);
assert_err(&"x".repeat(512), &cfg);
assert_err(" ", &cfg);
assert_err("\t\n", &cfg);
}
#[test]
fn iso_happy_paths_and_offsets() {
let cfg = def();
assert_rfc("2024-03-15", "2024-03-15T00:00:00Z", &cfg);
assert_rfc("2024-03-15T14:30:00Z", "2024-03-15T14:30:00Z", &cfg);
assert_rfc(
"2024-03-15T14:30:00.123456789Z",
"2024-03-15T14:30:00.123456789Z",
&cfg,
);
assert_rfc("2024-03-15T14:30:00.5Z", "2024-03-15T14:30:00.5Z", &cfg);
assert_rfc("20240315T143000", "2024-03-15T14:30:00Z", &cfg);
assert_rfc("2024-03-15T14:30:00+01:00", "2024-03-15T13:30:00Z", &cfg);
assert_rfc("2024-03-15T14:30:00-05:30", "2024-03-15T20:00:00Z", &cfg);
assert_rfc("2024-03-15 14:30 +0100", "2024-03-15T13:30:00Z", &cfg);
assert_rfc("2024-03-15 14:30 -0530", "2024-03-15T20:00:00Z", &cfg);
}
#[test]
fn week_and_ordinal_dates() {
let cfg = def();
assert_rfc("2024-W11-4", "2024-03-14T00:00:00Z", &cfg);
assert_rfc("2024-074", "2024-03-14T00:00:00Z", &cfg);
assert_rfc("2024074", "2024-03-14T00:00:00Z", &cfg);
assert_rfc("2024-366", "2024-12-31T00:00:00Z", &cfg); assert_rfc("2024W114", "2024-03-14T00:00:00Z", &cfg);
assert_rfc("2024W15", "2024-04-08T00:00:00Z", &cfg);
assert_err("2023-366", &cfg); assert_err("2024-000", &cfg);
assert_err("2024-367", &cfg);
assert_err("2024-W00", &cfg);
assert_err("2024-W54", &cfg);
assert_err("2024-W15-0", &cfg);
assert_err("2024-W15-8", &cfg);
}
#[test]
fn iso_week_no_weekday() {
let cfg = def();
assert_rfc("2024-W11", "2024-03-11T00:00:00Z", &cfg);
assert_rfc("2024W11", "2024-03-11T00:00:00Z", &cfg);
}
#[test]
fn calendar_bounds_and_leap_years() {
let cfg = def();
assert_rfc("0000-01-01", "0000-01-01T00:00:00Z", &cfg);
assert_rfc("9999-12-31", "9999-12-31T00:00:00Z", &cfg);
assert_rfc("-0001-01-01", "-0001-01-01T00:00:00Z", &cfg);
assert_rfc("-2024-03-15", "-2024-03-15T00:00:00Z", &cfg);
assert_rfc("2024-02-29", "2024-02-29T00:00:00Z", &cfg);
assert_rfc("2000-02-29", "2000-02-29T00:00:00Z", &cfg); assert_err("2023-02-29", &cfg);
assert_err("1900-02-29", &cfg); assert_err("2100-02-29", &cfg);
assert_err("2024-04-31", &cfg);
assert_err("2024-00-15", &cfg);
assert_err("2024-13-01", &cfg);
assert_err("2024-03-00", &cfg);
assert_err("2024-03-32", &cfg);
assert_err("0000-00-00", &cfg);
}
#[test]
fn time_component_bounds() {
let cfg = def();
assert_rfc(
"2024-03-15 12:00:00.999999999",
"2024-03-15T12:00:00.999999999Z",
&cfg,
);
assert_err("2024-03-15 24:00:00", &cfg);
assert_err("2024-03-15 25:00:00", &cfg);
assert_err("2024-03-15 12:60:00", &cfg);
let dt = parse("2024-03-15 12:00:60", &cfg);
assert_eq!(dt.to_ymd().sec(), 59);
}
#[test]
fn order_day_vs_month_vs_year_forced() {
assert_rfc("01/02/2003", "2003-02-01T00:00:00Z", &cfg_order(Order::Day));
assert_rfc(
"01/02/2003",
"2003-01-02T00:00:00Z",
&cfg_order(Order::Month),
);
assert_rfc(
"01/02/2003",
"2003-02-01T00:00:00Z",
&cfg_order(Order::Year),
);
assert_rfc("13/01/2003", "2003-01-13T00:00:00Z", &cfg_order(Order::Day));
assert_rfc(
"13/01/2003",
"2003-01-13T00:00:00Z",
&cfg_order(Order::Month),
); assert_rfc(
"01/13/2003",
"2003-01-13T00:00:00Z",
&cfg_order(Order::Month),
);
assert_rfc("01/13/2003", "2003-01-13T00:00:00Z", &cfg_order(Order::Day));
assert_rfc("15/03/24", "2024-03-15T00:00:00Z", &cfg_order(Order::Day));
assert_rfc("15/03/24", "2024-03-15T00:00:00Z", &cfg_order(Order::Month)); assert_rfc("15/03/24", "2015-03-24T00:00:00Z", &cfg_order(Order::Year));
assert_rfc("01/02/03", "2003-02-01T00:00:00Z", &cfg_order(Order::Day));
assert_rfc("01/02/03", "2003-01-02T00:00:00Z", &cfg_order(Order::Month));
assert_rfc("01/02/03", "2001-02-03T00:00:00Z", &cfg_order(Order::Year));
}
#[test]
fn order_smart_heuristic_signals() {
let smart = cfg_order(Order::Smart);
assert_rfc("13/01/2003", "2003-01-13T00:00:00Z", &smart); assert_rfc("01/13/2003", "2003-01-13T00:00:00Z", &smart); assert_rfc("2024.03.15", "2024-03-15T00:00:00Z", &smart); assert_rfc("03.15.24", "2024-03-15T00:00:00Z", &smart); assert_rfc("15/03/24", "2024-03-15T00:00:00Z", &smart); assert_rfc("01/02/03", "2003-02-01T00:00:00Z", &smart); assert_rfc("05/06/07", "2007-06-05T00:00:00Z", &smart);
assert_err("3-4-5", &smart);
}
#[test]
fn pure_numeric_years_and_compact_dates() {
let auto = cfg_mode(Mode::Auto);
let sci = cfg_mode(Mode::Scientific);
let leg = cfg_mode(Mode::Legacy);
assert_rfc("24", "2024-01-01T00:00:00Z", &auto);
assert_rfc("24", "0024-01-01T00:00:00Z", &sci);
assert_rfc("5", "0005-01-01T00:00:00Z", &sci);
assert_rfc("5", "1970-01-01T00:00:05Z", &auto);
assert_rfc("202", "1970-01-01T00:03:22Z", &auto);
assert_rfc("2024", "2024-01-01T00:00:00Z", &auto);
assert_rfc("240315", "2024-03-15T00:00:00Z", &auto);
assert_rfc("20240315", "2024-03-15T00:00:00Z", &auto);
assert_rfc("202403", "2024-03-01T00:00:00Z", &auto);
assert_rfc("202403", "2024-03-01T00:00:00Z", &sci);
assert_rfc("202403", "2024-03-01T00:00:00Z", &leg);
}
#[test]
fn pure_numeric_ordinal_mjd_jd_by_mode() {
let auto = cfg_mode(Mode::Auto);
let sci = cfg_mode(Mode::Scientific);
let leg = cfg_mode(Mode::Legacy);
assert_rfc("24123", "2024-05-02T00:00:00Z", &auto);
assert_rfc("24123", "2024-05-02T00:00:00Z", &leg);
assert_rfc("60400", "2024-03-31T00:00:00Z", &auto);
assert_rfc("60400", "2024-03-31T00:00:00Z", &sci);
assert_rfc("60400", "1970-01-01T16:46:40Z", &leg);
assert_rfc("60400.75", "2024-03-31T18:00:00Z", &auto);
assert_rfc("60400.75", "2024-03-31T18:00:00Z", &sci);
assert_rfc("2024123", "2024-05-02T00:00:00Z", &auto);
assert_rfc("2024123", "2024-05-02T00:00:00Z", &leg);
assert_rfc("2024123", "0829-10-05T00:00:00Z", &sci);
assert_rfc("2440587.5", "1970-01-01T00:00:00Z", &sci);
}
#[test]
fn pure_numeric_unix_timestamps() {
let auto = cfg_mode(Mode::Auto);
let unix = cfg_mode(Mode::UnixTimestamp);
assert_rfc("1735689600", "2025-01-01T00:00:00Z", &auto);
assert_rfc("1735689600", "2025-01-01T00:00:00Z", &unix);
assert_rfc("1735689600123", "2025-01-01T00:00:00.123Z", &auto);
assert_rfc("1735689600123456", "2025-01-01T00:00:00.123456Z", &auto);
assert_rfc(
"1735689600123456789",
"2025-01-01T00:00:00.123456789Z",
&auto,
);
assert_rfc("1735689600.5", "2025-01-01T00:00:00.5Z", &auto);
assert_rfc("0", "1970-01-01T00:00:00Z", &unix);
assert_rfc("-1", "1969-12-31T23:59:59Z", &unix);
assert_rfc("2024", "1970-01-01T00:33:44Z", &unix);
assert_rfc("1000000000", "2001-09-09T01:46:40Z", &unix); assert_rfc("1000000000000", "2001-09-09T01:46:40Z", &unix); }
#[test]
fn named_english_and_ordinal_days() {
let cfg = def();
let cases = [
("15 March 2024", "2024-03-15T00:00:00Z"),
("March 15, 2024", "2024-03-15T00:00:00Z"),
("15th March 2024", "2024-03-15T00:00:00Z"),
("the 15th of March 2024", "2024-03-15T00:00:00Z"),
("15 of March 2024", "2024-03-15T00:00:00Z"),
("Mar 15 2024", "2024-03-15T00:00:00Z"),
("15-Mar-2024", "2024-03-15T00:00:00Z"),
("2024 March 15", "2024-03-15T00:00:00Z"),
("Thursday, March 14, 2024", "2024-03-14T00:00:00Z"),
("1st January 2000", "2000-01-01T00:00:00Z"),
("31st December 1999", "1999-12-31T00:00:00Z"),
("Feb 29 2024", "2024-02-29T00:00:00Z"),
("Sept 15 2024", "2024-09-15T00:00:00Z"),
("Sep 15 2024", "2024-09-15T00:00:00Z"),
("September 15th, 2024", "2024-09-15T00:00:00Z"),
(
"on the 5th of april 2024 at 00:00am",
"2024-04-05T00:00:00Z",
),
];
for (input, expected) in cases {
assert_rfc(input, expected, &cfg);
}
assert_err("Feb 29 2023", &cfg);
}
#[test]
fn month_and_year_without_day() {
let cfg = def();
let cases = [
("2024-03", "2024-03-01T00:00:00Z"),
("2024/03", "2024-03-01T00:00:00Z"),
("2024/3", "2024-03-01T00:00:00Z"),
("202403", "2024-03-01T00:00:00Z"),
("03/2024", "2024-03-01T00:00:00Z"),
("3/2024", "2024-03-01T00:00:00Z"),
("12/2024", "2024-12-01T00:00:00Z"),
("03-2024", "2024-03-01T00:00:00Z"),
("March 2024", "2024-03-01T00:00:00Z"),
("Mar 2024", "2024-03-01T00:00:00Z"),
("March, 2024", "2024-03-01T00:00:00Z"),
("2024 March", "2024-03-01T00:00:00Z"),
("2024 Mar", "2024-03-01T00:00:00Z"),
("january 2000", "2000-01-01T00:00:00Z"),
("Dec 1999", "1999-12-01T00:00:00Z"),
("Sept 2024", "2024-09-01T00:00:00Z"),
];
for (input, expected) in cases {
assert_rfc(input, expected, &cfg);
}
assert_err("March", &cfg);
assert_err("Mar", &cfg);
assert_err("13/2024", &cfg);
assert_err("00/2024", &cfg);
assert_err("03/24", &cfg);
}
#[test]
fn twelve_hour_clock_and_at_glue() {
let cfg = def();
assert_rfc("14 Mar 2024 2:30 PM", "2024-03-14T14:30:00Z", &cfg);
assert_rfc("14 Mar 2024 2:30PM", "2024-03-14T14:30:00Z", &cfg);
assert_rfc("14 Mar 2024 at 2:30pm", "2024-03-14T14:30:00Z", &cfg);
}
#[test]
fn bare_hour_2pm() {
let cfg = def();
assert_rfc("14 Mar 2024 2PM", "2024-03-14T14:00:00Z", &cfg);
assert_rfc("14 Mar 2024 2pm", "2024-03-14T14:00:00Z", &cfg);
assert_rfc("14 Mar 2024 2 PM", "2024-03-14T14:00:00Z", &cfg);
assert_rfc("14 Mar 2024 2AM", "2024-03-14T02:00:00Z", &cfg);
assert_rfc("14 Mar 2024 12PM", "2024-03-14T12:00:00Z", &cfg);
assert_rfc("14 Mar 2024 12AM", "2024-03-14T00:00:00Z", &cfg);
assert_rfc("March 14, 2024 2PM", "2024-03-14T14:00:00Z", &cfg);
assert_rfc("2024-03-14 2PM", "2024-03-14T14:00:00Z", &cfg);
assert_rfc("14 Mar 2024 at 2PM", "2024-03-14T14:00:00Z", &cfg);
}
#[test]
fn syslog_year_inference_from_ref_time() {
let cfg = ref_cfg();
assert_rfc("Mar 5 10:23:45", "2024-03-05T10:23:45Z", &cfg);
assert_rfc("Dec 31 23:59:59", "2024-12-31T23:59:59Z", &cfg);
assert_rfc("Jun 16 12:00:00", "2024-06-16T12:00:00Z", &cfg);
assert_rfc("Jan 1 00:00:00", "2025-01-01T00:00:00Z", &cfg);
}
#[test]
fn relative_core_phrases() {
let cfg = ref_cfg(); let cases = [
("tomorrow", "2025-01-16T12:00:00Z"),
("yesterday", "2025-01-14T12:00:00Z"),
("today", "2025-01-15T12:00:00Z"),
("now", "2025-01-15T12:00:00Z"),
("next Monday", "2025-01-20T12:00:00Z"),
("last Friday", "2025-01-10T12:00:00Z"),
("this Monday", "2025-01-13T12:00:00Z"),
("coming Friday", "2025-01-17T12:00:00Z"),
("in 3 days", "2025-01-18T12:00:00Z"),
("2 weeks ago", "2025-01-01T12:00:00Z"),
("next Monday at 14:00", "2025-01-20T14:00:00Z"),
("tomorrow at 9am", "2025-01-16T09:00:00Z"),
("next year", "2026-01-15T12:00:00Z"),
("last year", "2024-01-15T12:00:00Z"),
("Monday next", "2025-01-20T12:00:00Z"),
("14:00 next Monday", "2025-01-20T14:00:00Z"),
("at 14:00", "2025-01-15T14:00:00Z"),
];
for (input, expected) in cases {
assert_rfc(input, expected, &cfg);
}
}
#[test]
fn relative_partial_and_surprising() {
let cfg = ref_cfg();
assert_rfc("the day after tomorrow", "2025-01-17T12:00:00Z", &cfg);
assert_rfc("day after tomorrow", "2025-01-17T12:00:00Z", &cfg);
assert_rfc("the day before yesterday", "2025-01-13T12:00:00Z", &cfg);
assert_rfc("day before yesterday", "2025-01-13T12:00:00Z", &cfg);
assert_rfc("in a week", "2025-01-22T12:00:00Z", &cfg);
assert_rfc("a week", "2025-01-22T12:00:00Z", &cfg);
assert_rfc("week", "2025-01-22T12:00:00Z", &cfg);
assert_rfc("week ago", "2025-01-08T12:00:00Z", &cfg);
assert_rfc("a month ago", "2024-12-15T12:00:00Z", &cfg);
assert_rfc("in a month", "2025-02-15T12:00:00Z", &cfg);
assert_rfc("next", "2025-01-15T12:00:00Z", &cfg);
assert_rfc("ago", "2025-01-15T12:00:00Z", &cfg);
assert_err("noon", &cfg);
assert_err("midnight", &cfg);
assert_rfc("9am", "2025-01-15T09:00:00Z", &cfg);
assert_rfc("9 am", "2025-01-15T09:00:00Z", &cfg);
assert_rfc("9pm", "2025-01-15T21:00:00Z", &cfg);
assert_rfc("12am", "2025-01-15T00:00:00Z", &cfg);
assert_rfc("12pm", "2025-01-15T12:00:00Z", &cfg);
}
#[test]
fn bare_time_of_day_uses_ref_date() {
let cfg = ref_cfg();
assert_rfc("17:00", "2025-01-15T17:00:00Z", &cfg);
assert_rfc("14:30", "2025-01-15T14:30:00Z", &cfg);
assert_rfc("2:30 PM", "2025-01-15T14:30:00Z", &cfg);
assert_rfc("9:00 am", "2025-01-15T09:00:00Z", &cfg);
assert_rfc("00:00", "2025-01-15T00:00:00Z", &cfg);
assert_rfc("15:30:45", "2025-01-15T15:30:45Z", &cfg);
assert_rfc("9:30:00", "2025-01-15T09:30:00Z", &cfg);
assert_rfc("12:00", "2025-01-15T12:00:00Z", &cfg);
assert_rfc("12:00:00", "2025-01-15T12:00:00Z", &cfg);
assert_rfc("12:00 am", "2025-01-15T00:00:00Z", &cfg);
assert_rfc("12:00 pm", "2025-01-15T12:00:00Z", &cfg);
assert_rfc("24:00", "2025-01-16T12:00:00Z", &cfg);
assert_rfc("72:30", "2025-01-18T12:30:00Z", &cfg);
assert_rfc("25:00:00", "2025-01-16T13:00:00Z", &cfg);
assert_rfc("72:30 ago", "2025-01-12T11:30:00Z", &cfg);
}
#[test]
fn invalid_clock_fields_do_not_glue_to_days() {
let cfg = ref_cfg();
assert_err("24:60", &cfg);
assert_err("25:99", &cfg);
assert_err("at 25:00", &cfg);
assert_err("in 3 days at 25:00", &cfg);
assert_err("tomorrow at 25:00", &cfg);
assert_err("tomorrow at 99:00", &cfg);
assert_rfc("in 3 days at 14:00", "2025-01-18T14:00:00Z", &cfg);
assert_rfc("at 14:00", "2025-01-15T14:00:00Z", &cfg);
}
#[test]
fn relative_cfg_false_disables_relative_phrases() {
let cfg = ParseCfg {
relative: false,
ref_time: Some(Dt::from_ymd(2025, 1, 15, Scale::UTC, 12, 0, 0, 0)),
..Default::default()
};
assert_err("tomorrow", &cfg);
assert_err("in 3 days", &cfg);
assert_rfc("2024-03-15", "2024-03-15T00:00:00Z", &cfg);
}
#[test]
fn scale_suffixes_and_leap_seconds() {
let cfg = def();
for s in [
"2024-03-15T12:00:00 TAI",
"2024-03-15T12:00:00 TT",
"2024-03-15T12:00:00 UTC",
"2024-03-15T12:00:00 GPS",
] {
assert!(
Dt::from_str_parse(s, &cfg).is_ok(),
"scale suffix should parse: {s}"
);
}
assert_rfc("2015-06-30T23:59:60", "2015-06-30T23:59:60Z", &cfg);
assert_rfc("1972-06-30T23:59:60", "1972-06-30T23:59:60Z", &cfg);
assert_rfc("2016-12-31T23:59:60", "2016-12-31T23:59:60Z", &cfg);
assert_err("2015-06-30T23:59:61", &cfg);
assert_rfc("2012-06-30T23:59:60", "2012-06-30T23:59:60Z", &cfg);
}
#[test]
fn separators_and_unicode_digits() {
let cfg = def();
assert_rfc(" 2024-03-15 ", "2024-03-15T00:00:00Z", &cfg);
assert_rfc("2024\u{2010}03\u{2010}15", "2024-03-15T00:00:00Z", &cfg); assert_rfc("2024\u{2013}03\u{2013}15", "2024-03-15T00:00:00Z", &cfg); assert_rfc("2024\u{2014}03\u{2014}15", "2024-03-15T00:00:00Z", &cfg); assert_rfc("2024_03_15", "2024-03-15T00:00:00Z", &cfg);
assert_rfc("2024,03,15", "2024-03-15T00:00:00Z", &cfg);
assert_rfc("2024-03-15", "2024-03-15T00:00:00Z", &cfg);
assert_rfc("15/03/2024", "2024-03-15T00:00:00Z", &cfg);
assert_rfc("2024年3月15日", "2024-03-15T00:00:00Z", &cfg);
assert_rfc("\u{200b}2024-03-15", "2024-03-15T00:00:00Z", &cfg);
assert_rfc("2024-03-15\t14:30", "2024-03-15T14:30:00Z", &cfg);
}
#[test]
fn compact_datetime_layouts() {
let cfg = def();
let cases = [
("20240315143045", "2024-03-15T14:30:45Z"),
("240315143045", "2024-03-15T14:30:45Z"),
("20240315 143045", "2024-03-15T14:30:45Z"),
("2024-03-15 1430", "2024-03-15T14:30:00Z"),
("20240315T14:30:45", "2024-03-15T14:30:45Z"),
("2024-03-15T143045", "2024-03-15T14:30:45Z"),
("2024031514:30:45", "2024-03-15T14:30:45Z"),
("2024-03", "2024-03-01T00:00:00Z"),
("202403", "2024-03-01T00:00:00Z"),
("2024/03", "2024-03-01T00:00:00Z"),
("03/2024", "2024-03-01T00:00:00Z"),
];
for (input, expected) in cases {
assert_rfc(input, expected, &cfg);
}
}
#[test]
fn http_and_rfc2822_style() {
let cfg = def();
assert_rfc(
"Thu, 14 Mar 2024 15:30:45 GMT",
"2024-03-14T15:30:45Z",
&cfg,
);
assert_rfc(
"Thu, 14 Mar 2024 15:30:45 +0000",
"2024-03-14T15:30:45Z",
&cfg,
);
assert_rfc("14 Mar 2024 15:30:45 GMT", "2024-03-14T15:30:45Z", &cfg);
}
#[test]
fn adversarial_hard_rejects() {
let cfg = ref_cfg();
for s in [
"not a date",
"null",
"NaN",
"Infinity",
"yes",
"no",
"true",
"false",
"----",
"Mar",
"March",
"Monday",
"1/2/3/4",
"0x2024",
"1e9",
"123abc",
"999999999999999999999999999999",
] {
assert_err(s, &cfg);
}
}
#[test]
fn adversarial_lenient_accepts() {
let cfg = ref_cfg();
assert_rfc("T14:30:00", "2025-01-15T14:30:00Z", &cfg);
assert_rfc("2024-03-15T14:30:00Z garbage", "2024-03-15T14:30:00Z", &cfg);
assert_rfc("garbage 2024-03-15", "2024-03-15T00:00:00Z", &cfg);
assert_rfc("2024-03-15T", "2024-03-15T00:00:00Z", &cfg);
assert_rfc("\"2024-03-15\"", "2024-03-15T00:00:00Z", &cfg);
assert_rfc("'2024-03-15'", "2024-03-15T00:00:00Z", &cfg);
assert_rfc("[2024-03-15]", "2024-03-15T00:00:00Z", &cfg);
assert_rfc("(2024-03-15)", "2024-03-15T00:00:00Z", &cfg);
assert_rfc("abc123", "1970-01-01T00:02:03Z", &cfg);
assert_rfc("2.5e10", "2010-05-02T00:00:00Z", &cfg);
}
#[test]
fn iana_zone_bracket_requires_tz_feature() {
let cfg = def();
#[cfg(not(any(feature = "jiff-tz", feature = "jiff-tz-bundle")))]
{
assert_err("2024-03-15T14:30:00[Europe/Paris]", &cfg);
assert_err("2024-03-15T14:30:00+01:00[Europe/Paris]", &cfg);
}
#[cfg(any(feature = "jiff-tz", feature = "jiff-tz-bundle"))]
{
assert!(
Dt::from_str_parse("2024-03-15T14:30:00+01:00[Europe/Paris]", &cfg).is_ok()
|| Dt::from_str_parse("2024-03-15T14:30:00[Europe/Paris]", &cfg).is_ok()
);
}
}
#[test]
fn explicit_mode_only_tries_listed_formats() {
let cfg = ParseCfg {
mode: Mode::Explicit,
parse: Some(vec!["%Y-%m-%d".into()]),
..Default::default()
};
assert_rfc("2024-03-15", "2024-03-15T00:00:00Z", &cfg);
assert_err("15/03/2024", &cfg);
assert_err("March 15, 2024", &cfg);
assert_rfc("2024-03-15 12:00", "2024-03-15T00:00:00Z", &cfg);
}
#[test]
fn explicit_formats_then_fallback_when_not_explicit_mode() {
let cfg = ParseCfg {
mode: Mode::Auto,
parse: Some(vec!["%Y-%m-%d".into()]),
..Default::default()
};
assert_rfc("2024-03-15", "2024-03-15T00:00:00Z", &cfg);
assert_rfc("15/03/2024", "2024-03-15T00:00:00Z", &cfg);
}
#[test]
fn to_lower_false_requires_already_lowercase_names() {
let cfg = ParseCfg {
to_lower: false,
..Default::default()
};
assert_rfc("2024-03-15", "2024-03-15T00:00:00Z", &cfg);
assert_err("March 15, 2024", &cfg);
assert_rfc("march 15, 2024", "2024-03-15T00:00:00Z", &cfg);
}
#[test]
fn str_to_helpers_agree_with_from_str_parse() {
let cfg = def();
let s = "2024-03-15T12:00:00Z";
let dt = parse(s, &cfg);
assert_eq!(Dt::str_to_attos(s, &cfg), Some(dt.to_attos()));
assert_eq!(Dt::str_to_ms(s, &cfg), Some(dt.to_ms().0));
assert_eq!(Dt::str_to_ns(s, &cfg), Some(dt.to_ns().0));
assert_eq!(
Dt::str_to_unix_ms(s, &cfg),
Some(dt.to_scale_and_diff(Dt::UNIX_EPOCH, false).to_ms().0)
);
assert_eq!(
Dt::str_to_unix_ns(s, &cfg),
Some(dt.to_scale_and_diff(Dt::UNIX_EPOCH, false).to_ns().0)
);
assert_eq!(Dt::str_to_attos("not-a-date", &cfg), None);
assert_eq!(Dt::str_to_unix_ms("not-a-date", &cfg), None);
}
#[test]
fn rfc3339_roundtrip_smoke() {
let cfg = def();
for s in [
"2024-03-15T00:00:00Z",
"2024-03-15T14:30:45.123456789Z",
"1970-01-01T00:00:00Z",
"2000-01-01T12:00:00Z",
"-0001-06-15T00:00:00Z",
] {
let dt = parse(s, &cfg);
let again = dt.to_str_rfc3339_nf(9);
let dt2 = parse(&again, &cfg);
assert_eq!(dt.to_attos(), dt2.to_attos(), "roundtrip {s} → {again}");
}
}
#[test]
fn smart_order_matrix_classic_pitfalls() {
let rows: &[(&str, &str, &str, &str, &str)] = &[
(
"01/02/2003",
"2003-02-01T00:00:00Z",
"2003-02-01T00:00:00Z",
"2003-01-02T00:00:00Z",
"2003-02-01T00:00:00Z",
),
(
"02/01/2003",
"2003-01-02T00:00:00Z",
"2003-01-02T00:00:00Z",
"2003-02-01T00:00:00Z",
"2003-01-02T00:00:00Z",
),
(
"12/11/10",
"2010-11-12T00:00:00Z",
"2010-11-12T00:00:00Z",
"2010-12-11T00:00:00Z",
"2012-11-10T00:00:00Z",
),
(
"15.03.24",
"2024-03-15T00:00:00Z",
"2024-03-15T00:00:00Z",
"2024-03-15T00:00:00Z",
"2015-03-24T00:00:00Z",
),
];
for &(input, smart, day, month, year) in rows {
for (order, expected) in [
(Order::Smart, smart),
(Order::Day, day),
(Order::Month, month),
(Order::Year, year),
] {
assert_rfc(input, expected, &cfg_order(order));
}
}
}
#[test]
fn digit_counter_no_panic_at_strtime_limit() {
let cfg = def();
for n in [255usize, 256, 300, 400, 512] {
let s = "1".repeat(n);
let _ = Dt::from_str_parse(&s, &cfg);
}
let heavy = format!("1{}", "/2".repeat(200));
assert!(heavy.len() <= 512, "fixture must fit STRTIME_SIZE");
let _ = Dt::from_str_parse(&heavy, &cfg);
assert_err_kind(&"1".repeat(513), &cfg, DtErrKind::InvalidLen);
}
#[test]
fn substring_token_false_positives_rejected() {
let cfg = ref_cfg();
assert_rfc("junk 2024-03-15", "2024-03-15T00:00:00Z", &cfg);
assert_rfc("decorate 2024-03-15", "2024-03-15T00:00:00Z", &cfg);
assert_rfc("aprilfool 2024", "2024-01-01T00:00:00Z", &cfg);
assert_rfc("mayhem 2024", "2024-01-01T00:00:00Z", &cfg);
assert_rfc("augustine 2024", "2024-01-01T00:00:00Z", &cfg);
assert_err("marching 15 2024", &cfg);
assert_err("knowledge", &cfg);
assert_err("snow", &cfg);
assert_err("unknown", &cfg);
assert_err("wagon", &cfg);
assert_rfc("tomorrowland 2024", "2024-01-01T00:00:00Z", &cfg);
assert_rfc("sunflower 2024", "2024-01-01T00:00:00Z", &cfg);
assert_rfc("monster 2024", "2024-01-01T00:00:00Z", &cfg);
assert_err("saturn 15 2024", &cfg);
assert_err("america", &cfg);
assert_rfc("12am", "2025-01-15T00:00:00Z", &cfg);
assert_rfc("14 Mar 2024 2pm", "2024-03-14T14:00:00Z", &cfg);
assert_rfc("jun 2024", "2024-06-01T00:00:00Z", &cfg);
assert_rfc("June 2024", "2024-06-01T00:00:00Z", &cfg);
assert_rfc("Sept 15 2024", "2024-09-15T00:00:00Z", &cfg);
assert_rfc("Sept 2024", "2024-09-01T00:00:00Z", &cfg);
assert_rfc("tomorrow", "2025-01-16T12:00:00Z", &cfg);
assert_rfc("coming Friday", "2025-01-17T12:00:00Z", &cfg);
assert_rfc("14Mar2024", "2024-03-14T00:00:00Z", &cfg);
assert_rfc("15-Mar-2024", "2024-03-15T00:00:00Z", &cfg);
}
#[test]
fn relative_plus_absolute_date_blows_up_years() {
let cfg = ref_cfg(); assert_rfc("tomorrow 2024-03-15", "57441-02-22T12:00:00Z", &cfg);
assert_rfc("in 3 days 2024-03-15", "57441-02-24T12:00:00Z", &cfg);
assert_rfc("next Monday 2024-03-15", "57441-02-26T12:00:00Z", &cfg);
assert_rfc("noway 2024-03-15", "2024-03-15T00:00:00Z", &cfg);
assert_rfc("2024-03-15 tomorrow", "2024-03-15T00:00:00Z", &cfg);
assert_rfc("tomorrow 15:00", "2025-01-16T15:00:00Z", &cfg);
assert_rfc("3 days", "2025-01-18T12:00:00Z", &cfg);
}
#[test]
fn dual_absolute_dates_partially_reinterpreted() {
let cfg = def();
assert_rfc("2024-03-15 2025-04-16", "2024-03-16T12:25:00Z", &cfg);
assert_rfc("15/03/2024 16/04/2025", "2024-03-15T16:00:00Z", &cfg);
assert_err("March 15 2024 March 16 2025", &cfg);
}
#[test]
fn invalid_compact_numeric_falls_through_to_unix() {
let cfg = def();
assert_rfc("20241315", "1970-08-23T06:35:15Z", &cfg); assert_rfc("20240230", "1970-08-23T06:17:10Z", &cfg); assert_rfc("99999999", "1973-03-03T09:46:39Z", &cfg);
assert_rfc("20240315249999", "2611-05-23T21:47:29.999Z", &cfg);
assert_rfc("20240315243000", "2611-05-23T21:47:23Z", &cfg);
assert_rfc("123456", "1970-01-02T10:17:36Z", &cfg);
assert_rfc("991332", "1970-01-12T11:22:12Z", &cfg);
}
#[test]
fn explicit_mode_empty_parse_list_falls_through() {
let empty = ParseCfg {
mode: Mode::Explicit,
parse: Some(vec![]),
..Default::default()
};
assert_rfc("15/03/2024", "2024-03-15T00:00:00Z", &empty);
let none = ParseCfg {
mode: Mode::Explicit,
parse: None,
..Default::default()
};
assert_rfc("15/03/2024", "2024-03-15T00:00:00Z", &none);
let ymd_only = ParseCfg {
mode: Mode::Explicit,
parse: Some(vec!["%Y-%m-%d".into()]),
..Default::default()
};
assert_rfc("2024-03-15", "2024-03-15T00:00:00Z", &ymd_only);
assert_err("15/03/2024", &ymd_only);
}
#[test]
fn explicit_partial_date_formats_default_missing_fields() {
let only_year = ParseCfg {
mode: Mode::Explicit,
parse: Some(vec!["%Y".into()]),
..Default::default()
};
assert_rfc("2024", "2024-01-01T00:00:00Z", &only_year);
assert_rfc("0001", "0001-01-01T00:00:00Z", &only_year);
assert_rfc("9999", "9999-01-01T00:00:00Z", &only_year);
assert_rfc("2024-03-15", "2024-01-01T00:00:00Z", &only_year);
let year_month = ParseCfg {
mode: Mode::Explicit,
parse: Some(vec!["%Y-%m".into()]),
..Default::default()
};
assert_rfc("2024-03", "2024-03-01T00:00:00Z", &year_month);
assert_rfc("2024-12", "2024-12-01T00:00:00Z", &year_month);
assert_err("15/03/2024", &year_month);
let two_digit = ParseCfg {
mode: Mode::Explicit,
parse: Some(vec!["%y".into()]),
..Default::default()
};
assert_rfc("24", "2024-01-01T00:00:00Z", &two_digit);
assert_rfc("99", "1999-01-01T00:00:00Z", &two_digit);
let multi = ParseCfg {
mode: Mode::Explicit,
parse: Some(vec!["%d/%m/%Y".into(), "%Y".into()]),
..Default::default()
};
assert_rfc("15/03/2024", "2024-03-15T00:00:00Z", &multi);
assert_rfc("2024", "2024-01-01T00:00:00Z", &multi);
}
#[test]
fn embedded_date_in_noise_is_accepted() {
let cfg = def();
assert_rfc("2024-03-15'; DROP TABLE", "2024-03-15T00:00:00Z", &cfg);
assert_rfc("../../../2024-03-15", "2024-03-15T00:00:00Z", &cfg);
assert_rfc("{\"date\":\"2024-03-15\"}", "2024-03-15T00:00:00Z", &cfg);
assert_rfc("<time>2024-03-15</time>", "2024-03-15T00:00:00Z", &cfg);
assert_rfc("\"2024-03-15\"", "2024-03-15T00:00:00Z", &cfg);
assert_rfc("2024-03-15x", "2024-03-15T00:00:00Z", &cfg);
assert_rfc("x2024-03-15", "2024-03-15T00:00:00Z", &cfg);
assert_rfc("2024-03-15Z", "2024-03-15T00:00:00Z", &cfg);
assert_rfc("2024-03-15 junk", "2024-03-15T00:00:00Z", &cfg);
assert_rfc("garbage 2024-03-15", "2024-03-15T00:00:00Z", &cfg);
}
#[test]
fn offset_and_clock_edge_cases() {
let cfg = def();
assert_rfc("2024-03-15T12:00:00+15:00", "2024-03-14T21:00:00Z", &cfg);
assert_rfc("2024-03-15T12:00:00-13:00", "2024-03-16T01:00:00Z", &cfg);
assert_rfc("2024-03-15T12:00:00+23:59", "2024-03-14T12:01:00Z", &cfg);
assert_err("2024-03-15T12:00:00+99:00", &cfg);
assert_rfc("2024-03-15T12:00:00+01", "2024-03-15T11:00:00Z", &cfg);
assert_err("2024-03-15 0am", &cfg);
assert_err("2024-03-15 13pm", &cfg);
assert_err("2024-03-15 24am", &cfg);
assert_rfc("2024-03-15 00:00am", "2024-03-15T00:00:00Z", &cfg);
assert_err("2024-03-15T24:00:00Z", &cfg);
}
#[test]
fn leap_second_slot_only_at_day_boundary_on_leap_days() {
let cfg = def();
assert_rfc("2015-06-30T23:59:60Z", "2015-06-30T23:59:60Z", &cfg);
assert_rfc("2012-06-30T23:59:60Z", "2012-06-30T23:59:60Z", &cfg);
assert_rfc("2024-01-01T23:59:60Z", "2024-01-01T23:59:59Z", &cfg);
assert_rfc("2024-03-15T23:59:60Z", "2024-03-15T23:59:59Z", &cfg);
assert_rfc("2024-03-15T12:00:60Z", "2024-03-15T12:00:59Z", &cfg);
}
#[test]
fn control_and_bidi_prefix_tolerance() {
let cfg = def();
assert_rfc("2024-03-15\0", "2024-03-15T00:00:00Z", &cfg);
assert_rfc("2024-03-15\r\n", "2024-03-15T00:00:00Z", &cfg);
assert_rfc("\u{feff}2024-03-15", "2024-03-15T00:00:00Z", &cfg);
assert_rfc("2024\u{202e}03-15", "2024-03-15T00:00:00Z", &cfg);
assert_rfc("\u{200f}2024-03-15", "2024-03-15T00:00:00Z", &cfg);
assert_rfc("2024-03-15\u{00a0}12:00", "2024-03-15T12:00:00Z", &cfg);
assert_rfc("2024\u{2003}03\u{2003}15", "2024-03-15T00:00:00Z", &cfg);
}
#[test]
fn pure_numeric_digit_length_unit_edges() {
let auto = cfg_mode(Mode::Auto);
assert_rfc("202403151", "2024-03-15T01:00:00Z", &auto);
assert_rfc("10000000000", "2286-11-20T17:46:40Z", &auto);
assert_rfc("17356896000", "2520-01-08T00:00:00Z", &auto);
assert_rfc("100000000000", "1973-03-03T09:46:40Z", &auto);
assert_rfc(
"9223372036854775807",
"2262-04-11T23:47:16.854775807Z",
&auto,
);
assert_rfc("68", "2068-01-01T00:00:00Z", &auto);
assert_rfc("69", "1969-01-01T00:00:00Z", &auto);
assert_rfc("00", "2000-01-01T00:00:00Z", &auto);
assert_rfc("+2024", "2024-01-01T00:00:00Z", &auto);
assert_rfc("+2024-03-15", "2024-03-15T00:00:00Z", &auto);
let neg = format!("-{}", "9".repeat(25));
let dt = parse(&neg, &auto);
assert!(dt.to_ymd().yr() < -9999);
}
#[test]
fn relative_false_also_blocks_bare_tod() {
let cfg = ParseCfg {
relative: false,
ref_time: Some(Dt::from_ymd(2025, 1, 15, Scale::UTC, 12, 0, 0, 0)),
..Default::default()
};
assert_err("tomorrow", &cfg);
assert_err("in 3 days", &cfg);
assert_err("14:00", &cfg);
assert_err("9am", &cfg);
assert_err("week", &cfg);
assert_rfc("2024-03-15", "2024-03-15T00:00:00Z", &cfg);
}
}