How to convert String with timezone to Date - oracle

How to convert below string to Date in Oracle
Wed Jan 13 23:01:24 GMT 2016
Tried below and this get's date as
SELECT TO_CHAR(SYSDATE, 'DY MON DD HH24:MM:SS YYYY') FROM dual;
FRI AUG 26 14:08:04 2016
Eventually, Wanted something like this
SELECT TO_CHAR('Wed Jan 13 23:01:24 GMT 2016', 'DY MON DD HH24:MM:SS ??? YYYY') FROM dual;

If the time zone is always a recognised abbreviation you can do:
select to_timestamp_tz('Wed Jan 13 23:01:24 GMT 2016', 'Dy Mon DD HH24:MI:SS TZD YYYY')
from dual;
TO_TIMESTAMP_TZ('WEDJAN1323:01:24GMT2016','DYMONDDHH24:MI:SSTZDYYYY')
---------------------------------------------------------------------
2016-01-13 23:01:24 EUROPE/LONDON
You can't convert directly to a date because the time zone format elements aren't allowed in to_date(). If you had a fixed value - always GMT - you could ignore that by treating it as a literal, but you don't.
If you want it as a timestamp or a date, rather than a timestamp with time zone, you need to decide how to convert it. You can assume it's local time and essentially ignore the time zone by casting it, or you can adjust it to a specific time zone, e.g. UTC. There are various ways, here are a couple with a CTE to provide your sample and one in another zone (well, in summertime anyway so you get a different string):
with t (str) as (
select 'Wed Jan 13 23:01:24 GMT 2016' from dual
union all select 'Fri Aug 26 19:53:27 BST 2016' from dual
)
select to_timestamp_tz(str, 'Dy Mon DD HH24:MI:SS TZD YYYY') as tstz,
cast(to_timestamp_tz(str, 'Dy Mon DD HH24:MI:SS TZD YYYY') as timestamp) as ts,
cast(to_timestamp_tz(str, 'Dy Mon DD HH24:MI:SS TZD YYYY') as date) as dt,
sys_extract_utc(to_timestamp_tz(str, 'Dy Mon DD HH24:MI:SS TZD YYYY')) as tsutc
from t;
TSTZ TS DT TSUTC
--------------------------------- ------------------- ------------------- -------------------
2016-01-13 23:01:24 EUROPE/LONDON 2016-01-13 23:01:24 2016-01-13 23:01:24 2016-01-13 23:01:24
2016-08-26 19:53:27 EUROPE/LONDON 2016-08-26 19:53:27 2016-08-26 19:53:27 2016-08-26 18:53:27
Exactly how you handle it depends on what you really need, of course.
Unfortunately that doesn't always work with abbreviations; Oracle doesn't necessarily recognise the values you see in Unix date command output, and the ones it does recognise aren't always available. Changing the session time zone can break it:
alter session set time_zone = 'America/Los_Angeles';
select to_timestamp_tz('Wed Jan 13 23:01:24 GMT 2016', 'Dy Mon DD HH24:MI:SS TZD YYYY')
from dual;
ORA-01857: not a valid time zone
You can change the session time zone to one that does recognise it (Europe/London) but that's a hack and won't work for all values anyway. It doesn't help that abbreviations can mean more than one thing.
If you have a list of known expected values and know what they really represent to you, you can swap the abbreviation for a region, but it doesn't really scale:
select to_timestamp_tz(
replace(replace('Wed Jan 13 23:01:24 GMT 2016', 'GMT', 'Europe/London'),
'BST', 'Europe/London'),
'Dy Mon DD HH24:MI:SS TZR YYYY') from dual;
Or with multiple output formats:
with t1 (str) as (
select 'Wed Jan 13 23:01:24 GMT 2016' from dual
union all select 'Fri Aug 26 19:53:27 BST 2016' from dual
),
t2 (adj_str) as (
select replace(replace(str, 'GMT', 'Europe/London'), 'BST', 'Europe/London')
from t1
)
select to_timestamp_tz(adj_str, 'Dy Mon DD HH24:MI:SS TZR YYYY') as tstz,
cast(to_timestamp_tz(adj_str, 'Dy Mon DD HH24:MI:SS TZR YYYY') as timestamp) as ts,
cast(to_timestamp_tz(adj_str, 'Dy Mon DD HH24:MI:SS TZR YYYY') as date) as dt,
sys_extract_utc(to_timestamp_tz(adj_str, 'Dy Mon DD HH24:MI:SS TZR YYYY')) as tsutc
from t2;
You'd need to have nested replace calls (or regexp_replace to reduce the repetition a little) for each abbreviation you expect; or could have a function that hides that mess away from your main query.

