Converting a timestamp with Time zone to just a timestamp - sql

Trying to insert some data into a a table. However, I tried i am receiving data compatibility issues. The column that I am trying to insert into is a timestamp(6) column while the column I am drawing data from is a timestamp with timezone column. I know how to use cast to convert from timestamp to a timestamp with timezone but not the inverse. Is there a way I can just strip out the 'UTC'?
Date I am starting out with:
'20-MAY-18 09.00.00.000000000 AM UTC'
Date I want to end up with:
'20-MAY-18 09.00.00.000000000 AM'
What I have tried thus far:
select to_date('20-MAY-18 09.00.00.000000000 AM UTC', 'dd-mon-yy hh:mi:ss A.M.') from dual;
However I receive an error, and I just can't seem to figure it out, what am I doing wrong? thanks in advance!

You can cast a timestamp with time zone to a plain timestamp:
cast(<your_value> as timestamp)
so with your value:
select cast(
to_timestamp_tz('20-MAY-18 09.00.00.000000000 AM UTC', 'DD-MON-RR HH:MI:SS.FF AM TZR')
as timestamp)
from dual;
CAST(TO_TIMESTAMP_T
-------------------
2018-05-20 09:00:00
If you insert as timestamp with tome zone value into a plain timestamp column then it will be converted automatically, just losing its tie zone information.
If the values might not always be UTC then you can convert them to UTC and to a plain timestamp in one go with sys_extract_tc():
with cte (tsz) as (
select timestamp '2018-05-20 09:00:00.0 UTC' from dual
union all select timestamp '2018-05-20 13:00:00.0 America/New_York' from dual
)
select tsz, cast(tsz as timestamp) as ts, sys_extract_utc(tsz) utc
from cte;
TSZ TS UTC
------------------------------ ------------------- -------------------
2018-05-20 09:00:00.000 +00:00 2018-05-20 09:00:00 2018-05-20 09:00:00
2018-05-20 13:00:00.000 -04:00 2018-05-20 13:00:00 2018-05-20 17:00:00

Related

How to calculate datetime interval

Wonder how to calculate de interval between this datetime. I've been trying Datetime_diff() function in SQL Bigqury but unsucessfuly.
started_at
ended_at
2020-04-26 17:45:00 UTC
2020-04-26 18:12:00 UTC
2020-04-17 17:08:00 UTC
2020-04-17 17:17:00 UTC
2020-04-01 17:54:00 UTC
2020-04-01 18:08:00 UTC
I would like to add a new column with the DURATION of this time travels.
started_at and ended_at looks TIMESTAMP type, so use TIMESTAMP_DIFF() like below.
WITH sample_data AS (
SELECT TIMESTAMP '2020-04-26 17:45:00 UTC' started_at, TIMESTAMP '2020-04-26 18:12:00 UTC' ended_at UNION ALL
SELECT '2020-04-17 17:08:00 UTC', '2020-04-17 17:17:00 UTC' UNION ALL
SELECT '2020-04-01 17:54:00 UTC', '2020-04-01 18:08:00 UTC'
)
SELECT TIMESTAMP_DIFF(ended_at, started_at,MINUTE) duration FROM sample_data;
If it's STRING type by any chance, cast it as TIMESTAMP like below instead.
TIMESTAMP_DIFF(TIMESTAMP(ended_at), TIMESTAMP(started_at), MINUTE)

How to convert string datetime to timestamp with time zone?

How can I convert this string "09/23/2022 12:44 PM EDT" to timestamp with time zone using "to_timestamp_tz" function in oracle 19c?
EDT and EST are ambiguous as they are only valid half the year. To be consistent, you need to use US/EASTERN instead:
SELECT TO_TIMESTAMP_TZ(
REGEXP_REPLACE(
value,
'(EDT|EST)$',
'US/EASTERN'
),
'MM/DD/YYYY HH12:MI AM TZR'
) AS tz
FROM table_name;
Which, for the sample data:
CREATE TABLE table_name (value) AS
SELECT '09/23/2022 12:44 PM EDT' FROM DUAL UNION ALL
SELECT '06/23/2022 12:44 PM EDT' FROM DUAL UNION ALL
SELECT '12/23/2022 12:44 PM EDT' FROM DUAL;
Outputs:
TZ
2022-09-23 12:44:00.000000000 -04:00
2022-06-23 12:44:00.000000000 -04:00
2022-12-23 12:44:00.000000000 -05:00
fiddle

Is it possible to order by truncated timestamp in SQL (oracle)?

I have a use case where I want to order by time, but at a certain resolution. For example my schema saves timestamps out to 9 decimals (nanosecond precision), but I only want to order by minutes and use a different field to order within that minute. I tried this
select * from myTable order by (cast(myTimeStamp at time zone 'UTC' as timestamp) - to_timestamp('01-JAN-01'))/1000000000*60 desc, id desc;
To convert the timestamp into epoch and then divide to get minute precision. But this gives the wrong ordering. Also when I do a dump on the above command to understand the returned data type I see data type: typ=190 and I can't find that type in the oracle docs which adds to my confusion.
So I'm wondering what I'm missing? It should be possible to order by a truncated (to minute) timestamp, any help is appreciated.
Convert the TIMESTAMP WITH TIME ZONE to the UTC time zone so that you can compare identical times and then TRUNCate it back to the start of the minute:
SELECT *
FROM myTable
ORDER BY
TRUNC(myTimeStamp at time zone 'UTC', 'MI') DESC,
id DESC;
Which, for the sample data:
CREATE TABLE myTable(
id NUMBER,
myTimestamp TIMESTAMP WITH TIME ZONE
);
INSERT INTO myTable(id, myTimestamp)
SELECT 1, TIMESTAMP '1970-01-01 00:00:00 UTC' FROM DUAL UNION ALL
SELECT 2, TIMESTAMP '1970-01-01 00:00:00 America/New_York' FROM DUAL UNION ALL
SELECT 3, TIMESTAMP '1970-01-01 00:00:00 Asia/Hong_Kong' FROM DUAL UNION ALL
SELECT 4, TIMESTAMP '1970-01-01 00:00:00 Europe/Paris' FROM DUAL UNION ALL
SELECT 5, TIMESTAMP '1970-01-01 01:00:00 UTC' FROM DUAL UNION ALL
SELECT 6, TIMESTAMP '1970-01-01 01:00:00 America/New_York' FROM DUAL UNION ALL
SELECT 7, TIMESTAMP '1970-01-01 01:00:00 Europe/Berlin' FROM DUAL;
Outputs:
ID
MYTIMESTAMP
6
01-JAN-70 01.00.00.000000 AMERICA/NEW_YORK
2
01-JAN-70 00.00.00.000000 AMERICA/NEW_YORK
5
01-JAN-70 01.00.00.000000 UTC
7
01-JAN-70 01.00.00.000000 EUROPE/BERLIN
1
01-JAN-70 00.00.00.000000 UTC
4
01-JAN-70 00.00.00.000000 EUROPE/PARIS
3
01-JAN-70 00.00.00.000000 ASIA/HONG_KONG
If you want to see the values converted to UTC that are being used in the sorting process then just add it in the output:
SELECT t.*,
TO_CHAR(TRUNC(myTimeStamp at time zone 'UTC', 'MI'), 'YYYY-MM-DD HH24:MI:SS')
AS converted_ts
FROM myTable t
ORDER BY
TRUNC(myTimeStamp at time zone 'UTC', 'MI') DESC,
id DESC;
Which outputs:
ID
MYTIMESTAMP
CONVERTED_TS
6
01-JAN-70 01.00.00.000000 AMERICA/NEW_YORK
1970-01-01 06:00:00
2
01-JAN-70 00.00.00.000000 AMERICA/NEW_YORK
1970-01-01 05:00:00
5
01-JAN-70 01.00.00.000000 UTC
1970-01-01 01:00:00
7
01-JAN-70 01.00.00.000000 EUROPE/BERLIN
1970-01-01 00:00:00
1
01-JAN-70 00.00.00.000000 UTC
1970-01-01 00:00:00
4
01-JAN-70 00.00.00.000000 EUROPE/PARIS
1969-12-31 23:00:00
3
01-JAN-70 00.00.00.000000 ASIA/HONG_KONG
1969-12-31 16:00:00
If you just use TRUNC without converting to a common time zone then it will order based on the date and time components without considering the relative difference in the time zones.
db<>fiddle here
Why don't you then just truncate timestamp to minutes?
SQL> alter session set nls_date_format = 'dd.mm.yyyy hh24:Mi:ss';
Session altered.
SQL> select systimestamp col_1,
2 trunc(systimestamp, 'mi') col_2
3 from dual;
COL_1 COL_2
---------------------------------------- -------------------
29.12.21 20:32:50,178000 +01:00 29.12.2021 20:32:00
SQL>
Then you'd
order by trunc(timestamp_column, 'mi'),
yet_another_column

Add 8AM UTC to a date in oracle

I have a date something like below :
Thu Nov 29 18:00:00 CST 2018
Thu Apr 26 01:00:00 BST 2018
I need to convert it to 8AM UTC in oracle.
How do i do this ?
It is a string not date.
Referred link deals with proper dates and there is no accepted answer for it.
Thanks in Advance
Since it's a string you could use regexp_replace.
regexp_replace(nmuloc, '[[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2} [A-Z]{3}', '08:00:00 GMT')
Oracle Setup
CREATE TABLE table_name ( datetime TIMESTAMP WITH TIME ZONE );
INSERT INTO table_name
SELECT TIMESTAMP '2018-11-29 18:00:00 CST' FROM DUAL UNION ALL
SELECT TIMESTAMP '2018-04-26 01:00:00 Europe/London' FROM DUAL UNION ALL
SELECT TIMESTAMP '2018-06-26 00:00:00 Europe/London' FROM DUAL;
Query 1:
Use datetime AS TIME ZONE 'UTC' to convert it from your time zone to UTC
Then use TRUNC() to truncate it back to the start of the UTC day (and also cast it to a date)
Because its now a date, use CAST( ... AS TIMESTAMP ) to get it back to a timestamp
Then use FROM_TZ( ..., 'UTC' ) to get it to be a timestamp in the UTC time zone
Then add INTERVAL '8' HOUR to be 8am.
Like this:
SELECT FROM_TZ(
CAST(
TRUNC( datetime AT TIME ZONE 'UTC' )
AS TIMESTAMP
),
'UTC'
) + INTERVAL '8' HOUR AS utc_date_at_8am_utc
FROM table_name;
Output:
UTC_DATE_AT_8AM_UTC
--------------------------------
30-NOV-18 08.00.00.000000 AM UTC
26-APR-18 08.00.00.000000 AM UTC
25-JUN-18 08.00.00.000000 AM UTC
Note: this translates 2018-06-26 00:00:00 BST to 2018-06-25 23:00:00 UTC before truncating. So it will be the same UTC day (but not necessarily the same day in the local time zone).
Query 2
If this is an issue then just remove the initial time zone conversion:
SELECT FROM_TZ(
CAST(
TRUNC( datetime )
AS TIMESTAMP
),
'UTC'
) + INTERVAL '8' HOUR AS date_at_8am_utc
FROM table_name
Output:
DATE_AT_8AM_UTC
--------------------------------
29-NOV-18 08.00.00.000000 AM UTC
26-APR-18 08.00.00.000000 AM UTC
26-JUN-18 08.00.00.000000 AM UTC

Oracle SQL: Handle with Daylight Saving Time

I have the following system information:
I use Oracle Database 10g
The SysTimeStamp is UTC
The SessionTimeZone is Europe/Athens
The dbTimeZone is +03:00
So, I have the column date_1 from tbl_1 table, with the following datetime:
date_1
-----------------
08.02.2017 10:00
08.02.2017 11:00
08.02.2017 12:00
-----------------
The results I want is like this:
date_2
-----------------
08.02.2017 13:00
08.02.2017 14:00
08.02.2017 15:00
For that I use:
SELECT TO_CHAR(date_1 + INTERVAL '3' HOUR, 'DD.MM.YYYY HH24:MI') as date_2
FROM tbl_1
WHERE date_1 >= TO_DATE('08.02.2017 10:00','DD.MM.YYYY HH24:MI')
AND date_1 <= TO_DATE('08.02.2017 12:00','DD.MM.YYYY HH24:MI')
My problem appear when the hour from March and October is changing because in the last Sunday from March we have 23 hours in a day and in the last Sunday from October we have 25 hours in a day.
Because of this I have to change my query 4 times/year (On summer time, on winter time, when we have 23 hour in March and when we have 25 hour in October)
Can you recommend a query in this select that solve this problem?
If you have a plain date or timestamp with no embedded time zone information, you can tell Oracle to treat it as being in a specific time zone with the from_tz() function. You can then convert that value - which now has data type 'timestamp with zone zone' rather than a plain 'timestamp' - to another zone with the at time zone datetime expression syntax, either using the session time zone as 'local' or with a specific named time zone:
alter session set nls_date_format='YYYY-MM-DD HH24:MI:SS';
alter session set nls_timestamp_format='YYYY-MM-DD HH24:MI:SS';
alter session set nls_timestamp_tz_format='YYYY-MM-DD HH24:MI:SS TZR';
alter session set time_zone = 'America/New_York';
with cte (ts) as (
select timestamp '2017-02-08 12:00:00' from dual
)
select ts,
from_tz(ts, 'UTC') as ts_utc,
from_tz(ts, 'UTC') at local as ts_local,
from_tz(ts, 'UTC') at time zone 'Europe/Athens' as ts_athens
from cte;
TS TS_UTC TS_LOCAL TS_ATHENS
------------------- ----------------------- ------------------------------------ ---------------------------------
2017-02-08 12:00:00 2017-02-08 12:00:00 UTC 2017-02-08 07:00:00 AMERICA/NEW_YORK 2017-02-08 14:00:00 EUROPE/ATHENS
If you're starting from a date then you have to convert it to a timestamp before calling from_tz():
with cte (dt) as (
select cast( timestamp '2017-02-08 12:00:00' as date) from dual
)
select dt,
from_tz(cast(dt as timestamp), 'UTC') as ts_utc,
from_tz(cast(dt as timestamp), 'UTC') at local as ts_local,
from_tz(cast(dt as timestamp), 'UTC') at time zone 'Europe/Athens' as ts_athens
from cte;
DT TS_UTC TS_LOCAL TS_ATHENS
------------------- ----------------------- ------------------------------------ ---------------------------------
2017-02-08 12:00:00 2017-02-08 12:00:00 UTC 2017-02-08 07:00:00 AMERICA/NEW_YORK 2017-02-08 14:00:00 EUROPE/ATHENS
So the data type of your original date_1 values matters, as does the nominal time zone it is supposed to represent. If it's a;ready a 'timestamp with time zone' or 'timestamp with local time zone' then it already has embedded time zone information, so you don't need the from_tz() part at all. If it's a date you need to convert it to a timestamp.
Assuming that date_1 is stored as a plain timestamp (maybe implied by your interval addition, but not by the column name and filters you used) and that it's nominally UTC, you could do:
from_tz(date_1, 'UTC') at time zone 'Europe/Athens'
... which will give you a 'timestamp with time zone' result; or you could use local to rely on your session time zone. If `date_1 is stored as a date you'd add the conversion to timestamp:
from_tz(cast(date_1 as timestamp), 'UTC') at time zone 'Europe/Athens'
As a demo, generating timestamps (not dates) in a CTE including some around the DST change for this year:
with tbl_1(date_1) as (
select timestamp '2017-02-08 10:00:00' from dual
union all select timestamp '2017-02-08 11:00:00' from dual
union all select timestamp '2017-02-08 12:00:00' from dual
union all select timestamp '2017-03-23 12:00:00' + numtodsinterval(level, 'day')
from dual connect by level <= 4
)
select date_1,
-- cast(from_tz(date_1, 'UTC') at time zone 'Europe/Athens' as timestamp) as date_2
to_char(from_tz(date_1, 'UTC') at time zone 'Europe/Athens',
'DD.MM.YYYY HH24:MI') as date_2
from tbl_1
order by date_1;
DATE_1 DATE_2
------------------- ----------------
2017-02-08 10:00:00 08.02.2017 12:00
2017-02-08 11:00:00 08.02.2017 13:00
2017-02-08 12:00:00 08.02.2017 14:00
2017-03-24 12:00:00 24.03.2017 14:00
2017-03-25 12:00:00 25.03.2017 14:00
2017-03-26 12:00:00 26.03.2017 15:00
2017-03-27 12:00:00 27.03.2017 15:00
You can see that an extra hour is added automatically after the clocks change on March 26th. But the results are out by an hour for your sample February data - so either your data isn't actually stored as UTC (but is -01:00, and you can change the from_tz() call to reflect that), or your expected results are wrong.
You can apply a case to the select:
select date_1 + case
when to_char(date_1 ,'MM') <= 3 then 2/24 -- Jan/Feb/Mar
when to_char(date_1,'MM') <= 10 then 3/24 -- Apr to Oct
else 2/24 -- Nov/Dec
end as date_2
from tbl_1
For USA timezone
SELECT SYSDATE,
NEXT_DAY ( TO_DATE (TO_CHAR (SYSDATE, 'YYYY') || '/03/01 02:00 AM', 'YYYY/MM/DD HH:MI AM') - 1, 'SUN') + 7 dst_start,
NEXT_DAY ( TO_DATE (TO_CHAR (SYSDATE, 'YYYY') || '/11/01 02:00 AM', 'YYYY/MM/DD HH:MI AM') - 1, 'SUN') dst_end,
CASE WHEN SYSDATE >= NEXT_DAY ( TO_DATE ( TO_CHAR (SYSDATE, 'YYYY') || '/03/01 02:00 AM', 'YYYY/MM/DD HH:MI AM') - 1, 'SUN') + 7 AND SYSDATE < NEXT_DAY ( TO_DATE ( TO_CHAR (SYSDATE, 'YYYY') || '/11/01 02:00 AM', 'YYYY/MM/DD HH:MI AM') - 1, 'SUN') THEN 'Y' ELSE 'N' END AS dst_check_usa,
NEW_TIME ( SYSDATE, CASE WHEN SYSDATE >= NEXT_DAY ( TO_DATE ( TO_CHAR (SYSDATE, 'YYYY') || '/03/01 02:00 AM', 'YYYY/MM/DD HH:MI AM') - 1, 'SUN') + 7 AND SYSDATE < NEXT_DAY ( TO_DATE ( TO_CHAR (SYSDATE, 'YYYY') || '/11/01 02:00 AM', 'YYYY/MM/DD HH:MI AM') - 1, 'SUN') THEN 'CDT' ELSE 'CST' END, 'GMT') AS current_time_gmt
FROM DUAL;
For Europe Timezone
SELECT SYSDATE,
NEXT_DAY(LAST_DAY(TO_DATE (TO_CHAR (SYSDATE, 'YYYY') || '/03/01 02:00 AM', 'YYYY/MM/DD HH:MI AM'))-7, 'SUN') dst_start_uk,
NEXT_DAY(LAST_DAY(TO_DATE (TO_CHAR (SYSDATE, 'YYYY') || '/10/01 02:00 AM', 'YYYY/MM/DD HH:MI AM'))-7, 'SUN') dst_end_uk,
CASE WHEN SYSDATE >= NEXT_DAY(LAST_DAY(TO_DATE (TO_CHAR (SYSDATE, 'YYYY') || '/03/01 02:00 AM', 'YYYY/MM/DD HH:MI AM'))-7, 'SUN') AND SYSDATE < NEXT_DAY(LAST_DAY(TO_DATE (TO_CHAR (SYSDATE, 'YYYY') || '/10/01 02:00 AM', 'YYYY/MM/DD HH:MI AM'))-7, 'SUN') THEN 'Y' ELSE 'N' END AS dst_check_uk
FROM DUAL;