postgresql: timezone sensitive query on a timestamp without timezone - postgresql

I have a table with a timestamp without time zone column (data entered is assumed to be in Australia/Sydney time zone).
Query on data for a time range (ie 8am-4pm) in America/New_York time zone.
Is there an easy way to achieve this?
thanks, p.

Figured it out.
You need to first convert the time to it's with time zone version ie my_ts at time zone 'Australia/Sydney' and then convert that to it's NY counterpart via at time zone 'America/New_York'
select
my_ts as "Default(syd)",
my_ts at time zone 'Australia/Sydney' as "SYD",
my_ts at time zone 'Australia/Sydney' at time zone 'America/New_York' as "NY",
date_part('hour', my_ts at time zone 'Australia/Sydney' at time zone 'America/New_York') as "NY-hr"
from my_table
where date_part('hour', my_ts at time zone 'Australia/Sydney' at time zone 'America/New_York')>=8
and date_part('hour', my_ts at time zone 'Australia/Sydney' at time zone 'America/New_York')<16

You can convert everything to the same time zone so you can compare them with (if the timezone was set):
select current_time, current_time at time zone 'gmt';
timetz | timezone
-------------------+-------------------
20:50:51.07742-07 | 03:50:51.07742+00
If the time zone is not set and you need to correct it some local time:
select now()::time, now()::time + '+8:00'::interval;
now | ?column?
-----------------+-----------------
20:57:49.420742 | 04:57:49.420742
Once you get the time the way you want, just the extract the hour and you can use a simple condition to select the proper times.
select *
from
(select extract(hour from now()::time + '+8:00'::interval) as hour) as t
where hour between 8 and 16;

Related

Convert timestamp to interval in postgresql

I am trying to convert a timestamp to interval & expecting an out of hh:mm only.
My code is like below
SELECT to_timestamp('2020-01-10 08:00', 'YYYY-MM-DD hh24:mi')::timestamp without time zone
at time zone 'Asia/Calcutta'
at time zone 'Etc/UTC'
Actual purpose of the code is change the time zone into utc for a given date time value.
It seems you are trying to get the timedifference (interval) between two timezones at a specific time.
This can be done like this for example:
SELECT to_timestamp('2020-01-10 08:00', 'YYYY-MM-DD hh24:mi')::timestamp without time zone
at time zone 'Asia/Calcutta'
-
to_timestamp('2020-01-10 08:00', 'YYYY-MM-DD hh24:mi')::timestamp without time zone
at time zone 'Etc/UTC'
This returns an interval = -05:30:00
You can of course convert it to hours and minutes with the to_char function, but that returns string, not interval.
Best regards,
Bjarni

Why datetime without time zone foormatted as it have timezone?

I am experimenting with time zones.
My postgres table created as:
Table "public.xx"
Column | Type | Collation | Nullable | Default
--------+-----------------------------+-----------+----------+---------
dtz | timestamp with time zone | | |
dt | timestamp without time zone | | |
The server timezone is 'UTC'. I know this from show timezone;
Then I insert data:
insert into xx values( TIMESTAMP WITH TIME ZONE '2018-08-01 13:00:00+3', TIMESTAMP WITH TIME ZONE '2018-08-01 13:00:00+3' );
INSERT 0 1
tucha=> select * from xx;
dtz | dt
------------------------+---------------------
2018-08-01 10:00:00+00 | 2018-08-01 10:00:00
(1 row)
Results are easy to understand: the dates are stored in UTC thus 3 is subtracted.
Also notice that dtz have +00
Now when I use at time zone
tucha=> select dtz at time zone 'UTC', dt at time zone 'UTC' from xx;
timezone | timezone
---------------------+------------------------
2018-08-01 10:00:00 | 2018-08-01 10:00:00+00
(1 row)
The +00 is added for field which has no timezone and viceversa: no +00 for dtz field.
Why this happened?
a short answer is - because it is desined this way:
https://www.postgresql.org/docs/current/static/functions-datetime.html#FUNCTIONS-DATETIME-ZONECONVERT
timestamp without time zone AT TIME ZONE zone returns timestamp
with time zone
timestamp with time zone AT TIME ZONE zone returns
timestamp without time zone
Longer:
as you said your client works in UTC timezone and server always stores timestamptz in UTC, thus select times match (you use UTC everywhere).
first select timestamptz is shown with +00 because this is timezone aware field - and it shows you the time zone. dt knows nothing of time zones, thus shows none...
Now when you use AT TIME ZONE you ask to show timestamps at SOME SPECIFIC time zone, thus time zone aware data type shows you the time for specific zone (adding hours and so), but after "moving" time it can't be reused as time zone aware (because it's not server UTC time anymore), thus it hides TZ. Opposite the time that was not aware of time zone, when displayed at some specific time zone, gains that "awareness" and deducting hours it shows +00 so you would know you operated on TZ not aware time stamp. I think that is the logic here.
I have found good article how to work with timezones (RU):
So – timestamp at time zone gives timestamptz which represents the moment in time when local time at given zone was as given.
and timestamptz at time zone gives timestamp, which shows what the time was in given timezone at given point in time.

What is difference between 4 ways convert timezone in postgresql

