In PostgreSQL, can we directly compare two timestamp with different time zone? - sql

SELECT * FROM table_a
WHERE time_1 >= to_timestamp('11/01/2014 10:00 PDT', 'MM/DD/YYYY HH24:MI TZ')
time_1 is in UTC time zone and it is a timestamp with timezone. So, will this give me what I want or I need to do exact UTC time in the function to_timestamp()?

Postgresql has two different timestamp data types and it is confusing which one should be used when. The two types are:
timestamp (also known as timestamp without time zone) It is most likely that this is the type in table_a
timestamp with time zone This is the data type returned by to_timestamp()
You must be sure that you are comparing apples with apples or pairs with pairs and not mix them or you may get undesirable results.
If your table_a.time_1 is a timestamp with time zone then the code you give in your question will work fine.
If your table_a.time_1 is a timestamp then you will need to change your code:
SELECT *
FROM table_a
WHERE time_1 >= to_timestamp('11/01/2014 10:00 PDT', 'MM/DD/YYYY HH24:MI TZ') at time zone 'utc';
The last part of this (at time zone 'utc') will strip the timezone (PDT) off the specified timestamp and translate the timestamp to UTC.
Edit: to help with your comments in this answer...
In order to understand how to translate time zones you need to understand the difference between the two forms of time stamp. It will become clear why you need to understand this below. As I indicate above the difference between the two forms of time stamp is confusing. There is a good manual page but for now just read on.
The main thing to understand is that neither version actually stores a time zone (despite the name). The naming would make much more sense if you added an extra word "translation". Think "timestamp without time zone translation" and "timestamp with time zone translation".
A timestamp with time zone translation doesn't store a time zone at all. It is designed to store time stamps which could come from anywhere in the world and not loose track of their meaning. So when entering one you must provide the time zone it came from or postgresql will assume it came from the time zone of your current session. Postgresql automatically translates it out of the given time zone into an internal time zone for the server. You don't need to know what time zone that is because postgresql will always translate it back from this internal time zone before giving you the value. When you retrieve the value (eg: SELECT my_time FROM foo) postgresql translates the time stamp to the time zone of your current session. Alternatively you can specify the time zone to translate into (eg: SELECT my_time AT TIME ZONE 'PDT' FROM foo).
With that in mind it's easier to understand that a timestamp without time zone translation will never be changed from the time you specify. Postgresql will regard 11:00:00 as happening before 12:00:00 even if you meant 11 in America and 12 in England. It's easy to see why that may not be what you want.
A very common programming error is to think that a timestamp with time zone is at a particular time zone. It isn't. It is at whatever time zone you ask for it to be. And if you don't specify what time zone you want it at then postgresql will assume you want it at your current session time zone.
You've stated that your field is a timestamp with time zone which are all at UTC. This isn't technically correct. Most likely your session time zone is UTC and postgresql is giving you everything in UTC as a result.
So you have a timestamp with time zone and you want to know what these times are in PDT? Easy: SELECT my_time AT TIME ZONE 'PDT' FROM foo.
It's important to understand that the AT TIME ZONE '...' syntax toggles between timestamp and timestamp with time zone.
timestamp AT TIME ZONE 'PDT' converts into a timestamp with time zone and tells postgresql to convert to the PDT time zone.
timestamp with time zone AT TIME ZONE 'PDT' converts into a timestamp telling postgresql to interpret it as coming from 'PDT'.
This symetry means that to reverse AT TIME ZONE 'foo' you just use AT TIME ZONE 'foo'. Put another way SELECT anything AT TIME ZONE 'PDT' AT TIME ZONE 'PDT' will always leave anything unchanged.

Related

Is there a "local timezone" suffix?

