Skip to Main Content

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question.

Asked: June 28, 2000 - 3:41 pm UTC

Last updated: March 03, 2009 - 9:39 am UTC

Version: 8i (8.1.6)

Viewed 10K+ times! This question is

You Asked

I asked a question about date validation a few days ago...but the response did not work for some reason. Here goes again (I know it's simple, but it's killing me right now)
I am accepting 3 values from a form on a web page - day(1), month('JAN') and year(2000) - and I need to convert them to a date, and check them against the server's date, to ensure that the date supplied is the current date (minus 2 hours, as I need to use pacific coast time)
If the date is current, the information will be processed, if not, the form will be re-posted with the information asking the user to choose the current date.
I have simplified my procedures as much as possible for debugging purposes, but they still give me the following error message. It seems REALLY basic, but it won't go for reasons that I do not understand. (I've included the procedures)

ERROR at line 1:
ORA-01898: too many precision specifiers
ORA-06512: at "DOGFISH.CHECK_DATE_FUNC", line 8
ORA-06512: at "DOGFISH.TEST_PROC", line 5
ORA-06512: at line 1

create or replace FUNCTION check_date_func
(in_date in date)
RETURN BOOLEAN
IS
l_date date;
l_today boolean;
BEGIN
l_date := trunc(in_date,'DD-MON-YY');
IF trunc(new_time(SYSDATE,'CDT','PDT'),'DD-MON-YY') = l_date THEN
l_today := true;
ELSE
l_today := false;
END IF;
return l_today;
END check_date_func;
/

create or replace procedure test_proc
IS
BEGIN
if check_date_func(sysdate) then
htp.prn('check_date_func = true');
else
htp.prn('false');
end if;
END test_proc;
/

Please, I am a very frustrated beginnner who cannot find help on any of the newsgroups. Any detailed help (code always welcome) would be greatly apppreciated!
THANKS!

and Tom said...

It is in your use of TRUNC. You are giving TRUNC a format mask -- one that you might use on a date with the to_char() function (eg: given a date and the format mask dd-mon-yy, it will format the 7 byte binary date into that character string). Or you might use it on a STRING with the to_date() function, eg:

to_date( '01-JAN-00', 'DD-MON-YY' );

That will convert the first string into a 7 byte binary date using the date format mask dd-mon-yy.


Your check date function (you are just trying to truncate off the TIME component of the date) can be written simply as:


ops$tkyte@8i> create or replace
2 FUNCTION check_date_func(in_date in date) RETURN BOOLEAN
3 IS
4 BEGIN
5 return trunc(in_date,'dy') =
6 trunc(new_time(sysdate,'cdt','pdt'),'dy');
7 end;
8 /

Function created.


You just want to TRUNC the dates using 'dy' (which is the default actually -- to trunc to the DAY). You can trunc dates to the hour, minute, day, week, month, year and so on. We return the boolean comparision of the input date with the converted sysdate...

ops$tkyte@8i> BEGIN
2 if check_date_func(sysdate) then
3 htp.p('check_date_func = true');
4 else
5 htp.p('false');
6 end if;
7 END test_proc;
8 /

PL/SQL procedure successfully completed.

ops$tkyte@8i>
ops$tkyte@8i> set serveroutput on
ops$tkyte@8i> exec owa_util.showpage
check_date_func = true

PL/SQL procedure successfully completed.

That shows that it works when we send in SYSDATE (careful -- if its close to midnight this will not work, it will be FALSE!)


ops$tkyte@8i> BEGIN
2 if check_date_func(sysdate+1) then
3 htp.p('check_date_func = true');
4 else
5 htp.p('false');
6 end if;
7 END test_proc;
8 /

PL/SQL procedure successfully completed.

ops$tkyte@8i> set serveroutput on
ops$tkyte@8i> exec owa_util.showpage
false

PL/SQL procedure successfully completed.

and that shows if we send in tomorrow -- it returns false (same caveat about midnight and timezones!)

