How can change/convert timezone when I query in PostgreSQL - postgresql

I need a query to convert timezone in PostgreSQL
I have already tried this query
Select users.last_update_time + interval '7 hours'
And it plus for my result 7 hours.
But I have a problem that if I choose results from 31/01/2022 to 06/02/2022. It will include the result on 07/02/2022. Example before I plus 7 hours, it's on around 17:00 06/02/2022 to 00:00 07/02/2022.
The correct result has to be after +7 hours. So it means that result after plus 7 hours from 17:00 06/02/2022 to 00:00 07/02/2022 shouldn't be in there.

Then you should include the time zone in the query like this:
SELECT ... FROM ...
WHERE tscol BETWEEN '2022-01-31 00:00:00 America/Vancouver'
AND '2022-02-07 00:00:00 America/Vancouver';
If you don't want to hard code the time zone, the best thing is to set the timezone database parameter to the correct time zone in your database session. Then you can use a simple type cast, which will respect the setting:
SELECT ... FROM ...
WHERE tscol BETWEEN CAST ('2022-01-31 00:00:00' AS timestamp with time zone)
AND CAST ('2022-02-07 00:00:00' AS timestamp with time zone);

Related

How can postgres change from unix epoch time with miliseconds to timestamptz?

I am using the below postgres sql to convert from unix epoch time to timestamp.
select to_timestamp(1608816600) at time zone 'UTC';
It works when the epoch time does not contain milliseconds but when it contains milliseconds, it does not work. The below query:
select to_timestamp(1611150788148) at time zone 'UTC';
returns:
However it should be Wed Jan 20 2021 13:53:08 with some milliseconds. Source: https://currentmillis.com/
As documented in the manual to_timestamp() expects a "seconds since 1970-01-01 00:00:00+00" - but it allows fractional values.
If you want to specify milliseconds you need to divide your value by 1000
select to_timestamp(1611150788148::double precision/1000) at time zone 'UTC';
select to_char(to_timestamp(1611150788148/1000.0) at time zone 'UTC', 'yyyy-mm-dd hh24:mi:ss.ff3');
to_char
-----------------------
2021-01-20 13:53:08.148

Postgres 9.6, timezones: why are those two queries giving me different times

Ok so I've always been fuzzy with the timezones. I'm getting better, but not there yet. Can you people enlighten me?
Context: I receive a UTC time from the frontend. For example, to denote the date 1 September 2019, I'll get 2019-08-31 22:00:00Z (we're in Brussels DST, hence the 2 hours difference).
From that date, I need to generate a series of 6 months before that. So that's March April May June July August.
I managed to find a solution, somewhat out of luck to be honest. I'm still not sure I understand the details of what happens below:
database=> show timezone;
TimeZone
----------
UTC
(1 row)
database=> select generate_series(
('2019-08-31 22:00:00Z'::timestamp at time zone 'Europe/Brussels')::date - '6 month'::interval,
('2019-08-31 22:00:00Z'::timestamp at time zone 'Europe/Brussels')::date - '1 month'::interval,
'1 month'::interval
);
generate_series
---------------------
2019-02-28 00:00:00
2019-03-28 00:00:00
2019-04-28 00:00:00
2019-05-28 00:00:00
2019-06-28 00:00:00
2019-07-28 00:00:00
(6 rows)
database=> select generate_series(
('2019-08-31 22:00:00Z' at time zone 'Europe/Brussels')::date - '6 month'::interval,
('2019-08-31 22:00:00Z' at time zone 'Europe/Brussels')::date - '1 month'::interval,
'1 month'::interval
);
generate_series
---------------------
2019-03-01 00:00:00
2019-04-01 00:00:00
2019-05-01 00:00:00
2019-06-01 00:00:00
2019-07-01 00:00:00
2019-08-01 00:00:00
Why do I get wrong results if I use ::timestamp?
There are actually two different &ldauo;operators” named AT TIME ZONE, one converting timestamp with time zone to timestamp without time zone and one the other way around.
If the first argument is a timestamp with time zone, the value is converted to a timestamp without time zone that shows what a wall clock in that time zone would.
If the first argument is a timestamp without time zone, it is interpreted in the session time zone (given by the value of the timezone parameter) and converted to an absolute time stamp.
Now timestamp with time zone is the preferred type of the date/time type category, so the string literal in your second query is interpreted as a timestamp with time zone in accordance with the documentation. In the first query it is a timestamp without time zone. Since there are different operators involved, it is not surprising that the results are different.
In your first query, the wall clock time 22:00 is interpreted as if the clock hung in Brussels, so it is actually 20:00 UTC. The first argument of generate_series, from which counting starts, is then Feb 28 2019, 20:00 UTC (before casting to date).

How to get the EPOCH of a concatenated timestamp in PostgreSQL