Is there any convention to add in a local timezone (whatever the user has set it as) in a database?
For example, like I can do the following for UTC time:
2014-01-01 01:02:03Z
Is there something like the following to mean local time?
2014-01-01 01:02:03L
Or some other suffix where it can either pick up the user's system time or take it from a variable that can be set? For example, something like (for Postgres):
ALTER DATABASE postgres SET timezone TO 'Europe/Berlin';
This has nothing to do with Postgres.
The format you're asking about is ISO 8601. Specifically in that format, the absence of a Z or an offset such as -07:00 or +05:30 is defined as "local time".
So what you are looking for is a string without an offset, such as 2014-01-01T01:02:03.
postgres would use the system timezone, or what you called local, if no timezone is specified. see manual
If no time zone is stated in the input string, then it is assumed to be in the time zone indicated by the system's TimeZone parameter, and is converted to UTC using the offset for the timezone zone.
...
Conversions between timestamp without time zone and timestamp with time zone normally assume that the timestamp without time zone value should be taken or given as timezone local time.
Your concept is flawed for a couple of reasons:
A user in Germany connects to a Web server in England that connects to a database server is America. What constitutes local?
More to the point Postgres does not store the time zone in with time zone fields. So you will not recover the entered time zone on data retrieval.
If you are dealing with multiple time zones then the field you need to use is timestamp with time zone. This will rotate entered timestamp values to UTC for storage. You now have a fixed point in time that you can rotate to whatever 'local' time you want on retrieval.
Just treat the undecorated time as a timestamp with time zone:
richard=> SET timezone='Europe/London';
SET
richard=> SELECT '2022-08-27 21:42:22.25891'::timestamptz;
timestamptz
------------------------------
2022-08-27 21:42:22.25891+01
(1 row)
richard=> SET timezone='Europe/Paris';
SET
richard=> SELECT '2022-08-27 21:42:22.25891'::timestamptz;
timestamptz
------------------------------
2022-08-27 21:42:22.25891+02
(1 row)

How to update a date with a time zone in postgresql?

I want to update a date with a timezone (+2 hours) but it ends up as UTC (0 hours)
Date type is 'timestamp-with-timezone'
Query...
update table set date = '2022-05-25 13:28+02:00'
will end up as this in the database.
2022-05-25 11:28:00+00
What's wrong here?
tl;dr
Nothing wrong. Postgres stores values of TIMESTAMP WITH TIME ZONE in UTC, always an offset from UTC of zero. Any submitted offset or zone is used to adjust to UTC.
Details
Date type is 'timestamp-with-timezone'
No such type in standard SQL, nor in Postgres.
I’ll assume you meant TIMESTAMP WITH TIME ZONE.
it ends up as UTC (0 hours)
Read the fine manual. You are seeing documented behavior.
Postgres always stores values in a column of type TIMESTAMP WITH TIME ZONE in UTC, that is, with an offset of zero hours-minutes-seconds.
Any time zone or offset provided with an input is used to adjust into UTC. That provided zone or offset is then discarded.
So the name of the type TIMESTAMP WITH TIME ZONE is a misnomer. First, the authors of the SQL were thinking in terms of offset, not real time zones. Second, any submitted time zone is not stored. A submitted zone is used to adjust and then discarded.
If you need to track the original offset or zone, add an extra column. You’ll have to add code to store the offset amount or the time zone name.
update table set date = '2022-05-25 13:28+02:00' will end up as this in the database. 2022-05-25 11:28:00+00 What's wrong here?
Nothing is wrong. That is a feature, not a bug. Both of those strings represent the very same simultaneous moment.
FYI, database engines vary widely in their behavior handling date-time types and behaviors.
Some do as Postgres does regarding TIMESTAMP WITH TIME ZONE, adjusting to UTC and then discarding any provided time zone or offset. Some others may not.
The SQL standard barely touches on the topic of date-time handling. It declares a few types, and does that poorly with incomplete coverage of all cases. And the standard neglects to define behavior.
So, be very careful when it comes to date-time handling in your database work. Read very carefully the documentation for your particular database engine. Do not make assumptions. Run experiments to validate your understanding. And know that writing portable SQL code for date-time may not be feasible.

How to get current TIMESTAMP in UTC from BigQuery?

