Trying to understand Trino and how it handles Time Zones - timezone

I'm observing strange behaviors when trying to convert timestamps between time zones in Trino. I believe it may be due to some conversions not behaving as expected. Perhaps someone can explain why CAST(current_timestamp as timestamp) observes the current time zone setting, but current_timestamp does not?
Example:
It is currently 4:08 PM Central Time (Chicago). When I run:
SET TIME ZONE 'America/Chicago';
SELECT
current_timezone() TZ
,current_timestamp TS
,CAST(current_timestamp as timestamp) CASTTS;
I get:
TZ: America/Chicago
TS: 2022-07-26 16:08:06
CASTTS: 2022-07-26 16:08:06
When I run:
SET TIME ZONE 'UTC';
SELECT
current_timezone() TZ
,current_timestamp TS
,CAST(current_timestamp as timestamp) CASTTS
I get:
TZ: UTC
TS: 2022-07-26 16:09:37
CASTTS: 2022-07-26 21:09:37
This is leading to additional issues when trying to convert a timestamp column from it's original timezone (UTC) to other timezones (Central/Eastern/etc)
Environment:
I'm in Central Time Zone.
Dbeaver 21.0.4
Trino 391 jdbc driver
Trino 380 (per SELECT VERSION())
Windows 10 Enterprise x64 21H2

Related

Storing and comparing UTC times

Say this is the current server time in UTC 2021-07-30 00:33:25 UTC, which in local time is just 2021-07-29 5:33 PM
25 hours from that time is 2021-07-31 01:33:25 UTC, which in local time is 2021-07-30 6:33 PM
The stored time being compared is saved as 2021-07-30T19:00:00+00:00, which in local time should be 2021-07-30 7:00 PM
The problem is that the stored time should be 2021-07-31T02:00:00+00:00 to equal 2021-07-30 7:00 PM,
but I'm not sure of the best approach to get that time.
Here's how I get the time being sent to my rails backend
import * as moment from 'moment-timezone';
...
const start_time = moment
.tz(
listingAvailability.listing_availability_ranges_attributes[0]
.start_time,
'YYYY-MM-DD hh:mm a'
)
.tz('America/Phoenix')
.toISOString(true)
I fixed it with
moment.utc(moment(listingAvailabilityTime).utc().local()).format();

Why does my TZ environment variable takes the wrong effect in puppeteer?

Through the env-object for the method launch, we can set a TZ environment variable to affect the timezone of the browser. This is confirmed by an issue, where one explicitly mentions that.
I'm taking a screenshot of this page which shows a Google calendar. Setting puppeteer's TZ to US/Eastern yields times two hours later. Setting TZ to Brazil/East yields the correct US/Eastern time.
What am I missing here?
The machine timezone is actually set to UTC:
$ date
Fri Nov 30 17:09:55 UTC 2018

Postgres at time zone '+07' syntax [duplicate]

