Repository navigation
Closed
Closed
Copy link
Labels
bugSomething isn't workingSomething isn't working
Description
Describe the bug
Using the ::timestamp (or CAST()) function strips timezone information.
To Reproduce
DataFusion CLI v40.0.0
> select arrow_typeof(now()) as tz;
+---------------------------------------+
| tz |
+---------------------------------------+
| Timestamp(Nanosecond, Some("+00:00")) |
+---------------------------------------+
> select arrow_typeof(now()::timestamp) as tz;
+-----------------------------+
| tz |
+-----------------------------+
| Timestamp(Nanosecond, None) |
+-----------------------------+
> select arrow_typeof((now() AT TIME ZONE 'America/Denver')::timestamp) as tz;
+-----------------------------+
| tz |
+-----------------------------+
| Timestamp(Nanosecond, None) |
+-----------------------------+
Or as seen with actual values:
DataFusion CLI v40.0.0
> select now();
+-----------------------------+
| now() |
+-----------------------------+
| 2024-08-28T17:08:08.698528Z |
+-----------------------------+
> select (now() AT TIME ZONE 'America/Denver');
+----------------------------------+
| now() |
+----------------------------------+
| 2024-08-28T11:08:13.578025-06:00 |
+----------------------------------+
> select now()::timestamp;
+----------------------------+
| now() |
+----------------------------+
| 2024-08-28T17:08:17.228985 |
+----------------------------+
> select (now() AT TIME ZONE 'America/Denver')::timestamp;
+----------------------------+
| now() |
+----------------------------+
| 2024-08-28T17:08:20.042920 |
+----------------------------+
Expected behavior
I would expect that using the ::timestamp cast on a timestamp would not impact its timezone. This is how Postgres behaves.
postgres@localhost:postgres> select now()::timestamp
+----------------------------+
| now |
|----------------------------|
| 2024-08-28 17:05:07.779323 |
+----------------------------+
postgres@localhost:postgres> select (now() at time zone 'America/Denver')::timestamp
+----------------------------+
| timezone |
|----------------------------|
| 2024-08-28 11:05:09.275359 |
+----------------------------+
Additional context
No response
Metadata
Metadata
Assignees
Labels
bugSomething isn't workingSomething isn't working
Activity
devanbenz commentedon Aug 30, 2024
I'd be happy to take this if it's not already being worked on.
devanbenz commentedon Aug 30, 2024
take
findepi commentedon Aug 30, 2024
i'd expect
::timestamp/CAST(x AS timestamp)to cast to a timestamp without time zone type.Is this what
Nonezone indicates?devanbenz commentedon Aug 30, 2024
@findepi It appears so
findepi commentedon Aug 31, 2024
The SQL spec compliant behavior when casting
timestamp with time zonetotimestampis to strip time zone information and retain year/month/day/hour/minute/second fields.You can observe this behavior in Trino
In a sense PostgreSQL's
timestamp with time zonetotimestampcast follows the spec because PostgreSQL'stimestamp with time zonevalue doesn't include the time zone information. It's "a point in time in UTC.Now, DF behavior when casting
timestamp with time zonetotimestampseems to be to convert timestamp with time zone to UTC (retaining point in time) and then strip zoneIMO we should change DF cast from
timestamp with time zonetotimestampso that it retains year/month/day/hour/minute/second fields and strips the zone information.cc @alamb
findepi commentedon Aug 31, 2024
apache/arrow-rs#5827 seems related.
devanbenz commentedon Sep 1, 2024
So would the expected outcome be that
timestampwill always have UTC time (which it seems to already be currently doing)?findepi commentedon Sep 2, 2024
I wouldn't say that timestamp has UTC time.
timestamp is local date/time. You can think of it as a struct with fields year/month/day/hour/minute/seconds(fractional) and it has no relationship with UTC.
However, when doing math on timestamp values using a date/time library it's often to represent it as UTC-based.
devanbenz commentedon Sep 2, 2024
Makes sense, so the expectation is for the date-time calculation to be correct for that TZ but no offset?
So for the example above:
Should actually be:
findepi commentedon Sep 4, 2024
@devanbenz Agreed
if
now() AT TIME ZONE 'America/Denver'is2024-08-28T11:08:13.578025-06:00(or2024-08-28 11:08:13.578025 America/Denver)then
(now() AT TIME ZONE 'America/Denver')::timestampshould be2024-08-28[T]11:08:13.578025devanbenz commentedon Sep 5, 2024
Okay sounds good @findepi -- I do see: https://github.com/apache/arrow-rs/pull/5831/files which appears to be relevant. I think this change would be better suited for arrow vs. datafusion I suppose?
findepi commentedon Sep 6, 2024
@devanbenz i think so, yes
devanbenz commentedon Sep 10, 2024
@findepi @alamb It actually appears that datafusion (and in regards apache arrow) seems to be casting as expected afaik. It looks like the
now()udf in datafusion is inconsistent with how other engines do it. Taking a look at 2 other systemsPostgresandClickhouseI'm seeing that when callingnow()without any adjustments it returns the local TZ. It appears that datafusion is returning Zulu time. Not sure if this is by design. But I'm recalling a thread: #12357 (comment)Should built in functions be following PG as well?
Postgres
Clickhouse
Datafusion
Note: All queries were ran at around 11:30 AM CST. The PG and CH queries are using local TZ where as DF does not and uses Zulu time instead. Clickhouse does not support timestamptz hence why it was not in the query outputs given in the examples.
Taking a look at a similar query with
ClickhouseI see that casting to atimestampwill strip the timezone information -- since they are using Apache Arrow the functionality should be the same afaik:But instead of stripping it and making it Zulu time it just sets it back to whatever the
now()call would do which is what Datafusion currently is doing... We are just using Zulu instead of the local TZ.@alamb I think that we shouldn't modify the current casting since it appears to be functioning as expected per the arrow spec apache/arrow-rs#5827 (comment) (at least for this bug specifically) but we should change the way that
now()works to have it function as expected and falls in line with the postgres spec 🤔4 remaining items
devanbenz commentedon Sep 13, 2024
Taking a look at this it appears that casting to a timezone does work in datafusion but it doesn't function to how I would expect (or maybe it needs to be a different type of cast similar to clickhouse syntax?):
Clickhouse:
Datafusion:
Thats more so what I mean -- that when casting to a timestamp with a given timezone the output should abide by the timezone where it appears that
AT TIME ZONEcasts from atimestampto atimestamptz(in postgres terminology).Omega359 commentedon Oct 27, 2024
Unfortunately as it stands the
now()function nor any other UDF function has no access to the timezone of the server (though it could likely be retrieved) nor the default timezone set in ExecutionOptions (datafusion.execution.time_zone). I would like to fix that at some point but I am fairly certain it would mean a breaking change to UDF's as they are currently implemented.findepi commentedon Oct 28, 2024
@Omega359 good point
we should be able to provide a UDF with some form of environment state while maintaining backwards-compatibility.
alamb commentedon Oct 28, 2024
I think you could potentially get the server time from the
SimplifyInfopassed tohttps://docs.rs/datafusion/latest/datafusion/logical_expr/struct.ScalarUDF.html#method.simplify
(aka you could rewrite
now()into something likenow('Americas/New_York')🤔Omega359 commentedon Oct 28, 2024
I looked into that @alamb but I think what is required is access to ConfigOptions (so we can get tz from config, etc). I don't think
SimplifyInfoprovides that. I was toying with some approaches this weekend for this and didn't come up with anything simple. The most obvious option to me is to add anInvokeInfothat provides access toExecutionPropsandConfigOptionsto the arguments for invoke_batch, etc. That of course is a breaking change though. Beyond that I toyed with:Augmenting
SimplyInfoto haveConfigOptionscould work but it's a lot of effort to rewrite calls just to apply tz info (or anything else that a UDF might want from config) . Seems like applying a jackhammer to insert a screw.alamb commentedon Oct 29, 2024
Yeah I agree. There are other usecases for storing state / customized in UDFs (like pre-compiling regexps once per query rather than once per batch, for example). I keep trying to find some common API / pattern we can put in and follow
Related discussions
test: characterization suite for timestamps with time zone (apache#25175
test: characterization suite for timestamps with time zone (apache#25175
test: PostgreSQL differential coverage for timestamps with time zone (a…
test: characterize `AT TIME ZONE` on a timezone-aware timestamp
fix: `AT TIME ZONE` on a tz-aware timestamp returns a naive timestamp
test: characterization suite for timestamps with time zone (apache#25175
test: PostgreSQL differential coverage for timestamps with time zone (a…
fix: `AT TIME ZONE` on a timezone-aware timestamp returns a naive tim…