ORA-01870 trying to convert from date into timestamp with time zone - oracle

when the following PL/SQL anonymous block is run using SQL DEVELOPER on Oracle DB 11g
SET SERVEROUTPUT ON;
DECLARE
d_next_run_date TIMESTAMP WITH TIME ZONE;
BEGIN
dbms_scheduler.evaluate_calendar_string(
calendar_string => 'FREQ=DAILY;INTERVAL=15',
start_date => TO_TIMESTAMP_TZ('04/06/2021', 'DD/MM/YYYY'),
return_date_after => TO_TIMESTAMP_TZ('01/01/2022', 'DD/MM/YYYY'),
next_run_date => d_next_run_date
);
dbms_output.put_line(d_next_run_date);
END;
the following exception is thrown
ORA-01870: the intervals or datetimes are not mutually comparable
ORA-06512: at "SYS.DBMS_SCHEDULER", line 3599
ORA-06512: at line 4
01870. 00000 - "the intervals or datetimes are not mutually comparable"
*Cause: The intervals or datetimes are not mutually comparable.
*Action: Specify a pair of intervals or datetimes that are mutually
comparable.
I suspect the issue is related to timezone.
The exception is not thrown if the arguments are changed to the following (notice that I changed the value that is passed into parameter return_date_after)
SET SERVEROUTPUT ON;
DECLARE
d_next_run_date TIMESTAMP WITH TIME ZONE;
BEGIN
dbms_scheduler.evaluate_calendar_string(
calendar_string => 'FREQ=DAILY;INTERVAL=15',
start_date => TO_TIMESTAMP_TZ('04/06/2021', 'DD/MM/YYYY'),
return_date_after => TO_TIMESTAMP_TZ('01/01/2021', 'DD/MM/YYYY'),
next_run_date => d_next_run_date
);
dbms_output.put_line(d_next_run_date);
END;
or if the arguments are changed to the following (again I changed the value that is passed into parameter return_date_after)
SET SERVEROUTPUT ON;
DECLARE
d_next_run_date TIMESTAMP WITH TIME ZONE;
BEGIN
dbms_scheduler.evaluate_calendar_string(
calendar_string => 'FREQ=DAILY;INTERVAL=15',
start_date => TO_TIMESTAMP_TZ('04/06/2021', 'DD/MM/YYYY'),
return_date_after => TO_TIMESTAMP_TZ('01/06/2022', 'DD/MM/YYYY'),
next_run_date => d_next_run_date
);
dbms_output.put_line(d_next_run_date);
END;
The requirements that I have to fullfill need not support timezone. If possible, I would like to use datatype DATE with procedure dbms_scheduler.evaluate_calendar_string. However, dbms_scheduler.evaluate_calendar_string parameters are of type TIMESTAMP WITH TIMEZONE. How could I convert from DATE into TIMESTAMP WITH TIMEZONE and avoid that exception?
I noticed that the exception is thrown when a date value that corresponds to summer is passed into parameter return_date_after. So the error might be related to daylight saving time.
database version: 11.2.0.1.0
nls parameters

I managed to avoid that error by adding time and timezone when casting the characters into TIMESTAMP WITH TIMEZONE
SET SERVEROUTPUT ON;
DECLARE
d_next_run_date TIMESTAMP WITH TIME ZONE;
BEGIN
dbms_scheduler.evaluate_calendar_string(
calendar_string => 'FREQ=DAILY;INTERVAL=15',
start_date => TO_TIMESTAMP_TZ('04/06/2021 00:00:00 00:00', 'DD/MM/YYYY HH24:MI:SS TZH:TZM'),
return_date_after => TO_TIMESTAMP_TZ('01/01/2022 00:00:00 00:00', 'DD/MM/YYYY HH24:MI:SS TZH:TZM'),
next_run_date => d_next_run_date
);
dbms_output.put_line(d_next_run_date);
END;
/
output
15/01/22 00:00:00,000000 +00:00

Related

cannot get year-month interval

