Convert Varchar to ANY time format - oracle

I have a varchar column called begin_time that stores the 24-hour time as a varchar with no time formatting, ie 1330
I need to convert this varchar to a usable timestamp, datetime, etc. where I can easily convert the varchar to a standard time format (1:30 PM)
The end format type doesn't matter as long as I can convert the varchar into a format that I can manipulate to a standard format.
I've tried looking into Cognos-specific format tricks (These functions are for Metric Designer, and I'm using Report Studio) to no avail. The methods I found when looking for oracle-specific tricks seemed to be way too convoluted (using insanely long regex rules) for what I need.
If I need to have a date involved, I can use the column start_date and append the varchar time.
Note: start_date is in the date format
Example
select
to_date('08/27/2018','MM/DD/YYYY') as start_date
, '1300' as begin_time
from dual
What I ultimately need is just to be able to output the time as 1:00 PM
Any help would be appreciated. I'm beating my head against the wall on this... I'm used to using proprietary codes for periods of time and don't have a lot of experience with the true datetime formats.
Updates answering questions
Alex Poole, I make no claims that this system is the best... It's vendor-provided. :)
The BEGIN_TIME is always 4 characters
It does look like I was overthinking it quite a bit... Littlefoot may have nailed it on the head, but I unfortunately won't have a chance to test that until tomorrow.
Thank you all for the fast responses. I might have hair left when this request is over now :)
Final Thought
My lesson learned from this is simple: If you're dealing with time formats, don't throw out the idea of using a Date format function.

Looking for this?
SQL> with test (col) as
2 (select '1330' from dual)
3 select to_char(to_date(col, 'hh24mi'), 'hh:mi am') result
4 from test;
RESULT
--------
01:30 PM
SQL>
What does it do?
TO_DATE converts string you have (such as 1330) into a valid DATE value. By default, it'll be a date value truncated to the first of current month:
SQL> alter session set nls_Date_format = 'dd.mm.yyyy hh24:Mi';
Session altered.
SQL> select to_date('1330', 'hh24mi') res1 from dual;
RES1
----------------
01.04.2019 13:30
SQL>
applying TO_CHAR to it, again with the appropriate format mask, returns the desired result

Related

Varchar to Timestamp but varchar data is yyyy-mm-dd-hh:mi:ss:ff format