Rating

  (15 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

Date and time difference

Jamil, December 03, 2003 - 6:19 am UTC

Dear Sir 
 In this example I am calculating the consume time for the cars journey, my table structure as follow:

SQL> descr travel_table 
 Name                            Null?    Type
 ------------------------------- -------- ----
 J_START_DATE                            DATE
 J_START_TIME                            CHAR(5)
 J_START_TIME_STATUS                     NUMBER(1)
 J_END_DATE                              DATE
 J_END_TIME                              CHAR(5)
 J_END_TIME_STATUS                       NUMBER(1)
 J_CONS_DAY                              CHAR(5)
 J_CONS_HOUR                             CHAR(5)
 J_CONS_MIN                              CHAR(5)


In my form  I am using this code for calculating 
the consume days and consume hours and the consume minutes 

the code for the bush button as follow 
--------------------------------
when-button-pressed 
DECLARE
  ST_DATE VARCHAR2(10);
  ST_TIME VARCHAR2(5);
  ST_TIME_STATUS VARCHAR2(2);
  EN_DATE VARCHAR2(10);
  EN_TIME VARCHAR2(5);
  EN_TIME_STATUS VARCHAR2(2);
  DATA_RET VARCHAR2(50);
  CON_L    NUMBER(4) := 0;
  
BEGIN
    
    CHECK_DATE_RANGE(:J_START_DATE,:J_END_DATE);
  ST_DATE := TO_CHAR(:J_START_DATE,'MM-DD-YYYY');
  ST_TIME := RTRIM(LTRIM(:J_START_TIME));

  IF    :J_START_TIME_STATUS  = 1 THEN
              ST_TIME_STATUS :='AM';
  ELSIF :J_START_TIME_STATUS  = 2 THEN
              ST_TIME_STATUS :='PM';
  END IF;

  EN_DATE :=to_char(:J_END_DATE,'MM-DD-YYYY');
  EN_TIME := RTRIM(LTRIM(:J_END_TIME));

  IF    :J_END_TIME_STATUS  = 1 THEN
            EN_TIME_STATUS :='AM';
  ELSIF :J_END_TIME_STATUS  = 2 THEN
             EN_TIME_STATUS :='PM';
  END IF;

  BEGIN
    DATA_RET :=travel_time(ST_DATE,ST_TIME,ST_TIME_STATUS,
                           EN_DATE,EN_TIME,EN_TIME_STATUS);
    MESSAGE('Time Calculated='||DATA_RET);

   
    :J_CONS_DAY  := SUBSTR(DATA_RET,2,INSTR(DATA_RET,' ')-1);
         CON_L :=INSTR(DATA_RET,'H');
         CON_L := CON_L + 1; 
              MESSAGE('CON_L ='||'  ' ||CON_L);

    :J_CONS_HOUR := SUBSTR(DATA_RET,CON_L,INSTR(DATA_RET,' ')-1);
         CON_L :=INSTR(DATA_RET,'M');
         CON_L := CON_L + 1; 

    :J_CONS_MIN  := SUBSTR(DATA_RET,CON_L,INSTR(DATA_RET,' ')-1);
    COMMIT;
  END;
END;
---------------------------------------
And the code for the function travel_time as follow:
---------------------------------------
FUNCTION travel_time (sd varchar2,st varchar2,s_ampm varchar2,
                      ed varchar2,et varchar2,e_ampm varchar2)
  RETURN varchar2
IS
  s_dt date;
  e_dt date;
  rtrn varchar2(50);
  ET1 NUMBER; -- Elapsed time
Begin
  begin
    s_dt := to_date(sd || st || s_ampm,'MM-DD-YYYYHH:MIAM');
    exception when others then
    MESSAGE('Invalid start date/time: '||sd||st||s_ampm);
    rtrn := 'Invalid start date/time: '||sd||st||s_ampm;
  end ;

  if rtrn is null then
    begin
      e_dt := to_date(ed || et || e_ampm,'MM-DD-YYYYHH:MIAM');
    exception when others then
      MESSAGE('Invalid end date/time: '||ed||et||e_ampm);
      rtrn := 'Invalid end date/time: '||ed||et||e_ampm;
    end;
  end if;
  if rtrn is null then
    ET1 := e_dt - s_dt;
    rtrn := 'D'||Trunc(ET1)||' '
          ||'H'||Mod(Trunc(ET1*24),24)||' '
          ||'M'||Mod(Round(ET1*1440),60);
  end if;
  return rtrn;
End travel_time;
-----------------------------------------
So my problem is when I run this form I am getting this message when I press the bush button

1) Invalid start date/time : 12-03-200310:30AM

