new_time returning time in 12hrs format - sql

In my function I want to convert time from GMT to BST.
My query is like below
SELECT TO_CHAR(NEW_TIME ((TRUNC(SYSDATE) + 20/24 + 21/1440),
'GMT',
'BST'),
'HH24:MI'
)
FROM DUAL;
Its returning me '9:21' instead of '21:21'.
Please help! Thank you!

From the documentation, BST refers to Bering Standard Time, and not British Summer Time as you are expecting.
Bering Standard Time is at UTC-11, so the result that you get is as expected.
NEW_TIME function can accept only limited timezones specified in the above documentation.If you want to convert GMT to British Summer Time,
you should probably use 'Europe/London' timezone.
select (cast(your_date as timestamp) at time zone 'GMT') at time zone 'Europe/London'
from dual;
So your query will be,
SQL> select
(cast((TRUNC(SYSDATE) + 20/24 + 21/1440) as timestamp) at time zone 'GMT') at time zone 'Europe/London',
to_char((cast((TRUNC(SYSDATE) + 20/24 + 21/1440) as timestamp) at time zone 'GMT') at time zone 'Europe/London','hh24:mi')
from dual;
(CAST((TRUNC(SYSDATE)+20/24+21/1440)ASTIMESTAMP)ATTIMEZONE'GMT')ATTIMEZONE' TO_CH
--------------------------------------------------------------------------- -----
14-MAY-14 09.21.00.000000 PM EUROPE/LONDON 21:21

Related

How to convert unix timestamp to date with time zone?

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)

ORA-01857: not a valid time zone

I am trying to convert from UTC time zone to GMT time zone.
I ran this below query and getting ORA error.
select NEW_TIME(SYSDATE, 'UTC', 'GMT') from dual;
And error is
Error starting at line : 1 in command -
select NEW_TIME(SYSDATE, 'UTC', 'GMT') from dual
Error report -
ORA-01857: not a valid time zone
I googled and find that NEW_TIME function is not accepting UTC time zone.
So, Can you please suggest me alternate solution/any way to convert from UTC to GMT?
UTC is also known as GMT, the latter which NEW_TIME already accepts. So, what you are trying to is equivalent to:
SELECT NEW_TIME(SYSDATE, 'GMT', 'GMT')
FROM dual;
The call to NEW_TIME doesn't make any sense of course. Check here for a list of accepted timezone codes.
Use FROM_TZ from convert a timestamp without a time zone to a timestamp with time zone (i.e. UTC) and then use AT TIME ZONE 'GMT' to convert it from the first time zone to the GMT time zone. You'll need to use CAST in various places as FROM_TZ expects a TIMESTAMP rather than a DATE and then you need to cast back to a DATE at the end (assuming you don't want a TIMESTAMP value):
SELECT CAST(
FROM_TZ(
CAST( SYSDATE AS TIMESTAMP ),
'UTC'
)
AT TIME ZONE 'GMT'
AS DATE
) As gmt_time
FROM DUAL
Output:
| GMT_TIME |
| :------------------ |
| 2019-04-10T14:05:37 |
db<>fiddle here

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