PostgreSQL grouping by day with timezone and DST - postgresql

I'm working on an existing database which date+time values are stored in BIGINT column (milliseconds since EPOCH). For each entry I can get the corresponding time zone from another table. But to make things easier to understand I will explain my problem by simulating the problem.
The timestamp 1609534800000 is equal to 2021-01-01 21:00:00 at GMT (00-00)
Now if I run the following queries (with DBeaver)
set time zone 'America/Asuncion'; -- (UTC-3)
select
to_timestamp(1609534800000 / 1000) as "1"
, to_timestamp(1609534800000 / 1000) at TIME zone 'America/New_York' as "2"
, date_trunc('day', (to_timestamp(1609534800000 / 1000) at TIME zone 'America/New_York')) as "3"
, to_timestamp(1609534800000 / 1000) at TIME zone 'Pacific/Wake' as "4"
, date_trunc('day', (to_timestamp(1609534800000 / 1000) at TIME zone 'Pacific/Wake')) as "5"
, date_part('epoch', (to_timestamp(1609534800000 / 1000) at TIME zone 'Pacific/Wake')) as "6"
, date_part('epoch', (date_trunc('day', (to_timestamp(1609534800000 / 1000) at TIME zone 'Pacific/Wake') ) ) ) as "7"
I'm getting
1 |2 |3 |4 |5 |6 |7 |
-------------------|-------------------|-------------------|-------------------|-------------------|----------|----------|
2021-01-01 16:00:00|2021-01-01 16:00:00|2021-01-01 00:00:00|2021-01-02 09:00:00|2021-01-02 00:00:00|1609578000|1609545600|
I don't understand the result at all. According to the documentation, the function to_timestamp is supposed to return a timestamp with time zone ? In this case the time zone applied should be the one in my session America/Asuncion (UTC-3). If at GMT the time is 2021-01-01 21:00:00, I should get 2021-01-01 18:00:00. Because (21:00 - 3h = 18:00). Why 16h ?
From my understanding this result is OK as 2021-01-01 21:00:00 at UTC -5h for timezone America/New_York is equal to 2021-01-01 16:00.
Here I'm asking the same thing as the #2 but I want to discard the time of the day. So 2021-01-01 16:00:00 is 2021-01-01 00:00:00. The result is OK.
This result is still OK as 2021-01-01 21:00:00 at UTC + 12h for timezone Pacific/Wake is equal to 2021-01-02 09:00:00.
5 I'm asking the same thing as the #4 but I want to discard the time of the day. So 2021-01-02 09:00:00 is 2021-01-02 00:00:00. The result is OK.
I want to extract the unix EPOCH time in seconds of this timestamp. If I well understand, the timestamp pass to the date_part function is now a timestamp without time zone. Now if I use an online converter to convert the resulting value 1609578000 to GMT time then I'm getting 2021-01-02 9:00:00. Which is OK for me.
This is the same operation as the #6 but I want the unix epoch from the beginning of the day of that local time. The resulting value 1609545600 correspond to the GMT time 2021-01-02 00:00:00. Which is NOT correct as I should get 2021-01-02 12:00:00 as 'Pacific/Wake' is 12h past GMT.
(UPDATED)
Also why Montreal locale time is not correct here ? I'm supposed to have 2021-01-01 00:00:00-05
select ((to_timestamp(1609477200000 /1000) at time zone 'America/Asuncion') at time zone 'America/Asuncion') as asuncion
, ((to_timestamp(1609477200000 /1000) at time zone 'America/Montreal') at time zone 'America/Montreal') as montreal
asuncion | montreal
------------------------+------------------------
2021-01-01 02:00:00-03 | 2021-01-01 02:00:00-03
How could I get
asuncion | montreal
------------------------+------------------------
2021-01-01 02:00:00-03 | 2021-01-01 00:00:00-05
Is there a way to see the time WITHOUT the configured session timeszone ?
PS : My Windows OS timezone is set at America/New_York and I'm using PostgreSQL 10.
Best regards,