2) FRM-40735:WHEN-BUTTON-PRESSED trigger raised unhandled exception  ORA-06502 

Can you please tell me what is wrong with the above code.



Notice :

The above code works fine if I change the 
NLS_LANG  from ARABIC_UNITED ARAB EMIRATES.AR8MSWIN1256 
To 
AMERICAN_AMERICA.AR8MSWIN1256
I have make this changed for NLS_LANG in the registry editor,But I required to use the NLS_LANG as 
ARABIC_UNITED ARAB EMIRATES.AR8MSWIN1256 
Because my form layout is Arabic not English 

Waiting for your valuable answer.

Best Regards

Jamil




  
 

Tom Kyte
December 03, 2003 - 7:14 am UTC

</code> http://asktom.oracle.com/Misc/DateDiff.html <code>

won't be NLS specific at all

Date difference

Jamil Shaibani, December 04, 2003 - 12:30 am UTC

Dear sir
Would you please tell me what is wrong with my code,because the user must enter the data in the form using these fields

J_START_DATE DATE
J_START_TIME CHAR(5)
J_START_TIME_STATUS NUMBER(1)
J_END_DATE DATE
J_END_TIME CHAR(5)
J_END_TIME_STATUS NUMBER(1)

And also the time format it should be in this format HH12:MI not as HH24:MI hours

If you have time to correct my code , it will be very help full to me, because this code is working fine except that as I mention before it is working when I change the NLS_LANG to English.

Thank you very much for your replay
waiting for your valuable replay.

Best regards

Jamil



Tom Kyte
December 04, 2003 - 7:48 am UTC

i see two dates there

start date
end date

use my code, no time to debug others ;) you have everything you need.

and remember -- formats are formats -- not data. DATES are stored in 7 bytes, in binary. formats are used to translate strings on the way in and format them on the way out of the database. they have no bearing on HOW the data is actually stored. they are purely for your viewing pleasure. so you can in fact format the dates however you like.

don't work with dates as strings (as you are). work with DATES.

excellent

&amp;#1593;&amp;#1575; &amp;#1588;&amp;#1583;, December 04, 2003 - 9:46 am UTC

&#1705;&#1740;&#1575;

Date validation using sql

A reader, March 19, 2004 - 11:22 am UTC

date validation:
----------------

I have two dates given to my procedure p.

Now I should validate them and see that the two dates do no span over one month i.e.

Dates like 01/01/2003 , 02/02/2003 are not valid. If the first date is 01/01/2003 then the second date
at the max can only be 01/31/2003 i.e. if should fall within the month of the first date.

I'm pondering how to do this validation using sql, and avoid writing a procedure to do the same.

can you help.


Tom Kyte
March 19, 2004 - 1:34 pm UTC

"when abs( months_between(a,b) ) <= 1"

ops$tkyte@ORA9IR2> select abs(months_between('01-jan-2003','02-feb-2003')) from dual
  2  /
 
ABS(MONTHS_BETWEEN('01-JAN-2003','02-FEB-2003'))
------------------------------------------------
                                      1.03225806
 
ops$tkyte@ORA9IR2> select abs(months_between('01-jan-2003','31-jan-2003')) from dual
  2  /
 
ABS(MONTHS_BETWEEN('01-JAN-2003','31-JAN-2003'))
------------------------------------------------
                                      .967741935
 