I don't know What is difference between 4 ways convert timezone in postgresql:
SELECT (timestamp '2018-01-20 00:00:00' at time zone 'Asia/Saigon') at time zone 'UTC';
SELECT CAST('2018-01-20 00:00:00' as timestamp without time zone) at time zone 'Asia/Saigon' at time zone 'UTC'
SELECT (TO_TIMESTAMP('2018-01-20 00:00:00', 'YYYY-MM-DD HH24:MI:SS') at time zone 'Asia/Saigon') at time zone 'UTC'
SELECT ('2018-01-20 00:00:00' at time zone 'Asia/Saigon') at time zone 'UTC';
The results are different. Why?
The first two statements do the same thing.
The difference is the way in which a constant of type timestamp without time zone is created, but the result is the same in both cases.
The third statement creates a timestamp with time zone using to_timestamp, where the string is interpreted in your session time zone. This is then converted to a timestamp without time zone as the wall clock in Saigon would show, and then converted to a timestamp with time zone imagining the wall clock were teleported to UTC.
The fourth statement does the same as the third, because the string is implicitly cast to timestamp with time zone. There is an ambiguity here because AT TIME ZONE can also be applied to timestamp without time zone, but in case of doubt the preferred type of its category is used, which is timestamp with time zone.
The SQL standard differentiates timestamp without time zone and timestamp with time zone literals by the presence of a "+" or "-" symbol and time zone offset after the time. Hence, according to the standard
Also you can see below articles:
Section 8.5.1.3. Time Stamps
Time zone

How do I specify “the start of today” in a specific time zone?

I have a table with a “timestamp with time zone” column. I would like to find all of the rows whose timestamp is earlier than today, where “today” is determined in a specific time zone.
I know how to use at time zone to interpret a literal timestamp as being in some particular time zone, and I know how to use date_trunc to get the beginning of this day. But I’m not sure how to combine them to get what I need. I tried
select date_trunc('day', current_date at time zone 'cst');
which gave me “2015-03-16 00:00:00”, but it’s unclear to me what time zone is used for this result (or whether it has one at all). How can I select the beginning of the current day according to a specific time zone?
It helped me to reframe the question as follows: what are the current date and time in the Central time zone? Then, what I want is the midnight at the beginning of that day [in the Central time zone]. I found that I could write this as
current_date::timestamp AT TIME ZONE 'cst'
This example may be useful to understand PostgreSQL timezone functionality:
SELECT now() as utc_now, -- 2021-08-29 11:38:08.552247 +00:00
pg_typeof(now()) as type_of_utc_now, -- timestamp with time zone
tehran_local_now, -- 2021-08-29 16:08:08.552247
pg_typeof(tehran_local_now), -- timestamp without time zone
utc_calculated_from_tehran_local_now, -- 2021-08-29 11:38:08.552247 +00:00
pg_typeof(utc_calculated_from_tehran_local_now), -- timestamp with time zone
tehran_local_start_of_today, -- 2021-08-29 00:00:00.000000
pg_typeof(tehran_local_start_of_today), -- timestamp without time zone
utc_start_of_today_at_tehran_timezone, -- 2021-08-28 19:30:00.000000 +00:00
pg_typeof(utc_start_of_today_at_tehran_timezone) -- timestamp with time zone
FROM (SELECT now() at time zone 'Asia/Tehran' as tehran_local_now) as tln
CROSS JOIN
(SELECT (now() at time zone 'Asia/Tehran') at time zone 'Asia/Tehran' as utc_calculated_from_tehran_local_now) as ucftln
CROSS JOIN
(SELECT date_trunc('day', now() at time zone 'Asia/Tehran') as tehran_local_start_of_today) as tlsot
CROSS JOIN
(SELECT date_trunc('day', now() at time zone 'Asia/Tehran') at time zone
'Asia/Tehran' as utc_start_of_today_at_tehran_timezone) as usotatt;
The PostgreSQL function now() (column utc_now) returns the current timestamp in UTC without any timezone. Its type is timestamp with time zone. Its Java equivalent is Instant.now().
When you use now() at time zone 'Asia/Tehran' (column tehran_local_now) it indicates the current date-time in Tehran local. Its type is timestamp without time zone. Its Java equivalent is:
Instant.now().atZone(ZoneId.of("Asia/Tehran")).toLocalDateTime()
When you use date_trunc('day', now() at time zone 'Asia/Tehran') (column tehran_local_start_of_today) it indicates the start of today in Tehran local. Its type is timestamp without time zone. Its Java equivalent is:
Instant.now().atZone(ZoneId.of("Asia/Tehran")).toLocalDate().atStartOfDay()
When you use date_trunc('day', now() at time zone 'Asia/Tehran') at time zone 'Asia/Tehran' (column utc_start_of_today_at_tehran_timezone) it indicates the start of today in Tehran timezone (not local). Its type is timestamp with time zone. You may need this one. Its Java equivalent is:
Instant.now().atZone(ZoneId.of("Asia/Tehran")).truncatedTo(DAYS).toInstant()

How to convert local timestamp (of a given time zone) to UTC?

I am working in a code where multiple time zones will be available. I need to create a function which takes the selected local timestamp as well as the local time zone. The return value of the function should the UTC time of the local time (local time for the given local time zone).
I am working on PostgreSQL 8.3
SELECT Now() AT TIME ZONE 'UTC';
This will return timestamp without time zone at specified time zone (UTC in my example) for given timestamp with time zone.
From PostgreSQL docs:
SELECT TIMESTAMP '2001-02-16 20:38:40' AT TIME ZONE 'MST';
Result: 2001-02-16 19:38:40-08
SELECT TIMESTAMP WITH TIME ZONE '2001-02-16 20:38:40-05' AT TIME ZONE 'MST';
Result: 2001-02-16 18:38:40
You can get list of time zones from pg_timezone_names:
select * from pg_timezone_names limit 5
name;abbrev;utc_offset;is_dst
----------------------
PRC;CST;08:00:00;f
Asia/Brunei;BNT;08:00:00;f
Asia/Ujung_Pandang;CIT;08:00:00;f
Asia/Ust-Nera;VLAT;11:00:00;f
Asia/Phnom_Penh;ICT;07:00:00;f