You need to use the TO_TIMESTAMP_TZ function.
The following example converts a character string to a value of TIMESTAMP WITH TIME ZONE:
SELECT TO_TIMESTAMP_TZ('1999-12-01 11:00:00 -8:00',
'YYYY-MM-DD HH:MI:SS TZH:TZM') FROM DUAL;
The -8:00 is the timezone.
https://docs.oracle.com/cd/B12037_01/server.101/b10759/functions179.htm#SQLRF06143

Related

Oracle convert from GMT to EST and EDT

I am using Oracle 19c.
I need to convert dates from GMT to EST and EDT.
I am using the following approach:
1. Get the destination time zone abbreviation for the p_date variable:
DEFINE p_date TO_DATE('03/11/2013 02:22:21', 'MM/DD/YYYY HH24:MI:SS');
SELECT TO_CHAR(FROM_TZ(CAST (&p_date AS TIMESTAMP), 'America/New_York'), 'TZD') INTO v_tzabbrev FROM DUAL;
Where:
p_date: is the date to be converted.
v_tzname: is the time zone name, such as America/New_York
v_tzabbrev: is the time zone abbreviation, such as 'EDT' or "EST" based on whether the date is during Daylight Saving Time or not
2. Convert the p_date using the time zone abbreviation obtained in #1
SELECT NEW_TIME(p_date, 'GMT', v_tzabbrev) INTO v_date FROM DUAL;
This seems to work. But, I believe the flaw is that it is using the GMT date to determine the destination time zone abbreviation, which is inaccurate.
For example, if p_date, in UTC, is '03/11/2013 02:22:21' and I need to convert it to 'America/New_York', Step #1 would return 'EDT', but this date in Eastern was actually "03/10/2013 21:22:21", which was before Daylight Saving started. So, it should actually be converted using "EST".
Daylight saving time in '2013 began at 2 a.m. on Sunday, March 10.
So, it seems that I need a way to take the GMT value and determine its new date in Eastern first, then apply additional logic based on whether that new date is EDT or EST.
Any assistance is appreciated.
You can define p_date directly as UTC time:
DEFINE p_date TO_TIMESTAMP_TZ('03/11/2013 02:22:21 UTC', 'MM/DD/YYYY HH24:MI:SS TZR');
SELECT TO_CHAR((&p_date AT TIME ZONE 'America/New_York'), 'TZD')
INTO v_tzabbrev
FROM DUAL;
Or in the statement:
DEFINE p_date TO_DATE('03/11/2013 02:22:21', 'MM/DD/YYYY HH24:MI:SS');
SELECT TO_CHAR((FROM_TZ(CAST(&p_date AS TIMESTAMP), 'UTC') AT TIME ZONE 'America/New_York'), 'TZD')
INTO v_tzabbrev
FROM DUAL;
Another possibility is to use SESSIONTIMEZONE implicitly, although I don't recommend this:
DEFINE p_date TO_DATE('03/11/2013 02:22:21', 'MM/DD/YYYY HH24:MI:SS');
ALTER SESSION SET TIME ZONE = 'UTC';
SELECT TO_CHAR((CAST(&p_date AS TIMESTAMP WITH TIME ZONE) AT TIME ZONE 'America/New_York'), 'TZD')
INTO v_tzabbrev
FROM DUAL;
"Daylight saving time in '2013 began at 2 a.m. on Sunday, March 10."
... which is correct, and you can see that happening with the UTC equivalent date/time as:
-- get New York DST start time as UTC
with cte (ts) as (
select timestamp '2013-03-10 01:59:59 America/New_York' from dual
union all
select timestamp '2013-03-10 03:00:00 America/New_York' from dual
)
select ts, to_char(ts, 'TZD') as tzd, ts at time zone 'UTC' as ts_utc
from cte
TS
TZD
TS_UTC
2013-03-10 01:59:59 AMERICA/NEW_YORK
EST
2013-03-10 06:59:59 UTC
2013-03-10 03:00:00 AMERICA/NEW_YORK
EDT
2013-03-10 07:00:00 UTC
but this date in Eastern was actually "03/10/2013 21:22:21", which was before Daylight Saving started.
No, it isn't, it's after DST started.
So, it should actually be converted using "EST".
No, it shouldn't. I'm afraid the premise of your question is wrong.
The conversion you are doing is getting the correct result:
-- get UTC timestamp as New York
with cte (ts) as (
select timestamp '2013-03-11 02:22:21 UTC' from dual
)
select ts as ts_utc, ts at time zone 'America/New_YORK' as ts, to_char(ts at time zone 'America/New_York', 'TZD') as tzd
from cte
TS_UTC
TS
TZD
2013-03-11 02:22:21 UTC
2013-03-10 22:22:21 AMERICA/NEW_YORK
EDT
fiddle
2013-03-11 02:22:21 UTC is after 2013-03-10 01:59:59 America/New_York, as it is the following day in UTC, and 19 hours after the New York DST switch occurred. In other words, 2013-03-11 02:22:21 UTC is 19 hours after 2012-03-10 07:00:00 UTC, which is the UTC equivalent of the EDT start-time from the first query above.
You seem to be confusing the date in your UTC value with the date that DST was applied in the USA that year.
Because the NEW_TIME() function is limited, I would prefer to use FROM_TZ and AT TIME ZONE, as Wernfried showed.

