How to convert unix timestamp to date with time zone? - sql

How can I convert for example timestamp:
1559347196759
to datetime with time zone 'GMT+2' using PostgreSQL?

I believe this does what you want:
select (timestamp 'epoch' + 1559347196759 * interval '1 millisecond') at time zone '+02:00'
The first expression converts the value to UTC. The second shifts the timezone.

Convert it to a timestamp with time zone (absolute time) and convert it to the appropriate time zone:
SELECT to_timestamp(1559347196759 / 1000.0) AT TIME ZONE '-02';
timezone
-------------------------
2019-06-01 01:59:56.759
(1 row)

Related

Combining date and UTC time fields in Postgres

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;

OracleSQL convert Date fields into another timezone

in my Oracle DB I have a DATE column where store date values.
All date values are in TimeZone Europe/Berlin. Now the application changes its TimeZone to UTC, this means I need to convert all existing Dates from Europe/Berlin into UTC.
Is there a way to do this natively in Oracle?
Use FROM_TZ( timestampvalue, timezone ) to convert a timestamp to a timestamp at a specific time zone and then you can use AT TIME ZONE 'UTC' to convert it to the UTC time zone and cast it back to a date:
SELECT CAST(
FROM_TZ(
CAST( your_column AS TIMESTAMP ),
'Europe/Berlin'
)
AT TIME ZONE 'UTC'
AS DATE
)
FROM your_table;

PostgreSQL: Adding an interval to a timestamp in a different time zone