I can't replicate 1). I get:
set time zone 'America/Asuncion';
select to_timestamp(1609534800000 / 1000);
to_timestamp
------------------------
2021-01-01 18:00:00-03
As to 6), you did not account for the SET timezone:
select date_part('epoch', date_trunc('day', to_timestamp(1609534800000 / 1000) at TIME zone 'Pacific/Wake') at TIME zone 'Pacific/Wake');
date_part
------------
1609502400
select to_timestamp(1609502400);
to_timestamp
------------------------
2021-01-01 09:00:00-03
--What happened
select to_timestamp(1609534800000 / 1000) at TIME zone 'Pacific/Wake';
timezone
---------------------
2021-01-02 09:00:00
(1 row)
--- Note you lopped off 9 hours and the returned timestamp has no time zone offset
--- so it is now local time 'America/Asuncion'
select date_trunc('day', to_timestamp(1609534800000 / 1000) at TIME zone 'Pacific/Wake');
date_trunc
---------------------
2021-01-02 00:00:00
--- This gives it back a timezone offset.
--- Running the date_part on this then gets you the proper value.
select date_trunc('day', to_timestamp(1609534800000 / 1000) at TIME zone 'Pacific/Wake') at TIME zone 'Pacific/Wake';
timezone
------------------------
2021-01-01 09:00:00-03

Related

Postgresql extracting 'epoch' from timestamp cuts off last date in date range

My table has the column event_ts with column type numeric.
Here is my query:
select
min(to_timestamp(event_ts)), max(to_timestamp(event_ts))
from
table1
where
event_ts >= extract('epoch' from '2021-07-01'::timestamp) and
event_ts <= extract('epoch' from '2021-07-31'::timestamp)
However, the results are
min: 2021-06-30 20:00:00.000 -0400
max: 2021-07-30 20:00:00.000 -0400
I would think the where clause would include data from 2021-07-01 to 2021-07-31.
There is data for July 31st, 2021.
Why does this query start at 2021-06-30 and end 2021-07-30?
show timezone;
TimeZone
------------
US/Pacific
select extract('epoch' from '2021-07-01'::timestamp);
extract
-------------------
1625097600.000000
select to_timestamp(1625097600);;
to_timestamp
-------------------------
06/30/2021 17:00:00 PDT
select extract('epoch' from '2021-07-01'::timestamptz);
extract
-------------------
1625122800.000000
(1 row)
test(5432)=# select to_timestamp(1625122800);
to_timestamp
-------------------------
07/01/2021 00:00:00 PDT
So by using timestamp you are creating a local time offset by the timezone offset. Using timestamptz will return a timestamp at 0:00:00.
This is because from here:
https://www.postgresql.org/docs/current/functions-datetime.html#FUNCTIONS-DATETIME-EXTRACT
epoch
For timestamp with time zone values, the number of seconds since 1970-01-01 00:00:00 UTC (negative for timestamps before that); for date and timestamp values, the nominal number of seconds since 1970-01-01 00:00:00, without regard to timezone or daylight-savings rules; for interval values, the total number of seconds in the interval
Epoch is based on UTC timezone.
Not sure why you are using epoch anyway?
Why not?:
...
where
event_ts between '2021-07-01'::timestamptz and '2021-07-31'::timestamptz

Select a date in my time zone when database is in UTC with PostgreSQL

I want to select a day in my timezone for example:
from the 2021-09-09 00:00 +02 to 2021-09-10 00:00 +02. But the following code:
set timezone TO 'Europe/Berlin';
SELECT TIMESTAMP::timestamptz,sensor_id,value
FROM my_table
WHERE sensor_id IN (1,2,3)
AND TIMESTAMP > '2021-09-09' AND TIMESTAMP < '2021-09-10'
ORDER BY TIMESTAMP
gives me the right values but with wrong timezone for example first rows are:
timestamp,sensor_id,value
2021-09-09 02:00 +02,1,21
2021-09-09 02:00 +02,2,34
2021-09-09 02:00 +02,3,54
but should be 2021-09-09 00:00 +02 or 2021-09-08 22:00 +00
The problem is bigger when the difference with utc changes between winter and summer
Anybony can help me?
PostgreSQL v.12
SELECT TIMESTAMP::timestamptz AT TIME ZONE 'CETDST',sensor_id,value
FROM my_table
WHERE sensor_id IN (1,2,3)
AND TIMESTAMP AT TIME ZONE 'CETDST' > '2021-09-09'
AND TIMESTAMP AT TIME ZONE 'CETDST' < '2021-09-10'
ORDER BY TIMESTAMP

FROM_TZ equivalent in PostgreSQL