Oracle TO_DATE format, symbol for "any character"

In this answer the following query is suggested:
SELECT TO_DATE(
'Thu Nov 24 15:20:52 CET 2016',
'DY MON DD HH24:MI:SS "CET" YYYY'
)
FROM DUAL
¿ there are some character to be used in the format with the meaning of "any" ? I like replace "CET" in the format string by something like "..." or "###", that is:
'DY MON DD HH24:MI:SS ... YYYY'
One example, ¿ which format will accept all the following date strings :
2017-12-31
2017_12_31
2017x12x31
You can extract relevant part by regular expression.
select
TO_DATE(
regexp_substr('Thu Nov 24 15:20:52 CET 2016', '^.+\d{2}:\d{2}:\d{2}')
||regexp_substr('Thu Nov 24 15:20:52 CET 2016', ' \d{4}$'),
'DY MON DD HH24:MI:SS YYYY')
from dual;
or
select
TO_DATE(
REGEXP_REPLACE('Thu Nov 24 15:20:52 CET 2016', '\w+ (\d{4})$', '\1'),
'DY MON DD HH24:MI:SS YYYY')
from dual;
Here's another way using to_timestamp_tz:
WITH sample_data AS (SELECT 'Thu Nov 24 15:20:52 CET 2016' str FROM dual UNION ALL
SELECT 'Fri Dec 11 23:16:39 PST 2015' str FROM dual)
SELECT str,
to_timestamp_tz(str, 'Dy Mon dd hh24:mi:ss TZR yyyy', 'nls_date_language = english') timestamp_at_orig_tz,
CAST(to_timestamp_tz(str, 'Dy Mon dd hh24:mi:ss TZR yyyy', 'nls_date_language = english') AS DATE) dt,
CAST(to_timestamp_tz(str, 'Dy Mon dd hh24:mi:ss TZR yyyy', 'nls_date_language = english') AT TIME ZONE '-5:00' AS DATE) dt_at_utc_minus_5,
cast(sys_extract_utc(to_timestamp_tz(str, 'Dy Mon dd hh24:mi:ss TZR yyyy', 'nls_date_language = english')) AS DATE) dt_at_utc
FROM sample_data;
STR TIMESTAMP_AT_ORIG_TZ DT DT_AT_UTC_MINUS_5 DT_AT_UTC
---------------------------- -------------------------------- ------------------- ------------------- -------------------
Thu Nov 24 15:20:52 CET 2016 24-NOV-16 15.20.52.000000000 CET 24/11/2016 15:20:52 24/11/2016 09:20:52 24/11/2016 14:20:52
Fri Dec 11 23:16:39 PST 2015 11-DEC-15 23.16.39.000000000 PST 11/12/2015 23:16:39 12/12/2015 02:16:39 12/12/2015 07:16:39
I've given a three different ways of converting the timestamp-as-a-string into a date:
Convert the timestamp as is into a date. Note that this will lose any timestamp information you have - useful if you really don't care about timezone information.
Convert the timestamp into a specific timezone (in my example, UTC - 5) before converting it into a date.
Convert the timestamp into UTC before converting it into a date (you can use the previous method to do this, but sys_extract_utc() is a little more self-explanatory, IMHO, which can only aid readability and maintainability!).
With your updated question (which seems to be more around the date part separator, not the timezone), the following should do the trick:
WITH sample_data AS (SELECT '2017-12-31' dt_str FROM dual UNION ALL
SELECT '2017_12_31' dt_str FROM dual UNION ALL
SELECT '2017x12x31' dt_str FROM dual)
SELECT dt_str,
to_date(regexp_replace(dt_str,
'([[:digit:]]{4}).([[:digit:]]{2}).([[:digit:]]{2})',
'\1-\2-\3'), 'yyyy-mm-dd')
FROM sample_data;
DT_STR DT
---------- ----------
2017-12-31 31/12/2017
2017_12_31 31/12/2017
2017x12x31 31/12/2017

