ORACLE SQL: add time to a timestamp with timezone - sql

I need to add seconds and substract variables with type TIMESTAMP WITH TIMEZONE, however, to my understanding, adding numbers to such data causes the information about the timezone to be lost, perhaps because it's converted to a DATE type
That is:
SELECT FROM_TZ(
TO_TIMESTAMP(
TO_DATE('03/09/2012 2:30:30','DD/MM/YYYY HH:MI:SS')
)
, 'America/Chicago')
FROM DUAL;
Gives:
03/09/2012 00:00:00, -05:00
Then
SELECT FROM_TZ(
TO_TIMESTAMP(
TO_DATE('03/09/2012 2:30:30','DD/MM/YYYY HH:MI:SS')
)
, 'America/Chicago') + 1/24 -- add 1 hour
FROM DUAL;
Gives
03/09/2012 01:00:00
and loses the timezone information.
But
SELECT FROM_TZ(
TO_TIMESTAMP(
TO_DATE('03/09/2012 2:30:30','DD/MM/YYYY HH:MI:SS'))
, 'America/Chicago') + INTERVAL '1' hour
FROM DUAL;
Correctly gives
03/09/2012 01:00:00,000000000 -05:00
However the INTERVAL.. syntax expect a char constant, so I can't use that with variables.
How can I perform that kind of arithmetic with TIMESTAMP WITH TIME ZONE datatype while retaining timezone information?
TIA

You can use the NUMTODSINTERVAL function to convert a number to an interval.
SELECT FROM_TZ( TIMESTAMP '2012-10-08 00:00:00','-5:00')
+ NUMTODSINTERVAL(1,'HOUR')
FROM dual;

Related

Casting Local Time to UTC Formating Incorrect