I am running PostgreSQL 9.6.6 on x86_64-pc-linux-gnu and my time zone is set to 'UTC'.
Does anyone know why the results of the following SELECT statements are different?
A)
SELECT timezone('EST', '2017-12-21');
timezone
---------------------
2017-12-20 19:00:00
B)
SELECT timezone('-05', '2017-12-21');
timezone
---------------------
2017-12-21 05:00:00
According to the pg_timezone_names table -05 should have the same offset as EST... Any thoughts? Thanks.
https://www.postgresql.org/docs/current/static/view-pg-timezone-names.html
The view pg_timezone_names provides a list of time zone names that are
recognized by SET TIMEZONE
and further:
utc_offset interval Offset from UTC (positive means east of Greenwich)
when you set timezone to 'EST' - you declare that your client is in EST time zone, thus returned time will be adjusted for your tz:
t=# select '2017-12-21'::timestamptz;
timestamptz
------------------------
2017-12-21 00:00:00-05
(1 row)
the interval match utc_offset from pg_timezone_names and isequal -05, so it works as expected. (indeed in EST will be 5 hours less then UTC) same result if you set timezone to '-05'.
Both -05 and EST give same result for SET TIMEZONE as described in docs.
Now you answer reconciles with docs on using interval: https://www.postgresql.org/docs/current/static/functions-datetime.html#FUNCTIONS-DATETIME-ZONECONVERT
In these expressions, the desired time zone zone can be specified
either as a text string (e.g., 'PST') or as an interval (e.g.,
INTERVAL '-08:00').
following these rules it works as well:
t=# select '2017-12-21'::timestamptz at time zone 'EST';
timezone
---------------------
2017-12-20 19:00:00
(1 row)
t=# select '2017-12-21'::timestamptz at time zone interval '-05:00';
timezone
---------------------
2017-12-20 19:00:00
(1 row)
but further, docs say:
In the text case, a time zone name can be specified in any of the ways
described in Section 8.5.3.
which is https://www.postgresql.org/docs/current/static/datatype-datetime.html#DATATYPE-TIMEZONES
PostgreSQL allows you to specify time zones in three different forms:
recognized time zone names are listed in the pg_timezone_names
recognized abbreviations are listed in the pg_timezone_abbrevs
POSIX-style time zone specifications of the form STDoffset or STDoffsetDST
(formatting mine)
and lastly:
One should be wary that the POSIX-style time zone feature can lead to
silently accepting bogus input...Another issue to keep in mind is that
in POSIX time zone names, positive offsets are used for locations west
of Greenwich. Everywhere else, PostgreSQL follows the ISO-8601
convention that positive timezone offsets are east of Greenwich.
TL;DR
So in short - when you define '-05' as text (not interval) input for timezone() function or AT TIME ZONE directive (effectively same) Postgres thinks this is an attempt to use POSIX style time zone and thus inverts sign, thus you get "opposite" result...
a simple demonstration of this documented inversion:
t=# select '2017-12-21'::timestamptz at time zone '05';
timezone
---------------------
2017-12-20 19:00:00
(1 row)
Okay I think I found an answer to my own question:
According to the PostgreSQL docs, section 9.9.3 at the following link https://www.postgresql.org/docs/9.6/static/functions-datetime.html
In these expressions, the desired time zone zone can be specified either as a text string (e.g., 'PST') or as an interval (e.g., INTERVAL '-08:00'). In the text case, a time zone name can be specified in any of the ways described in Section 8.5.3.
So using the INTERVAL syntax, the following appears to work:
SELECT timezone(INTERVAL '-05:00', '2017-12-21');
timezone
---------------------
2017-12-20 19:00:00
I think it is still curious, what exactly SELECT timezone('-05', '2017-12-21'); means, as the following also provides the expected result (with the addition of a TZ offset):
SELECT timezone('-05', '2017-12-21'::timestamp);
timezone
------------------------
2017-12-20 19:00:00+00

Daylight Savings Time ignored using in_time_zone Rails 4

I'm having a frustrating issue that I can't seem to narrow down. I have searched many similar articles but they are not close enough to my issue to resolve. I am trying to pull a time from the database and display it in more than one time zone. My Rails app is using UTC as default. Here is what I'm doing:
On the create action I take the string of time which will be saved in the time column in my DB:
params[:schedule][:start] = "09:00"
Time.zone = "Central Time (US & Canada)"
#schedule.start = Time.zone.parse(params[:schedule][:start])
The above formats the time as it is supposed to:
2016-04-12 09:00:00 -0500
This is saved in the DB as:
2000-01-01 14:00:00
This has no time offset which is fine since I know it's in UTC. The problem happens when I go to display the time:
#schedule.start.in_time_zone("Central Time (US & Canada)")
This returns:
Sat, 01 Jan 2000 08:00:00 CST -06:00
Now, since this is a time column, I don't care about the date. I plan on formatting the value to only show the time. However, it is showing CST when it is currently CDT.
I can't figure out why this is happening. As I said I am not setting the Time Zone anywhere in my application.rb or anywhere else and I only set the Time zone on the create action which should be fine when moving to a new action.
Any help on clarifying this would be awesome!
This seems to be because when the time is stored it is stored with the date in the year 2000-01-01 which seems to be why it is using CST. How can I ignore the date when converting it to a particular timezone or will I need to change the column type to DateTime to get this to work properly?
It is showing CST simply because the time is read from the database including the stored date, i.e. it's read as 09:00 of Jan 1st 2000.
I guess you'd have to parse the time upon reading the attribute back. You can use a helper method in your model, for example:
# schedule model
def start_in_zone(zone)
self.start.strftime("%H:%M").in_time_zone(zone)
end
This will take only the hours and minutes part of the stored time and parse it in the given time zone with the date set to today. See this example:
"Sat, 01 Jan 2000 08:00:00".to_time.
strftime("%H:%M").
in_time_zone("Central Time (US & Canada)")
# => Tue, 12 Apr 2016 08:00:00 CDT -05:00
The fact that it matters whether it's CST or CDT means you do, on some level, care about the date. While I'm not familiar with the exact rules of Daylight Savings in that region, I do know that Jan 1 is the middle of winter and will definitely not be on Daylight Savings time.
Add the relevant date into your #schedule before putting it into a time zone, and it should fix the problem.

