Oracle TO_DATE format, symbol for "any character" - oracle

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

Related

How to convert varchar with short time zone to timestamp in oracle

We have date stored in varchar2 column as 'Tue Dec 18 16:49:31 EST 2018', I would like to convert this to timestamp. Tried with to_timestamp('Tue Dec 18 16:49:31 EST 2018','DY Mon DD HH24:MI:SS TS YYYY') but not working. Please help.
If you like to preserve the time zone information, you have to use TO_TIMESTAMP_TZ, otherwise you can do it like this:
SELECT
TO_TIMESTAMP_TZ(
REGEXP_REPLACE(
'Tue Dec 18 16:49:31 EST 2018',
'\w+ (\w+) (\d+) (\d\d:\d\d:\d\d) (\w+) (\d+)',
'\5-\1-\2 \3 \4'
),
'YYYY-Mon-DD HH24:MI:SS TZR',
'NLS_DATE_LANGUAGE=American'),
TO_TIMESTAMP(
REGEXP_REPLACE(
'Tue Dec 18 16:49:31 EST 2018',
'\w+ (\w+) (\d+) (\d\d:\d\d:\d\d) \w+ (\d+)',
'\4-\1-\2 \3'
),
'YYYY-Mon-DD HH24:MI:SS',
'NLS_DATE_LANGUAGE=American')
FROM DUAL;
However, it will fail for EDT, see:
SELECT TZNAME, TZABBREV, TZ_OFFSET(TZNAME)
FROM V$TIMEZONE_NAMES
WHERE TZNAME IN ('EST','EDT')
You could use REPLACE('Tue Dec 18 16:49:31 EDT 2018', 'EDT', 'EST5EDT') to make it working.

How to convert String with timezone to Date

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

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