#![allow(clippy::all, clippy::pedantic, clippy::restriction, warnings)]
#[cfg(feature = "lang")]
mod tests {
use deep_time::civil_parts::Parts;
use deep_time::{Dt, Lang, Mode, Order, ParseCfg, Scale};
#[cfg(any(feature = "jiff-tz-bundle", feature = "jiff-tz"))]
#[test]
fn roundtrip_gap_boundary_new_york() {
let our_input = "2023-03-12 02:00:00 America/New_York";
let expected_snapped = "2023-03-12 03:00:00 America/New_York";
let our_dt: Dt = Dt::from_str_parse(our_input, &ParseCfg::DEFAULT)
.expect("parse should succeed (lenient gap handling)");
assert_eq!(
our_dt.to_unix().to_sec(),
1678604400,
"internal unix timestamp should be the snapped UTC instant"
);
let fmt = "%Y-%m-%d %H:%M:%S %Q";
let output = our_dt
.to_str_in_tz(fmt, "America/New_York", Lang::En)
.expect("to_str_in_tz should succeed");
assert_eq!(
output, expected_snapped,
"gap time should silently snap forward to the next valid local time (post-DST)"
);
let our_dt2: Dt = Dt::from_str_parse(&output, &ParseCfg::DEFAULT)
.expect("second parse should also succeed");
let output2 = our_dt2
.to_str_in_tz(fmt, "America/New_York", Lang::En)
.expect("second format should succeed");
assert_eq!(output2, expected_snapped, "round-trip must be stable");
}
#[cfg(feature = "jiff-tz")]
#[test]
fn tz_output() {
use deep_time::{Dt, Scale};
let x: Dt = "2000-01-01 12:00:00".parse().unwrap();
let s = x
.to_str_in_tz("%A, %B %d, %Y %H:%M:%S %Q", "America/New_York", Lang::En)
.unwrap();
let b = x
.to_str_lite_in_tz("%A, %B %d, %Y %H:%M:%S %Q", "America/New_York", Lang::En)
.unwrap();
assert_eq!(s, "Saturday, January 01, 2000 07:00:00 America/New_York");
assert_eq!(
b.as_str(),
"Saturday, January 01, 2000 07:00:00 America/New_York"
);
}
fn assert_date(input: &str, expected_rfc3339: &str, opts: Option<ParseCfg>) {
let d = ParseCfg::DEFAULT;
let o = opts.as_ref().unwrap_or(&d);
let dt = Dt::from_str_parse(input.trim(), o)
.unwrap_or_else(|e| panic!("Failed to parse '{}': {}", input, e));
let actual = dt.to_str_rfc3339();
assert_eq!(actual, expected_rfc3339, "Input: {}", input);
}
fn assert_millis(input: &str, expected_millis: i128, opts: Option<ParseCfg>) {
let d = ParseCfg::DEFAULT;
let o = opts.as_ref().unwrap_or(&d);
let millis = Dt::str_to_unix_ms(input, o)
.unwrap_or_else(|| panic!("Failed millis parse: {}", input));
assert_eq!(millis, expected_millis, "Input: {}", input);
}
fn assert_fails(input: &str, opts: Option<ParseCfg>) {
let d = ParseCfg::DEFAULT;
let o = opts.as_ref().unwrap_or(&d);
assert!(
Dt::from_str_parse(input, o).is_err(),
"Expected failure: {}",
input
);
}
fn generate_date_test_cases() -> Vec<(String, String, Option<ParseCfg>)> {
let mut cases = Vec::new();
let date_only_bases = [
"2024-03-14",
"2024.03.14",
"2024/03/14",
"2024 03 14",
"20240314",
"14Mar24",
"14-Mar-24",
"14 Mar 2024",
"Mar 14 2024",
"March 14, 2024",
"14 March 2024",
"14-March-2024",
"14/Mar/2024",
"2024-W11-4",
"2024-074",
"2024/074",
"2024.074",
"240314",
];
let dt_separators = [" ", "T", "", ":"];
let time_variants = [
("", "T00:00:00Z"), ("15:30", "T15:30:00Z"),
("15:30:45", "T15:30:45Z"),
("15:30:45.123", "T15:30:45.123Z"),
("15:30:45.123456", "T15:30:45.123456Z"),
("15:30:45.123456789", "T15:30:45.123456789Z"),
("03:30:45 AM", "T03:30:45Z"),
("03:30:45 PM", "T15:30:45Z"),
("03:30:45.123456789 PM", "T15:30:45.123456789Z"),
];
let tz_variants = ["", "+0000", " +0000", "+00:00", " +00:00", "Z", "-0000"];
let prefixes = ["", " ", "Thu ", "Thu. ", "Thursday, ", "Thu, "];
for lang in [Lang::En, Lang::Es, Lang::De, Lang::Fr] {
let opts = ParseCfg {
order: Order::Year,
lang: lang,
..Default::default()
};
for date in date_only_bases {
for prefix in prefixes {
for dt_sep in dt_separators {
for (time_in, time_expected) in time_variants {
for tz in tz_variants {
if time_in.is_empty()
&& !tz.is_empty()
&& (dt_sep.is_empty() || dt_sep == " ")
{
continue;
}
if time_in.is_empty() && (dt_sep == "T" || dt_sep == ":") {
continue;
}
if (time_in.contains("PM") || time_in.contains("AM"))
&& !tz.is_empty()
&& !tz.starts_with(' ')
{
continue;
}
if dt_sep.is_empty()
&& !time_in.is_empty()
&& date.chars().any(|c| c.is_alphabetic())
{
continue;
}
if !prefix.trim().is_empty()
&& (prefix.contains("Thu") || prefix.contains("Thursday"))
&& date.contains(' ')
&& !date.chars().any(|c| c.is_alphabetic())
&& (dt_sep == ":" || dt_sep.is_empty())
&& !time_in.is_empty()
{
continue;
}
if date.contains(' ')
&& !date.chars().any(|c| c.is_alphabetic())
&& dt_sep == ":"
&& !time_in.is_empty()
{
continue;
}
if date.len() == 8
&& matches!(
date.chars().nth(4),
Some('-') | Some('/') | Some('.')
)
&& date[5..].chars().all(|c| c.is_ascii_digit())
&& !time_in.is_empty()
&& dt_sep.is_empty()
{
continue;
}
if dt_sep.is_empty()
&& !time_in.is_empty()
&& date.chars().all(|c| c.is_ascii_digit())
&& time_in.chars().all(|c| c.is_ascii_digit())
{
continue;
}
if date.len() == 6
&& date.chars().all(|c| c.is_ascii_digit())
&& dt_sep.is_empty()
&& !time_in.is_empty()
{
continue;
}
let input =
format!("{}{}{}{}{}", prefix, date, dt_sep, time_in, tz)
.trim()
.to_string();
let expected = if time_in.is_empty() {
"2024-03-14T00:00:00Z".to_string()
} else {
format!("2024-03-14{}", time_expected)
};
cases.push((input, expected, Some(opts.clone())));
}
}
}
}
}
}
let special_cases: Vec<(String, String, Option<ParseCfg>)> = vec![
(
"2024-W11".to_string(),
"2024-03-11T00:00:00Z".to_string(),
None,
),
(
"2024W11".to_string(),
"2024-03-11T00:00:00Z".to_string(),
None,
),
(
"2024-074T15:30:45.123456789Z".to_string(),
"2024-03-14T15:30:45.123456789Z".to_string(),
None,
),
(
"20240314T153045Z".to_string(),
"2024-03-14T15:30:45Z".to_string(),
None,
),
(
"2024-03-14T15:30:45Z".to_string(),
"2024-03-14T15:30:45Z".to_string(),
None,
),
(
"Thu Mar 14 15:30:45 2024".to_string(),
"2024-03-14T15:30:45Z".to_string(),
None,
),
(
"14/Mar/2024:15:30:45 +0000".to_string(),
"2024-03-14T15:30:45Z".to_string(),
None,
),
(
"20240314153045.123456789".to_string(),
"2024-03-14T15:30:45.123456789Z".to_string(),
None,
),
(
"60400".to_string(),
"2024-03-31T00:00:00Z".to_string(),
Some(ParseCfg {
mode: Mode::Scientific,
..Default::default()
}),
),
(
"05/06/2024".to_string(),
"2024-06-05T00:00:00Z".to_string(),
Some(ParseCfg {
order: Order::Day,
..Default::default()
}),
),
(
"14/03/2024 15:30".to_string(),
"2024-03-14T15:30:00Z".to_string(),
Some(ParseCfg {
parse: Some(vec!["%d/%m/%Y %H:%M".to_string()]),
..Default::default()
}),
),
("2024".to_string(), "2024-01-01T00:00:00Z".to_string(), None),
(
"-2024-03-14".to_string(),
"-2024-03-14T00:00:00Z".to_string(),
None,
),
(
"Dec 31 23:59:59".to_string(),
"2025-12-31T23:59:59Z".to_string(),
Some(ParseCfg {
ref_time: Some(Dt::from_ymd(2025, 12, 31, Scale::UTC, 23, 59, 59, 0)),
..Default::default()
}),
),
];
cases.extend(special_cases);
cases
}
#[test]
fn date_parser_roundtrip() {
let tp1 = Dt::from_sec(5, Scale::LTC);
let tp2 = Dt::from_sec(5, Scale::GPS);
let xp1 = tp1
.target(Scale::UTC)
.to_str("%Y-%m-%dT%H:%M:%S%.f", Lang::En)
.unwrap();
let xp2 = tp2
.target(Scale::UTC)
.to_str("%Y-%m-%dT%H:%M:%S%.f", Lang::En)
.unwrap();
let res_tp1 = Dt::from_str(&xp1, "%Y-%m-%dT%H:%M:%S%.f", true, true, false).unwrap();
let res_tp2 = Dt::from_str(&xp2, "%Y-%m-%dT%H:%M:%S%.f", true, true, false).unwrap();
assert!(tp1 == res_tp1);
assert!(tp2 == res_tp2);
}
#[test]
fn round_trip_fixed_offsets() {
for tp in [Dt::from_tai_sec(5), Dt::from_tai_sec(-5)] {
let xp1 = tp
.target(Scale::UTC)
.to_str_in_offset("%Y-%m-%dT%H:%M:%S%.~f %:z", 3600, Lang::En)
.unwrap();
let tp2 = Dt::from_str_parse(&xp1, &ParseCfg::DEFAULT).unwrap();
let xp2 = tp2
.to_str_in_offset("%Y-%m-%dT%H:%M:%S%.~f %:z", 3600, Lang::En)
.unwrap();
let tp3 = Dt::from_str_parse(&xp2, &ParseCfg::DEFAULT).unwrap();
assert_eq!(tp, tp3);
}
}
#[test]
fn date_parser_comprehensive() {
let cases: Vec<(&str, &str, Option<ParseCfg>)> = vec![
(
"2440587.5",
"1970-01-01T00:00:00Z",
Some(ParseCfg {
mode: Mode::Scientific,
..Default::default()
}),
),
(
"2024-03-14 03:30:45.123 PM",
"2024-03-14T15:30:45.123Z",
None,
),
("2024.03.14", "2024-03-14T00:00:00Z", None),
("The 14th of March, 2024", "2024-03-14T00:00:00Z", None),
("2024-03-14", "2024-03-14T00:00:00Z", None),
("2024/03/14", "2024-03-14T00:00:00Z", None),
("2024 03 14", "2024-03-14T00:00:00Z", None),
("2024.03.14 15:30:45", "2024-03-14T15:30:45Z", None),
("2024-03-14 15:30:45 +0000", "2024-03-14T15:30:45Z", None),
("2024-03-14 15:30:45 +00:00", "2024-03-14T15:30:45Z", None),
("2024-03-14T15:30:45 +00:00", "2024-03-14T15:30:45Z", None),
(
"2024.03.14 15:30:45.123456789",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"2024-03-14T15:30:45.123456789 +00:00",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"2024-03-14T15:30:45.123456789 +0000",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"2024-03-14 15:30:45.123456789 +00:00",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"2024-03-14 15:30:45.123456789 +0000",
"2024-03-14T15:30:45.123456789Z",
None,
),
("14Mar24", "2024-03-14T00:00:00Z", None),
("20240314", "2024-03-14T00:00:00Z", None),
("2024-W11", "2024-03-11T00:00:00Z", None),
("2024-W11", "2024-03-11T00:00:00Z", None),
("2024-074", "2024-03-14T00:00:00Z", None),
("2024/074", "2024-03-14T00:00:00Z", None),
("2024.074", "2024-03-14T00:00:00Z", None),
("14Mar2024", "2024-03-14T00:00:00Z", None),
("14-Mar-24", "2024-03-14T00:00:00Z", None),
(" 14-Mar-24", "2024-03-14T00:00:00Z", None),
("2024-W11-4", "2024-03-14T00:00:00Z", None),
("2024-W11-4", "2024-03-14T00:00:00Z", None),
("14 Mar 2024", "2024-03-14T00:00:00Z", None),
("14-Mar-2024", "2024-03-14T00:00:00Z", None),
("Mar 14 2024", "2024-03-14T00:00:00Z", None),
("2024 Mar 14", "2024-03-14T00:00:00Z", None),
("14 Mar 2024", "2024-03-14T00:00:00Z", None),
("14-Mar-2024", "2024-03-14T00:00:00Z", None),
("20240314 15:30", "2024-03-14T15:30:00Z", None),
("2024-074 15:30", "2024-03-14T15:30:00Z", None),
("2024.074 15:30", "2024-03-14T15:30:00Z", None),
("2024/074 15:30", "2024-03-14T15:30:00Z", None),
("20240314T153045", "2024-03-14T15:30:45Z", None),
("2024-03-14 15:30", "2024-03-14T15:30:00Z", None),
("2024-03-14T15:30", "2024-03-14T15:30:00Z", None),
("14-03-2024 15:30", "2024-03-14T15:30:00Z", None),
("14.03.2024 15:30", "2024-03-14T15:30:00Z", None),
("2024 03 14 15:30", "2024-03-14T15:30:00Z", None),
("2024-074T15:30:45", "2024-03-14T15:30:45Z", None),
("2024-074 15:30:45", "2024-03-14T15:30:45Z", None),
("2024/074T15:30:45", "2024-03-14T15:30:45Z", None),
("2024/074 15:30:45", "2024-03-14T15:30:45Z", None),
("2024.074T15:30:45", "2024-03-14T15:30:45Z", None),
("2024.074 15:30:45", "2024-03-14T15:30:45Z", None),
("2024 Mar 14 15:30", "2024-03-14T15:30:00Z", None),
("14 Mar 2024 15:30", "2024-03-14T15:30:00Z", None),
("2024 Mar 14 15:30", "2024-03-14T15:30:00Z", None),
("20240314 15:30:45", "2024-03-14T15:30:45Z", None),
("2024-03-1415:30:45", "2024-03-14T15:30:45Z", None),
("2024-03-14 15:30:45", "2024-03-14T15:30:45Z", None),
("2024-03-14T15:30:45", "2024-03-14T15:30:45Z", None),
("2024/03/14 15:30:45", "2024-03-14T15:30:45Z", None),
("2024 03 14 15:30:45", "2024-03-14T15:30:45Z", None),
("14-03-2024 15:30:45", "2024-03-14T15:30:45Z", None),
("14.03.2024 15:30:45", "2024-03-14T15:30:45Z", None),
("2024-W11-4 15:30:45", "2024-03-14T15:30:45Z", None),
("2024-W11-4 15:30:45", "2024-03-14T15:30:45Z", None),
("14/Mar/2024:15:30:45", "2024-03-14T15:30:45Z", None),
("14-Mar-2024 15:30:45", "2024-03-14T15:30:45Z", None),
("Mar 14 2024 15:30:45", "2024-03-14T15:30:45Z", None),
("14 Mar 2024 15:30:45", "2024-03-14T15:30:45Z", None),
("2024 Mar 14 15:30:45", "2024-03-14T15:30:45Z", None),
("2024 Mar 14 15:30:45", "2024-03-14T15:30:45Z", None),
("14 Mar 2024 15:30:45", "2024-03-14T15:30:45Z", None),
("14-Mar-2024 15:30:45", "2024-03-14T15:30:45Z", None),
("14 Mar, 2024 15:30:45", "2024-03-14T15:30:45Z", None),
("Mar 14, 2024 15:30:45", "2024-03-14T15:30:45Z", None),
("2024.03.14 15:30:45", "2024-03-14T15:30:45Z", None),
("2024-03-14 15:30:45 +0000", "2024-03-14T15:30:45Z", None),
("2024-03-14 15:30:45 +00:00", "2024-03-14T15:30:45Z", None),
("2024-03-14T15:30:45 +00:00", "2024-03-14T15:30:45Z", None),
(
"2024-03-14T15:30:45.123456789 +00:00",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"2024-03-14T15:30:45.123456789 +0000",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"2024-03-14 15:30:45.123456789 +00:00",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"2024-03-14 15:30:45.123456789 +0000",
"2024-03-14T15:30:45.123456789Z",
None,
),
("2024-03-1415:30:45.123", "2024-03-14T15:30:45.123Z", None),
("14 Mar 2024 03:30:45 PM", "2024-03-14T15:30:45Z", None),
("Mar 14 2024 03:30:45 PM", "2024-03-14T15:30:45Z", None),
("Mar 14, 2024 03:30:45 PM", "2024-03-14T15:30:45Z", None),
("2024-03-14 15:30:45+0000", "2024-03-14T15:30:45Z", None),
("2024-03-14T15:30:45+0000", "2024-03-14T15:30:45Z", None),
("Thu Mar 14 15:30:45 2024", "2024-03-14T15:30:45Z", None),
("14 Mar, 2024 03:30:45 PM", "2024-03-14T15:30:45Z", None),
("2024-03-14T15:30:45+00:00", "2024-03-14T15:30:45Z", None),
(
"2024-03-1415:30:45.123456",
"2024-03-14T15:30:45.123456Z",
None,
),
("14/Mar/2024:15:30:45 +0000", "2024-03-14T15:30:45Z", None),
(
"Thu Mar 14 15:30:45 +00:00 2024",
"2024-03-14T15:30:45Z",
None,
),
("Mar 14, 2024 15:30:45 +0000", "2024-03-14T15:30:45Z", None),
("Mar 14, 2024 15:30:45 +00:00", "2024-03-14T15:30:45Z", None),
(
"Thu Mar 14 15:30:45 +0000 2024",
"2024-03-14T15:30:45Z",
None,
),
(
"Thu, 14 Mar 2024 15:30:45 +0000",
"2024-03-14T15:30:45Z",
None,
),
(
"Thu Mar 14 15:30:45 +00:00 2024",
"2024-03-14T15:30:45Z",
None,
),
("20240314T153045Z", "2024-03-14T15:30:45Z", None),
("2024-03-14T15:30:45Z", "2024-03-14T15:30:45Z", None),
("March 14, 2024 03:30:45 PM", "2024-03-14T15:30:45Z", None),
("March 14, 2024 15:30:45", "2024-03-14T15:30:45Z", None),
(
"March 14, 2024 03:30:45.123456789 PM",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"March 14, 2024 15:30:45.123456789",
"2024-03-14T15:30:45.123456789Z",
None,
),
("March 14, 2024", "2024-03-14T00:00:00Z", None),
(
"March 14, 2024 15:30:45 +00:00",
"2024-03-14T15:30:45Z",
None,
),
(
"March 14, 2024 15:30:45 +0000",
"2024-03-14T15:30:45Z",
None,
),
(
"March 14 2024 15:30:45 +00:00",
"2024-03-14T15:30:45Z",
None,
),
(
"Mar 14, 2024 03:30:45.123456789 PM",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"Mar 14, 2024 15:30:45.123456789",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"Mar 14 2024 03:30:45.123456789 PM",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"Mar 14 2024 15:30:45.123456789",
"2024-03-14T15:30:45.123456789Z",
None,
),
("Thursday, 14 March 2024", "2024-03-14T00:00:00Z", None),
("Thursday 14 March 2024", "2024-03-14T00:00:00Z", None),
("Thursday, 14 Mar 2024", "2024-03-14T00:00:00Z", None),
("Thursday 14 Mar 2024", "2024-03-14T00:00:00Z", None),
(
"Thursday, 14 Mar 2024 15:30:45.123456789",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"Thursday 14 Mar 2024 15:30:45.123456789",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"Thu Mar 14 15:30:45.123456789 2024",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"Thu Mar 14 15:30:45.123456789 +00:00 2024",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"Thu Mar 14 15:30:45.123456789 +0000 2024",
"2024-03-14T15:30:45.123456789Z",
None,
),
("14 March 2024", "2024-03-14T00:00:00Z", None),
("14 March, 2024", "2024-03-14T00:00:00Z", None),
("14 March 2024", "2024-03-14T00:00:00Z", None),
("14 March, 2024", "2024-03-14T00:00:00Z", None),
("14-March-2024", "2024-03-14T00:00:00Z", None),
("14 March 2024 15:30:45", "2024-03-14T15:30:45Z", None),
("14 March, 2024 15:30:45", "2024-03-14T15:30:45Z", None),
("14 March 2024 15:30:45", "2024-03-14T15:30:45Z", None),
("14-March-2024 15:30:45", "2024-03-14T15:30:45Z", None),
("Mar 14, 2024 15:30:45 +00:00", "2024-03-14T15:30:45Z", None),
(
"Mar 14, 2024 15:30:45.123456789 +00:00",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"Mar 14, 2024 15:30:45.123456789 +0000",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"14 Mar, 2024 15:30:45.123456789 +00:00",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"14 Mar, 2024 15:30:45.123456789 +0000",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"14 March 2024 15:30:45.123456789",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"14 March, 2024 15:30:45.123456789",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"14 March 2024 15:30:45.123456789",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"14-March-2024 15:30:45.123456789",
"2024-03-14T15:30:45.123456789Z",
None,
),
("14 March 2024 03:30:45 PM", "2024-03-14T15:30:45Z", None),
("14 March, 2024 03:30:45 PM", "2024-03-14T15:30:45Z", None),
(
"14 March 2024 03:30:45.123456789 PM",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"14 March, 2024 03:30:45.123456789 PM",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"14 Mar 2024 15:30:45.123456789",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"14 Mar, 2024 15:30:45.123456789",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"14 Mar, 2024 03:30:45.123456789 PM",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"14 Mar 2024 15:30:45.123456789",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"14-Mar-2024 15:30:45.123456789",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"14/Mar/2024:15:30:45.123456789 +0000",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"20240314T153045.123456789Z",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"2024-074T15:30:45.123456789Z", "2024-03-14T15:30:45.123456789Z",
None,
),
(
"2024.074T15:30:45.123456789Z",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"2024/074T15:30:45.123456789Z",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"2024-03-14T15:30:45.123456789",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"2024-03-14T15:30:45.123456789+0000",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"2024-03-14 15:30:45.123456789",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"2024-03-1415:30:45.123456789",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"2024/03/14 15:30:45.123456789",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"20240314T153045.123456789",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"20240314153045.123456789",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"20240314 15:30:45.123456789",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"2024 March 14 15:30:45.123456789",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"2024 Mar 14 15:30:45.123456789",
"2024-03-14T15:30:45.123456789Z",
None,
),
("2024 March 14 15:30:45", "2024-03-14T15:30:45Z", None),
("2024 March 14", "2024-03-14T00:00:00Z", None),
(
"2024 Mar 14 15:30:45.123456789",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"2024 03 14 15:30:45.123456789",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"2024-074T15:30:45.123456789",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"2024-074 15:30:45.123456789",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"2024-074T15:30:45.123456789+0000",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"2024-074 15:30:45.123456789+0000",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"2024-074T15:30:45.123456789+00:00",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"2024-074 15:30:45.123456789+00:00",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"2024.074T15:30:45.123456789",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"2024.074 15:30:45.123456789",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"2024.074T15:30:45.123456789+0000",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"2024.074T15:30:45.123456789+00:00",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"2024/074T15:30:45.123456789",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"2024/074 15:30:45.123456789",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"2024/074T15:30:45.123456789+0000",
"2024-03-14T15:30:45.123456789Z",
None,
),
(
"2024/074T15:30:45.123456789+00:00",
"2024-03-14T15:30:45.123456789Z",
None,
),
("2024-03-14", "2024-03-14T00:00:00Z", None),
("14 Mar 2024", "2024-03-14T00:00:00Z", None),
("15 Mar 2024 14:30", "2024-03-15T14:30:00Z", None),
("Mar 14, 2024", "2024-03-14T00:00:00Z", None),
("2024/03/14", "2024-03-14T00:00:00Z", None),
("14.03.2024", "2024-03-14T00:00:00Z", None),
("14/03.2024", "2024-03-14T00:00:00Z", None),
("14/03.2024T00:00 -1", "2024-03-14T01:00:00Z", None),
("240314", "2024-03-14T00:00:00Z", None),
("202403", "2024-03-01T00:00:00Z", None),
("2024073", "2024-03-13T00:00:00Z", None),
("24073", "2024-03-13T00:00:00Z", None),
("60400", "2024-03-31T00:00:00Z", None),
("2460000", "2023-02-25T00:00:00Z", None),
("1700000000", "2023-11-14T22:13:20Z", None),
("1700000000000", "2023-11-14T22:13:20Z", None),
(
"60400",
"2024-03-31T00:00:00Z",
Some(ParseCfg {
parse: None,
mode: Mode::Scientific,
order: Order::default(),
..Default::default()
}),
),
(
"24073",
"2024-03-13T00:00:00Z",
Some(ParseCfg {
parse: None,
mode: Mode::Legacy,
order: Order::default(),
..Default::default()
}),
),
(
"05/06/2024",
"2024-06-05T00:00:00Z",
Some(ParseCfg {
parse: None,
mode: Mode::Scientific,
order: Order::Day,
..Default::default()
}),
),
(
"05/06/2024",
"2024-05-06T00:00:00Z",
Some(ParseCfg {
order: Order::Month,
..Default::default()
}),
),
("2024", "2024-01-01T00:00:00Z", None),
("24", "2024-01-01T00:00:00Z", None),
("2024.074", "2024-03-14T00:00:00Z", None),
("-2024-03-14", "-2024-03-14T00:00:00Z", None),
("-2025/01/01", "-2025-01-01T00:00:00Z", None),
("-0001-01-01", "-0001-01-01T00:00:00Z", None),
(
"Dec 31 23:59:59",
"2025-12-31T23:59:59Z",
Some(ParseCfg {
ref_time: Some(Dt::from_ymd(2025, 12, 31, Scale::UTC, 23, 59, 59, 0)),
..Default::default()
}),
),
(
"14/03/2024 15:30",
"2024-03-14T15:30:00Z",
Some(ParseCfg {
parse: Some(vec!["%d/%m/%Y %H:%M".to_string()]),
..Default::default()
}),
),
(
"2026年4月5日",
"2026-04-05T00:00:00Z",
Some(ParseCfg {
order: Order::Year,
..Default::default()
}),
),
(
"߂߀߂߄-߀߄-߀߅",
"2024-04-05T00:00:00Z",
Some(ParseCfg {
order: Order::Year,
..Default::default()
}),
),
(
"on the 5th of april 2024 at 00:00am",
"2024-04-05T00:00:00Z",
None,
),
];
for (input, expected, opts) in cases {
assert_date(input, expected, opts);
}
let cases = generate_date_test_cases();
for (input, expected, opts) in cases {
assert_date(&input, &expected, opts);
}
}
fn generate_relative_duration_test_cases() -> Vec<String> {
let mut cases: Vec<String> = Vec::new();
let core_phrases = ["now", "today", "tomorrow", "yesterday"];
cases.extend(core_phrases.iter().map(|&s| s.to_string()));
let numbers = [
"1", "2", "3", "5", "10", "42", "0.5", "1.5", "2,5", "3.75", "1_000",
];
let units = [
"sec", "second", "seconds", "min", "minute", "minutes", "hr", "hour", "hours", "day",
"days", "wk", "week", "weeks", "mo", "month", "months", "yr", "year", "years",
];
let past_suffixes = [" ago"];
let future_prefixes = ["in "];
for num in numbers {
for unit in units {
for suffix in past_suffixes {
cases.push(format!("{} {}{}", num, unit, suffix));
}
for prefix in future_prefixes {
if !prefix.is_empty() || num.parse::<f64>().unwrap_or(0.0) != 1.0 {
cases.push(format!("{}{} {}", prefix, num, unit).trim().to_string());
}
}
}
}
let multi_unit_cases = [
"2 hours and 30 minutes ago",
"1 day 12 hours from now",
"3 weeks 4 days later",
"1day ,5hr ago",
"2hrs30min from now",
"45min 15sec later",
"1 day, 2 hours, and 30 minutes ago",
"the 1.5 days ago",
"in 2day 3hr 45min",
"1 week and 2 days from now",
"2 weeks 3 days 4 hours ago",
"in 1 week and 2 days",
"3 days 5 hours later",
];
cases.extend(multi_unit_cases.iter().map(|&s| s.to_string()));
cases
}
#[test]
fn relative_duration_parser_comprehensive() {
let cases = generate_relative_duration_test_cases();
let opts = ParseCfg {
ref_time: Some(Dt::from_tai_sec(5_000_000)),
..Default::default()
};
for input in cases {
let result = Dt::from_str_parse(input.trim(), &opts);
assert!(result.is_ok(), "Failed to parse relative date: '{}'", input);
}
}
#[test]
fn date_millis_and_errors() {
assert_millis("2024-03-14", 1710374400000, None);
assert_millis("1700000000000", 1700000000000, None);
assert_fails("not-a-date", None);
assert_fails("2024-13-01", None);
}
}