How can I convert this to work in PostgreSQL?
TO_CHAR(CAST(FROM_TZ(CAST(columnname AS TIMESTAMP), 'GMT') AT TIME ZONE 'US/Eastern' AS DATE),'MM/DD/YY HH:MI AM') AS dt
testdb=# select TO_CHAR(CAST('2020-02-28T18:43' AS TIMESTAMP) AT TIME ZONE 'UTC' AT TIME ZONE 'US/Eastern','MM/DD/YY HH:MI AM') as dt;
dt
-------------------
02/28/20 01:43 PM
(1 row)
To make it clear what's going on, we'll start with the cast to TIMESTAMP, show that adding the first AT TIME ZONE makes it a tz-aware timestamp, and then how the 2nd does the timezone conversion.
testdb=# select CAST('2020-02-28T18:43' AS TIMESTAMP),
testdb-# CAST('2020-02-28T18:43' AS TIMESTAMP) AT TIME ZONE 'GMT',
testdb-# CAST('2020-02-28T18:43' AS TIMESTAMP) AT TIME ZONE 'GMT' AT TIME ZONE 'US/Eastern';
timestamp | timezone | timezone
---------------------+------------------------+---------------------
2020-02-28 18:43:00 | 2020-02-28 18:43:00+00 | 2020-02-28 13:43:00
(1 row)
See the timezone conversion docs for more details.

Group by Date and sum of total duration for that day

I am using workbench/j Postgres DB for my query which is as follows -
Input
ID |utc_tune_start_time |utc_tune_end_time
----------------------------------------------
A |04-03-2019 19:00:00 |04-03-2019 20:00:00
----------------------------------------------
A |04-03-2019 23:00:00 |05-03-2019 01:00:00
-----------------------------------------------
A |05-03-2019 10:00:00 |05-03-2019 10:30:00
-----------------------------------------------
Output
ID |Day |Duration in Minutes
----------------------------------------
A |04-03-2019 |120
-----------------------------------
A |05-03-2019 |90
-----------------------------------
I require the duration elapsed from the utc_tune_start_time till the end of the day and similarly, the time elapsed for utc_tune_end_time since the start of the day.
Thanks for your clarifications. This is possible with some case statements. Basically, if utc_tune_start_time and utc_tune_end_time are on the same day, just use the difference, otherwise calculate the difference from the end or start of the day.
WITH all_activity as (
select date_trunc('day', utc_tune_start_time) as day,
case when date_trunc('day', utc_tune_start_time) =
date_trunc('day', utc_tune_end_time)
then utc_tune_end_time - utc_tune_start_time
else date_trunc('day', utc_tune_start_time) +
interval '1 day' - utc_tune_start_time
end as time_spent
from test
UNION ALL
select date_trunc('day', utc_tune_end_time),
case when date_trunc('day', utc_tune_start_time) =
date_trunc('day', utc_tune_end_time)
then null -- we already calculated this earlier
else utc_tune_end_time - date_trunc('day', utc_tune_end_time)
end
FROM test
)
select day, sum(time_spent)
FROM all_activity
GROUP BY day;
day | sum
---------------------+----------
2019-03-04 00:00:00 | 02:00:00
2019-03-05 00:00:00 | 01:30:00
(2 rows)

How many seconds passed by grouped by hour between two dates

Let's suppose I have a start date 2016-06-19 09:30:00 and an end date 2016-06-19 10:20:00
I would like to get the time that elapsed every hour before starting the next hour or before getting to the final time in seconds grouped by hour and date, the result I'm trying to achieve (without having any success) would be something like this:
hour | date | time_elapsed_in_seconds
9 | 2016-06-19 | 1800 (there are 1800 seconds between 09:30:00 and 10:00:00)
10 | 2016-06-19 | 1200 (there are 1200 seconds between 10:00:00 and 10:20:00)
Try this :
with table1 as (
select '2016-06-19 09:30:00'::timestamp without time zone start_date,'2016-06-19 10:20:00'::timestamp without time zone end_date
)
select extract(hour from the_hour) "hour",the_hour::date "date",extract (epoch from (new_end-new_start)) "time_elapsed" from (
select the_hour,CASE WHEN date_trunc('hour',start_date)=the_hour then start_date else the_hour end new_start,
CASE WHEN date_trunc('hour',end_date)=the_hour then end_date else the_hour+'1 hour'::interval end new_end
from (
select generate_series(date_trunc('hour',start_date),end_date,'1 hour'::interval) the_hour,start_date,end_date from table1
) a
) b