I am learning PL-SQL and the exercise is to find the year-month interval difference between two dates. I wrote the following code:
DECLARE
t1 TIMESTAMP (2) WITH TIME ZONE := to_timestamp_tz('2019-01-21 21:05:53.46 +02:00',
'YYYY-MM-DD HH24:MI:SS.FF TZH:TZM');
t3 TIMESTAMP WITH TIME ZONE := to_timestamp_tz('2020-01-21 21:05:53.46 +02:00',
'YYYY-MM-DD HH24:MI:SS.FF TZH:TZM');
ym INTERVAL YEAR(2) to MONTH;
BEGIN
-- ym := '10-2';
ym := t3-t1;
DBMS_OUTPUT.PUT_LINE(ym);
END;
I would expect the ym variable to give '01-0' (1 year difference), but I get an error:
Error report -
ORA-06550: line 9, column 9:
PLS-00382: expression is of wrong type
ORA-06550: line 9, column 3:
PL/SQL: Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
I am really confused why this is happening, I tried changing the precision of the YEAR(_), but that doesn't help.
If ym is of datatype INTERVAL DAY(2) to SECOND(2), I get correct result. If I replace ym to ym := '10-2'; it also works. But with ym INTERVAL YEAR(2) to MONTH it is not working :(
I found in the psoug.org examples that you could use this syntax:
ym := (t3-t1) year to month;
dbfiddle demo
t3-t1 does not work as the resulting value is of the INTERVAL DAY TO SECOND data type and there is no implicit cast to INTERVAL YEAR TO MONTH.
Instead, you can use NUMTOYMINTERVAL( MONTHS_BETWEEN( t3, t1 ), 'MONTH' ); to dynamically create the correct data type:
DECLARE
t1 TIMESTAMP (2) WITH TIME ZONE := to_timestamp_tz('2019-01-21 21:05:53.46 +02:00',
'YYYY-MM-DD HH24:MI:SS.FF TZH:TZM');
t3 TIMESTAMP WITH TIME ZONE := to_timestamp_tz('2020-01-21 21:05:53.46 +02:00',
'YYYY-MM-DD HH24:MI:SS.FF TZH:TZM');
ym INTERVAL YEAR(2) to MONTH;
BEGIN
ym := NUMTOYMINTERVAL( MONTHS_BETWEEN( t3, t1 ), 'MONTH' );
DBMS_OUTPUT.PUT_LINE(ym);
END;
/
outputs:
+01-00
db<>fiddle here

How to get the current date time in plsql?

My Code:
DECLARE
BEGIN
-- outputs 06-DEC-18
dbms_output.put_line(sysdate);
-- also outputs 18-DEC-06
dbms_output.put_line(to_date(sysdate,'yyyy-mm-dd hh24:mi:ss'));
END;
/
The output only shows the date. I want to get also the time.
SYSDATE does have a time component. The first one outputs 06-DEC-18 because your session's parameter NLS_DATE_FORMAT is set to DD-MON-YY.
You have these options.
use TO_CHAR
dbms_output.put_line(TO_CHAR(SYSDATE,'yyyy-mm-dd hh24:mi:ss'));
Modify NLS_DATE_FORMAT to show time.
ALTER SESSION SET NLS_DATE_FORMAT='yyyy-mm-dd hh24:mi:ss';
BEGIN
dbms_output.put_line(SYSDATE);
END;
/
Or use SYSTIMESTAMP to show higher precision of time like milliseconds.
BEGIN
dbms_output.put_line(SYSTIMESTAMP);
END;
/
You can use SYSTIMESTAMP
SELECT SYSTIMESTAMP
FROM DUAL
To convert date type to string with some format use to_char
to_char(sysdate::timestamp, 'YYYY-MM-DD HH24:MI:SS')

Oracle Datetime difference issue with AM/ PM

I have the following stored procedure which calculates the time taken for a merge statement
create or replace procedure ModAuditData(
O_UpdatedCount out int
,O_EndTime out timestamp
,O_Duration out int)
as
P_StartTime timestamp(3) WITH LOCAL TIME ZONE;
-- EndTime timestamp;
begin
P_StartTime:=to_timestamp(to_char(current_timestamp,'DD/MM/YYYY HH:MI:SS'),'DD/MM/YYYY HH:MI:SS');
-- merge Statment that does UPSERT
O_UpdatedCount :=SQL%ROWCOUNT;
commit;
O_EndTime:=to_timestamp(to_char(current_timestamp,'DD/MM/YYYY HH:MI:SS'),'DD/MM/YYYY HH:MI:SS');
begin
select extract( second from (O_EndTime-P_StartTime) )
into O_Duration
from dual;
Exception When others then
O_Duration:=0;
end;
end ModAuditData;
The Issue is
O_EndTime:=to_timestamp(to_char(current_timestamp,'DD/MM/YYYY HH:MI:SS'),'DD/MM/YYYY HH:MI:SS')
gives exact opposite of
P_StartTime:=to_timestamp(to_char(current_timestamp,'DD/MM/YYYY HH:MI:SS'),'DD/MM/YYYY HH:MI:SS');
in terms of AM/PM
What is the correct way to calculate the start and end time
CURRENT_TIMESTAMP is already a TIMESTAMP WITH TIME ZONE, there is no reason to convert it first to VARCHAR2 and then back again into a TIMESTAMP.
Also timestamp(3) (which provides precision up to millisecond) does not make much sense when you return duration as INTEGER, i.e. full seconds.
Try it like this:
P_StartTime timestamp(3) WITH TIME ZONE;
begin
P_StartTime := current_timestamp;
-- merge Statment that does UPSERT
O_UpdatedCount :=SQL%ROWCOUNT;
commit;
O_Duration := EXTRACT(SECOND FROM (current_timestamp - P_StartTime));
end;
In case of SQL*Plus consider to use TIMING command.

Job calls procedure not exactly same interval, which is defined in schedule

I have table with TIMESTAMP type column.
I have procedure which inserts into this table:
CREATE OR REPLACE PROCEDURE my_procedure
AS
BEGIN
INSERT INTO t2
(dt)
VALUES
(LOCALTIMESTAMP);
END;
Then create schedule:
BEGIN
dbms_scheduler.create_job(
job_name => 'my_job',
job_type => 'stored_procedure',
job_action => 'my_procedure',
start_date => '2016-10-31 17:01:01',
repeat_interval => 'FREQ=SECONDLY;INTERVAL=10',
end_date => '2016-11-30 17:01:01',
auto_drop => FALSE
);
END;
EXEC dbms_scheduler.ENABLE('my_job');
Then SELECT * FROM t2 ORDER BY dt
Results are:
2016-10-31 17:40:13
2016-10-31 17:40:21
2016-10-31 17:40:34
2016-10-31 17:40:42
2016-10-31 17:40:54
As you see, INTERVAL for schedule is 10 second, but difference between inserts (rows) are sometimes 8 second, sometimes 13 second, sometimes 12 second and so on.
Question: why is so not exactly that interval between procedure calls, which is defined in schedule?

How to format a Date variable in PLSQL

I am new to PL/SQL and have this question.
I created a procedure with the following specification:
PROCEDURE runschedule (i_RunDate IN DATE)
this i_RunDate comes in a specific format, and I need to change it to this format:
'MM/dd/yyyy hh:mi:ss PM'
I couldn't find how to re-format a Date variable.
You need to use the TO_CHAR function. Here's an example:
SELECT TO_CHAR(CURRENT_TIMESTAMP, 'MM/DD/YYYY HH12:MI:SS AM') FROM dual;
The DATE has not especific format, a DATE is a DATE. When stored in the database column, date values include the time of day in seconds since midnight, and a DATE variable has the same, only that when in some point you show the variable is formatted. PL/SQL can convert the datatype of a value implicitly, for example, can convert the CHAR value '02-JUN-92' to a DATE value.. but I don't recommend you rely on this implicit conversiosn. I always use the explicit conversion.
For more on this topic, you could read this:
http://download.oracle.com/docs/cd/B28359_01/appdev.111/b28370/datatypes.htm#i9118
In your case, if you want to format your DATE to log something, or to show in your UI or wathever in a different format, you need to assig it to a varchar variable as Justin said, something like this:
....
v_generated_run_date DATE;
v_var_date VARCHAR2(30);
BEGIN -- generate sysdate if required
IF (i_RunDate is null)
THEN
v_var_date:=TO_CHAR(sysdate, 'MM/DD/YYYY HH12:MI:SS AM');
ELSE
v_var_date:=TO_CHAR(i_RunDate,'MM/DD/YYYY HH12:MI:SS AM');
END IF;
pkgschedule.createschedule (v_var_date);
commit;
END runschedule;
END
Your createschedule procedure, in this case, will have a varchar2 parameter...

Resources