nevi-me opened a new issue, #23841:
URL: https://github.com/apache/datafusion/issues/23841

   ### Describe the bug
   
   Since #19078, the `to_timestamp` / `to_timestamp_seconds` / 
`to_timestamp_millis` / `to_timestamp_micros` / `to_timestamp_nanos` functions 
override the timezone of an input that is already timezone-aware with the 
session's `datafusion.execution.time_zone`.
   
   When that config is unset (the default `None`), an input of type 
`Timestamp(_, Some(tz))` is returned as `Timestamp(_, None)`. The zone 
annotation is dropped while the underlying epoch is kept, which silently 
changes the wall-clock meaning of the value.
   
   Previously, these functions had a dedicated match arm that preserved a zoned 
input's timezone:
   
   ```rust
   // before #19078
   Timestamp(_, Some(tz)) => args[0].cast_to(&Timestamp(Millisecond, Some(tz)), 
None),
   ```
   
   #19078 collapsed that arm into the generic one, which casts to the execution 
timezone:
   
   ```rust
   // after #19078 (current main)
   Null | Int32 | Int64 | Timestamp(_, _) => 
args[0].cast_to(&Timestamp(Millisecond, self.timezone.clone()), None),
   ```
   
   
   The PR's rationale when I read it, only concerns naive timestamp strings 
being interpreted in the execution timezone, which is reasonable. Dropping the 
timezone of inputs that are already zoned appears to be an unintended side 
effectas it makes a normalisation function silently discard a caller's explicit 
timezone.
   
   ### To Reproduce
   
   Run in `datafusion-cli` with default settings (no `SET 
datafusion.execution.time_zone`):
   
   ```sql
   -- input epoch = 2024-01-01T00:00:00Z, annotated +05:00 => local wall clock 
05:00
   SELECT
     arrow_typeof(to_timestamp_millis(arrow_cast(1704067200000, 
'Timestamp(Millisecond, Some("+05:00"))'))) AS result_type,
     date_part('hour', arrow_cast(1704067200000, 'Timestamp(Millisecond, 
Some("+05:00"))')) AS hour_direct,
     date_part('hour', to_timestamp_millis(arrow_cast(1704067200000, 
'Timestamp(Millisecond, Some("+05:00"))'))) AS hour_via_to_timestamp;
   ```
   
   **Actual**
   
   | result_type | hour_direct | hour_via_to_timestamp |
   |---|---|---|
   | `Timestamp(Millisecond, None)` | 5 | 0 |
   
   **Befor the change**
   
   | result_type | hour_direct | hour_via_to_timestamp |
   |---|---|---|
   | `Timestamp(Millisecond, Some("+05:00"))` | 5 | 5 |
   
   ### Expected behavior
   
   `to_timestamp_millis` (and the rest of the family) should not silently drop 
the timezone of an already-zoned input. Passing a `Timestamp(_, 
Some("+05:00"))` through the function should either:
   
   1. preserve the input zone, or
   2. if honoring `datafusion.execution.time_zone`, convert the timezone but 
never relabel to a naive type, which changes the wall-clock value.
   
   In particular, when `datafusion.execution.time_zone` is unset (`None`), a 
zoned input should keep its zone rather than becoming naive. As it stands, 
`date_part('hour', to_timestamp_millis(<zoned column>))` returns the UTC hour 
instead of the local hour, which corrupts any hour-of-day / day-of-month 
bucketing over zoned columns.
   
   ### Additional context
   
   Relevant commit: `ada0923a3927d16dc5d810637cfbea4146c54f54` (PR #19078 that 
closed #17998)


-- 
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]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to