Combining date and UTC time fields in Postgres - sql

I have 2 separate fields for date and time. The time field is stored in UTC time. How can I combine the 2 into a datetime field into local time
Example:
date: 2021-03-08
time in UTC: 23:00
time zone: GMT+8
I would like to get 2021-03-08 07:00 in local time
or even 2021-03-07 23:00 in UTC
Note: Combining the fields is not an option unfortunately.

Need to know your time zone to convert. To convert utc to america/los_angeles time zone:
select '2021-03-08 23:00'::timestamp at time zone 'UTC' at time zone 'america/los_angeles'
you can check out below codes:
If you have a timestamp without time zone column and you're storing timestamps as UTC, you need to tell PostgreSQL that, and then tell it to convert it to your local time zone.
select created_at at time zone 'utc' at time zone 'america/los_angeles'
from users;
To be more concise, you can also use the abbreviation for the time zone:
select created_at at time zone 'utc' at time zone 'pst'
from users;
To see the list of time zones PostgreSQL supports:
select * from pg_timezone_names;

SInce the time difference is 8 hours, try
SELECT '2021-03-08'::date + ('23:00'::time + '8 hours'::interval);
If you want this to work with arbitrary time zones, the query becomes more complicated:
SELECT '2021-03-08'::date
+ ((current_date + '23:00'::time)
AT TIME ZONE 'UTC'
AT TIME ZONE 'Asia/Ulaanbaatar'
)::time;

Related

How to show real time difference between timestamps in two different zones with SQL?

This query returns 0
SELECT (CURRENT_TIMESTAMP AT TIME ZONE 'PST'
- CURRENT_TIMESTAMP AT TIME ZONE 'UTC') AS td
FROM dual
How can I make it to actually show the real difference in time? For example, in this case I want to see a difference of -8 hours.
In this example I used CURRENT_TIMESTAMP, but I have a real use case where I have timestamps in two different time zones. And I want the real time difference between those two.
Cast the values to a TIMESTAMP without a time zone:
SELECT CAST(CURRENT_TIMESTAMP AT TIME ZONE 'PST' AS TIMESTAMP)
- CAST(CURRENT_TIMESTAMP AT TIME ZONE 'UTC' AS TIMESTAMP)
as td
FROM DUAL;
Which outputs:
TD
-000000000 08:00:00.000000
Or, considering times around the DST boundary:
-- Times around the DST boundary.
WITH times (t) AS (
SELECT TIMESTAMP '2021-03-14 09:30:00 UTC' FROM DUAL UNION ALL
SELECT TIMESTAMP '2021-03-14 10:30:00 UTC' FROM DUAL
)
SELECT t As t_utc,
t AT TIME ZONE 'PST8PDT' AS t_pstpdt,
(CAST(t AT TIME ZONE 'PST8PDT' AS TIMESTAMP)
- CAST(t AT TIME ZONE 'UTC' AS TIMESTAMP)) AS td
FROM times
Outputs:
T_UTC
T_PSTPDT
TD
2021-03-14 09:30:00.000000000 UTC
2021-03-14 01:30:00.000000000 PST8PDT
-000000000 08:00:00.000000
2021-03-14 10:30:00.000000000 UTC
2021-03-14 03:30:00.000000000 PST8PDT
-000000000 07:00:00.000000
db<>fiddle here
So, you want to find the time difference between the time zones. (This is not what the title says; the title is misleading.)
If so, then you don't need to reference current_timestamp, or anything of the kind.
Since you are comparing PST to UTC, this is the same as finding the UTC offset of PST. This makes the problem even easier. (In the general case, you can find the offset of both time zones and subtract; in your example, the offset of UTC to itself is zero, obviously).
select to_char(to_timestamp_tz('PST', 'tzr'), 'tzh:tzm') as pst_offset
from dual;
PST_OFFSET
----------
-08:00

Is there an SQL function to convert the time zone to local time (LDT)?