ops$tkyte@ORA9IR2> select abs(months_between('15-jan-2003','15-feb-2003')) from dual
  2  /
 
ABS(MONTHS_BETWEEN('15-JAN-2003','15-FEB-2003'))
------------------------------------------------
                                               1
 
 

doesnt work for me, but good solution

A reader, March 19, 2004 - 1:56 pm UTC

Tom

date validation:
----------------

I have two dates given to my procedure p.

Now I should validate them and see that the two dates do no span over one month
i.e.

Dates like 01/01/2003 , 02/02/2003 are not valid. If the first date is
01/01/2003 then the second date
at the max can only be 01/31/2003 i.e. if should fall within the month of the
first date.

I'm pondering how to do this validation using sql, and avoid writing a procedure
to do the same.

can you help.



Followup:
"when abs( months_between(a,b) ) <= 1"

ops$tkyte@ORA9IR2> select abs(months_between('01-jan-2003','02-feb-2003')) from
dual
2 /

ABS(MONTHS_BETWEEN('01-JAN-2003','02-FEB-2003'))
------------------------------------------------
1.03225806

ops$tkyte@ORA9IR2> select abs(months_between('01-jan-2003','31-jan-2003')) from
dual
2 /

ABS(MONTHS_BETWEEN('01-JAN-2003','31-JAN-2003'))
------------------------------------------------
.967741935

ops$tkyte@ORA9IR2> select abs(months_between('15-jan-2003','15-feb-2003')) from
dual
2 /

ABS(MONTHS_BETWEEN('15-JAN-2003','15-FEB-2003'))
------------------------------------------------
1



This will not work for me, I cant have the dates spanning across months, the dates should be within one month.

The whole point behind this question was to force the users enter begin and end dates which fall within a month, any month but within that month. If they enter 01/01/2003 as the begin date , then they should enter a date which falls in jan for the end date.

I should capture what they have entered and let them know if their dates span accross more than 1 month i.e. jan in the give example.

Tom Kyte
March 19, 2004 - 2:20 pm UTC

then it is even easier?

where trunc(d1,'mm') = trunc(d2,'mm')




Converting numbers to date format

Arya, July 07, 2005 - 11:20 am UTC

Hi Tom,
I am loading data from source table in which dates have been stored as numbers and I have no control over the source tables.

When I looked at the data there are many records with invalid dates here is the sample data I have provided.

Column Name Null? Data Type
COL1 Y NUMBER
COL2 Y NUMBER
COL3 Y NUMBER
CREATE_DTS Y NUMBER
UPDATE_DTS Y NUMBER


COL1 COL2 COL3 CREATE_DTS UPDATE_DTS

1 10 100 2000366
2 20 200 2004366
3 30 300 2005365
4 40 400 2005190
5 50 500 2005366
6 60 600 2005190
7 70 700 2005365
8 80 800 2005366
9 90 900 2005367

I need to select records which have valid dates. so I tried the following sql.

select col1,to_date(create_dts,'yyyyddd')
from test_source
where substr(create_dts,5,3) <=366

But with the above query 1 and 2 records are ignored even though they are valid.

I appreciate your help to get all the records which are valid for ex.

1 10 100 2000366
2 20 200 2004366
3 30 300 2005365
4 40 400 2005190
6 60 600 2005190
7 70 700 2005365

Thanks
Arya

Tom Kyte
July 07, 2005 - 1:44 pm UTC

ops$tkyte@ORA9IR2> create table t ( x number );
 
Table created.
 
ops$tkyte@ORA9IR2> insert into t values ( 2000366 );
 
1 row created.
 

ops$tkyte@ORA9IR2> select * from t where substr(x,5,3) <= 366;
 
         X
----------
   2000366
 
ops$tkyte@ORA9IR2>


you'll have to give me an example - create tables, insert intos etc etc