inserting data into Timestamp datatype column is returning ORA-01858: a non-numeric character was found where a numeric was expected

Our application is trying to insert data into Oracle database in the format "Tue Feb 26 15:30:00 EST 2016" into column(FLD_TIMESTAMP) of Timestamp(6) datatype .We are receiving "ORA-01843: not a valid month" .
Wrote a trigger to convert the input data into timestamp format as below :
SQL> create or replace trigger test_wifi before insert on ISG_SESSION_tempfeb15 for each row
2 begin
3 :NEW.fld_timestamp :=to_char(to_timestamp_tz(':NEW.fld_timestamp','Dy Mon DD hh24:mi:ss TZD YYYY'),'DD-MON-YY hh:mi:ss AM');
4 end;
5 /
Trigger created.
While i am trying to insert data , it is still erroring out :
SQL> insert into ISG_SESSION_tempfeb15(isg_session_id,mac_address,nas_ip,fld_timestamp,created_dte)
2 values('5','6','7','Wed Jan 13 16:29:00 EST 2016','16-FEB-16 03:05:00.0000');
values('5','6','7','Wed Jan 13 16:29:00 EST 2016','16-FEB-16 03:05:00.0000')
*
ERROR at line 2:
ORA-01858: a non-numeric character was found where a numeric was expected
Can someone please tell me how to resolve this ? Thanks in Advance .
Why do you have to_char after converting to timestamp?
That's because you can't do the data conversion as part of a trigger like that. The issue is that the insert statement expects the data to be passed in with the correct format.
What you need to do is scrap your trigger and amend your insert statement to something like:
insert into ISG_SESSION_tempfeb15 (isg_session_id,
mac_address,
nas_ip,
fld_timestamp,
created_dte)
values ('5',
'6',
'7',
to_timestamp_tz('Wed Jan 13 16:29:00 EST 2016', 'Dy Mon dd hh24:mi:ss TZR yyyy'),
to_timestamp('16-FEB-2016 03:05:00.0000', 'dd-MON/yyyy hh24:mi:ss.ff4'));
N.B. I have amended the year of the created_dte that you're passing in to have four digits, not two. Y2K isn't a fairy tale!
Also, please note that if your column has a datatype of TIMESTAMP(6) and not TIMESTAMP(6) WITH TIME ZONE, you will lose any information about the timezone of the data.
eg.
create table t1 (col1 timestamp(6),
col2 timestamp(6) with time zone);
insert into t1 values (to_timestamp_tz('Wed Jan 13 16:29:00 EST 2016', 'Dy Mon dd hh24:mi:ss TZR yyyy'),
to_timestamp_tz('Wed Jan 13 16:29:00 EST 2016', 'Dy Mon dd hh24:mi:ss TZR yyyy'));
insert into t1 values (to_timestamp_tz('Wed Jan 13 16:29:00 UTC 2016', 'Dy Mon dd hh24:mi:ss TZR yyyy'),
to_timestamp_tz('Wed Jan 13 16:29:00 UTC 2016', 'Dy Mon dd hh24:mi:ss TZR yyyy'));
commit;
select * from t1;
COL1 COL2
--------------------------- ----------------------------------
13/01/2016 16:29:00.000000 13/01/2016 16:29:00.000000 -05:00
13/01/2016 16:29:00.000000 13/01/2016 16:29:00.000000 +00:00
See how the first column looks like it contains the same value, but the second column reveals that it doesn't?
Try change ':NEW.fld_timestamp' to :NEW.fld_timestamp
:NEW.fld_timestamp :=to_char(to_timestamp_tz(:NEW.fld_timestamp,'Dy Mon DD hh24:mi:ss TZD YYYY'),'DD-MON-YY hh:mi:ss AM');
this is your problem

Oracle EDT/EST time to GMT conversion