I am quite new to SQL, and trying to pull a data table from the database (flight) using the following command:
select
flight.FLT_NBR,
flight.LEG_NBR,
flight.LEG_TAIL_NBR,
flight.LEG_IATA_ORIG_CD as FLT_SCHD_ORIG_ARPT_CD,
flight.LEG_IATA_DEST_CD as FLT_SCHD_DEST_ARPT_CD,
flight.SCHD_ARR_TMSTP as Scheduled_Arrival,
flight.ACTL_ARR_TMSTP AS Actual_Arrival,
flight.SCHD_DPRT_TMSTP as Scheduled_Departure,
flight.ACTL_DPRT_TMSTP AS Actual_Departure,
from home/tulips/FT_FLIGHT_LEG flight
Now the problem is there are multiple country origin and destination with different times. How do I incorporate same time zone for all the countries? I tried using the command as time zone 'UTC' such as below but it didn't work... May be I am adding it in a wrong place?
select
flight.FLT_NBR,
flight.LEG_NBR,
flight.LEG_TAIL_NBR,
flight.LEG_IATA_ORIG_CD as FLT_SCHD_ORIG_ARPT_CD,
flight.LEG_IATA_DEST_CD as FLT_SCHD_DEST_ARPT_CD,
flight.SCHD_ARR_TMSTP as Scheduled_Arrival as time zone 'UTC',
flight.ACTL_ARR_TMSTP AS Actual_Arrival as time zone 'UTC',
flight.SCHD_DPRT_TMSTP as Scheduled_Departure as time zone 'UTC',
flight.ACTL_DPRT_TMSTP AS Actual_Departure as time zone 'UTC',
from home/tulips/FT_FLIGHT_LEG flight
Please help me a way to have one time zone for all the Scheduled_Arrival,Actual_Arrival,Scheduled_Departure and Actual_Departure
The expression you want is at time zone, not as time zone.
In order to use it, you need to know what time zone the original datetime value represents. For example, I have a SQL server in Sydney Australia, so getdate() will return my local date and time. However, to convert it to UTC I must first inform SQL of the fact that the value starts off in AUS Eastern Standard Time, and then ask it to convert it to UTC, by chaining at time zone expressions together. Like this:
select getdate() at time zone 'AUS Eastern Standard Time' at time zone 'UTC'
If you don't know the time zone of the original datetime value, there is no way for SQL to know how to change it to a different time zone's value.

Group by time with timezone conversion in Postgresql

I am working with time data that is currently stores in UTC but I want it to be in PST, which is 8 hours behind. I have a pretty lengthy and involved query, but the only thing I am interested in is the time right now so I have included those parts. I want to convert the times to PST and then group by the date for the last week of data. The query has the following structure:
select
date_trunc('day', time1) AT TIME ZONE 'US/Pacific'
...
where
time1 AT TIME ZONE 'US/Pacific' > now() AT TIME ZONE current_setting('TimeZone') - INTERVAL '168 HOURS'
...
group by date_trunc('day', time1)
This results in the following time groupings. From my understanding, it groups from the 0:00 UTC, which is 16:00 in PST. However, I want the groupby to start at 0:00 PST. How do I do this? Right now, the counts in each group are misleading for each day because they go from 4 pm to 4 pm instead of 12 am to 12 am. For example, Sundays have uncharacteristically high counts because Sunday includes part of Monday's data in the groupby. I would appreciate any input to fix this issue. Thank you.
The answer depends on whether it is a timestamp with time zone or one without:
If it's a timestamp with time zone, you can convert to PST with select time1 AT TIME ZONE 'US/Pacific' and get the date with select date_trunc('day', time1 AT TIME ZONE 'US/Pacific')
If it's a timestamp without time zone stored in UTC that you want to convert, you first have to tell PostgreSQL to interpret it as UTC, then convert it, like so: select (time1 AT TIME ZONE 'Z') AT TIME ZONE 'US/Pacific' and of course you can get the date with select date_trunc('day', (time1 AT TIME ZONE 'Z') AT TIME ZONE 'US/Pacific')
In either case you have to convert time zones before truncating to the day level or you may end up with inaccurate results.