I want to add to my query TIMESTAMP column in UTC.
I know BigQuery has CURRENT_TIMESTAMP() function but it doesn't say how to convert it to UTC.
In MYSQL I would have do:
SELECT UTC_TIMESTAMP()
What is the equivalent in BigQuery (Standard SQL only) ?
A TIMESTAMP does not store or use a time zone, as described in the documentation. When parsing it it may have a time zone which is converted from when saving.
A timestamp represents an absolute point in time, independent of any time zone or convention such as Daylight Savings Time.
And more specifically
A timestamp is simply an offset from 1970-01-01 00:00:00 UTC, assuming there are exactly 60 seconds per minute. Leap seconds are not represented as part of a stored timestamp.
When querying for CURRENT_TIMESTAMP() it also shows explicitly it’s in UTC by having zero time zone offset. So it is a timestamp in UTC based on this.
There may be a problem using it since it does not store anything in UTC and it does not care about leap seconds. The specification says this:
Leap seconds are only observable through functions that measure real-world time. In these functions, it is possible for a timestamp second to be skipped or repeated when there is a leap second.
So if these leap seconds are needed it may be required to use another data type in specific time zone to handle leap seconds.
The conversion is also explained:
If your input contains values that use ":60" in the seconds field to represent a leap second, that leap second is not preserved when converting to a timestamp value. Instead that value is interpreted as a timestamp with ":00" in the seconds field of the following minute.

Get the time the users in their timezone submitted

Our application uses CURRENT_TIMESTAMP to store an event when a user submitted the data. This is stored in a TIMESTAMP WITH LOCAL TIME ZONE type column. Our servers are in PST timezone, but I want to see what time one of our users in Australia submitted the data in Australian time? I am confused how to do this properly? When I query the database right now this column seems to show the date the row was submitted but in PST time for every row.
Something like this:
SELECT
datetime_submitted -- I want this to display the time this value was created in Australian time not PST
FROM my_table
WHERE user = 'AUSTRALIAN';
Use the AT TIME ZONE clause:
select systimestamp as server_timestamp,
systimestamp at time zone 'Australia/Sydney' as australia_timestamp
from dual;
SERVER_TIMESTAMP AUSTRALIA_TIMESTAMP
-------------------------------------- --------------------------------------
12-AUG-16 04.12.23.789000000 PM -05:00 13-AUG-16 07.12.23.789000000 AM AUSTRALIA/SYDNEY
Right now it is 4:12:23 PM on 12 August 2016 at my location (Central time, US); it is already tomorrow in Australia, as you can see from the example. (Reminds me the joke - don't worry about the end of the world coming today, it's already tomorrow in Australia!)
If you want to just look at the time in the user's time zone you can set the session to whatever time zone the user is located in then when you select the times it should give it back to you in that time zone as if you were that user.
% setenv ORA_SDTZ '+09:30'
or
ALTER SESSION SET TIME_ZONE='+9:30';
Reference for more details: https://docs.oracle.com/cd/E11882_01/server.112/e10729/ch4datetime.htm#NLSPG263
Updated
If you only want one column displayed in a different time zone then you can convert it.
To convert the timezone just in the query you can just use CAST(date_field AS TIMESTAMP) AT TIME ZONE 'US/Eastern' AS time_name on the column you want to change.
Reference for more details: https://docs.oracle.com/cd/B19306_01/server.102/b14225/ch4datetime.htm#i1007699
First, your application need to receive exact date time with user local timezone. For example, if you have typical web application with frontend, backend and database, then your frontend need to send exact date time with local user timezone (for example "2016-08-08 03:00:00CST") to your backend. Backend need to save this in a table as-is keeping date, time, timezone information received from frontend. Oracle needs to store date, time and timezone into database as-is so datetime_submitted column should have information about time zone. Suitable data type for it will be TIMESTAMP WITH TIME ZONE, as described here: https://docs.oracle.com/cd/B28359_01/server.111/b28318/datatype.htm#CDEGDFFE
Maybe you did not fully understand data type TIMESTAMP WITH LOCAL TIME ZONE. Data type TIMESTAMP WITH LOCAL TIME ZONE shows time values always and only in current user session time zone.
When you work with time zones then often you get an advise like: "Store all times in UTC time and convert displayed value at application layer." That is exactly how TIMESTAMP WITH LOCAL TIME ZONE works. The mayor difference is, times are not stored at UTC but at DBTIMEZONE (unless you set DBTIMEZONE = UTC, of course). Hence you cannot modify DBTIMEZONE setting once you have any data in a TIMESTAMP WITH LOCAL TIME ZONE column.
You get an error if you try for example TO_CHAR(datetime_submitted, 'hh24:mi:ss TZR'), because time zone region is by definition always your current user session time zone.
The database does not know the time zone of your users in Australia, because all stored times where converted to PST time zone (well, most likely to -08:00 but skip the details here).
If you like to see the values in a different time zone then you have to change your current user session time zone. Typically you do this by ALTER SESSION SET TIME_ZONE=... or by environment variable, resp. Registry setting ORA_SDTZ.
Another solution is to cast to TIMESTAMP WITH TIME ZONE or TIMESTAMP data type.

