From ae822e4b9a6d29b6048bf6507d68ec3096ba5bce Mon Sep 17 00:00:00 2001 From: Christopher Illarionova Date: Wed, 4 Feb 2026 14:39:20 +0000 Subject: [PATCH 1/2] fix: TZ prefix should override base date timezone in parse_datetime_at_date --- src/items/builder.rs | 5 ++++- tests/date.rs | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/items/builder.rs b/src/items/builder.rs index 7aa033a..199ddbc 100644 --- a/src/items/builder.rs +++ b/src/items/builder.rs @@ -187,8 +187,11 @@ impl DateTimeBuilder { /// - e. Apply final fixed offset if present. pub(super) fn build(self) -> Result { // 1. Choose the base instant. + // If a TZ="..." prefix was parsed, it should override the base's timezone + // while keeping the base's timestamp for relative date calculations. let base = match (self.base, &self.timezone) { - (Some(b), _) => b, + (Some(b), Some(tz)) => b.timestamp().to_zoned(tz.clone()), + (Some(b), None) => b, (None, Some(tz)) => jiff::Timestamp::now().to_zoned(tz.clone()), (None, None) => Zoned::now(), }; diff --git a/tests/date.rs b/tests/date.rs index 6944d77..0f12cc0 100644 --- a/tests/date.rs +++ b/tests/date.rs @@ -95,3 +95,20 @@ fn test_date_omitting_year(#[case] input: &str, #[case] year: u32, #[case] expec .unwrap(); check_relative(now, input, expected); } + +#[rstest] +#[case::tz_prefix_est5("TZ=\"EST5\" 1970-01-01 00:00", "1970-01-01 00:00:00-05:00")] +#[case::tz_prefix_pst8("TZ=\"PST8\" 1970-01-01 00:00", "1970-01-01 00:00:00-08:00")] +#[case::tz_prefix_utc("TZ=\"UTC\" 1970-01-01 12:00", "1970-01-01 12:00:00+00:00")] +#[case::tz_prefix_europe_paris( + r#"TZ="Europe/Paris" 2025-01-02 03:04:05"#, + "2025-01-02 03:04:05+01:00" +)] +fn test_tz_prefix_with_base_date(#[case] input: &str, #[case] expected: &str) { + let base = "2020-06-15 12:00:00" + .parse::() + .unwrap() + .to_zoned(TimeZone::UTC) + .unwrap(); + check_relative(base, input, expected); +} From 1cafd1fd9330798332d8b743c823375feaf17d7b Mon Sep 17 00:00:00 2001 From: Christopher Illarionova Date: Thu, 12 Feb 2026 01:43:30 +0000 Subject: [PATCH 2/2] Remove unnecessary clones by consuming self.timezone in match --- src/items/builder.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/items/builder.rs b/src/items/builder.rs index 199ddbc..5fe841c 100644 --- a/src/items/builder.rs +++ b/src/items/builder.rs @@ -189,10 +189,11 @@ impl DateTimeBuilder { // 1. Choose the base instant. // If a TZ="..." prefix was parsed, it should override the base's timezone // while keeping the base's timestamp for relative date calculations. - let base = match (self.base, &self.timezone) { - (Some(b), Some(tz)) => b.timestamp().to_zoned(tz.clone()), + let has_timezone = self.timezone.is_some(); + let base = match (self.base, self.timezone) { + (Some(b), Some(tz)) => b.timestamp().to_zoned(tz), (Some(b), None) => b, - (None, Some(tz)) => jiff::Timestamp::now().to_zoned(tz.clone()), + (None, Some(tz)) => jiff::Timestamp::now().to_zoned(tz), (None, None) => Zoned::now(), }; @@ -207,7 +208,7 @@ impl DateTimeBuilder { || self.time.is_some() || self.weekday.is_some() || self.offset.is_some() - || self.timezone.is_some(); + || has_timezone; let mut dt = if need_midnight { base.with().time(civil::time(0, 0, 0, 0)).build()?