Rails/Postgres query all records created after 5 PM in taking into account daylight savings switch?

I am looking to query for all records on a table with a created_at time of after 5 PM, EST WITH taking into account daylight savings.
My DB is Postgres and all timestamps are stored in UTC as a normal Rails app.
So, say I have four records
ID, created_at, time in EST (-5 offset from UTC),
1, "2017-01-01 22:46:21.829333", 5:46 PM
2, "2017-01-01 21:23:27.259393", 4:23 PM
-------- DST SWITCH -----
ID, created_at, time in EDT (-4 offset from UTC),
3, "2017-03-20 21:52:46.135713", 5:52 PM
4, "2017-06-21 20:08:53.034377", 4:08 PM
My query should return records 1 and 3, but should ignore 2 and 4.
I have tried
SELECT "id",
"created_at"
FROM "orders"
WHERE (created_at::TIME WITHOUT TIME ZONE AT TIME ZONE 'utc' AT TIME ZONE 'US/Eastern' > ("created_at"::DATE + TIME '21:00')::TIME AT TIME ZONE 'utc' AT TIME ZONE 'US/Eastern')
ORDER BY "orders"."created_at" ASC
But this query will return record 1,2,3 when it should not return 2.
Sorry about my previous wrong answer. I don't do much with USA time.
all timestamps are stored in UTC
So ("created_at"::DATE + TIME '21:00')::TIME AT TIME ZONE 'utc' AT TIME ZONE 'US/Eastern' actually says it is utc time 21:00, which does not necessarily guarantee it is 5 PM in EST because the timezone offset changes from 5 to 4. So the compare condition is not correct.
You need to get the local time to make the comparison, rather than the utc time.
Try following code:
SELECT
"id",
"created_at"
FROM "orders"
WHERE (created_at AT TIME ZONE 'utc' AT TIME ZONE 'US/Eastern') :: TIME > TIME '5:00 PM'

I want to adjust a date for summer/winter time and time zone when insterting it into a table

I have a date and time variable in TABLE_A that is in GMT. I want to insert this date and time into TABLE_B, but I want the insterted value to be adjusted for time zone and summer/winter time.
That is:
INSERT into TABLE_A (ADJUSTED_DATE_AND_TIME)
SELECT GMT_DATE_AND_TIME [Perform proper adjustments here..?]
FROM TABLE_A
Can I do this? In that case, how do I write ?
Thank.
I think you can simply convert the GMT/UTC time. However, you have to take the full region name of your time zone.
SELECT TIMESTAMP '2014-06-10 12:00:00 +00:00' AT TIME ZONE 'Europe/Zurich' AS summer FROM dual;
SUMMER
---------------------------------------
10.06.2014 14:00:00.000000000 +02:00
SELECT TIMESTAMP '2014-12-10 12:00:00 +00:00' AT TIME ZONE 'Europe/Zurich' AS winter FROM dual;
WINTER
---------------------------------------
10.12.2014 13:00:00.000000000 +01:00
Since your source value is data type DATE you have to do following steps.
Cast DATE to TIMESTAMP
Set Time zone of the value using FROM_TZ
Convert the value to new time zone using AT TIME ZONE '...'
Cast the value to DATE
Written in a single statement it is
select
CAST(FROM_TZ(CAST(sy_sttime AS TIMESTAMP), 'UTC') AT TIME ZONE 'Europe/Zurich' AS DATE)
from sy_request
or a bit less clear
select
CAST((CAST(sy_sttime AS TIMESTAMP) AT TIME ZONE 'UTC') AT TIME ZONE 'Europe/Zurich' AS DATE)
from sy_request