Skip to Main Content
  • Questions
  • Datetime.MaxValue timestamp ORA-01841

Breadcrumb

Question and Answer

Chris Saxon

Thanks for the question, Mateusz.

Asked: December 13, 2024 - 10:35 am UTC

Last updated: December 16, 2024 - 6:36 pm UTC

Version: Oracle EE 19.25

Viewed 100+ times

You Asked

Hello,

I've been struggling with "strange" behavior with handling "extreme " timestamp values.

Our applications in .NET execute stored procedures with timestamp parameter and in some cases pass "31/12/9999 23:59:59.9999999" - it is value of Datetime.MaxValue function

After upgrade to 19.25 we get "ORA-01841: (full) year must be between -4713 and +9999, and not be 0" for this value.

Lets look on my examples

Following statement returns same value for oracle 19.25 and < 19.25 versions
select to_timestamp('12/31/9999 23:59:59.9999999','MM/DD/YYYY HH24:MI:SS.FF') from dual;


31-DEC-99 11.59.59.999999900 PM

Lets create simple procedure
CREATE OR REPLACE PROCEDURE testmh( p_data in timestamp )
IS  
  vdata timestamp(9);
BEGIN  
  dbms_output.put_line('a');
END; 


and execute like
execute testmh(to_timestamp('12/31/9999 23:59:59.9999999','MM/DD/YYYY HH24:MI:SS.FF'));


on 19.25 I get
ORA-01841: (full) year must be between -4713 and +9999, and not be 0

on < 19.25
PL/SQL procedure successfully completed.

Another test - create the function
CREATE OR REPLACE FUNCTION testmhf (
     p_data TIMESTAMP )
RETURN TIMESTAMP
IS
BEGIN
    RETURN p_data;
END testmhf;


and execute
select testmhf(to_timestamp('12/31/9999 23:59:59.9999999','MM/DD/YYYY HH24:MI:SS.FF')) from dual;


for <19.25 result is
01-JAN-00 12.00.00.000000000 AM

for 19.25 result is
ORA-01841: (full) year must be between -4713 and +9999, and not be 0

Workaround for this problem is create the subtype and use that as type for procedure parameter

CREATE OR REPLACE PACKAGE XY
AS
  SUBTYPE timestamp_t IS TIMESTAMP(7);
END;
/


CREATE OR REPLACE PROCEDURE testmh_t( p_data in XY.timestamp_t )
IS
BEGIN  
  insert into zone(zone) values(p_data);
END; 
/
execute testmh_t(to_timestamp('12/31/9999 23:59:59.9999999','MM/DD/YYYY HH24:MI:SS.FF'));


Do you know any other workaround or why passing this "extreme" value is causing that this value is rounding up?

M.

and Chris said...

I'm not sure how this worked on earlier patches of 19c. The behaviour (albeit with a different error message) was present in 12.2:

select banner from v$version;

BANNER
--------------------------------------------------------------------------------
Oracle Database 12c EE High Perf Release 12.2.0.1.0 - 64bit Production
PL/SQL Release 12.2.0.1.0 - Production
CORE 12.2.0.1.0 Production  
TNS for Linux: Version 12.2.0.1.0 - Production
NLSRTL Version 12.2.0.1.0 - Production

CREATE OR REPLACE PROCEDURE testmh( p_data in timestamp )
IS  
BEGIN  
  dbms_output.put_line('a' || p_data );
END; 
/
execute testmh(to_timestamp('12/31/9998 23:59:59.9999999','MM/DD/YYYY HH24:MI:SS.FF'));
a01/01/9999 00:00:00.000000

execute testmh(to_timestamp('12/31/9999 23:59:59.9999999','MM/DD/YYYY HH24:MI:SS.FF'));
ORA-01877: string is too long for internal buffer


I see similar on 21c and 23ai too. This is because the parameter p_data is a timestamp(6)

select argument_name, data_type, data_precision 
from   user_arguments
where  object_name = 'TESTMH';

ARGUMENT_NAME    DATA_TYPE    DATA_PRECISION    
P_DATA           TIMESTAMP                    6


Your timestamp has seven fractional digits, so gets rounded up when converted to six.

select cast ( 
  to_timestamp('12/31/9998 23:59:59.9999999','MM/DD/YYYY HH24:MI:SS.FF')
  as timestamp(6)
) from dual;

CAST(TO_TIMESTAMP('12/31/999823:59:59.9999999','MM/DD/YYYYHH24:MI:SS.FF')ASTIMESTAMP(6))    
01/01/9999 00:00:00.000000000  


To overcome this you need to make the argument a timestamp(7) or more. A subtype is one way, %type anchoring it to a column with enough precision is another:

create table t ( c1 timestamp(7) );

CREATE OR REPLACE PROCEDURE testmh( p_data in t.c1%type )
IS  
BEGIN  
  dbms_output.put_line('a' || p_data );
END; 
/
execute testmh(to_timestamp('12/31/9998 23:59:59.9999999','MM/DD/YYYY HH24:MI:SS.FF'));
a31/12/9998 23:59:59.9999999

execute testmh(to_timestamp('12/31/9999 23:59:59.9999999','MM/DD/YYYY HH24:MI:SS.FF'));
a31/12/9999 23:59:59.9999999

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