all dates which we store in oracle are in EDT(summer) and EST(winter). I need a way to convert that date to UTC/GMT.
The conversion should be based on the db date and not the servers local TZ.
In other words if the date in the resultset is between:
"11/02/2014 02:00:00" ---> "03/08/2015 01:59:59": TZ offset = "-0500"
"03/08/2015 02:00:00" ---> "11/01/2015 01:59:59": TZ offset = "-0400".
And same for previous years.
Is there a solution for that?
Assuming some_date_column is actually a date column then you can turn it into a timestamp with time zone using a cast (from date to timestamp) and the from_tz function with a region that has the EST/EDT change defined:
with t (some_date_column) as (
select to_date('11/02/2014 02:00:00', 'MM/DD/YYYY HH24:MI:SS') from dual
union all
select to_date('03/08/2014 01:59:59', 'MM/DD/YYYY HH24:MI:SS') from dual
union all
select to_date('03/08/2015 03:00:00', 'MM/DD/YYYY HH24:MI:SS') from dual
union all
select to_date('11/01/2015 00:59:59', 'MM/DD/YYYY HH24:MI:SS') from dual
union all
select to_date('11/01/2015 01:59:59', 'MM/DD/YYYY HH24:MI:SS') from dual
union all
select to_date('11/02/2015 02:00:00', 'MM/DD/YYYY HH24:MI:SS') from dual
)
select t.some_date_column,
cast(t.some_date_column as timestamp) as some_timestamp,
from_tz(cast(t.some_date_column as timestamp), 'America/New_York')
as some_timestamp_tz
from t;
SOME_DATE_COLUMN SOME_TIMESTAMP SOME_TIMESTAMP_TZ
------------------- ------------------- ---------------------------
11/02/2014 02:00:00 11/02/2014 02:00:00 11/02/2014 02:00:00 -05:00
03/08/2014 01:59:59 03/08/2014 01:59:59 03/08/2014 01:59:59 -05:00
03/08/2015 03:00:00 03/08/2015 03:00:00 03/08/2015 03:00:00 -04:00
11/01/2015 00:59:59 11/01/2015 00:59:59 11/01/2015 00:59:59 -04:00
11/01/2015 01:59:59 11/01/2015 01:59:59 11/01/2015 01:59:59 -05:00
11/02/2015 02:00:00 11/02/2015 02:00:00 11/02/2015 02:00:00 -05:00
This doesn't quite line up with the ranges in your question. There is no 02:00 when you move from EST to EDT, so I've used 03:00 in the demo CTE instead; and as the period between 01:00 and 02:00 is (sort of) repeated when you go the other way from EDT to EST the offset for a plain time in that hour is ambiguous, and Oracle is choosing to treat it as -05:00 rather than -04:00.
If you want the UTC equivalent of the EST/EDT time then you can then use at time zone, and optionally cast back to a date:
...
select t.some_date_column,
from_tz(cast(t.some_date_column as timestamp), 'America/New_York')
at time zone 'UTC' as utc_timestamp_tz,
cast(from_tz(cast(t.some_date_column as timestamp), 'America/New_York')
at time zone 'UTC' as date) as utc_date
from t;
SOME_DATE_COLUMN UTC_TIMESTAMP_TZ UTC_DATE
------------------- --------------------------- -------------------
11/02/2014 02:00:00 11/02/2014 07:00:00 +00:00 11/02/2014 07:00:00
03/08/2014 01:59:59 03/08/2014 06:59:59 +00:00 03/08/2014 06:59:59
03/08/2015 03:00:00 03/08/2015 07:00:00 +00:00 03/08/2015 07:00:00
11/01/2015 00:59:59 11/01/2015 04:59:59 +00:00 11/01/2015 04:59:59
11/01/2015 01:59:59 11/01/2015 06:59:59 +00:00 11/01/2015 06:59:59
11/02/2015 02:00:00 11/02/2015 07:00:00 +00:00 11/02/2015 07:00:00
If your original column is a timestamp (with no time zone information) rather than a date then you can skip the first cast from date to timestamp, but still use from_tz to specify it's original region.

GMT+5.30 string convert to Date in Oracle

As per below LINK,
I try to convert my string 'Tue May 24 20:11:20 GMT+05:30 2011' to date in oracle
select TO_TIMESTAMP_TZ('Tue May 24 20:11:20 GMT+05:30 2011', 'DY MON DD HH24:MI:SS TZDTZH:TZM YYYY') from dual;
But it's throw the error
ORA-01857: not a valid time zone
01857. 00000 - "not a valid time zone"
Please guide me how to convert with GMT Zone Name in Date conversion.
I don't think GMT has any significance here. Since, +5:30 always means it is relative to GMT, you can remove GMT from your string and then convert.
SELECT TO_TIMESTAMP_TZ (
REPLACE ('Tue May 24 20:11:20 GMT+05:30 2011', 'GMT'),
'DY MON DD HH24:MI:SS TZH:TZM YYYY')
FROM DUAL;
Or,
You can do this also, by putting GMT in double quotes.
SELECT TO_TIMESTAMP_TZ (
'Tue May 24 20:11:20 GMT+05:30 2011',
'DY MON DD HH24:MI:SS "GMT"TZH:TZM YYYY')
FROM DUAL;

Resources