My source is from Oracle and the col1 is varchar2(26) but the value looks like YYYY-MM-DD-hh:mi:ss:ff (Sample rec: 2014-08-15-02.03.34.979946).
I have to extract only 6 months records based on COL1. Since there is a hypen between date part and time part - i could not consider as timestamp. Is there any idea how to have this as timestamp to lookup only 6 months data.
If it is possible at all, fix the data first. Storing timestamps in string data type is terrible. How do you know you don't have a time like 25:30:00 in the strings? Or a date like February 30? Besides, you can't really use an index on that column (so queries will be very slow), you will have to write a lot of code whenever referencing that column, etc.
Anyway - to deal with the immediate problem, use TO_TIMESTAMP(), exactly with the format model you show in your post - including the dash between the date part and the time part. Something like this:
select case when to_timestamp('2014-08-15-02.03.34.979946', 'YYYY-MM-DD-HH24:MI:SS.FF')
>= systimestamp - interval '6' month
then 'TRUE' else 'FALSE' end
as result
from dual;
RESULT
------
FALSE
EDIT: As Alex Poole points out (correctly as always) in a Comment below this Answer, interval arithmetic won't work correctly in all cases. It is better, than, to use something like
cast ( timestamp (...., format-model) as date ) <= add_months (sysdate, -6).
Maybe something like this will do:
select *
from your_table
where to_date(substr(col1,1,19),'yyyy-mm-dd-HH24.MI.SS') between add_months(sysdate,-6) and sysdate;
Assuming all the data format in col1 is always the same.
Also note that I used HH24 for hour segment, however could be not your case.
You can include the dash in your format model, as #mathguy showed, to convert your string to a timestamp:
select to_timestamp('2014-08-15-02.03.34.979946', 'YYYY-MM-DD-HH24:MI:SS.FF') from dual;
TO_TIMESTAMP('2014-08-15-02.
----------------------------
15-AUG-14 02.03.34.979946000
although unless you explicitly tell it not to be via the FX modifier, Oracle is flexible enough to allow a dash even if the model has a space (see the text below this table in the documentation:
select to_timestamp('2014-08-15-02.03.34.979946', 'YYYY-MM-DD HH24:MI:SS.FF') from dual;
TO_TIMESTAMP('2014-08-15-02.
----------------------------
15-AUG-14 02.03.34.979946000
However, converting all of the values in your col1 column and then comparing them may be a lot of work, and will prevent any index on that string column being used. Given the format, you can convert your date range to string instead, and use string comparison; e.g. to find everything in the six months up to midnight this morning:
select col1 -- or whichever columns you need
from your_table
where col1 >= to_char(cast(add_months(trunc(sysdate), -6) as timestamp), 'YYYY-MM-DD-HH24:MI:SS.FF6')
and col1 < to_char(cast(trunc(sysdate) as timestamp), 'YYYY-MM-DD-HH24:MI:SS.FF6');
or since the time part can be fixed for that example, you can use character literals instead of casting:
select col1 -- or whichever columns you need
from your_table
where col1 >= to_char(add_months(sysdate, -6), 'YYYY-MM-DD"-00:00:00.000000"')
and col1 < to_char(sysdate, 'YYYY-MM-DD"-00:00:00.000000"');
Of course, storing data in the correct native data type would be a much better solution. Any other solution only works at all if your string data actually contains what you think, and the data is all sane (or as sane as it can be in the wrong data type).

oracle date format issue

We use Oracle 10.2.0.4.0 database, oracle form builder and report builder for creating forms and reports.
Now the problem is in our production database nls_date_format is dd-mon-rr format. When developer create form in developer suit they give dd-mm-rr format at form level and when data stored in table that date format is dd-mm-rr.
Now when developer run form or report within form builder it gives dd-mm-rr format.but when same form or report run from application server side it gives junk characters in month.date and year print same as date format only month display in junk characters.
Hope you all guide well.
There are two issues.
when data stored in table that date format is dd-mm-rr.
This is completely wrong. Oracle doesn't store the date in the format you see, what you see is for display. Oracle stores DATE in an internal proprietary format in 7 bytes with each byte representing different elements of the DATE.
Byte Description
---- -------------------------------------------------
1 Century value but before storing it add 100 to it
2 Year and 100 is added to it before storing
3 Month
4 Day of the month
5 Hours but add 1 before storing it
6 Minutes but add 1 before storing it
7 Seconds but add 1 before storing it
Do not depend on the locale_specific NLS_DATE_FORMAT. Always use:
TO_CHAR to display the date in your desired format
TO_DATE to explicitly convert the string into date.
Remember, TO_DATE is NLS dependent.
If you only have a date element, and if you do not care about the time element, then better use ANSI Date literal which follows a fixed format 'YYYY-MM-DD'.
only month display in junk characters
This is again because you are depending on the NLS_DATE_LANGUAGE. As I said, you should avoid depending on the locale-specific client settings. Explicitly mention the NLS_DATE_LANGUAGE or use ANSI Date literal if you are not concerned about the time element.
For example,
SQL> SELECT TO_CHAR(SYSDATE, 'DD-MON-RR') dt FROM DUAL;
DT
---------
26-OCT-15
SQL> alter session set nls_date_language='french';
Session altered.
SQL> SELECT TO_CHAR(SYSDATE, 'DD-MON-RR') dt FROM DUAL;
DT
-----------
26-OCT. -15
So, what happened above? for a person using FRENCH nls_date_language, the MONTH is showing junk value. Let's make it NLS independent by explicitly mentioning the nls_date_language.
SQL> SELECT TO_CHAR(SYSDATE, 'DD-MON-RR', 'nls_date_language=english') dt FROM DUAL;
DT
---------
26-OCT-15
Also, the NLS_LANG value might not be correctly set in the OS environmental variable. See Why are junk values/special characters/question marks displayed on my client?

How to change default date,timestamp dataype for columns in oracle

I have created a table in Oracle in which I have KPI_START_DATE column which is a Date datatype, and KPI_START_TIME which is a TIMESTAMP datatype.
Now I want to modify this date dataype for
KPI_START_DATE to dd/mm/yyyy
and
KPI_START_TIME to HH:MI:SS.
So that user should always enter the date and time in this column in this proper format.
I tried below query but its was giving error:
Alter table KPI_DEFINITION MODIFY(to_char(KPI_START_DATE,'dd/mm/yyyy') )
DATE and TIMESTAMP columns do not have any inherent readable format. The values are stored in Oracle's own internal representation, which has no resemblance to a human-readable date or time. At the point to retrieve or display a value you can convert it to whatever format you want, with to_char().
Both DATE and TIMESTAMP have date and time components (to second precision with DATE, and with fractional seconds with TIMESTAMP; plus time zone information with the extended data types), and you should not try to store them separately as two columns. Have a single column and extract the information you need at any time; to get the information out of a single column but split into two fields you could do:
select to_char(KPI_START, 'dd/mm/yyyy') as KPI_START_DATE,
to_char(KPI_START, 'hh24:mi:ss') as KPI_START_TIME
but you'd generally want both together anyway:
select to_char(KPI_START, 'dd/mm/yyyy hh24:mi:ss')
Also notice the 'hh24' format model to get the 24-hour clock time; otherwise you wouldn't see any difference between 3 a.m. and 3 p.m.
You can store a value in either type of column with the time set to midnight, but it does still have a time component - it is just midnight. You can't store a value in either type of column with just a time component - it has to have a date too. You could make that a nominal date and just ignore it, but I've never seen a valid reason to do that - you're wasting storage in two columns, and making searching for and comparing values much harder. Oracle even provides a default date if you don't specify one (first day of current month). But the value always has both a date and a time part:
create table KPI_DEFINITION (KPI_START date);
insert into KPI_DEFINITION (KPI_START)
values (to_date('27/01/2015', 'DD/MM/YYYY'));
insert into KPI_DEFINITION (KPI_START)
values (to_date('12:41:57', 'HH24:MI:SS'));
select to_char(KPI_START, 'YYYY-MM-DD HH24:MI:SS') from KPI_DEFINITION;
TO_CHAR(KPI_START,'YYYY-MM-DDHH24:MI:SS')
-----------------------------------------
2015-01-27 00:00:00
2015-01-01 12:41:57
Your users should be inserting a single value with both date and time as one:
insert into KPI_DEFINITION (KPI_START)
values (to_date('27/01/2015 12:41:57', 'DD/MM/YYYY HH24:MI:SS'));
select to_char(KPI_START, 'YYYY-MM-DD HH24:MI:SS') from KPI_DEFINITION;
TO_CHAR(KPI_START,'YYYY-MM-DDHH24:MI:SS')
-----------------------------------------
2015-01-27 12:41:57
You can also use date or timestamp literals, and if using to_date() you should always specify the full format - don't rely on NLS settings as they may be different for other users.
You should understand difference between datatype and format. DATE is a datatype. TIMESTAMP is a datatype. None of them have formats, they're just numbers.
When converting character datatype to or from date datatype, format should be applied. It's an attribute of an actual conversion, nothing else.
Look at this:
SQL> create table tmp$date(d date);
Table created
SQL> insert into tmp$date values (DATE '2010-11-01');
1 row inserted
SQL> insert into tmp$date values (DATE '2014-12-28');
1 row inserted
SQL> select d, dump(d) from tmp$date;
D DUMP(D)
----------- ---------------------------------
01.11.2010 Typ=12 Len=7: 120,110,11,1,1,1,1
28.12.2014 Typ=12 Len=7: 120,114,12,28,1,1,1
There is no any 'format' here.
DISPLAYING and STORING are NOT the same when it comes to DATE.
When people say Oracle isn’t storing the date in the format they wanted, what is really happening is Oracle is not presenting the date in the character string format they expected or wanted.
When a data element of type DATE is selected, it must be converted from its internal, binary format, to a string of characters for human consumption. The conversion of data from one type to another is known as known a “conversion”, “type casting” or “coercion”. In Oracle the conversion between dates and character strings is controlled by the NLS_DATE_FORMAT model. The NLS_DATE_FORMAT can be set in any of several different locations, each with its own scope of influence.
I could go on with my leacture over DATE data type, but I am glad that someone has already got a good writeup over this. Please read this https://edstevensdba.wordpress.com/2011/04/07/nls_date_format/

Oracle date corruption during update

I'm migrating some data from one oracle schema/table to a new schema/table on the same database.
The migration script does the following:
create table newtable as select
...
cast(ACTIVITYDATE as date) as ACTIVITY_DATE,
...
FROM oldtable where ACTIVITYDATE > sysdate - 1000;
If I look at the original data, it looks fine - here's one record:
select
activitydate,
to_char(activitydate, 'MON DD,YYYY'),
to_char(activitydate, 'DD-MON-YYYY HH24:MI:SS'),
dump(activitydate),
length(activitydate)
from orginaltable where oldpk = 1067514
Result:
18-NOV-10 NOV 18,2010 18-NOV-2010 12:59:15 Typ=12 Len=7: 120,110,11,18,13,60,16
The migrated data, showing that the data is corrupt:
select
activity_date,
to_char(activity_date, 'MON DD,YYYY'),
to_char(activity_date, 'DD-MON-YYYY HH24:MI:SS'),
dump(activity_date),
length(activity_date)
from newtable
where id = 1067514
Result:
18-NOV-10 000 00,0000 00-000-0000 00:00:00 Typ=12 Len=7: 120,110,11,18,13,0,16
Around 5000 out of 350k records show this problem.
Can anyone explain how this happened?
UPDATE:
I don't find any published reference to this specific type of DATE corruption on the Oracle support site. (It may be there, my quick searches just didn't turn it up.)
Baddate Script To Check Database For Corrupt dates [ID 95402.1]
Bug 2790435 - Serial INSERT with parallel SELECT and type conversion can insert corrupt data [ID 2790435.8]
The output from the DUMP() function is showing the date value is indeed invalid:
Typ=12 Len=7: 120,110,11,18,13,0,16
We expect that the minutes byte should be a value between one and sixty, not zero.
The 7 bytes of a DATE value represent, in order, century(+100), year(+100), month, day, hour(+1), minutes(+1), seconds(+1).
The only time I have seen invalid DATE values like this when a DATE value was being supplied as a bind variable, from a Pro*C program (where the bind value is supplied in the internal 7 byte representation, entirely bypassing the normal validation routines that catch invalid dates e.g. Feb 30)
There is no reason to expect the behavior you're seeing, given the Oracle syntax you posted.
This is either a spurious anomaly (memory corruption?) or if this is repeatable, then it's a flaw (bug) in the Oracle code. If it's a flaw in the Oracle code, the most likely suspects would be "newish" features in an un-patched release.
(I know CAST is a standard SQL function that's been around for ages in other databases. I guess I'm old school, and have never introduced it into my Oracle-syntax repertoire. I don't know what version of Oracle it was that introduced the CAST, but I would have stayed away from it in the first release it appeared in.)
The big 'red flag' (that another commenter noted) is that CAST( datecol AS DATE).
You would expect the optimizer to treat that as equivalent to date_col ... but past experience shows us that TO_NUMBER( number_col ) is actually interpreted by the optimizer as TO_NUMBER( TO_CHAR ( number_col ) ).
I suspect something similar might be going on with that unneeded CAST.
Based on that one record you showed, I suspect the issue is with values with a "59" value for minutes or seconds, and possibly a "23" value for hours, would be the ones that show the error.
I would try checking for places where the minutes, hour or seconds are stored as 0:
SELECT id, DUMP(activitydate)
FROM newtable
WHERE DUMP(activitydate) LIKE '%,0,%'
OR DUMP(activitydate) LIKE '%,0'
I've seen similar things to spence7593, again with Pro*C.
It is possible to create invalid dates programmatically using a DBMS_STATS package.
Not sure if there is a similar mechanism to reverse that.
create or replace function stats_raw_to_date (p_in raw) return date is
v_date date;
v_char varchar2(25);
begin
dbms_stats.CONVERT_RAW_VALUE(p_in, v_date);
return v_date;
exception
when others then return null;
end;
/
select stats_raw_to_date(utl_raw.cast_to_raw(
chr(120)||chr(110)||chr(11)||chr(18)||chr(13)||chr(0)||chr(16)))
from dual;

Oracle last_ddl_time format

I have to query all_objects table where last_ddl_time='01 jan 2010' but it refuses the date format...
Any body give me the exact format to query?
As AKF said, you should be using Trunc unless you know the exact time the DDL was modified. Your query you added in the comments is looking for any objects where the DDL changed at 1/1/2010 00:00:00. Try:
SELECT *
FROM all_objects
WHERE trunc(last_ddl_time) = to_date('01-01-2010','dd-mm-yyyy');
I suggest you to use de date literal:
where trunc(last_ddl_time) = date '2010-01-01'
You can use the to_date function to format your date. If you enter a literal string, Oracle will attempt to convert that string using to_date with a default format 'DD-MON-YY', so your date would look like "01-JAN-10". As Oracle will be using this same function, you might want to put it in yourself and enjoy the finer granularity that custom formatting can provide.
It would be good to note that the dates stored in that column most likely have more precise dates, including hours and minutes, etc. Though you will be taking a bit of a performance hit, you might be better served using trunc(last_ddl_time) if you are testing with =.
There is some good info on Dates in Oracle at this link.
SELECT *
FROM all_objects t
WHERE trunc(t.last_ddl_time, 'DD') = to_date('2010-JAN-01', 'YYYY-MON-DD');

Resources