(does not it scare you to compare a string to a number?  and does the where clause HAVE to take place before the SELECT, best to use case

select case when mod(x,1000) <= 366 then to_date(......) end, ...
  from t
 where mod(x,1000) <= 366;

 

Converting to Dates

Arya, July 11, 2005 - 11:05 am UTC

Hi Tom,

Here is the table test_source

COL1 COL2 COL3 CREATE_DTS UPDATE_DTS

1 10 100 2000366
2 20 200 2004366
3 30 300 2005365
4 40 400 2005190
5 50 500 2005366
6 60 600 2005190
7 70 700 2005365
8 80 800 2005366
9 90 900 2005367

Select *
from test_source
Where substr(create_dts,5,3) <= 366;
Returns 8 records except the date with 367 days which is invalid.

1 10 100 2000366
2 20 200 2004366
3 30 300 2005365
4 40 400 2005190
5 50 500 2005366
6 60 600 2005190
7 70 700 2005365
8 80 800 2005366

To insert into another table test_dest same as test_source except dates are dates not numbers.
test_dest
col1 number
col2 number
col3 number
create_dts date
update_dts date

insert into test_dest
select col1,col2,col3,To_Date(create_dts,'YYYYDDD'),to_date(update_dts,'YYYYDDD')
from test_source
where substr(create_dts,5,3) <= 366

I'm getting ora-1848 error

So I did the following to avoid this error looks like it working fine.

insert into test_dest
SELECT col1, col2, col3, to_date(create_dts,'YYYYDDD'), to_date(update_dts,'YYYYDDD')
FROM test_source
WHERE TO_NUMBER (SUBSTR (create_dts, 5)) <= (TO_DATE (SUBSTR (create_dts, 1, 4), 'YYYY') - TO_DATE (TO_CHAR (TO_NUMBER (SUBSTR (create_dts, 1, 4) - 1)),'YYYY'))

Thanks
Arya



Tom Kyte
July 11, 2005 - 11:45 am UTC

hmmm....

(does not it scare you to compare a string to a number? and does the where
clause HAVE to take place before the SELECT, best to use case

select case when mod(x,1000) <= 366 then to_date(......) end, ...
from t
where mod(x,1000) <= 366;

..... just repeating myself.

Converting to dates

Arya, July 11, 2005 - 3:25 pm UTC

Hi Tom,

I have to insert into test_dest table and create_dts and update_dts columns in this table are in date datatype so when I convert create_dts of test_source table to date i'm getting ora-1848 error because of record no 8.

select COL1, col2,col3, case when mod(create_dts,1000) <= 366 then to_date(CREATE_DTS,'YYYYDDD') end AS A
from test_source
where mod(create_dts,1000) <= 366;

if i try the following query it works fine but retreives wrong data for ex. record no 8 which is not right 2005366( it is not a leap year so it cannot have 366, as I told you earlier that we have no control of the data coming in)

select COL1, col2,col3, case when mod(create_dts,1000) <= 366 then CREATE_DTS end AS A
from test_source
where mod(create_dts,1000) <= 366;

COL1 COL2 COL3 create_dts
1 10 100 2000366
2 20 200 2004366
3 30 300 2005365
4 40 400 2005190
5 50 500 2005366
6 60 600 2005190
7 70 700 2005365
8 80 800 2005366


Thanks
Arya

Tom Kyte
July 11, 2005 - 3:52 pm UTC

then you need quite simply a more sophisticated case statement, you can use and/ors in there.

Converting to Dates

Arya, July 12, 2005 - 4:33 pm UTC

Hi Tom,

Do you see any problems with the way I have written conversions Could you please explain me with an example where it can go wrong. It is working fine as of now.

Your input is highly appreciated.

Thanks
Arya

Tom Kyte
July 13, 2005 - 10:53 am UTC

it is up to you, i didn't thoroughly debug you code, no.

I prefer to use the case statement, because you cannot control when oracle will call to_date, there is nothing say we cannot to_date the field BEFORE the predicate is applied. the case statement is "safer"

Data Validation

Thirumaran, November 23, 2005 - 4:42 am UTC

Hi Tom,

I am working on moving data from one oracle DB to another oracle DB using Sql*loader.

I am thinking about the validation mechansim. The big question "How will i verify that the data migrated from As Is to NEW DB is 100% accurate?".
What mechanism should i adopt to validate the data migration.

A small example will be greater help to me.

Thanks
Thirumaran


Tom Kyte
November 23, 2005 - 9:49 am UTC

why not just use a dblink and know the data got moved?

what does 100% accurate mean to you?

same number of rows?
same data byte for byte (think about character set translations....)

Date Format

Ramu, December 29, 2006 - 3:44 am UTC

Hi Tom
Is there any better way to validate my data before inserting into a table which has column with date as a datatype¿The source data has a character filed and we don¿t know the format how it comes(ex mm/yyyy,dd/mm/yy etc..) . Currently I am programmatically added some date formats but I am facing a problem when the format other than mentioned in the program comes. Also I need to check before itself the source data has a valid date format or not. Could you please help us how to handle this situation if possible with an example.

Thanks In Advance

Ramu
Tom Kyte
December 29, 2006 - 9:48 am UTC

insufficient data to answer.

the only answer I could think to give given this input, would be the original answer above to the original question.

time diff

ram, April 05, 2007 - 2:37 pm UTC

I have situation where i need find the time diff in two dates

I need your help how do we calculate the diff between sydate time - create_date

for ex : 1) sysdate -> 04/12/2006 12:05:36 PM

create_date -> 04/12/2006 11:48:36 PM

it should display as 0 hrs 17 min

2) sysdate -> 04/12/2006 12:45:36 PM