What is the best way to add a specified interval to a timestamp with time zone, if I don't want to do the calculation in the time zone of the server. This is particularly important around daylight savings transitions.
e.g.
consider the evening that we "spring forward". (Here in Toronto, I think it was 2016-03-13 at 2am).
If I take a time stamp:
2016-03-13 00:00:00-05
and add '1 day' to it, in Canada/Eastern, I would expect to get 2016-03-14 00:00:00-04 -> 1 day later, but actually only 23 hours
But if I add 1 day to it in Saskatchewan (a place that doesn't use DST), I would want it to add 24 hours, so that I'd end up with
2016-03-13 01:00:00-04.
If I have columns / variables
t1 timestamp with time zone;
t2 timestamp with time zone;
step interval;
zoneid text; --represents the time zone
I essentially want to say
t2 = t1 + step; --in a time zone of my choosing
Postgres documentation seems to indicate that timestamp with time zone is internally stored in UTC time, which seems to indicate that a timestamptz column has no reckoning of a time zone in it.
The SQL standard indicates that
datetime + interval operation should maintain the time zone of the first operand.
t2 = (t1 AT TIME ZONE zoneid + step) AT TIME ZONE zoneid;
doesn't seem to work because the first cast turns t1 into a timezone-less timestamp and thus can't reckon DST transitions
t2 = t1 + step;
doesn't seem to work as it does the operation in the time zone of my SQL server
set the postgres time zone before the operation and change it back after?
A better illustration:
CREATE TABLE timestamps (t1 timestamp with time zone, timelocation text);
SET Timezone 'America/Toronto';
INSERT INTO timestamps(t1, timelocation) VALUES('2016-03-13 00:00:00 America/Toronto', 'America/Toronto');
INSERT INTO timestamps(t1, timelocation) VALUES('2016-03-13 00:00:00 America/Regina', 'America/Regina');
SELECT t1, timelocation FROM timestamps; -- shows times formatted in Toronto time. OK
"2016-03-13 00:00:00-05";"America/Toronto"
"2016-03-13 01:00:00-05";"America/Regina"
SELECT t1 + '1 day', timelocation FROM timestamps; -- Toronto timestamp has advanced by 23 hours. OK. Regina time stamp has also advanced by 23 hours. NOT OK.
"2016-03-14 00:00:00-04";"America/Toronto"
"2016-03-14 01:00:00-04";"America/Regina"
How to get around this?
a) Cast the timestamptz to a timestamp tz in the appropriate time zone?
SELECT t1 AT TIME ZONE timelocation + '1 day', timelocation FROM timestamps; --OK. Though my results are timestamps without time zone now.
"2016-03-14 00:00:00";"America/Toronto"
"2016-03-14 00:00:00";"America/Regina"
SELECT t1 AT TIME ZONE timelocation + '4 hours', timelocation FROM timestamps; -- NOT OK. I want the Toronto time to be 5am
"2016-03-13 04:00:00";"America/Toronto"
"2016-03-13 04:00:00";"America/Regina"
b) Change timezone of postgres and proceed.
SET TIMEZONE = 'America/Regina';
SELECT t1 + '1 day', timelocation FROM timestamps; -- Now the Regina time stamp is correct, but toronto time stamp is incorrect (should be 22:00-06)
"2016-03-13 23:00:00-06";"America/Toronto"
"2016-03-14 00:00:00-06";"America/Regina"
SET TIMEZONE = 'America/Toronto';
SELECT t1 + '1 day', timelocation FROM timestamps; -- toronto is correct, regina is not, as before
"2016-03-14 00:00:00-04";"America/Toronto"
"2016-03-14 01:00:00-04";"America/Regina"
This solution will only work if I continually switch the postgres timezone before every operation time interval operation.
It is a combination of two properties that causes your problem:
timestamp with time zone is stored in UTC and does not contain any time zone information. A better name for it would be “UTC timestamp”.
Addition of timestamp with time zone and interval is always performed in the current time zone, i.e. the one set with the configuration parameter TimeZone.
Since what you really need to store is a timestamp and the time zone in which it is valid, you should store a combination of timestamp without time zone and a text representing the time zone.
As you correctly noticed, you would have to switch the current time zone to perform interval addition over the daylight savings time shift correctly (otherwise PostgreSQL does not know how long 1 day is).
But you don't have to do that by hand, you can use a PL/pgSQL function to do it for you:
CREATE OR REPLACE FUNCTION add_in_timezone(
ts timestamp without time zone,
tz text,
delta interval
) RETURNS timestamp without time zone
LANGUAGE plpgsql IMMUTABLE AS
$$DECLARE
result timestamp without time zone;
oldtz text := current_setting('TimeZone');
BEGIN
PERFORM set_config('TimeZone', tz, true);
result := (ts AT TIME ZONE tz) + delta;
PERFORM set_config('TimeZone', oldtz, true);
RETURN result;
END;$$;
That would give you the following, where the result is to be understood in the same time zone as the argument:
test=> SELECT add_in_timezone('2016-03-13 00:00:00', 'America/Toronto', '1 day');
add_in_timezone
---------------------
2016-03-14 00:00:00
(1 row)
test=> SELECT add_in_timezone('2016-03-13 00:00:00', 'America/Regina', '1 day');
add_in_timezone
---------------------
2016-03-14 00:00:00
(1 row)
test=> SELECT add_in_timezone('2016-03-13 00:00:00', 'America/Toronto', '4 hours');
add_in_timezone
---------------------
2016-03-13 05:00:00
(1 row)
test=> SELECT add_in_timezone('2016-03-13 00:00:00', 'America/Regina', '4 hours');
add_in_timezone
---------------------
2016-03-13 04:00:00
(1 row)
You could consider creating a combined type
CREATE TYPE timestampattz AS (
ts timestamp without time zone,
zone text
);
and define operators and casts on it, but that's probably a major project that exceeds what you want for this.
There even is a PostgreSQL extension timestampandtz that does exactly that; maybe that's just what you need (I didn't look what the semantics for addition are).
Based on the informations given by #LaurenzAlbe, I used something different to handle adding an interval to a timestamptz and using DST. I had the problem this 2020-10-25 since in Belgium (timezone "Europe/Brussels" which is UTC+1) we observe DST and on 2020-10-25 at 03:00 we went backward for 1 hour ending at 02:00, i.e. we went from summer time UTC+2 to winter time UTC+1.
The code below which has to find the timestamptz at 16:00 on a given day failed that day and instead ended at 15:00 because we went backward 1h. The problematic line of code was:
select date_trunc('day', myfct.datetime) + interval 'PT16H'
For example, first query is ok, second is not.
select date_trunc('day', timestamptz '2020-10-27 17:00:00+01') + interval 'PT16H';
?column?
------------------------
2020-10-27 16:00:00+01
select date_trunc('day', timestamptz '2020-10-25 17:00:00+01') + interval 'PT16H';
?column?
------------------------
2020-10-25 15:00:00+01
The idea is to make the addition with a timestamp (without time zone) instead of a timestamptz and finally convert it to a timestamptz with the configured time zone of the database.
hydro_dev=> select date_trunc('day', timestamptz '2020-10-25 17:00:00+01');
date_trunc
------------------------
2020-10-25 00:00:00+02
(1 row)
hydro_dev=> select date_trunc('day', timestamptz '2020-10-25 17:00:00+01')::timestamp;
date_trunc
---------------------
2020-10-25 00:00:00
(1 row)
hydro_dev=> select date_trunc('day', timestamptz '2020-10-25 17:00:00+01')::timestamp + interval 'PT16H';
?column?
---------------------
2020-10-25 16:00:00
(1 row)
select (date_trunc('day', timestamptz '2020-10-25 17:00:00+01')::timestamp + interval 'PT16H')::timestamptz;
timestamptz
------------------------
2020-10-25 16:00:00+01

Converting NUMBER to DATE

I have a numeric field in my Oracle database that represents the date.
I'm not so familiar with Oracle commands.
I was wondering if anyone could provide some guide here.
Thanks.
example: 1435755908 = 7/1/2015 9:05
This is a Unix Timestamp, i.e. the seconds since January 1970, try this formula:
timestamp '1970-01-01 00:00:00' + 1435755908/86400
Since there seems to be a time zone difference:
select date '1970-01-01' + 1435755908/86400 as converted from dual;
CONVERTED
----------------------------------------
2015-07-01 13:05:08
you seem to need to do some time zone manipulation. Epoch times are UTC so you can use from_tz to declare that, and then at time zone to get the US/East Coast equivalent:
select from_tz(cast(date '1970-01-01' + 1435755908/86400 as timestamp), 'UTC')
at time zone 'America/New_York' as converted from dual;
CONVERTED
----------------------------------------
2015-07-01 09:05:08.000 AMERICA/NEW_YORK
Which is a time stamp with time zone. If you want it as a plain date then cast it:
select cast(from_tz(cast(date '1970-01-01' + 1435755908/86400 as timestamp), 'UTC')
at time zone 'America/New_York' as date) as converted from dual;
CONVERTED
----------------------------------------
2015-07-01 09:05:08

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