TIMESTAMP WITHOUT TIME ZONE, INTERVAL and DST extravaganza

I'm working on a Rails application which stores all dates to PostgreSQL as "TIMESTAMP WITHOUT TIME ZONE". (Rails handles the time zone on the application layer which for this application is "Europe/Berlin".) Unfortunately, Daylight Savings Time (DST) becomes an issue.
The simplified "projects" table has the following columns:
started_at TIMESTAMP WITHOUT TIME ZONE
duration INTEGER
Projects start at started_at and run for duration days.
Now, say there's only one project which starts on 2015-01-01 at 10:00. Since this is "Europe/Berlin" and it's January (no DST), the record looks like this on the database:
SET TimeZone = 'UTC';
SELECT started_at from projects;
# => 2015-01-01 09:00:00
It should end on 2015-06-30 at 10:00 (Europe/Berlin). But it's summer now, so DST applies and 10:00 in "Europe/Berlin" is now 08:00 in UTC.
Due to this, finding all projects for which the duration has elapsed by use of the following query does not work for projects which start/end across DST boundaries:
SELECT * FROM projects WHERE started_at + INTERVAL '1 day' * duration < NOW()
I guess it would be best if the above WHERE did the calculation in timezone "Europe/Berlin" rather than "UTC". I've tried a few things with ::TIMESTAMTZ and AT TIME ZONE none of which has worked.
As a side note: According to the PostgreSQL docs, + INTERVAL should deal with '1 day' intervals differently from '24 hours' intervals when it comes to DST. Adding days ignores DST, so 10:00 always stays 10:00. When adding hours on the other hand, 10:00 may become 09:00 or 11:00 if you cross the DST boundary one way or another.
Thanks a lot for any hints!
I think you've got two strategies for avoiding headache:
Let Rails handle everything to do with Timezones, so Postgres doesn't have to at all
or
Let Postgres handle everything to do with Timezones, so Rails doesn't have to at all
Mixing the two will always be a pain, and is basically what's causing your problems now. I'd go with strategy 1 (let Rails handle it). To do this, your Postgres database should store a start time, and a finish time, both in UTC. duration may be a thing in your user interface still, but if a user enters a start time and a duration, then you should calculate a finish time, and store that finish time in your database. The start time the users enters, and the finish time that you calculate in your app, with both be timezone-specific, and you just let Rails handle the conversion to UTC when it saves to the database.
Your query would then be simply:
SELECT * FROM projects WHERE finished_at < NOW()
(BTW, You could also store the duration in your database, but it's superfluous, since it can be calculated from the start time and finish time)
I've created a function which calculates ended_at by adding duration days to started_at honoring DST changes of a given time zone. Both started_at and ended_at, however, are in UTC and therefore play nice with Rails.
It turns started_at (timestamp without time zone, implicit UTC by Rails) to a timestamp with time zone UTC, then to the given time zone, adds the duration and returns the timestamp without time zone (implicit UTC).
# ended_at(started_at, duration, time_zone)
CREATE FUNCTION ended_at(timestamp, integer, text = 'Europe/Zurich') RETURNS timestamp AS $$
SELECT (($1::timestamp AT TIME ZONE 'UTC' AT TIME ZONE $3 + INTERVAL '1 day' * $2) AT TIME ZONE $3)::timestamp
$$ LANGUAGE SQL IMMUTABLE SET search_path = public, pg_temp;
With this function, I can omit having to add ended_at as an explicit column which would have to be kept in sync. And it's easy to use:
SELECT ended_at(started_at, duration) FROM projects

Resources