HIRE_DATE is in a 'DATE' column. The timestamp is local (Los Angeles); I would like to convert it to UTC.
I can't for the life of me fathom why the UTC output is mangled (Last 2 digits of YY is the DD; and vice-versa) -- and the time does not convert to UTC.
HIRE_DATE: 30/04/2019 12:00:00 AM
select from_tz(to_timestamp(HIRE_DATE,'DD-MM-YY HH24:MI:SS'), 'America/Los_Angeles') at time zone 'UTC' from TABLE
OUTPUT: 19/04/2030 12:00:00 AM
If HIRE_DATE is a DATE data type then you don't need TO_TIMESTAMP.
TO_TIMESTAMP is used to convert a string (i.e. VARCHAR2) into a TIMESTAMP value but you have a DATE value.
Just do
select from_tz(CAST(HIRE_DATE AS TIMESTAMP), 'America/Los_Angeles') at time zone 'UTC'
from TABLE
Actually I don't understand why FROM_TZ does not accept DATE values whereas almost any other date/timestamp related function accept either DATE or TIMESTAMP value as input.
Note, the default output display format of this query is defined by current user session NLS_TIMESTAMP_TZ_FORMAT setting. If you are not satisfied with the output format, either change NLS_TIMESTAMP_TZ_FORMAT setting by executing ALTER SESSION SET NLS_TIMESTAMP_TZ_FORMAT = '...' or use TO_CHAR function to set output format explicitly.
Instead of
... AT TIME ZONE 'UTC'
you can also use
SYS_EXTRACT_UTC(...)
The upper returns a TIMESTAMP WITH TIME ZONE value, the second one returns a TIMESTAMP value.
Would this do any good?
SQL> select from_tz(cast (sysdate as timestamp), 'UTC') result from dual;
RESULT
---------------------------------------------------------------------------
27.09.20 10:59:28,000000 UTC
Or, in your case
select from_tz(cast (hire_date as timestamp), 'UTC' from dual
No need to apply any format mask to hire_date as it is a DATE datatype (at least, that's what you said).
You use the word "convert" which can mean one of two things:
change the data type, which is what FROM_TZ does
change the value from one time zone to another, which FROM_TZ does not do.
You didn't give your expected output, so we may misunderstand.
To change the data type:
with data(dte) as (
select date '2019-04-30' + interval '12' hour from dual
)
select from_tz(cast(dte as timestamp), 'America/Los_Angeles') from data
FROM_TZ(CAST(DTEASTIMESTAMP),'AMERICA/LOS_ANGELES')
30-APR-19 12.00.00.000000 PM AMERICA/LOS_ANGELES
To get the simultaneous datetime value in UTC:
with data(dte) as (
select date '2019-04-30' + interval '12' hour from dual
)
select cast(sys_extract_utc(from_tz(cast(dte as timestamp), 'America/Los_Angeles')) as date) from data
CAST(SYS_EXTRACT_UTC(FROM_TZ(CAST(DTEASTIMESTAMP),'AMERICA/LOS_ANGELES'))ASDATE)
2019-04-30 19:00:00

Subtracting hours from Unix Timestamp

The following query selects a unix timestamp. It should be 1pm but says 5pm because of UTC. It needs to be Eastern time 1pm, so I need to subtract 4 hours from it. What's the best way to go about this?
SELECT CAST(to_date('1970-01-01', 'YYYY-MM-DD') + substr(STARTTIME,0,10)/60/60/24 as timestamp)
You shouldn't caluclate manually. You should add or substract the timezone.
Oracle how to convert time in UTC to the local time (offset information is missing)
Here an example-query:
SELECT TO_CHAR (
FROM_TZ (CAST (SYSDATE AS TIMESTAMP), 'UTC')
AT TIME ZONE 'EUROPE/BERLIN',
'YYYY-MM-DD HH24:MI:SS TZH:TZM TZR')
AS BERLIN_Time_complete,
TO_CHAR (
FROM_TZ (CAST (SYSDATE AS TIMESTAMP), 'UTC')
AT TIME ZONE 'UTC',
'YYYY-MM-DD HH24:MI:SS TZH:TZM TZR')
AS UTC_Complete,
TO_CHAR (
FROM_TZ (CAST (SYSDATE AS TIMESTAMP), 'UTC')
AT TIME ZONE 'EUROPE/BERLIN',
'YYYY-MM-DD HH24:MI:SS TZH:TZM')
AS BERLIN_Time_complete,
TO_CHAR (
FROM_TZ (CAST (SYSDATE AS TIMESTAMP), 'UTC')
AT TIME ZONE 'EUROPE/BERLIN',
'TZH:TZM')
AS BERLIN_Timezone,
TO_CHAR (
FROM_TZ (CAST (SYSDATE AS TIMESTAMP), 'UTC')
AT TIME ZONE 'EUROPE/BERLIN',
'TZR')
AS Timezone_Name
FROM DUAL;
The key is for Format:
YYYY: Year with four digits (0000-9999 ex: 2018)
MM: Month with two digits (01-12)
DD: Day with two digits (01-31)
HH24: Hour 00-23
MI: Minutes 00-59
SS: Seconds 00-59
TZH: Timezone-Hours
TZM: Timezone-Minutes (There are timezones with 30mins offset)
TZR: Name of the timezone
You should play around with those, to understand the to_date()/to_char(). You'll need it.
If you realy want to add hours. Here an example:
select sysdate + INTERVAL '2' HOUR from dual;
You can try this :
SELECT TO_CHAR(sysdate, 'DD-MON-YYYY HH24:MI:SS') AS myDate
FROM dual;
For more information read this document.

Extract time from a column

I've a column like last_located_time which contain values like
2017-05-13T17:33:36.000+0000.
I have tried to remove only time, but no luck.
SELECT USERNAME, TO_DATE(SUBSTR(LAST_LOCATED_TIME,11,17),'HH:MI:SS') "lAST TIME"
FROM Tb_089
How should I extract only time value from the columns for all users?
Thanks in advance!
If you want the time component in the UTC time zone (so all times are being displayed in a common time zone) then:
(assuming you have a TIMESTAMP WITH TIME ZONE data type)
SELECT TO_CHAR(
last_located_time AT TIME ZONE 'UTC',
'HH24:MI:SS'
)
FROM Tb_089;
If you, instead, the column is an ISO 8601 formatted string:
SELECT TO_CHAR(
TO_TIMESTAMP_TZ(
last_located_time,
'YYYY-MM-DD"T"HH24:MI:SS.FFTZHTZM'
) AT TIME ZONE 'UTC',
'HH24:MI:SS'
)
FROM Tb_089;
If you want the time component as an interval:
SELECT CAST( utc_last_located_time AS TIMESTAMP ) - TRUNC( utc_last_located_time )
AS time_interval
FROM (
SELECT TO_TIMESTAMP_TZ(
last_located_time,
'YYYY-MM-DD"T"HH24:MI:SS.FFTZHTZM'
) AT TIME ZONE 'UTC' AS utc_last_located_time
FROM Tb_089
);
If you want the time component of the string (without adjusting for disparate time zones) then you could just do:
SELECT SUBSTR( last_located_time, 12, 8 )
FROM Tb_089
To_Date is used to convert Char and Varchar2 into Date.
You need to use To_Char which is opposite of To_Date
Select To_Char(LAST_LOCATED_TIME, 'HH24:MI:SS') "Last Time" from Tb_089;

Convert number to date

In Oracle, I have column named Create_date with data returning as 1400003659, 1400072380, and 1403796514 as examples. The column type is NOT NULL NUMBER(15). I'm trying to edit my SELECT statement to return these values as dates (or are these dates and times?).
I've tried the below, but all are returning errors:
SELECT to_date(Create_date, 'YYMMDD'),
SELECT to_date(to_char(Create_date), 'YYMMDD'),
SELECT to_timestamp(Create_date, 'YYMMDD'),
SELECT to_date(LPAD(Create_date, 15, '0'), 'YYMMDD'),
SELECT to_date(LPAD(Create_date), 'YYMMDD'),
An example error message I'm receiving:
SQL Error: ORA-01843: not a valid month
01843. 00000 - "not a valid month"
This looks like a unix timestamp which is the number of seconds since 1/1/1970.
If you want just the date, you need to calculate the number of days and add it to 1/1/1970 like so:
to_date('1970-01-01','YYYY-MM-DD') + numtodsinterval(1400003659,'SECOND')
If you want to retain the timestamp, you can do so like this:
to_char(to_date('1970-01-01','YYYY-MM-DD') + numtodsinterval(1400003659,'SECOND'),'YYYY-MM-DD HH24:MI:SS')
See this for more information.
You need to know what that number means.
If those are UNIX dates (number of second since January 1, 1970), then you can use simple date arithmetic from that baseline by adding the value / 86400 (seconds in a day) to jan 1 1970:
select to_date('01011970','ddmmyyyy') + (1400003659/86400) from dual;
returns: 13/05/2014 5:54:19 PM
Try like this:
select to_date('01-01-1970 1:00:00','MM-DD-YYYY HH24:Mi:SS') + (1400003659/86400) from dual;
FIDDLE DEMO
The answers are not precise! UNIX time is seconds since 1970-01-01 00:00:00 UTC!
So, unless your database server runs on UTC you should do it like this:
SELECT
(TIMESTAMP '1970-01-01 00:00:00' AT TIME ZONE 'UTC'
+ 1400003659 * INTERVAL '1' SECOND) AT LOCAL
FROM dual;
or
SELECT
(TIMESTAMP '1970-01-01 00:00:00' AT TIME ZONE 'UTC'
+ numtodsinterval(1400003659,'second')) AT LOCAL
FROM dual;
or to get the time at time zone of database server's operating system
SELECT
(TIMESTAMP '1970-01-01 00:00:00' AT TIME ZONE 'UTC'
+ numtodsinterval(1400003659,'second')) AT TO_CHAR(SYSTIMESTAMP, 'tzr')
FROM dual;

Oracle SQL: convert UTC to CST

I would like to convert UTC date/time to local CST.
The below function works however it gives 6 hours difference when there should only be 5 hours (until day light saving on 11/2/2014).
CAST((FROM_TZ(CAST(utc_date AS TIMESTAMP),'UTC') AT TIME ZONE 'CST') AS DATE) cst_date
also tried a variation
to_date(to_char((from_tz(to_timestamp(to_char(utc_date, 'YYYY-MM-DD HH24:MI:SS'), 'YYYY-MM-DD HH24:MI:SS') ,'UTC')
at time zone 'CST'),'YYYY-MM-DD HH24:MI:SS'),'YYYY-MM-DD HH24:MI:SS') as cst_date,
Using the "US/Central" as the target timezone seems to produce the right result.
select from_tz(CAST ('15-oct-2014' AS TIMESTAMP),'GMT') at TIME ZONE 'US/Central' with_daylight_savings,
from_tz(CAST ('15-nov-2014' AS TIMESTAMP),'GMT') at TIME ZONE 'US/Central' without_daylight_savings
from dual;
WITH_DAYLIGHT_SAVINGS WITHOUT_DAYLIGHT_SAVINGS
--------------------------------------------------------------------------------------
14-OCT-14 07.00.00.000000000 PM US/CENTRAL 14-NOV-14 06.00.00.000000000 PM US/CENTRAL
Use timezone region instead of timezone abbr ('CST'). You may find the desired timezone here:
SELECT * from v$timezone_names where tzabbrev = 'CST';
Maybe you need 'CST6CDT' instead of 'CST'
Maybe a stupid approach, but what do you get from this query?
SELECT
TO_CHAR((TIMESTAMP '2014-01-01 00:00:00' + LEVEL * INTERVAL '1' DAY) AT TIME ZONE 'America/Chicago', 'yyyy-mm-dd hh24:mi TZH:TZM') AS dst,
TO_CHAR((FROM_TZ(CAST(DATE '2014-01-01' AS TIMESTAMP), 'UTC') + LEVEL * INTERVAL '1' DAY) AT TIME ZONE 'America/Chicago', 'yyyy-mm-dd hh24:mi TZH:TZM') AS dst
FROM dual
CONNECT BY LEVEL <= 365;
Is it as expected?