adriangb opened a new issue, #11039:
URL: https://github.com/apache/arrow-rs/issues/11039

   **Describe the bug**
   
   `string_to_datetime` in `arrow-cast/src/parse.rs`, and therefore every 
`Utf8`/`LargeUtf8`/`Utf8View` → `Timestamp(_, Some(tz))` cast, fails for 
wall-clock readings that fall on a daylight saving transition of a **named** 
timezone:
   
   - the hour repeated by a "fall back" transition (ambiguous), and
   - the hour skipped by a "spring forward" transition (nonexistent).
   
   The failure is `Error parsing timestamp from '...': error computing timezone 
offset`. Under `CastOptions { safe: true }` the affected values silently become 
`NULL`. It affects both a string without a zone that is interpreted in the 
target timezone, and a string that carries the zone explicitly 
(`'2024-03-10T02:30:00 America/New_York'`).
   
   This is the string-parsing sibling of #11037. The cause is the same: three 
call sites in `string_to_datetime` resolve the local time with 
`LocalResult::single()`, which is `None` for both `LocalResult::Ambiguous` and 
`LocalResult::None`:
   
   ```rust
   return timezone
       .from_local_datetime(&datetime)
       .single()
       .ok_or_else(|| err("error computing timezone offset"));
   // ... (twice more, including for the parsed `Tz` suffix)
   ```
   
   **To Reproduce**
   
   arrow-cast 59.3.0, arrow-array with the `chrono-tz` feature:
   
   ```rust
   use std::sync::Arc;
   use arrow_array::{Array, ArrayRef, StringArray};
   use arrow_array::timezone::Tz;
   use arrow_cast::parse::string_to_datetime;
   use arrow_cast::{cast_with_options, CastOptions};
   use arrow_schema::{DataType, TimeUnit};
   
   fn main() {
       let tz: Tz = "America/New_York".parse().unwrap();
       for s in [
           "2024-11-01T00:00:00",                  // unambiguous
           "2024-11-03T01:30:00",                  // ambiguous   (fall back)
           "2024-03-10T02:30:00",                  // nonexistent (spring 
forward)
           "2024-03-10T02:30:00 America/New_York", // nonexistent, zone given 
in the string
       ] {
           println!("{s:<40} -> {:?}", string_to_datetime(&tz, s).map(|d| 
d.to_rfc3339()));
       }
   
       let strings: ArrayRef = Arc::new(StringArray::from(vec![
           "2024-11-01T00:00:00", "2024-11-03T01:30:00", "2024-03-10T02:30:00",
       ]));
       let to = DataType::Timestamp(TimeUnit::Second, 
Some("America/New_York".into()));
       let strict = CastOptions { safe: false, ..Default::default() };
       println!("Utf8 -> Timestamp(tz) safe=false: {:?}", 
cast_with_options(&strings, &to, &strict).map(|a| a.len()));
       let out = cast_with_options(&strings, &to, &CastOptions { safe: true, 
..Default::default() }).unwrap();
       let shown: Vec<String> = (0..out.len())
           .map(|i| arrow_cast::display::array_value_to_string(&out, 
i).unwrap())
           .collect();
       println!("Utf8 -> Timestamp(tz) safe=true:  {shown:?}");
   }
   ```
   
   ```
   2024-11-01T00:00:00                      -> Ok("2024-11-01T00:00:00-04:00")
   2024-11-03T01:30:00                      -> Err(ParseError("Error parsing 
timestamp from '2024-11-03T01:30:00': error computing timezone offset"))
   2024-03-10T02:30:00                      -> Err(ParseError("Error parsing 
timestamp from '2024-03-10T02:30:00': error computing timezone offset"))
   2024-03-10T02:30:00 America/New_York     -> Err(ParseError("Error parsing 
timestamp from '2024-03-10T02:30:00 America/New_York': error computing timezone 
offset"))
   Utf8 -> Timestamp(tz) safe=false: Err(ParseError("Error parsing timestamp 
from '2024-11-03T01:30:00': error computing timezone offset"))
   Utf8 -> Timestamp(tz) safe=true:  ["2024-11-01T00:00:00-04:00", "", ""]
   ```
   
   **Expected behavior**
   
   The same resolution as #11037, which records the PostgreSQL and DuckDB 
outputs and links to the source of both:
   
   - ambiguous → the **later** instant (post-transition offset): 
`2024-11-03T01:30:00` → `2024-11-03T01:30:00-05:00`;
   - nonexistent → shift **forward** by the gap (pre-transition offset): 
`2024-03-10T02:30:00` → `2024-03-10T03:30:00-04:00`.
   
   **Additional context**
   
   - Once https://github.com/apache/arrow-rs/pull/11038 lands, arrow-cast is 
inconsistent with itself: casting the naive **timestamp** `2024-03-10T02:30:00` 
to `Timestamp(_, Some("America/New_York"))` returns `03:30:00-04:00`, while 
casting the **string** `'2024-03-10T02:30:00'` to the same type still errors. 
Same wall-clock value, same target type, different result depending on the 
source type.
   - In DataFusion this is the difference between 
`'2024-03-10T02:30:00'::timestamp::timestamptz` (works after 11038) and 
`'2024-03-10T02:30:00'::timestamptz` (still fails) under a named session 
timezone. DataFusion's `timestamps.slt` currently asserts that `TIMESTAMPTZ 
'2023-03-12 02:00:00 America/Los_Angeles'` is an error, so that expectation 
flips when this is fixed.
   - Kept separate from #11037 because `string_to_datetime` is public API and 
generic over `T: TimeZone`, so the fix has a different shape: 
`from_local_datetime(..).latest()` for the overlap, and for the gap a probe of 
`from_local_datetime(dt - 24h).earliest()` to recover the pre-transition offset 
followed by `from_utc_datetime`. Three call sites plus tests for the 
zone-suffix form.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to