Is there a simple and clean way to convert `DATE` to the `TIMESTAMP AT TIME ZONE` of that date at midnight UTC?

This should be really simple, but I'm having an embarrassing amount of trouble with it.
In PostgreSQL 9.1, I need to intepret a field stored as a DATE in the DB as if it were a TIMESTAMPTZ representing midnight UTC on that date. I'd like to do it in a clean and readable way that someone can come along, look at, and understand what's happening.
The only ways I've found to do it so far are both very ugly. One converts it to a TIMESTAMP WITHOUT TIME ZONE then creates a TIMESTAMP WITH TIME ZONE from it by interpreting it as if it were UTC:
SELECT CAST(DATE '2012-01-01' AS TIMESTAMP WITHOUT TIME ZONE) AT TIME ZONE 'utc'
The other way is worse:
('2012-01-01'::date)::timestamptz - (current_timestamp AT TIME ZONE 'UTC' - current_timestamp)
in that it converts the date to a timestamp for midnight local time, then subtracts the time zone offset. I couldn't find any way to get that offset as an interval natively (which seems crazy) so I landed up getting it by comparing current_timestamp in local time with current_timestamp in UTC.
The only other way I could work out used extract to get the date parts and assembled a new timestamptz from them. I won't even show that one, it's too ugly.
Both approaches feel all kinds of weird and wrong. Is there any sane way - standard or no - to convert in a readable and easily understood way from a DATE to a timestamptz of midnight UTC on that date?
I'm looking for something like (imaginary, won't work)
'2012-01-01'::date AS TIMESTAMPTZ IN TIME ZONE '00:00';
or
to_timestamp('2012-01-01'::date, '00:00'::time, 'UTC');
Please point out the stupidly obvious thing I'm missing.
Note that I'm testing to make sure the date is truly right internally, not just at display, with extract(epoch from $1) where $1 is the converted date.
Your first approach is correct. And it's not that ugly, is it? In simplified Postgres syntax:
SELECT '2012-1-1'::date::timestamp AT TIME ZONE 'UTC';
Applied to a variable or column it looks even more elegant:
SELECT mydate::timestamp AT TIME ZONE 'UTC';
If you are going to enter the date manually, you can shortcut to:
SELECT '2012-1-1 0:0'::timestamp AT TIME ZONE 'UTC'
The result will always be displayed according to the local timezone of the client (i.e. with the according offset), but that has no influence on the value.
Note: I don't know much about PSQL, but I've some experience with date/time matters.
Your first way feels right to me. You're effectively going from "local date" to "local date/time" to "date/time in a particular time zone". Those are all reasonable steps, and ones I'd expect to see in normal date/time APIs.
This approach never introduces the system default time zone as far as I can tell, which is a thoroughly good thing. It performs one logical step at a time, assuming that the cast the sensible thing.
You don't need to worry about the local date/time being either ambiguous or missing in the target time zone, as UTC doesn't have any DST transitions.
Basically, it looks fine. If it works and performs as well as you need it to, I'd stick with it.