create_date -> 04/12/2006 9:44:36 PM

it should display as 2 hrs 59 min

can you please help me


Tom Kyte
April 05, 2007 - 3:38 pm UTC

  1* select username, systimestamp - cast( created as timestamp ) diff  from all_users
ops$tkyte%ORA10GR2> /

USERNAME                       DIFF
------------------------------ ---------------------------
PERFSTAT                       +000000018 23:14:26.607458
U2                             +000000105 07:57:32.607458
OPS$TKYTE                      +000000001 18:54:32.607458
....

Job gets aborted due to invalid data in the table

A reader, April 25, 2008 - 12:11 am UTC

Tom:

We have a datastage job (Query) which gets aborted after fetching few million records from the table due to data issue (either because of bad/invalid data in the columns).

Is there any way to find out which particular record(s) or the column in a table having the invalid data.

We do have some date columns used in the query, which we would like to further check. Appreciate your suggestions and inputs.


Thanks
Tom Kyte
April 28, 2008 - 12:33 pm UTC

no clue what capabilities 'datastage' might or might not have - this would be a question for datastage.


If you have access to the query and can run it in something we have control over - you can remove all of the implicit/explicit datatype conversions and we can then certainly scan the data...

date validation

A reader, February 26, 2009 - 9:48 pm UTC


Sql Comaprision of dates

Himansu, February 27, 2009 - 3:42 am UTC

the code was quite resourceful but the requirment is to compare two dates....if the date is validated it shows a voilation in the sql database
Tom Kyte
March 03, 2009 - 9:39 am UTC

umm, the question was about "why am I getting this error", which I corrected.

The already had their logic in place. And well, I redid their logic more efficiently:

ops$tkyte@8i> create or replace
  2  FUNCTION check_date_func(in_date in date) RETURN BOOLEAN
  3  IS
  4  BEGIN
  5      return trunc(in_date,'dy') =
  6                trunc(new_time(sysdate,'cdt','pdt'),'dy');
  7  end;
  8  /



so, what exactly what missing in your mind? code fixed, logic implemented, routine demonstrated.

More to Explore

PL/SQL demos

Check out more PL/SQL tutorials on our LiveSQL tool.

PL/SQL docs

PL/SQL reference manual from the Oracle documentation library