In PostgreSQL, how do I get the epoch (UTC time) of a table date field whose time zone is assumed to be GMT and whose hours, min, seconds value is assumed to be '00:00:00'?
e.g. mytable.DT_GMT has values like '2018-10-16'.
Here's what I've tried so far. Instead of DT_GMT, I've just used '2018-10-16'. I'll replace it with the table field once I have some thing that works.
postgres=> select extract(epoch from TIMESTAMP WITH TIME ZONE concat('2018-10-16',' 00:00:00.00-00') );
ERROR: syntax error at or near "concat"
LINE 1: ...elect extract(epoch from TIMESTAMP WITH TIME ZONE concat('20...
Trying something related:
postgres=> SELECT TIMESTAMP ('2018-10-16' || ' 00:00:00.00') AT TIME ZONE 'GMT';
ERROR: syntax error at or near "'2018-10-16'"
LINE 1: SELECT TIMESTAMP ('2018-10-16' || ' 20:38:40') AT TIME ZONE ...
Note that the hours...sec is necessary because without it those values are not what's required.
postgres=> SELECT TIMESTAMP '2018-10-16' AT TIME ZONE 'GMT';
timezone
------------------------
2018-10-15 17:00:00-07
(1 row)
Instead of a string concatenation, you add them as date and time with time zone.
test=> select extract(epoch from date '2018-10-16' + time with time zone '00:00:00.00-00');
date_part
------------
1539648000
(1 row)
If you're only adding '00:00:00' to make the time zones work, you don't need it with a date.
test=> select date_part('epoch', date '2018-10-16');
date_part
------------
1539648000
$ date --date '#1539648000' --utc
Tue Oct 16 00:00:00 UTC 2018
This is a good reason to store dates and times as their proper type, especially in Postgres which cares a lot about types, rather than as text.

Column of type "timestamp(6) with timezone" and "current time" difference in minutes

I have Oracle 12c DB table and one of it's column utc_timestamp is of type
UTC_TIMESTAMP TIMESTAMP(6) WITH TIME ZONE
It stores timestamp in UTC while current_timestamp and systimestamp both gives timestamp in different timezones.
How can I get time difference in MAX(utc_timestamp) and current_timestamp in minutes ignoring time difference due to different time zones.
For example:
select current_timestamp from dual;
Gives=> 23-AUG-17 04.43.16.253931000 PM AMERICA/CHICAGO
select systimestamp from dual;
Gives=> 23-AUG-17 05.43.16.253925000 PM -04:00
select max(UTC_TIMESTAMP) from table_name;
Gives=> 23-AUG-17 09.40.02.000000000 PM +00:00
For above condition when I run SQL to check time difference between in MAX(utc_timestamp) and current_timestamp I should get number 3.
I think I need something like:
select (extract(minute from current_timestamp) - extract(minute from max(UTC_TIMESTAMP)) * 1440) AS minutesBetween from table_name;
But different timezones are messing it up and I get negative number like -4317. This might be correct as current_timestamp will be higher than max(utc_timestamp) being in CST. So I tried:
select (extract(minute from CAST(current_timestamp as TIMESTAMP(6) WITH TIME ZONE)) - extract(minute from max(UTC_TIMESTAMP)) * 1440) AS minutesBetween from table_name;
This SQL runs without error but producing a big negative number like -83461. Please help me find what am I doing wrong.
You really have two problems here.
One is to convert CURRENT_TIMESTAMP to UTC. That is trivial:
select CURRENT_TIMESTAMP AT TIME ZONE 'UTC' from dual [.....]
(use the AT TIME ZONE clause https://docs.oracle.com/cd/B19306_01/server.102/b14225/ch4datetime.htm#i1007699)
The other is that the difference between two timestamps is an interval, not a number.
select current_timestamp at time zone 'UTC'
- to_timestamp_tz('24-AUG-17 04.00.00.000 AM UTC', 'dd-MON-yy hh.mi.ss.ff AM TZR')
from dual;
produces something like
+00 00:02:39.366000
which means + (positive difference) 00 days, 00 hours, 02 minutes, 39.366 seconds.
If you just want the minutes (always rounded down), you may wrap this whole expression within extract( minute from < ...... > ). Be aware though that the answer will still be 2 (minutes) even if the difference is five hours and two minutes. It is probably best to leave the result in interval data type, unless you are 100% sure (or more) that the result is always less than 1 hour.

Postgres: get local timestamp with time zone of latest midnight

I'm in the Time Zone Europe/Berlin (+02), Postgresql is running at UTC (+00).
I need to get the timestamp with time zone at the latest local midnight date (The start of day date of the current day in my time zone).
So my end result would be something like this if we have 2013-03-03 14:00:00+02
2013-03-03 22:00:00+00
2013-03-04 00:00:00+02 // the same
I tried to get this date with
SELECT TIMESTAMP 'today' AT TIME ZONE 'Europe/Berlin'
Unfortunately this yields the wrong date (the previous day midnight) during 00:00 and 02:00 as the UTC time is stil at the previous day and today seems to use utc to calculate the rest.
If we have 2013-03-03 00:05 at Europe/Berlin this will return
2013-05-01 22:00:00+00
If I want to have the correct date I need to use
SELECT date_trunc('day', now() AT TIME ZONE 'Europe/Berlin') AT TIME ZONE 'Europe/Berlin';
2013-05-02 22:00:00+00
which is correct, but quite ugly.
Is there a cleaner variant of this command?
Use timestamptz. The tz at the end meaning with time zone:
SELECT TIMESTAMPTZ 'today' AT TIME ZONE 'Europe/Berlin'
Or if you like it more explicit:
SELECT TIMESTAMP with time zone 'today' AT TIME ZONE 'Europe/Berlin'
Wrap it in a function:
create function midnight() returns timestamptz as $$
select date_trunc('day', now() AT TIME ZONE 'Europe/Berlin') AT TIME ZONE 'Europe/Berlin';
$$ language sql;
Based on Erwin's answer to a related question, this was the simplest and fastest way I figured out how to do it:
SELECT timezone('Europe/Berlin', now()::date::timestamp) AS local_midnight_in_utc;
The key is the cast to a timestamp, which removes the time zone from the date.
You can test your sample time of '2013-03-03 00:05' with this:
SELECT timezone('Europe/Berlin', '2013-03-03 00:05'::date::timestamp) AS midnight;
and it returns
2013-03-02 23:00:00+00
According to explain analyze, this is about 3x as fast as the datetrunc version. A runtime of .017ms vs 0.006ms based on a best of 5 runs.